18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * linux/drivers/video/mmp/fb/mmpfb.c
48c2ecf20Sopenharmony_ci * Framebuffer driver for Marvell Display controller.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) 2012 Marvell Technology Group Ltd.
78c2ecf20Sopenharmony_ci * Authors: Zhou Zhu <zzhu3@marvell.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
118c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
128c2ecf20Sopenharmony_ci#include "mmpfb.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_cistatic int var_to_pixfmt(struct fb_var_screeninfo *var)
158c2ecf20Sopenharmony_ci{
168c2ecf20Sopenharmony_ci	/*
178c2ecf20Sopenharmony_ci	 * Pseudocolor mode?
188c2ecf20Sopenharmony_ci	 */
198c2ecf20Sopenharmony_ci	if (var->bits_per_pixel == 8)
208c2ecf20Sopenharmony_ci		return PIXFMT_PSEUDOCOLOR;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci	/*
238c2ecf20Sopenharmony_ci	 * Check for YUV422PLANAR.
248c2ecf20Sopenharmony_ci	 */
258c2ecf20Sopenharmony_ci	if (var->bits_per_pixel == 16 && var->red.length == 8 &&
268c2ecf20Sopenharmony_ci			var->green.length == 4 && var->blue.length == 4) {
278c2ecf20Sopenharmony_ci		if (var->green.offset >= var->blue.offset)
288c2ecf20Sopenharmony_ci			return PIXFMT_YUV422P;
298c2ecf20Sopenharmony_ci		else
308c2ecf20Sopenharmony_ci			return PIXFMT_YVU422P;
318c2ecf20Sopenharmony_ci	}
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	/*
348c2ecf20Sopenharmony_ci	 * Check for YUV420PLANAR.
358c2ecf20Sopenharmony_ci	 */
368c2ecf20Sopenharmony_ci	if (var->bits_per_pixel == 12 && var->red.length == 8 &&
378c2ecf20Sopenharmony_ci			var->green.length == 2 && var->blue.length == 2) {
388c2ecf20Sopenharmony_ci		if (var->green.offset >= var->blue.offset)
398c2ecf20Sopenharmony_ci			return PIXFMT_YUV420P;
408c2ecf20Sopenharmony_ci		else
418c2ecf20Sopenharmony_ci			return PIXFMT_YVU420P;
428c2ecf20Sopenharmony_ci	}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	/*
458c2ecf20Sopenharmony_ci	 * Check for YUV422PACK.
468c2ecf20Sopenharmony_ci	 */
478c2ecf20Sopenharmony_ci	if (var->bits_per_pixel == 16 && var->red.length == 16 &&
488c2ecf20Sopenharmony_ci			var->green.length == 16 && var->blue.length == 16) {
498c2ecf20Sopenharmony_ci		if (var->red.offset == 0)
508c2ecf20Sopenharmony_ci			return PIXFMT_YUYV;
518c2ecf20Sopenharmony_ci		else if (var->green.offset >= var->blue.offset)
528c2ecf20Sopenharmony_ci			return PIXFMT_UYVY;
538c2ecf20Sopenharmony_ci		else
548c2ecf20Sopenharmony_ci			return PIXFMT_VYUY;
558c2ecf20Sopenharmony_ci	}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	/*
588c2ecf20Sopenharmony_ci	 * Check for 565/1555.
598c2ecf20Sopenharmony_ci	 */
608c2ecf20Sopenharmony_ci	if (var->bits_per_pixel == 16 && var->red.length <= 5 &&
618c2ecf20Sopenharmony_ci			var->green.length <= 6 && var->blue.length <= 5) {
628c2ecf20Sopenharmony_ci		if (var->transp.length == 0) {
638c2ecf20Sopenharmony_ci			if (var->red.offset >= var->blue.offset)
648c2ecf20Sopenharmony_ci				return PIXFMT_RGB565;
658c2ecf20Sopenharmony_ci			else
668c2ecf20Sopenharmony_ci				return PIXFMT_BGR565;
678c2ecf20Sopenharmony_ci		}
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	/*
718c2ecf20Sopenharmony_ci	 * Check for 888/A888.
728c2ecf20Sopenharmony_ci	 */
738c2ecf20Sopenharmony_ci	if (var->bits_per_pixel <= 32 && var->red.length <= 8 &&
748c2ecf20Sopenharmony_ci			var->green.length <= 8 && var->blue.length <= 8) {
758c2ecf20Sopenharmony_ci		if (var->bits_per_pixel == 24 && var->transp.length == 0) {
768c2ecf20Sopenharmony_ci			if (var->red.offset >= var->blue.offset)
778c2ecf20Sopenharmony_ci				return PIXFMT_RGB888PACK;
788c2ecf20Sopenharmony_ci			else
798c2ecf20Sopenharmony_ci				return PIXFMT_BGR888PACK;
808c2ecf20Sopenharmony_ci		}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci		if (var->bits_per_pixel == 32 && var->transp.offset == 24) {
838c2ecf20Sopenharmony_ci			if (var->red.offset >= var->blue.offset)
848c2ecf20Sopenharmony_ci				return PIXFMT_RGBA888;
858c2ecf20Sopenharmony_ci			else
868c2ecf20Sopenharmony_ci				return PIXFMT_BGRA888;
878c2ecf20Sopenharmony_ci		} else {
888c2ecf20Sopenharmony_ci			if (var->red.offset >= var->blue.offset)
898c2ecf20Sopenharmony_ci				return PIXFMT_RGB888UNPACK;
908c2ecf20Sopenharmony_ci			else
918c2ecf20Sopenharmony_ci				return PIXFMT_BGR888UNPACK;
928c2ecf20Sopenharmony_ci		}
938c2ecf20Sopenharmony_ci	}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	return -EINVAL;
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic void pixfmt_to_var(struct fb_var_screeninfo *var, int pix_fmt)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	switch (pix_fmt) {
1018c2ecf20Sopenharmony_ci	case PIXFMT_RGB565:
1028c2ecf20Sopenharmony_ci		var->bits_per_pixel = 16;
1038c2ecf20Sopenharmony_ci		var->red.offset = 11;	var->red.length = 5;
1048c2ecf20Sopenharmony_ci		var->green.offset = 5;   var->green.length = 6;
1058c2ecf20Sopenharmony_ci		var->blue.offset = 0;	var->blue.length = 5;
1068c2ecf20Sopenharmony_ci		var->transp.offset = 0;  var->transp.length = 0;
1078c2ecf20Sopenharmony_ci		break;
1088c2ecf20Sopenharmony_ci	case PIXFMT_BGR565:
1098c2ecf20Sopenharmony_ci		var->bits_per_pixel = 16;
1108c2ecf20Sopenharmony_ci		var->red.offset = 0;	var->red.length = 5;
1118c2ecf20Sopenharmony_ci		var->green.offset = 5;	 var->green.length = 6;
1128c2ecf20Sopenharmony_ci		var->blue.offset = 11;	var->blue.length = 5;
1138c2ecf20Sopenharmony_ci		var->transp.offset = 0;  var->transp.length = 0;
1148c2ecf20Sopenharmony_ci		break;
1158c2ecf20Sopenharmony_ci	case PIXFMT_RGB888UNPACK:
1168c2ecf20Sopenharmony_ci		var->bits_per_pixel = 32;
1178c2ecf20Sopenharmony_ci		var->red.offset = 16;	var->red.length = 8;
1188c2ecf20Sopenharmony_ci		var->green.offset = 8;   var->green.length = 8;
1198c2ecf20Sopenharmony_ci		var->blue.offset = 0;	var->blue.length = 8;
1208c2ecf20Sopenharmony_ci		var->transp.offset = 0;  var->transp.length = 0;
1218c2ecf20Sopenharmony_ci		break;
1228c2ecf20Sopenharmony_ci	case PIXFMT_BGR888UNPACK:
1238c2ecf20Sopenharmony_ci		var->bits_per_pixel = 32;
1248c2ecf20Sopenharmony_ci		var->red.offset = 0;	var->red.length = 8;
1258c2ecf20Sopenharmony_ci		var->green.offset = 8;	 var->green.length = 8;
1268c2ecf20Sopenharmony_ci		var->blue.offset = 16;	var->blue.length = 8;
1278c2ecf20Sopenharmony_ci		var->transp.offset = 0;  var->transp.length = 0;
1288c2ecf20Sopenharmony_ci		break;
1298c2ecf20Sopenharmony_ci	case PIXFMT_RGBA888:
1308c2ecf20Sopenharmony_ci		var->bits_per_pixel = 32;
1318c2ecf20Sopenharmony_ci		var->red.offset = 16;	var->red.length = 8;
1328c2ecf20Sopenharmony_ci		var->green.offset = 8;   var->green.length = 8;
1338c2ecf20Sopenharmony_ci		var->blue.offset = 0;	var->blue.length = 8;
1348c2ecf20Sopenharmony_ci		var->transp.offset = 24; var->transp.length = 8;
1358c2ecf20Sopenharmony_ci		break;
1368c2ecf20Sopenharmony_ci	case PIXFMT_BGRA888:
1378c2ecf20Sopenharmony_ci		var->bits_per_pixel = 32;
1388c2ecf20Sopenharmony_ci		var->red.offset = 0;	var->red.length = 8;
1398c2ecf20Sopenharmony_ci		var->green.offset = 8;	 var->green.length = 8;
1408c2ecf20Sopenharmony_ci		var->blue.offset = 16;	var->blue.length = 8;
1418c2ecf20Sopenharmony_ci		var->transp.offset = 24; var->transp.length = 8;
1428c2ecf20Sopenharmony_ci		break;
1438c2ecf20Sopenharmony_ci	case PIXFMT_RGB888PACK:
1448c2ecf20Sopenharmony_ci		var->bits_per_pixel = 24;
1458c2ecf20Sopenharmony_ci		var->red.offset = 16;	var->red.length = 8;
1468c2ecf20Sopenharmony_ci		var->green.offset = 8;   var->green.length = 8;
1478c2ecf20Sopenharmony_ci		var->blue.offset = 0;	var->blue.length = 8;
1488c2ecf20Sopenharmony_ci		var->transp.offset = 0;  var->transp.length = 0;
1498c2ecf20Sopenharmony_ci		break;
1508c2ecf20Sopenharmony_ci	case PIXFMT_BGR888PACK:
1518c2ecf20Sopenharmony_ci		var->bits_per_pixel = 24;
1528c2ecf20Sopenharmony_ci		var->red.offset = 0;	var->red.length = 8;
1538c2ecf20Sopenharmony_ci		var->green.offset = 8;	 var->green.length = 8;
1548c2ecf20Sopenharmony_ci		var->blue.offset = 16;	var->blue.length = 8;
1558c2ecf20Sopenharmony_ci		var->transp.offset = 0;  var->transp.length = 0;
1568c2ecf20Sopenharmony_ci		break;
1578c2ecf20Sopenharmony_ci	case PIXFMT_YUV420P:
1588c2ecf20Sopenharmony_ci		var->bits_per_pixel = 12;
1598c2ecf20Sopenharmony_ci		var->red.offset = 4;	 var->red.length = 8;
1608c2ecf20Sopenharmony_ci		var->green.offset = 2;   var->green.length = 2;
1618c2ecf20Sopenharmony_ci		var->blue.offset = 0;   var->blue.length = 2;
1628c2ecf20Sopenharmony_ci		var->transp.offset = 0;  var->transp.length = 0;
1638c2ecf20Sopenharmony_ci		break;
1648c2ecf20Sopenharmony_ci	case PIXFMT_YVU420P:
1658c2ecf20Sopenharmony_ci		var->bits_per_pixel = 12;
1668c2ecf20Sopenharmony_ci		var->red.offset = 4;	 var->red.length = 8;
1678c2ecf20Sopenharmony_ci		var->green.offset = 0;	 var->green.length = 2;
1688c2ecf20Sopenharmony_ci		var->blue.offset = 2;	var->blue.length = 2;
1698c2ecf20Sopenharmony_ci		var->transp.offset = 0;  var->transp.length = 0;
1708c2ecf20Sopenharmony_ci		break;
1718c2ecf20Sopenharmony_ci	case PIXFMT_YUV422P:
1728c2ecf20Sopenharmony_ci		var->bits_per_pixel = 16;
1738c2ecf20Sopenharmony_ci		var->red.offset = 8;	 var->red.length = 8;
1748c2ecf20Sopenharmony_ci		var->green.offset = 4;   var->green.length = 4;
1758c2ecf20Sopenharmony_ci		var->blue.offset = 0;   var->blue.length = 4;
1768c2ecf20Sopenharmony_ci		var->transp.offset = 0;  var->transp.length = 0;
1778c2ecf20Sopenharmony_ci		break;
1788c2ecf20Sopenharmony_ci	case PIXFMT_YVU422P:
1798c2ecf20Sopenharmony_ci		var->bits_per_pixel = 16;
1808c2ecf20Sopenharmony_ci		var->red.offset = 8;	 var->red.length = 8;
1818c2ecf20Sopenharmony_ci		var->green.offset = 0;	 var->green.length = 4;
1828c2ecf20Sopenharmony_ci		var->blue.offset = 4;	var->blue.length = 4;
1838c2ecf20Sopenharmony_ci		var->transp.offset = 0;  var->transp.length = 0;
1848c2ecf20Sopenharmony_ci		break;
1858c2ecf20Sopenharmony_ci	case PIXFMT_UYVY:
1868c2ecf20Sopenharmony_ci		var->bits_per_pixel = 16;
1878c2ecf20Sopenharmony_ci		var->red.offset = 8;	 var->red.length = 16;
1888c2ecf20Sopenharmony_ci		var->green.offset = 4;   var->green.length = 16;
1898c2ecf20Sopenharmony_ci		var->blue.offset = 0;   var->blue.length = 16;
1908c2ecf20Sopenharmony_ci		var->transp.offset = 0;  var->transp.length = 0;
1918c2ecf20Sopenharmony_ci		break;
1928c2ecf20Sopenharmony_ci	case PIXFMT_VYUY:
1938c2ecf20Sopenharmony_ci		var->bits_per_pixel = 16;
1948c2ecf20Sopenharmony_ci		var->red.offset = 8;	 var->red.length = 16;
1958c2ecf20Sopenharmony_ci		var->green.offset = 0;	 var->green.length = 16;
1968c2ecf20Sopenharmony_ci		var->blue.offset = 4;	var->blue.length = 16;
1978c2ecf20Sopenharmony_ci		var->transp.offset = 0;  var->transp.length = 0;
1988c2ecf20Sopenharmony_ci		break;
1998c2ecf20Sopenharmony_ci	case PIXFMT_YUYV:
2008c2ecf20Sopenharmony_ci		var->bits_per_pixel = 16;
2018c2ecf20Sopenharmony_ci		var->red.offset = 0;	 var->red.length = 16;
2028c2ecf20Sopenharmony_ci		var->green.offset = 4;	 var->green.length = 16;
2038c2ecf20Sopenharmony_ci		var->blue.offset = 8;	var->blue.length = 16;
2048c2ecf20Sopenharmony_ci		var->transp.offset = 0;  var->transp.length = 0;
2058c2ecf20Sopenharmony_ci		break;
2068c2ecf20Sopenharmony_ci	case PIXFMT_PSEUDOCOLOR:
2078c2ecf20Sopenharmony_ci		var->bits_per_pixel = 8;
2088c2ecf20Sopenharmony_ci		var->red.offset = 0;	 var->red.length = 8;
2098c2ecf20Sopenharmony_ci		var->green.offset = 0;   var->green.length = 8;
2108c2ecf20Sopenharmony_ci		var->blue.offset = 0;	var->blue.length = 8;
2118c2ecf20Sopenharmony_ci		var->transp.offset = 0;  var->transp.length = 0;
2128c2ecf20Sopenharmony_ci		break;
2138c2ecf20Sopenharmony_ci	}
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci/*
2178c2ecf20Sopenharmony_ci * fb framework has its limitation:
2188c2ecf20Sopenharmony_ci * 1. input color/output color is not seprated
2198c2ecf20Sopenharmony_ci * 2. fb_videomode not include output color
2208c2ecf20Sopenharmony_ci * so for fb usage, we keep a output format which is not changed
2218c2ecf20Sopenharmony_ci *  then it's added for mmpmode
2228c2ecf20Sopenharmony_ci */
2238c2ecf20Sopenharmony_cistatic void fbmode_to_mmpmode(struct mmp_mode *mode,
2248c2ecf20Sopenharmony_ci		struct fb_videomode *videomode, int output_fmt)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	u64 div_result = 1000000000000ll;
2278c2ecf20Sopenharmony_ci	mode->name = videomode->name;
2288c2ecf20Sopenharmony_ci	mode->refresh = videomode->refresh;
2298c2ecf20Sopenharmony_ci	mode->xres = videomode->xres;
2308c2ecf20Sopenharmony_ci	mode->yres = videomode->yres;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	do_div(div_result, videomode->pixclock);
2338c2ecf20Sopenharmony_ci	mode->pixclock_freq = (u32)div_result;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	mode->left_margin = videomode->left_margin;
2368c2ecf20Sopenharmony_ci	mode->right_margin = videomode->right_margin;
2378c2ecf20Sopenharmony_ci	mode->upper_margin = videomode->upper_margin;
2388c2ecf20Sopenharmony_ci	mode->lower_margin = videomode->lower_margin;
2398c2ecf20Sopenharmony_ci	mode->hsync_len = videomode->hsync_len;
2408c2ecf20Sopenharmony_ci	mode->vsync_len = videomode->vsync_len;
2418c2ecf20Sopenharmony_ci	mode->hsync_invert = !!(videomode->sync & FB_SYNC_HOR_HIGH_ACT);
2428c2ecf20Sopenharmony_ci	mode->vsync_invert = !!(videomode->sync & FB_SYNC_VERT_HIGH_ACT);
2438c2ecf20Sopenharmony_ci	/* no defined flag in fb, use vmode>>3*/
2448c2ecf20Sopenharmony_ci	mode->invert_pixclock = !!(videomode->vmode & 8);
2458c2ecf20Sopenharmony_ci	mode->pix_fmt_out = output_fmt;
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistatic void mmpmode_to_fbmode(struct fb_videomode *videomode,
2498c2ecf20Sopenharmony_ci		struct mmp_mode *mode)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	u64 div_result = 1000000000000ll;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	videomode->name = mode->name;
2548c2ecf20Sopenharmony_ci	videomode->refresh = mode->refresh;
2558c2ecf20Sopenharmony_ci	videomode->xres = mode->xres;
2568c2ecf20Sopenharmony_ci	videomode->yres = mode->yres;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	do_div(div_result, mode->pixclock_freq);
2598c2ecf20Sopenharmony_ci	videomode->pixclock = (u32)div_result;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	videomode->left_margin = mode->left_margin;
2628c2ecf20Sopenharmony_ci	videomode->right_margin = mode->right_margin;
2638c2ecf20Sopenharmony_ci	videomode->upper_margin = mode->upper_margin;
2648c2ecf20Sopenharmony_ci	videomode->lower_margin = mode->lower_margin;
2658c2ecf20Sopenharmony_ci	videomode->hsync_len = mode->hsync_len;
2668c2ecf20Sopenharmony_ci	videomode->vsync_len = mode->vsync_len;
2678c2ecf20Sopenharmony_ci	videomode->sync = (mode->hsync_invert ? FB_SYNC_HOR_HIGH_ACT : 0)
2688c2ecf20Sopenharmony_ci		| (mode->vsync_invert ? FB_SYNC_VERT_HIGH_ACT : 0);
2698c2ecf20Sopenharmony_ci	videomode->vmode = mode->invert_pixclock ? 8 : 0;
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_cistatic int mmpfb_check_var(struct fb_var_screeninfo *var,
2738c2ecf20Sopenharmony_ci		struct fb_info *info)
2748c2ecf20Sopenharmony_ci{
2758c2ecf20Sopenharmony_ci	struct mmpfb_info *fbi = info->par;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	if (var->bits_per_pixel == 8)
2788c2ecf20Sopenharmony_ci		return -EINVAL;
2798c2ecf20Sopenharmony_ci	/*
2808c2ecf20Sopenharmony_ci	 * Basic geometry sanity checks.
2818c2ecf20Sopenharmony_ci	 */
2828c2ecf20Sopenharmony_ci	if (var->xoffset + var->xres > var->xres_virtual)
2838c2ecf20Sopenharmony_ci		return -EINVAL;
2848c2ecf20Sopenharmony_ci	if (var->yoffset + var->yres > var->yres_virtual)
2858c2ecf20Sopenharmony_ci		return -EINVAL;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	/*
2888c2ecf20Sopenharmony_ci	 * Check size of framebuffer.
2898c2ecf20Sopenharmony_ci	 */
2908c2ecf20Sopenharmony_ci	if (var->xres_virtual * var->yres_virtual *
2918c2ecf20Sopenharmony_ci			(var->bits_per_pixel >> 3) > fbi->fb_size)
2928c2ecf20Sopenharmony_ci		return -EINVAL;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	return 0;
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_cistatic unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	return ((chan & 0xffff) >> (16 - bf->length)) << bf->offset;
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic u32 to_rgb(u16 red, u16 green, u16 blue)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	red >>= 8;
3058c2ecf20Sopenharmony_ci	green >>= 8;
3068c2ecf20Sopenharmony_ci	blue >>= 8;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	return (red << 16) | (green << 8) | blue;
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_cistatic int mmpfb_setcolreg(unsigned int regno, unsigned int red,
3128c2ecf20Sopenharmony_ci		unsigned int green, unsigned int blue,
3138c2ecf20Sopenharmony_ci		unsigned int trans, struct fb_info *info)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	struct mmpfb_info *fbi = info->par;
3168c2ecf20Sopenharmony_ci	u32 val;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 16) {
3198c2ecf20Sopenharmony_ci		val =  chan_to_field(red,   &info->var.red);
3208c2ecf20Sopenharmony_ci		val |= chan_to_field(green, &info->var.green);
3218c2ecf20Sopenharmony_ci		val |= chan_to_field(blue , &info->var.blue);
3228c2ecf20Sopenharmony_ci		fbi->pseudo_palette[regno] = val;
3238c2ecf20Sopenharmony_ci	}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR && regno < 256) {
3268c2ecf20Sopenharmony_ci		val = to_rgb(red, green, blue);
3278c2ecf20Sopenharmony_ci		/* TODO */
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	return 0;
3318c2ecf20Sopenharmony_ci}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_cistatic int mmpfb_pan_display(struct fb_var_screeninfo *var,
3348c2ecf20Sopenharmony_ci		struct fb_info *info)
3358c2ecf20Sopenharmony_ci{
3368c2ecf20Sopenharmony_ci	struct mmpfb_info *fbi = info->par;
3378c2ecf20Sopenharmony_ci	struct mmp_addr addr;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	memset(&addr, 0, sizeof(addr));
3408c2ecf20Sopenharmony_ci	addr.phys[0] = (var->yoffset * var->xres_virtual + var->xoffset)
3418c2ecf20Sopenharmony_ci		* var->bits_per_pixel / 8 + fbi->fb_start_dma;
3428c2ecf20Sopenharmony_ci	mmp_overlay_set_addr(fbi->overlay, &addr);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	return 0;
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cistatic int var_update(struct fb_info *info)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	struct mmpfb_info *fbi = info->par;
3508c2ecf20Sopenharmony_ci	struct fb_var_screeninfo *var = &info->var;
3518c2ecf20Sopenharmony_ci	struct fb_videomode *m;
3528c2ecf20Sopenharmony_ci	int pix_fmt;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	/* set pix_fmt */
3558c2ecf20Sopenharmony_ci	pix_fmt = var_to_pixfmt(var);
3568c2ecf20Sopenharmony_ci	if (pix_fmt < 0)
3578c2ecf20Sopenharmony_ci		return -EINVAL;
3588c2ecf20Sopenharmony_ci	pixfmt_to_var(var, pix_fmt);
3598c2ecf20Sopenharmony_ci	fbi->pix_fmt = pix_fmt;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	/* set var according to best video mode*/
3628c2ecf20Sopenharmony_ci	m = (struct fb_videomode *)fb_match_mode(var, &info->modelist);
3638c2ecf20Sopenharmony_ci	if (!m) {
3648c2ecf20Sopenharmony_ci		dev_err(fbi->dev, "set par: no match mode, use best mode\n");
3658c2ecf20Sopenharmony_ci		m = (struct fb_videomode *)fb_find_best_mode(var,
3668c2ecf20Sopenharmony_ci				&info->modelist);
3678c2ecf20Sopenharmony_ci		fb_videomode_to_var(var, m);
3688c2ecf20Sopenharmony_ci	}
3698c2ecf20Sopenharmony_ci	memcpy(&fbi->mode, m, sizeof(struct fb_videomode));
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	/* fix to 2* yres */
3728c2ecf20Sopenharmony_ci	var->yres_virtual = var->yres * 2;
3738c2ecf20Sopenharmony_ci	info->fix.visual = (pix_fmt == PIXFMT_PSEUDOCOLOR) ?
3748c2ecf20Sopenharmony_ci		FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
3758c2ecf20Sopenharmony_ci	info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8;
3768c2ecf20Sopenharmony_ci	info->fix.ypanstep = var->yres;
3778c2ecf20Sopenharmony_ci	return 0;
3788c2ecf20Sopenharmony_ci}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_cistatic void mmpfb_set_win(struct fb_info *info)
3818c2ecf20Sopenharmony_ci{
3828c2ecf20Sopenharmony_ci	struct mmpfb_info *fbi = info->par;
3838c2ecf20Sopenharmony_ci	struct fb_var_screeninfo *var = &info->var;
3848c2ecf20Sopenharmony_ci	struct mmp_win win;
3858c2ecf20Sopenharmony_ci	u32 stride;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	memset(&win, 0, sizeof(win));
3888c2ecf20Sopenharmony_ci	win.xsrc = win.xdst = fbi->mode.xres;
3898c2ecf20Sopenharmony_ci	win.ysrc = win.ydst = fbi->mode.yres;
3908c2ecf20Sopenharmony_ci	win.pix_fmt = fbi->pix_fmt;
3918c2ecf20Sopenharmony_ci	stride = pixfmt_to_stride(win.pix_fmt);
3928c2ecf20Sopenharmony_ci	win.pitch[0] = var->xres_virtual * stride;
3938c2ecf20Sopenharmony_ci	win.pitch[1] = win.pitch[2] =
3948c2ecf20Sopenharmony_ci		(stride == 1) ? (var->xres_virtual >> 1) : 0;
3958c2ecf20Sopenharmony_ci	mmp_overlay_set_win(fbi->overlay, &win);
3968c2ecf20Sopenharmony_ci}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_cistatic int mmpfb_set_par(struct fb_info *info)
3998c2ecf20Sopenharmony_ci{
4008c2ecf20Sopenharmony_ci	struct mmpfb_info *fbi = info->par;
4018c2ecf20Sopenharmony_ci	struct fb_var_screeninfo *var = &info->var;
4028c2ecf20Sopenharmony_ci	struct mmp_addr addr;
4038c2ecf20Sopenharmony_ci	struct mmp_mode mode;
4048c2ecf20Sopenharmony_ci	int ret;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	ret = var_update(info);
4078c2ecf20Sopenharmony_ci	if (ret != 0)
4088c2ecf20Sopenharmony_ci		return ret;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	/* set window/path according to new videomode */
4118c2ecf20Sopenharmony_ci	fbmode_to_mmpmode(&mode, &fbi->mode, fbi->output_fmt);
4128c2ecf20Sopenharmony_ci	mmp_path_set_mode(fbi->path, &mode);
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	/* set window related info */
4158c2ecf20Sopenharmony_ci	mmpfb_set_win(info);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	/* set address always */
4188c2ecf20Sopenharmony_ci	memset(&addr, 0, sizeof(addr));
4198c2ecf20Sopenharmony_ci	addr.phys[0] = (var->yoffset * var->xres_virtual + var->xoffset)
4208c2ecf20Sopenharmony_ci		* var->bits_per_pixel / 8 + fbi->fb_start_dma;
4218c2ecf20Sopenharmony_ci	mmp_overlay_set_addr(fbi->overlay, &addr);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	return 0;
4248c2ecf20Sopenharmony_ci}
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_cistatic void mmpfb_power(struct mmpfb_info *fbi, int power)
4278c2ecf20Sopenharmony_ci{
4288c2ecf20Sopenharmony_ci	struct mmp_addr addr;
4298c2ecf20Sopenharmony_ci	struct fb_var_screeninfo *var = &fbi->fb_info->var;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	/* for power on, always set address/window again */
4328c2ecf20Sopenharmony_ci	if (power) {
4338c2ecf20Sopenharmony_ci		/* set window related info */
4348c2ecf20Sopenharmony_ci		mmpfb_set_win(fbi->fb_info);
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci		/* set address always */
4378c2ecf20Sopenharmony_ci		memset(&addr, 0, sizeof(addr));
4388c2ecf20Sopenharmony_ci		addr.phys[0] = fbi->fb_start_dma +
4398c2ecf20Sopenharmony_ci			(var->yoffset * var->xres_virtual + var->xoffset)
4408c2ecf20Sopenharmony_ci			* var->bits_per_pixel / 8;
4418c2ecf20Sopenharmony_ci		mmp_overlay_set_addr(fbi->overlay, &addr);
4428c2ecf20Sopenharmony_ci	}
4438c2ecf20Sopenharmony_ci	mmp_overlay_set_onoff(fbi->overlay, power);
4448c2ecf20Sopenharmony_ci}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_cistatic int mmpfb_blank(int blank, struct fb_info *info)
4478c2ecf20Sopenharmony_ci{
4488c2ecf20Sopenharmony_ci	struct mmpfb_info *fbi = info->par;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	mmpfb_power(fbi, (blank == FB_BLANK_UNBLANK));
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	return 0;
4538c2ecf20Sopenharmony_ci}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_cistatic const struct fb_ops mmpfb_ops = {
4568c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
4578c2ecf20Sopenharmony_ci	.fb_blank	= mmpfb_blank,
4588c2ecf20Sopenharmony_ci	.fb_check_var	= mmpfb_check_var,
4598c2ecf20Sopenharmony_ci	.fb_set_par	= mmpfb_set_par,
4608c2ecf20Sopenharmony_ci	.fb_setcolreg	= mmpfb_setcolreg,
4618c2ecf20Sopenharmony_ci	.fb_pan_display	= mmpfb_pan_display,
4628c2ecf20Sopenharmony_ci	.fb_fillrect	= cfb_fillrect,
4638c2ecf20Sopenharmony_ci	.fb_copyarea	= cfb_copyarea,
4648c2ecf20Sopenharmony_ci	.fb_imageblit	= cfb_imageblit,
4658c2ecf20Sopenharmony_ci};
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_cistatic int modes_setup(struct mmpfb_info *fbi)
4688c2ecf20Sopenharmony_ci{
4698c2ecf20Sopenharmony_ci	struct fb_videomode *videomodes;
4708c2ecf20Sopenharmony_ci	struct mmp_mode *mmp_modes;
4718c2ecf20Sopenharmony_ci	struct fb_info *info = fbi->fb_info;
4728c2ecf20Sopenharmony_ci	int videomode_num, i;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	/* get videomodes from path */
4758c2ecf20Sopenharmony_ci	videomode_num = mmp_path_get_modelist(fbi->path, &mmp_modes);
4768c2ecf20Sopenharmony_ci	if (!videomode_num) {
4778c2ecf20Sopenharmony_ci		dev_warn(fbi->dev, "can't get videomode num\n");
4788c2ecf20Sopenharmony_ci		return 0;
4798c2ecf20Sopenharmony_ci	}
4808c2ecf20Sopenharmony_ci	/* put videomode list to info structure */
4818c2ecf20Sopenharmony_ci	videomodes = kcalloc(videomode_num, sizeof(struct fb_videomode),
4828c2ecf20Sopenharmony_ci			     GFP_KERNEL);
4838c2ecf20Sopenharmony_ci	if (!videomodes)
4848c2ecf20Sopenharmony_ci		return -ENOMEM;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	for (i = 0; i < videomode_num; i++)
4878c2ecf20Sopenharmony_ci		mmpmode_to_fbmode(&videomodes[i], &mmp_modes[i]);
4888c2ecf20Sopenharmony_ci	fb_videomode_to_modelist(videomodes, videomode_num, &info->modelist);
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	/* set videomode[0] as default mode */
4918c2ecf20Sopenharmony_ci	memcpy(&fbi->mode, &videomodes[0], sizeof(struct fb_videomode));
4928c2ecf20Sopenharmony_ci	fbi->output_fmt = mmp_modes[0].pix_fmt_out;
4938c2ecf20Sopenharmony_ci	fb_videomode_to_var(&info->var, &fbi->mode);
4948c2ecf20Sopenharmony_ci	mmp_path_set_mode(fbi->path, &mmp_modes[0]);
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	kfree(videomodes);
4978c2ecf20Sopenharmony_ci	return videomode_num;
4988c2ecf20Sopenharmony_ci}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_cistatic int fb_info_setup(struct fb_info *info,
5018c2ecf20Sopenharmony_ci			struct mmpfb_info *fbi)
5028c2ecf20Sopenharmony_ci{
5038c2ecf20Sopenharmony_ci	int ret = 0;
5048c2ecf20Sopenharmony_ci	/* Initialise static fb parameters.*/
5058c2ecf20Sopenharmony_ci	info->flags = FBINFO_DEFAULT | FBINFO_PARTIAL_PAN_OK |
5068c2ecf20Sopenharmony_ci		FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN;
5078c2ecf20Sopenharmony_ci	info->node = -1;
5088c2ecf20Sopenharmony_ci	strcpy(info->fix.id, fbi->name);
5098c2ecf20Sopenharmony_ci	info->fix.type = FB_TYPE_PACKED_PIXELS;
5108c2ecf20Sopenharmony_ci	info->fix.type_aux = 0;
5118c2ecf20Sopenharmony_ci	info->fix.xpanstep = 0;
5128c2ecf20Sopenharmony_ci	info->fix.ypanstep = info->var.yres;
5138c2ecf20Sopenharmony_ci	info->fix.ywrapstep = 0;
5148c2ecf20Sopenharmony_ci	info->fix.accel = FB_ACCEL_NONE;
5158c2ecf20Sopenharmony_ci	info->fix.smem_start = fbi->fb_start_dma;
5168c2ecf20Sopenharmony_ci	info->fix.smem_len = fbi->fb_size;
5178c2ecf20Sopenharmony_ci	info->fix.visual = (fbi->pix_fmt == PIXFMT_PSEUDOCOLOR) ?
5188c2ecf20Sopenharmony_ci		FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
5198c2ecf20Sopenharmony_ci	info->fix.line_length = info->var.xres_virtual *
5208c2ecf20Sopenharmony_ci		info->var.bits_per_pixel / 8;
5218c2ecf20Sopenharmony_ci	info->fbops = &mmpfb_ops;
5228c2ecf20Sopenharmony_ci	info->pseudo_palette = fbi->pseudo_palette;
5238c2ecf20Sopenharmony_ci	info->screen_buffer = fbi->fb_start;
5248c2ecf20Sopenharmony_ci	info->screen_size = fbi->fb_size;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	/* For FB framework: Allocate color map and Register framebuffer*/
5278c2ecf20Sopenharmony_ci	if (fb_alloc_cmap(&info->cmap, 256, 0) < 0)
5288c2ecf20Sopenharmony_ci		ret = -ENOMEM;
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	return ret;
5318c2ecf20Sopenharmony_ci}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_cistatic void fb_info_clear(struct fb_info *info)
5348c2ecf20Sopenharmony_ci{
5358c2ecf20Sopenharmony_ci	fb_dealloc_cmap(&info->cmap);
5368c2ecf20Sopenharmony_ci}
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_cistatic int mmpfb_probe(struct platform_device *pdev)
5398c2ecf20Sopenharmony_ci{
5408c2ecf20Sopenharmony_ci	struct mmp_buffer_driver_mach_info *mi;
5418c2ecf20Sopenharmony_ci	struct fb_info *info;
5428c2ecf20Sopenharmony_ci	struct mmpfb_info *fbi;
5438c2ecf20Sopenharmony_ci	int ret, modes_num;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	mi = pdev->dev.platform_data;
5468c2ecf20Sopenharmony_ci	if (mi == NULL) {
5478c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "no platform data defined\n");
5488c2ecf20Sopenharmony_ci		return -EINVAL;
5498c2ecf20Sopenharmony_ci	}
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	/* initialize fb */
5528c2ecf20Sopenharmony_ci	info = framebuffer_alloc(sizeof(struct mmpfb_info), &pdev->dev);
5538c2ecf20Sopenharmony_ci	if (info == NULL)
5548c2ecf20Sopenharmony_ci		return -ENOMEM;
5558c2ecf20Sopenharmony_ci	fbi = info->par;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	/* init fb */
5588c2ecf20Sopenharmony_ci	fbi->fb_info = info;
5598c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, fbi);
5608c2ecf20Sopenharmony_ci	fbi->dev = &pdev->dev;
5618c2ecf20Sopenharmony_ci	fbi->name = mi->name;
5628c2ecf20Sopenharmony_ci	fbi->pix_fmt = mi->default_pixfmt;
5638c2ecf20Sopenharmony_ci	pixfmt_to_var(&info->var, fbi->pix_fmt);
5648c2ecf20Sopenharmony_ci	mutex_init(&fbi->access_ok);
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	/* get display path by name */
5678c2ecf20Sopenharmony_ci	fbi->path = mmp_get_path(mi->path_name);
5688c2ecf20Sopenharmony_ci	if (!fbi->path) {
5698c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "can't get the path %s\n", mi->path_name);
5708c2ecf20Sopenharmony_ci		ret = -EINVAL;
5718c2ecf20Sopenharmony_ci		goto failed_destroy_mutex;
5728c2ecf20Sopenharmony_ci	}
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	dev_info(fbi->dev, "path %s get\n", fbi->path->name);
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	/* get overlay */
5778c2ecf20Sopenharmony_ci	fbi->overlay = mmp_path_get_overlay(fbi->path, mi->overlay_id);
5788c2ecf20Sopenharmony_ci	if (!fbi->overlay) {
5798c2ecf20Sopenharmony_ci		ret = -EINVAL;
5808c2ecf20Sopenharmony_ci		goto failed_destroy_mutex;
5818c2ecf20Sopenharmony_ci	}
5828c2ecf20Sopenharmony_ci	/* set fetch used */
5838c2ecf20Sopenharmony_ci	mmp_overlay_set_fetch(fbi->overlay, mi->dmafetch_id);
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	modes_num = modes_setup(fbi);
5868c2ecf20Sopenharmony_ci	if (modes_num < 0) {
5878c2ecf20Sopenharmony_ci		ret = modes_num;
5888c2ecf20Sopenharmony_ci		goto failed_destroy_mutex;
5898c2ecf20Sopenharmony_ci	}
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	/*
5928c2ecf20Sopenharmony_ci	 * if get modes success, means not hotplug panels, use caculated buffer
5938c2ecf20Sopenharmony_ci	 * or use default size
5948c2ecf20Sopenharmony_ci	 */
5958c2ecf20Sopenharmony_ci	if (modes_num > 0) {
5968c2ecf20Sopenharmony_ci		/* fix to 2* yres */
5978c2ecf20Sopenharmony_ci		info->var.yres_virtual = info->var.yres * 2;
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci		/* Allocate framebuffer memory: size = modes xy *4 */
6008c2ecf20Sopenharmony_ci		fbi->fb_size = info->var.xres_virtual * info->var.yres_virtual
6018c2ecf20Sopenharmony_ci				* info->var.bits_per_pixel / 8;
6028c2ecf20Sopenharmony_ci	} else {
6038c2ecf20Sopenharmony_ci		fbi->fb_size = MMPFB_DEFAULT_SIZE;
6048c2ecf20Sopenharmony_ci	}
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	fbi->fb_start = dma_alloc_coherent(&pdev->dev, PAGE_ALIGN(fbi->fb_size),
6078c2ecf20Sopenharmony_ci				&fbi->fb_start_dma, GFP_KERNEL);
6088c2ecf20Sopenharmony_ci	if (fbi->fb_start == NULL) {
6098c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "can't alloc framebuffer\n");
6108c2ecf20Sopenharmony_ci		ret = -ENOMEM;
6118c2ecf20Sopenharmony_ci		goto failed_destroy_mutex;
6128c2ecf20Sopenharmony_ci	}
6138c2ecf20Sopenharmony_ci	dev_info(fbi->dev, "fb %dk allocated\n", fbi->fb_size/1024);
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	/* fb power on */
6168c2ecf20Sopenharmony_ci	if (modes_num > 0)
6178c2ecf20Sopenharmony_ci		mmpfb_power(fbi, 1);
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	ret = fb_info_setup(info, fbi);
6208c2ecf20Sopenharmony_ci	if (ret < 0)
6218c2ecf20Sopenharmony_ci		goto failed_free_buff;
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	ret = register_framebuffer(info);
6248c2ecf20Sopenharmony_ci	if (ret < 0) {
6258c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to register fb: %d\n", ret);
6268c2ecf20Sopenharmony_ci		ret = -ENXIO;
6278c2ecf20Sopenharmony_ci		goto failed_clear_info;
6288c2ecf20Sopenharmony_ci	}
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	dev_info(fbi->dev, "loaded to /dev/fb%d <%s>.\n",
6318c2ecf20Sopenharmony_ci		info->node, info->fix.id);
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci#ifdef CONFIG_LOGO
6348c2ecf20Sopenharmony_ci	if (fbi->fb_start) {
6358c2ecf20Sopenharmony_ci		fb_prepare_logo(info, 0);
6368c2ecf20Sopenharmony_ci		fb_show_logo(info, 0);
6378c2ecf20Sopenharmony_ci	}
6388c2ecf20Sopenharmony_ci#endif
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	return 0;
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_cifailed_clear_info:
6438c2ecf20Sopenharmony_ci	fb_info_clear(info);
6448c2ecf20Sopenharmony_cifailed_free_buff:
6458c2ecf20Sopenharmony_ci	dma_free_coherent(&pdev->dev, PAGE_ALIGN(fbi->fb_size), fbi->fb_start,
6468c2ecf20Sopenharmony_ci		fbi->fb_start_dma);
6478c2ecf20Sopenharmony_cifailed_destroy_mutex:
6488c2ecf20Sopenharmony_ci	mutex_destroy(&fbi->access_ok);
6498c2ecf20Sopenharmony_ci	dev_err(fbi->dev, "mmp-fb: frame buffer device init failed\n");
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	framebuffer_release(info);
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	return ret;
6548c2ecf20Sopenharmony_ci}
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_cistatic struct platform_driver mmpfb_driver = {
6578c2ecf20Sopenharmony_ci	.driver		= {
6588c2ecf20Sopenharmony_ci		.name	= "mmp-fb",
6598c2ecf20Sopenharmony_ci	},
6608c2ecf20Sopenharmony_ci	.probe		= mmpfb_probe,
6618c2ecf20Sopenharmony_ci};
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_cistatic int mmpfb_init(void)
6648c2ecf20Sopenharmony_ci{
6658c2ecf20Sopenharmony_ci	return platform_driver_register(&mmpfb_driver);
6668c2ecf20Sopenharmony_ci}
6678c2ecf20Sopenharmony_cimodule_init(mmpfb_init);
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ciMODULE_AUTHOR("Zhou Zhu <zhou.zhu@marvell.com>");
6708c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Framebuffer driver for Marvell displays");
6718c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
672