1 /*
2  * BRIEF MODULE DESCRIPTION
3  *	Au1100 LCD Driver.
4  *
5  * Copyright 2002 MontaVista Software
6  * Author: MontaVista Software, Inc.
7  *		ppopov@mvista.com or source@mvista.com
8  *
9  * Copyright 2002 Alchemy Semiconductor
10  * Author: Alchemy Semiconductor
11  *
12  * Based on:
13  * linux/drivers/video/skeletonfb.c -- Skeleton for a frame buffer device
14  *  Created 28 Dec 1997 by Geert Uytterhoeven
15  *
16  *  This program is free software; you can redistribute	 it and/or modify it
17  *  under  the terms of	 the GNU General  Public License as published by the
18  *  Free Software Foundation;  either version 2 of the	License, or (at your
19  *  option) any later version.
20  *
21  *  THIS  SOFTWARE  IS PROVIDED	  ``AS	IS'' AND   ANY	EXPRESS OR IMPLIED
22  *  WARRANTIES,	  INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
23  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
24  *  NO	EVENT  SHALL   THE AUTHOR  BE	 LIABLE FOR ANY	  DIRECT, INDIRECT,
25  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26  *  NOT LIMITED	  TO, PROCUREMENT OF  SUBSTITUTE GOODS	OR SERVICES; LOSS OF
27  *  USE, DATA,	OR PROFITS; OR	BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
28  *  ANY THEORY OF LIABILITY, WHETHER IN	 CONTRACT, STRICT LIABILITY, OR TORT
29  *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30  *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  *  You should have received a copy of the  GNU General Public License along
33  *  with this program; if not, write  to the Free Software Foundation, Inc.,
34  *  675 Mass Ave, Cambridge, MA 02139, USA.
35  */
36 
37 #include <linux/module.h>
38 #include <linux/kernel.h>
39 #include <linux/errno.h>
40 #include <linux/string.h>
41 #include <linux/mm.h>
42 #include <linux/tty.h>
43 #include <linux/slab.h>
44 #include <linux/delay.h>
45 #include <linux/fb.h>
46 #include <linux/init.h>
47 #include <linux/pci.h>
48 
49 #include <asm/au1000.h>
50 #include <asm/pb1100.h>
51 #include "au1100fb.h"
52 
53 #include <video/fbcon.h>
54 #include <video/fbcon-mfb.h>
55 #include <video/fbcon-cfb2.h>
56 #include <video/fbcon-cfb4.h>
57 #include <video/fbcon-cfb8.h>
58 #include <video/fbcon-cfb16.h>
59 
60 /*
61  * Sanity check. If this is a new Au1100 based board, search for
62  * the PB1100 ifdefs to make sure you modify the code accordingly.
63  */
64 #if defined(CONFIG_MIPS_PB1100) || defined(CONFIG_MIPS_DB1100) || defined(CONFIG_MIPS_HYDROGEN3)
65 #else
66 error Unknown Au1100 board
67 #endif
68 
69 #define CMAPSIZE 16
70 
71 static int my_lcd_index; /* default is zero */
72 struct known_lcd_panels *p_lcd;
73 AU1100_LCD *p_lcd_reg = (AU1100_LCD *)AU1100_LCD_ADDR;
74 
75 struct au1100fb_info {
76 	struct fb_info_gen gen;
77 	unsigned long fb_virt_start;
78 	unsigned long fb_size;
79 	unsigned long fb_phys;
80 	int mmaped;
81 	int nohwcursor;
82 
83 	struct { unsigned red, green, blue, pad; } palette[256];
84 
85 #if defined(FBCON_HAS_CFB16)
86 	u16 fbcon_cmap16[16];
87 #endif
88 };
89 
90 
91 struct au1100fb_par {
92         struct fb_var_screeninfo var;
93 
94 	int line_length;  // in bytes
95 	int cmap_len;     // color-map length
96 };
97 
98 
99 static struct au1100fb_info fb_info;
100 static struct au1100fb_par current_par;
101 static struct display disp;
102 
103 int au1100fb_init(void);
104 void au1100fb_setup(char *options, int *ints);
105 static int au1100fb_mmap(struct fb_info *fb, struct file *file,
106 		struct vm_area_struct *vma);
107 static int au1100_blank(int blank_mode, struct fb_info_gen *info);
108 static int au1100fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
109 			  u_long arg, int con, struct fb_info *info);
110 
au1100_nocursor(struct display * p,int mode,int xx,int yy)111 void au1100_nocursor(struct display *p, int mode, int xx, int yy){};
112 
113 static struct fb_ops au1100fb_ops = {
114 	owner:		THIS_MODULE,
115 	fb_get_fix:	fbgen_get_fix,
116 	fb_get_var:	fbgen_get_var,
117 	fb_set_var:	fbgen_set_var,
118 	fb_get_cmap:	fbgen_get_cmap,
119 	fb_set_cmap:	fbgen_set_cmap,
120 	fb_pan_display: fbgen_pan_display,
121         fb_ioctl:       au1100fb_ioctl,
122 	fb_mmap:        au1100fb_mmap,
123 };
124 
au1100_detect(void)125 static void au1100_detect(void)
126 {
127 	/*
128 	 *  This function should detect the current video mode settings
129 	 *  and store it as the default video mode
130 	 */
131 
132 	/*
133 	 * Yeh, well, we're not going to change any settings so we're
134 	 * always stuck with the default ...
135 	 */
136 
137 }
138 
au1100_encode_fix(struct fb_fix_screeninfo * fix,const void * _par,struct fb_info_gen * _info)139 static int au1100_encode_fix(struct fb_fix_screeninfo *fix,
140 		const void *_par, struct fb_info_gen *_info)
141 {
142         struct au1100fb_info *info = (struct au1100fb_info *) _info;
143         struct au1100fb_par *par = (struct au1100fb_par *) _par;
144 	struct fb_var_screeninfo *var = &par->var;
145 
146 	memset(fix, 0, sizeof(struct fb_fix_screeninfo));
147 
148 	fix->smem_start = info->fb_phys;
149 	fix->smem_len = info->fb_size;
150 	fix->type = FB_TYPE_PACKED_PIXELS;
151 	fix->type_aux = 0;
152         fix->visual = (var->bits_per_pixel == 8) ?
153 	       	FB_VISUAL_PSEUDOCOLOR	: FB_VISUAL_TRUECOLOR;
154 	fix->ywrapstep = 0;
155 	fix->xpanstep = 1;
156 	fix->ypanstep = 1;
157 	fix->line_length = current_par.line_length;
158 	return 0;
159 }
160 
set_color_bitfields(struct fb_var_screeninfo * var)161 static void set_color_bitfields(struct fb_var_screeninfo *var)
162 {
163 	switch (var->bits_per_pixel) {
164 	case 8:
165 		var->red.offset = 0;
166 		var->red.length = 8;
167 		var->green.offset = 0;
168 		var->green.length = 8;
169 		var->blue.offset = 0;
170 		var->blue.length = 8;
171 		var->transp.offset = 0;
172 		var->transp.length = 0;
173 		break;
174 	case 16:	/* RGB 565 */
175 		var->red.offset = 11;
176 		var->red.length = 5;
177 		var->green.offset = 5;
178 		var->green.length = 6;
179 		var->blue.offset = 0;
180 		var->blue.length = 5;
181 		var->transp.offset = 0;
182 		var->transp.length = 0;
183 		break;
184 	}
185 
186 	var->red.msb_right = 0;
187 	var->green.msb_right = 0;
188 	var->blue.msb_right = 0;
189 	var->transp.msb_right = 0;
190 }
191 
au1100_decode_var(const struct fb_var_screeninfo * var,void * _par,struct fb_info_gen * _info)192 static int au1100_decode_var(const struct fb_var_screeninfo *var,
193 		void *_par, struct fb_info_gen *_info)
194 {
195 
196 	struct au1100fb_par *par = (struct au1100fb_par *)_par;
197 
198 	/*
199 	 * Don't allow setting any of these yet: xres and yres don't
200 	 * make sense for LCD panels.
201 	 */
202 	if (var->xres != p_lcd->xres ||
203 	    var->yres != p_lcd->yres ||
204 	    var->xres != p_lcd->xres ||
205 	    var->yres != p_lcd->yres) {
206 		return -EINVAL;
207 	}
208 	if(var->bits_per_pixel != p_lcd->bpp) {
209 		return -EINVAL;
210 	}
211 
212 	memset(par, 0, sizeof(struct au1100fb_par));
213 	par->var = *var;
214 
215 	/* FIXME */
216 	switch (var->bits_per_pixel) {
217 		case 8:
218 			par->var.bits_per_pixel = 8;
219 			break;
220 		case 16:
221 			par->var.bits_per_pixel = 16;
222 			break;
223 		default:
224 			printk("color depth %d bpp not supported\n",
225 					var->bits_per_pixel);
226 			return -EINVAL;
227 
228 	}
229 	set_color_bitfields(&par->var);
230 	par->cmap_len = (par->var.bits_per_pixel == 8) ? 256 : 16;
231 	return 0;
232 }
233 
au1100_encode_var(struct fb_var_screeninfo * var,const void * par,struct fb_info_gen * _info)234 static int au1100_encode_var(struct fb_var_screeninfo *var,
235 		const void *par, struct fb_info_gen *_info)
236 {
237 
238 	*var = ((struct au1100fb_par *)par)->var;
239 	return 0;
240 }
241 
242 static void
au1100_get_par(void * _par,struct fb_info_gen * _info)243 au1100_get_par(void *_par, struct fb_info_gen *_info)
244 {
245 	*(struct au1100fb_par *)_par = current_par;
246 }
247 
au1100_set_par(const void * par,struct fb_info_gen * info)248 static void au1100_set_par(const void *par, struct fb_info_gen *info)
249 {
250 	/* nothing to do: we don't change any settings */
251 }
252 
au1100_getcolreg(unsigned regno,unsigned * red,unsigned * green,unsigned * blue,unsigned * transp,struct fb_info * info)253 static int au1100_getcolreg(unsigned regno, unsigned *red, unsigned *green,
254 			 unsigned *blue, unsigned *transp,
255 			 struct fb_info *info)
256 {
257 
258 	struct au1100fb_info* i = (struct au1100fb_info*)info;
259 
260 	if (regno > 255)
261 		return 1;
262 
263 	*red    = i->palette[regno].red;
264 	*green  = i->palette[regno].green;
265 	*blue   = i->palette[regno].blue;
266 	*transp = 0;
267 
268 	return 0;
269 }
270 
au1100_setcolreg(unsigned regno,unsigned red,unsigned green,unsigned blue,unsigned transp,struct fb_info * info)271 static int au1100_setcolreg(unsigned regno, unsigned red, unsigned green,
272 			 unsigned blue, unsigned transp,
273 			 struct fb_info *info)
274 {
275 	struct au1100fb_info* i = (struct au1100fb_info *)info;
276 	u32 rgbcol;
277 
278 	if (regno > 255)
279 		return 1;
280 
281 	i->palette[regno].red    = red;
282 	i->palette[regno].green  = green;
283 	i->palette[regno].blue   = blue;
284 
285 	switch(p_lcd->bpp) {
286 #ifdef FBCON_HAS_CFB8
287 	case 8:
288 		red >>= 10;
289 		green >>= 10;
290 		blue >>= 10;
291 		p_lcd_reg->lcd_pallettebase[regno] = (blue&0x1f) |
292 			((green&0x3f)<<5) | ((red&0x1f)<<11);
293 		break;
294 #endif
295 #ifdef FBCON_HAS_CFB16
296 	case 16:
297 		i->fbcon_cmap16[regno] =
298 			((red & 0xf800) >> 0) |
299 			((green & 0xfc00) >> 5) |
300 			((blue & 0xf800) >> 11);
301 		break;
302 #endif
303 	default:
304 		break;
305 	}
306 
307 	return 0;
308 }
309 
310 
au1100_blank(int blank_mode,struct fb_info_gen * _info)311 static int  au1100_blank(int blank_mode, struct fb_info_gen *_info)
312 {
313 
314 	switch (blank_mode) {
315 	case VESA_NO_BLANKING:
316 		/* turn on panel */
317 		//printk("turn on panel\n");
318 #ifdef CONFIG_MIPS_PB1100
319 		p_lcd_reg->lcd_control |= LCD_CONTROL_GO;
320 		au_writew(au_readw(PB1100_G_CONTROL) | p_lcd->mode_backlight,
321 			PB1100_G_CONTROL);
322 #endif
323 #ifdef CONFIG_MIPS_HYDROGEN3
324 		/*  Turn controller & power supply on,  GPIO213 */
325 		au_writel(0x20002000, 0xB1700008);
326 		au_writel(0x00040000, 0xB1900108);
327 		au_writel(0x01000100, 0xB1700008);
328 #endif
329 		au_sync();
330 		break;
331 
332 	case VESA_VSYNC_SUSPEND:
333 	case VESA_HSYNC_SUSPEND:
334 	case VESA_POWERDOWN:
335 		/* turn off panel */
336 		//printk("turn off panel\n");
337 #ifdef CONFIG_MIPS_PB1100
338 		au_writew(au_readw(PB1100_G_CONTROL) & ~p_lcd->mode_backlight,
339 			PB1100_G_CONTROL);
340 		p_lcd_reg->lcd_control &= ~LCD_CONTROL_GO;
341 #endif
342 		au_sync();
343 		break;
344 	default:
345 		break;
346 
347 	}
348 	return 0;
349 }
350 
au1100_set_disp(const void * unused,struct display * disp,struct fb_info_gen * info)351 static void au1100_set_disp(const void *unused, struct display *disp,
352 			 struct fb_info_gen *info)
353 {
354 	disp->screen_base = (char *)fb_info.fb_virt_start;
355 
356 	switch (disp->var.bits_per_pixel) {
357 #ifdef FBCON_HAS_CFB8
358 	case 8:
359 		disp->dispsw = &fbcon_cfb8;
360 		if (fb_info.nohwcursor)
361 			fbcon_cfb8.cursor = au1100_nocursor;
362 		break;
363 #endif
364 #ifdef FBCON_HAS_CFB16
365 	case 16:
366 		disp->dispsw = &fbcon_cfb16;
367 		disp->dispsw_data = fb_info.fbcon_cmap16;
368 		if (fb_info.nohwcursor)
369 			fbcon_cfb16.cursor = au1100_nocursor;
370 		break;
371 #endif
372 	default:
373 		disp->dispsw = &fbcon_dummy;
374 		disp->dispsw_data = NULL;
375 		break;
376 	}
377 }
378 
379 static int
au1100fb_mmap(struct fb_info * _fb,struct file * file,struct vm_area_struct * vma)380 au1100fb_mmap(struct fb_info *_fb,
381 	     struct file *file,
382 	     struct vm_area_struct *vma)
383 {
384 	unsigned int len;
385 	unsigned long start=0, off;
386 	struct au1100fb_info *fb = (struct au1100fb_info *)_fb;
387 
388 	if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) {
389 		return -EINVAL;
390 	}
391 
392 	start = fb_info.fb_phys & PAGE_MASK;
393 	len = PAGE_ALIGN((start & ~PAGE_MASK) + fb_info.fb_size);
394 
395 	off = vma->vm_pgoff << PAGE_SHIFT;
396 
397 	if ((vma->vm_end - vma->vm_start + off) > len) {
398 		return -EINVAL;
399 	}
400 
401 	off += start;
402 	vma->vm_pgoff = off >> PAGE_SHIFT;
403 
404 	pgprot_val(vma->vm_page_prot) &= ~_CACHE_MASK;
405 	//pgprot_val(vma->vm_page_prot) |= _CACHE_CACHABLE_NONCOHERENT;
406 	pgprot_val(vma->vm_page_prot) |= (6 << 9); //CCA=6
407 
408 	/* This is an IO map - tell maydump to skip this VMA */
409 	vma->vm_flags |= VM_IO;
410 
411 	if (io_remap_page_range(vma->vm_start, off,
412 				vma->vm_end - vma->vm_start,
413 				vma->vm_page_prot)) {
414 		return -EAGAIN;
415 	}
416 
417 	fb->mmaped = 1;
418 	return 0;
419 }
420 
au1100_pan_display(const struct fb_var_screeninfo * var,struct fb_info_gen * info)421 int au1100_pan_display(const struct fb_var_screeninfo *var,
422 		       struct fb_info_gen *info)
423 {
424 	return 0;
425 }
426 
au1100fb_ioctl(struct inode * inode,struct file * file,u_int cmd,u_long arg,int con,struct fb_info * info)427 static int au1100fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
428 			  u_long arg, int con, struct fb_info *info)
429 {
430 	/* nothing to do yet */
431 	return -EINVAL;
432 }
433 
434 static struct fbgen_hwswitch au1100_switch = {
435 	au1100_detect,
436 	au1100_encode_fix,
437 	au1100_decode_var,
438 	au1100_encode_var,
439 	au1100_get_par,
440 	au1100_set_par,
441 	au1100_getcolreg,
442 	au1100_setcolreg,
443 	au1100_pan_display,
444 	au1100_blank,
445 	au1100_set_disp
446 };
447 
448 
au1100_setmode(void)449 int au1100_setmode(void)
450 {
451 	int words;
452 
453 	/* FIXME Need to accomodate for swivel mode and 12bpp, <8bpp*/
454 	switch (p_lcd->mode_control & LCD_CONTROL_SM)
455 	{
456 		case LCD_CONTROL_SM_0:
457 		case LCD_CONTROL_SM_180:
458 		words = (p_lcd->xres * p_lcd->yres * p_lcd->bpp) / 32;
459 			break;
460 		case LCD_CONTROL_SM_90:
461 		case LCD_CONTROL_SM_270:
462 			/* is this correct? */
463 		words = (p_lcd->xres * p_lcd->bpp) / 8;
464 			break;
465 		default:
466 			printk("mode_control reg not initialized\n");
467 			return -EINVAL;
468 	}
469 
470 	/*
471 	 * Setup LCD controller
472 	 */
473 
474 	p_lcd_reg->lcd_control = p_lcd->mode_control;
475 	p_lcd_reg->lcd_intstatus = 0;
476 	p_lcd_reg->lcd_intenable = 0;
477 	p_lcd_reg->lcd_horztiming = p_lcd->mode_horztiming;
478 	p_lcd_reg->lcd_verttiming = p_lcd->mode_verttiming;
479 	p_lcd_reg->lcd_clkcontrol = p_lcd->mode_clkcontrol;
480 	p_lcd_reg->lcd_words = words - 1;
481 	p_lcd_reg->lcd_dmaaddr0 = fb_info.fb_phys;
482 
483 	/* turn on panel */
484 #ifdef CONFIG_MIPS_PB1100
485 	au_writew(au_readw(PB1100_G_CONTROL) | p_lcd->mode_backlight,
486 			PB1100_G_CONTROL);
487 #endif
488 #ifdef CONFIG_MIPS_HYDROGEN3
489 	/*  Turn controller & power supply on,  GPIO213 */
490 	au_writel(0x20002000, 0xB1700008);
491 	au_writel(0x00040000, 0xB1900108);
492 	au_writel(0x01000100, 0xB1700008);
493 #endif
494 
495 	p_lcd_reg->lcd_control |= LCD_CONTROL_GO;
496 
497 	return 0;
498 }
499 
500 
au1100fb_init(void)501 int __init au1100fb_init(void)
502 {
503 	uint32 sys_clksrc;
504 	unsigned long page;
505 
506 	/*
507 	* Get the panel information/display mode and update the registry
508 	*/
509 	p_lcd = &panels[my_lcd_index];
510 
511 	switch (p_lcd->mode_control & LCD_CONTROL_SM)
512 	{
513 		case LCD_CONTROL_SM_0:
514 		case LCD_CONTROL_SM_180:
515 		p_lcd->xres =
516 			(p_lcd->mode_horztiming & LCD_HORZTIMING_PPL) + 1;
517 		p_lcd->yres =
518 			(p_lcd->mode_verttiming & LCD_VERTTIMING_LPP) + 1;
519 			break;
520 		case LCD_CONTROL_SM_90:
521 		case LCD_CONTROL_SM_270:
522 		p_lcd->yres =
523 			(p_lcd->mode_horztiming & LCD_HORZTIMING_PPL) + 1;
524 		p_lcd->xres =
525 			(p_lcd->mode_verttiming & LCD_VERTTIMING_LPP) + 1;
526 			break;
527 	}
528 
529 	/*
530 	 * Panel dimensions x bpp must be divisible by 32
531 	 */
532 	if (((p_lcd->yres * p_lcd->bpp) % 32) != 0)
533 		printk("VERT %% 32\n");
534 	if (((p_lcd->xres * p_lcd->bpp) % 32) != 0)
535 		printk("HORZ %% 32\n");
536 
537 	/*
538 	 * Allocate LCD framebuffer from system memory
539 	 */
540 	fb_info.fb_size = (p_lcd->xres * p_lcd->yres * p_lcd->bpp) / 8;
541 
542 	current_par.var.xres = p_lcd->xres;
543 	current_par.var.xres_virtual = p_lcd->xres;
544 	current_par.var.yres = p_lcd->yres;
545 	current_par.var.yres_virtual = p_lcd->yres;
546 	current_par.var.bits_per_pixel = p_lcd->bpp;
547 
548 	/* FIX!!! only works for 8/16 bpp */
549 	current_par.line_length = p_lcd->xres * p_lcd->bpp / 8; /* in bytes */
550 	fb_info.fb_virt_start = (unsigned long )
551 		__get_free_pages(GFP_ATOMIC | GFP_DMA,
552 				get_order(fb_info.fb_size + 0x1000));
553 	if (!fb_info.fb_virt_start) {
554 		printk("Unable to allocate fb memory\n");
555 		return -ENOMEM;
556 	}
557 	fb_info.fb_phys = virt_to_bus((void *)fb_info.fb_virt_start);
558 
559 	/*
560 	 * Set page reserved so that mmap will work. This is necessary
561 	 * since we'll be remapping normal memory.
562 	 */
563 	for (page = fb_info.fb_virt_start;
564 	     page < PAGE_ALIGN(fb_info.fb_virt_start + fb_info.fb_size);
565 	     page += PAGE_SIZE) {
566 		SetPageReserved(virt_to_page(page));
567 	}
568 
569 	memset((void *)fb_info.fb_virt_start, 0, fb_info.fb_size);
570 
571 	/* set freqctrl now to allow more time to stabilize */
572 	/* zero-out out LCD bits */
573 	sys_clksrc = au_readl(SYS_CLKSRC) & ~0x000003e0;
574 	sys_clksrc |= p_lcd->mode_toyclksrc;
575 	au_writel(sys_clksrc, SYS_CLKSRC);
576 
577 	/* FIXME add check to make sure auxpll is what is expected! */
578 	au1100_setmode();
579 
580 	fb_info.gen.parsize = sizeof(struct au1100fb_par);
581 	fb_info.gen.fbhw = &au1100_switch;
582 
583 	strcpy(fb_info.gen.info.modename, "Au1100 LCD");
584 	fb_info.gen.info.changevar = NULL;
585 	fb_info.gen.info.node = -1;
586 
587 	fb_info.gen.info.fbops = &au1100fb_ops;
588 	fb_info.gen.info.disp = &disp;
589 	fb_info.gen.info.switch_con = &fbgen_switch;
590 	fb_info.gen.info.updatevar = &fbgen_update_var;
591 	fb_info.gen.info.blank = &fbgen_blank;
592 	fb_info.gen.info.flags = FBINFO_FLAG_DEFAULT;
593 
594 	/* This should give a reasonable default video mode */
595 	fbgen_get_var(&disp.var, -1, &fb_info.gen.info);
596 	fbgen_do_set_var(&disp.var, 1, &fb_info.gen);
597 	fbgen_set_disp(-1, &fb_info.gen);
598 	fbgen_install_cmap(0, &fb_info.gen);
599 	if (register_framebuffer(&fb_info.gen.info) < 0)
600 		return -EINVAL;
601 	printk(KERN_INFO "fb%d: %s frame buffer device\n",
602 			GET_FB_IDX(fb_info.gen.info.node),
603 			fb_info.gen.info.modename);
604 
605 	/* uncomment this if your driver cannot be unloaded */
606 	/* MOD_INC_USE_COUNT; */
607 	return 0;
608 }
609 
610 
au1100fb_cleanup(struct fb_info * info)611 void au1100fb_cleanup(struct fb_info *info)
612 {
613 	unregister_framebuffer(info);
614 }
615 
616 
au1100fb_setup(char * options,int * ints)617 void au1100fb_setup(char *options, int *ints)
618 {
619 	char* this_opt;
620 	int i;
621 	int num_panels = sizeof(panels)/sizeof(struct known_lcd_panels);
622 
623 
624 	if (!options || !*options)
625 		return;
626 
627 	for(this_opt=strtok(options, ","); this_opt;
628 	    this_opt=strtok(NULL, ",")) {
629 		if (!strncmp(this_opt, "panel:", 6)) {
630 #if defined(CONFIG_MIPS_PB1100) || defined(CONFIG_MIPS_DB1100)
631 			/* Read Pb1100 Switch S10 ? */
632 			if (!strncmp(this_opt+6, "s10", 3))
633 			{
634 				int panel;
635 				panel = *(volatile int *)0xAE000008; /* BCSR SWITCHES */
636 				panel >>= 8;
637 				panel &= 0x0F;
638 				if (panel >= num_panels) panel = 0;
639 				my_lcd_index = panel;
640 			}
641 			else
642 #endif
643 			/* Get the panel name, everything else if fixed */
644 			for (i=0; i<num_panels; i++) {
645 				if (!strncmp(this_opt+6, panels[i].panel_name,
646 							strlen(this_opt))) {
647 					my_lcd_index = i;
648 					break;
649 				}
650 			}
651 		}
652 		else if (!strncmp(this_opt, "nohwcursor", 10)) {
653 			printk("nohwcursor\n");
654 			fb_info.nohwcursor = 1;
655 		}
656 	}
657 
658 	printk("au1100fb: Panel %d %s\n", my_lcd_index,
659 		panels[my_lcd_index].panel_name);
660 }
661 
662 
663 
664 #ifdef MODULE
665 MODULE_LICENSE("GPL");
init_module(void)666 int init_module(void)
667 {
668 	return au1100fb_init();
669 }
670 
cleanup_module(void)671 void cleanup_module(void)
672 {
673 	au1100fb_cleanup(void);
674 }
675 
676 MODULE_AUTHOR("Pete Popov <ppopov@mvista.com>");
677 MODULE_DESCRIPTION("Au1100 LCD framebuffer device driver");
678 #endif /* MODULE */
679