File: /usr/src/linux/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 e1355_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->screen_base = (void *)E1355_FB_BASE;
436     	disp->dispsw = &e1355_dispsw;
437     	
438     	switch(disp->var.bits_per_pixel) {
439     #ifdef FBCON_HAS_MFB
440     	case 1:
441     		d = &fbcon_mfb; break;
442     #endif	       
443     #ifdef FBCON_HAS_CFB8
444     	case 8:
445     		d = &fbcon_cfb8; break;
446     #endif
447     	default:
448     		BUG(); break;
449     	}
450     
451     	memcpy(&e1355_dispsw, d, sizeof *d);
452     
453     	/* reading is terribly slow for us */
454     #if 0 /* XXX: need to work out why this doesn't work */
455     	e1355_dispsw.bmove = fbcon_redraw_bmove;
456     #endif
457     }
458     
459     /* ------------ Interfaces to hardware functions ------------ */
460     
461     
462     struct fbgen_hwswitch e1355_switch = {
463     	detect:		e1355_detect,
464     	encode_fix:	e1355_encode_fix,
465     	decode_var:	e1355_decode_var,
466     	encode_var:	e1355_encode_var,
467     	get_par:	e1355_get_par,
468     	set_par:	e1355_set_par,
469     	getcolreg:	e1355_getcolreg,
470     	setcolreg:	e1355_setcolreg,
471     	pan_display:	e1355_pan_display,
472     	blank:		e1355_blank,
473     	set_disp:	e1355_set_disp,
474     };
475     
476     
477     /* ------------ Hardware Independent Functions ------------ */
478     
479     
480     static struct fb_ops e1355fb_ops = {
481     	owner:		THIS_MODULE,
482     	fb_get_fix:	fbgen_get_fix,
483     	fb_get_var:	fbgen_get_var,
484     	fb_set_var:	fbgen_set_var,
485     	fb_get_cmap:	fbgen_get_cmap,
486     	fb_set_cmap:	fbgen_set_cmap,
487     	fb_pan_display:	fbgen_pan_display,
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.node = -1;
504     	fb_info.gen.info.fbops = &e1355fb_ops;
505     	fb_info.gen.info.disp = &disp;
506     	fb_info.gen.parsize = sizeof(struct e1355_par);
507     	fb_info.gen.info.switch_con = &fbgen_switch;
508     	fb_info.gen.info.updatevar = &fbgen_update_var;
509     	fb_info.gen.info.blank = &fbgen_blank;
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     		fbgen_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", GET_FB_IDX(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");
542