aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/vt8500lcdfb.c
diff options
context:
space:
mode:
authorAlexey Charkov <alchark@gmail.com>2010-11-08 18:42:39 -0500
committerPaul Mundt <lethal@linux-sh.org>2010-11-09 04:52:07 -0500
commitd6ff7d0fe22cdf3ea41c48b50da9a9181500d1bf (patch)
treef14609b2e10c9b381c1905a3a895729293119929 /drivers/video/vt8500lcdfb.c
parenta7bcf21e60c73cb7f7c13fad928967d7e47c3cac (diff)
ARM: Add support for the display controllers in VT8500 and WM8505
This adds drivers for the LCD controller found in VIA VT8500 SoC, GOVR display controller found in WonderMedia WM8505 SoC and for the Graphics Engine present in both of them that provides hardware accelerated raster operations (used for copyarea and fillrect). Signed-off-by: Alexey Charkov <alchark@gmail.com> Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'drivers/video/vt8500lcdfb.c')
-rw-r--r--drivers/video/vt8500lcdfb.c447
1 files changed, 447 insertions, 0 deletions
diff --git a/drivers/video/vt8500lcdfb.c b/drivers/video/vt8500lcdfb.c
new file mode 100644
index 000000000000..7617f12e4fd7
--- /dev/null
+++ b/drivers/video/vt8500lcdfb.c
@@ -0,0 +1,447 @@
1/*
2 * linux/drivers/video/vt8500lcdfb.c
3 *
4 * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
5 *
6 * Based on skeletonfb.c and pxafb.c
7 *
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.
11 *
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.
16 */
17
18#include <linux/module.h>
19#include <linux/kernel.h>
20#include <linux/errno.h>
21#include <linux/string.h>
22#include <linux/mm.h>
23#include <linux/slab.h>
24#include <linux/delay.h>
25#include <linux/fb.h>
26#include <linux/init.h>
27#include <linux/interrupt.h>
28#include <linux/io.h>
29#include <linux/dma-mapping.h>
30#include <linux/platform_device.h>
31#include <linux/wait.h>
32
33#include <mach/vt8500fb.h>
34
35#include "vt8500lcdfb.h"
36#include "wmt_ge_rops.h"
37
38#define to_vt8500lcd_info(__info) container_of(__info, \
39 struct vt8500lcd_info, fb)
40
41static int vt8500lcd_set_par(struct fb_info *info)
42{
43 struct vt8500lcd_info *fbi = to_vt8500lcd_info(info);
44 int reg_bpp = 5; /* 16bpp */
45 int i;
46 unsigned long control0;
47
48 if (!fbi)
49 return -EINVAL;
50
51 if (info->var.bits_per_pixel <= 8) {
52 /* palettized */
53 info->var.red.offset = 0;
54 info->var.red.length = info->var.bits_per_pixel;
55 info->var.red.msb_right = 0;
56
57 info->var.green.offset = 0;
58 info->var.green.length = info->var.bits_per_pixel;
59 info->var.green.msb_right = 0;
60
61 info->var.blue.offset = 0;
62 info->var.blue.length = info->var.bits_per_pixel;
63 info->var.blue.msb_right = 0;
64
65 info->var.transp.offset = 0;
66 info->var.transp.length = 0;
67 info->var.transp.msb_right = 0;
68
69 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
70 info->fix.line_length = info->var.xres_virtual /
71 (8/info->var.bits_per_pixel);
72 } else {
73 /* non-palettized */
74 info->var.transp.offset = 0;
75 info->var.transp.length = 0;
76 info->var.transp.msb_right = 0;
77
78 if (info->var.bits_per_pixel == 16) {
79 /* RGB565 */
80 info->var.red.offset = 11;
81 info->var.red.length = 5;
82 info->var.red.msb_right = 0;
83 info->var.green.offset = 5;
84 info->var.green.length = 6;
85 info->var.green.msb_right = 0;
86 info->var.blue.offset = 0;
87 info->var.blue.length = 5;
88 info->var.blue.msb_right = 0;
89 } else {
90 /* Equal depths per channel */
91 info->var.red.offset = info->var.bits_per_pixel
92 * 2 / 3;
93 info->var.red.length = info->var.bits_per_pixel / 3;
94 info->var.red.msb_right = 0;
95 info->var.green.offset = info->var.bits_per_pixel / 3;
96 info->var.green.length = info->var.bits_per_pixel / 3;
97 info->var.green.msb_right = 0;
98 info->var.blue.offset = 0;
99 info->var.blue.length = info->var.bits_per_pixel / 3;
100 info->var.blue.msb_right = 0;
101 }
102
103 info->fix.visual = FB_VISUAL_TRUECOLOR;
104 info->fix.line_length = info->var.bits_per_pixel > 16 ?
105 info->var.xres_virtual << 2 :
106 info->var.xres_virtual << 1;
107 }
108
109 for (i = 0; i < 8; i++) {
110 if (bpp_values[i] == info->var.bits_per_pixel) {
111 reg_bpp = i;
112 continue;
113 }
114 }
115
116 control0 = readl(fbi->regbase) & ~0xf;
117 writel(0, fbi->regbase);
118 while (readl(fbi->regbase + 0x38) & 0x10)
119 /* wait */;
120 writel((((info->var.hsync_len - 1) & 0x3f) << 26)
121 | ((info->var.left_margin & 0xff) << 18)
122 | (((info->var.xres - 1) & 0x3ff) << 8)
123 | (info->var.right_margin & 0xff), fbi->regbase + 0x4);
124 writel((((info->var.vsync_len - 1) & 0x3f) << 26)
125 | ((info->var.upper_margin & 0xff) << 18)
126 | (((info->var.yres - 1) & 0x3ff) << 8)
127 | (info->var.lower_margin & 0xff), fbi->regbase + 0x8);
128 writel((((info->var.yres - 1) & 0x400) << 2)
129 | ((info->var.xres - 1) & 0x400), fbi->regbase + 0x10);
130 writel(0x80000000, fbi->regbase + 0x20);
131 writel(control0 | (reg_bpp << 1) | 0x100, fbi->regbase);
132
133 return 0;
134}
135
136static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
137{
138 chan &= 0xffff;
139 chan >>= 16 - bf->length;
140 return chan << bf->offset;
141}
142
143static int vt8500lcd_setcolreg(unsigned regno, unsigned red, unsigned green,
144 unsigned blue, unsigned transp,
145 struct fb_info *info) {
146 struct vt8500lcd_info *fbi = to_vt8500lcd_info(info);
147 int ret = 1;
148 unsigned int val;
149 if (regno >= 256)
150 return -EINVAL;
151
152 if (info->var.grayscale)
153 red = green = blue =
154 (19595 * red + 38470 * green + 7471 * blue) >> 16;
155
156 switch (fbi->fb.fix.visual) {
157 case FB_VISUAL_TRUECOLOR:
158 if (regno < 16) {
159 u32 *pal = fbi->fb.pseudo_palette;
160
161 val = chan_to_field(red, &fbi->fb.var.red);
162 val |= chan_to_field(green, &fbi->fb.var.green);
163 val |= chan_to_field(blue, &fbi->fb.var.blue);
164
165 pal[regno] = val;
166 ret = 0;
167 }
168 break;
169
170 case FB_VISUAL_STATIC_PSEUDOCOLOR:
171 case FB_VISUAL_PSEUDOCOLOR:
172 writew((red & 0xf800)
173 | ((green >> 5) & 0x7e0)
174 | ((blue >> 11) & 0x1f),
175 fbi->palette_cpu + sizeof(u16) * regno);
176 break;
177 }
178
179 return ret;
180}
181
182static int vt8500lcd_ioctl(struct fb_info *info, unsigned int cmd,
183 unsigned long arg)
184{
185 int ret = 0;
186 struct vt8500lcd_info *fbi = to_vt8500lcd_info(info);
187
188 if (cmd == FBIO_WAITFORVSYNC) {
189 /* Unmask End of Frame interrupt */
190 writel(0xffffffff ^ (1 << 3), fbi->regbase + 0x3c);
191 ret = wait_event_interruptible_timeout(fbi->wait,
192 readl(fbi->regbase + 0x38) & (1 << 3), HZ / 10);
193 /* Mask back to reduce unwanted interrupt traffic */
194 writel(0xffffffff, fbi->regbase + 0x3c);
195 if (ret < 0)
196 return ret;
197 if (ret == 0)
198 return -ETIMEDOUT;
199 }
200
201 return ret;
202}
203
204static int vt8500lcd_pan_display(struct fb_var_screeninfo *var,
205 struct fb_info *info)
206{
207 unsigned pixlen = info->fix.line_length / info->var.xres_virtual;
208 unsigned off = pixlen * var->xoffset
209 + info->fix.line_length * var->yoffset;
210 struct vt8500lcd_info *fbi = to_vt8500lcd_info(info);
211
212 writel((1 << 31)
213 | (((var->xres_virtual - var->xres) * pixlen / 4) << 20)
214 | (off >> 2), fbi->regbase + 0x20);
215 return 0;
216}
217
218static struct fb_ops vt8500lcd_ops = {
219 .owner = THIS_MODULE,
220 .fb_set_par = vt8500lcd_set_par,
221 .fb_setcolreg = vt8500lcd_setcolreg,
222 .fb_fillrect = wmt_ge_fillrect,
223 .fb_copyarea = wmt_ge_copyarea,
224 .fb_imageblit = sys_imageblit,
225 .fb_sync = wmt_ge_sync,
226 .fb_ioctl = vt8500lcd_ioctl,
227 .fb_pan_display = vt8500lcd_pan_display,
228};
229
230static irqreturn_t vt8500lcd_handle_irq(int irq, void *dev_id)
231{
232 struct vt8500lcd_info *fbi = dev_id;
233
234 if (readl(fbi->regbase + 0x38) & (1 << 3))
235 wake_up_interruptible(&fbi->wait);
236
237 writel(0xffffffff, fbi->regbase + 0x38);
238 return IRQ_HANDLED;
239}
240
241static int __devinit vt8500lcd_probe(struct platform_device *pdev)
242{
243 struct vt8500lcd_info *fbi;
244 struct resource *res;
245 struct vt8500fb_platform_data *pdata = pdev->dev.platform_data;
246 void *addr;
247 int irq, ret;
248
249 ret = -ENOMEM;
250 fbi = NULL;
251
252 fbi = kzalloc(sizeof(struct vt8500lcd_info) + sizeof(u32) * 16,
253 GFP_KERNEL);
254 if (!fbi) {
255 dev_err(&pdev->dev, "Failed to initialize framebuffer device\n");
256 ret = -ENOMEM;
257 goto failed;
258 }
259
260 strcpy(fbi->fb.fix.id, "VT8500 LCD");
261
262 fbi->fb.fix.type = FB_TYPE_PACKED_PIXELS;
263 fbi->fb.fix.xpanstep = 0;
264 fbi->fb.fix.ypanstep = 1;
265 fbi->fb.fix.ywrapstep = 0;
266 fbi->fb.fix.accel = FB_ACCEL_NONE;
267
268 fbi->fb.var.nonstd = 0;
269 fbi->fb.var.activate = FB_ACTIVATE_NOW;
270 fbi->fb.var.height = -1;
271 fbi->fb.var.width = -1;
272 fbi->fb.var.vmode = FB_VMODE_NONINTERLACED;
273
274 fbi->fb.fbops = &vt8500lcd_ops;
275 fbi->fb.flags = FBINFO_DEFAULT
276 | FBINFO_HWACCEL_COPYAREA
277 | FBINFO_HWACCEL_FILLRECT
278 | FBINFO_HWACCEL_YPAN
279 | FBINFO_VIRTFB
280 | FBINFO_PARTIAL_PAN_OK;
281 fbi->fb.node = -1;
282
283 addr = fbi;
284 addr = addr + sizeof(struct vt8500lcd_info);
285 fbi->fb.pseudo_palette = addr;
286
287 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
288 if (res == NULL) {
289 dev_err(&pdev->dev, "no I/O memory resource defined\n");
290 ret = -ENODEV;
291 goto failed_fbi;
292 }
293
294 res = request_mem_region(res->start, resource_size(res), "vt8500lcd");
295 if (res == NULL) {
296 dev_err(&pdev->dev, "failed to request I/O memory\n");
297 ret = -EBUSY;
298 goto failed_fbi;
299 }
300
301 fbi->regbase = ioremap(res->start, resource_size(res));
302 if (fbi->regbase == NULL) {
303 dev_err(&pdev->dev, "failed to map I/O memory\n");
304 ret = -EBUSY;
305 goto failed_free_res;
306 }
307
308 fbi->fb.fix.smem_start = pdata->video_mem_phys;
309 fbi->fb.fix.smem_len = pdata->video_mem_len;
310 fbi->fb.screen_base = pdata->video_mem_virt;
311
312 fbi->palette_size = PAGE_ALIGN(512);
313 fbi->palette_cpu = dma_alloc_coherent(&pdev->dev,
314 fbi->palette_size,
315 &fbi->palette_phys,
316 GFP_KERNEL);
317 if (fbi->palette_cpu == NULL) {
318 dev_err(&pdev->dev, "Failed to allocate palette buffer\n");
319 ret = -ENOMEM;
320 goto failed_free_io;
321 }
322
323 irq = platform_get_irq(pdev, 0);
324 if (irq < 0) {
325 dev_err(&pdev->dev, "no IRQ defined\n");
326 ret = -ENODEV;
327 goto failed_free_palette;
328 }
329
330 ret = request_irq(irq, vt8500lcd_handle_irq, IRQF_DISABLED, "LCD", fbi);
331 if (ret) {
332 dev_err(&pdev->dev, "request_irq failed: %d\n", ret);
333 ret = -EBUSY;
334 goto failed_free_palette;
335 }
336
337 init_waitqueue_head(&fbi->wait);
338
339 if (fb_alloc_cmap(&fbi->fb.cmap, 256, 0) < 0) {
340 dev_err(&pdev->dev, "Failed to allocate color map\n");
341 ret = -ENOMEM;
342 goto failed_free_irq;
343 }
344
345 fb_videomode_to_var(&fbi->fb.var, &pdata->mode);
346 fbi->fb.var.bits_per_pixel = pdata->bpp;
347 fbi->fb.var.xres_virtual = pdata->xres_virtual;
348 fbi->fb.var.yres_virtual = pdata->yres_virtual;
349
350 ret = vt8500lcd_set_par(&fbi->fb);
351 if (ret) {
352 dev_err(&pdev->dev, "Failed to set parameters\n");
353 goto failed_free_cmap;
354 }
355
356 writel(fbi->fb.fix.smem_start >> 22, fbi->regbase + 0x1c);
357 writel((fbi->palette_phys & 0xfffffe00) | 1, fbi->regbase + 0x18);
358
359 platform_set_drvdata(pdev, fbi);
360
361 ret = register_framebuffer(&fbi->fb);
362 if (ret < 0) {
363 dev_err(&pdev->dev,
364 "Failed to register framebuffer device: %d\n", ret);
365 goto failed_free_cmap;
366 }
367
368 /*
369 * Ok, now enable the LCD controller
370 */
371 writel(readl(fbi->regbase) | 1, fbi->regbase);
372
373 return 0;
374
375failed_free_cmap:
376 if (fbi->fb.cmap.len)
377 fb_dealloc_cmap(&fbi->fb.cmap);
378failed_free_irq:
379 free_irq(irq, fbi);
380failed_free_palette:
381 dma_free_coherent(&pdev->dev, fbi->palette_size,
382 fbi->palette_cpu, fbi->palette_phys);
383failed_free_io:
384 iounmap(fbi->regbase);
385failed_free_res:
386 release_mem_region(res->start, resource_size(res));
387failed_fbi:
388 platform_set_drvdata(pdev, NULL);
389 kfree(fbi);
390failed:
391 return ret;
392}
393
394static int __devexit vt8500lcd_remove(struct platform_device *pdev)
395{
396 struct vt8500lcd_info *fbi = platform_get_drvdata(pdev);
397 struct resource *res;
398 int irq;
399
400 unregister_framebuffer(&fbi->fb);
401
402 writel(0, fbi->regbase);
403
404 if (fbi->fb.cmap.len)
405 fb_dealloc_cmap(&fbi->fb.cmap);
406
407 irq = platform_get_irq(pdev, 0);
408 free_irq(irq, fbi);
409
410 dma_free_coherent(&pdev->dev, fbi->palette_size,
411 fbi->palette_cpu, fbi->palette_phys);
412
413 iounmap(fbi->regbase);
414
415 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
416 release_mem_region(res->start, resource_size(res));
417
418 kfree(fbi);
419
420 return 0;
421}
422
423static struct platform_driver vt8500lcd_driver = {
424 .probe = vt8500lcd_probe,
425 .remove = __devexit_p(vt8500lcd_remove),
426 .driver = {
427 .owner = THIS_MODULE,
428 .name = "vt8500-lcd",
429 },
430};
431
432static int __init vt8500lcd_init(void)
433{
434 return platform_driver_register(&vt8500lcd_driver);
435}
436
437static void __exit vt8500lcd_exit(void)
438{
439 platform_driver_unregister(&vt8500lcd_driver);
440}
441
442module_init(vt8500lcd_init);
443module_exit(vt8500lcd_exit);
444
445MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>");
446MODULE_DESCRIPTION("LCD controller driver for VIA VT8500");
447MODULE_LICENSE("GPL");