2 * WonderMedia WM8505 Frame Buffer device driver
4 * Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com>
5 * Based on vt8500lcdfb.c
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.
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.
17 #include <linux/module.h>
18 #include <linux/kernel.h>
19 #include <linux/errno.h>
20 #include <linux/string.h>
22 #include <linux/slab.h>
23 #include <linux/delay.h>
25 #include <linux/init.h>
26 #include <linux/interrupt.h>
28 #include <linux/dma-mapping.h>
29 #include <linux/platform_device.h>
30 #include <linux/wait.h>
32 #include <mach/vt8500fb.h>
34 #include "wm8505fb_regs.h"
35 #include "wmt_ge_rops.h"
37 #define DRIVER_NAME "wm8505-fb"
39 #define to_wm8505fb_info(__info) container_of(__info, \
40 struct wm8505fb_info, fb)
41 struct wm8505fb_info
{
43 void __iomem
*regbase
;
44 unsigned int contrast
;
48 static int wm8505fb_init_hw(struct fb_info
*info
)
50 struct wm8505fb_info
*fbi
= to_wm8505fb_info(info
);
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
);
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
);
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
);
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
);
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
);
79 static int wm8505fb_set_timing(struct fb_info
*info
)
81 struct wm8505fb_info
*fbi
= to_wm8505fb_info(info
);
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
;
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
;
93 writel(0, fbi
->regbase
+ WMT_GOVR_TG
);
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
);
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
);
105 writel(1, fbi
->regbase
+ WMT_GOVR_TG
);
111 static int wm8505fb_set_par(struct fb_info
*info
)
113 struct wm8505fb_info
*fbi
= to_wm8505fb_info(info
);
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;
132 wm8505fb_set_timing(info
);
134 writel(fbi
->contrast
<<16 | fbi
->contrast
<<8 | fbi
->contrast
,
135 fbi
->regbase
+ WMT_GOVR_CONTRAST
);
140 static ssize_t
contrast_show(struct device
*dev
,
141 struct device_attribute
*attr
, char *buf
)
143 struct fb_info
*info
= dev_get_drvdata(dev
);
144 struct wm8505fb_info
*fbi
= to_wm8505fb_info(info
);
146 return sprintf(buf
, "%d\n", fbi
->contrast
);
149 static ssize_t
contrast_store(struct device
*dev
,
150 struct device_attribute
*attr
,
151 const char *buf
, size_t count
)
153 struct fb_info
*info
= dev_get_drvdata(dev
);
154 struct wm8505fb_info
*fbi
= to_wm8505fb_info(info
);
157 if (strict_strtoul(buf
, 10, &tmp
) || (tmp
> 0xff))
161 wm8505fb_set_par(info
);
166 static DEVICE_ATTR(contrast
, 0644, contrast_show
, contrast_store
);
168 static inline u_int
chan_to_field(u_int chan
, struct fb_bitfield
*bf
)
171 chan
>>= 16 - bf
->length
;
172 return chan
<< bf
->offset
;
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
);
184 if (info
->var
.grayscale
)
186 (19595 * red
+ 38470 * green
+ 7471 * blue
) >> 16;
188 switch (fbi
->fb
.fix
.visual
) {
189 case FB_VISUAL_TRUECOLOR
:
191 u32
*pal
= info
->pseudo_palette
;
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
);
206 static int wm8505fb_pan_display(struct fb_var_screeninfo
*var
,
207 struct fb_info
*info
)
209 struct wm8505fb_info
*fbi
= to_wm8505fb_info(info
);
211 writel(var
->xoffset
, fbi
->regbase
+ WMT_GOVR_XPAN
);
212 writel(var
->yoffset
, fbi
->regbase
+ WMT_GOVR_YPAN
);
216 static int wm8505fb_blank(int blank
, struct fb_info
*info
)
218 struct wm8505fb_info
*fbi
= to_wm8505fb_info(info
);
221 case FB_BLANK_UNBLANK
:
222 wm8505fb_set_timing(info
);
225 writel(0, fbi
->regbase
+ WMT_GOVR_TIMING_V_SYNC
);
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
,
244 static int __devinit
wm8505fb_probe(struct platform_device
*pdev
)
246 struct wm8505fb_info
*fbi
;
247 struct resource
*res
;
249 struct vt8500fb_platform_data
*pdata
;
252 pdata
= pdev
->dev
.platform_data
;
257 fbi
= kzalloc(sizeof(struct wm8505fb_info
) + sizeof(u32
) * 16,
260 dev_err(&pdev
->dev
, "Failed to initialize framebuffer device\n");
265 strcpy(fbi
->fb
.fix
.id
, DRIVER_NAME
);
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
;
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
280 | FBINFO_PARTIAL_PAN_OK
;
284 addr
= addr
+ sizeof(struct wm8505fb_info
);
285 fbi
->fb
.pseudo_palette
= addr
;
287 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
289 dev_err(&pdev
->dev
, "no I/O memory resource defined\n");
294 res
= request_mem_region(res
->start
, resource_size(res
), DRIVER_NAME
);
296 dev_err(&pdev
->dev
, "failed to request I/O memory\n");
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");
305 goto failed_free_res
;
308 fb_videomode_to_var(&fbi
->fb
.var
, &pdata
->mode
);
310 fbi
->fb
.var
.nonstd
= 0;
311 fbi
->fb
.var
.activate
= FB_ACTIVATE_NOW
;
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
;
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
;
324 if (fb_alloc_cmap(&fbi
->fb
.cmap
, 256, 0) < 0) {
325 dev_err(&pdev
->dev
, "Failed to allocate color map\n");
330 wm8505fb_init_hw(&fbi
->fb
);
332 fbi
->contrast
= 0x80;
333 ret
= wm8505fb_set_par(&fbi
->fb
);
335 dev_err(&pdev
->dev
, "Failed to set parameters\n");
336 goto failed_free_cmap
;
339 platform_set_drvdata(pdev
, fbi
);
341 ret
= register_framebuffer(&fbi
->fb
);
344 "Failed to register framebuffer device: %d\n", ret
);
345 goto failed_free_cmap
;
348 ret
= device_create_file(&pdev
->dev
, &dev_attr_contrast
);
350 printk(KERN_WARNING
"fb%d: failed to register attributes (%d)\n",
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);
361 if (fbi
->fb
.cmap
.len
)
362 fb_dealloc_cmap(&fbi
->fb
.cmap
);
364 iounmap(fbi
->regbase
);
366 release_mem_region(res
->start
, resource_size(res
));
368 platform_set_drvdata(pdev
, NULL
);
374 static int __devexit
wm8505fb_remove(struct platform_device
*pdev
)
376 struct wm8505fb_info
*fbi
= platform_get_drvdata(pdev
);
377 struct resource
*res
;
379 device_remove_file(&pdev
->dev
, &dev_attr_contrast
);
381 unregister_framebuffer(&fbi
->fb
);
383 writel(0, fbi
->regbase
);
385 if (fbi
->fb
.cmap
.len
)
386 fb_dealloc_cmap(&fbi
->fb
.cmap
);
388 iounmap(fbi
->regbase
);
390 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
391 release_mem_region(res
->start
, resource_size(res
));
398 static struct platform_driver wm8505fb_driver
= {
399 .probe
= wm8505fb_probe
,
400 .remove
= __devexit_p(wm8505fb_remove
),
402 .owner
= THIS_MODULE
,
407 static int __init
wm8505fb_init(void)
409 return platform_driver_register(&wm8505fb_driver
);
412 static void __exit
wm8505fb_exit(void)
414 platform_driver_unregister(&wm8505fb_driver
);
417 module_init(wm8505fb_init
);
418 module_exit(wm8505fb_exit
);
420 MODULE_AUTHOR("Ed Spiridonov <edo.rus@gmail.com>");
421 MODULE_DESCRIPTION("Framebuffer driver for WMT WM8505");
422 MODULE_LICENSE("GPL");