2 * linux/drivers/video/vt8500lcdfb.c
4 * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
6 * Based on skeletonfb.c and pxafb.c
8 * This software is licensed under the terms of the GNU General Public
9 * License version 2, as published by the Free Software Foundation, and
10 * may be copied, distributed, and modified under those terms.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
18 #include <linux/module.h>
19 #include <linux/kernel.h>
20 #include <linux/errno.h>
21 #include <linux/string.h>
23 #include <linux/slab.h>
24 #include <linux/delay.h>
26 #include <linux/init.h>
27 #include <linux/interrupt.h>
29 #include <linux/dma-mapping.h>
30 #include <linux/platform_device.h>
31 #include <linux/wait.h>
33 #include <linux/platform_data/video-vt8500lcdfb.h>
35 #include "vt8500lcdfb.h"
36 #include "wmt_ge_rops.h"
40 #include <linux/of_fdt.h>
41 #include <linux/memblock.h>
45 #define to_vt8500lcd_info(__info) container_of(__info, \
46 struct vt8500lcd_info, fb)
48 static int vt8500lcd_set_par(struct fb_info
*info
)
50 struct vt8500lcd_info
*fbi
= to_vt8500lcd_info(info
);
51 int reg_bpp
= 5; /* 16bpp */
53 unsigned long control0
;
58 if (info
->var
.bits_per_pixel
<= 8) {
60 info
->var
.red
.offset
= 0;
61 info
->var
.red
.length
= info
->var
.bits_per_pixel
;
62 info
->var
.red
.msb_right
= 0;
64 info
->var
.green
.offset
= 0;
65 info
->var
.green
.length
= info
->var
.bits_per_pixel
;
66 info
->var
.green
.msb_right
= 0;
68 info
->var
.blue
.offset
= 0;
69 info
->var
.blue
.length
= info
->var
.bits_per_pixel
;
70 info
->var
.blue
.msb_right
= 0;
72 info
->var
.transp
.offset
= 0;
73 info
->var
.transp
.length
= 0;
74 info
->var
.transp
.msb_right
= 0;
76 info
->fix
.visual
= FB_VISUAL_PSEUDOCOLOR
;
77 info
->fix
.line_length
= info
->var
.xres_virtual
/
78 (8/info
->var
.bits_per_pixel
);
81 info
->var
.transp
.offset
= 0;
82 info
->var
.transp
.length
= 0;
83 info
->var
.transp
.msb_right
= 0;
85 if (info
->var
.bits_per_pixel
== 16) {
87 info
->var
.red
.offset
= 11;
88 info
->var
.red
.length
= 5;
89 info
->var
.red
.msb_right
= 0;
90 info
->var
.green
.offset
= 5;
91 info
->var
.green
.length
= 6;
92 info
->var
.green
.msb_right
= 0;
93 info
->var
.blue
.offset
= 0;
94 info
->var
.blue
.length
= 5;
95 info
->var
.blue
.msb_right
= 0;
97 /* Equal depths per channel */
98 info
->var
.red
.offset
= info
->var
.bits_per_pixel
100 info
->var
.red
.length
= info
->var
.bits_per_pixel
/ 3;
101 info
->var
.red
.msb_right
= 0;
102 info
->var
.green
.offset
= info
->var
.bits_per_pixel
/ 3;
103 info
->var
.green
.length
= info
->var
.bits_per_pixel
/ 3;
104 info
->var
.green
.msb_right
= 0;
105 info
->var
.blue
.offset
= 0;
106 info
->var
.blue
.length
= info
->var
.bits_per_pixel
/ 3;
107 info
->var
.blue
.msb_right
= 0;
110 info
->fix
.visual
= FB_VISUAL_TRUECOLOR
;
111 info
->fix
.line_length
= info
->var
.bits_per_pixel
> 16 ?
112 info
->var
.xres_virtual
<< 2 :
113 info
->var
.xres_virtual
<< 1;
116 for (i
= 0; i
< 8; i
++) {
117 if (bpp_values
[i
] == info
->var
.bits_per_pixel
) {
123 control0
= readl(fbi
->regbase
) & ~0xf;
124 writel(0, fbi
->regbase
);
125 while (readl(fbi
->regbase
+ 0x38) & 0x10)
127 writel((((info
->var
.hsync_len
- 1) & 0x3f) << 26)
128 | ((info
->var
.left_margin
& 0xff) << 18)
129 | (((info
->var
.xres
- 1) & 0x3ff) << 8)
130 | (info
->var
.right_margin
& 0xff), fbi
->regbase
+ 0x4);
131 writel((((info
->var
.vsync_len
- 1) & 0x3f) << 26)
132 | ((info
->var
.upper_margin
& 0xff) << 18)
133 | (((info
->var
.yres
- 1) & 0x3ff) << 8)
134 | (info
->var
.lower_margin
& 0xff), fbi
->regbase
+ 0x8);
135 writel((((info
->var
.yres
- 1) & 0x400) << 2)
136 | ((info
->var
.xres
- 1) & 0x400), fbi
->regbase
+ 0x10);
137 writel(0x80000000, fbi
->regbase
+ 0x20);
138 writel(control0
| (reg_bpp
<< 1) | 0x100, fbi
->regbase
);
143 static inline u_int
chan_to_field(u_int chan
, struct fb_bitfield
*bf
)
146 chan
>>= 16 - bf
->length
;
147 return chan
<< bf
->offset
;
150 static int vt8500lcd_setcolreg(unsigned regno
, unsigned red
, unsigned green
,
151 unsigned blue
, unsigned transp
,
152 struct fb_info
*info
) {
153 struct vt8500lcd_info
*fbi
= to_vt8500lcd_info(info
);
159 if (info
->var
.grayscale
)
161 (19595 * red
+ 38470 * green
+ 7471 * blue
) >> 16;
163 switch (fbi
->fb
.fix
.visual
) {
164 case FB_VISUAL_TRUECOLOR
:
166 u32
*pal
= fbi
->fb
.pseudo_palette
;
168 val
= chan_to_field(red
, &fbi
->fb
.var
.red
);
169 val
|= chan_to_field(green
, &fbi
->fb
.var
.green
);
170 val
|= chan_to_field(blue
, &fbi
->fb
.var
.blue
);
177 case FB_VISUAL_STATIC_PSEUDOCOLOR
:
178 case FB_VISUAL_PSEUDOCOLOR
:
179 writew((red
& 0xf800)
180 | ((green
>> 5) & 0x7e0)
181 | ((blue
>> 11) & 0x1f),
182 fbi
->palette_cpu
+ sizeof(u16
) * regno
);
189 static int vt8500lcd_ioctl(struct fb_info
*info
, unsigned int cmd
,
193 struct vt8500lcd_info
*fbi
= to_vt8500lcd_info(info
);
195 if (cmd
== FBIO_WAITFORVSYNC
) {
196 /* Unmask End of Frame interrupt */
197 writel(0xffffffff ^ (1 << 3), fbi
->regbase
+ 0x3c);
198 ret
= wait_event_interruptible_timeout(fbi
->wait
,
199 readl(fbi
->regbase
+ 0x38) & (1 << 3), HZ
/ 10);
200 /* Mask back to reduce unwanted interrupt traffic */
201 writel(0xffffffff, fbi
->regbase
+ 0x3c);
211 static int vt8500lcd_pan_display(struct fb_var_screeninfo
*var
,
212 struct fb_info
*info
)
214 unsigned pixlen
= info
->fix
.line_length
/ info
->var
.xres_virtual
;
215 unsigned off
= pixlen
* var
->xoffset
216 + info
->fix
.line_length
* var
->yoffset
;
217 struct vt8500lcd_info
*fbi
= to_vt8500lcd_info(info
);
220 | (((info
->var
.xres_virtual
- info
->var
.xres
) * pixlen
/ 4) << 20)
221 | (off
>> 2), fbi
->regbase
+ 0x20);
227 * Blank the display by setting all palette values to zero. Note,
228 * True Color modes do not really use the palette, so this will not
229 * blank the display in all modes.
231 static int vt8500lcd_blank(int blank
, struct fb_info
*info
)
236 case FB_BLANK_POWERDOWN
:
237 case FB_BLANK_VSYNC_SUSPEND
:
238 case FB_BLANK_HSYNC_SUSPEND
:
239 case FB_BLANK_NORMAL
:
240 if (info
->fix
.visual
== FB_VISUAL_PSEUDOCOLOR
||
241 info
->fix
.visual
== FB_VISUAL_STATIC_PSEUDOCOLOR
)
242 for (i
= 0; i
< 256; i
++)
243 vt8500lcd_setcolreg(i
, 0, 0, 0, 0, info
);
244 case FB_BLANK_UNBLANK
:
245 if (info
->fix
.visual
== FB_VISUAL_PSEUDOCOLOR
||
246 info
->fix
.visual
== FB_VISUAL_STATIC_PSEUDOCOLOR
)
247 fb_set_cmap(&info
->cmap
, info
);
252 static struct fb_ops vt8500lcd_ops
= {
253 .owner
= THIS_MODULE
,
254 .fb_set_par
= vt8500lcd_set_par
,
255 .fb_setcolreg
= vt8500lcd_setcolreg
,
256 .fb_fillrect
= wmt_ge_fillrect
,
257 .fb_copyarea
= wmt_ge_copyarea
,
258 .fb_imageblit
= sys_imageblit
,
259 .fb_sync
= wmt_ge_sync
,
260 .fb_ioctl
= vt8500lcd_ioctl
,
261 .fb_pan_display
= vt8500lcd_pan_display
,
262 .fb_blank
= vt8500lcd_blank
,
265 static irqreturn_t
vt8500lcd_handle_irq(int irq
, void *dev_id
)
267 struct vt8500lcd_info
*fbi
= dev_id
;
269 if (readl(fbi
->regbase
+ 0x38) & (1 << 3))
270 wake_up_interruptible(&fbi
->wait
);
272 writel(0xffffffff, fbi
->regbase
+ 0x38);
276 static int vt8500lcd_probe(struct platform_device
*pdev
)
278 struct vt8500lcd_info
*fbi
;
279 struct resource
*res
;
283 struct fb_videomode of_mode
;
284 struct device_node
*np
;
286 dma_addr_t fb_mem_phys
;
287 unsigned long fb_mem_len
;
293 fbi
= devm_kzalloc(&pdev
->dev
, sizeof(struct vt8500lcd_info
)
294 + sizeof(u32
) * 16, GFP_KERNEL
);
296 dev_err(&pdev
->dev
, "Failed to initialize framebuffer device\n");
301 strcpy(fbi
->fb
.fix
.id
, "VT8500 LCD");
303 fbi
->fb
.fix
.type
= FB_TYPE_PACKED_PIXELS
;
304 fbi
->fb
.fix
.xpanstep
= 0;
305 fbi
->fb
.fix
.ypanstep
= 1;
306 fbi
->fb
.fix
.ywrapstep
= 0;
307 fbi
->fb
.fix
.accel
= FB_ACCEL_NONE
;
309 fbi
->fb
.var
.nonstd
= 0;
310 fbi
->fb
.var
.activate
= FB_ACTIVATE_NOW
;
311 fbi
->fb
.var
.height
= -1;
312 fbi
->fb
.var
.width
= -1;
313 fbi
->fb
.var
.vmode
= FB_VMODE_NONINTERLACED
;
315 fbi
->fb
.fbops
= &vt8500lcd_ops
;
316 fbi
->fb
.flags
= FBINFO_DEFAULT
317 | FBINFO_HWACCEL_COPYAREA
318 | FBINFO_HWACCEL_FILLRECT
319 | FBINFO_HWACCEL_YPAN
321 | FBINFO_PARTIAL_PAN_OK
;
325 addr
= addr
+ sizeof(struct vt8500lcd_info
);
326 fbi
->fb
.pseudo_palette
= addr
;
328 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
330 dev_err(&pdev
->dev
, "no I/O memory resource defined\n");
335 res
= request_mem_region(res
->start
, resource_size(res
), "vt8500lcd");
337 dev_err(&pdev
->dev
, "failed to request I/O memory\n");
342 fbi
->regbase
= ioremap(res
->start
, resource_size(res
));
343 if (fbi
->regbase
== NULL
) {
344 dev_err(&pdev
->dev
, "failed to map I/O memory\n");
346 goto failed_free_res
;
349 np
= of_parse_phandle(pdev
->dev
.of_node
, "default-mode", 0);
351 pr_err("%s: No display description in Device Tree\n", __func__
);
353 goto failed_free_res
;
357 * This code is copied from Sascha Hauer's of_videomode helper
358 * and can be replaced with a call to the helper once mainlined
361 ret
|= of_property_read_u32(np
, "hactive", &of_mode
.xres
);
362 ret
|= of_property_read_u32(np
, "vactive", &of_mode
.yres
);
363 ret
|= of_property_read_u32(np
, "hback-porch", &of_mode
.left_margin
);
364 ret
|= of_property_read_u32(np
, "hfront-porch", &of_mode
.right_margin
);
365 ret
|= of_property_read_u32(np
, "hsync-len", &of_mode
.hsync_len
);
366 ret
|= of_property_read_u32(np
, "vback-porch", &of_mode
.upper_margin
);
367 ret
|= of_property_read_u32(np
, "vfront-porch", &of_mode
.lower_margin
);
368 ret
|= of_property_read_u32(np
, "vsync-len", &of_mode
.vsync_len
);
369 ret
|= of_property_read_u32(np
, "bpp", &bpp
);
371 pr_err("%s: Unable to read display properties\n", __func__
);
372 goto failed_free_res
;
374 of_mode
.vmode
= FB_VMODE_NONINTERLACED
;
376 /* try allocating the framebuffer */
377 fb_mem_len
= of_mode
.xres
* of_mode
.yres
* 2 * (bpp
/ 8);
378 fb_mem_virt
= dma_alloc_coherent(&pdev
->dev
, fb_mem_len
, &fb_mem_phys
,
381 pr_err("%s: Failed to allocate framebuffer\n", __func__
);
385 fbi
->fb
.fix
.smem_start
= fb_mem_phys
;
386 fbi
->fb
.fix
.smem_len
= fb_mem_len
;
387 fbi
->fb
.screen_base
= fb_mem_virt
;
389 fbi
->palette_size
= PAGE_ALIGN(512);
390 fbi
->palette_cpu
= dma_alloc_coherent(&pdev
->dev
,
394 if (fbi
->palette_cpu
== NULL
) {
395 dev_err(&pdev
->dev
, "Failed to allocate palette buffer\n");
400 irq
= platform_get_irq(pdev
, 0);
402 dev_err(&pdev
->dev
, "no IRQ defined\n");
404 goto failed_free_palette
;
407 ret
= request_irq(irq
, vt8500lcd_handle_irq
, 0, "LCD", fbi
);
409 dev_err(&pdev
->dev
, "request_irq failed: %d\n", ret
);
411 goto failed_free_palette
;
414 init_waitqueue_head(&fbi
->wait
);
416 if (fb_alloc_cmap(&fbi
->fb
.cmap
, 256, 0) < 0) {
417 dev_err(&pdev
->dev
, "Failed to allocate color map\n");
419 goto failed_free_irq
;
422 fb_videomode_to_var(&fbi
->fb
.var
, &of_mode
);
424 fbi
->fb
.var
.xres_virtual
= of_mode
.xres
;
425 fbi
->fb
.var
.yres_virtual
= of_mode
.yres
* 2;
426 fbi
->fb
.var
.bits_per_pixel
= bpp
;
428 ret
= vt8500lcd_set_par(&fbi
->fb
);
430 dev_err(&pdev
->dev
, "Failed to set parameters\n");
431 goto failed_free_cmap
;
434 writel(fbi
->fb
.fix
.smem_start
>> 22, fbi
->regbase
+ 0x1c);
435 writel((fbi
->palette_phys
& 0xfffffe00) | 1, fbi
->regbase
+ 0x18);
437 platform_set_drvdata(pdev
, fbi
);
439 ret
= register_framebuffer(&fbi
->fb
);
442 "Failed to register framebuffer device: %d\n", ret
);
443 goto failed_free_cmap
;
447 * Ok, now enable the LCD controller
449 writel(readl(fbi
->regbase
) | 1, fbi
->regbase
);
454 if (fbi
->fb
.cmap
.len
)
455 fb_dealloc_cmap(&fbi
->fb
.cmap
);
459 dma_free_coherent(&pdev
->dev
, fbi
->palette_size
,
460 fbi
->palette_cpu
, fbi
->palette_phys
);
462 iounmap(fbi
->regbase
);
464 release_mem_region(res
->start
, resource_size(res
));
466 platform_set_drvdata(pdev
, NULL
);
472 static int vt8500lcd_remove(struct platform_device
*pdev
)
474 struct vt8500lcd_info
*fbi
= platform_get_drvdata(pdev
);
475 struct resource
*res
;
478 unregister_framebuffer(&fbi
->fb
);
480 writel(0, fbi
->regbase
);
482 if (fbi
->fb
.cmap
.len
)
483 fb_dealloc_cmap(&fbi
->fb
.cmap
);
485 irq
= platform_get_irq(pdev
, 0);
488 dma_free_coherent(&pdev
->dev
, fbi
->palette_size
,
489 fbi
->palette_cpu
, fbi
->palette_phys
);
491 iounmap(fbi
->regbase
);
493 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
494 release_mem_region(res
->start
, resource_size(res
));
501 static const struct of_device_id via_dt_ids
[] = {
502 { .compatible
= "via,vt8500-fb", },
506 static struct platform_driver vt8500lcd_driver
= {
507 .probe
= vt8500lcd_probe
,
508 .remove
= vt8500lcd_remove
,
510 .owner
= THIS_MODULE
,
511 .name
= "vt8500-lcd",
512 .of_match_table
= of_match_ptr(via_dt_ids
),
516 module_platform_driver(vt8500lcd_driver
);
518 MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>");
519 MODULE_DESCRIPTION("LCD controller driver for VIA VT8500");
520 MODULE_LICENSE("GPL v2");
521 MODULE_DEVICE_TABLE(of
, via_dt_ids
);