]>
Commit | Line | Data |
---|---|---|
b53cde35 DB |
1 | /* |
2 | * Frame Buffer Device for Toshiba Mobile IO(TMIO) controller | |
3 | * | |
4 | * Copyright(C) 2005-2006 Chris Humbert | |
5 | * Copyright(C) 2005 Dirk Opfer | |
6 | * Copytight(C) 2007,2008 Dmitry Baryshkov | |
7 | * | |
8 | * Based on: | |
9 | * drivers/video/w100fb.c | |
10 | * code written by Sharp/Lineo for 2.4 kernels | |
11 | * | |
12 | * This program is free software; you can redistribute it and/or modify | |
13 | * it under the terms of the GNU General Public License version 2 | |
14 | * as published by the Free Software Foundation; | |
15 | * | |
16 | * This program is distributed in the hope that it will be useful, | |
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 | * GNU General Public License for more details. | |
20 | */ | |
21 | ||
22 | #include <linux/kernel.h> | |
23 | #include <linux/module.h> | |
24 | #include <linux/platform_device.h> | |
25 | #include <linux/fb.h> | |
26 | #include <linux/interrupt.h> | |
27 | #include <linux/delay.h> | |
ac751efa | 28 | /* Why should fb driver call console functions? because console_lock() */ |
b53cde35 DB |
29 | #include <linux/console.h> |
30 | #include <linux/mfd/core.h> | |
31 | #include <linux/mfd/tmio.h> | |
32 | #include <linux/uaccess.h> | |
33 | ||
34 | /* | |
35 | * accelerator commands | |
36 | */ | |
37 | #define TMIOFB_ACC_CSADR(x) (0x00000000 | ((x) & 0x001ffffe)) | |
38 | #define TMIOFB_ACC_CHPIX(x) (0x01000000 | ((x) & 0x000003ff)) | |
39 | #define TMIOFB_ACC_CVPIX(x) (0x02000000 | ((x) & 0x000003ff)) | |
40 | #define TMIOFB_ACC_PSADR(x) (0x03000000 | ((x) & 0x00fffffe)) | |
41 | #define TMIOFB_ACC_PHPIX(x) (0x04000000 | ((x) & 0x000003ff)) | |
42 | #define TMIOFB_ACC_PVPIX(x) (0x05000000 | ((x) & 0x000003ff)) | |
43 | #define TMIOFB_ACC_PHOFS(x) (0x06000000 | ((x) & 0x000003ff)) | |
44 | #define TMIOFB_ACC_PVOFS(x) (0x07000000 | ((x) & 0x000003ff)) | |
45 | #define TMIOFB_ACC_POADR(x) (0x08000000 | ((x) & 0x00fffffe)) | |
46 | #define TMIOFB_ACC_RSTR(x) (0x09000000 | ((x) & 0x000000ff)) | |
47 | #define TMIOFB_ACC_TCLOR(x) (0x0A000000 | ((x) & 0x0000ffff)) | |
48 | #define TMIOFB_ACC_FILL(x) (0x0B000000 | ((x) & 0x0000ffff)) | |
49 | #define TMIOFB_ACC_DSADR(x) (0x0C000000 | ((x) & 0x00fffffe)) | |
50 | #define TMIOFB_ACC_SSADR(x) (0x0D000000 | ((x) & 0x00fffffe)) | |
51 | #define TMIOFB_ACC_DHPIX(x) (0x0E000000 | ((x) & 0x000003ff)) | |
52 | #define TMIOFB_ACC_DVPIX(x) (0x0F000000 | ((x) & 0x000003ff)) | |
53 | #define TMIOFB_ACC_SHPIX(x) (0x10000000 | ((x) & 0x000003ff)) | |
54 | #define TMIOFB_ACC_SVPIX(x) (0x11000000 | ((x) & 0x000003ff)) | |
55 | #define TMIOFB_ACC_LBINI(x) (0x12000000 | ((x) & 0x0000ffff)) | |
56 | #define TMIOFB_ACC_LBK2(x) (0x13000000 | ((x) & 0x0000ffff)) | |
57 | #define TMIOFB_ACC_SHBINI(x) (0x14000000 | ((x) & 0x0000ffff)) | |
58 | #define TMIOFB_ACC_SHBK2(x) (0x15000000 | ((x) & 0x0000ffff)) | |
59 | #define TMIOFB_ACC_SVBINI(x) (0x16000000 | ((x) & 0x0000ffff)) | |
60 | #define TMIOFB_ACC_SVBK2(x) (0x17000000 | ((x) & 0x0000ffff)) | |
61 | ||
62 | #define TMIOFB_ACC_CMGO 0x20000000 | |
63 | #define TMIOFB_ACC_CMGO_CEND 0x00000001 | |
64 | #define TMIOFB_ACC_CMGO_INT 0x00000002 | |
65 | #define TMIOFB_ACC_CMGO_CMOD 0x00000010 | |
66 | #define TMIOFB_ACC_CMGO_CDVRV 0x00000020 | |
67 | #define TMIOFB_ACC_CMGO_CDHRV 0x00000040 | |
68 | #define TMIOFB_ACC_CMGO_RUND 0x00008000 | |
69 | #define TMIOFB_ACC_SCGO 0x21000000 | |
70 | #define TMIOFB_ACC_SCGO_CEND 0x00000001 | |
71 | #define TMIOFB_ACC_SCGO_INT 0x00000002 | |
72 | #define TMIOFB_ACC_SCGO_ROP3 0x00000004 | |
73 | #define TMIOFB_ACC_SCGO_TRNS 0x00000008 | |
74 | #define TMIOFB_ACC_SCGO_DVRV 0x00000010 | |
75 | #define TMIOFB_ACC_SCGO_DHRV 0x00000020 | |
76 | #define TMIOFB_ACC_SCGO_SVRV 0x00000040 | |
77 | #define TMIOFB_ACC_SCGO_SHRV 0x00000080 | |
78 | #define TMIOFB_ACC_SCGO_DSTXY 0x00008000 | |
79 | #define TMIOFB_ACC_SBGO 0x22000000 | |
80 | #define TMIOFB_ACC_SBGO_CEND 0x00000001 | |
81 | #define TMIOFB_ACC_SBGO_INT 0x00000002 | |
82 | #define TMIOFB_ACC_SBGO_DVRV 0x00000010 | |
83 | #define TMIOFB_ACC_SBGO_DHRV 0x00000020 | |
84 | #define TMIOFB_ACC_SBGO_SVRV 0x00000040 | |
85 | #define TMIOFB_ACC_SBGO_SHRV 0x00000080 | |
86 | #define TMIOFB_ACC_SBGO_SBMD 0x00000100 | |
87 | #define TMIOFB_ACC_FLGO 0x23000000 | |
88 | #define TMIOFB_ACC_FLGO_CEND 0x00000001 | |
89 | #define TMIOFB_ACC_FLGO_INT 0x00000002 | |
90 | #define TMIOFB_ACC_FLGO_ROP3 0x00000004 | |
91 | #define TMIOFB_ACC_LDGO 0x24000000 | |
92 | #define TMIOFB_ACC_LDGO_CEND 0x00000001 | |
93 | #define TMIOFB_ACC_LDGO_INT 0x00000002 | |
94 | #define TMIOFB_ACC_LDGO_ROP3 0x00000004 | |
95 | #define TMIOFB_ACC_LDGO_ENDPX 0x00000008 | |
96 | #define TMIOFB_ACC_LDGO_LVRV 0x00000010 | |
97 | #define TMIOFB_ACC_LDGO_LHRV 0x00000020 | |
98 | #define TMIOFB_ACC_LDGO_LDMOD 0x00000040 | |
99 | ||
100 | /* a FIFO is always allocated, even if acceleration is not used */ | |
101 | #define TMIOFB_FIFO_SIZE 512 | |
102 | ||
103 | /* | |
104 | * LCD Host Controller Configuration Register | |
105 | * | |
106 | * This iomem area supports only 16-bit IO. | |
107 | */ | |
108 | #define CCR_CMD 0x04 /* Command */ | |
109 | #define CCR_REVID 0x08 /* Revision ID */ | |
110 | #define CCR_BASEL 0x10 /* LCD Control Reg Base Addr Low */ | |
111 | #define CCR_BASEH 0x12 /* LCD Control Reg Base Addr High */ | |
112 | #define CCR_UGCC 0x40 /* Unified Gated Clock Control */ | |
113 | #define CCR_GCC 0x42 /* Gated Clock Control */ | |
114 | #define CCR_USC 0x50 /* Unified Software Clear */ | |
115 | #define CCR_VRAMRTC 0x60 /* VRAM Timing Control */ | |
116 | /* 0x61 VRAM Refresh Control */ | |
117 | #define CCR_VRAMSAC 0x62 /* VRAM Access Control */ | |
118 | /* 0x63 VRAM Status */ | |
119 | #define CCR_VRAMBC 0x64 /* VRAM Block Control */ | |
120 | ||
121 | /* | |
122 | * LCD Control Register | |
123 | * | |
124 | * This iomem area supports only 16-bit IO. | |
125 | */ | |
126 | #define LCR_UIS 0x000 /* Unified Interrupt Status */ | |
127 | #define LCR_VHPN 0x008 /* VRAM Horizontal Pixel Number */ | |
128 | #define LCR_CFSAL 0x00a /* Command FIFO Start Address Low */ | |
129 | #define LCR_CFSAH 0x00c /* Command FIFO Start Address High */ | |
130 | #define LCR_CFS 0x00e /* Command FIFO Size */ | |
131 | #define LCR_CFWS 0x010 /* Command FIFO Writeable Size */ | |
132 | #define LCR_BBIE 0x012 /* BitBLT Interrupt Enable */ | |
133 | #define LCR_BBISC 0x014 /* BitBLT Interrupt Status and Clear */ | |
134 | #define LCR_CCS 0x016 /* Command Count Status */ | |
135 | #define LCR_BBES 0x018 /* BitBLT Execution Status */ | |
136 | #define LCR_CMDL 0x01c /* Command Low */ | |
137 | #define LCR_CMDH 0x01e /* Command High */ | |
138 | #define LCR_CFC 0x022 /* Command FIFO Clear */ | |
139 | #define LCR_CCIFC 0x024 /* CMOS Camera IF Control */ | |
140 | #define LCR_HWT 0x026 /* Hardware Test */ | |
141 | #define LCR_LCDCCRC 0x100 /* LCDC Clock and Reset Control */ | |
142 | #define LCR_LCDCC 0x102 /* LCDC Control */ | |
143 | #define LCR_LCDCOPC 0x104 /* LCDC Output Pin Control */ | |
144 | #define LCR_LCDIS 0x108 /* LCD Interrupt Status */ | |
145 | #define LCR_LCDIM 0x10a /* LCD Interrupt Mask */ | |
146 | #define LCR_LCDIE 0x10c /* LCD Interrupt Enable */ | |
147 | #define LCR_GDSAL 0x122 /* Graphics Display Start Address Low */ | |
148 | #define LCR_GDSAH 0x124 /* Graphics Display Start Address High */ | |
149 | #define LCR_VHPCL 0x12a /* VRAM Horizontal Pixel Count Low */ | |
150 | #define LCR_VHPCH 0x12c /* VRAM Horizontal Pixel Count High */ | |
151 | #define LCR_GM 0x12e /* Graphic Mode(VRAM access enable) */ | |
152 | #define LCR_HT 0x140 /* Horizontal Total */ | |
153 | #define LCR_HDS 0x142 /* Horizontal Display Start */ | |
154 | #define LCR_HSS 0x144 /* H-Sync Start */ | |
155 | #define LCR_HSE 0x146 /* H-Sync End */ | |
156 | #define LCR_HNP 0x14c /* Horizontal Number of Pixels */ | |
157 | #define LCR_VT 0x150 /* Vertical Total */ | |
158 | #define LCR_VDS 0x152 /* Vertical Display Start */ | |
159 | #define LCR_VSS 0x154 /* V-Sync Start */ | |
160 | #define LCR_VSE 0x156 /* V-Sync End */ | |
161 | #define LCR_CDLN 0x160 /* Current Display Line Number */ | |
162 | #define LCR_ILN 0x162 /* Interrupt Line Number */ | |
163 | #define LCR_SP 0x164 /* Sync Polarity */ | |
164 | #define LCR_MISC 0x166 /* MISC(RGB565 mode) */ | |
165 | #define LCR_VIHSS 0x16a /* Video Interface H-Sync Start */ | |
166 | #define LCR_VIVS 0x16c /* Video Interface Vertical Start */ | |
167 | #define LCR_VIVE 0x16e /* Video Interface Vertical End */ | |
168 | #define LCR_VIVSS 0x170 /* Video Interface V-Sync Start */ | |
169 | #define LCR_VCCIS 0x17e /* Video / CMOS Camera Interface Select */ | |
170 | #define LCR_VIDWSAL 0x180 /* VI Data Write Start Address Low */ | |
171 | #define LCR_VIDWSAH 0x182 /* VI Data Write Start Address High */ | |
172 | #define LCR_VIDRSAL 0x184 /* VI Data Read Start Address Low */ | |
173 | #define LCR_VIDRSAH 0x186 /* VI Data Read Start Address High */ | |
174 | #define LCR_VIPDDST 0x188 /* VI Picture Data Display Start Timing */ | |
175 | #define LCR_VIPDDET 0x186 /* VI Picture Data Display End Timing */ | |
176 | #define LCR_VIE 0x18c /* Video Interface Enable */ | |
177 | #define LCR_VCS 0x18e /* Video/Camera Select */ | |
178 | #define LCR_VPHWC 0x194 /* Video Picture Horizontal Wait Count */ | |
179 | #define LCR_VPHS 0x196 /* Video Picture Horizontal Size */ | |
180 | #define LCR_VPVWC 0x198 /* Video Picture Vertical Wait Count */ | |
181 | #define LCR_VPVS 0x19a /* Video Picture Vertical Size */ | |
182 | #define LCR_PLHPIX 0x1a0 /* PLHPIX */ | |
183 | #define LCR_XS 0x1a2 /* XStart */ | |
184 | #define LCR_XCKHW 0x1a4 /* XCK High Width */ | |
185 | #define LCR_STHS 0x1a8 /* STH Start */ | |
186 | #define LCR_VT2 0x1aa /* Vertical Total */ | |
187 | #define LCR_YCKSW 0x1ac /* YCK Start Wait */ | |
188 | #define LCR_YSTS 0x1ae /* YST Start */ | |
189 | #define LCR_PPOLS 0x1b0 /* #PPOL Start */ | |
190 | #define LCR_PRECW 0x1b2 /* PREC Width */ | |
191 | #define LCR_VCLKHW 0x1b4 /* VCLK High Width */ | |
192 | #define LCR_OC 0x1b6 /* Output Control */ | |
193 | ||
194 | static char *mode_option __devinitdata; | |
195 | ||
196 | struct tmiofb_par { | |
197 | u32 pseudo_palette[16]; | |
198 | ||
199 | #ifdef CONFIG_FB_TMIO_ACCELL | |
200 | wait_queue_head_t wait_acc; | |
201 | bool use_polling; | |
202 | #endif | |
203 | ||
204 | void __iomem *ccr; | |
205 | void __iomem *lcr; | |
206 | }; | |
207 | ||
208 | /*--------------------------------------------------------------------------*/ | |
209 | ||
210 | /* | |
211 | * reasons for an interrupt: | |
212 | * uis bbisc lcdis | |
213 | * 0100 0001 accelerator command completed | |
214 | * 2000 0001 vsync start | |
215 | * 2000 0002 display start | |
216 | * 2000 0004 line number match(0x1ff mask???) | |
217 | */ | |
218 | static irqreturn_t tmiofb_irq(int irq, void *__info) | |
219 | { | |
220 | struct fb_info *info = __info; | |
221 | struct tmiofb_par *par = info->par; | |
222 | unsigned int bbisc = tmio_ioread16(par->lcr + LCR_BBISC); | |
223 | ||
224 | ||
a495a6d3 DB |
225 | tmio_iowrite16(bbisc, par->lcr + LCR_BBISC); |
226 | ||
227 | #ifdef CONFIG_FB_TMIO_ACCELL | |
b53cde35 DB |
228 | /* |
229 | * We were in polling mode and now we got correct irq. | |
230 | * Switch back to IRQ-based sync of command FIFO | |
231 | */ | |
232 | if (unlikely(par->use_polling && irq != -1)) { | |
233 | printk(KERN_INFO "tmiofb: switching to waitq\n"); | |
234 | par->use_polling = false; | |
235 | } | |
236 | ||
b53cde35 DB |
237 | if (bbisc & 1) |
238 | wake_up(&par->wait_acc); | |
239 | #endif | |
240 | ||
241 | return IRQ_HANDLED; | |
242 | } | |
243 | ||
244 | ||
245 | /*--------------------------------------------------------------------------*/ | |
246 | ||
247 | ||
248 | /* | |
249 | * Turns off the LCD controller and LCD host controller. | |
250 | */ | |
251 | static int tmiofb_hw_stop(struct platform_device *dev) | |
252 | { | |
1f8c666c | 253 | struct tmio_fb_data *data = dev->dev.platform_data; |
b53cde35 DB |
254 | struct fb_info *info = platform_get_drvdata(dev); |
255 | struct tmiofb_par *par = info->par; | |
256 | ||
257 | tmio_iowrite16(0, par->ccr + CCR_UGCC); | |
258 | tmio_iowrite16(0, par->lcr + LCR_GM); | |
259 | data->lcd_set_power(dev, 0); | |
260 | tmio_iowrite16(0x0010, par->lcr + LCR_LCDCCRC); | |
261 | ||
262 | return 0; | |
263 | } | |
264 | ||
265 | /* | |
266 | * Initializes the LCD host controller. | |
267 | */ | |
268 | static int tmiofb_hw_init(struct platform_device *dev) | |
269 | { | |
944dc035 | 270 | const struct mfd_cell *cell = mfd_get_cell(dev); |
b53cde35 DB |
271 | struct fb_info *info = platform_get_drvdata(dev); |
272 | struct tmiofb_par *par = info->par; | |
273 | const struct resource *nlcr = &cell->resources[0]; | |
274 | const struct resource *vram = &cell->resources[2]; | |
275 | unsigned long base; | |
276 | ||
277 | if (nlcr == NULL || vram == NULL) | |
278 | return -EINVAL; | |
279 | ||
280 | base = nlcr->start; | |
281 | ||
282 | tmio_iowrite16(0x003a, par->ccr + CCR_UGCC); | |
283 | tmio_iowrite16(0x003a, par->ccr + CCR_GCC); | |
284 | tmio_iowrite16(0x3f00, par->ccr + CCR_USC); | |
285 | ||
286 | msleep(2); /* wait for device to settle */ | |
287 | ||
288 | tmio_iowrite16(0x0000, par->ccr + CCR_USC); | |
289 | tmio_iowrite16(base >> 16, par->ccr + CCR_BASEH); | |
290 | tmio_iowrite16(base, par->ccr + CCR_BASEL); | |
291 | tmio_iowrite16(0x0002, par->ccr + CCR_CMD); /* base address enable */ | |
292 | tmio_iowrite16(0x40a8, par->ccr + CCR_VRAMRTC); /* VRAMRC, VRAMTC */ | |
293 | tmio_iowrite16(0x0018, par->ccr + CCR_VRAMSAC); /* VRAMSTS, VRAMAC */ | |
294 | tmio_iowrite16(0x0002, par->ccr + CCR_VRAMBC); | |
295 | msleep(2); /* wait for device to settle */ | |
296 | tmio_iowrite16(0x000b, par->ccr + CCR_VRAMBC); | |
297 | ||
298 | base = vram->start + info->screen_size; | |
299 | tmio_iowrite16(base >> 16, par->lcr + LCR_CFSAH); | |
300 | tmio_iowrite16(base, par->lcr + LCR_CFSAL); | |
301 | tmio_iowrite16(TMIOFB_FIFO_SIZE - 1, par->lcr + LCR_CFS); | |
302 | tmio_iowrite16(1, par->lcr + LCR_CFC); | |
303 | tmio_iowrite16(1, par->lcr + LCR_BBIE); | |
304 | tmio_iowrite16(0, par->lcr + LCR_CFWS); | |
305 | ||
306 | return 0; | |
307 | } | |
308 | ||
309 | /* | |
310 | * Sets the LCD controller's output resolution and pixel clock | |
311 | */ | |
312 | static void tmiofb_hw_mode(struct platform_device *dev) | |
313 | { | |
1f8c666c | 314 | struct tmio_fb_data *data = dev->dev.platform_data; |
b53cde35 DB |
315 | struct fb_info *info = platform_get_drvdata(dev); |
316 | struct fb_videomode *mode = info->mode; | |
317 | struct tmiofb_par *par = info->par; | |
318 | unsigned int i; | |
319 | ||
320 | tmio_iowrite16(0, par->lcr + LCR_GM); | |
321 | data->lcd_set_power(dev, 0); | |
322 | tmio_iowrite16(0x0010, par->lcr + LCR_LCDCCRC); | |
323 | data->lcd_mode(dev, mode); | |
324 | data->lcd_set_power(dev, 1); | |
325 | ||
326 | tmio_iowrite16(info->fix.line_length, par->lcr + LCR_VHPN); | |
327 | tmio_iowrite16(0, par->lcr + LCR_GDSAH); | |
328 | tmio_iowrite16(0, par->lcr + LCR_GDSAL); | |
329 | tmio_iowrite16(info->fix.line_length >> 16, par->lcr + LCR_VHPCH); | |
330 | tmio_iowrite16(info->fix.line_length, par->lcr + LCR_VHPCL); | |
331 | tmio_iowrite16(i = 0, par->lcr + LCR_HSS); | |
332 | tmio_iowrite16(i += mode->hsync_len, par->lcr + LCR_HSE); | |
333 | tmio_iowrite16(i += mode->left_margin, par->lcr + LCR_HDS); | |
334 | tmio_iowrite16(i += mode->xres + mode->right_margin, par->lcr + LCR_HT); | |
335 | tmio_iowrite16(mode->xres, par->lcr + LCR_HNP); | |
336 | tmio_iowrite16(i = 0, par->lcr + LCR_VSS); | |
337 | tmio_iowrite16(i += mode->vsync_len, par->lcr + LCR_VSE); | |
338 | tmio_iowrite16(i += mode->upper_margin, par->lcr + LCR_VDS); | |
339 | tmio_iowrite16(i += mode->yres, par->lcr + LCR_ILN); | |
340 | tmio_iowrite16(i += mode->lower_margin, par->lcr + LCR_VT); | |
341 | tmio_iowrite16(3, par->lcr + LCR_MISC); /* RGB565 mode */ | |
342 | tmio_iowrite16(1, par->lcr + LCR_GM); /* VRAM enable */ | |
343 | tmio_iowrite16(0x4007, par->lcr + LCR_LCDCC); | |
344 | tmio_iowrite16(3, par->lcr + LCR_SP); /* sync polarity */ | |
345 | ||
346 | tmio_iowrite16(0x0010, par->lcr + LCR_LCDCCRC); | |
347 | msleep(5); /* wait for device to settle */ | |
348 | tmio_iowrite16(0x0014, par->lcr + LCR_LCDCCRC); /* STOP_CKP */ | |
349 | msleep(5); /* wait for device to settle */ | |
350 | tmio_iowrite16(0x0015, par->lcr + LCR_LCDCCRC); /* STOP_CKP|SOFT_RESET*/ | |
351 | tmio_iowrite16(0xfffa, par->lcr + LCR_VCS); | |
352 | } | |
353 | ||
354 | /*--------------------------------------------------------------------------*/ | |
355 | ||
356 | #ifdef CONFIG_FB_TMIO_ACCELL | |
357 | static int __must_check | |
358 | tmiofb_acc_wait(struct fb_info *info, unsigned int ccs) | |
359 | { | |
360 | struct tmiofb_par *par = info->par; | |
361 | /* | |
25985edc | 362 | * This code can be called with interrupts disabled. |
b53cde35 DB |
363 | * So instead of relaying on irq to trigger the event, |
364 | * poll the state till the necessary command is executed. | |
365 | */ | |
366 | if (irqs_disabled() || par->use_polling) { | |
367 | int i = 0; | |
368 | while (tmio_ioread16(par->lcr + LCR_CCS) > ccs) { | |
369 | udelay(1); | |
370 | i++; | |
371 | if (i > 10000) { | |
372 | pr_err("tmiofb: timeout waiting for %d\n", | |
373 | ccs); | |
374 | return -ETIMEDOUT; | |
375 | } | |
376 | tmiofb_irq(-1, info); | |
377 | } | |
378 | } else { | |
379 | if (!wait_event_interruptible_timeout(par->wait_acc, | |
380 | tmio_ioread16(par->lcr + LCR_CCS) <= ccs, | |
381 | 1000)) { | |
382 | pr_err("tmiofb: timeout waiting for %d\n", ccs); | |
383 | return -ETIMEDOUT; | |
384 | } | |
385 | } | |
386 | ||
387 | return 0; | |
388 | } | |
389 | ||
390 | /* | |
391 | * Writes an accelerator command to the accelerator's FIFO. | |
392 | */ | |
393 | static int | |
394 | tmiofb_acc_write(struct fb_info *info, const u32 *cmd, unsigned int count) | |
395 | { | |
396 | struct tmiofb_par *par = info->par; | |
397 | int ret; | |
398 | ||
399 | ret = tmiofb_acc_wait(info, TMIOFB_FIFO_SIZE - count); | |
400 | if (ret) | |
401 | return ret; | |
402 | ||
403 | for (; count; count--, cmd++) { | |
404 | tmio_iowrite16(*cmd >> 16, par->lcr + LCR_CMDH); | |
405 | tmio_iowrite16(*cmd, par->lcr + LCR_CMDL); | |
406 | } | |
407 | ||
408 | return ret; | |
409 | } | |
410 | ||
411 | /* | |
412 | * Wait for the accelerator to finish its operations before writing | |
413 | * to the framebuffer for consistent display output. | |
414 | */ | |
415 | static int tmiofb_sync(struct fb_info *fbi) | |
416 | { | |
417 | struct tmiofb_par *par = fbi->par; | |
418 | ||
419 | int ret; | |
420 | int i = 0; | |
421 | ||
422 | ret = tmiofb_acc_wait(fbi, 0); | |
423 | ||
424 | while (tmio_ioread16(par->lcr + LCR_BBES) & 2) { /* blit active */ | |
425 | udelay(1); | |
426 | i++ ; | |
427 | if (i > 10000) { | |
428 | printk(KERN_ERR "timeout waiting for blit to end!\n"); | |
429 | return -ETIMEDOUT; | |
430 | } | |
431 | } | |
432 | ||
433 | return ret; | |
434 | } | |
435 | ||
436 | static void | |
437 | tmiofb_fillrect(struct fb_info *fbi, const struct fb_fillrect *rect) | |
438 | { | |
439 | const u32 cmd[] = { | |
440 | TMIOFB_ACC_DSADR((rect->dy * fbi->mode->xres + rect->dx) * 2), | |
441 | TMIOFB_ACC_DHPIX(rect->width - 1), | |
442 | TMIOFB_ACC_DVPIX(rect->height - 1), | |
443 | TMIOFB_ACC_FILL(rect->color), | |
444 | TMIOFB_ACC_FLGO, | |
445 | }; | |
446 | ||
447 | if (fbi->state != FBINFO_STATE_RUNNING || | |
448 | fbi->flags & FBINFO_HWACCEL_DISABLED) { | |
449 | cfb_fillrect(fbi, rect); | |
450 | return; | |
451 | } | |
452 | ||
453 | tmiofb_acc_write(fbi, cmd, ARRAY_SIZE(cmd)); | |
454 | } | |
455 | ||
456 | static void | |
457 | tmiofb_copyarea(struct fb_info *fbi, const struct fb_copyarea *area) | |
458 | { | |
459 | const u32 cmd[] = { | |
460 | TMIOFB_ACC_DSADR((area->dy * fbi->mode->xres + area->dx) * 2), | |
461 | TMIOFB_ACC_DHPIX(area->width - 1), | |
462 | TMIOFB_ACC_DVPIX(area->height - 1), | |
463 | TMIOFB_ACC_SSADR((area->sy * fbi->mode->xres + area->sx) * 2), | |
464 | TMIOFB_ACC_SCGO, | |
465 | }; | |
466 | ||
467 | if (fbi->state != FBINFO_STATE_RUNNING || | |
468 | fbi->flags & FBINFO_HWACCEL_DISABLED) { | |
469 | cfb_copyarea(fbi, area); | |
470 | return; | |
471 | } | |
472 | ||
473 | tmiofb_acc_write(fbi, cmd, ARRAY_SIZE(cmd)); | |
474 | } | |
475 | #endif | |
476 | ||
477 | static void tmiofb_clearscreen(struct fb_info *info) | |
478 | { | |
479 | const struct fb_fillrect rect = { | |
480 | .dx = 0, | |
481 | .dy = 0, | |
482 | .width = info->mode->xres, | |
483 | .height = info->mode->yres, | |
484 | .color = 0, | |
485 | .rop = ROP_COPY, | |
486 | }; | |
487 | ||
488 | info->fbops->fb_fillrect(info, &rect); | |
489 | } | |
490 | ||
491 | static int tmiofb_vblank(struct fb_info *fbi, struct fb_vblank *vblank) | |
492 | { | |
493 | struct tmiofb_par *par = fbi->par; | |
494 | struct fb_videomode *mode = fbi->mode; | |
495 | unsigned int vcount = tmio_ioread16(par->lcr + LCR_CDLN); | |
496 | unsigned int vds = mode->vsync_len + mode->upper_margin; | |
497 | ||
498 | vblank->vcount = vcount; | |
499 | vblank->flags = FB_VBLANK_HAVE_VBLANK | FB_VBLANK_HAVE_VCOUNT | |
500 | | FB_VBLANK_HAVE_VSYNC; | |
501 | ||
502 | if (vcount < mode->vsync_len) | |
503 | vblank->flags |= FB_VBLANK_VSYNCING; | |
504 | ||
505 | if (vcount < vds || vcount > vds + mode->yres) | |
506 | vblank->flags |= FB_VBLANK_VBLANKING; | |
507 | ||
508 | return 0; | |
509 | } | |
510 | ||
511 | ||
512 | static int tmiofb_ioctl(struct fb_info *fbi, | |
513 | unsigned int cmd, unsigned long arg) | |
514 | { | |
515 | switch (cmd) { | |
516 | case FBIOGET_VBLANK: { | |
517 | struct fb_vblank vblank = {0}; | |
518 | void __user *argp = (void __user *) arg; | |
519 | ||
520 | tmiofb_vblank(fbi, &vblank); | |
521 | if (copy_to_user(argp, &vblank, sizeof vblank)) | |
522 | return -EFAULT; | |
523 | return 0; | |
524 | } | |
525 | ||
526 | #ifdef CONFIG_FB_TMIO_ACCELL | |
527 | case FBIO_TMIO_ACC_SYNC: | |
528 | tmiofb_sync(fbi); | |
529 | return 0; | |
530 | ||
531 | case FBIO_TMIO_ACC_WRITE: { | |
532 | u32 __user *argp = (void __user *) arg; | |
533 | u32 len; | |
534 | u32 acc[16]; | |
535 | ||
536 | if (get_user(len, argp)) | |
537 | return -EFAULT; | |
538 | if (len > ARRAY_SIZE(acc)) | |
539 | return -EINVAL; | |
540 | if (copy_from_user(acc, argp + 1, sizeof(u32) * len)) | |
541 | return -EFAULT; | |
542 | ||
543 | return tmiofb_acc_write(fbi, acc, len); | |
544 | } | |
545 | #endif | |
546 | } | |
547 | ||
548 | return -ENOTTY; | |
549 | } | |
550 | ||
551 | /*--------------------------------------------------------------------------*/ | |
552 | ||
553 | /* Select the smallest mode that allows the desired resolution to be | |
554 | * displayed. If desired, the x and y parameters can be rounded up to | |
555 | * match the selected mode. | |
556 | */ | |
557 | static struct fb_videomode * | |
558 | tmiofb_find_mode(struct fb_info *info, struct fb_var_screeninfo *var) | |
559 | { | |
1f8c666c | 560 | struct tmio_fb_data *data = info->device->platform_data; |
b53cde35 DB |
561 | struct fb_videomode *best = NULL; |
562 | int i; | |
563 | ||
564 | for (i = 0; i < data->num_modes; i++) { | |
565 | struct fb_videomode *mode = data->modes + i; | |
566 | ||
567 | if (mode->xres >= var->xres && mode->yres >= var->yres | |
568 | && (!best || (mode->xres < best->xres | |
569 | && mode->yres < best->yres))) | |
570 | best = mode; | |
571 | } | |
572 | ||
573 | return best; | |
574 | } | |
575 | ||
576 | static int tmiofb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) | |
577 | { | |
578 | ||
579 | struct fb_videomode *mode; | |
1f8c666c | 580 | struct tmio_fb_data *data = info->device->platform_data; |
b53cde35 DB |
581 | |
582 | mode = tmiofb_find_mode(info, var); | |
583 | if (!mode || var->bits_per_pixel > 16) | |
584 | return -EINVAL; | |
585 | ||
586 | fb_videomode_to_var(var, mode); | |
587 | ||
588 | var->xres_virtual = mode->xres; | |
589 | var->yres_virtual = info->screen_size / (mode->xres * 2); | |
590 | ||
591 | if (var->yres_virtual < var->yres) | |
592 | return -EINVAL; | |
593 | ||
594 | var->xoffset = 0; | |
595 | var->yoffset = 0; | |
596 | var->bits_per_pixel = 16; | |
597 | var->grayscale = 0; | |
598 | var->red.offset = 11; | |
599 | var->red.length = 5; | |
600 | var->green.offset = 5; | |
601 | var->green.length = 6; | |
602 | var->blue.offset = 0; | |
603 | var->blue.length = 5; | |
604 | var->transp.offset = 0; | |
605 | var->transp.length = 0; | |
606 | var->nonstd = 0; | |
607 | var->height = data->height; /* mm */ | |
608 | var->width = data->width; /* mm */ | |
609 | var->rotate = 0; | |
610 | return 0; | |
611 | } | |
612 | ||
613 | static int tmiofb_set_par(struct fb_info *info) | |
614 | { | |
615 | struct fb_var_screeninfo *var = &info->var; | |
616 | struct fb_videomode *mode; | |
617 | ||
618 | mode = tmiofb_find_mode(info, var); | |
619 | if (!mode) | |
620 | return -EINVAL; | |
621 | ||
622 | info->mode = mode; | |
623 | info->fix.line_length = info->mode->xres * | |
624 | var->bits_per_pixel / 8; | |
625 | ||
626 | tmiofb_hw_mode(to_platform_device(info->device)); | |
627 | tmiofb_clearscreen(info); | |
628 | return 0; | |
629 | } | |
630 | ||
631 | static int tmiofb_setcolreg(unsigned regno, unsigned red, unsigned green, | |
632 | unsigned blue, unsigned transp, | |
633 | struct fb_info *info) | |
634 | { | |
635 | struct tmiofb_par *par = info->par; | |
636 | ||
637 | if (regno < ARRAY_SIZE(par->pseudo_palette)) { | |
638 | par->pseudo_palette[regno] = | |
639 | ((red & 0xf800)) | | |
640 | ((green & 0xfc00) >> 5) | | |
641 | ((blue & 0xf800) >> 11); | |
642 | return 0; | |
643 | } | |
644 | ||
645 | return -EINVAL; | |
646 | } | |
647 | ||
648 | static int tmiofb_blank(int blank, struct fb_info *info) | |
649 | { | |
650 | /* | |
651 | * everything is done in lcd/bl drivers. | |
652 | * this is purely to make sysfs happy and work. | |
653 | */ | |
654 | return 0; | |
655 | } | |
656 | ||
657 | static struct fb_ops tmiofb_ops = { | |
658 | .owner = THIS_MODULE, | |
659 | ||
660 | .fb_ioctl = tmiofb_ioctl, | |
661 | .fb_check_var = tmiofb_check_var, | |
662 | .fb_set_par = tmiofb_set_par, | |
663 | .fb_setcolreg = tmiofb_setcolreg, | |
664 | .fb_blank = tmiofb_blank, | |
665 | .fb_imageblit = cfb_imageblit, | |
666 | #ifdef CONFIG_FB_TMIO_ACCELL | |
667 | .fb_sync = tmiofb_sync, | |
668 | .fb_fillrect = tmiofb_fillrect, | |
669 | .fb_copyarea = tmiofb_copyarea, | |
670 | #else | |
671 | .fb_fillrect = cfb_fillrect, | |
672 | .fb_copyarea = cfb_copyarea, | |
673 | #endif | |
674 | }; | |
675 | ||
676 | /*--------------------------------------------------------------------------*/ | |
677 | ||
678 | static int __devinit tmiofb_probe(struct platform_device *dev) | |
679 | { | |
944dc035 | 680 | const struct mfd_cell *cell = mfd_get_cell(dev); |
1f8c666c | 681 | struct tmio_fb_data *data = dev->dev.platform_data; |
b53cde35 DB |
682 | struct resource *ccr = platform_get_resource(dev, IORESOURCE_MEM, 1); |
683 | struct resource *lcr = platform_get_resource(dev, IORESOURCE_MEM, 0); | |
684 | struct resource *vram = platform_get_resource(dev, IORESOURCE_MEM, 2); | |
685 | int irq = platform_get_irq(dev, 0); | |
686 | struct fb_info *info; | |
687 | struct tmiofb_par *par; | |
688 | int retval; | |
689 | ||
690 | /* | |
691 | * This is the only way ATM to disable the fb | |
692 | */ | |
693 | if (data == NULL) { | |
694 | dev_err(&dev->dev, "NULL platform data!\n"); | |
695 | return -EINVAL; | |
696 | } | |
697 | ||
698 | info = framebuffer_alloc(sizeof(struct tmiofb_par), &dev->dev); | |
699 | ||
700 | if (!info) | |
701 | return -ENOMEM; | |
702 | ||
703 | par = info->par; | |
704 | ||
705 | #ifdef CONFIG_FB_TMIO_ACCELL | |
706 | init_waitqueue_head(&par->wait_acc); | |
707 | ||
708 | par->use_polling = true; | |
709 | ||
710 | info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA | |
711 | | FBINFO_HWACCEL_FILLRECT; | |
712 | #else | |
713 | info->flags = FBINFO_DEFAULT; | |
714 | #endif | |
715 | ||
716 | info->fbops = &tmiofb_ops; | |
717 | ||
718 | strcpy(info->fix.id, "tmio-fb"); | |
719 | info->fix.smem_start = vram->start; | |
720 | info->fix.smem_len = resource_size(vram); | |
721 | info->fix.type = FB_TYPE_PACKED_PIXELS; | |
722 | info->fix.visual = FB_VISUAL_TRUECOLOR; | |
723 | info->fix.mmio_start = lcr->start; | |
724 | info->fix.mmio_len = resource_size(lcr); | |
725 | info->fix.accel = FB_ACCEL_NONE; | |
726 | info->screen_size = info->fix.smem_len - (4 * TMIOFB_FIFO_SIZE); | |
727 | info->pseudo_palette = par->pseudo_palette; | |
728 | ||
729 | par->ccr = ioremap(ccr->start, resource_size(ccr)); | |
730 | if (!par->ccr) { | |
731 | retval = -ENOMEM; | |
732 | goto err_ioremap_ccr; | |
733 | } | |
734 | ||
735 | par->lcr = ioremap(info->fix.mmio_start, info->fix.mmio_len); | |
736 | if (!par->lcr) { | |
737 | retval = -ENOMEM; | |
738 | goto err_ioremap_lcr; | |
739 | } | |
740 | ||
741 | info->screen_base = ioremap(info->fix.smem_start, info->fix.smem_len); | |
742 | if (!info->screen_base) { | |
743 | retval = -ENOMEM; | |
744 | goto err_ioremap_vram; | |
745 | } | |
746 | ||
747 | retval = request_irq(irq, &tmiofb_irq, IRQF_DISABLED, | |
7ad33e74 | 748 | dev_name(&dev->dev), info); |
b53cde35 DB |
749 | |
750 | if (retval) | |
751 | goto err_request_irq; | |
752 | ||
753 | platform_set_drvdata(dev, info); | |
754 | ||
755 | retval = fb_find_mode(&info->var, info, mode_option, | |
756 | data->modes, data->num_modes, | |
757 | data->modes, 16); | |
758 | if (!retval) { | |
759 | retval = -EINVAL; | |
760 | goto err_find_mode; | |
761 | } | |
762 | ||
763 | if (cell->enable) { | |
764 | retval = cell->enable(dev); | |
765 | if (retval) | |
766 | goto err_enable; | |
767 | } | |
768 | ||
769 | retval = tmiofb_hw_init(dev); | |
770 | if (retval) | |
771 | goto err_hw_init; | |
772 | ||
773 | fb_videomode_to_modelist(data->modes, data->num_modes, | |
774 | &info->modelist); | |
775 | ||
776 | retval = register_framebuffer(info); | |
777 | if (retval < 0) | |
778 | goto err_register_framebuffer; | |
779 | ||
780 | printk(KERN_INFO "fb%d: %s frame buffer device\n", | |
781 | info->node, info->fix.id); | |
782 | ||
783 | return 0; | |
784 | ||
785 | err_register_framebuffer: | |
786 | /*err_set_par:*/ | |
787 | tmiofb_hw_stop(dev); | |
788 | err_hw_init: | |
789 | if (cell->disable) | |
790 | cell->disable(dev); | |
791 | err_enable: | |
792 | err_find_mode: | |
793 | platform_set_drvdata(dev, NULL); | |
794 | free_irq(irq, info); | |
795 | err_request_irq: | |
796 | iounmap(info->screen_base); | |
797 | err_ioremap_vram: | |
798 | iounmap(par->lcr); | |
799 | err_ioremap_lcr: | |
800 | iounmap(par->ccr); | |
801 | err_ioremap_ccr: | |
802 | framebuffer_release(info); | |
803 | return retval; | |
804 | } | |
805 | ||
806 | static int __devexit tmiofb_remove(struct platform_device *dev) | |
807 | { | |
944dc035 | 808 | const struct mfd_cell *cell = mfd_get_cell(dev); |
b53cde35 DB |
809 | struct fb_info *info = platform_get_drvdata(dev); |
810 | int irq = platform_get_irq(dev, 0); | |
811 | struct tmiofb_par *par; | |
812 | ||
813 | if (info) { | |
814 | par = info->par; | |
815 | unregister_framebuffer(info); | |
816 | ||
817 | tmiofb_hw_stop(dev); | |
818 | ||
819 | if (cell->disable) | |
820 | cell->disable(dev); | |
821 | ||
822 | platform_set_drvdata(dev, NULL); | |
823 | ||
824 | free_irq(irq, info); | |
825 | ||
826 | iounmap(info->screen_base); | |
827 | iounmap(par->lcr); | |
828 | iounmap(par->ccr); | |
829 | ||
830 | framebuffer_release(info); | |
831 | } | |
832 | ||
833 | return 0; | |
834 | } | |
835 | ||
836 | #ifdef DEBUG | |
837 | static void tmiofb_dump_regs(struct platform_device *dev) | |
838 | { | |
839 | struct fb_info *info = platform_get_drvdata(dev); | |
840 | struct tmiofb_par *par = info->par; | |
841 | ||
842 | printk(KERN_DEBUG "lhccr:\n"); | |
843 | #define CCR_PR(n) printk(KERN_DEBUG "\t" #n " = \t%04x\n",\ | |
844 | tmio_ioread16(par->ccr + CCR_ ## n)); | |
845 | CCR_PR(CMD); | |
846 | CCR_PR(REVID); | |
847 | CCR_PR(BASEL); | |
848 | CCR_PR(BASEH); | |
849 | CCR_PR(UGCC); | |
850 | CCR_PR(GCC); | |
851 | CCR_PR(USC); | |
852 | CCR_PR(VRAMRTC); | |
853 | CCR_PR(VRAMSAC); | |
854 | CCR_PR(VRAMBC); | |
855 | #undef CCR_PR | |
856 | ||
857 | printk(KERN_DEBUG "lcr: \n"); | |
858 | #define LCR_PR(n) printk(KERN_DEBUG "\t" #n " = \t%04x\n",\ | |
859 | tmio_ioread16(par->lcr + LCR_ ## n)); | |
860 | LCR_PR(UIS); | |
861 | LCR_PR(VHPN); | |
862 | LCR_PR(CFSAL); | |
863 | LCR_PR(CFSAH); | |
864 | LCR_PR(CFS); | |
865 | LCR_PR(CFWS); | |
866 | LCR_PR(BBIE); | |
867 | LCR_PR(BBISC); | |
868 | LCR_PR(CCS); | |
869 | LCR_PR(BBES); | |
870 | LCR_PR(CMDL); | |
871 | LCR_PR(CMDH); | |
872 | LCR_PR(CFC); | |
873 | LCR_PR(CCIFC); | |
874 | LCR_PR(HWT); | |
875 | LCR_PR(LCDCCRC); | |
876 | LCR_PR(LCDCC); | |
877 | LCR_PR(LCDCOPC); | |
878 | LCR_PR(LCDIS); | |
879 | LCR_PR(LCDIM); | |
880 | LCR_PR(LCDIE); | |
881 | LCR_PR(GDSAL); | |
882 | LCR_PR(GDSAH); | |
883 | LCR_PR(VHPCL); | |
884 | LCR_PR(VHPCH); | |
885 | LCR_PR(GM); | |
886 | LCR_PR(HT); | |
887 | LCR_PR(HDS); | |
888 | LCR_PR(HSS); | |
889 | LCR_PR(HSE); | |
890 | LCR_PR(HNP); | |
891 | LCR_PR(VT); | |
892 | LCR_PR(VDS); | |
893 | LCR_PR(VSS); | |
894 | LCR_PR(VSE); | |
895 | LCR_PR(CDLN); | |
896 | LCR_PR(ILN); | |
897 | LCR_PR(SP); | |
898 | LCR_PR(MISC); | |
899 | LCR_PR(VIHSS); | |
900 | LCR_PR(VIVS); | |
901 | LCR_PR(VIVE); | |
902 | LCR_PR(VIVSS); | |
903 | LCR_PR(VCCIS); | |
904 | LCR_PR(VIDWSAL); | |
905 | LCR_PR(VIDWSAH); | |
906 | LCR_PR(VIDRSAL); | |
907 | LCR_PR(VIDRSAH); | |
908 | LCR_PR(VIPDDST); | |
909 | LCR_PR(VIPDDET); | |
910 | LCR_PR(VIE); | |
911 | LCR_PR(VCS); | |
912 | LCR_PR(VPHWC); | |
913 | LCR_PR(VPHS); | |
914 | LCR_PR(VPVWC); | |
915 | LCR_PR(VPVS); | |
916 | LCR_PR(PLHPIX); | |
917 | LCR_PR(XS); | |
918 | LCR_PR(XCKHW); | |
919 | LCR_PR(STHS); | |
920 | LCR_PR(VT2); | |
921 | LCR_PR(YCKSW); | |
922 | LCR_PR(YSTS); | |
923 | LCR_PR(PPOLS); | |
924 | LCR_PR(PRECW); | |
925 | LCR_PR(VCLKHW); | |
926 | LCR_PR(OC); | |
927 | #undef LCR_PR | |
928 | } | |
929 | #endif | |
930 | ||
931 | #ifdef CONFIG_PM | |
932 | static int tmiofb_suspend(struct platform_device *dev, pm_message_t state) | |
933 | { | |
934 | struct fb_info *info = platform_get_drvdata(dev); | |
a495a6d3 | 935 | #ifdef CONFIG_FB_TMIO_ACCELL |
b53cde35 | 936 | struct tmiofb_par *par = info->par; |
a495a6d3 | 937 | #endif |
944dc035 | 938 | const struct mfd_cell *cell = mfd_get_cell(dev); |
b53cde35 DB |
939 | int retval = 0; |
940 | ||
ac751efa | 941 | console_lock(); |
b53cde35 DB |
942 | |
943 | fb_set_suspend(info, 1); | |
944 | ||
945 | if (info->fbops->fb_sync) | |
946 | info->fbops->fb_sync(info); | |
947 | ||
948 | ||
a495a6d3 | 949 | #ifdef CONFIG_FB_TMIO_ACCELL |
b53cde35 DB |
950 | /* |
951 | * The fb should be usable even if interrupts are disabled (and they are | |
952 | * during suspend/resume). Switch temporary to forced polling. | |
953 | */ | |
954 | printk(KERN_INFO "tmiofb: switching to polling\n"); | |
955 | par->use_polling = true; | |
a495a6d3 | 956 | #endif |
b53cde35 DB |
957 | tmiofb_hw_stop(dev); |
958 | ||
959 | if (cell->suspend) | |
960 | retval = cell->suspend(dev); | |
961 | ||
ac751efa | 962 | console_unlock(); |
b53cde35 DB |
963 | |
964 | return retval; | |
965 | } | |
966 | ||
967 | static int tmiofb_resume(struct platform_device *dev) | |
968 | { | |
969 | struct fb_info *info = platform_get_drvdata(dev); | |
944dc035 | 970 | const struct mfd_cell *cell = mfd_get_cell(dev); |
a9672c4b | 971 | int retval = 0; |
b53cde35 | 972 | |
ac751efa | 973 | console_lock(); |
b53cde35 DB |
974 | |
975 | if (cell->resume) { | |
976 | retval = cell->resume(dev); | |
977 | if (retval) | |
978 | goto out; | |
979 | } | |
980 | ||
981 | tmiofb_irq(-1, info); | |
982 | ||
983 | tmiofb_hw_init(dev); | |
984 | ||
985 | tmiofb_hw_mode(dev); | |
986 | ||
987 | fb_set_suspend(info, 0); | |
988 | out: | |
ac751efa | 989 | console_unlock(); |
b53cde35 DB |
990 | return retval; |
991 | } | |
992 | #else | |
993 | #define tmiofb_suspend NULL | |
994 | #define tmiofb_resume NULL | |
995 | #endif | |
996 | ||
997 | static struct platform_driver tmiofb_driver = { | |
998 | .driver.name = "tmio-fb", | |
999 | .driver.owner = THIS_MODULE, | |
1000 | .probe = tmiofb_probe, | |
1001 | .remove = __devexit_p(tmiofb_remove), | |
1002 | .suspend = tmiofb_suspend, | |
1003 | .resume = tmiofb_resume, | |
1004 | }; | |
1005 | ||
1006 | /*--------------------------------------------------------------------------*/ | |
1007 | ||
1008 | #ifndef MODULE | |
1009 | static void __init tmiofb_setup(char *options) | |
1010 | { | |
1011 | char *this_opt; | |
1012 | ||
1013 | if (!options || !*options) | |
1014 | return; | |
1015 | ||
1016 | while ((this_opt = strsep(&options, ",")) != NULL) { | |
1017 | if (!*this_opt) | |
1018 | continue; | |
1019 | /* | |
1020 | * FIXME | |
1021 | */ | |
1022 | } | |
1023 | } | |
1024 | #endif | |
1025 | ||
1026 | static int __init tmiofb_init(void) | |
1027 | { | |
1028 | #ifndef MODULE | |
1029 | char *option = NULL; | |
1030 | ||
1031 | if (fb_get_options("tmiofb", &option)) | |
1032 | return -ENODEV; | |
1033 | tmiofb_setup(option); | |
1034 | #endif | |
1035 | return platform_driver_register(&tmiofb_driver); | |
1036 | } | |
1037 | ||
1038 | static void __exit tmiofb_cleanup(void) | |
1039 | { | |
1040 | platform_driver_unregister(&tmiofb_driver); | |
1041 | } | |
1042 | ||
1043 | module_init(tmiofb_init); | |
1044 | module_exit(tmiofb_cleanup); | |
1045 | ||
1046 | MODULE_DESCRIPTION("TMIO framebuffer driver"); | |
1047 | MODULE_AUTHOR("Chris Humbert, Dirk Opfer, Dmitry Baryshkov"); | |
1048 | MODULE_LICENSE("GPL"); |