diff options
-rw-r--r-- | drivers/video/Kconfig | 26 | ||||
-rw-r--r-- | drivers/video/Makefile | 3 | ||||
-rw-r--r-- | drivers/video/vt8500lcdfb.c | 447 | ||||
-rw-r--r-- | drivers/video/vt8500lcdfb.h | 34 | ||||
-rw-r--r-- | drivers/video/wm8505fb.c | 422 | ||||
-rw-r--r-- | drivers/video/wm8505fb_regs.h | 76 | ||||
-rw-r--r-- | drivers/video/wmt_ge_rops.c | 192 | ||||
-rw-r--r-- | drivers/video/wmt_ge_rops.h | 5 |
8 files changed, 1205 insertions, 0 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 27c1fb4b1e0d..954f6e9d8d5a 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig | |||
@@ -186,6 +186,14 @@ config FB_SYS_FOPS | |||
186 | depends on FB | 186 | depends on FB |
187 | default n | 187 | default n |
188 | 188 | ||
189 | config FB_WMT_GE_ROPS | ||
190 | tristate | ||
191 | depends on FB | ||
192 | default n | ||
193 | ---help--- | ||
194 | Include functions for accelerated rectangle filling and area | ||
195 | copying using WonderMedia Graphics Engine operations. | ||
196 | |||
189 | config FB_DEFERRED_IO | 197 | config FB_DEFERRED_IO |
190 | bool | 198 | bool |
191 | depends on FB | 199 | depends on FB |
@@ -1722,6 +1730,24 @@ config FB_AU1200 | |||
1722 | various panels and CRTs by passing in kernel cmd line option | 1730 | various panels and CRTs by passing in kernel cmd line option |
1723 | au1200fb:panel=<name>. | 1731 | au1200fb:panel=<name>. |
1724 | 1732 | ||
1733 | config FB_VT8500 | ||
1734 | bool "VT8500 LCD Driver" | ||
1735 | depends on (FB = y) && ARM && ARCH_VT8500 && VTWM_VERSION_VT8500 | ||
1736 | select FB_WMT_GE_ROPS | ||
1737 | select FB_SYS_IMAGEBLIT | ||
1738 | help | ||
1739 | This is the framebuffer driver for VIA VT8500 integrated LCD | ||
1740 | controller. | ||
1741 | |||
1742 | config FB_WM8505 | ||
1743 | bool "WM8505 frame buffer support" | ||
1744 | depends on (FB = y) && ARM && ARCH_VT8500 && VTWM_VERSION_WM8505 | ||
1745 | select FB_WMT_GE_ROPS | ||
1746 | select FB_SYS_IMAGEBLIT | ||
1747 | help | ||
1748 | This is the framebuffer driver for WonderMedia WM8505 | ||
1749 | integrated LCD controller. | ||
1750 | |||
1725 | source "drivers/video/geode/Kconfig" | 1751 | source "drivers/video/geode/Kconfig" |
1726 | 1752 | ||
1727 | config FB_HIT | 1753 | config FB_HIT |
diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 485e8ed1318c..8d916dcb379f 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile | |||
@@ -26,6 +26,7 @@ obj-$(CONFIG_FB_SVGALIB) += svgalib.o | |||
26 | obj-$(CONFIG_FB_MACMODES) += macmodes.o | 26 | obj-$(CONFIG_FB_MACMODES) += macmodes.o |
27 | obj-$(CONFIG_FB_DDC) += fb_ddc.o | 27 | obj-$(CONFIG_FB_DDC) += fb_ddc.o |
28 | obj-$(CONFIG_FB_DEFERRED_IO) += fb_defio.o | 28 | obj-$(CONFIG_FB_DEFERRED_IO) += fb_defio.o |
29 | obj-$(CONFIG_FB_WMT_GE_ROPS) += wmt_ge_rops.o | ||
29 | 30 | ||
30 | # Hardware specific drivers go first | 31 | # Hardware specific drivers go first |
31 | obj-$(CONFIG_FB_AMIGA) += amifb.o c2p_planar.o | 32 | obj-$(CONFIG_FB_AMIGA) += amifb.o c2p_planar.o |
@@ -104,6 +105,8 @@ obj-$(CONFIG_FB_W100) += w100fb.o | |||
104 | obj-$(CONFIG_FB_TMIO) += tmiofb.o | 105 | obj-$(CONFIG_FB_TMIO) += tmiofb.o |
105 | obj-$(CONFIG_FB_AU1100) += au1100fb.o | 106 | obj-$(CONFIG_FB_AU1100) += au1100fb.o |
106 | obj-$(CONFIG_FB_AU1200) += au1200fb.o | 107 | obj-$(CONFIG_FB_AU1200) += au1200fb.o |
108 | obj-$(CONFIG_FB_VT8500) += vt8500lcdfb.o | ||
109 | obj-$(CONFIG_FB_WM8505) += wm8505fb.o | ||
107 | obj-$(CONFIG_FB_PMAG_AA) += pmag-aa-fb.o | 110 | obj-$(CONFIG_FB_PMAG_AA) += pmag-aa-fb.o |
108 | obj-$(CONFIG_FB_PMAG_BA) += pmag-ba-fb.o | 111 | obj-$(CONFIG_FB_PMAG_BA) += pmag-ba-fb.o |
109 | obj-$(CONFIG_FB_PMAGB_B) += pmagb-b-fb.o | 112 | obj-$(CONFIG_FB_PMAGB_B) += pmagb-b-fb.o |
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 | |||
41 | static 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 | |||
136 | static 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 | |||
143 | static 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 | |||
182 | static 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 | |||
204 | static 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 | |||
218 | static 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 | |||
230 | static 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 | |||
241 | static 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 | |||
375 | failed_free_cmap: | ||
376 | if (fbi->fb.cmap.len) | ||
377 | fb_dealloc_cmap(&fbi->fb.cmap); | ||
378 | failed_free_irq: | ||
379 | free_irq(irq, fbi); | ||
380 | failed_free_palette: | ||
381 | dma_free_coherent(&pdev->dev, fbi->palette_size, | ||
382 | fbi->palette_cpu, fbi->palette_phys); | ||
383 | failed_free_io: | ||
384 | iounmap(fbi->regbase); | ||
385 | failed_free_res: | ||
386 | release_mem_region(res->start, resource_size(res)); | ||
387 | failed_fbi: | ||
388 | platform_set_drvdata(pdev, NULL); | ||
389 | kfree(fbi); | ||
390 | failed: | ||
391 | return ret; | ||
392 | } | ||
393 | |||
394 | static 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 | |||
423 | static 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 | |||
432 | static int __init vt8500lcd_init(void) | ||
433 | { | ||
434 | return platform_driver_register(&vt8500lcd_driver); | ||
435 | } | ||
436 | |||
437 | static void __exit vt8500lcd_exit(void) | ||
438 | { | ||
439 | platform_driver_unregister(&vt8500lcd_driver); | ||
440 | } | ||
441 | |||
442 | module_init(vt8500lcd_init); | ||
443 | module_exit(vt8500lcd_exit); | ||
444 | |||
445 | MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>"); | ||
446 | MODULE_DESCRIPTION("LCD controller driver for VIA VT8500"); | ||
447 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/video/vt8500lcdfb.h b/drivers/video/vt8500lcdfb.h new file mode 100644 index 000000000000..36ca3ca09d83 --- /dev/null +++ b/drivers/video/vt8500lcdfb.h | |||
@@ -0,0 +1,34 @@ | |||
1 | /* | ||
2 | * linux/drivers/video/vt8500lcdfb.h | ||
3 | * | ||
4 | * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com> | ||
5 | * | ||
6 | * This software is licensed under the terms of the GNU General Public | ||
7 | * License version 2, as published by the Free Software Foundation, and | ||
8 | * may be copied, distributed, and modified under those terms. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | */ | ||
15 | |||
16 | struct vt8500lcd_info { | ||
17 | struct fb_info fb; | ||
18 | void __iomem *regbase; | ||
19 | void __iomem *palette_cpu; | ||
20 | dma_addr_t palette_phys; | ||
21 | size_t palette_size; | ||
22 | wait_queue_head_t wait; | ||
23 | }; | ||
24 | |||
25 | static int bpp_values[] = { | ||
26 | 1, | ||
27 | 2, | ||
28 | 4, | ||
29 | 8, | ||
30 | 12, | ||
31 | 16, | ||
32 | 18, | ||
33 | 24, | ||
34 | }; | ||
diff --git a/drivers/video/wm8505fb.c b/drivers/video/wm8505fb.c new file mode 100644 index 000000000000..e37251b792c9 --- /dev/null +++ b/drivers/video/wm8505fb.c | |||
@@ -0,0 +1,422 @@ | |||
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"); | ||
diff --git a/drivers/video/wm8505fb_regs.h b/drivers/video/wm8505fb_regs.h new file mode 100644 index 000000000000..4dd41668c6d1 --- /dev/null +++ b/drivers/video/wm8505fb_regs.h | |||
@@ -0,0 +1,76 @@ | |||
1 | /* | ||
2 | * GOVR registers list for WM8505 chips | ||
3 | * | ||
4 | * Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com> | ||
5 | * Based on VIA/WonderMedia wm8510-govrh-reg.h | ||
6 | * http://github.com/projectgus/kernel_wm8505/blob/wm8505_2.6.29/ | ||
7 | * drivers/video/wmt/register/wm8510/wm8510-govrh-reg.h | ||
8 | * | ||
9 | * This software is licensed under the terms of the GNU General Public | ||
10 | * License version 2, as published by the Free Software Foundation, and | ||
11 | * may be copied, distributed, and modified under those terms. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | */ | ||
18 | |||
19 | #ifndef _WM8505FB_REGS_H | ||
20 | #define _WM8505FB_REGS_H | ||
21 | |||
22 | /* | ||
23 | * Color space select register, default value 0x1c | ||
24 | * BIT0 GOVRH_DVO_YUV2RGB_ENABLE | ||
25 | * BIT1 GOVRH_VGA_YUV2RGB_ENABLE | ||
26 | * BIT2 GOVRH_RGB_MODE | ||
27 | * BIT3 GOVRH_DAC_CLKINV | ||
28 | * BIT4 GOVRH_BLANK_ZERO | ||
29 | */ | ||
30 | #define WMT_GOVR_COLORSPACE 0x1e4 | ||
31 | /* | ||
32 | * Another colorspace select register, default value 1 | ||
33 | * BIT0 GOVRH_DVO_RGB | ||
34 | * BIT1 GOVRH_DVO_YUV422 | ||
35 | */ | ||
36 | #define WMT_GOVR_COLORSPACE1 0x30 | ||
37 | |||
38 | #define WMT_GOVR_CONTRAST 0x1b8 | ||
39 | #define WMT_GOVR_BRGHTNESS 0x1bc /* incompatible with RGB? */ | ||
40 | |||
41 | /* Framubeffer address */ | ||
42 | #define WMT_GOVR_FBADDR 0x90 | ||
43 | #define WMT_GOVR_FBADDR1 0x94 /* UV offset in YUV mode */ | ||
44 | |||
45 | /* Offset of visible window */ | ||
46 | #define WMT_GOVR_XPAN 0xa4 | ||
47 | #define WMT_GOVR_YPAN 0xa0 | ||
48 | |||
49 | #define WMT_GOVR_XRES 0x98 | ||
50 | #define WMT_GOVR_XRES_VIRTUAL 0x9c | ||
51 | |||
52 | #define WMT_GOVR_MIF_ENABLE 0x80 | ||
53 | #define WMT_GOVR_FHI 0xa8 | ||
54 | #define WMT_GOVR_REG_UPDATE 0xe4 | ||
55 | |||
56 | /* | ||
57 | * BIT0 GOVRH_DVO_OUTWIDTH | ||
58 | * BIT1 GOVRH_DVO_SYNC_POLAR | ||
59 | * BIT2 GOVRH_DVO_ENABLE | ||
60 | */ | ||
61 | #define WMT_GOVR_DVO_SET 0x148 | ||
62 | |||
63 | /* Timing generator? */ | ||
64 | #define WMT_GOVR_TG 0x100 | ||
65 | |||
66 | /* Timings */ | ||
67 | #define WMT_GOVR_TIMING_H_ALL 0x108 | ||
68 | #define WMT_GOVR_TIMING_V_ALL 0x10c | ||
69 | #define WMT_GOVR_TIMING_V_START 0x110 | ||
70 | #define WMT_GOVR_TIMING_V_END 0x114 | ||
71 | #define WMT_GOVR_TIMING_H_START 0x118 | ||
72 | #define WMT_GOVR_TIMING_H_END 0x11c | ||
73 | #define WMT_GOVR_TIMING_V_SYNC 0x128 | ||
74 | #define WMT_GOVR_TIMING_H_SYNC 0x12c | ||
75 | |||
76 | #endif /* _WM8505FB_REGS_H */ | ||
diff --git a/drivers/video/wmt_ge_rops.c b/drivers/video/wmt_ge_rops.c new file mode 100644 index 000000000000..f31883f8eaf7 --- /dev/null +++ b/drivers/video/wmt_ge_rops.c | |||
@@ -0,0 +1,192 @@ | |||
1 | /* | ||
2 | * linux/drivers/video/wmt_ge_rops.c | ||
3 | * | ||
4 | * Accelerators for raster operations using WonderMedia Graphics Engine | ||
5 | * | ||
6 | * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com> | ||
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/fb.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include "fb_draw.h" | ||
22 | |||
23 | #define GE_COMMAND_OFF 0x00 | ||
24 | #define GE_DEPTH_OFF 0x04 | ||
25 | #define GE_HIGHCOLOR_OFF 0x08 | ||
26 | #define GE_ROPCODE_OFF 0x14 | ||
27 | #define GE_FIRE_OFF 0x18 | ||
28 | #define GE_SRCBASE_OFF 0x20 | ||
29 | #define GE_SRCDISPW_OFF 0x24 | ||
30 | #define GE_SRCDISPH_OFF 0x28 | ||
31 | #define GE_SRCAREAX_OFF 0x2c | ||
32 | #define GE_SRCAREAY_OFF 0x30 | ||
33 | #define GE_SRCAREAW_OFF 0x34 | ||
34 | #define GE_SRCAREAH_OFF 0x38 | ||
35 | #define GE_DESTBASE_OFF 0x3c | ||
36 | #define GE_DESTDISPW_OFF 0x40 | ||
37 | #define GE_DESTDISPH_OFF 0x44 | ||
38 | #define GE_DESTAREAX_OFF 0x48 | ||
39 | #define GE_DESTAREAY_OFF 0x4c | ||
40 | #define GE_DESTAREAW_OFF 0x50 | ||
41 | #define GE_DESTAREAH_OFF 0x54 | ||
42 | #define GE_PAT0C_OFF 0x88 /* Pattern 0 color */ | ||
43 | #define GE_ENABLE_OFF 0xec | ||
44 | #define GE_INTEN_OFF 0xf0 | ||
45 | #define GE_STATUS_OFF 0xf8 | ||
46 | |||
47 | static void __iomem *regbase; | ||
48 | |||
49 | void wmt_ge_fillrect(struct fb_info *p, const struct fb_fillrect *rect) | ||
50 | { | ||
51 | unsigned long fg, pat; | ||
52 | |||
53 | if (p->state != FBINFO_STATE_RUNNING) | ||
54 | return; | ||
55 | |||
56 | if (p->fix.visual == FB_VISUAL_TRUECOLOR || | ||
57 | p->fix.visual == FB_VISUAL_DIRECTCOLOR) | ||
58 | fg = ((u32 *) (p->pseudo_palette))[rect->color]; | ||
59 | else | ||
60 | fg = rect->color; | ||
61 | |||
62 | pat = pixel_to_pat(p->var.bits_per_pixel, fg); | ||
63 | |||
64 | if (p->fbops->fb_sync) | ||
65 | p->fbops->fb_sync(p); | ||
66 | |||
67 | writel(p->var.bits_per_pixel == 32 ? 3 : | ||
68 | (p->var.bits_per_pixel == 8 ? 0 : 1), regbase + GE_DEPTH_OFF); | ||
69 | writel(p->var.bits_per_pixel == 15 ? 1 : 0, regbase + GE_HIGHCOLOR_OFF); | ||
70 | writel(p->fix.smem_start, regbase + GE_DESTBASE_OFF); | ||
71 | writel(p->var.xres_virtual - 1, regbase + GE_DESTDISPW_OFF); | ||
72 | writel(p->var.yres_virtual - 1, regbase + GE_DESTDISPH_OFF); | ||
73 | writel(rect->dx, regbase + GE_DESTAREAX_OFF); | ||
74 | writel(rect->dy, regbase + GE_DESTAREAY_OFF); | ||
75 | writel(rect->width - 1, regbase + GE_DESTAREAW_OFF); | ||
76 | writel(rect->height - 1, regbase + GE_DESTAREAH_OFF); | ||
77 | |||
78 | writel(pat, regbase + GE_PAT0C_OFF); | ||
79 | writel(1, regbase + GE_COMMAND_OFF); | ||
80 | writel(rect->rop == ROP_XOR ? 0x5a : 0xf0, regbase + GE_ROPCODE_OFF); | ||
81 | writel(1, regbase + GE_FIRE_OFF); | ||
82 | } | ||
83 | EXPORT_SYMBOL_GPL(wmt_ge_fillrect); | ||
84 | |||
85 | void wmt_ge_copyarea(struct fb_info *p, const struct fb_copyarea *area) | ||
86 | { | ||
87 | if (p->state != FBINFO_STATE_RUNNING) | ||
88 | return; | ||
89 | |||
90 | if (p->fbops->fb_sync) | ||
91 | p->fbops->fb_sync(p); | ||
92 | |||
93 | writel(p->var.bits_per_pixel > 16 ? 3 : | ||
94 | (p->var.bits_per_pixel > 8 ? 1 : 0), regbase + GE_DEPTH_OFF); | ||
95 | |||
96 | writel(p->fix.smem_start, regbase + GE_SRCBASE_OFF); | ||
97 | writel(p->var.xres_virtual - 1, regbase + GE_SRCDISPW_OFF); | ||
98 | writel(p->var.yres_virtual - 1, regbase + GE_SRCDISPH_OFF); | ||
99 | writel(area->sx, regbase + GE_SRCAREAX_OFF); | ||
100 | writel(area->sy, regbase + GE_SRCAREAY_OFF); | ||
101 | writel(area->width - 1, regbase + GE_SRCAREAW_OFF); | ||
102 | writel(area->height - 1, regbase + GE_SRCAREAH_OFF); | ||
103 | |||
104 | writel(p->fix.smem_start, regbase + GE_DESTBASE_OFF); | ||
105 | writel(p->var.xres_virtual - 1, regbase + GE_DESTDISPW_OFF); | ||
106 | writel(p->var.yres_virtual - 1, regbase + GE_DESTDISPH_OFF); | ||
107 | writel(area->dx, regbase + GE_DESTAREAX_OFF); | ||
108 | writel(area->dy, regbase + GE_DESTAREAY_OFF); | ||
109 | writel(area->width - 1, regbase + GE_DESTAREAW_OFF); | ||
110 | writel(area->height - 1, regbase + GE_DESTAREAH_OFF); | ||
111 | |||
112 | writel(0xcc, regbase + GE_ROPCODE_OFF); | ||
113 | writel(1, regbase + GE_COMMAND_OFF); | ||
114 | writel(1, regbase + GE_FIRE_OFF); | ||
115 | } | ||
116 | EXPORT_SYMBOL_GPL(wmt_ge_copyarea); | ||
117 | |||
118 | int wmt_ge_sync(struct fb_info *p) | ||
119 | { | ||
120 | int loops = 5000000; | ||
121 | while ((readl(regbase + GE_STATUS_OFF) & 4) && --loops) | ||
122 | cpu_relax(); | ||
123 | return loops > 0 ? 0 : -EBUSY; | ||
124 | } | ||
125 | EXPORT_SYMBOL_GPL(wmt_ge_sync); | ||
126 | |||
127 | static int __devinit wmt_ge_rops_probe(struct platform_device *pdev) | ||
128 | { | ||
129 | struct resource *res; | ||
130 | int ret; | ||
131 | |||
132 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
133 | if (res == NULL) { | ||
134 | dev_err(&pdev->dev, "no I/O memory resource defined\n"); | ||
135 | ret = -ENODEV; | ||
136 | goto error; | ||
137 | } | ||
138 | |||
139 | /* Only one ROP engine is presently supported. */ | ||
140 | if (unlikely(regbase)) { | ||
141 | WARN_ON(1); | ||
142 | return -EBUSY; | ||
143 | } | ||
144 | |||
145 | regbase = ioremap(res->start, resource_size(res)); | ||
146 | if (regbase == NULL) { | ||
147 | dev_err(&pdev->dev, "failed to map I/O memory\n"); | ||
148 | ret = -EBUSY; | ||
149 | goto error; | ||
150 | } | ||
151 | |||
152 | writel(1, regbase + GE_ENABLE_OFF); | ||
153 | printk(KERN_INFO "Enabled support for WMT GE raster acceleration\n"); | ||
154 | |||
155 | return 0; | ||
156 | |||
157 | error: | ||
158 | return ret; | ||
159 | } | ||
160 | |||
161 | static int __devexit wmt_ge_rops_remove(struct platform_device *pdev) | ||
162 | { | ||
163 | iounmap(regbase); | ||
164 | return 0; | ||
165 | } | ||
166 | |||
167 | static struct platform_driver wmt_ge_rops_driver = { | ||
168 | .probe = wmt_ge_rops_probe, | ||
169 | .remove = __devexit_p(wmt_ge_rops_remove), | ||
170 | .driver = { | ||
171 | .owner = THIS_MODULE, | ||
172 | .name = "wmt_ge_rops", | ||
173 | }, | ||
174 | }; | ||
175 | |||
176 | static int __init wmt_ge_rops_init(void) | ||
177 | { | ||
178 | return platform_driver_register(&wmt_ge_rops_driver); | ||
179 | } | ||
180 | |||
181 | static void __exit wmt_ge_rops_exit(void) | ||
182 | { | ||
183 | platform_driver_unregister(&wmt_ge_rops_driver); | ||
184 | } | ||
185 | |||
186 | module_init(wmt_ge_rops_init); | ||
187 | module_exit(wmt_ge_rops_exit); | ||
188 | |||
189 | MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com"); | ||
190 | MODULE_DESCRIPTION("Accelerators for raster operations using " | ||
191 | "WonderMedia Graphics Engine"); | ||
192 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/video/wmt_ge_rops.h b/drivers/video/wmt_ge_rops.h new file mode 100644 index 000000000000..87380751a443 --- /dev/null +++ b/drivers/video/wmt_ge_rops.h | |||
@@ -0,0 +1,5 @@ | |||
1 | extern void wmt_ge_fillrect(struct fb_info *info, | ||
2 | const struct fb_fillrect *rect); | ||
3 | extern void wmt_ge_copyarea(struct fb_info *info, | ||
4 | const struct fb_copyarea *area); | ||
5 | extern int wmt_ge_sync(struct fb_info *info); | ||