xref: /openbmc/linux/drivers/video/fbdev/wm8505fb.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
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