2 * linux/drivers/video/amba-clcd.c
4 * Copyright (C) 2001 ARM Limited, by David A Rusling
5 * Updated to 2.5, Deep Blue Solutions Ltd.
7 * This file is subject to the terms and conditions of the GNU General Public
8 * License. See the file COPYING in the main directory of this archive
11 * ARM PrimeCell PL110 Color LCD Controller
13 #include <linux/module.h>
14 #include <linux/kernel.h>
15 #include <linux/errno.h>
16 #include <linux/string.h>
17 #include <linux/slab.h>
18 #include <linux/delay.h>
20 #include <linux/init.h>
21 #include <linux/ioport.h>
22 #include <linux/list.h>
24 #include <asm/hardware/amba.h>
25 #include <asm/hardware/clock.h>
27 #include <asm/hardware/amba_clcd.h>
29 #define to_clcd(info) container_of(info, struct clcd_fb, fb)
31 /* This is limited to 16 characters when displayed by X startup */
32 static const char *clcd_name
= "CLCD FB";
35 * Unfortunately, the enable/disable functions may be called either from
36 * process or IRQ context, and we _need_ to delay. This is _not_ good.
38 static inline void clcdfb_sleep(unsigned int ms
)
47 static inline void clcdfb_set_start(struct clcd_fb
*fb
)
49 unsigned long ustart
= fb
->fb
.fix
.smem_start
;
52 ustart
+= fb
->fb
.var
.yoffset
* fb
->fb
.fix
.line_length
;
53 lstart
= ustart
+ fb
->fb
.var
.yres
* fb
->fb
.fix
.line_length
/ 2;
55 writel(ustart
, fb
->regs
+ CLCD_UBAS
);
56 writel(lstart
, fb
->regs
+ CLCD_LBAS
);
59 static void clcdfb_disable(struct clcd_fb
*fb
)
63 if (fb
->board
->disable
)
64 fb
->board
->disable(fb
);
66 val
= readl(fb
->regs
+ CLCD_CNTL
);
67 if (val
& CNTL_LCDPWR
) {
69 writel(val
, fb
->regs
+ CLCD_CNTL
);
73 if (val
& CNTL_LCDEN
) {
75 writel(val
, fb
->regs
+ CLCD_CNTL
);
79 * Disable CLCD clock source.
84 static void clcdfb_enable(struct clcd_fb
*fb
, u32 cntl
)
87 * Enable the CLCD clock source.
92 * Bring up by first enabling..
95 writel(cntl
, fb
->regs
+ CLCD_CNTL
);
100 * and now apply power.
103 writel(cntl
, fb
->regs
+ CLCD_CNTL
);
106 * finally, enable the interface.
108 if (fb
->board
->enable
)
109 fb
->board
->enable(fb
);
113 clcdfb_set_bitfields(struct clcd_fb
*fb
, struct fb_var_screeninfo
*var
)
117 memset(&var
->transp
, 0, sizeof(var
->transp
));
118 memset(&var
->red
, 0, sizeof(var
->red
));
119 memset(&var
->green
, 0, sizeof(var
->green
));
120 memset(&var
->blue
, 0, sizeof(var
->blue
));
122 switch (var
->bits_per_pixel
) {
129 var
->green
.length
= 8;
130 var
->green
.offset
= 0;
131 var
->blue
.length
= 8;
132 var
->blue
.offset
= 0;
136 var
->green
.length
= 5;
137 var
->blue
.length
= 5;
138 if (fb
->panel
->cntl
& CNTL_BGR
) {
139 var
->red
.offset
= 10;
140 var
->green
.offset
= 5;
141 var
->blue
.offset
= 0;
144 var
->green
.offset
= 5;
145 var
->blue
.offset
= 10;
149 if (fb
->panel
->cntl
& CNTL_LCDTFT
) {
151 var
->green
.length
= 8;
152 var
->blue
.length
= 8;
154 if (fb
->panel
->cntl
& CNTL_BGR
) {
155 var
->red
.offset
= 16;
156 var
->green
.offset
= 8;
157 var
->blue
.offset
= 0;
160 var
->green
.offset
= 8;
161 var
->blue
.offset
= 16;
173 static int clcdfb_check_var(struct fb_var_screeninfo
*var
, struct fb_info
*info
)
175 struct clcd_fb
*fb
= to_clcd(info
);
178 if (fb
->board
->check
)
179 ret
= fb
->board
->check(fb
, var
);
181 ret
= clcdfb_set_bitfields(fb
, var
);
186 static int clcdfb_set_par(struct fb_info
*info
)
188 struct clcd_fb
*fb
= to_clcd(info
);
189 struct clcd_regs regs
;
191 fb
->fb
.fix
.line_length
= fb
->fb
.var
.xres_virtual
*
192 fb
->fb
.var
.bits_per_pixel
/ 8;
194 if (fb
->fb
.var
.bits_per_pixel
<= 8)
195 fb
->fb
.fix
.visual
= FB_VISUAL_PSEUDOCOLOR
;
197 fb
->fb
.fix
.visual
= FB_VISUAL_TRUECOLOR
;
199 fb
->board
->decode(fb
, ®s
);
203 writel(regs
.tim0
, fb
->regs
+ CLCD_TIM0
);
204 writel(regs
.tim1
, fb
->regs
+ CLCD_TIM1
);
205 writel(regs
.tim2
, fb
->regs
+ CLCD_TIM2
);
206 writel(regs
.tim3
, fb
->regs
+ CLCD_TIM3
);
208 clcdfb_set_start(fb
);
210 clk_set_rate(fb
->clk
, (1000000000 / regs
.pixclock
) * 1000);
212 fb
->clcd_cntl
= regs
.cntl
;
214 clcdfb_enable(fb
, regs
.cntl
);
217 printk(KERN_INFO
"CLCD: Registers set to\n"
218 KERN_INFO
" %08x %08x %08x %08x\n"
219 KERN_INFO
" %08x %08x %08x %08x\n",
220 readl(fb
->regs
+ CLCD_TIM0
), readl(fb
->regs
+ CLCD_TIM1
),
221 readl(fb
->regs
+ CLCD_TIM2
), readl(fb
->regs
+ CLCD_TIM3
),
222 readl(fb
->regs
+ CLCD_UBAS
), readl(fb
->regs
+ CLCD_LBAS
),
223 readl(fb
->regs
+ CLCD_IENB
), readl(fb
->regs
+ CLCD_CNTL
));
229 static inline u32
convert_bitfield(int val
, struct fb_bitfield
*bf
)
231 unsigned int mask
= (1 << bf
->length
) - 1;
233 return (val
>> (16 - bf
->length
) & mask
) << bf
->offset
;
237 * Set a single color register. The values supplied have a 16 bit
238 * magnitude. Return != 0 for invalid regno.
241 clcdfb_setcolreg(unsigned int regno
, unsigned int red
, unsigned int green
,
242 unsigned int blue
, unsigned int transp
, struct fb_info
*info
)
244 struct clcd_fb
*fb
= to_clcd(info
);
247 fb
->cmap
[regno
] = convert_bitfield(transp
, &fb
->fb
.var
.transp
) |
248 convert_bitfield(blue
, &fb
->fb
.var
.blue
) |
249 convert_bitfield(green
, &fb
->fb
.var
.green
) |
250 convert_bitfield(red
, &fb
->fb
.var
.red
);
252 if (fb
->fb
.var
.bits_per_pixel
== 8 && regno
< 256) {
253 int hw_reg
= CLCD_PALETTE
+ ((regno
* 2) & ~3);
254 u32 val
, mask
, newval
;
256 newval
= (red
>> 11) & 0x001f;
257 newval
|= (green
>> 6) & 0x03e0;
258 newval
|= (blue
>> 1) & 0x7c00;
261 * 3.2.11: if we're configured for big endian
262 * byte order, the palette entries are swapped.
264 if (fb
->clcd_cntl
& CNTL_BEBO
)
274 val
= readl(fb
->regs
+ hw_reg
) & mask
;
275 writel(val
| newval
, fb
->regs
+ hw_reg
);
282 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
283 * then the caller blanks by setting the CLUT (Color Look Up Table) to all
284 * black. Return 0 if blanking succeeded, != 0 if un-/blanking failed due
285 * to e.g. a video mode which doesn't support it. Implements VESA suspend
286 * and powerdown modes on hardware that supports disabling hsync/vsync:
287 * blank_mode == 2: suspend vsync
288 * blank_mode == 3: suspend hsync
289 * blank_mode == 4: powerdown
291 static int clcdfb_blank(int blank_mode
, struct fb_info
*info
)
293 struct clcd_fb
*fb
= to_clcd(info
);
295 if (blank_mode
!= 0) {
298 clcdfb_enable(fb
, fb
->clcd_cntl
);
303 static struct fb_ops clcdfb_ops
= {
304 .owner
= THIS_MODULE
,
305 .fb_check_var
= clcdfb_check_var
,
306 .fb_set_par
= clcdfb_set_par
,
307 .fb_setcolreg
= clcdfb_setcolreg
,
308 .fb_blank
= clcdfb_blank
,
309 .fb_fillrect
= cfb_fillrect
,
310 .fb_copyarea
= cfb_copyarea
,
311 .fb_imageblit
= cfb_imageblit
,
312 .fb_cursor
= soft_cursor
,
315 static int clcdfb_register(struct clcd_fb
*fb
)
319 fb
->clk
= clk_get(&fb
->dev
->dev
, "CLCDCLK");
320 if (IS_ERR(fb
->clk
)) {
321 ret
= PTR_ERR(fb
->clk
);
325 ret
= clk_use(fb
->clk
);
329 fb
->fb
.fix
.mmio_start
= fb
->dev
->res
.start
;
330 fb
->fb
.fix
.mmio_len
= SZ_4K
;
332 fb
->regs
= ioremap(fb
->fb
.fix
.mmio_start
, fb
->fb
.fix
.mmio_len
);
334 printk(KERN_ERR
"CLCD: unable to remap registers\n");
339 fb
->fb
.fbops
= &clcdfb_ops
;
340 fb
->fb
.flags
= FBINFO_FLAG_DEFAULT
;
341 fb
->fb
.pseudo_palette
= fb
->cmap
;
343 strncpy(fb
->fb
.fix
.id
, clcd_name
, sizeof(fb
->fb
.fix
.id
));
344 fb
->fb
.fix
.type
= FB_TYPE_PACKED_PIXELS
;
345 fb
->fb
.fix
.type_aux
= 0;
346 fb
->fb
.fix
.xpanstep
= 0;
347 fb
->fb
.fix
.ypanstep
= 0;
348 fb
->fb
.fix
.ywrapstep
= 0;
349 fb
->fb
.fix
.accel
= FB_ACCEL_NONE
;
351 fb
->fb
.var
.xres
= fb
->panel
->mode
.xres
;
352 fb
->fb
.var
.yres
= fb
->panel
->mode
.yres
;
353 fb
->fb
.var
.xres_virtual
= fb
->panel
->mode
.xres
;
354 fb
->fb
.var
.yres_virtual
= fb
->panel
->mode
.yres
;
355 fb
->fb
.var
.bits_per_pixel
= fb
->panel
->bpp
;
356 fb
->fb
.var
.grayscale
= fb
->panel
->grayscale
;
357 fb
->fb
.var
.pixclock
= fb
->panel
->mode
.pixclock
;
358 fb
->fb
.var
.left_margin
= fb
->panel
->mode
.left_margin
;
359 fb
->fb
.var
.right_margin
= fb
->panel
->mode
.right_margin
;
360 fb
->fb
.var
.upper_margin
= fb
->panel
->mode
.upper_margin
;
361 fb
->fb
.var
.lower_margin
= fb
->panel
->mode
.lower_margin
;
362 fb
->fb
.var
.hsync_len
= fb
->panel
->mode
.hsync_len
;
363 fb
->fb
.var
.vsync_len
= fb
->panel
->mode
.vsync_len
;
364 fb
->fb
.var
.sync
= fb
->panel
->mode
.sync
;
365 fb
->fb
.var
.vmode
= fb
->panel
->mode
.vmode
;
366 fb
->fb
.var
.activate
= FB_ACTIVATE_NOW
;
367 fb
->fb
.var
.nonstd
= 0;
368 fb
->fb
.var
.height
= fb
->panel
->height
;
369 fb
->fb
.var
.width
= fb
->panel
->width
;
370 fb
->fb
.var
.accel_flags
= 0;
372 fb
->fb
.monspecs
.hfmin
= 0;
373 fb
->fb
.monspecs
.hfmax
= 100000;
374 fb
->fb
.monspecs
.vfmin
= 0;
375 fb
->fb
.monspecs
.vfmax
= 400;
376 fb
->fb
.monspecs
.dclkmin
= 1000000;
377 fb
->fb
.monspecs
.dclkmax
= 100000000;
380 * Make sure that the bitfields are set appropriately.
382 clcdfb_set_bitfields(fb
, &fb
->fb
.var
);
385 * Allocate colourmap.
387 fb_alloc_cmap(&fb
->fb
.cmap
, 256, 0);
390 * Ensure interrupts are disabled.
392 writel(0, fb
->regs
+ CLCD_IENB
);
394 fb_set_var(&fb
->fb
, &fb
->fb
.var
);
396 printk(KERN_INFO
"CLCD: %s hardware, %s display\n",
397 fb
->board
->name
, fb
->panel
->mode
.name
);
399 ret
= register_framebuffer(&fb
->fb
);
403 printk(KERN_ERR
"CLCD: cannot register framebuffer (%d)\n", ret
);
414 static int clcdfb_probe(struct amba_device
*dev
, void *id
)
416 struct clcd_board
*board
= dev
->dev
.platform_data
;
423 ret
= amba_request_regions(dev
, NULL
);
425 printk(KERN_ERR
"CLCD: unable to reserve regs region\n");
429 fb
= (struct clcd_fb
*) kmalloc(sizeof(struct clcd_fb
), GFP_KERNEL
);
431 printk(KERN_INFO
"CLCD: could not allocate new clcd_fb struct\n");
435 memset(fb
, 0, sizeof(struct clcd_fb
));
440 ret
= fb
->board
->setup(fb
);
444 ret
= clcdfb_register(fb
);
446 amba_set_drvdata(dev
, fb
);
450 fb
->board
->remove(fb
);
454 amba_release_regions(dev
);
459 static int clcdfb_remove(struct amba_device
*dev
)
461 struct clcd_fb
*fb
= amba_get_drvdata(dev
);
463 amba_set_drvdata(dev
, NULL
);
466 unregister_framebuffer(&fb
->fb
);
471 fb
->board
->remove(fb
);
475 amba_release_regions(dev
);
480 static struct amba_id clcdfb_id_table
[] = {
488 static struct amba_driver clcd_driver
= {
490 .name
= "clcd-pl110",
492 .probe
= clcdfb_probe
,
493 .remove
= clcdfb_remove
,
494 .id_table
= clcdfb_id_table
,
497 int __init
amba_clcdfb_init(void)
499 if (fb_get_options("ambafb", NULL
))
502 return amba_driver_register(&clcd_driver
);
505 module_init(amba_clcdfb_init
);
507 static void __exit
amba_clcdfb_exit(void)
509 amba_driver_unregister(&clcd_driver
);
512 module_exit(amba_clcdfb_exit
);
514 MODULE_DESCRIPTION("ARM PrimeCell PL110 CLCD core driver");
515 MODULE_LICENSE("GPL");