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 
e1355_read_reg(int index)52 static inline u8 e1355_read_reg(int index)
53 {
54 	return ctrl_inb(E1355_REG_BASE + index);
55 }
56 
e1355_write_reg(u8 data,int index)57 static inline void e1355_write_reg(u8 data, int index)
58 {
59 	ctrl_outb(data, E1355_REG_BASE + index);
60 }
61 
e1355_read_reg16(int index)62 static inline u16 e1355_read_reg16(int index)
63 {
64 	return e1355_read_reg(index) + (e1355_read_reg(index+1) << 8);
65 }
66 
e1355_write_reg16(u16 data,int index)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 
disable_hw_cursor(void)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 
e1355_detect(void)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 
e1355_encode_fix(struct fb_fix_screeninfo * fix,const void * raw_par,struct fb_info_gen * info)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 
e1355_set_bpp(struct e1355_par * par,int bpp)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 
e1355_decode_var(const struct fb_var_screeninfo * var,void * raw_par,struct fb_info_gen * info)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 
dump_panel_data(void)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 
e1355_bpp_to_var(int bpp,struct fb_var_screeninfo * var)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 
e1355_encode_var(struct fb_var_screeninfo * var,const void * raw_par,struct fb_info_gen * info)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 
get_panel_data(struct e1355_par * par)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 
e1355_get_par(void * raw_par,struct fb_info_gen * info)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 
e1355_set_par(const void * par,struct fb_info_gen * info)342 static void e1355_set_par(const void *par, struct fb_info_gen *info)
343 {
344 }
345 
e1355_getcolreg(unsigned regno,unsigned * red,unsigned * green,unsigned * blue,unsigned * transp,struct fb_info * info)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 
e1355_setcolreg(unsigned regno,unsigned red,unsigned green,unsigned blue,unsigned transp,struct fb_info * info)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 
e1355_pan_display(const struct fb_var_screeninfo * var,struct fb_info_gen * info)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 
e1355_blank(int blank_mode,struct fb_info_gen * info)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 
e1355_set_disp(const void * unused,struct display * disp,struct fb_info_gen * info)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 
e1355fb_setup(char * str)492 int __init e1355fb_setup(char *str)
493 {
494 	return 0;
495 }
496 
e1355fb_init(void)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 
e1355fb_cleanup(struct fb_info * info)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