2 * BRIEF MODULE DESCRIPTION
5 * Copyright 2002 MontaVista Software
6 * Author: MontaVista Software, Inc.
7 * ppopov@mvista.com or source@mvista.com
9 * Copyright 2002 Alchemy Semiconductor
10 * Author: Alchemy Semiconductor
13 * linux/drivers/video/skeletonfb.c -- Skeleton for a frame buffer device
14 * Created 28 Dec 1997 by Geert Uytterhoeven
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.
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.
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.
37 #include <linux/module.h>
38 #include <linux/kernel.h>
39 #include <linux/errno.h>
40 #include <linux/string.h>
42 #include <linux/tty.h>
43 #include <linux/slab.h>
44 #include <linux/delay.h>
46 #include <linux/init.h>
47 #include <linux/pci.h>
49 #include <asm/au1000.h>
50 #include <asm/pb1100.h>
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>
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.
64 #if defined(CONFIG_MIPS_PB1100) || defined(CONFIG_MIPS_DB1100) || defined(CONFIG_MIPS_HYDROGEN3)
66 error Unknown Au1100 board
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
;
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
;
83 struct { unsigned red
, green
, blue
, pad
; } palette
[256];
85 #if defined(FBCON_HAS_CFB16)
92 struct fb_var_screeninfo var
;
94 int line_length
; // in bytes
95 int cmap_len
; // color-map length
99 static struct au1100fb_info fb_info
;
100 static struct au1100fb_par current_par
;
101 static struct display disp
;
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
);
111 void au1100_nocursor(struct display
*p
, int mode
, int xx
, int yy
){};
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
,
125 static void au1100_detect(void)
128 * This function should detect the current video mode settings
129 * and store it as the default video mode
133 * Yeh, well, we're not going to change any settings so we're
134 * always stuck with the default ...
139 static int au1100_encode_fix(struct fb_fix_screeninfo
*fix
,
140 const void *_par
, struct fb_info_gen
*_info
)
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
;
146 memset(fix
, 0, sizeof(struct fb_fix_screeninfo
));
148 fix
->smem_start
= info
->fb_phys
;
149 fix
->smem_len
= info
->fb_size
;
150 fix
->type
= FB_TYPE_PACKED_PIXELS
;
152 fix
->visual
= (var
->bits_per_pixel
== 8) ?
153 FB_VISUAL_PSEUDOCOLOR
: FB_VISUAL_TRUECOLOR
;
157 fix
->line_length
= current_par
.line_length
;
161 static void set_color_bitfields(struct fb_var_screeninfo
*var
)
163 switch (var
->bits_per_pixel
) {
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;
174 case 16: /* RGB 565 */
175 var
->red
.offset
= 11;
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;
186 var
->red
.msb_right
= 0;
187 var
->green
.msb_right
= 0;
188 var
->blue
.msb_right
= 0;
189 var
->transp
.msb_right
= 0;
192 static int au1100_decode_var(const struct fb_var_screeninfo
*var
,
193 void *_par
, struct fb_info_gen
*_info
)
196 struct au1100fb_par
*par
= (struct au1100fb_par
*)_par
;
199 * Don't allow setting any of these yet: xres and yres don't
200 * make sense for LCD panels.
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
) {
208 if(var
->bits_per_pixel
!= p_lcd
->bpp
) {
212 memset(par
, 0, sizeof(struct au1100fb_par
));
216 switch (var
->bits_per_pixel
) {
218 par
->var
.bits_per_pixel
= 8;
221 par
->var
.bits_per_pixel
= 16;
224 printk("color depth %d bpp not supported\n",
225 var
->bits_per_pixel
);
229 set_color_bitfields(&par
->var
);
230 par
->cmap_len
= (par
->var
.bits_per_pixel
== 8) ? 256 : 16;
234 static int au1100_encode_var(struct fb_var_screeninfo
*var
,
235 const void *par
, struct fb_info_gen
*_info
)
238 *var
= ((struct au1100fb_par
*)par
)->var
;
243 au1100_get_par(void *_par
, struct fb_info_gen
*_info
)
245 *(struct au1100fb_par
*)_par
= current_par
;
248 static void au1100_set_par(const void *par
, struct fb_info_gen
*info
)
250 /* nothing to do: we don't change any settings */
253 static int au1100_getcolreg(unsigned regno
, unsigned *red
, unsigned *green
,
254 unsigned *blue
, unsigned *transp
,
255 struct fb_info
*info
)
258 struct au1100fb_info
* i
= (struct au1100fb_info
*)info
;
263 *red
= i
->palette
[regno
].red
;
264 *green
= i
->palette
[regno
].green
;
265 *blue
= i
->palette
[regno
].blue
;
271 static int au1100_setcolreg(unsigned regno
, unsigned red
, unsigned green
,
272 unsigned blue
, unsigned transp
,
273 struct fb_info
*info
)
275 struct au1100fb_info
* i
= (struct au1100fb_info
*)info
;
281 i
->palette
[regno
].red
= red
;
282 i
->palette
[regno
].green
= green
;
283 i
->palette
[regno
].blue
= blue
;
286 #ifdef FBCON_HAS_CFB8
291 p_lcd_reg
->lcd_pallettebase
[regno
] = (blue
&0x1f) |
292 ((green
&0x3f)<<5) | ((red
&0x1f)<<11);
295 #ifdef FBCON_HAS_CFB16
297 i
->fbcon_cmap16
[regno
] =
298 ((red
& 0xf800) >> 0) |
299 ((green
& 0xfc00) >> 5) |
300 ((blue
& 0xf800) >> 11);
311 static int au1100_blank(int blank_mode
, struct fb_info_gen
*_info
)
314 switch (blank_mode
) {
315 case VESA_NO_BLANKING
:
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
,
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);
332 case VESA_VSYNC_SUSPEND
:
333 case VESA_HSYNC_SUSPEND
:
336 //printk("turn off panel\n");
337 #ifdef CONFIG_MIPS_PB1100
338 au_writew(au_readw(PB1100_G_CONTROL
) & ~p_lcd
->mode_backlight
,
340 p_lcd_reg
->lcd_control
&= ~LCD_CONTROL_GO
;
351 static void au1100_set_disp(const void *unused
, struct display
*disp
,
352 struct fb_info_gen
*info
)
354 disp
->screen_base
= (char *)fb_info
.fb_virt_start
;
356 switch (disp
->var
.bits_per_pixel
) {
357 #ifdef FBCON_HAS_CFB8
359 disp
->dispsw
= &fbcon_cfb8
;
360 if (fb_info
.nohwcursor
)
361 fbcon_cfb8
.cursor
= au1100_nocursor
;
364 #ifdef FBCON_HAS_CFB16
366 disp
->dispsw
= &fbcon_cfb16
;
367 disp
->dispsw_data
= fb_info
.fbcon_cmap16
;
368 if (fb_info
.nohwcursor
)
369 fbcon_cfb16
.cursor
= au1100_nocursor
;
373 disp
->dispsw
= &fbcon_dummy
;
374 disp
->dispsw_data
= NULL
;
380 au1100fb_mmap(struct fb_info
*_fb
,
382 struct vm_area_struct
*vma
)
385 unsigned long start
=0, off
;
386 struct au1100fb_info
*fb
= (struct au1100fb_info
*)_fb
;
388 if (vma
->vm_pgoff
> (~0UL >> PAGE_SHIFT
)) {
392 start
= fb_info
.fb_phys
& PAGE_MASK
;
393 len
= PAGE_ALIGN((start
& ~PAGE_MASK
) + fb_info
.fb_size
);
395 off
= vma
->vm_pgoff
<< PAGE_SHIFT
;
397 if ((vma
->vm_end
- vma
->vm_start
+ off
) > len
) {
402 vma
->vm_pgoff
= off
>> PAGE_SHIFT
;
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
408 /* This is an IO map - tell maydump to skip this VMA */
409 vma
->vm_flags
|= VM_IO
;
411 if (io_remap_pfn_range(vma
, vma
->vm_start
, off
>> PAGE_SHIFT
,
412 vma
->vm_end
- vma
->vm_start
,
413 vma
->vm_page_prot
)) {
421 int au1100_pan_display(const struct fb_var_screeninfo
*var
,
422 struct fb_info_gen
*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
)
430 /* nothing to do yet */
434 static struct fbgen_hwswitch au1100_switch
= {
449 int au1100_setmode(void)
453 /* FIXME Need to accomodate for swivel mode and 12bpp, <8bpp*/
454 switch (p_lcd
->mode_control
& LCD_CONTROL_SM
)
456 case LCD_CONTROL_SM_0
:
457 case LCD_CONTROL_SM_180
:
458 words
= (p_lcd
->xres
* p_lcd
->yres
* p_lcd
->bpp
) / 32;
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;
466 printk("mode_control reg not initialized\n");
471 * Setup LCD controller
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
;
484 #ifdef CONFIG_MIPS_PB1100
485 au_writew(au_readw(PB1100_G_CONTROL
) | p_lcd
->mode_backlight
,
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);
495 p_lcd_reg
->lcd_control
|= LCD_CONTROL_GO
;
501 int __init
au1100fb_init(void)
507 * Get the panel information/display mode and update the registry
509 p_lcd
= &panels
[my_lcd_index
];
511 switch (p_lcd
->mode_control
& LCD_CONTROL_SM
)
513 case LCD_CONTROL_SM_0
:
514 case LCD_CONTROL_SM_180
:
516 (p_lcd
->mode_horztiming
& LCD_HORZTIMING_PPL
) + 1;
518 (p_lcd
->mode_verttiming
& LCD_VERTTIMING_LPP
) + 1;
520 case LCD_CONTROL_SM_90
:
521 case LCD_CONTROL_SM_270
:
523 (p_lcd
->mode_horztiming
& LCD_HORZTIMING_PPL
) + 1;
525 (p_lcd
->mode_verttiming
& LCD_VERTTIMING_LPP
) + 1;
530 * Panel dimensions x bpp must be divisible by 32
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");
538 * Allocate LCD framebuffer from system memory
540 fb_info
.fb_size
= (p_lcd
->xres
* p_lcd
->yres
* p_lcd
->bpp
) / 8;
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
;
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");
557 fb_info
.fb_phys
= virt_to_bus((void *)fb_info
.fb_virt_start
);
560 * Set page reserved so that mmap will work. This is necessary
561 * since we'll be remapping normal memory.
563 for (page
= fb_info
.fb_virt_start
;
564 page
< PAGE_ALIGN(fb_info
.fb_virt_start
+ fb_info
.fb_size
);
566 SetPageReserved(virt_to_page(page
));
569 memset((void *)fb_info
.fb_virt_start
, 0, fb_info
.fb_size
);
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
);
577 /* FIXME add check to make sure auxpll is what is expected! */
580 fb_info
.gen
.parsize
= sizeof(struct au1100fb_par
);
581 fb_info
.gen
.fbhw
= &au1100_switch
;
583 strcpy(fb_info
.gen
.info
.modename
, "Au1100 LCD");
584 fb_info
.gen
.info
.changevar
= NULL
;
585 fb_info
.gen
.info
.node
= -1;
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
;
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)
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
);
609 void au1100fb_cleanup(struct fb_info
*info
)
611 unregister_framebuffer(info
);
615 void au1100fb_setup(char *options
, int *ints
)
619 int num_panels
= sizeof(panels
)/sizeof(struct known_lcd_panels
);
622 if (!options
|| !*options
)
625 for(this_opt
=strtok(options
, ","); this_opt
;
626 this_opt
=strtok(NULL
, ",")) {
627 if (!strncmp(this_opt
, "panel:", 6)) {
628 #if defined(CONFIG_MIPS_PB1100) || defined(CONFIG_MIPS_DB1100)
629 /* Read Pb1100 Switch S10 ? */
630 if (!strncmp(this_opt
+6, "s10", 3))
633 panel
= *(volatile int *)0xAE000008; /* BCSR SWITCHES */
636 if (panel
>= num_panels
) panel
= 0;
637 my_lcd_index
= panel
;
641 /* Get the panel name, everything else if fixed */
642 for (i
=0; i
<num_panels
; i
++) {
643 if (!strncmp(this_opt
+6, panels
[i
].panel_name
,
650 else if (!strncmp(this_opt
, "nohwcursor", 10)) {
651 printk("nohwcursor\n");
652 fb_info
.nohwcursor
= 1;
656 printk("au1100fb: Panel %d %s\n", my_lcd_index
,
657 panels
[my_lcd_index
].panel_name
);
663 MODULE_LICENSE("GPL");
664 int init_module(void)
666 return au1100fb_init();
669 void cleanup_module(void)
671 au1100fb_cleanup(void);
674 MODULE_AUTHOR("Pete Popov <ppopov@mvista.com>");
675 MODULE_DESCRIPTION("Au1100 LCD framebuffer device driver");