diff options
Diffstat (limited to 'drivers/video/fbdev/tmiofb.c')
-rw-r--r-- | drivers/video/fbdev/tmiofb.c | 1048 |
1 files changed, 1048 insertions, 0 deletions
diff --git a/drivers/video/fbdev/tmiofb.c b/drivers/video/fbdev/tmiofb.c new file mode 100644 index 000000000000..7fb4e321a431 --- /dev/null +++ b/drivers/video/fbdev/tmiofb.c | |||
@@ -0,0 +1,1048 @@ | |||
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> | ||
28 | /* Why should fb driver call console functions? because console_lock() */ | ||
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; | ||
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 | |||
225 | tmio_iowrite16(bbisc, par->lcr + LCR_BBISC); | ||
226 | |||
227 | #ifdef CONFIG_FB_TMIO_ACCELL | ||
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 | |||
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 | { | ||
253 | struct tmio_fb_data *data = dev_get_platdata(&dev->dev); | ||
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 | { | ||
270 | const struct mfd_cell *cell = mfd_get_cell(dev); | ||
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 | { | ||
314 | struct tmio_fb_data *data = dev_get_platdata(&dev->dev); | ||
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 | /* | ||
362 | * This code can be called with interrupts disabled. | ||
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 | { | ||
560 | struct tmio_fb_data *data = dev_get_platdata(info->device); | ||
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; | ||
580 | struct tmio_fb_data *data = dev_get_platdata(info->device); | ||
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 tmiofb_probe(struct platform_device *dev) | ||
679 | { | ||
680 | const struct mfd_cell *cell = mfd_get_cell(dev); | ||
681 | struct tmio_fb_data *data = dev_get_platdata(&dev->dev); | ||
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 | if (ccr == NULL || lcr == NULL || vram == NULL || irq < 0) { | ||
698 | dev_err(&dev->dev, "missing resources\n"); | ||
699 | return -EINVAL; | ||
700 | } | ||
701 | |||
702 | info = framebuffer_alloc(sizeof(struct tmiofb_par), &dev->dev); | ||
703 | |||
704 | if (!info) | ||
705 | return -ENOMEM; | ||
706 | |||
707 | par = info->par; | ||
708 | |||
709 | #ifdef CONFIG_FB_TMIO_ACCELL | ||
710 | init_waitqueue_head(&par->wait_acc); | ||
711 | |||
712 | par->use_polling = true; | ||
713 | |||
714 | info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA | ||
715 | | FBINFO_HWACCEL_FILLRECT; | ||
716 | #else | ||
717 | info->flags = FBINFO_DEFAULT; | ||
718 | #endif | ||
719 | |||
720 | info->fbops = &tmiofb_ops; | ||
721 | |||
722 | strcpy(info->fix.id, "tmio-fb"); | ||
723 | info->fix.smem_start = vram->start; | ||
724 | info->fix.smem_len = resource_size(vram); | ||
725 | info->fix.type = FB_TYPE_PACKED_PIXELS; | ||
726 | info->fix.visual = FB_VISUAL_TRUECOLOR; | ||
727 | info->fix.mmio_start = lcr->start; | ||
728 | info->fix.mmio_len = resource_size(lcr); | ||
729 | info->fix.accel = FB_ACCEL_NONE; | ||
730 | info->screen_size = info->fix.smem_len - (4 * TMIOFB_FIFO_SIZE); | ||
731 | info->pseudo_palette = par->pseudo_palette; | ||
732 | |||
733 | par->ccr = ioremap(ccr->start, resource_size(ccr)); | ||
734 | if (!par->ccr) { | ||
735 | retval = -ENOMEM; | ||
736 | goto err_ioremap_ccr; | ||
737 | } | ||
738 | |||
739 | par->lcr = ioremap(info->fix.mmio_start, info->fix.mmio_len); | ||
740 | if (!par->lcr) { | ||
741 | retval = -ENOMEM; | ||
742 | goto err_ioremap_lcr; | ||
743 | } | ||
744 | |||
745 | info->screen_base = ioremap(info->fix.smem_start, info->fix.smem_len); | ||
746 | if (!info->screen_base) { | ||
747 | retval = -ENOMEM; | ||
748 | goto err_ioremap_vram; | ||
749 | } | ||
750 | |||
751 | retval = request_irq(irq, &tmiofb_irq, 0, | ||
752 | dev_name(&dev->dev), info); | ||
753 | |||
754 | if (retval) | ||
755 | goto err_request_irq; | ||
756 | |||
757 | platform_set_drvdata(dev, info); | ||
758 | |||
759 | retval = fb_find_mode(&info->var, info, mode_option, | ||
760 | data->modes, data->num_modes, | ||
761 | data->modes, 16); | ||
762 | if (!retval) { | ||
763 | retval = -EINVAL; | ||
764 | goto err_find_mode; | ||
765 | } | ||
766 | |||
767 | if (cell->enable) { | ||
768 | retval = cell->enable(dev); | ||
769 | if (retval) | ||
770 | goto err_enable; | ||
771 | } | ||
772 | |||
773 | retval = tmiofb_hw_init(dev); | ||
774 | if (retval) | ||
775 | goto err_hw_init; | ||
776 | |||
777 | fb_videomode_to_modelist(data->modes, data->num_modes, | ||
778 | &info->modelist); | ||
779 | |||
780 | retval = register_framebuffer(info); | ||
781 | if (retval < 0) | ||
782 | goto err_register_framebuffer; | ||
783 | |||
784 | fb_info(info, "%s frame buffer device\n", info->fix.id); | ||
785 | |||
786 | return 0; | ||
787 | |||
788 | err_register_framebuffer: | ||
789 | /*err_set_par:*/ | ||
790 | tmiofb_hw_stop(dev); | ||
791 | err_hw_init: | ||
792 | if (cell->disable) | ||
793 | cell->disable(dev); | ||
794 | err_enable: | ||
795 | err_find_mode: | ||
796 | free_irq(irq, info); | ||
797 | err_request_irq: | ||
798 | iounmap(info->screen_base); | ||
799 | err_ioremap_vram: | ||
800 | iounmap(par->lcr); | ||
801 | err_ioremap_lcr: | ||
802 | iounmap(par->ccr); | ||
803 | err_ioremap_ccr: | ||
804 | framebuffer_release(info); | ||
805 | return retval; | ||
806 | } | ||
807 | |||
808 | static int tmiofb_remove(struct platform_device *dev) | ||
809 | { | ||
810 | const struct mfd_cell *cell = mfd_get_cell(dev); | ||
811 | struct fb_info *info = platform_get_drvdata(dev); | ||
812 | int irq = platform_get_irq(dev, 0); | ||
813 | struct tmiofb_par *par; | ||
814 | |||
815 | if (info) { | ||
816 | par = info->par; | ||
817 | unregister_framebuffer(info); | ||
818 | |||
819 | tmiofb_hw_stop(dev); | ||
820 | |||
821 | if (cell->disable) | ||
822 | cell->disable(dev); | ||
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); | ||
935 | #ifdef CONFIG_FB_TMIO_ACCELL | ||
936 | struct tmiofb_par *par = info->par; | ||
937 | #endif | ||
938 | const struct mfd_cell *cell = mfd_get_cell(dev); | ||
939 | int retval = 0; | ||
940 | |||
941 | console_lock(); | ||
942 | |||
943 | fb_set_suspend(info, 1); | ||
944 | |||
945 | if (info->fbops->fb_sync) | ||
946 | info->fbops->fb_sync(info); | ||
947 | |||
948 | |||
949 | #ifdef CONFIG_FB_TMIO_ACCELL | ||
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; | ||
956 | #endif | ||
957 | tmiofb_hw_stop(dev); | ||
958 | |||
959 | if (cell->suspend) | ||
960 | retval = cell->suspend(dev); | ||
961 | |||
962 | console_unlock(); | ||
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); | ||
970 | const struct mfd_cell *cell = mfd_get_cell(dev); | ||
971 | int retval = 0; | ||
972 | |||
973 | console_lock(); | ||
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: | ||
989 | console_unlock(); | ||
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 = 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"); | ||