ARM: Add support for the display controllers in VT8500 and WM8505
[pandora-kernel.git] / drivers / video / wm8505fb.c
1 /*
2  *  WonderMedia WM8505 Frame Buffer device driver
3  *
4  *  Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com>
5  *    Based on vt8500lcdfb.c
6  *
7  * This software is licensed under the terms of the GNU General Public
8  * License version 2, as published by the Free Software Foundation, and
9  * may be copied, distributed, and modified under those terms.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  */
16
17 #include <linux/module.h>
18 #include <linux/kernel.h>
19 #include <linux/errno.h>
20 #include <linux/string.h>
21 #include <linux/mm.h>
22 #include <linux/slab.h>
23 #include <linux/delay.h>
24 #include <linux/fb.h>
25 #include <linux/init.h>
26 #include <linux/interrupt.h>
27 #include <linux/io.h>
28 #include <linux/dma-mapping.h>
29 #include <linux/platform_device.h>
30 #include <linux/wait.h>
31
32 #include <mach/vt8500fb.h>
33
34 #include "wm8505fb_regs.h"
35 #include "wmt_ge_rops.h"
36
37 #define DRIVER_NAME "wm8505-fb"
38
39 #define to_wm8505fb_info(__info) container_of(__info, \
40                                                 struct wm8505fb_info, fb)
41 struct wm8505fb_info {
42         struct fb_info          fb;
43         void __iomem            *regbase;
44         unsigned int            contrast;
45 };
46
47
48 static int wm8505fb_init_hw(struct fb_info *info)
49 {
50         struct wm8505fb_info *fbi = to_wm8505fb_info(info);
51
52         int i;
53
54         /* I know the purpose only of few registers, so clear unknown */
55         for (i = 0; i < 0x200; i += 4)
56                 writel(0, fbi->regbase + i);
57
58         /* Set frame buffer address */
59         writel(fbi->fb.fix.smem_start, fbi->regbase + WMT_GOVR_FBADDR);
60         writel(fbi->fb.fix.smem_start, fbi->regbase + WMT_GOVR_FBADDR1);
61
62         /* Set in-memory picture format to RGB 32bpp */
63         writel(0x1c,                   fbi->regbase + WMT_GOVR_COLORSPACE);
64         writel(1,                      fbi->regbase + WMT_GOVR_COLORSPACE1);
65
66         /* Virtual buffer size */
67         writel(info->var.xres,         fbi->regbase + WMT_GOVR_XRES);
68         writel(info->var.xres_virtual, fbi->regbase + WMT_GOVR_XRES_VIRTUAL);
69
70         /* black magic ;) */
71         writel(0xf,                    fbi->regbase + WMT_GOVR_FHI);
72         writel(4,                      fbi->regbase + WMT_GOVR_DVO_SET);
73         writel(1,                      fbi->regbase + WMT_GOVR_MIF_ENABLE);
74         writel(1,                      fbi->regbase + WMT_GOVR_REG_UPDATE);
75
76         return 0;
77 }
78
79 static int wm8505fb_set_timing(struct fb_info *info)
80 {
81         struct wm8505fb_info *fbi = to_wm8505fb_info(info);
82
83         int h_start = info->var.left_margin;
84         int h_end = h_start + info->var.xres;
85         int h_all = h_end + info->var.right_margin;
86         int h_sync = info->var.hsync_len;
87
88         int v_start = info->var.upper_margin;
89         int v_end = v_start + info->var.yres;
90         int v_all = v_end + info->var.lower_margin;
91         int v_sync = info->var.vsync_len + 1;
92
93         writel(0, fbi->regbase + WMT_GOVR_TG);
94
95         writel(h_start, fbi->regbase + WMT_GOVR_TIMING_H_START);
96         writel(h_end,   fbi->regbase + WMT_GOVR_TIMING_H_END);
97         writel(h_all,   fbi->regbase + WMT_GOVR_TIMING_H_ALL);
98         writel(h_sync,  fbi->regbase + WMT_GOVR_TIMING_H_SYNC);
99
100         writel(v_start, fbi->regbase + WMT_GOVR_TIMING_V_START);
101         writel(v_end,   fbi->regbase + WMT_GOVR_TIMING_V_END);
102         writel(v_all,   fbi->regbase + WMT_GOVR_TIMING_V_ALL);
103         writel(v_sync,  fbi->regbase + WMT_GOVR_TIMING_V_SYNC);
104
105         writel(1, fbi->regbase + WMT_GOVR_TG);
106
107         return 0;
108 }
109
110
111 static int wm8505fb_set_par(struct fb_info *info)
112 {
113         struct wm8505fb_info *fbi = to_wm8505fb_info(info);
114
115         if (!fbi)
116                 return -EINVAL;
117
118         if (info->var.bits_per_pixel == 32) {
119                 info->var.red.offset = 16;
120                 info->var.red.length = 8;
121                 info->var.red.msb_right = 0;
122                 info->var.green.offset = 8;
123                 info->var.green.length = 8;
124                 info->var.green.msb_right = 0;
125                 info->var.blue.offset = 0;
126                 info->var.blue.length = 8;
127                 info->var.blue.msb_right = 0;
128                 info->fix.visual = FB_VISUAL_TRUECOLOR;
129                 info->fix.line_length = info->var.xres_virtual << 2;
130         }
131
132         wm8505fb_set_timing(info);
133
134         writel(fbi->contrast<<16 | fbi->contrast<<8 | fbi->contrast,
135                 fbi->regbase + WMT_GOVR_CONTRAST);
136
137         return 0;
138 }
139
140 static ssize_t contrast_show(struct device *dev,
141                              struct device_attribute *attr, char *buf)
142 {
143         struct fb_info *info = dev_get_drvdata(dev);
144         struct wm8505fb_info *fbi = to_wm8505fb_info(info);
145
146         return sprintf(buf, "%d\n", fbi->contrast);
147 }
148
149 static ssize_t contrast_store(struct device *dev,
150                               struct device_attribute *attr,
151                               const char *buf, size_t count)
152 {
153         struct fb_info *info = dev_get_drvdata(dev);
154         struct wm8505fb_info *fbi = to_wm8505fb_info(info);
155         unsigned long tmp;
156
157         if (strict_strtoul(buf, 10, &tmp) || (tmp > 0xff))
158                 return -EINVAL;
159         fbi->contrast = tmp;
160
161         wm8505fb_set_par(info);
162
163         return count;
164 }
165
166 static DEVICE_ATTR(contrast, 0644, contrast_show, contrast_store);
167
168 static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
169 {
170         chan &= 0xffff;
171         chan >>= 16 - bf->length;
172         return chan << bf->offset;
173 }
174
175 static int wm8505fb_setcolreg(unsigned regno, unsigned red, unsigned green,
176                            unsigned blue, unsigned transp,
177                            struct fb_info *info) {
178         struct wm8505fb_info *fbi = to_wm8505fb_info(info);
179         int ret = 1;
180         unsigned int val;
181         if (regno >= 256)
182                 return -EINVAL;
183
184         if (info->var.grayscale)
185                 red = green = blue =
186                         (19595 * red + 38470 * green + 7471 * blue) >> 16;
187
188         switch (fbi->fb.fix.visual) {
189         case FB_VISUAL_TRUECOLOR:
190                 if (regno < 16) {
191                         u32 *pal = info->pseudo_palette;
192
193                         val  = chan_to_field(red, &fbi->fb.var.red);
194                         val |= chan_to_field(green, &fbi->fb.var.green);
195                         val |= chan_to_field(blue, &fbi->fb.var.blue);
196
197                         pal[regno] = val;
198                         ret = 0;
199                 }
200                 break;
201         }
202
203         return ret;
204 }
205
206 static int wm8505fb_pan_display(struct fb_var_screeninfo *var,
207                                 struct fb_info *info)
208 {
209         struct wm8505fb_info *fbi = to_wm8505fb_info(info);
210
211         writel(var->xoffset, fbi->regbase + WMT_GOVR_XPAN);
212         writel(var->yoffset, fbi->regbase + WMT_GOVR_YPAN);
213         return 0;
214 }
215
216 static int wm8505fb_blank(int blank, struct fb_info *info)
217 {
218         struct wm8505fb_info *fbi = to_wm8505fb_info(info);
219
220         switch (blank) {
221         case FB_BLANK_UNBLANK:
222                 wm8505fb_set_timing(info);
223                 break;
224         default:
225                 writel(0,  fbi->regbase + WMT_GOVR_TIMING_V_SYNC);
226                 break;
227         }
228
229         return 0;
230 }
231
232 static struct fb_ops wm8505fb_ops = {
233         .owner          = THIS_MODULE,
234         .fb_set_par     = wm8505fb_set_par,
235         .fb_setcolreg   = wm8505fb_setcolreg,
236         .fb_fillrect    = wmt_ge_fillrect,
237         .fb_copyarea    = wmt_ge_copyarea,
238         .fb_imageblit   = sys_imageblit,
239         .fb_sync        = wmt_ge_sync,
240         .fb_pan_display = wm8505fb_pan_display,
241         .fb_blank       = wm8505fb_blank,
242 };
243
244 static int __devinit wm8505fb_probe(struct platform_device *pdev)
245 {
246         struct wm8505fb_info    *fbi;
247         struct resource         *res;
248         void                    *addr;
249         struct vt8500fb_platform_data *pdata;
250         int ret;
251
252         pdata = pdev->dev.platform_data;
253
254         ret = -ENOMEM;
255         fbi = NULL;
256
257         fbi = kzalloc(sizeof(struct wm8505fb_info) + sizeof(u32) * 16,
258                                                         GFP_KERNEL);
259         if (!fbi) {
260                 dev_err(&pdev->dev, "Failed to initialize framebuffer device\n");
261                 ret = -ENOMEM;
262                 goto failed;
263         }
264
265         strcpy(fbi->fb.fix.id, DRIVER_NAME);
266
267         fbi->fb.fix.type        = FB_TYPE_PACKED_PIXELS;
268         fbi->fb.fix.xpanstep    = 1;
269         fbi->fb.fix.ypanstep    = 1;
270         fbi->fb.fix.ywrapstep   = 0;
271         fbi->fb.fix.accel       = FB_ACCEL_NONE;
272
273         fbi->fb.fbops           = &wm8505fb_ops;
274         fbi->fb.flags           = FBINFO_DEFAULT
275                                 | FBINFO_HWACCEL_COPYAREA
276                                 | FBINFO_HWACCEL_FILLRECT
277                                 | FBINFO_HWACCEL_XPAN
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 wm8505fb_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), "wm8505fb");
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         fb_videomode_to_var(&fbi->fb.var, &pdata->mode);
309
310         fbi->fb.var.nonstd              = 0;
311         fbi->fb.var.activate            = FB_ACTIVATE_NOW;
312
313         fbi->fb.var.height              = -1;
314         fbi->fb.var.width               = -1;
315         fbi->fb.var.xres_virtual        = pdata->xres_virtual;
316         fbi->fb.var.yres_virtual        = pdata->yres_virtual;
317         fbi->fb.var.bits_per_pixel      = pdata->bpp;
318
319         fbi->fb.fix.smem_start  = pdata->video_mem_phys;
320         fbi->fb.fix.smem_len    = pdata->video_mem_len;
321         fbi->fb.screen_base     = pdata->video_mem_virt;
322         fbi->fb.screen_size     = pdata->video_mem_len;
323
324         if (fb_alloc_cmap(&fbi->fb.cmap, 256, 0) < 0) {
325                 dev_err(&pdev->dev, "Failed to allocate color map\n");
326                 ret = -ENOMEM;
327                 goto failed_free_io;
328         }
329
330         wm8505fb_init_hw(&fbi->fb);
331
332         fbi->contrast = 0x80;
333         ret = wm8505fb_set_par(&fbi->fb);
334         if (ret) {
335                 dev_err(&pdev->dev, "Failed to set parameters\n");
336                 goto failed_free_cmap;
337         }
338
339         platform_set_drvdata(pdev, fbi);
340
341         ret = register_framebuffer(&fbi->fb);
342         if (ret < 0) {
343                 dev_err(&pdev->dev,
344                         "Failed to register framebuffer device: %d\n", ret);
345                 goto failed_free_cmap;
346         }
347
348         ret = device_create_file(&pdev->dev, &dev_attr_contrast);
349         if (ret < 0) {
350                 printk(KERN_WARNING "fb%d: failed to register attributes (%d)\n",
351                         fbi->fb.node, ret);
352         }
353
354         printk(KERN_INFO "fb%d: %s frame buffer at 0x%lx-0x%lx\n",
355                fbi->fb.node, fbi->fb.fix.id, fbi->fb.fix.smem_start,
356                fbi->fb.fix.smem_start + fbi->fb.fix.smem_len - 1);
357
358         return 0;
359
360 failed_free_cmap:
361         if (fbi->fb.cmap.len)
362                 fb_dealloc_cmap(&fbi->fb.cmap);
363 failed_free_io:
364         iounmap(fbi->regbase);
365 failed_free_res:
366         release_mem_region(res->start, resource_size(res));
367 failed_fbi:
368         platform_set_drvdata(pdev, NULL);
369         kfree(fbi);
370 failed:
371         return ret;
372 }
373
374 static int __devexit wm8505fb_remove(struct platform_device *pdev)
375 {
376         struct wm8505fb_info *fbi = platform_get_drvdata(pdev);
377         struct resource *res;
378
379         device_remove_file(&pdev->dev, &dev_attr_contrast);
380
381         unregister_framebuffer(&fbi->fb);
382
383         writel(0, fbi->regbase);
384
385         if (fbi->fb.cmap.len)
386                 fb_dealloc_cmap(&fbi->fb.cmap);
387
388         iounmap(fbi->regbase);
389
390         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
391         release_mem_region(res->start, resource_size(res));
392
393         kfree(fbi);
394
395         return 0;
396 }
397
398 static struct platform_driver wm8505fb_driver = {
399         .probe          = wm8505fb_probe,
400         .remove         = __devexit_p(wm8505fb_remove),
401         .driver         = {
402                 .owner  = THIS_MODULE,
403                 .name   = DRIVER_NAME,
404         },
405 };
406
407 static int __init wm8505fb_init(void)
408 {
409         return platform_driver_register(&wm8505fb_driver);
410 }
411
412 static void __exit wm8505fb_exit(void)
413 {
414         platform_driver_unregister(&wm8505fb_driver);
415 }
416
417 module_init(wm8505fb_init);
418 module_exit(wm8505fb_exit);
419
420 MODULE_AUTHOR("Ed Spiridonov <edo.rus@gmail.com>");
421 MODULE_DESCRIPTION("Framebuffer driver for WMT WM8505");
422 MODULE_LICENSE("GPL");