diff options
Diffstat (limited to 'drivers/video/fbdev/s1d13xxxfb.c')
-rw-r--r-- | drivers/video/fbdev/s1d13xxxfb.c | 1040 |
1 files changed, 1040 insertions, 0 deletions
diff --git a/drivers/video/fbdev/s1d13xxxfb.c b/drivers/video/fbdev/s1d13xxxfb.c new file mode 100644 index 000000000000..83433cb0dfba --- /dev/null +++ b/drivers/video/fbdev/s1d13xxxfb.c | |||
@@ -0,0 +1,1040 @@ | |||
1 | /* drivers/video/s1d13xxxfb.c | ||
2 | * | ||
3 | * (c) 2004 Simtec Electronics | ||
4 | * (c) 2005 Thibaut VARENE <varenet@parisc-linux.org> | ||
5 | * (c) 2009 Kristoffer Ericson <kristoffer.ericson@gmail.com> | ||
6 | * | ||
7 | * Driver for Epson S1D13xxx series framebuffer chips | ||
8 | * | ||
9 | * Adapted from | ||
10 | * linux/drivers/video/skeletonfb.c | ||
11 | * linux/drivers/video/epson1355fb.c | ||
12 | * linux/drivers/video/epson/s1d13xxxfb.c (2.4 driver by Epson) | ||
13 | * | ||
14 | * TODO: - handle dual screen display (CRT and LCD at the same time). | ||
15 | * - check_var(), mode change, etc. | ||
16 | * - probably not SMP safe :) | ||
17 | * - support all bitblt operations on all cards | ||
18 | * | ||
19 | * This file is subject to the terms and conditions of the GNU General Public | ||
20 | * License. See the file COPYING in the main directory of this archive for | ||
21 | * more details. | ||
22 | */ | ||
23 | |||
24 | #include <linux/module.h> | ||
25 | #include <linux/platform_device.h> | ||
26 | #include <linux/delay.h> | ||
27 | #include <linux/types.h> | ||
28 | #include <linux/errno.h> | ||
29 | #include <linux/mm.h> | ||
30 | #include <linux/mman.h> | ||
31 | #include <linux/fb.h> | ||
32 | #include <linux/spinlock_types.h> | ||
33 | #include <linux/spinlock.h> | ||
34 | #include <linux/slab.h> | ||
35 | |||
36 | #include <asm/io.h> | ||
37 | |||
38 | #include <video/s1d13xxxfb.h> | ||
39 | |||
40 | #define PFX "s1d13xxxfb: " | ||
41 | #define BLIT "s1d13xxxfb_bitblt: " | ||
42 | |||
43 | /* | ||
44 | * set this to enable debugging on general functions | ||
45 | */ | ||
46 | #if 0 | ||
47 | #define dbg(fmt, args...) do { printk(KERN_INFO fmt, ## args); } while(0) | ||
48 | #else | ||
49 | #define dbg(fmt, args...) do { } while (0) | ||
50 | #endif | ||
51 | |||
52 | /* | ||
53 | * set this to enable debugging on 2D acceleration | ||
54 | */ | ||
55 | #if 0 | ||
56 | #define dbg_blit(fmt, args...) do { printk(KERN_INFO BLIT fmt, ## args); } while (0) | ||
57 | #else | ||
58 | #define dbg_blit(fmt, args...) do { } while (0) | ||
59 | #endif | ||
60 | |||
61 | /* | ||
62 | * we make sure only one bitblt operation is running | ||
63 | */ | ||
64 | static DEFINE_SPINLOCK(s1d13xxxfb_bitblt_lock); | ||
65 | |||
66 | /* | ||
67 | * list of card production ids | ||
68 | */ | ||
69 | static const int s1d13xxxfb_prod_ids[] = { | ||
70 | S1D13505_PROD_ID, | ||
71 | S1D13506_PROD_ID, | ||
72 | S1D13806_PROD_ID, | ||
73 | }; | ||
74 | |||
75 | /* | ||
76 | * List of card strings | ||
77 | */ | ||
78 | static const char *s1d13xxxfb_prod_names[] = { | ||
79 | "S1D13505", | ||
80 | "S1D13506", | ||
81 | "S1D13806", | ||
82 | }; | ||
83 | |||
84 | /* | ||
85 | * here we define the default struct fb_fix_screeninfo | ||
86 | */ | ||
87 | static struct fb_fix_screeninfo s1d13xxxfb_fix = { | ||
88 | .id = S1D_FBID, | ||
89 | .type = FB_TYPE_PACKED_PIXELS, | ||
90 | .visual = FB_VISUAL_PSEUDOCOLOR, | ||
91 | .xpanstep = 0, | ||
92 | .ypanstep = 1, | ||
93 | .ywrapstep = 0, | ||
94 | .accel = FB_ACCEL_NONE, | ||
95 | }; | ||
96 | |||
97 | static inline u8 | ||
98 | s1d13xxxfb_readreg(struct s1d13xxxfb_par *par, u16 regno) | ||
99 | { | ||
100 | #if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_OPSPUT) || defined(CONFIG_PLAT_MAPPI3) | ||
101 | regno=((regno & 1) ? (regno & ~1L) : (regno + 1)); | ||
102 | #endif | ||
103 | return readb(par->regs + regno); | ||
104 | } | ||
105 | |||
106 | static inline void | ||
107 | s1d13xxxfb_writereg(struct s1d13xxxfb_par *par, u16 regno, u8 value) | ||
108 | { | ||
109 | #if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_OPSPUT) || defined(CONFIG_PLAT_MAPPI3) | ||
110 | regno=((regno & 1) ? (regno & ~1L) : (regno + 1)); | ||
111 | #endif | ||
112 | writeb(value, par->regs + regno); | ||
113 | } | ||
114 | |||
115 | static inline void | ||
116 | s1d13xxxfb_runinit(struct s1d13xxxfb_par *par, | ||
117 | const struct s1d13xxxfb_regval *initregs, | ||
118 | const unsigned int size) | ||
119 | { | ||
120 | int i; | ||
121 | |||
122 | for (i = 0; i < size; i++) { | ||
123 | if ((initregs[i].addr == S1DREG_DELAYOFF) || | ||
124 | (initregs[i].addr == S1DREG_DELAYON)) | ||
125 | mdelay((int)initregs[i].value); | ||
126 | else { | ||
127 | s1d13xxxfb_writereg(par, initregs[i].addr, initregs[i].value); | ||
128 | } | ||
129 | } | ||
130 | |||
131 | /* make sure the hardware can cope with us */ | ||
132 | mdelay(1); | ||
133 | } | ||
134 | |||
135 | static inline void | ||
136 | lcd_enable(struct s1d13xxxfb_par *par, int enable) | ||
137 | { | ||
138 | u8 mode = s1d13xxxfb_readreg(par, S1DREG_COM_DISP_MODE); | ||
139 | |||
140 | if (enable) | ||
141 | mode |= 0x01; | ||
142 | else | ||
143 | mode &= ~0x01; | ||
144 | |||
145 | s1d13xxxfb_writereg(par, S1DREG_COM_DISP_MODE, mode); | ||
146 | } | ||
147 | |||
148 | static inline void | ||
149 | crt_enable(struct s1d13xxxfb_par *par, int enable) | ||
150 | { | ||
151 | u8 mode = s1d13xxxfb_readreg(par, S1DREG_COM_DISP_MODE); | ||
152 | |||
153 | if (enable) | ||
154 | mode |= 0x02; | ||
155 | else | ||
156 | mode &= ~0x02; | ||
157 | |||
158 | s1d13xxxfb_writereg(par, S1DREG_COM_DISP_MODE, mode); | ||
159 | } | ||
160 | |||
161 | |||
162 | /************************************************************* | ||
163 | framebuffer control functions | ||
164 | *************************************************************/ | ||
165 | static inline void | ||
166 | s1d13xxxfb_setup_pseudocolour(struct fb_info *info) | ||
167 | { | ||
168 | info->fix.visual = FB_VISUAL_PSEUDOCOLOR; | ||
169 | |||
170 | info->var.red.length = 4; | ||
171 | info->var.green.length = 4; | ||
172 | info->var.blue.length = 4; | ||
173 | } | ||
174 | |||
175 | static inline void | ||
176 | s1d13xxxfb_setup_truecolour(struct fb_info *info) | ||
177 | { | ||
178 | info->fix.visual = FB_VISUAL_TRUECOLOR; | ||
179 | info->var.bits_per_pixel = 16; | ||
180 | |||
181 | info->var.red.length = 5; | ||
182 | info->var.red.offset = 11; | ||
183 | |||
184 | info->var.green.length = 6; | ||
185 | info->var.green.offset = 5; | ||
186 | |||
187 | info->var.blue.length = 5; | ||
188 | info->var.blue.offset = 0; | ||
189 | } | ||
190 | |||
191 | /** | ||
192 | * s1d13xxxfb_set_par - Alters the hardware state. | ||
193 | * @info: frame buffer structure | ||
194 | * | ||
195 | * Using the fb_var_screeninfo in fb_info we set the depth of the | ||
196 | * framebuffer. This function alters the par AND the | ||
197 | * fb_fix_screeninfo stored in fb_info. It doesn't not alter var in | ||
198 | * fb_info since we are using that data. This means we depend on the | ||
199 | * data in var inside fb_info to be supported by the hardware. | ||
200 | * xxxfb_check_var is always called before xxxfb_set_par to ensure this. | ||
201 | * | ||
202 | * XXX TODO: write proper s1d13xxxfb_check_var(), without which that | ||
203 | * function is quite useless. | ||
204 | */ | ||
205 | static int | ||
206 | s1d13xxxfb_set_par(struct fb_info *info) | ||
207 | { | ||
208 | struct s1d13xxxfb_par *s1dfb = info->par; | ||
209 | unsigned int val; | ||
210 | |||
211 | dbg("s1d13xxxfb_set_par: bpp=%d\n", info->var.bits_per_pixel); | ||
212 | |||
213 | if ((s1dfb->display & 0x01)) /* LCD */ | ||
214 | val = s1d13xxxfb_readreg(s1dfb, S1DREG_LCD_DISP_MODE); /* read colour control */ | ||
215 | else /* CRT */ | ||
216 | val = s1d13xxxfb_readreg(s1dfb, S1DREG_CRT_DISP_MODE); /* read colour control */ | ||
217 | |||
218 | val &= ~0x07; | ||
219 | |||
220 | switch (info->var.bits_per_pixel) { | ||
221 | case 4: | ||
222 | dbg("pseudo colour 4\n"); | ||
223 | s1d13xxxfb_setup_pseudocolour(info); | ||
224 | val |= 2; | ||
225 | break; | ||
226 | case 8: | ||
227 | dbg("pseudo colour 8\n"); | ||
228 | s1d13xxxfb_setup_pseudocolour(info); | ||
229 | val |= 3; | ||
230 | break; | ||
231 | case 16: | ||
232 | dbg("true colour\n"); | ||
233 | s1d13xxxfb_setup_truecolour(info); | ||
234 | val |= 5; | ||
235 | break; | ||
236 | |||
237 | default: | ||
238 | dbg("bpp not supported!\n"); | ||
239 | return -EINVAL; | ||
240 | } | ||
241 | |||
242 | dbg("writing %02x to display mode register\n", val); | ||
243 | |||
244 | if ((s1dfb->display & 0x01)) /* LCD */ | ||
245 | s1d13xxxfb_writereg(s1dfb, S1DREG_LCD_DISP_MODE, val); | ||
246 | else /* CRT */ | ||
247 | s1d13xxxfb_writereg(s1dfb, S1DREG_CRT_DISP_MODE, val); | ||
248 | |||
249 | info->fix.line_length = info->var.xres * info->var.bits_per_pixel; | ||
250 | info->fix.line_length /= 8; | ||
251 | |||
252 | dbg("setting line_length to %d\n", info->fix.line_length); | ||
253 | |||
254 | dbg("done setup\n"); | ||
255 | |||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | /** | ||
260 | * s1d13xxxfb_setcolreg - sets a color register. | ||
261 | * @regno: Which register in the CLUT we are programming | ||
262 | * @red: The red value which can be up to 16 bits wide | ||
263 | * @green: The green value which can be up to 16 bits wide | ||
264 | * @blue: The blue value which can be up to 16 bits wide. | ||
265 | * @transp: If supported the alpha value which can be up to 16 bits wide. | ||
266 | * @info: frame buffer info structure | ||
267 | * | ||
268 | * Returns negative errno on error, or zero on success. | ||
269 | */ | ||
270 | static int | ||
271 | s1d13xxxfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, | ||
272 | u_int transp, struct fb_info *info) | ||
273 | { | ||
274 | struct s1d13xxxfb_par *s1dfb = info->par; | ||
275 | unsigned int pseudo_val; | ||
276 | |||
277 | if (regno >= S1D_PALETTE_SIZE) | ||
278 | return -EINVAL; | ||
279 | |||
280 | dbg("s1d13xxxfb_setcolreg: %d: rgb=%d,%d,%d, tr=%d\n", | ||
281 | regno, red, green, blue, transp); | ||
282 | |||
283 | if (info->var.grayscale) | ||
284 | red = green = blue = (19595*red + 38470*green + 7471*blue) >> 16; | ||
285 | |||
286 | switch (info->fix.visual) { | ||
287 | case FB_VISUAL_TRUECOLOR: | ||
288 | if (regno >= 16) | ||
289 | return -EINVAL; | ||
290 | |||
291 | /* deal with creating pseudo-palette entries */ | ||
292 | |||
293 | pseudo_val = (red >> 11) << info->var.red.offset; | ||
294 | pseudo_val |= (green >> 10) << info->var.green.offset; | ||
295 | pseudo_val |= (blue >> 11) << info->var.blue.offset; | ||
296 | |||
297 | dbg("s1d13xxxfb_setcolreg: pseudo %d, val %08x\n", | ||
298 | regno, pseudo_val); | ||
299 | |||
300 | #if defined(CONFIG_PLAT_MAPPI) | ||
301 | ((u32 *)info->pseudo_palette)[regno] = cpu_to_le16(pseudo_val); | ||
302 | #else | ||
303 | ((u32 *)info->pseudo_palette)[regno] = pseudo_val; | ||
304 | #endif | ||
305 | |||
306 | break; | ||
307 | case FB_VISUAL_PSEUDOCOLOR: | ||
308 | s1d13xxxfb_writereg(s1dfb, S1DREG_LKUP_ADDR, regno); | ||
309 | s1d13xxxfb_writereg(s1dfb, S1DREG_LKUP_DATA, red); | ||
310 | s1d13xxxfb_writereg(s1dfb, S1DREG_LKUP_DATA, green); | ||
311 | s1d13xxxfb_writereg(s1dfb, S1DREG_LKUP_DATA, blue); | ||
312 | |||
313 | break; | ||
314 | default: | ||
315 | return -ENOSYS; | ||
316 | } | ||
317 | |||
318 | dbg("s1d13xxxfb_setcolreg: done\n"); | ||
319 | |||
320 | return 0; | ||
321 | } | ||
322 | |||
323 | /** | ||
324 | * s1d13xxxfb_blank - blanks the display. | ||
325 | * @blank_mode: the blank mode we want. | ||
326 | * @info: frame buffer structure that represents a single frame buffer | ||
327 | * | ||
328 | * Blank the screen if blank_mode != 0, else unblank. Return 0 if | ||
329 | * blanking succeeded, != 0 if un-/blanking failed due to e.g. a | ||
330 | * video mode which doesn't support it. Implements VESA suspend | ||
331 | * and powerdown modes on hardware that supports disabling hsync/vsync: | ||
332 | * blank_mode == 2: suspend vsync | ||
333 | * blank_mode == 3: suspend hsync | ||
334 | * blank_mode == 4: powerdown | ||
335 | * | ||
336 | * Returns negative errno on error, or zero on success. | ||
337 | */ | ||
338 | static int | ||
339 | s1d13xxxfb_blank(int blank_mode, struct fb_info *info) | ||
340 | { | ||
341 | struct s1d13xxxfb_par *par = info->par; | ||
342 | |||
343 | dbg("s1d13xxxfb_blank: blank=%d, info=%p\n", blank_mode, info); | ||
344 | |||
345 | switch (blank_mode) { | ||
346 | case FB_BLANK_UNBLANK: | ||
347 | case FB_BLANK_NORMAL: | ||
348 | if ((par->display & 0x01) != 0) | ||
349 | lcd_enable(par, 1); | ||
350 | if ((par->display & 0x02) != 0) | ||
351 | crt_enable(par, 1); | ||
352 | break; | ||
353 | case FB_BLANK_VSYNC_SUSPEND: | ||
354 | case FB_BLANK_HSYNC_SUSPEND: | ||
355 | break; | ||
356 | case FB_BLANK_POWERDOWN: | ||
357 | lcd_enable(par, 0); | ||
358 | crt_enable(par, 0); | ||
359 | break; | ||
360 | default: | ||
361 | return -EINVAL; | ||
362 | } | ||
363 | |||
364 | /* let fbcon do a soft blank for us */ | ||
365 | return ((blank_mode == FB_BLANK_NORMAL) ? 1 : 0); | ||
366 | } | ||
367 | |||
368 | /** | ||
369 | * s1d13xxxfb_pan_display - Pans the display. | ||
370 | * @var: frame buffer variable screen structure | ||
371 | * @info: frame buffer structure that represents a single frame buffer | ||
372 | * | ||
373 | * Pan (or wrap, depending on the `vmode' field) the display using the | ||
374 | * `yoffset' field of the `var' structure (`xoffset' not yet supported). | ||
375 | * If the values don't fit, return -EINVAL. | ||
376 | * | ||
377 | * Returns negative errno on error, or zero on success. | ||
378 | */ | ||
379 | static int | ||
380 | s1d13xxxfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) | ||
381 | { | ||
382 | struct s1d13xxxfb_par *par = info->par; | ||
383 | u32 start; | ||
384 | |||
385 | if (var->xoffset != 0) /* not yet ... */ | ||
386 | return -EINVAL; | ||
387 | |||
388 | if (var->yoffset + info->var.yres > info->var.yres_virtual) | ||
389 | return -EINVAL; | ||
390 | |||
391 | start = (info->fix.line_length >> 1) * var->yoffset; | ||
392 | |||
393 | if ((par->display & 0x01)) { | ||
394 | /* LCD */ | ||
395 | s1d13xxxfb_writereg(par, S1DREG_LCD_DISP_START0, (start & 0xff)); | ||
396 | s1d13xxxfb_writereg(par, S1DREG_LCD_DISP_START1, ((start >> 8) & 0xff)); | ||
397 | s1d13xxxfb_writereg(par, S1DREG_LCD_DISP_START2, ((start >> 16) & 0x0f)); | ||
398 | } else { | ||
399 | /* CRT */ | ||
400 | s1d13xxxfb_writereg(par, S1DREG_CRT_DISP_START0, (start & 0xff)); | ||
401 | s1d13xxxfb_writereg(par, S1DREG_CRT_DISP_START1, ((start >> 8) & 0xff)); | ||
402 | s1d13xxxfb_writereg(par, S1DREG_CRT_DISP_START2, ((start >> 16) & 0x0f)); | ||
403 | } | ||
404 | |||
405 | return 0; | ||
406 | } | ||
407 | |||
408 | /************************************************************ | ||
409 | functions to handle bitblt acceleration | ||
410 | ************************************************************/ | ||
411 | |||
412 | /** | ||
413 | * bltbit_wait_bitclear - waits for change in register value | ||
414 | * @info : frambuffer structure | ||
415 | * @bit : value currently in register | ||
416 | * @timeout : ... | ||
417 | * | ||
418 | * waits until value changes FROM bit | ||
419 | * | ||
420 | */ | ||
421 | static u8 | ||
422 | bltbit_wait_bitclear(struct fb_info *info, u8 bit, int timeout) | ||
423 | { | ||
424 | while (s1d13xxxfb_readreg(info->par, S1DREG_BBLT_CTL0) & bit) { | ||
425 | udelay(10); | ||
426 | if (!--timeout) { | ||
427 | dbg_blit("wait_bitclear timeout\n"); | ||
428 | break; | ||
429 | } | ||
430 | } | ||
431 | |||
432 | return timeout; | ||
433 | } | ||
434 | |||
435 | /* | ||
436 | * s1d13xxxfb_bitblt_copyarea - accelerated copyarea function | ||
437 | * @info : framebuffer structure | ||
438 | * @area : fb_copyarea structure | ||
439 | * | ||
440 | * supports (atleast) S1D13506 | ||
441 | * | ||
442 | */ | ||
443 | static void | ||
444 | s1d13xxxfb_bitblt_copyarea(struct fb_info *info, const struct fb_copyarea *area) | ||
445 | { | ||
446 | u32 dst, src; | ||
447 | u32 stride; | ||
448 | u16 reverse = 0; | ||
449 | u16 sx = area->sx, sy = area->sy; | ||
450 | u16 dx = area->dx, dy = area->dy; | ||
451 | u16 width = area->width, height = area->height; | ||
452 | u16 bpp; | ||
453 | |||
454 | spin_lock(&s1d13xxxfb_bitblt_lock); | ||
455 | |||
456 | /* bytes per xres line */ | ||
457 | bpp = (info->var.bits_per_pixel >> 3); | ||
458 | stride = bpp * info->var.xres; | ||
459 | |||
460 | /* reverse, calculate the last pixel in rectangle */ | ||
461 | if ((dy > sy) || ((dy == sy) && (dx >= sx))) { | ||
462 | dst = (((dy + height - 1) * stride) + (bpp * (dx + width - 1))); | ||
463 | src = (((sy + height - 1) * stride) + (bpp * (sx + width - 1))); | ||
464 | reverse = 1; | ||
465 | /* not reverse, calculate the first pixel in rectangle */ | ||
466 | } else { /* (y * xres) + (bpp * x) */ | ||
467 | dst = (dy * stride) + (bpp * dx); | ||
468 | src = (sy * stride) + (bpp * sx); | ||
469 | } | ||
470 | |||
471 | /* set source address */ | ||
472 | s1d13xxxfb_writereg(info->par, S1DREG_BBLT_SRC_START0, (src & 0xff)); | ||
473 | s1d13xxxfb_writereg(info->par, S1DREG_BBLT_SRC_START1, (src >> 8) & 0x00ff); | ||
474 | s1d13xxxfb_writereg(info->par, S1DREG_BBLT_SRC_START2, (src >> 16) & 0x00ff); | ||
475 | |||
476 | /* set destination address */ | ||
477 | s1d13xxxfb_writereg(info->par, S1DREG_BBLT_DST_START0, (dst & 0xff)); | ||
478 | s1d13xxxfb_writereg(info->par, S1DREG_BBLT_DST_START1, (dst >> 8) & 0x00ff); | ||
479 | s1d13xxxfb_writereg(info->par, S1DREG_BBLT_DST_START2, (dst >> 16) & 0x00ff); | ||
480 | |||
481 | /* program height and width */ | ||
482 | s1d13xxxfb_writereg(info->par, S1DREG_BBLT_WIDTH0, (width & 0xff) - 1); | ||
483 | s1d13xxxfb_writereg(info->par, S1DREG_BBLT_WIDTH1, (width >> 8)); | ||
484 | |||
485 | s1d13xxxfb_writereg(info->par, S1DREG_BBLT_HEIGHT0, (height & 0xff) - 1); | ||
486 | s1d13xxxfb_writereg(info->par, S1DREG_BBLT_HEIGHT1, (height >> 8)); | ||
487 | |||
488 | /* negative direction ROP */ | ||
489 | if (reverse == 1) { | ||
490 | dbg_blit("(copyarea) negative rop\n"); | ||
491 | s1d13xxxfb_writereg(info->par, S1DREG_BBLT_OP, 0x03); | ||
492 | } else /* positive direction ROP */ { | ||
493 | s1d13xxxfb_writereg(info->par, S1DREG_BBLT_OP, 0x02); | ||
494 | dbg_blit("(copyarea) positive rop\n"); | ||
495 | } | ||
496 | |||
497 | /* set for rectangel mode and not linear */ | ||
498 | s1d13xxxfb_writereg(info->par, S1DREG_BBLT_CTL0, 0x0); | ||
499 | |||
500 | /* setup the bpp 1 = 16bpp, 0 = 8bpp*/ | ||
501 | s1d13xxxfb_writereg(info->par, S1DREG_BBLT_CTL1, (bpp >> 1)); | ||
502 | |||
503 | /* set words per xres */ | ||
504 | s1d13xxxfb_writereg(info->par, S1DREG_BBLT_MEM_OFF0, (stride >> 1) & 0xff); | ||
505 | s1d13xxxfb_writereg(info->par, S1DREG_BBLT_MEM_OFF1, (stride >> 9)); | ||
506 | |||
507 | dbg_blit("(copyarea) dx=%d, dy=%d\n", dx, dy); | ||
508 | dbg_blit("(copyarea) sx=%d, sy=%d\n", sx, sy); | ||
509 | dbg_blit("(copyarea) width=%d, height=%d\n", width - 1, height - 1); | ||
510 | dbg_blit("(copyarea) stride=%d\n", stride); | ||
511 | dbg_blit("(copyarea) bpp=%d=0x0%d, mem_offset1=%d, mem_offset2=%d\n", bpp, (bpp >> 1), | ||
512 | (stride >> 1) & 0xff, stride >> 9); | ||
513 | |||
514 | s1d13xxxfb_writereg(info->par, S1DREG_BBLT_CC_EXP, 0x0c); | ||
515 | |||
516 | /* initialize the engine */ | ||
517 | s1d13xxxfb_writereg(info->par, S1DREG_BBLT_CTL0, 0x80); | ||
518 | |||
519 | /* wait to complete */ | ||
520 | bltbit_wait_bitclear(info, 0x80, 8000); | ||
521 | |||
522 | spin_unlock(&s1d13xxxfb_bitblt_lock); | ||
523 | } | ||
524 | |||
525 | /** | ||
526 | * | ||
527 | * s1d13xxxfb_bitblt_solidfill - accelerated solidfill function | ||
528 | * @info : framebuffer structure | ||
529 | * @rect : fb_fillrect structure | ||
530 | * | ||
531 | * supports (atleast 13506) | ||
532 | * | ||
533 | **/ | ||
534 | static void | ||
535 | s1d13xxxfb_bitblt_solidfill(struct fb_info *info, const struct fb_fillrect *rect) | ||
536 | { | ||
537 | u32 screen_stride, dest; | ||
538 | u32 fg; | ||
539 | u16 bpp = (info->var.bits_per_pixel >> 3); | ||
540 | |||
541 | /* grab spinlock */ | ||
542 | spin_lock(&s1d13xxxfb_bitblt_lock); | ||
543 | |||
544 | /* bytes per x width */ | ||
545 | screen_stride = (bpp * info->var.xres); | ||
546 | |||
547 | /* bytes to starting point */ | ||
548 | dest = ((rect->dy * screen_stride) + (bpp * rect->dx)); | ||
549 | |||
550 | dbg_blit("(solidfill) dx=%d, dy=%d, stride=%d, dest=%d\n" | ||
551 | "(solidfill) : rect_width=%d, rect_height=%d\n", | ||
552 | rect->dx, rect->dy, screen_stride, dest, | ||
553 | rect->width - 1, rect->height - 1); | ||
554 | |||
555 | dbg_blit("(solidfill) : xres=%d, yres=%d, bpp=%d\n", | ||
556 | info->var.xres, info->var.yres, | ||
557 | info->var.bits_per_pixel); | ||
558 | dbg_blit("(solidfill) : rop=%d\n", rect->rop); | ||
559 | |||
560 | /* We split the destination into the three registers */ | ||
561 | s1d13xxxfb_writereg(info->par, S1DREG_BBLT_DST_START0, (dest & 0x00ff)); | ||
562 | s1d13xxxfb_writereg(info->par, S1DREG_BBLT_DST_START1, ((dest >> 8) & 0x00ff)); | ||
563 | s1d13xxxfb_writereg(info->par, S1DREG_BBLT_DST_START2, ((dest >> 16) & 0x00ff)); | ||
564 | |||
565 | /* give information regarding rectangel width */ | ||
566 | s1d13xxxfb_writereg(info->par, S1DREG_BBLT_WIDTH0, ((rect->width) & 0x00ff) - 1); | ||
567 | s1d13xxxfb_writereg(info->par, S1DREG_BBLT_WIDTH1, (rect->width >> 8)); | ||
568 | |||
569 | /* give information regarding rectangel height */ | ||
570 | s1d13xxxfb_writereg(info->par, S1DREG_BBLT_HEIGHT0, ((rect->height) & 0x00ff) - 1); | ||
571 | s1d13xxxfb_writereg(info->par, S1DREG_BBLT_HEIGHT1, (rect->height >> 8)); | ||
572 | |||
573 | if (info->fix.visual == FB_VISUAL_TRUECOLOR || | ||
574 | info->fix.visual == FB_VISUAL_DIRECTCOLOR) { | ||
575 | fg = ((u32 *)info->pseudo_palette)[rect->color]; | ||
576 | dbg_blit("(solidfill) truecolor/directcolor\n"); | ||
577 | dbg_blit("(solidfill) pseudo_palette[%d] = %d\n", rect->color, fg); | ||
578 | } else { | ||
579 | fg = rect->color; | ||
580 | dbg_blit("(solidfill) color = %d\n", rect->color); | ||
581 | } | ||
582 | |||
583 | /* set foreground color */ | ||
584 | s1d13xxxfb_writereg(info->par, S1DREG_BBLT_FGC0, (fg & 0xff)); | ||
585 | s1d13xxxfb_writereg(info->par, S1DREG_BBLT_FGC1, (fg >> 8) & 0xff); | ||
586 | |||
587 | /* set rectangual region of memory (rectangle and not linear) */ | ||
588 | s1d13xxxfb_writereg(info->par, S1DREG_BBLT_CTL0, 0x0); | ||
589 | |||
590 | /* set operation mode SOLID_FILL */ | ||
591 | s1d13xxxfb_writereg(info->par, S1DREG_BBLT_OP, BBLT_SOLID_FILL); | ||
592 | |||
593 | /* set bits per pixel (1 = 16bpp, 0 = 8bpp) */ | ||
594 | s1d13xxxfb_writereg(info->par, S1DREG_BBLT_CTL1, (info->var.bits_per_pixel >> 4)); | ||
595 | |||
596 | /* set the memory offset for the bblt in word sizes */ | ||
597 | s1d13xxxfb_writereg(info->par, S1DREG_BBLT_MEM_OFF0, (screen_stride >> 1) & 0x00ff); | ||
598 | s1d13xxxfb_writereg(info->par, S1DREG_BBLT_MEM_OFF1, (screen_stride >> 9)); | ||
599 | |||
600 | /* and away we go.... */ | ||
601 | s1d13xxxfb_writereg(info->par, S1DREG_BBLT_CTL0, 0x80); | ||
602 | |||
603 | /* wait until its done */ | ||
604 | bltbit_wait_bitclear(info, 0x80, 8000); | ||
605 | |||
606 | /* let others play */ | ||
607 | spin_unlock(&s1d13xxxfb_bitblt_lock); | ||
608 | } | ||
609 | |||
610 | /* framebuffer information structures */ | ||
611 | static struct fb_ops s1d13xxxfb_fbops = { | ||
612 | .owner = THIS_MODULE, | ||
613 | .fb_set_par = s1d13xxxfb_set_par, | ||
614 | .fb_setcolreg = s1d13xxxfb_setcolreg, | ||
615 | .fb_blank = s1d13xxxfb_blank, | ||
616 | |||
617 | .fb_pan_display = s1d13xxxfb_pan_display, | ||
618 | |||
619 | /* gets replaced at chip detection time */ | ||
620 | .fb_fillrect = cfb_fillrect, | ||
621 | .fb_copyarea = cfb_copyarea, | ||
622 | .fb_imageblit = cfb_imageblit, | ||
623 | }; | ||
624 | |||
625 | static int s1d13xxxfb_width_tab[2][4] = { | ||
626 | {4, 8, 16, -1}, | ||
627 | {9, 12, 18, -1}, | ||
628 | }; | ||
629 | |||
630 | /** | ||
631 | * s1d13xxxfb_fetch_hw_state - Configure the framebuffer according to | ||
632 | * hardware setup. | ||
633 | * @info: frame buffer structure | ||
634 | * | ||
635 | * We setup the framebuffer structures according to the current | ||
636 | * hardware setup. On some machines, the BIOS will have filled | ||
637 | * the chip registers with such info, on others, these values will | ||
638 | * have been written in some init procedure. In any case, the | ||
639 | * software values needs to match the hardware ones. This is what | ||
640 | * this function ensures. | ||
641 | * | ||
642 | * Note: some of the hardcoded values here might need some love to | ||
643 | * work on various chips, and might need to no longer be hardcoded. | ||
644 | */ | ||
645 | static void s1d13xxxfb_fetch_hw_state(struct fb_info *info) | ||
646 | { | ||
647 | struct fb_var_screeninfo *var = &info->var; | ||
648 | struct fb_fix_screeninfo *fix = &info->fix; | ||
649 | struct s1d13xxxfb_par *par = info->par; | ||
650 | u8 panel, display; | ||
651 | u16 offset; | ||
652 | u32 xres, yres; | ||
653 | u32 xres_virtual, yres_virtual; | ||
654 | int bpp, lcd_bpp; | ||
655 | int is_color, is_dual, is_tft; | ||
656 | int lcd_enabled, crt_enabled; | ||
657 | |||
658 | fix->type = FB_TYPE_PACKED_PIXELS; | ||
659 | |||
660 | /* general info */ | ||
661 | par->display = s1d13xxxfb_readreg(par, S1DREG_COM_DISP_MODE); | ||
662 | crt_enabled = (par->display & 0x02) != 0; | ||
663 | lcd_enabled = (par->display & 0x01) != 0; | ||
664 | |||
665 | if (lcd_enabled && crt_enabled) | ||
666 | printk(KERN_WARNING PFX "Warning: LCD and CRT detected, using LCD\n"); | ||
667 | |||
668 | if (lcd_enabled) | ||
669 | display = s1d13xxxfb_readreg(par, S1DREG_LCD_DISP_MODE); | ||
670 | else /* CRT */ | ||
671 | display = s1d13xxxfb_readreg(par, S1DREG_CRT_DISP_MODE); | ||
672 | |||
673 | bpp = display & 0x07; | ||
674 | |||
675 | switch (bpp) { | ||
676 | case 2: /* 4 bpp */ | ||
677 | case 3: /* 8 bpp */ | ||
678 | var->bits_per_pixel = 8; | ||
679 | var->red.offset = var->green.offset = var->blue.offset = 0; | ||
680 | var->red.length = var->green.length = var->blue.length = 8; | ||
681 | break; | ||
682 | case 5: /* 16 bpp */ | ||
683 | s1d13xxxfb_setup_truecolour(info); | ||
684 | break; | ||
685 | default: | ||
686 | dbg("bpp: %i\n", bpp); | ||
687 | } | ||
688 | fb_alloc_cmap(&info->cmap, 256, 0); | ||
689 | |||
690 | /* LCD info */ | ||
691 | panel = s1d13xxxfb_readreg(par, S1DREG_PANEL_TYPE); | ||
692 | is_color = (panel & 0x04) != 0; | ||
693 | is_dual = (panel & 0x02) != 0; | ||
694 | is_tft = (panel & 0x01) != 0; | ||
695 | lcd_bpp = s1d13xxxfb_width_tab[is_tft][(panel >> 4) & 3]; | ||
696 | |||
697 | if (lcd_enabled) { | ||
698 | xres = (s1d13xxxfb_readreg(par, S1DREG_LCD_DISP_HWIDTH) + 1) * 8; | ||
699 | yres = (s1d13xxxfb_readreg(par, S1DREG_LCD_DISP_VHEIGHT0) + | ||
700 | ((s1d13xxxfb_readreg(par, S1DREG_LCD_DISP_VHEIGHT1) & 0x03) << 8) + 1); | ||
701 | |||
702 | offset = (s1d13xxxfb_readreg(par, S1DREG_LCD_MEM_OFF0) + | ||
703 | ((s1d13xxxfb_readreg(par, S1DREG_LCD_MEM_OFF1) & 0x7) << 8)); | ||
704 | } else { /* crt */ | ||
705 | xres = (s1d13xxxfb_readreg(par, S1DREG_CRT_DISP_HWIDTH) + 1) * 8; | ||
706 | yres = (s1d13xxxfb_readreg(par, S1DREG_CRT_DISP_VHEIGHT0) + | ||
707 | ((s1d13xxxfb_readreg(par, S1DREG_CRT_DISP_VHEIGHT1) & 0x03) << 8) + 1); | ||
708 | |||
709 | offset = (s1d13xxxfb_readreg(par, S1DREG_CRT_MEM_OFF0) + | ||
710 | ((s1d13xxxfb_readreg(par, S1DREG_CRT_MEM_OFF1) & 0x7) << 8)); | ||
711 | } | ||
712 | xres_virtual = offset * 16 / var->bits_per_pixel; | ||
713 | yres_virtual = fix->smem_len / (offset * 2); | ||
714 | |||
715 | var->xres = xres; | ||
716 | var->yres = yres; | ||
717 | var->xres_virtual = xres_virtual; | ||
718 | var->yres_virtual = yres_virtual; | ||
719 | var->xoffset = var->yoffset = 0; | ||
720 | |||
721 | fix->line_length = offset * 2; | ||
722 | |||
723 | var->grayscale = !is_color; | ||
724 | |||
725 | var->activate = FB_ACTIVATE_NOW; | ||
726 | |||
727 | dbg(PFX "bpp=%d, lcd_bpp=%d, " | ||
728 | "crt_enabled=%d, lcd_enabled=%d\n", | ||
729 | var->bits_per_pixel, lcd_bpp, crt_enabled, lcd_enabled); | ||
730 | dbg(PFX "xres=%d, yres=%d, vxres=%d, vyres=%d " | ||
731 | "is_color=%d, is_dual=%d, is_tft=%d\n", | ||
732 | xres, yres, xres_virtual, yres_virtual, is_color, is_dual, is_tft); | ||
733 | } | ||
734 | |||
735 | |||
736 | static int | ||
737 | s1d13xxxfb_remove(struct platform_device *pdev) | ||
738 | { | ||
739 | struct fb_info *info = platform_get_drvdata(pdev); | ||
740 | struct s1d13xxxfb_par *par = NULL; | ||
741 | |||
742 | if (info) { | ||
743 | par = info->par; | ||
744 | if (par && par->regs) { | ||
745 | /* disable output & enable powersave */ | ||
746 | s1d13xxxfb_writereg(par, S1DREG_COM_DISP_MODE, 0x00); | ||
747 | s1d13xxxfb_writereg(par, S1DREG_PS_CNF, 0x11); | ||
748 | iounmap(par->regs); | ||
749 | } | ||
750 | |||
751 | fb_dealloc_cmap(&info->cmap); | ||
752 | |||
753 | if (info->screen_base) | ||
754 | iounmap(info->screen_base); | ||
755 | |||
756 | framebuffer_release(info); | ||
757 | } | ||
758 | |||
759 | release_mem_region(pdev->resource[0].start, | ||
760 | pdev->resource[0].end - pdev->resource[0].start +1); | ||
761 | release_mem_region(pdev->resource[1].start, | ||
762 | pdev->resource[1].end - pdev->resource[1].start +1); | ||
763 | return 0; | ||
764 | } | ||
765 | |||
766 | static int s1d13xxxfb_probe(struct platform_device *pdev) | ||
767 | { | ||
768 | struct s1d13xxxfb_par *default_par; | ||
769 | struct fb_info *info; | ||
770 | struct s1d13xxxfb_pdata *pdata = NULL; | ||
771 | int ret = 0; | ||
772 | int i; | ||
773 | u8 revision, prod_id; | ||
774 | |||
775 | dbg("probe called: device is %p\n", pdev); | ||
776 | |||
777 | printk(KERN_INFO "Epson S1D13XXX FB Driver\n"); | ||
778 | |||
779 | /* enable platform-dependent hardware glue, if any */ | ||
780 | if (dev_get_platdata(&pdev->dev)) | ||
781 | pdata = dev_get_platdata(&pdev->dev); | ||
782 | |||
783 | if (pdata && pdata->platform_init_video) | ||
784 | pdata->platform_init_video(); | ||
785 | |||
786 | if (pdev->num_resources != 2) { | ||
787 | dev_err(&pdev->dev, "invalid num_resources: %i\n", | ||
788 | pdev->num_resources); | ||
789 | ret = -ENODEV; | ||
790 | goto bail; | ||
791 | } | ||
792 | |||
793 | /* resource[0] is VRAM, resource[1] is registers */ | ||
794 | if (pdev->resource[0].flags != IORESOURCE_MEM | ||
795 | || pdev->resource[1].flags != IORESOURCE_MEM) { | ||
796 | dev_err(&pdev->dev, "invalid resource type\n"); | ||
797 | ret = -ENODEV; | ||
798 | goto bail; | ||
799 | } | ||
800 | |||
801 | if (!request_mem_region(pdev->resource[0].start, | ||
802 | pdev->resource[0].end - pdev->resource[0].start +1, "s1d13xxxfb mem")) { | ||
803 | dev_dbg(&pdev->dev, "request_mem_region failed\n"); | ||
804 | ret = -EBUSY; | ||
805 | goto bail; | ||
806 | } | ||
807 | |||
808 | if (!request_mem_region(pdev->resource[1].start, | ||
809 | pdev->resource[1].end - pdev->resource[1].start +1, "s1d13xxxfb regs")) { | ||
810 | dev_dbg(&pdev->dev, "request_mem_region failed\n"); | ||
811 | ret = -EBUSY; | ||
812 | goto bail; | ||
813 | } | ||
814 | |||
815 | info = framebuffer_alloc(sizeof(struct s1d13xxxfb_par) + sizeof(u32) * 256, &pdev->dev); | ||
816 | if (!info) { | ||
817 | ret = -ENOMEM; | ||
818 | goto bail; | ||
819 | } | ||
820 | |||
821 | platform_set_drvdata(pdev, info); | ||
822 | default_par = info->par; | ||
823 | default_par->regs = ioremap_nocache(pdev->resource[1].start, | ||
824 | pdev->resource[1].end - pdev->resource[1].start +1); | ||
825 | if (!default_par->regs) { | ||
826 | printk(KERN_ERR PFX "unable to map registers\n"); | ||
827 | ret = -ENOMEM; | ||
828 | goto bail; | ||
829 | } | ||
830 | info->pseudo_palette = default_par->pseudo_palette; | ||
831 | |||
832 | info->screen_base = ioremap_nocache(pdev->resource[0].start, | ||
833 | pdev->resource[0].end - pdev->resource[0].start +1); | ||
834 | |||
835 | if (!info->screen_base) { | ||
836 | printk(KERN_ERR PFX "unable to map framebuffer\n"); | ||
837 | ret = -ENOMEM; | ||
838 | goto bail; | ||
839 | } | ||
840 | |||
841 | /* production id is top 6 bits */ | ||
842 | prod_id = s1d13xxxfb_readreg(default_par, S1DREG_REV_CODE) >> 2; | ||
843 | /* revision id is lower 2 bits */ | ||
844 | revision = s1d13xxxfb_readreg(default_par, S1DREG_REV_CODE) & 0x3; | ||
845 | ret = -ENODEV; | ||
846 | |||
847 | for (i = 0; i < ARRAY_SIZE(s1d13xxxfb_prod_ids); i++) { | ||
848 | if (prod_id == s1d13xxxfb_prod_ids[i]) { | ||
849 | /* looks like we got it in our list */ | ||
850 | default_par->prod_id = prod_id; | ||
851 | default_par->revision = revision; | ||
852 | ret = 0; | ||
853 | break; | ||
854 | } | ||
855 | } | ||
856 | |||
857 | if (!ret) { | ||
858 | printk(KERN_INFO PFX "chip production id %i = %s\n", | ||
859 | prod_id, s1d13xxxfb_prod_names[i]); | ||
860 | printk(KERN_INFO PFX "chip revision %i\n", revision); | ||
861 | } else { | ||
862 | printk(KERN_INFO PFX | ||
863 | "unknown chip production id %i, revision %i\n", | ||
864 | prod_id, revision); | ||
865 | printk(KERN_INFO PFX "please contact maintainer\n"); | ||
866 | goto bail; | ||
867 | } | ||
868 | |||
869 | info->fix = s1d13xxxfb_fix; | ||
870 | info->fix.mmio_start = pdev->resource[1].start; | ||
871 | info->fix.mmio_len = pdev->resource[1].end - pdev->resource[1].start + 1; | ||
872 | info->fix.smem_start = pdev->resource[0].start; | ||
873 | info->fix.smem_len = pdev->resource[0].end - pdev->resource[0].start + 1; | ||
874 | |||
875 | printk(KERN_INFO PFX "regs mapped at 0x%p, fb %d KiB mapped at 0x%p\n", | ||
876 | default_par->regs, info->fix.smem_len / 1024, info->screen_base); | ||
877 | |||
878 | info->par = default_par; | ||
879 | info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN; | ||
880 | info->fbops = &s1d13xxxfb_fbops; | ||
881 | |||
882 | switch(prod_id) { | ||
883 | case S1D13506_PROD_ID: /* activate acceleration */ | ||
884 | s1d13xxxfb_fbops.fb_fillrect = s1d13xxxfb_bitblt_solidfill; | ||
885 | s1d13xxxfb_fbops.fb_copyarea = s1d13xxxfb_bitblt_copyarea; | ||
886 | info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN | | ||
887 | FBINFO_HWACCEL_FILLRECT | FBINFO_HWACCEL_COPYAREA; | ||
888 | break; | ||
889 | default: | ||
890 | break; | ||
891 | } | ||
892 | |||
893 | /* perform "manual" chip initialization, if needed */ | ||
894 | if (pdata && pdata->initregs) | ||
895 | s1d13xxxfb_runinit(info->par, pdata->initregs, pdata->initregssize); | ||
896 | |||
897 | s1d13xxxfb_fetch_hw_state(info); | ||
898 | |||
899 | if (register_framebuffer(info) < 0) { | ||
900 | ret = -EINVAL; | ||
901 | goto bail; | ||
902 | } | ||
903 | |||
904 | fb_info(info, "%s frame buffer device\n", info->fix.id); | ||
905 | |||
906 | return 0; | ||
907 | |||
908 | bail: | ||
909 | s1d13xxxfb_remove(pdev); | ||
910 | return ret; | ||
911 | |||
912 | } | ||
913 | |||
914 | #ifdef CONFIG_PM | ||
915 | static int s1d13xxxfb_suspend(struct platform_device *dev, pm_message_t state) | ||
916 | { | ||
917 | struct fb_info *info = platform_get_drvdata(dev); | ||
918 | struct s1d13xxxfb_par *s1dfb = info->par; | ||
919 | struct s1d13xxxfb_pdata *pdata = NULL; | ||
920 | |||
921 | /* disable display */ | ||
922 | lcd_enable(s1dfb, 0); | ||
923 | crt_enable(s1dfb, 0); | ||
924 | |||
925 | if (dev_get_platdata(&dev->dev)) | ||
926 | pdata = dev_get_platdata(&dev->dev); | ||
927 | |||
928 | #if 0 | ||
929 | if (!s1dfb->disp_save) | ||
930 | s1dfb->disp_save = kmalloc(info->fix.smem_len, GFP_KERNEL); | ||
931 | |||
932 | if (!s1dfb->disp_save) { | ||
933 | printk(KERN_ERR PFX "no memory to save screen"); | ||
934 | return -ENOMEM; | ||
935 | } | ||
936 | |||
937 | memcpy_fromio(s1dfb->disp_save, info->screen_base, info->fix.smem_len); | ||
938 | #else | ||
939 | s1dfb->disp_save = NULL; | ||
940 | #endif | ||
941 | |||
942 | if (!s1dfb->regs_save) | ||
943 | s1dfb->regs_save = kmalloc(info->fix.mmio_len, GFP_KERNEL); | ||
944 | |||
945 | if (!s1dfb->regs_save) { | ||
946 | printk(KERN_ERR PFX "no memory to save registers"); | ||
947 | return -ENOMEM; | ||
948 | } | ||
949 | |||
950 | /* backup all registers */ | ||
951 | memcpy_fromio(s1dfb->regs_save, s1dfb->regs, info->fix.mmio_len); | ||
952 | |||
953 | /* now activate power save mode */ | ||
954 | s1d13xxxfb_writereg(s1dfb, S1DREG_PS_CNF, 0x11); | ||
955 | |||
956 | if (pdata && pdata->platform_suspend_video) | ||
957 | return pdata->platform_suspend_video(); | ||
958 | else | ||
959 | return 0; | ||
960 | } | ||
961 | |||
962 | static int s1d13xxxfb_resume(struct platform_device *dev) | ||
963 | { | ||
964 | struct fb_info *info = platform_get_drvdata(dev); | ||
965 | struct s1d13xxxfb_par *s1dfb = info->par; | ||
966 | struct s1d13xxxfb_pdata *pdata = NULL; | ||
967 | |||
968 | /* awaken the chip */ | ||
969 | s1d13xxxfb_writereg(s1dfb, S1DREG_PS_CNF, 0x10); | ||
970 | |||
971 | /* do not let go until SDRAM "wakes up" */ | ||
972 | while ((s1d13xxxfb_readreg(s1dfb, S1DREG_PS_STATUS) & 0x01)) | ||
973 | udelay(10); | ||
974 | |||
975 | if (dev_get_platdata(&dev->dev)) | ||
976 | pdata = dev_get_platdata(&dev->dev); | ||
977 | |||
978 | if (s1dfb->regs_save) { | ||
979 | /* will write RO regs, *should* get away with it :) */ | ||
980 | memcpy_toio(s1dfb->regs, s1dfb->regs_save, info->fix.mmio_len); | ||
981 | kfree(s1dfb->regs_save); | ||
982 | } | ||
983 | |||
984 | if (s1dfb->disp_save) { | ||
985 | memcpy_toio(info->screen_base, s1dfb->disp_save, | ||
986 | info->fix.smem_len); | ||
987 | kfree(s1dfb->disp_save); /* XXX kmalloc()'d when? */ | ||
988 | } | ||
989 | |||
990 | if ((s1dfb->display & 0x01) != 0) | ||
991 | lcd_enable(s1dfb, 1); | ||
992 | if ((s1dfb->display & 0x02) != 0) | ||
993 | crt_enable(s1dfb, 1); | ||
994 | |||
995 | if (pdata && pdata->platform_resume_video) | ||
996 | return pdata->platform_resume_video(); | ||
997 | else | ||
998 | return 0; | ||
999 | } | ||
1000 | #endif /* CONFIG_PM */ | ||
1001 | |||
1002 | static struct platform_driver s1d13xxxfb_driver = { | ||
1003 | .probe = s1d13xxxfb_probe, | ||
1004 | .remove = s1d13xxxfb_remove, | ||
1005 | #ifdef CONFIG_PM | ||
1006 | .suspend = s1d13xxxfb_suspend, | ||
1007 | .resume = s1d13xxxfb_resume, | ||
1008 | #endif | ||
1009 | .driver = { | ||
1010 | .name = S1D_DEVICENAME, | ||
1011 | }, | ||
1012 | }; | ||
1013 | |||
1014 | |||
1015 | static int __init | ||
1016 | s1d13xxxfb_init(void) | ||
1017 | { | ||
1018 | |||
1019 | #ifndef MODULE | ||
1020 | if (fb_get_options("s1d13xxxfb", NULL)) | ||
1021 | return -ENODEV; | ||
1022 | #endif | ||
1023 | |||
1024 | return platform_driver_register(&s1d13xxxfb_driver); | ||
1025 | } | ||
1026 | |||
1027 | |||
1028 | static void __exit | ||
1029 | s1d13xxxfb_exit(void) | ||
1030 | { | ||
1031 | platform_driver_unregister(&s1d13xxxfb_driver); | ||
1032 | } | ||
1033 | |||
1034 | module_init(s1d13xxxfb_init); | ||
1035 | module_exit(s1d13xxxfb_exit); | ||
1036 | |||
1037 | |||
1038 | MODULE_LICENSE("GPL"); | ||
1039 | MODULE_DESCRIPTION("Framebuffer driver for S1D13xxx devices"); | ||
1040 | MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>, Thibaut VARENE <varenet@parisc-linux.org>"); | ||