18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  WonderMedia WM8505 Frame Buffer device driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com>
68c2ecf20Sopenharmony_ci *    Based on vt8500lcdfb.c
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/delay.h>
108c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
118c2ecf20Sopenharmony_ci#include <linux/fb.h>
128c2ecf20Sopenharmony_ci#include <linux/errno.h>
138c2ecf20Sopenharmony_ci#include <linux/err.h>
148c2ecf20Sopenharmony_ci#include <linux/init.h>
158c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
168c2ecf20Sopenharmony_ci#include <linux/io.h>
178c2ecf20Sopenharmony_ci#include <linux/kernel.h>
188c2ecf20Sopenharmony_ci#include <linux/memblock.h>
198c2ecf20Sopenharmony_ci#include <linux/mm.h>
208c2ecf20Sopenharmony_ci#include <linux/module.h>
218c2ecf20Sopenharmony_ci#include <linux/of.h>
228c2ecf20Sopenharmony_ci#include <linux/of_fdt.h>
238c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
248c2ecf20Sopenharmony_ci#include <linux/slab.h>
258c2ecf20Sopenharmony_ci#include <linux/string.h>
268c2ecf20Sopenharmony_ci#include <linux/wait.h>
278c2ecf20Sopenharmony_ci#include <video/of_display_timing.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#include "wm8505fb_regs.h"
308c2ecf20Sopenharmony_ci#include "wmt_ge_rops.h"
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define DRIVER_NAME "wm8505-fb"
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define to_wm8505fb_info(__info) container_of(__info, \
358c2ecf20Sopenharmony_ci						struct wm8505fb_info, fb)
368c2ecf20Sopenharmony_cistruct wm8505fb_info {
378c2ecf20Sopenharmony_ci	struct fb_info		fb;
388c2ecf20Sopenharmony_ci	void __iomem		*regbase;
398c2ecf20Sopenharmony_ci	unsigned int		contrast;
408c2ecf20Sopenharmony_ci};
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic int wm8505fb_init_hw(struct fb_info *info)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	struct wm8505fb_info *fbi = to_wm8505fb_info(info);
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	int i;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	/* I know the purpose only of few registers, so clear unknown */
508c2ecf20Sopenharmony_ci	for (i = 0; i < 0x200; i += 4)
518c2ecf20Sopenharmony_ci		writel(0, fbi->regbase + i);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	/* Set frame buffer address */
548c2ecf20Sopenharmony_ci	writel(fbi->fb.fix.smem_start, fbi->regbase + WMT_GOVR_FBADDR);
558c2ecf20Sopenharmony_ci	writel(fbi->fb.fix.smem_start, fbi->regbase + WMT_GOVR_FBADDR1);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	/*
588c2ecf20Sopenharmony_ci	 * Set in-memory picture format to RGB
598c2ecf20Sopenharmony_ci	 * 0x31C sets the correct color mode (RGB565) for WM8650
608c2ecf20Sopenharmony_ci	 * Bit 8+9 (0x300) are ignored on WM8505 as reserved
618c2ecf20Sopenharmony_ci	 */
628c2ecf20Sopenharmony_ci	writel(0x31c,		       fbi->regbase + WMT_GOVR_COLORSPACE);
638c2ecf20Sopenharmony_ci	writel(1,		       fbi->regbase + WMT_GOVR_COLORSPACE1);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	/* Virtual buffer size */
668c2ecf20Sopenharmony_ci	writel(info->var.xres,	       fbi->regbase + WMT_GOVR_XRES);
678c2ecf20Sopenharmony_ci	writel(info->var.xres_virtual, fbi->regbase + WMT_GOVR_XRES_VIRTUAL);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	/* black magic ;) */
708c2ecf20Sopenharmony_ci	writel(0xf,		       fbi->regbase + WMT_GOVR_FHI);
718c2ecf20Sopenharmony_ci	writel(4,		       fbi->regbase + WMT_GOVR_DVO_SET);
728c2ecf20Sopenharmony_ci	writel(1,		       fbi->regbase + WMT_GOVR_MIF_ENABLE);
738c2ecf20Sopenharmony_ci	writel(1,		       fbi->regbase + WMT_GOVR_REG_UPDATE);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	return 0;
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic int wm8505fb_set_timing(struct fb_info *info)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	struct wm8505fb_info *fbi = to_wm8505fb_info(info);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	int h_start = info->var.left_margin;
838c2ecf20Sopenharmony_ci	int h_end = h_start + info->var.xres;
848c2ecf20Sopenharmony_ci	int h_all = h_end + info->var.right_margin;
858c2ecf20Sopenharmony_ci	int h_sync = info->var.hsync_len;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	int v_start = info->var.upper_margin;
888c2ecf20Sopenharmony_ci	int v_end = v_start + info->var.yres;
898c2ecf20Sopenharmony_ci	int v_all = v_end + info->var.lower_margin;
908c2ecf20Sopenharmony_ci	int v_sync = info->var.vsync_len;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	writel(0, fbi->regbase + WMT_GOVR_TG);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	writel(h_start, fbi->regbase + WMT_GOVR_TIMING_H_START);
958c2ecf20Sopenharmony_ci	writel(h_end,   fbi->regbase + WMT_GOVR_TIMING_H_END);
968c2ecf20Sopenharmony_ci	writel(h_all,   fbi->regbase + WMT_GOVR_TIMING_H_ALL);
978c2ecf20Sopenharmony_ci	writel(h_sync,  fbi->regbase + WMT_GOVR_TIMING_H_SYNC);
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	writel(v_start, fbi->regbase + WMT_GOVR_TIMING_V_START);
1008c2ecf20Sopenharmony_ci	writel(v_end,   fbi->regbase + WMT_GOVR_TIMING_V_END);
1018c2ecf20Sopenharmony_ci	writel(v_all,   fbi->regbase + WMT_GOVR_TIMING_V_ALL);
1028c2ecf20Sopenharmony_ci	writel(v_sync,  fbi->regbase + WMT_GOVR_TIMING_V_SYNC);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	writel(1, fbi->regbase + WMT_GOVR_TG);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	return 0;
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic int wm8505fb_set_par(struct fb_info *info)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	struct wm8505fb_info *fbi = to_wm8505fb_info(info);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	if (!fbi)
1158c2ecf20Sopenharmony_ci		return -EINVAL;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	if (info->var.bits_per_pixel == 32) {
1188c2ecf20Sopenharmony_ci		info->var.red.offset = 16;
1198c2ecf20Sopenharmony_ci		info->var.red.length = 8;
1208c2ecf20Sopenharmony_ci		info->var.red.msb_right = 0;
1218c2ecf20Sopenharmony_ci		info->var.green.offset = 8;
1228c2ecf20Sopenharmony_ci		info->var.green.length = 8;
1238c2ecf20Sopenharmony_ci		info->var.green.msb_right = 0;
1248c2ecf20Sopenharmony_ci		info->var.blue.offset = 0;
1258c2ecf20Sopenharmony_ci		info->var.blue.length = 8;
1268c2ecf20Sopenharmony_ci		info->var.blue.msb_right = 0;
1278c2ecf20Sopenharmony_ci		info->fix.visual = FB_VISUAL_TRUECOLOR;
1288c2ecf20Sopenharmony_ci		info->fix.line_length = info->var.xres_virtual << 2;
1298c2ecf20Sopenharmony_ci	} else if (info->var.bits_per_pixel == 16) {
1308c2ecf20Sopenharmony_ci		info->var.red.offset = 11;
1318c2ecf20Sopenharmony_ci		info->var.red.length = 5;
1328c2ecf20Sopenharmony_ci		info->var.red.msb_right = 0;
1338c2ecf20Sopenharmony_ci		info->var.green.offset = 5;
1348c2ecf20Sopenharmony_ci		info->var.green.length = 6;
1358c2ecf20Sopenharmony_ci		info->var.green.msb_right = 0;
1368c2ecf20Sopenharmony_ci		info->var.blue.offset = 0;
1378c2ecf20Sopenharmony_ci		info->var.blue.length = 5;
1388c2ecf20Sopenharmony_ci		info->var.blue.msb_right = 0;
1398c2ecf20Sopenharmony_ci		info->fix.visual = FB_VISUAL_TRUECOLOR;
1408c2ecf20Sopenharmony_ci		info->fix.line_length = info->var.xres_virtual << 1;
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	wm8505fb_set_timing(info);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	writel(fbi->contrast<<16 | fbi->contrast<<8 | fbi->contrast,
1468c2ecf20Sopenharmony_ci		fbi->regbase + WMT_GOVR_CONTRAST);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	return 0;
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic ssize_t contrast_show(struct device *dev,
1528c2ecf20Sopenharmony_ci			     struct device_attribute *attr, char *buf)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	struct fb_info *info = dev_get_drvdata(dev);
1558c2ecf20Sopenharmony_ci	struct wm8505fb_info *fbi = to_wm8505fb_info(info);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	return sprintf(buf, "%u\n", fbi->contrast);
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic ssize_t contrast_store(struct device *dev,
1618c2ecf20Sopenharmony_ci			      struct device_attribute *attr,
1628c2ecf20Sopenharmony_ci			      const char *buf, size_t count)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	struct fb_info *info = dev_get_drvdata(dev);
1658c2ecf20Sopenharmony_ci	struct wm8505fb_info *fbi = to_wm8505fb_info(info);
1668c2ecf20Sopenharmony_ci	unsigned long tmp;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	if (kstrtoul(buf, 10, &tmp) || (tmp > 0xff))
1698c2ecf20Sopenharmony_ci		return -EINVAL;
1708c2ecf20Sopenharmony_ci	fbi->contrast = tmp;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	wm8505fb_set_par(info);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	return count;
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(contrast);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic struct attribute *wm8505fb_attrs[] = {
1808c2ecf20Sopenharmony_ci	&dev_attr_contrast.attr,
1818c2ecf20Sopenharmony_ci	NULL,
1828c2ecf20Sopenharmony_ci};
1838c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(wm8505fb);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	chan &= 0xffff;
1888c2ecf20Sopenharmony_ci	chan >>= 16 - bf->length;
1898c2ecf20Sopenharmony_ci	return chan << bf->offset;
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic int wm8505fb_setcolreg(unsigned regno, unsigned red, unsigned green,
1938c2ecf20Sopenharmony_ci			   unsigned blue, unsigned transp,
1948c2ecf20Sopenharmony_ci			   struct fb_info *info) {
1958c2ecf20Sopenharmony_ci	struct wm8505fb_info *fbi = to_wm8505fb_info(info);
1968c2ecf20Sopenharmony_ci	int ret = 1;
1978c2ecf20Sopenharmony_ci	unsigned int val;
1988c2ecf20Sopenharmony_ci	if (regno >= 256)
1998c2ecf20Sopenharmony_ci		return -EINVAL;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	if (info->var.grayscale)
2028c2ecf20Sopenharmony_ci		red = green = blue =
2038c2ecf20Sopenharmony_ci			(19595 * red + 38470 * green + 7471 * blue) >> 16;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	switch (fbi->fb.fix.visual) {
2068c2ecf20Sopenharmony_ci	case FB_VISUAL_TRUECOLOR:
2078c2ecf20Sopenharmony_ci		if (regno < 16) {
2088c2ecf20Sopenharmony_ci			u32 *pal = info->pseudo_palette;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci			val  = chan_to_field(red, &fbi->fb.var.red);
2118c2ecf20Sopenharmony_ci			val |= chan_to_field(green, &fbi->fb.var.green);
2128c2ecf20Sopenharmony_ci			val |= chan_to_field(blue, &fbi->fb.var.blue);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci			pal[regno] = val;
2158c2ecf20Sopenharmony_ci			ret = 0;
2168c2ecf20Sopenharmony_ci		}
2178c2ecf20Sopenharmony_ci		break;
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	return ret;
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cistatic int wm8505fb_pan_display(struct fb_var_screeninfo *var,
2248c2ecf20Sopenharmony_ci				struct fb_info *info)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	struct wm8505fb_info *fbi = to_wm8505fb_info(info);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	writel(var->xoffset, fbi->regbase + WMT_GOVR_XPAN);
2298c2ecf20Sopenharmony_ci	writel(var->yoffset, fbi->regbase + WMT_GOVR_YPAN);
2308c2ecf20Sopenharmony_ci	return 0;
2318c2ecf20Sopenharmony_ci}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_cistatic int wm8505fb_blank(int blank, struct fb_info *info)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	struct wm8505fb_info *fbi = to_wm8505fb_info(info);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	switch (blank) {
2388c2ecf20Sopenharmony_ci	case FB_BLANK_UNBLANK:
2398c2ecf20Sopenharmony_ci		wm8505fb_set_timing(info);
2408c2ecf20Sopenharmony_ci		break;
2418c2ecf20Sopenharmony_ci	default:
2428c2ecf20Sopenharmony_ci		writel(0,  fbi->regbase + WMT_GOVR_TIMING_V_SYNC);
2438c2ecf20Sopenharmony_ci		break;
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	return 0;
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic const struct fb_ops wm8505fb_ops = {
2508c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
2518c2ecf20Sopenharmony_ci	.fb_set_par	= wm8505fb_set_par,
2528c2ecf20Sopenharmony_ci	.fb_setcolreg	= wm8505fb_setcolreg,
2538c2ecf20Sopenharmony_ci	.fb_fillrect	= wmt_ge_fillrect,
2548c2ecf20Sopenharmony_ci	.fb_copyarea	= wmt_ge_copyarea,
2558c2ecf20Sopenharmony_ci	.fb_imageblit	= sys_imageblit,
2568c2ecf20Sopenharmony_ci	.fb_sync	= wmt_ge_sync,
2578c2ecf20Sopenharmony_ci	.fb_pan_display	= wm8505fb_pan_display,
2588c2ecf20Sopenharmony_ci	.fb_blank	= wm8505fb_blank,
2598c2ecf20Sopenharmony_ci};
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_cistatic int wm8505fb_probe(struct platform_device *pdev)
2628c2ecf20Sopenharmony_ci{
2638c2ecf20Sopenharmony_ci	struct wm8505fb_info	*fbi;
2648c2ecf20Sopenharmony_ci	struct resource	*res;
2658c2ecf20Sopenharmony_ci	struct display_timings *disp_timing;
2668c2ecf20Sopenharmony_ci	void			*addr;
2678c2ecf20Sopenharmony_ci	int ret;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	struct fb_videomode	mode;
2708c2ecf20Sopenharmony_ci	u32			bpp;
2718c2ecf20Sopenharmony_ci	dma_addr_t fb_mem_phys;
2728c2ecf20Sopenharmony_ci	unsigned long fb_mem_len;
2738c2ecf20Sopenharmony_ci	void *fb_mem_virt;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	fbi = devm_kzalloc(&pdev->dev, sizeof(struct wm8505fb_info) +
2768c2ecf20Sopenharmony_ci			sizeof(u32) * 16, GFP_KERNEL);
2778c2ecf20Sopenharmony_ci	if (!fbi)
2788c2ecf20Sopenharmony_ci		return -ENOMEM;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	strcpy(fbi->fb.fix.id, DRIVER_NAME);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	fbi->fb.fix.type	= FB_TYPE_PACKED_PIXELS;
2838c2ecf20Sopenharmony_ci	fbi->fb.fix.xpanstep	= 1;
2848c2ecf20Sopenharmony_ci	fbi->fb.fix.ypanstep	= 1;
2858c2ecf20Sopenharmony_ci	fbi->fb.fix.ywrapstep	= 0;
2868c2ecf20Sopenharmony_ci	fbi->fb.fix.accel	= FB_ACCEL_NONE;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	fbi->fb.fbops		= &wm8505fb_ops;
2898c2ecf20Sopenharmony_ci	fbi->fb.flags		= FBINFO_DEFAULT
2908c2ecf20Sopenharmony_ci				| FBINFO_HWACCEL_COPYAREA
2918c2ecf20Sopenharmony_ci				| FBINFO_HWACCEL_FILLRECT
2928c2ecf20Sopenharmony_ci				| FBINFO_HWACCEL_XPAN
2938c2ecf20Sopenharmony_ci				| FBINFO_HWACCEL_YPAN
2948c2ecf20Sopenharmony_ci				| FBINFO_VIRTFB
2958c2ecf20Sopenharmony_ci				| FBINFO_PARTIAL_PAN_OK;
2968c2ecf20Sopenharmony_ci	fbi->fb.node		= -1;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	addr = fbi;
2998c2ecf20Sopenharmony_ci	addr = addr + sizeof(struct wm8505fb_info);
3008c2ecf20Sopenharmony_ci	fbi->fb.pseudo_palette	= addr;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
3038c2ecf20Sopenharmony_ci	fbi->regbase = devm_ioremap_resource(&pdev->dev, res);
3048c2ecf20Sopenharmony_ci	if (IS_ERR(fbi->regbase))
3058c2ecf20Sopenharmony_ci		return PTR_ERR(fbi->regbase);
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	disp_timing = of_get_display_timings(pdev->dev.of_node);
3088c2ecf20Sopenharmony_ci	if (!disp_timing)
3098c2ecf20Sopenharmony_ci		return -EINVAL;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	ret = of_get_fb_videomode(pdev->dev.of_node, &mode, OF_USE_NATIVE_MODE);
3128c2ecf20Sopenharmony_ci	if (ret)
3138c2ecf20Sopenharmony_ci		return ret;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	ret = of_property_read_u32(pdev->dev.of_node, "bits-per-pixel", &bpp);
3168c2ecf20Sopenharmony_ci	if (ret)
3178c2ecf20Sopenharmony_ci		return ret;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	fb_videomode_to_var(&fbi->fb.var, &mode);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	fbi->fb.var.nonstd		= 0;
3228c2ecf20Sopenharmony_ci	fbi->fb.var.activate		= FB_ACTIVATE_NOW;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	fbi->fb.var.height		= -1;
3258c2ecf20Sopenharmony_ci	fbi->fb.var.width		= -1;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	/* try allocating the framebuffer */
3288c2ecf20Sopenharmony_ci	fb_mem_len = mode.xres * mode.yres * 2 * (bpp / 8);
3298c2ecf20Sopenharmony_ci	fb_mem_virt = dmam_alloc_coherent(&pdev->dev, fb_mem_len, &fb_mem_phys,
3308c2ecf20Sopenharmony_ci				GFP_KERNEL);
3318c2ecf20Sopenharmony_ci	if (!fb_mem_virt) {
3328c2ecf20Sopenharmony_ci		pr_err("%s: Failed to allocate framebuffer\n", __func__);
3338c2ecf20Sopenharmony_ci		return -ENOMEM;
3348c2ecf20Sopenharmony_ci	}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	fbi->fb.var.xres_virtual	= mode.xres;
3378c2ecf20Sopenharmony_ci	fbi->fb.var.yres_virtual	= mode.yres * 2;
3388c2ecf20Sopenharmony_ci	fbi->fb.var.bits_per_pixel	= bpp;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	fbi->fb.fix.smem_start		= fb_mem_phys;
3418c2ecf20Sopenharmony_ci	fbi->fb.fix.smem_len		= fb_mem_len;
3428c2ecf20Sopenharmony_ci	fbi->fb.screen_buffer		= fb_mem_virt;
3438c2ecf20Sopenharmony_ci	fbi->fb.screen_size		= fb_mem_len;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	fbi->contrast = 0x10;
3468c2ecf20Sopenharmony_ci	ret = wm8505fb_set_par(&fbi->fb);
3478c2ecf20Sopenharmony_ci	if (ret) {
3488c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to set parameters\n");
3498c2ecf20Sopenharmony_ci		return ret;
3508c2ecf20Sopenharmony_ci	}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	if (fb_alloc_cmap(&fbi->fb.cmap, 256, 0) < 0) {
3538c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to allocate color map\n");
3548c2ecf20Sopenharmony_ci		return -ENOMEM;
3558c2ecf20Sopenharmony_ci	}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	wm8505fb_init_hw(&fbi->fb);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, fbi);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	ret = register_framebuffer(&fbi->fb);
3628c2ecf20Sopenharmony_ci	if (ret < 0) {
3638c2ecf20Sopenharmony_ci		dev_err(&pdev->dev,
3648c2ecf20Sopenharmony_ci			"Failed to register framebuffer device: %d\n", ret);
3658c2ecf20Sopenharmony_ci		if (fbi->fb.cmap.len)
3668c2ecf20Sopenharmony_ci			fb_dealloc_cmap(&fbi->fb.cmap);
3678c2ecf20Sopenharmony_ci		return ret;
3688c2ecf20Sopenharmony_ci	}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	fb_info(&fbi->fb, "%s frame buffer at 0x%lx-0x%lx\n",
3718c2ecf20Sopenharmony_ci		fbi->fb.fix.id, fbi->fb.fix.smem_start,
3728c2ecf20Sopenharmony_ci		fbi->fb.fix.smem_start + fbi->fb.fix.smem_len - 1);
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	return 0;
3758c2ecf20Sopenharmony_ci}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_cistatic int wm8505fb_remove(struct platform_device *pdev)
3788c2ecf20Sopenharmony_ci{
3798c2ecf20Sopenharmony_ci	struct wm8505fb_info *fbi = platform_get_drvdata(pdev);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	unregister_framebuffer(&fbi->fb);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	writel(0, fbi->regbase);
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	if (fbi->fb.cmap.len)
3868c2ecf20Sopenharmony_ci		fb_dealloc_cmap(&fbi->fb.cmap);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	return 0;
3898c2ecf20Sopenharmony_ci}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_cistatic const struct of_device_id wmt_dt_ids[] = {
3928c2ecf20Sopenharmony_ci	{ .compatible = "wm,wm8505-fb", },
3938c2ecf20Sopenharmony_ci	{}
3948c2ecf20Sopenharmony_ci};
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cistatic struct platform_driver wm8505fb_driver = {
3978c2ecf20Sopenharmony_ci	.probe		= wm8505fb_probe,
3988c2ecf20Sopenharmony_ci	.remove		= wm8505fb_remove,
3998c2ecf20Sopenharmony_ci	.driver		= {
4008c2ecf20Sopenharmony_ci		.name	= DRIVER_NAME,
4018c2ecf20Sopenharmony_ci		.of_match_table = wmt_dt_ids,
4028c2ecf20Sopenharmony_ci		.dev_groups	= wm8505fb_groups,
4038c2ecf20Sopenharmony_ci	},
4048c2ecf20Sopenharmony_ci};
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_cimodule_platform_driver(wm8505fb_driver);
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ed Spiridonov <edo.rus@gmail.com>");
4098c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Framebuffer driver for WMT WM8505");
4108c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
4118c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, wmt_dt_ids);
412