19c92ab61SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f7018c21STomi Valkeinen /*
3f7018c21STomi Valkeinen * WonderMedia WM8505 Frame Buffer device driver
4f7018c21STomi Valkeinen *
5f7018c21STomi Valkeinen * Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com>
6f7018c21STomi Valkeinen * Based on vt8500lcdfb.c
7f7018c21STomi Valkeinen */
8f7018c21STomi Valkeinen
9f7018c21STomi Valkeinen #include <linux/delay.h>
10f7018c21STomi Valkeinen #include <linux/dma-mapping.h>
11f7018c21STomi Valkeinen #include <linux/fb.h>
12f7018c21STomi Valkeinen #include <linux/errno.h>
13f7018c21STomi Valkeinen #include <linux/err.h>
14f7018c21STomi Valkeinen #include <linux/init.h>
15f7018c21STomi Valkeinen #include <linux/interrupt.h>
16f7018c21STomi Valkeinen #include <linux/io.h>
17f7018c21STomi Valkeinen #include <linux/kernel.h>
18f7018c21STomi Valkeinen #include <linux/memblock.h>
19f7018c21STomi Valkeinen #include <linux/mm.h>
20f7018c21STomi Valkeinen #include <linux/module.h>
21f7018c21STomi Valkeinen #include <linux/of.h>
22f7018c21STomi Valkeinen #include <linux/of_fdt.h>
23f7018c21STomi Valkeinen #include <linux/platform_device.h>
24f7018c21STomi Valkeinen #include <linux/slab.h>
25f7018c21STomi Valkeinen #include <linux/string.h>
26f7018c21STomi Valkeinen #include <linux/wait.h>
27f7018c21STomi Valkeinen #include <video/of_display_timing.h>
28f7018c21STomi Valkeinen
29f7018c21STomi Valkeinen #include "wm8505fb_regs.h"
30f7018c21STomi Valkeinen #include "wmt_ge_rops.h"
31f7018c21STomi Valkeinen
32f7018c21STomi Valkeinen #define DRIVER_NAME "wm8505-fb"
33f7018c21STomi Valkeinen
34f7018c21STomi Valkeinen #define to_wm8505fb_info(__info) container_of(__info, \
35f7018c21STomi Valkeinen struct wm8505fb_info, fb)
36f7018c21STomi Valkeinen struct wm8505fb_info {
37f7018c21STomi Valkeinen struct fb_info fb;
38f7018c21STomi Valkeinen void __iomem *regbase;
39f7018c21STomi Valkeinen unsigned int contrast;
40f7018c21STomi Valkeinen };
41f7018c21STomi Valkeinen
42f7018c21STomi Valkeinen
wm8505fb_init_hw(struct fb_info * info)43f7018c21STomi Valkeinen static int wm8505fb_init_hw(struct fb_info *info)
44f7018c21STomi Valkeinen {
45f7018c21STomi Valkeinen struct wm8505fb_info *fbi = to_wm8505fb_info(info);
46f7018c21STomi Valkeinen
47f7018c21STomi Valkeinen int i;
48f7018c21STomi Valkeinen
49f7018c21STomi Valkeinen /* I know the purpose only of few registers, so clear unknown */
50f7018c21STomi Valkeinen for (i = 0; i < 0x200; i += 4)
51f7018c21STomi Valkeinen writel(0, fbi->regbase + i);
52f7018c21STomi Valkeinen
53f7018c21STomi Valkeinen /* Set frame buffer address */
54f7018c21STomi Valkeinen writel(fbi->fb.fix.smem_start, fbi->regbase + WMT_GOVR_FBADDR);
55f7018c21STomi Valkeinen writel(fbi->fb.fix.smem_start, fbi->regbase + WMT_GOVR_FBADDR1);
56f7018c21STomi Valkeinen
57f7018c21STomi Valkeinen /*
58f7018c21STomi Valkeinen * Set in-memory picture format to RGB
59f7018c21STomi Valkeinen * 0x31C sets the correct color mode (RGB565) for WM8650
60f7018c21STomi Valkeinen * Bit 8+9 (0x300) are ignored on WM8505 as reserved
61f7018c21STomi Valkeinen */
62f7018c21STomi Valkeinen writel(0x31c, fbi->regbase + WMT_GOVR_COLORSPACE);
63f7018c21STomi Valkeinen writel(1, fbi->regbase + WMT_GOVR_COLORSPACE1);
64f7018c21STomi Valkeinen
65f7018c21STomi Valkeinen /* Virtual buffer size */
66f7018c21STomi Valkeinen writel(info->var.xres, fbi->regbase + WMT_GOVR_XRES);
67f7018c21STomi Valkeinen writel(info->var.xres_virtual, fbi->regbase + WMT_GOVR_XRES_VIRTUAL);
68f7018c21STomi Valkeinen
69f7018c21STomi Valkeinen /* black magic ;) */
70f7018c21STomi Valkeinen writel(0xf, fbi->regbase + WMT_GOVR_FHI);
71f7018c21STomi Valkeinen writel(4, fbi->regbase + WMT_GOVR_DVO_SET);
72f7018c21STomi Valkeinen writel(1, fbi->regbase + WMT_GOVR_MIF_ENABLE);
73f7018c21STomi Valkeinen writel(1, fbi->regbase + WMT_GOVR_REG_UPDATE);
74f7018c21STomi Valkeinen
75f7018c21STomi Valkeinen return 0;
76f7018c21STomi Valkeinen }
77f7018c21STomi Valkeinen
wm8505fb_set_timing(struct fb_info * info)78f7018c21STomi Valkeinen static int wm8505fb_set_timing(struct fb_info *info)
79f7018c21STomi Valkeinen {
80f7018c21STomi Valkeinen struct wm8505fb_info *fbi = to_wm8505fb_info(info);
81f7018c21STomi Valkeinen
82f7018c21STomi Valkeinen int h_start = info->var.left_margin;
83f7018c21STomi Valkeinen int h_end = h_start + info->var.xres;
84f7018c21STomi Valkeinen int h_all = h_end + info->var.right_margin;
85f7018c21STomi Valkeinen int h_sync = info->var.hsync_len;
86f7018c21STomi Valkeinen
87f7018c21STomi Valkeinen int v_start = info->var.upper_margin;
88f7018c21STomi Valkeinen int v_end = v_start + info->var.yres;
89f7018c21STomi Valkeinen int v_all = v_end + info->var.lower_margin;
90f7018c21STomi Valkeinen int v_sync = info->var.vsync_len;
91f7018c21STomi Valkeinen
92f7018c21STomi Valkeinen writel(0, fbi->regbase + WMT_GOVR_TG);
93f7018c21STomi Valkeinen
94f7018c21STomi Valkeinen writel(h_start, fbi->regbase + WMT_GOVR_TIMING_H_START);
95f7018c21STomi Valkeinen writel(h_end, fbi->regbase + WMT_GOVR_TIMING_H_END);
96f7018c21STomi Valkeinen writel(h_all, fbi->regbase + WMT_GOVR_TIMING_H_ALL);
97f7018c21STomi Valkeinen writel(h_sync, fbi->regbase + WMT_GOVR_TIMING_H_SYNC);
98f7018c21STomi Valkeinen
99f7018c21STomi Valkeinen writel(v_start, fbi->regbase + WMT_GOVR_TIMING_V_START);
100f7018c21STomi Valkeinen writel(v_end, fbi->regbase + WMT_GOVR_TIMING_V_END);
101f7018c21STomi Valkeinen writel(v_all, fbi->regbase + WMT_GOVR_TIMING_V_ALL);
102f7018c21STomi Valkeinen writel(v_sync, fbi->regbase + WMT_GOVR_TIMING_V_SYNC);
103f7018c21STomi Valkeinen
104f7018c21STomi Valkeinen writel(1, fbi->regbase + WMT_GOVR_TG);
105f7018c21STomi Valkeinen
106f7018c21STomi Valkeinen return 0;
107f7018c21STomi Valkeinen }
108f7018c21STomi Valkeinen
109f7018c21STomi Valkeinen
wm8505fb_set_par(struct fb_info * info)110f7018c21STomi Valkeinen static int wm8505fb_set_par(struct fb_info *info)
111f7018c21STomi Valkeinen {
112f7018c21STomi Valkeinen struct wm8505fb_info *fbi = to_wm8505fb_info(info);
113f7018c21STomi Valkeinen
114f7018c21STomi Valkeinen if (!fbi)
115f7018c21STomi Valkeinen return -EINVAL;
116f7018c21STomi Valkeinen
117f7018c21STomi Valkeinen if (info->var.bits_per_pixel == 32) {
118f7018c21STomi Valkeinen info->var.red.offset = 16;
119f7018c21STomi Valkeinen info->var.red.length = 8;
120f7018c21STomi Valkeinen info->var.red.msb_right = 0;
121f7018c21STomi Valkeinen info->var.green.offset = 8;
122f7018c21STomi Valkeinen info->var.green.length = 8;
123f7018c21STomi Valkeinen info->var.green.msb_right = 0;
124f7018c21STomi Valkeinen info->var.blue.offset = 0;
125f7018c21STomi Valkeinen info->var.blue.length = 8;
126f7018c21STomi Valkeinen info->var.blue.msb_right = 0;
127f7018c21STomi Valkeinen info->fix.visual = FB_VISUAL_TRUECOLOR;
128f7018c21STomi Valkeinen info->fix.line_length = info->var.xres_virtual << 2;
129f7018c21STomi Valkeinen } else if (info->var.bits_per_pixel == 16) {
130f7018c21STomi Valkeinen info->var.red.offset = 11;
131f7018c21STomi Valkeinen info->var.red.length = 5;
132f7018c21STomi Valkeinen info->var.red.msb_right = 0;
133f7018c21STomi Valkeinen info->var.green.offset = 5;
134f7018c21STomi Valkeinen info->var.green.length = 6;
135f7018c21STomi Valkeinen info->var.green.msb_right = 0;
136f7018c21STomi Valkeinen info->var.blue.offset = 0;
137f7018c21STomi Valkeinen info->var.blue.length = 5;
138f7018c21STomi Valkeinen info->var.blue.msb_right = 0;
139f7018c21STomi Valkeinen info->fix.visual = FB_VISUAL_TRUECOLOR;
140f7018c21STomi Valkeinen info->fix.line_length = info->var.xres_virtual << 1;
141f7018c21STomi Valkeinen }
142f7018c21STomi Valkeinen
143f7018c21STomi Valkeinen wm8505fb_set_timing(info);
144f7018c21STomi Valkeinen
145f7018c21STomi Valkeinen writel(fbi->contrast<<16 | fbi->contrast<<8 | fbi->contrast,
146f7018c21STomi Valkeinen fbi->regbase + WMT_GOVR_CONTRAST);
147f7018c21STomi Valkeinen
148f7018c21STomi Valkeinen return 0;
149f7018c21STomi Valkeinen }
150f7018c21STomi Valkeinen
contrast_show(struct device * dev,struct device_attribute * attr,char * buf)151f7018c21STomi Valkeinen static ssize_t contrast_show(struct device *dev,
152f7018c21STomi Valkeinen struct device_attribute *attr, char *buf)
153f7018c21STomi Valkeinen {
154f7018c21STomi Valkeinen struct fb_info *info = dev_get_drvdata(dev);
155f7018c21STomi Valkeinen struct wm8505fb_info *fbi = to_wm8505fb_info(info);
156f7018c21STomi Valkeinen
1573ea4ccd0SMasanari Iida return sprintf(buf, "%u\n", fbi->contrast);
158f7018c21STomi Valkeinen }
159f7018c21STomi Valkeinen
contrast_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)160f7018c21STomi Valkeinen static ssize_t contrast_store(struct device *dev,
161f7018c21STomi Valkeinen struct device_attribute *attr,
162f7018c21STomi Valkeinen const char *buf, size_t count)
163f7018c21STomi Valkeinen {
164f7018c21STomi Valkeinen struct fb_info *info = dev_get_drvdata(dev);
165f7018c21STomi Valkeinen struct wm8505fb_info *fbi = to_wm8505fb_info(info);
166f7018c21STomi Valkeinen unsigned long tmp;
167f7018c21STomi Valkeinen
168f7018c21STomi Valkeinen if (kstrtoul(buf, 10, &tmp) || (tmp > 0xff))
169f7018c21STomi Valkeinen return -EINVAL;
170f7018c21STomi Valkeinen fbi->contrast = tmp;
171f7018c21STomi Valkeinen
172f7018c21STomi Valkeinen wm8505fb_set_par(info);
173f7018c21STomi Valkeinen
174f7018c21STomi Valkeinen return count;
175f7018c21STomi Valkeinen }
176f7018c21STomi Valkeinen
177a0a74270SJulia Lawall static DEVICE_ATTR_RW(contrast);
178f7018c21STomi Valkeinen
1790abd02edSGreg Kroah-Hartman static struct attribute *wm8505fb_attrs[] = {
1800abd02edSGreg Kroah-Hartman &dev_attr_contrast.attr,
1810abd02edSGreg Kroah-Hartman NULL,
1820abd02edSGreg Kroah-Hartman };
1830abd02edSGreg Kroah-Hartman ATTRIBUTE_GROUPS(wm8505fb);
1840abd02edSGreg Kroah-Hartman
chan_to_field(u_int chan,struct fb_bitfield * bf)185f7018c21STomi Valkeinen static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
186f7018c21STomi Valkeinen {
187f7018c21STomi Valkeinen chan &= 0xffff;
188f7018c21STomi Valkeinen chan >>= 16 - bf->length;
189f7018c21STomi Valkeinen return chan << bf->offset;
190f7018c21STomi Valkeinen }
191f7018c21STomi Valkeinen
wm8505fb_setcolreg(unsigned regno,unsigned red,unsigned green,unsigned blue,unsigned transp,struct fb_info * info)192f7018c21STomi Valkeinen static int wm8505fb_setcolreg(unsigned regno, unsigned red, unsigned green,
193f7018c21STomi Valkeinen unsigned blue, unsigned transp,
194f7018c21STomi Valkeinen struct fb_info *info) {
195f7018c21STomi Valkeinen struct wm8505fb_info *fbi = to_wm8505fb_info(info);
196f7018c21STomi Valkeinen int ret = 1;
197f7018c21STomi Valkeinen unsigned int val;
198f7018c21STomi Valkeinen if (regno >= 256)
199f7018c21STomi Valkeinen return -EINVAL;
200f7018c21STomi Valkeinen
201f7018c21STomi Valkeinen if (info->var.grayscale)
202f7018c21STomi Valkeinen red = green = blue =
203f7018c21STomi Valkeinen (19595 * red + 38470 * green + 7471 * blue) >> 16;
204f7018c21STomi Valkeinen
205f7018c21STomi Valkeinen switch (fbi->fb.fix.visual) {
206f7018c21STomi Valkeinen case FB_VISUAL_TRUECOLOR:
207f7018c21STomi Valkeinen if (regno < 16) {
208f7018c21STomi Valkeinen u32 *pal = info->pseudo_palette;
209f7018c21STomi Valkeinen
210f7018c21STomi Valkeinen val = chan_to_field(red, &fbi->fb.var.red);
211f7018c21STomi Valkeinen val |= chan_to_field(green, &fbi->fb.var.green);
212f7018c21STomi Valkeinen val |= chan_to_field(blue, &fbi->fb.var.blue);
213f7018c21STomi Valkeinen
214f7018c21STomi Valkeinen pal[regno] = val;
215f7018c21STomi Valkeinen ret = 0;
216f7018c21STomi Valkeinen }
217f7018c21STomi Valkeinen break;
218f7018c21STomi Valkeinen }
219f7018c21STomi Valkeinen
220f7018c21STomi Valkeinen return ret;
221f7018c21STomi Valkeinen }
222f7018c21STomi Valkeinen
wm8505fb_pan_display(struct fb_var_screeninfo * var,struct fb_info * info)223f7018c21STomi Valkeinen static int wm8505fb_pan_display(struct fb_var_screeninfo *var,
224f7018c21STomi Valkeinen struct fb_info *info)
225f7018c21STomi Valkeinen {
226f7018c21STomi Valkeinen struct wm8505fb_info *fbi = to_wm8505fb_info(info);
227f7018c21STomi Valkeinen
228f7018c21STomi Valkeinen writel(var->xoffset, fbi->regbase + WMT_GOVR_XPAN);
229f7018c21STomi Valkeinen writel(var->yoffset, fbi->regbase + WMT_GOVR_YPAN);
230f7018c21STomi Valkeinen return 0;
231f7018c21STomi Valkeinen }
232f7018c21STomi Valkeinen
wm8505fb_blank(int blank,struct fb_info * info)233f7018c21STomi Valkeinen static int wm8505fb_blank(int blank, struct fb_info *info)
234f7018c21STomi Valkeinen {
235f7018c21STomi Valkeinen struct wm8505fb_info *fbi = to_wm8505fb_info(info);
236f7018c21STomi Valkeinen
237f7018c21STomi Valkeinen switch (blank) {
238f7018c21STomi Valkeinen case FB_BLANK_UNBLANK:
239f7018c21STomi Valkeinen wm8505fb_set_timing(info);
240f7018c21STomi Valkeinen break;
241f7018c21STomi Valkeinen default:
242f7018c21STomi Valkeinen writel(0, fbi->regbase + WMT_GOVR_TIMING_V_SYNC);
243f7018c21STomi Valkeinen break;
244f7018c21STomi Valkeinen }
245f7018c21STomi Valkeinen
246f7018c21STomi Valkeinen return 0;
247f7018c21STomi Valkeinen }
248f7018c21STomi Valkeinen
2498a48ac33SJani Nikula static const struct fb_ops wm8505fb_ops = {
250f7018c21STomi Valkeinen .owner = THIS_MODULE,
251f7018c21STomi Valkeinen .fb_set_par = wm8505fb_set_par,
252f7018c21STomi Valkeinen .fb_setcolreg = wm8505fb_setcolreg,
253f7018c21STomi Valkeinen .fb_fillrect = wmt_ge_fillrect,
254f7018c21STomi Valkeinen .fb_copyarea = wmt_ge_copyarea,
255f7018c21STomi Valkeinen .fb_imageblit = sys_imageblit,
256f7018c21STomi Valkeinen .fb_sync = wmt_ge_sync,
257f7018c21STomi Valkeinen .fb_pan_display = wm8505fb_pan_display,
258f7018c21STomi Valkeinen .fb_blank = wm8505fb_blank,
259f7018c21STomi Valkeinen };
260f7018c21STomi Valkeinen
wm8505fb_probe(struct platform_device * pdev)261f7018c21STomi Valkeinen static int wm8505fb_probe(struct platform_device *pdev)
262f7018c21STomi Valkeinen {
263f7018c21STomi Valkeinen struct wm8505fb_info *fbi;
264f7018c21STomi Valkeinen struct display_timings *disp_timing;
265f7018c21STomi Valkeinen void *addr;
266f7018c21STomi Valkeinen int ret;
267f7018c21STomi Valkeinen
268f7018c21STomi Valkeinen struct fb_videomode mode;
269f7018c21STomi Valkeinen u32 bpp;
270f7018c21STomi Valkeinen dma_addr_t fb_mem_phys;
271f7018c21STomi Valkeinen unsigned long fb_mem_len;
272f7018c21STomi Valkeinen void *fb_mem_virt;
273f7018c21STomi Valkeinen
274f7018c21STomi Valkeinen fbi = devm_kzalloc(&pdev->dev, sizeof(struct wm8505fb_info) +
275f7018c21STomi Valkeinen sizeof(u32) * 16, GFP_KERNEL);
276d961b8a9SMarkus Elfring if (!fbi)
277f7018c21STomi Valkeinen return -ENOMEM;
278f7018c21STomi Valkeinen
279f7018c21STomi Valkeinen strcpy(fbi->fb.fix.id, DRIVER_NAME);
280f7018c21STomi Valkeinen
281f7018c21STomi Valkeinen fbi->fb.fix.type = FB_TYPE_PACKED_PIXELS;
282f7018c21STomi Valkeinen fbi->fb.fix.xpanstep = 1;
283f7018c21STomi Valkeinen fbi->fb.fix.ypanstep = 1;
284f7018c21STomi Valkeinen fbi->fb.fix.ywrapstep = 0;
285f7018c21STomi Valkeinen fbi->fb.fix.accel = FB_ACCEL_NONE;
286f7018c21STomi Valkeinen
287f7018c21STomi Valkeinen fbi->fb.fbops = &wm8505fb_ops;
288*45733d28SThomas Zimmermann fbi->fb.flags = FBINFO_HWACCEL_COPYAREA
289f7018c21STomi Valkeinen | FBINFO_HWACCEL_FILLRECT
290f7018c21STomi Valkeinen | FBINFO_HWACCEL_XPAN
291f7018c21STomi Valkeinen | FBINFO_HWACCEL_YPAN
292f7018c21STomi Valkeinen | FBINFO_VIRTFB
293f7018c21STomi Valkeinen | FBINFO_PARTIAL_PAN_OK;
294f7018c21STomi Valkeinen fbi->fb.node = -1;
295f7018c21STomi Valkeinen
296f7018c21STomi Valkeinen addr = fbi;
297f7018c21STomi Valkeinen addr = addr + sizeof(struct wm8505fb_info);
298f7018c21STomi Valkeinen fbi->fb.pseudo_palette = addr;
299f7018c21STomi Valkeinen
300be66c2cbSYang Li fbi->regbase = devm_platform_ioremap_resource(pdev, 0);
301f7018c21STomi Valkeinen if (IS_ERR(fbi->regbase))
302f7018c21STomi Valkeinen return PTR_ERR(fbi->regbase);
303f7018c21STomi Valkeinen
304f7018c21STomi Valkeinen disp_timing = of_get_display_timings(pdev->dev.of_node);
305f7018c21STomi Valkeinen if (!disp_timing)
306f7018c21STomi Valkeinen return -EINVAL;
307f7018c21STomi Valkeinen
308f7018c21STomi Valkeinen ret = of_get_fb_videomode(pdev->dev.of_node, &mode, OF_USE_NATIVE_MODE);
309f7018c21STomi Valkeinen if (ret)
310f7018c21STomi Valkeinen return ret;
311f7018c21STomi Valkeinen
312f7018c21STomi Valkeinen ret = of_property_read_u32(pdev->dev.of_node, "bits-per-pixel", &bpp);
313f7018c21STomi Valkeinen if (ret)
314f7018c21STomi Valkeinen return ret;
315f7018c21STomi Valkeinen
316f7018c21STomi Valkeinen fb_videomode_to_var(&fbi->fb.var, &mode);
317f7018c21STomi Valkeinen
318f7018c21STomi Valkeinen fbi->fb.var.nonstd = 0;
319f7018c21STomi Valkeinen fbi->fb.var.activate = FB_ACTIVATE_NOW;
320f7018c21STomi Valkeinen
321f7018c21STomi Valkeinen fbi->fb.var.height = -1;
322f7018c21STomi Valkeinen fbi->fb.var.width = -1;
323f7018c21STomi Valkeinen
324f7018c21STomi Valkeinen /* try allocating the framebuffer */
325f7018c21STomi Valkeinen fb_mem_len = mode.xres * mode.yres * 2 * (bpp / 8);
326f7018c21STomi Valkeinen fb_mem_virt = dmam_alloc_coherent(&pdev->dev, fb_mem_len, &fb_mem_phys,
327f7018c21STomi Valkeinen GFP_KERNEL);
328f7018c21STomi Valkeinen if (!fb_mem_virt) {
329f7018c21STomi Valkeinen pr_err("%s: Failed to allocate framebuffer\n", __func__);
330f7018c21STomi Valkeinen return -ENOMEM;
331f7018c21STomi Valkeinen }
332f7018c21STomi Valkeinen
333f7018c21STomi Valkeinen fbi->fb.var.xres_virtual = mode.xres;
334f7018c21STomi Valkeinen fbi->fb.var.yres_virtual = mode.yres * 2;
335f7018c21STomi Valkeinen fbi->fb.var.bits_per_pixel = bpp;
336f7018c21STomi Valkeinen
337f7018c21STomi Valkeinen fbi->fb.fix.smem_start = fb_mem_phys;
338f7018c21STomi Valkeinen fbi->fb.fix.smem_len = fb_mem_len;
339459b0fa7SBartlomiej Zolnierkiewicz fbi->fb.screen_buffer = fb_mem_virt;
340f7018c21STomi Valkeinen fbi->fb.screen_size = fb_mem_len;
341f7018c21STomi Valkeinen
342f7018c21STomi Valkeinen fbi->contrast = 0x10;
343f7018c21STomi Valkeinen ret = wm8505fb_set_par(&fbi->fb);
344f7018c21STomi Valkeinen if (ret) {
345f7018c21STomi Valkeinen dev_err(&pdev->dev, "Failed to set parameters\n");
346f7018c21STomi Valkeinen return ret;
347f7018c21STomi Valkeinen }
348f7018c21STomi Valkeinen
349f7018c21STomi Valkeinen if (fb_alloc_cmap(&fbi->fb.cmap, 256, 0) < 0) {
350f7018c21STomi Valkeinen dev_err(&pdev->dev, "Failed to allocate color map\n");
351f7018c21STomi Valkeinen return -ENOMEM;
352f7018c21STomi Valkeinen }
353f7018c21STomi Valkeinen
354f7018c21STomi Valkeinen wm8505fb_init_hw(&fbi->fb);
355f7018c21STomi Valkeinen
356f7018c21STomi Valkeinen platform_set_drvdata(pdev, fbi);
357f7018c21STomi Valkeinen
358f7018c21STomi Valkeinen ret = register_framebuffer(&fbi->fb);
359f7018c21STomi Valkeinen if (ret < 0) {
360f7018c21STomi Valkeinen dev_err(&pdev->dev,
361f7018c21STomi Valkeinen "Failed to register framebuffer device: %d\n", ret);
362f7018c21STomi Valkeinen if (fbi->fb.cmap.len)
363f7018c21STomi Valkeinen fb_dealloc_cmap(&fbi->fb.cmap);
364f7018c21STomi Valkeinen return ret;
365f7018c21STomi Valkeinen }
366f7018c21STomi Valkeinen
367f7018c21STomi Valkeinen fb_info(&fbi->fb, "%s frame buffer at 0x%lx-0x%lx\n",
368f7018c21STomi Valkeinen fbi->fb.fix.id, fbi->fb.fix.smem_start,
369f7018c21STomi Valkeinen fbi->fb.fix.smem_start + fbi->fb.fix.smem_len - 1);
370f7018c21STomi Valkeinen
371f7018c21STomi Valkeinen return 0;
372f7018c21STomi Valkeinen }
373f7018c21STomi Valkeinen
wm8505fb_remove(struct platform_device * pdev)374024a3cafSUwe Kleine-König static void wm8505fb_remove(struct platform_device *pdev)
375f7018c21STomi Valkeinen {
376f7018c21STomi Valkeinen struct wm8505fb_info *fbi = platform_get_drvdata(pdev);
377f7018c21STomi Valkeinen
378f7018c21STomi Valkeinen unregister_framebuffer(&fbi->fb);
379f7018c21STomi Valkeinen
380f7018c21STomi Valkeinen writel(0, fbi->regbase);
381f7018c21STomi Valkeinen
382f7018c21STomi Valkeinen if (fbi->fb.cmap.len)
383f7018c21STomi Valkeinen fb_dealloc_cmap(&fbi->fb.cmap);
384f7018c21STomi Valkeinen }
385f7018c21STomi Valkeinen
386f7018c21STomi Valkeinen static const struct of_device_id wmt_dt_ids[] = {
387f7018c21STomi Valkeinen { .compatible = "wm,wm8505-fb", },
388f7018c21STomi Valkeinen {}
389f7018c21STomi Valkeinen };
390f7018c21STomi Valkeinen
391f7018c21STomi Valkeinen static struct platform_driver wm8505fb_driver = {
392f7018c21STomi Valkeinen .probe = wm8505fb_probe,
393024a3cafSUwe Kleine-König .remove_new = wm8505fb_remove,
394f7018c21STomi Valkeinen .driver = {
395f7018c21STomi Valkeinen .name = DRIVER_NAME,
396f7018c21STomi Valkeinen .of_match_table = wmt_dt_ids,
3970abd02edSGreg Kroah-Hartman .dev_groups = wm8505fb_groups,
398f7018c21STomi Valkeinen },
399f7018c21STomi Valkeinen };
400f7018c21STomi Valkeinen
401f7018c21STomi Valkeinen module_platform_driver(wm8505fb_driver);
402f7018c21STomi Valkeinen
403f7018c21STomi Valkeinen MODULE_AUTHOR("Ed Spiridonov <edo.rus@gmail.com>");
404f7018c21STomi Valkeinen MODULE_DESCRIPTION("Framebuffer driver for WMT WM8505");
405f7018c21STomi Valkeinen MODULE_DEVICE_TABLE(of, wmt_dt_ids);
406