ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / video / epson1355fb.c
1 /*
2  * linux/drivers/video/epson1355fb.c
3  *      -- Support for the Epson SED1355 LCD/CRT controller
4  *
5  * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
6  *
7  * based on linux/drivers/video/skeletonfb.c, which was
8  *  Created 28 Dec 1997 by Geert Uytterhoeven
9  *
10  * This file is subject to the terms and conditions of the GNU General Public
11  * License.  See the file COPYING in the main directory of this archive
12  * for more details.
13  */
14 /* TODO (roughly in order of priority):
15  * 16 bpp support
16  * crt support
17  * hw cursor support
18  * SwivelView
19  */
20
21 #include <asm/io.h>
22 #include <linux/config.h>
23 #include <linux/delay.h>
24 #include <linux/errno.h>
25 #include <linux/fb.h>
26 #include <linux/init.h>
27 #include <linux/kernel.h>
28 #include <linux/slab.h>
29 #include <linux/mm.h>
30 #include <linux/module.h>
31 #include <linux/sched.h>
32 #include <linux/string.h>
33 #include <linux/tty.h>
34 #include <video/fbcon-cfb8.h>
35 #include <video/fbcon-mfb.h>
36 #include <video/fbcon.h>
37
38 /* Register defines.  The docs don't seem to provide nice mnemonic names
39  * so I made them up myself ... */
40
41 #define E1355_PANEL     0x02
42 #define E1355_DISPLAY   0x0D
43 #define E1355_MISC      0x1B
44 #define E1355_GPIO      0x20
45 #define E1355_LUT_INDEX 0x24
46 #define E1355_LUT_DATA  0x26
47
48 #ifdef CONFIG_SUPERH
49 #define E1355_REG_BASE  CONFIG_E1355_REG_BASE
50 #define E1355_FB_BASE   CONFIG_E1355_FB_BASE
51
52 static inline u8 e1355_read_reg(int index)
53 {
54         return ctrl_inb(E1355_REG_BASE + index);
55 }
56
57 static inline void e1355_write_reg(u8 data, int index)
58 {
59         ctrl_outb(data, E1355_REG_BASE + index);
60 }
61
62 static inline u16 e1355_read_reg16(int index)
63 {
64         return e1355_read_reg(index) + (e1355_read_reg(index+1) << 8);
65 }
66
67 static inline void e1355_write_reg16(u16 data, int index)
68 {
69         e1355_write_reg((data&0xff), index);
70         e1355_write_reg(((data>>8)&0xff), index + 1);
71 }
72 #else
73 #error unknown architecture
74 #endif
75
76 struct e1355fb_info {
77         struct fb_info_gen gen;
78 };
79
80 static int current_par_valid = 0;
81 static struct display disp;
82
83 static struct fb_var_screeninfo default_var;
84
85 int e1355fb_init(void);
86 int e1355fb_setup(char*);
87 static int e1355_encode_var(struct fb_var_screeninfo *var, const void *par,
88                             struct fb_info_gen *info);
89 /* ------------------- chipset specific functions -------------------------- */
90
91
92 static void disable_hw_cursor(void)
93 {
94         u8 curs;
95
96         curs = e1355_read_reg(0x27);
97         curs &= ~0xc0;
98         e1355_write_reg(curs, 0x27);
99 }
100
101 static void e1355_detect(void)
102 {
103         u8 rev;
104
105         e1355_write_reg(0x00, E1355_MISC);
106
107         rev = e1355_read_reg(0x00);
108
109         if ((rev & 0xfc) != 0x0c) {
110                 printk(KERN_WARNING "Epson 1355 not detected\n");
111         }
112
113         /* XXX */
114         disable_hw_cursor();
115
116         e1355_encode_var(&default_var, NULL, NULL);
117 }
118
119 struct e1355_par {
120         u32 xres;
121         u32 yres;
122
123         int bpp;
124         int mem_bpp;
125
126         u32 panel_xres;
127         u32 panel_yres;
128         
129         int panel_width;
130         int panel_ymul;
131 };
132
133 static int e1355_encode_fix(struct fb_fix_screeninfo *fix,
134                             const void *raw_par,
135                             struct fb_info_gen *info)
136 {
137         const struct e1355_par *par = raw_par;
138         
139         memset(fix, 0, sizeof *fix);
140         
141         fix->type= FB_TYPE_PACKED_PIXELS;
142
143         if (!par)
144                 BUG();
145
146         if (par->bpp == 1) {
147                 fix->visual = FB_VISUAL_MONO10;
148         } else if (par->bpp <= 8) {
149                 fix->visual = FB_VISUAL_PSEUDOCOLOR;
150         } else {
151                 fix->visual = FB_VISUAL_TRUECOLOR;
152         }
153
154         return 0;
155 }
156
157 static int e1355_set_bpp(struct e1355_par *par, int bpp)
158 {
159         int code;
160         u8 disp;
161         u16 bytes_per_line;
162
163         switch(bpp) {
164         case 1:
165                 code = 0; break;
166         case 2:
167                 code = 1; break;
168         case 4:
169                 code = 2; break;
170         case 8:
171                 code = 3; break;
172         case 16:
173                 code = 5; break;
174         default:
175                 return -EINVAL; break;
176         }
177
178         disp = e1355_read_reg(E1355_DISPLAY);
179         disp &= ~0x1c;
180         disp |= code << 2;
181         e1355_write_reg(disp, E1355_DISPLAY);
182         
183         bytes_per_line = (par->xres * bpp) >> 3;
184         
185         e1355_write_reg16(bytes_per_line, 0x16);
186
187         par->bpp = bpp;
188
189         return 0;
190 }
191                 
192 static int e1355_decode_var(const struct fb_var_screeninfo *var,
193                             void *raw_par,
194                             struct fb_info_gen *info)
195 {
196         struct e1355_par *par = raw_par;
197         int ret;
198
199         if (!par)
200                 BUG();
201
202         /*
203          * Don't allow setting any of these yet: xres and yres don't
204          * make sense for LCD panels; xres_virtual and yres_virtual
205          * should be supported fine by our hardware though.
206          */
207         if (var->xres != par->xres ||
208             var->yres != par->yres ||
209             var->xres != var->xres_virtual ||
210             var->yres != var->yres_virtual ||
211             var->xoffset != 0 ||
212             var->yoffset != 0)
213                 return -EINVAL;
214
215         if(var->bits_per_pixel != par->bpp) {
216                 ret = e1355_set_bpp(par, var->bits_per_pixel);
217
218                 if (ret)
219                         goto out_err;
220         }
221                 
222         return 0;
223
224  out_err:
225         return ret;
226 }
227
228 static void dump_panel_data(void)
229 {
230         u8 panel = e1355_read_reg(E1355_PANEL);
231         int width[2][4] = { { 4, 8, 16, -1 }, { 9, 12, 16, -1 } };
232
233         printk("%s %s %s panel, width %d bits\n",
234                panel & 2 ? "dual" : "single",
235                panel & 4 ? "color" : "mono",
236                panel & 1 ? "TFT" : "passive",
237                width[panel&1][(panel>>4)&3]);
238
239         printk("resolution %d x %d\n",
240                (e1355_read_reg(0x04) + 1) * 8,
241                ((e1355_read_reg16(0x08) + 1) * (1 + ((panel & 3) == 2))));
242 }
243
244 static int e1355_bpp_to_var(int bpp, struct fb_var_screeninfo *var)
245 {
246         switch(bpp) {
247         case 1:
248         case 2:
249         case 4:
250         case 8:
251                 var->bits_per_pixel = bpp;
252                 var->red.offset = var->green.offset = var->blue.offset = 0;
253                 var->red.length = var->green.length = var->blue.length = bpp;
254                 break;
255         case 16:
256                 var->bits_per_pixel = 16;
257                 var->red.offset = 11;
258                 var->red.length = 5;
259                 var->green.offset = 5;
260                 var->green.length = 6;
261                 var->blue.offset = 0;
262                 var->blue.length = 5;
263                 break;
264         }
265
266         return 0;
267 }
268
269 static int e1355_encode_var(struct fb_var_screeninfo *var, const void *raw_par,
270                             struct fb_info_gen *info)
271 {
272         u8 panel, display;
273         u32 xres, xres_virtual, yres;
274         static int width[2][4] = { { 4, 8, 16, -1 }, { 9, 12, 16, -1 } };
275         static int bpp_tab[8] = { 1, 2, 4, 8, 15, 16 };
276         int bpp, hw_bpp;
277         int is_color, is_dual, is_tft;
278         int lcd_enabled, crt_enabled;
279
280         panel = e1355_read_reg(E1355_PANEL);
281         display = e1355_read_reg(E1355_DISPLAY);
282
283         is_color = (panel & 0x04) != 0;
284         is_dual  = (panel & 0x02) != 0;
285         is_tft   = (panel & 0x01) != 0;
286
287         bpp = bpp_tab[(display>>2)&7]; 
288         e1355_bpp_to_var(bpp, var);
289
290         crt_enabled = (display & 0x02) != 0;
291         lcd_enabled = (display & 0x02) != 0;
292
293         hw_bpp = width[is_tft][(panel>>4)&3];
294
295         xres = e1355_read_reg(0x04) + 1;
296         yres = e1355_read_reg16(0x08) + 1;
297         
298         xres *= 8;
299         /* talk about weird hardware .. */
300         yres *= (is_dual && !crt_enabled) ? 2 : 1;
301
302         xres_virtual = e1355_read_reg16(0x16);
303         /* it's in 2-byte words initially */
304         xres_virtual *= 16;
305         xres_virtual /= var->bits_per_pixel;
306
307         var->xres = xres;
308         var->yres = yres;
309         var->xres_virtual = xres_virtual;
310         var->yres_virtual = yres;
311
312         var->xoffset = var->yoffset = 0;
313
314         var->grayscale = !is_color;
315         
316         return 0;
317 }
318
319 #define is_dual(panel) (((panel)&3)==2)
320
321 static void get_panel_data(struct e1355_par *par)
322 {
323         u8 panel;
324         int width[2][4] = { { 4, 8, 16, -1 }, { 9, 12, 16, -1 } };
325
326         panel = e1355_read_reg(E1355_PANEL);
327
328         par->panel_width = width[panel&1][(panel>>4)&3];
329         par->panel_xres = (e1355_read_reg(0x04) + 1) * 8;
330         par->panel_ymul = is_dual(panel) ? 2 : 1;
331         par->panel_yres = ((e1355_read_reg16(0x08) + 1)
332                            * par->panel_ymul);
333 }
334
335 static void e1355_get_par(void *raw_par, struct fb_info_gen *info)
336 {
337         struct e1355_par *par = raw_par;
338
339         get_panel_data(par);
340 }
341
342 static void e1355_set_par(const void *par, struct fb_info_gen *info)
343 {
344 }
345
346 static int e1355_getcolreg(unsigned regno, unsigned *red, unsigned *green,
347                            unsigned *blue, unsigned *transp,
348                            struct fb_info *info)
349 {
350         u8 r, g, b;
351
352         e1355_write_reg(regno, E1355_LUT_INDEX);
353         r = e1355_read_reg(E1355_LUT_DATA);
354         g = e1355_read_reg(E1355_LUT_DATA);
355         b = e1355_read_reg(E1355_LUT_DATA);
356
357         *red = r << 8;
358         *green = g << 8;
359         *blue = b << 8;
360
361         return 0;
362 }
363
364 static int e1355fb_setcolreg(unsigned regno, unsigned red, unsigned green,
365                              unsigned blue, unsigned transp,
366                              struct fb_info *info)
367 {
368         u8 r = (red >> 8) & 0xf0;
369         u8 g = (green>>8) & 0xf0;
370         u8 b = (blue>> 8) & 0xf0;
371
372         e1355_write_reg(regno, E1355_LUT_INDEX);
373         e1355_write_reg(r, E1355_LUT_DATA);
374         e1355_write_reg(g, E1355_LUT_DATA);
375         e1355_write_reg(b, E1355_LUT_DATA);
376         
377         return 0;
378 }
379
380 static int e1355_pan_display(const struct fb_var_screeninfo *var,
381                              struct fb_info_gen *info)
382 {
383         BUG();
384         
385         return -EINVAL;
386 }
387
388 /*
389  * The AERO_HACKS parts disable/enable the backlight on the Compaq Aero 8000.
390  * I'm not sure they aren't dangerous to the hardware, so be warned.
391  */
392 #undef AERO_HACKS
393
394 static int e1355_blank(int blank_mode, struct fb_info_gen *info)
395 {
396         u8 disp;
397
398         switch (blank_mode) {
399         case VESA_NO_BLANKING:
400                 disp = e1355_read_reg(E1355_DISPLAY);
401                 disp |= 1;
402                 e1355_write_reg(disp, E1355_DISPLAY);
403                 
404 #ifdef AERO_HACKS
405                 e1355_write_reg(0x6, 0x20);
406 #endif
407                 break;
408
409         case VESA_VSYNC_SUSPEND:
410         case VESA_HSYNC_SUSPEND:
411         case VESA_POWERDOWN:
412                 disp = e1355_read_reg(E1355_DISPLAY);
413                 disp &= ~1;
414                 e1355_write_reg(disp, E1355_DISPLAY);
415
416 #ifdef AERO_HACKS
417                 e1355_write_reg(0x0, 0x20);
418 #endif
419                 break;
420
421         default:
422                 return -EINVAL;
423         }
424
425         return 0;
426 }
427
428 static struct display_switch e1355_dispsw;
429
430 static void e1355_set_disp(const void *unused, struct display *disp,
431                            struct fb_info_gen *info)
432 {
433         struct display_switch *d;
434
435         disp->dispsw = &e1355_dispsw;
436         
437         switch(disp->var.bits_per_pixel) {
438 #ifdef FBCON_HAS_MFB
439         case 1:
440                 d = &fbcon_mfb; break;
441 #endif         
442 #ifdef FBCON_HAS_CFB8
443         case 8:
444                 d = &fbcon_cfb8; break;
445 #endif
446         default:
447                 BUG(); break;
448         }
449
450         memcpy(&e1355_dispsw, d, sizeof *d);
451
452         /* reading is terribly slow for us */
453 #if 0 /* XXX: need to work out why this doesn't work */
454         e1355_dispsw.bmove = fbcon_redraw_bmove;
455 #endif
456 }
457
458 /* ------------ Interfaces to hardware functions ------------ */
459
460
461 struct fbgen_hwswitch e1355_switch = {
462         .detect =       e1355_detect,
463         .encode_fix =   e1355_encode_fix,
464         .decode_var =   e1355_decode_var,
465         .encode_var =   e1355_encode_var,
466         .get_par =      e1355_get_par,
467         .set_par =      e1355_set_par,
468         .getcolreg =    e1355_getcolreg,
469         .pan_display =  e1355_pan_display,
470         .blank =        e1355_blank,
471         .set_disp =     e1355_set_disp,
472 };
473
474
475 /* ------------ Hardware Independent Functions ------------ */
476
477
478 static struct fb_ops e1355fb_ops = {
479         .owner =        THIS_MODULE,
480         .fb_get_fix =   fbgen_get_fix,
481         .fb_get_var =   fbgen_get_var,
482         .fb_set_var =   fbgen_set_var,
483         .fb_get_cmap =  fbgen_get_cmap,
484         .fb_set_cmap =  gen_set_cmap,
485         .fb_setcolreg = e1355fb_setcolreg,
486         .fb_pan_display =fbgen_pan_display,
487         .fb_blank =     fbgen_blank,
488 };
489
490 static struct e1355fb_info fb_info;
491
492 int __init e1355fb_setup(char *str)
493 {
494         return 0;
495 }
496
497 int __init e1355fb_init(void)
498 {
499         fb_info.gen.fbhw = &e1355_switch;
500         fb_info.gen.fbhw->detect();
501         strcpy(fb_info.gen.info.modename, "SED1355");
502         fb_info.gen.info.changevar = NULL;
503         fb_info.gen.info.fbops = &e1355fb_ops;
504         fb_info.gen.info.screen_base = (void *)E1355_FB_BASE;
505         fb_info.gen.currcon = -1;
506         fb_info.gen.info.disp = &disp;
507         fb_info.gen.parsize = sizeof(struct e1355_par);
508         fb_info.gen.info.switch_con = &fbgen_switch;
509         fb_info.gen.info.updatevar = &fbgen_update_var;
510         fb_info.gen.info.flags = FBINFO_FLAG_DEFAULT;
511         /* This should give a reasonable default video mode */
512         fbgen_get_var(&disp.var, -1, &fb_info.gen.info);
513         fbgen_do_set_var(&disp.var, 1, &fb_info.gen);
514         fbgen_set_disp(-1, &fb_info.gen);
515         if (disp.var.bits_per_pixel > 1) 
516                 do_install_cmap(0, &fb_info.gen);
517         if (register_framebuffer(&fb_info.gen.info) < 0)
518                 return -EINVAL;
519         printk(KERN_INFO "fb%d: %s frame buffer device\n", fb_info.gen.info.node,
520                fb_info.gen.info.modename);
521
522         return 0;
523 }
524
525
526     /*
527      *  Cleanup
528      */
529
530 void e1355fb_cleanup(struct fb_info *info)
531 {
532         /*
533          *  If your driver supports multiple boards, you should unregister and
534          *  clean up all instances.
535          */
536         
537         unregister_framebuffer(info);
538         /* ... */
539 }
540
541 MODULE_LICENSE("GPL");