2 * linux/drivers/mq1100fb.c
4 * Copyright © 2003 Keith Packard
6 * This file is subject to the terms and conditions of the GNU General Public
7 * License. See the file COPYING in the main directory of this archive for
10 * MediaQ 1100/32/68/78/88 LCD Controller Frame Buffer Driver
12 * Please direct your questions and comments on this driver to the following
19 * 2003-12-06: Andrew Zabolotny <zap@homelink.ru>
20 * - Modified to use the MediaQ SoC base driver to allow
21 * sharing of other MediaQ subdevices with other drivers.
22 * 2003-05-18: <keithp@keithp.com>
23 * - Ported from PCI development board to ARM H5400
26 #include <linux/module.h>
27 #include <linux/kernel.h>
28 #include <linux/errno.h>
29 #include <linux/string.h>
31 #include <linux/init.h>
32 #include <linux/platform_device.h>
33 #include <linux/soc-old.h>
34 #include <linux/notifier.h>
35 #include <../drivers/soc/mq11xx.h>
37 #include "console/fbcon.h"
40 # define debug(s, args...) printk (KERN_INFO s, ##args)
42 # define debug(s, args...)
44 #define debug_func(s, args...) debug ("%s: " s, __FUNCTION__, ##args)
47 #define MQ_Rotate_90 2
48 #define MQ_Rotate_180 4
49 #define MQ_Rotate_270 8
51 #define MQ_Reflect_X 16
52 #define MQ_Reflect_Y 32
55 unsigned char red
,green
,blue
,transp
;
58 struct mq1100fb_info
{
59 /* Framebuffer info */
61 /* The link to the base SoC driver */
62 struct mediaq11xx_base
*base
;
64 struct mq1100fb_rgb palette
[256];
66 struct notifier_block notify
;
67 /* Framebuffer offset inside MediaQ RAM */
69 /* The pseudo-palette */
71 /* Device instance number (0-7) */
73 /* Combination of MQ_Rotate_XXX bits */
75 /* 1 if device is active, 0 if poweroff */
77 /* 1 if initialization is complete, 0 while it is deferred */
78 unsigned initcomplete
:1;
79 /* 1 if power is enabled to the MEDIAQ_11XX_FB_DEVICE_ID subdevice */
82 #define to_mq1100fb_info(n) container_of(n, struct mq1100fb_info, fb)
84 #define MAX_MQFB_INSTANCES 8
86 static int ppm
[MAX_MQFB_INSTANCES
];
87 module_param_array(ppm
, int, MAX_MQFB_INSTANCES
, S_IRUGO
);
88 MODULE_PARM_DESC(ppm
, "LCD pixel density in pixels per meter");
90 /* ------------------- chipset specific functions -------------------------- */
92 static void mq1100fb_power (struct mq1100fb_info
*info
, int val
)
94 debug_func ("val:%d\n", val
);
96 info
->active
= (val
== 0) ? 1 : 0;
98 if (info
->active
!= info
->poweron
) {
99 info
->poweron
= info
->active
;
100 info
->base
->set_power (info
->base
, MEDIAQ_11XX_FB_DEVICE_ID
,
105 static void mq1100fb_update_screeninfo (struct mq1100fb_info
*info
, int ppm
)
107 u32 control
= info
->base
->regs
->GC
.control
;
108 u32 horizontal_width
= info
->base
->regs
->GC
.horizontal_window
;
109 u32 vertical_height
= info
->base
->regs
->GC
.vertical_window
;
110 u32 window_stride
= info
->base
->regs
->GC
.window_stride
;
113 debug_func ("ppm:%d\n", ppm
);
115 info
->fb
.var
.grayscale
= 0;
117 switch (control
& MQ_GC_DEPTH
) {
118 case MQ_GC_DEPTH_PSEUDO_1
:
119 info
->fb
.var
.bits_per_pixel
= 1;
120 info
->fb
.fix
.visual
= FB_VISUAL_MONO01
;
122 case MQ_GC_DEPTH_PSEUDO_2
:
123 info
->fb
.var
.bits_per_pixel
= 2;
124 info
->fb
.fix
.visual
= FB_VISUAL_PSEUDOCOLOR
;
126 case MQ_GC_DEPTH_PSEUDO_4
:
127 info
->fb
.var
.bits_per_pixel
= 4;
128 info
->fb
.fix
.visual
= FB_VISUAL_PSEUDOCOLOR
;
130 case MQ_GC_DEPTH_PSEUDO_8
:
131 info
->fb
.var
.bits_per_pixel
= 8;
132 info
->fb
.fix
.visual
= FB_VISUAL_PSEUDOCOLOR
;
134 case MQ_GC_DEPTH_GRAY_1
:
135 info
->fb
.var
.bits_per_pixel
= 1;
136 info
->fb
.var
.grayscale
= 1;
137 info
->fb
.fix
.visual
= FB_VISUAL_MONO01
;
139 case MQ_GC_DEPTH_GRAY_2
:
140 info
->fb
.var
.bits_per_pixel
= 2;
141 info
->fb
.var
.grayscale
= 1;
142 info
->fb
.fix
.visual
= FB_VISUAL_PSEUDOCOLOR
;
144 case MQ_GC_DEPTH_GRAY_4
:
145 info
->fb
.var
.bits_per_pixel
= 4;
146 info
->fb
.var
.grayscale
= 1;
147 info
->fb
.fix
.visual
= FB_VISUAL_PSEUDOCOLOR
;
149 case MQ_GC_DEPTH_GRAY_8
:
150 info
->fb
.var
.bits_per_pixel
= 8;
151 info
->fb
.var
.grayscale
= 1;
152 info
->fb
.fix
.visual
= FB_VISUAL_PSEUDOCOLOR
;
154 case MQ_GC_DEPTH_TRUE_16
:
155 info
->fb
.var
.bits_per_pixel
= 16;
156 info
->fb
.fix
.visual
= FB_VISUAL_TRUECOLOR
;
160 info
->fb
.var
.xres
= info
->fb
.var
.xres_virtual
=
161 ((horizontal_width
& MQ_GC_HORIZONTAL_WINDOW_WIDTH
) >> 16) + 1;
162 info
->fb
.var
.yres
= info
->fb
.var
.yres_virtual
=
163 ((vertical_height
& MQ_GC_VERTICAL_WINDOW_HEIGHT
) >> 16) + 1;
164 stride
= (short)window_stride
;
165 info
->fb
.fix
.line_length
= (stride
< 0) ? -stride
: stride
;
167 switch (control
& (MQ_GC_X_SCANNING_DIRECTION
|MQ_GC_LINE_SCANNING_DIRECTION
)) {
170 info
->rotation
= MQ_Rotate_0
; /* 000 */
172 info
->rotation
|= MQ_Rotate_0
| MQ_Reflect_Y
; /* 010 */
174 case MQ_GC_X_SCANNING_DIRECTION
:
176 info
->rotation
= MQ_Rotate_0
| MQ_Reflect_X
; /* 001 */
178 info
->rotation
= MQ_Rotate_180
; /* 011 */
180 case MQ_GC_LINE_SCANNING_DIRECTION
:
182 info
->rotation
= MQ_Rotate_90
| MQ_Reflect_X
; /* 100 */
184 info
->rotation
= MQ_Rotate_90
; /* 110 */
186 case MQ_GC_LINE_SCANNING_DIRECTION
|MQ_GC_X_SCANNING_DIRECTION
:
188 info
->rotation
= MQ_Rotate_270
; /* 101 */
190 info
->rotation
= MQ_Rotate_270
| MQ_Reflect_X
; /* 111 */
194 info
->fb
.var
.width
= info
->fb
.var
.xres
* 1000 / ppm
;
195 info
->fb
.var
.height
= info
->fb
.var
.yres
* 1000 / ppm
;
199 * mq1100fb_check_var():
200 * Round up in the following order: bits_per_pixel, xres,
201 * yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale,
202 * bitfields, horizontal timing, vertical timing.
204 static int mq1100fb_check_var(struct fb_var_screeninfo
*var
,
205 struct fb_info
*fbinfo
)
209 switch (var
->bits_per_pixel
) {
212 var
->green
.offset
= 0;
213 var
->blue
.offset
= 0;
215 var
->green
.length
= 6;
216 var
->blue
.length
= 6;
219 var
->red
.offset
= 11;
220 var
->green
.offset
= 5;
221 var
->blue
.offset
= 0;
223 var
->green
.length
= 6;
224 var
->blue
.length
= 5;
233 static int mq1100fb_set_par(struct fb_info
*fbinfo
)
235 struct mq1100fb_info
*info
= to_mq1100fb_info(fbinfo
);
240 /* Enable the LCD controller */
241 mq1100fb_power (info
, 0);
246 static int mq1100fb_setcolreg(unsigned regno
, unsigned red
, unsigned green
,
247 unsigned blue
, unsigned transp
,
248 struct fb_info
*fbinfo
)
250 struct mq1100fb_info
*info
= to_mq1100fb_info(fbinfo
);
253 debug_func ("color:%d rgba:%d/%d/%d/%d\n",
254 regno
, red
>>8, green
>>8, blue
>>8, transp
>>8);
256 bpp
= info
->fb
.var
.bits_per_pixel
;
257 m
= (bpp
<= 8) ? (1 << bpp
) : 256;
259 debug ("regno %d out of range (max %d)\n", regno
, m
);
263 info
->palette
[regno
].red
= red
;
264 info
->palette
[regno
].green
= green
;
265 info
->palette
[regno
].blue
= blue
;
266 info
->palette
[regno
].transp
= transp
;
273 info
->pseudo_pal
[regno
] = ((red
& 0xF800) |
274 ((green
& 0xFC00) >> 5) |
275 ((blue
& 0xF800) >> 11));
282 static int mq1100fb_pan_display(struct fb_var_screeninfo
*var
,
283 struct fb_info
*fbinfo
)
287 * Pan (or wrap, depending on the `vmode' field) the display using the
288 * `xoffset' and `yoffset' fields of the `var' structure.
289 * If the values don't fit, return -EINVAL.
296 static void mq1100fb_rotate (struct fb_info
*fbinfo
, int angle
)
298 /* For now it is not implemented but mq1100 fully
299 * supports display rotation.
304 static int mq1100fb_blank(int blank_mode
, struct fb_info
*fbinfo
)
306 struct mq1100fb_info
*info
= to_mq1100fb_info(fbinfo
);
308 debug_func ("blank_mode:%d\n", blank_mode
);
311 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
312 * then the caller blanks by setting the CLUT (Color Look Up Table) to all
313 * black. Return 0 if blanking succeeded, != 0 if un-/blanking failed due
314 * to e.g. a video mode which doesn't support it. Implements VESA suspend
315 * and powerdown modes on hardware that supports disabling hsync/vsync:
316 * blank_mode == 2: suspend vsync
317 * blank_mode == 3: suspend hsync
318 * blank_mode == 4: powerdown
321 mq1100fb_power (info
, blank_mode
);
326 /* ------------ Interfaces to hardware functions ------------ */
328 static struct fb_ops mq1100fb_ops
= {
329 .owner
= THIS_MODULE
,
330 .fb_check_var
= mq1100fb_check_var
,
331 .fb_set_par
= mq1100fb_set_par
,
332 .fb_setcolreg
= mq1100fb_setcolreg
,
333 .fb_fillrect
= cfb_fillrect
,
334 .fb_copyarea
= cfb_copyarea
,
335 .fb_imageblit
= cfb_imageblit
,
336 .fb_blank
= mq1100fb_blank
,
337 .fb_pan_display
= mq1100fb_pan_display
,
338 .fb_rotate
= mq1100fb_rotate
,
339 #ifdef CONFIG_FB_SOFT_CURSOR
340 .fb_cursor
= soft_cursor
344 static struct fb_bitfield def_rgb
[3] = {
345 { 11, 5, 0 }, /* red */
346 { 5, 6, 0 }, /* green */
347 { 0, 5, 0 }, /* blue */
350 static int mq1100fb_finish_init (struct mq1100fb_info
*info
)
353 int inst_ppm
= ppm
[info
->inst
];
359 info
->fb
.var
.red
= def_rgb
[0];
360 info
->fb
.var
.green
= def_rgb
[1];
361 info
->fb
.var
.blue
= def_rgb
[2];
362 info
->fb
.var
.activate
= FB_ACTIVATE_NOW
;
364 strcpy (info
->fb
.fix
.id
, "mq1100");
365 info
->fb
.fix
.type
= FB_TYPE_PACKED_PIXELS
;
366 info
->fb
.fix
.type_aux
= 0;
367 info
->fb
.fix
.xpanstep
= info
->fb
.fix
.ypanstep
= 0;
368 info
->fb
.fix
.ywrapstep
= 0;
369 info
->fb
.fix
.accel
= FB_ACCEL_NONE
;
371 info
->fb
.screen_base
= info
->base
->gfxram
;
372 info
->fb
.pseudo_palette
= &info
->pseudo_pal
;
373 info
->fb
.fbops
= &mq1100fb_ops
;
375 mq1100fb_update_screeninfo (info
, inst_ppm
);
377 info
->fb_size
= info
->fb
.fix
.line_length
* info
->fb
.var
.yres_virtual
;
378 info
->fb_addr
= info
->base
->alloc (info
->base
, info
->fb_size
, 1);
380 if (info
->fb_addr
== (u32
)-1) {
382 printk (KERN_ERR
"Cannot allocate %u bytes in MediaQ internal RAM for framebuffer\n",
388 //info->fb.fix.mmio_start = info->base->paddr_regs;
389 //info->fb.fix.mmio_len = MQ11xx_REG_SIZE;
390 info
->fb
.fix
.smem_start
= info
->base
->paddr_gfxram
+ info
->fb_addr
;
391 info
->fb
.fix
.smem_len
= info
->fb_size
;
393 /* Program framebuffer start address */
394 info
->base
->regs
->GC
.window_start_address
= info
->fb_addr
;
396 if ((err
= register_framebuffer(&info
->fb
)) < 0) {
397 printk (KERN_ERR
"Could not register mq1100 framebuffer\n");
398 info
->base
->free (info
->base
, info
->fb_addr
, info
->fb_size
);
402 info
->initcomplete
= 1;
404 /* Clear the screen in the case we don't use fbcon */
405 memset (info
->base
->gfxram
+ info
->fb_addr
, 0, info
->fb_size
);
407 debug ("frame buffer device %dx%d-%dbpp\n",
408 info
->fb
.var
.xres
, info
->fb
.var
.yres
, info
->fb
.var
.bits_per_pixel
);
412 static int mq1100fb_probe (struct device
*dev
, struct mediaq11xx_base
*base
)
414 struct platform_device
*pdev
= to_platform_device(dev
);
415 struct mq1100fb_info
*info
;
417 info
= kmalloc (sizeof (struct mq1100fb_info
), GFP_KERNEL
);
418 memset (info
, 0, sizeof (struct mq1100fb_info
));
420 info
->inst
= pdev
->id
;
422 dev_set_drvdata (dev
, info
);
424 /* Turn on the power to the MediaQ chip */
425 mq1100fb_power (info
, 0);
426 mq1100fb_finish_init (info
);
431 static int mq1100fb_platform_probe (struct device
*dev
)
434 return mq1100fb_probe (dev
, (struct mediaq11xx_base
*)dev
->platform_data
);
437 static int mq1100fb_platform_remove (struct device
*dev
)
439 struct mq1100fb_info
*info
=
440 (struct mq1100fb_info
*)dev_get_drvdata (dev
);
445 mq1100fb_power (info
, 4);
446 unregister_framebuffer (&info
->fb
);
447 info
->base
->free (info
->base
, info
->fb_addr
, info
->fb_size
);
453 static int mq1100fb_platform_suspend (struct device
*dev
, pm_message_t state
)
455 struct mq1100fb_info
*info
=
456 (struct mq1100fb_info
*)dev_get_drvdata (dev
);
460 mq1100fb_power (info
, 4);
465 static int mq1100fb_platform_resume (struct device
*dev
)
467 struct mq1100fb_info
*info
=
468 (struct mq1100fb_info
*)dev_get_drvdata (dev
);
472 mq1100fb_power (info
, 0);
477 /* We need the framebuffer subdevice */
478 struct device_driver mq1100fb_device_driver
= {
480 .bus
= &platform_bus_type
,
481 .probe
= mq1100fb_platform_probe
,
482 .remove
= mq1100fb_platform_remove
,
483 .suspend
= mq1100fb_platform_suspend
,
484 .resume
= mq1100fb_platform_resume
487 static int __init
mq1100fb_init(void)
489 return driver_register (&mq1100fb_device_driver
);
492 static void __exit
mq1100fb_exit(void)
494 driver_unregister (&mq1100fb_device_driver
);
497 module_init(mq1100fb_init
);
498 module_exit(mq1100fb_exit
);
500 MODULE_AUTHOR("Keith Packard <keithp@keithp.com>");
501 MODULE_DESCRIPTION("Framebuffer driver for MediaQ 1100/32/68/78/88 chips");
502 MODULE_LICENSE("GPL");