162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * linux/drivers/video/mmp/fb/mmpfb.c
462306a36Sopenharmony_ci * Framebuffer driver for Marvell Display controller.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 2012 Marvell Technology Group Ltd.
762306a36Sopenharmony_ci * Authors: Zhou Zhu <zzhu3@marvell.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1162306a36Sopenharmony_ci#include <linux/platform_device.h>
1262306a36Sopenharmony_ci#include "mmpfb.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic int var_to_pixfmt(struct fb_var_screeninfo *var)
1562306a36Sopenharmony_ci{
1662306a36Sopenharmony_ci	/*
1762306a36Sopenharmony_ci	 * Pseudocolor mode?
1862306a36Sopenharmony_ci	 */
1962306a36Sopenharmony_ci	if (var->bits_per_pixel == 8)
2062306a36Sopenharmony_ci		return PIXFMT_PSEUDOCOLOR;
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci	/*
2362306a36Sopenharmony_ci	 * Check for YUV422PLANAR.
2462306a36Sopenharmony_ci	 */
2562306a36Sopenharmony_ci	if (var->bits_per_pixel == 16 && var->red.length == 8 &&
2662306a36Sopenharmony_ci			var->green.length == 4 && var->blue.length == 4) {
2762306a36Sopenharmony_ci		if (var->green.offset >= var->blue.offset)
2862306a36Sopenharmony_ci			return PIXFMT_YUV422P;
2962306a36Sopenharmony_ci		else
3062306a36Sopenharmony_ci			return PIXFMT_YVU422P;
3162306a36Sopenharmony_ci	}
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	/*
3462306a36Sopenharmony_ci	 * Check for YUV420PLANAR.
3562306a36Sopenharmony_ci	 */
3662306a36Sopenharmony_ci	if (var->bits_per_pixel == 12 && var->red.length == 8 &&
3762306a36Sopenharmony_ci			var->green.length == 2 && var->blue.length == 2) {
3862306a36Sopenharmony_ci		if (var->green.offset >= var->blue.offset)
3962306a36Sopenharmony_ci			return PIXFMT_YUV420P;
4062306a36Sopenharmony_ci		else
4162306a36Sopenharmony_ci			return PIXFMT_YVU420P;
4262306a36Sopenharmony_ci	}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	/*
4562306a36Sopenharmony_ci	 * Check for YUV422PACK.
4662306a36Sopenharmony_ci	 */
4762306a36Sopenharmony_ci	if (var->bits_per_pixel == 16 && var->red.length == 16 &&
4862306a36Sopenharmony_ci			var->green.length == 16 && var->blue.length == 16) {
4962306a36Sopenharmony_ci		if (var->red.offset == 0)
5062306a36Sopenharmony_ci			return PIXFMT_YUYV;
5162306a36Sopenharmony_ci		else if (var->green.offset >= var->blue.offset)
5262306a36Sopenharmony_ci			return PIXFMT_UYVY;
5362306a36Sopenharmony_ci		else
5462306a36Sopenharmony_ci			return PIXFMT_VYUY;
5562306a36Sopenharmony_ci	}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	/*
5862306a36Sopenharmony_ci	 * Check for 565/1555.
5962306a36Sopenharmony_ci	 */
6062306a36Sopenharmony_ci	if (var->bits_per_pixel == 16 && var->red.length <= 5 &&
6162306a36Sopenharmony_ci			var->green.length <= 6 && var->blue.length <= 5) {
6262306a36Sopenharmony_ci		if (var->transp.length == 0) {
6362306a36Sopenharmony_ci			if (var->red.offset >= var->blue.offset)
6462306a36Sopenharmony_ci				return PIXFMT_RGB565;
6562306a36Sopenharmony_ci			else
6662306a36Sopenharmony_ci				return PIXFMT_BGR565;
6762306a36Sopenharmony_ci		}
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	/*
7162306a36Sopenharmony_ci	 * Check for 888/A888.
7262306a36Sopenharmony_ci	 */
7362306a36Sopenharmony_ci	if (var->bits_per_pixel <= 32 && var->red.length <= 8 &&
7462306a36Sopenharmony_ci			var->green.length <= 8 && var->blue.length <= 8) {
7562306a36Sopenharmony_ci		if (var->bits_per_pixel == 24 && var->transp.length == 0) {
7662306a36Sopenharmony_ci			if (var->red.offset >= var->blue.offset)
7762306a36Sopenharmony_ci				return PIXFMT_RGB888PACK;
7862306a36Sopenharmony_ci			else
7962306a36Sopenharmony_ci				return PIXFMT_BGR888PACK;
8062306a36Sopenharmony_ci		}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci		if (var->bits_per_pixel == 32 && var->transp.offset == 24) {
8362306a36Sopenharmony_ci			if (var->red.offset >= var->blue.offset)
8462306a36Sopenharmony_ci				return PIXFMT_RGBA888;
8562306a36Sopenharmony_ci			else
8662306a36Sopenharmony_ci				return PIXFMT_BGRA888;
8762306a36Sopenharmony_ci		} else {
8862306a36Sopenharmony_ci			if (var->red.offset >= var->blue.offset)
8962306a36Sopenharmony_ci				return PIXFMT_RGB888UNPACK;
9062306a36Sopenharmony_ci			else
9162306a36Sopenharmony_ci				return PIXFMT_BGR888UNPACK;
9262306a36Sopenharmony_ci		}
9362306a36Sopenharmony_ci	}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	return -EINVAL;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic void pixfmt_to_var(struct fb_var_screeninfo *var, int pix_fmt)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	switch (pix_fmt) {
10162306a36Sopenharmony_ci	case PIXFMT_RGB565:
10262306a36Sopenharmony_ci		var->bits_per_pixel = 16;
10362306a36Sopenharmony_ci		var->red.offset = 11;	var->red.length = 5;
10462306a36Sopenharmony_ci		var->green.offset = 5;   var->green.length = 6;
10562306a36Sopenharmony_ci		var->blue.offset = 0;	var->blue.length = 5;
10662306a36Sopenharmony_ci		var->transp.offset = 0;  var->transp.length = 0;
10762306a36Sopenharmony_ci		break;
10862306a36Sopenharmony_ci	case PIXFMT_BGR565:
10962306a36Sopenharmony_ci		var->bits_per_pixel = 16;
11062306a36Sopenharmony_ci		var->red.offset = 0;	var->red.length = 5;
11162306a36Sopenharmony_ci		var->green.offset = 5;	 var->green.length = 6;
11262306a36Sopenharmony_ci		var->blue.offset = 11;	var->blue.length = 5;
11362306a36Sopenharmony_ci		var->transp.offset = 0;  var->transp.length = 0;
11462306a36Sopenharmony_ci		break;
11562306a36Sopenharmony_ci	case PIXFMT_RGB888UNPACK:
11662306a36Sopenharmony_ci		var->bits_per_pixel = 32;
11762306a36Sopenharmony_ci		var->red.offset = 16;	var->red.length = 8;
11862306a36Sopenharmony_ci		var->green.offset = 8;   var->green.length = 8;
11962306a36Sopenharmony_ci		var->blue.offset = 0;	var->blue.length = 8;
12062306a36Sopenharmony_ci		var->transp.offset = 0;  var->transp.length = 0;
12162306a36Sopenharmony_ci		break;
12262306a36Sopenharmony_ci	case PIXFMT_BGR888UNPACK:
12362306a36Sopenharmony_ci		var->bits_per_pixel = 32;
12462306a36Sopenharmony_ci		var->red.offset = 0;	var->red.length = 8;
12562306a36Sopenharmony_ci		var->green.offset = 8;	 var->green.length = 8;
12662306a36Sopenharmony_ci		var->blue.offset = 16;	var->blue.length = 8;
12762306a36Sopenharmony_ci		var->transp.offset = 0;  var->transp.length = 0;
12862306a36Sopenharmony_ci		break;
12962306a36Sopenharmony_ci	case PIXFMT_RGBA888:
13062306a36Sopenharmony_ci		var->bits_per_pixel = 32;
13162306a36Sopenharmony_ci		var->red.offset = 16;	var->red.length = 8;
13262306a36Sopenharmony_ci		var->green.offset = 8;   var->green.length = 8;
13362306a36Sopenharmony_ci		var->blue.offset = 0;	var->blue.length = 8;
13462306a36Sopenharmony_ci		var->transp.offset = 24; var->transp.length = 8;
13562306a36Sopenharmony_ci		break;
13662306a36Sopenharmony_ci	case PIXFMT_BGRA888:
13762306a36Sopenharmony_ci		var->bits_per_pixel = 32;
13862306a36Sopenharmony_ci		var->red.offset = 0;	var->red.length = 8;
13962306a36Sopenharmony_ci		var->green.offset = 8;	 var->green.length = 8;
14062306a36Sopenharmony_ci		var->blue.offset = 16;	var->blue.length = 8;
14162306a36Sopenharmony_ci		var->transp.offset = 24; var->transp.length = 8;
14262306a36Sopenharmony_ci		break;
14362306a36Sopenharmony_ci	case PIXFMT_RGB888PACK:
14462306a36Sopenharmony_ci		var->bits_per_pixel = 24;
14562306a36Sopenharmony_ci		var->red.offset = 16;	var->red.length = 8;
14662306a36Sopenharmony_ci		var->green.offset = 8;   var->green.length = 8;
14762306a36Sopenharmony_ci		var->blue.offset = 0;	var->blue.length = 8;
14862306a36Sopenharmony_ci		var->transp.offset = 0;  var->transp.length = 0;
14962306a36Sopenharmony_ci		break;
15062306a36Sopenharmony_ci	case PIXFMT_BGR888PACK:
15162306a36Sopenharmony_ci		var->bits_per_pixel = 24;
15262306a36Sopenharmony_ci		var->red.offset = 0;	var->red.length = 8;
15362306a36Sopenharmony_ci		var->green.offset = 8;	 var->green.length = 8;
15462306a36Sopenharmony_ci		var->blue.offset = 16;	var->blue.length = 8;
15562306a36Sopenharmony_ci		var->transp.offset = 0;  var->transp.length = 0;
15662306a36Sopenharmony_ci		break;
15762306a36Sopenharmony_ci	case PIXFMT_YUV420P:
15862306a36Sopenharmony_ci		var->bits_per_pixel = 12;
15962306a36Sopenharmony_ci		var->red.offset = 4;	 var->red.length = 8;
16062306a36Sopenharmony_ci		var->green.offset = 2;   var->green.length = 2;
16162306a36Sopenharmony_ci		var->blue.offset = 0;   var->blue.length = 2;
16262306a36Sopenharmony_ci		var->transp.offset = 0;  var->transp.length = 0;
16362306a36Sopenharmony_ci		break;
16462306a36Sopenharmony_ci	case PIXFMT_YVU420P:
16562306a36Sopenharmony_ci		var->bits_per_pixel = 12;
16662306a36Sopenharmony_ci		var->red.offset = 4;	 var->red.length = 8;
16762306a36Sopenharmony_ci		var->green.offset = 0;	 var->green.length = 2;
16862306a36Sopenharmony_ci		var->blue.offset = 2;	var->blue.length = 2;
16962306a36Sopenharmony_ci		var->transp.offset = 0;  var->transp.length = 0;
17062306a36Sopenharmony_ci		break;
17162306a36Sopenharmony_ci	case PIXFMT_YUV422P:
17262306a36Sopenharmony_ci		var->bits_per_pixel = 16;
17362306a36Sopenharmony_ci		var->red.offset = 8;	 var->red.length = 8;
17462306a36Sopenharmony_ci		var->green.offset = 4;   var->green.length = 4;
17562306a36Sopenharmony_ci		var->blue.offset = 0;   var->blue.length = 4;
17662306a36Sopenharmony_ci		var->transp.offset = 0;  var->transp.length = 0;
17762306a36Sopenharmony_ci		break;
17862306a36Sopenharmony_ci	case PIXFMT_YVU422P:
17962306a36Sopenharmony_ci		var->bits_per_pixel = 16;
18062306a36Sopenharmony_ci		var->red.offset = 8;	 var->red.length = 8;
18162306a36Sopenharmony_ci		var->green.offset = 0;	 var->green.length = 4;
18262306a36Sopenharmony_ci		var->blue.offset = 4;	var->blue.length = 4;
18362306a36Sopenharmony_ci		var->transp.offset = 0;  var->transp.length = 0;
18462306a36Sopenharmony_ci		break;
18562306a36Sopenharmony_ci	case PIXFMT_UYVY:
18662306a36Sopenharmony_ci		var->bits_per_pixel = 16;
18762306a36Sopenharmony_ci		var->red.offset = 8;	 var->red.length = 16;
18862306a36Sopenharmony_ci		var->green.offset = 4;   var->green.length = 16;
18962306a36Sopenharmony_ci		var->blue.offset = 0;   var->blue.length = 16;
19062306a36Sopenharmony_ci		var->transp.offset = 0;  var->transp.length = 0;
19162306a36Sopenharmony_ci		break;
19262306a36Sopenharmony_ci	case PIXFMT_VYUY:
19362306a36Sopenharmony_ci		var->bits_per_pixel = 16;
19462306a36Sopenharmony_ci		var->red.offset = 8;	 var->red.length = 16;
19562306a36Sopenharmony_ci		var->green.offset = 0;	 var->green.length = 16;
19662306a36Sopenharmony_ci		var->blue.offset = 4;	var->blue.length = 16;
19762306a36Sopenharmony_ci		var->transp.offset = 0;  var->transp.length = 0;
19862306a36Sopenharmony_ci		break;
19962306a36Sopenharmony_ci	case PIXFMT_YUYV:
20062306a36Sopenharmony_ci		var->bits_per_pixel = 16;
20162306a36Sopenharmony_ci		var->red.offset = 0;	 var->red.length = 16;
20262306a36Sopenharmony_ci		var->green.offset = 4;	 var->green.length = 16;
20362306a36Sopenharmony_ci		var->blue.offset = 8;	var->blue.length = 16;
20462306a36Sopenharmony_ci		var->transp.offset = 0;  var->transp.length = 0;
20562306a36Sopenharmony_ci		break;
20662306a36Sopenharmony_ci	case PIXFMT_PSEUDOCOLOR:
20762306a36Sopenharmony_ci		var->bits_per_pixel = 8;
20862306a36Sopenharmony_ci		var->red.offset = 0;	 var->red.length = 8;
20962306a36Sopenharmony_ci		var->green.offset = 0;   var->green.length = 8;
21062306a36Sopenharmony_ci		var->blue.offset = 0;	var->blue.length = 8;
21162306a36Sopenharmony_ci		var->transp.offset = 0;  var->transp.length = 0;
21262306a36Sopenharmony_ci		break;
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci/*
21762306a36Sopenharmony_ci * fb framework has its limitation:
21862306a36Sopenharmony_ci * 1. input color/output color is not seprated
21962306a36Sopenharmony_ci * 2. fb_videomode not include output color
22062306a36Sopenharmony_ci * so for fb usage, we keep a output format which is not changed
22162306a36Sopenharmony_ci *  then it's added for mmpmode
22262306a36Sopenharmony_ci */
22362306a36Sopenharmony_cistatic void fbmode_to_mmpmode(struct mmp_mode *mode,
22462306a36Sopenharmony_ci		struct fb_videomode *videomode, int output_fmt)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	u64 div_result = 1000000000000ll;
22762306a36Sopenharmony_ci	mode->name = videomode->name;
22862306a36Sopenharmony_ci	mode->refresh = videomode->refresh;
22962306a36Sopenharmony_ci	mode->xres = videomode->xres;
23062306a36Sopenharmony_ci	mode->yres = videomode->yres;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	do_div(div_result, videomode->pixclock);
23362306a36Sopenharmony_ci	mode->pixclock_freq = (u32)div_result;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	mode->left_margin = videomode->left_margin;
23662306a36Sopenharmony_ci	mode->right_margin = videomode->right_margin;
23762306a36Sopenharmony_ci	mode->upper_margin = videomode->upper_margin;
23862306a36Sopenharmony_ci	mode->lower_margin = videomode->lower_margin;
23962306a36Sopenharmony_ci	mode->hsync_len = videomode->hsync_len;
24062306a36Sopenharmony_ci	mode->vsync_len = videomode->vsync_len;
24162306a36Sopenharmony_ci	mode->hsync_invert = !!(videomode->sync & FB_SYNC_HOR_HIGH_ACT);
24262306a36Sopenharmony_ci	mode->vsync_invert = !!(videomode->sync & FB_SYNC_VERT_HIGH_ACT);
24362306a36Sopenharmony_ci	/* no defined flag in fb, use vmode>>3*/
24462306a36Sopenharmony_ci	mode->invert_pixclock = !!(videomode->vmode & 8);
24562306a36Sopenharmony_ci	mode->pix_fmt_out = output_fmt;
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_cistatic void mmpmode_to_fbmode(struct fb_videomode *videomode,
24962306a36Sopenharmony_ci		struct mmp_mode *mode)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	u64 div_result = 1000000000000ll;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	videomode->name = mode->name;
25462306a36Sopenharmony_ci	videomode->refresh = mode->refresh;
25562306a36Sopenharmony_ci	videomode->xres = mode->xres;
25662306a36Sopenharmony_ci	videomode->yres = mode->yres;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	do_div(div_result, mode->pixclock_freq);
25962306a36Sopenharmony_ci	videomode->pixclock = (u32)div_result;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	videomode->left_margin = mode->left_margin;
26262306a36Sopenharmony_ci	videomode->right_margin = mode->right_margin;
26362306a36Sopenharmony_ci	videomode->upper_margin = mode->upper_margin;
26462306a36Sopenharmony_ci	videomode->lower_margin = mode->lower_margin;
26562306a36Sopenharmony_ci	videomode->hsync_len = mode->hsync_len;
26662306a36Sopenharmony_ci	videomode->vsync_len = mode->vsync_len;
26762306a36Sopenharmony_ci	videomode->sync = (mode->hsync_invert ? FB_SYNC_HOR_HIGH_ACT : 0)
26862306a36Sopenharmony_ci		| (mode->vsync_invert ? FB_SYNC_VERT_HIGH_ACT : 0);
26962306a36Sopenharmony_ci	videomode->vmode = mode->invert_pixclock ? 8 : 0;
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic int mmpfb_check_var(struct fb_var_screeninfo *var,
27362306a36Sopenharmony_ci		struct fb_info *info)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	struct mmpfb_info *fbi = info->par;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	if (var->bits_per_pixel == 8)
27862306a36Sopenharmony_ci		return -EINVAL;
27962306a36Sopenharmony_ci	/*
28062306a36Sopenharmony_ci	 * Basic geometry sanity checks.
28162306a36Sopenharmony_ci	 */
28262306a36Sopenharmony_ci	if (var->xoffset + var->xres > var->xres_virtual)
28362306a36Sopenharmony_ci		return -EINVAL;
28462306a36Sopenharmony_ci	if (var->yoffset + var->yres > var->yres_virtual)
28562306a36Sopenharmony_ci		return -EINVAL;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	/*
28862306a36Sopenharmony_ci	 * Check size of framebuffer.
28962306a36Sopenharmony_ci	 */
29062306a36Sopenharmony_ci	if (var->xres_virtual * var->yres_virtual *
29162306a36Sopenharmony_ci			(var->bits_per_pixel >> 3) > fbi->fb_size)
29262306a36Sopenharmony_ci		return -EINVAL;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	return 0;
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistatic unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	return ((chan & 0xffff) >> (16 - bf->length)) << bf->offset;
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic u32 to_rgb(u16 red, u16 green, u16 blue)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	red >>= 8;
30562306a36Sopenharmony_ci	green >>= 8;
30662306a36Sopenharmony_ci	blue >>= 8;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	return (red << 16) | (green << 8) | blue;
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_cistatic int mmpfb_setcolreg(unsigned int regno, unsigned int red,
31262306a36Sopenharmony_ci		unsigned int green, unsigned int blue,
31362306a36Sopenharmony_ci		unsigned int trans, struct fb_info *info)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	struct mmpfb_info *fbi = info->par;
31662306a36Sopenharmony_ci	u32 val;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 16) {
31962306a36Sopenharmony_ci		val =  chan_to_field(red,   &info->var.red);
32062306a36Sopenharmony_ci		val |= chan_to_field(green, &info->var.green);
32162306a36Sopenharmony_ci		val |= chan_to_field(blue , &info->var.blue);
32262306a36Sopenharmony_ci		fbi->pseudo_palette[regno] = val;
32362306a36Sopenharmony_ci	}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR && regno < 256) {
32662306a36Sopenharmony_ci		val = to_rgb(red, green, blue);
32762306a36Sopenharmony_ci		/* TODO */
32862306a36Sopenharmony_ci	}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	return 0;
33162306a36Sopenharmony_ci}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_cistatic int mmpfb_pan_display(struct fb_var_screeninfo *var,
33462306a36Sopenharmony_ci		struct fb_info *info)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	struct mmpfb_info *fbi = info->par;
33762306a36Sopenharmony_ci	struct mmp_addr addr;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	memset(&addr, 0, sizeof(addr));
34062306a36Sopenharmony_ci	addr.phys[0] = (var->yoffset * var->xres_virtual + var->xoffset)
34162306a36Sopenharmony_ci		* var->bits_per_pixel / 8 + fbi->fb_start_dma;
34262306a36Sopenharmony_ci	mmp_overlay_set_addr(fbi->overlay, &addr);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	return 0;
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic int var_update(struct fb_info *info)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	struct mmpfb_info *fbi = info->par;
35062306a36Sopenharmony_ci	struct fb_var_screeninfo *var = &info->var;
35162306a36Sopenharmony_ci	struct fb_videomode *m;
35262306a36Sopenharmony_ci	int pix_fmt;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	/* set pix_fmt */
35562306a36Sopenharmony_ci	pix_fmt = var_to_pixfmt(var);
35662306a36Sopenharmony_ci	if (pix_fmt < 0)
35762306a36Sopenharmony_ci		return -EINVAL;
35862306a36Sopenharmony_ci	pixfmt_to_var(var, pix_fmt);
35962306a36Sopenharmony_ci	fbi->pix_fmt = pix_fmt;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	/* set var according to best video mode*/
36262306a36Sopenharmony_ci	m = (struct fb_videomode *)fb_match_mode(var, &info->modelist);
36362306a36Sopenharmony_ci	if (!m) {
36462306a36Sopenharmony_ci		dev_err(fbi->dev, "set par: no match mode, use best mode\n");
36562306a36Sopenharmony_ci		m = (struct fb_videomode *)fb_find_best_mode(var,
36662306a36Sopenharmony_ci				&info->modelist);
36762306a36Sopenharmony_ci		fb_videomode_to_var(var, m);
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci	memcpy(&fbi->mode, m, sizeof(struct fb_videomode));
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	/* fix to 2* yres */
37262306a36Sopenharmony_ci	var->yres_virtual = var->yres * 2;
37362306a36Sopenharmony_ci	info->fix.visual = (pix_fmt == PIXFMT_PSEUDOCOLOR) ?
37462306a36Sopenharmony_ci		FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
37562306a36Sopenharmony_ci	info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8;
37662306a36Sopenharmony_ci	info->fix.ypanstep = var->yres;
37762306a36Sopenharmony_ci	return 0;
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_cistatic void mmpfb_set_win(struct fb_info *info)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	struct mmpfb_info *fbi = info->par;
38362306a36Sopenharmony_ci	struct fb_var_screeninfo *var = &info->var;
38462306a36Sopenharmony_ci	struct mmp_win win;
38562306a36Sopenharmony_ci	u32 stride;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	memset(&win, 0, sizeof(win));
38862306a36Sopenharmony_ci	win.xsrc = win.xdst = fbi->mode.xres;
38962306a36Sopenharmony_ci	win.ysrc = win.ydst = fbi->mode.yres;
39062306a36Sopenharmony_ci	win.pix_fmt = fbi->pix_fmt;
39162306a36Sopenharmony_ci	stride = pixfmt_to_stride(win.pix_fmt);
39262306a36Sopenharmony_ci	win.pitch[0] = var->xres_virtual * stride;
39362306a36Sopenharmony_ci	win.pitch[1] = win.pitch[2] =
39462306a36Sopenharmony_ci		(stride == 1) ? (var->xres_virtual >> 1) : 0;
39562306a36Sopenharmony_ci	mmp_overlay_set_win(fbi->overlay, &win);
39662306a36Sopenharmony_ci}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_cistatic int mmpfb_set_par(struct fb_info *info)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	struct mmpfb_info *fbi = info->par;
40162306a36Sopenharmony_ci	struct fb_var_screeninfo *var = &info->var;
40262306a36Sopenharmony_ci	struct mmp_addr addr;
40362306a36Sopenharmony_ci	struct mmp_mode mode;
40462306a36Sopenharmony_ci	int ret;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	ret = var_update(info);
40762306a36Sopenharmony_ci	if (ret != 0)
40862306a36Sopenharmony_ci		return ret;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	/* set window/path according to new videomode */
41162306a36Sopenharmony_ci	fbmode_to_mmpmode(&mode, &fbi->mode, fbi->output_fmt);
41262306a36Sopenharmony_ci	mmp_path_set_mode(fbi->path, &mode);
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	/* set window related info */
41562306a36Sopenharmony_ci	mmpfb_set_win(info);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	/* set address always */
41862306a36Sopenharmony_ci	memset(&addr, 0, sizeof(addr));
41962306a36Sopenharmony_ci	addr.phys[0] = (var->yoffset * var->xres_virtual + var->xoffset)
42062306a36Sopenharmony_ci		* var->bits_per_pixel / 8 + fbi->fb_start_dma;
42162306a36Sopenharmony_ci	mmp_overlay_set_addr(fbi->overlay, &addr);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	return 0;
42462306a36Sopenharmony_ci}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_cistatic void mmpfb_power(struct mmpfb_info *fbi, int power)
42762306a36Sopenharmony_ci{
42862306a36Sopenharmony_ci	struct mmp_addr addr;
42962306a36Sopenharmony_ci	struct fb_var_screeninfo *var = &fbi->fb_info->var;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	/* for power on, always set address/window again */
43262306a36Sopenharmony_ci	if (power) {
43362306a36Sopenharmony_ci		/* set window related info */
43462306a36Sopenharmony_ci		mmpfb_set_win(fbi->fb_info);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci		/* set address always */
43762306a36Sopenharmony_ci		memset(&addr, 0, sizeof(addr));
43862306a36Sopenharmony_ci		addr.phys[0] = fbi->fb_start_dma +
43962306a36Sopenharmony_ci			(var->yoffset * var->xres_virtual + var->xoffset)
44062306a36Sopenharmony_ci			* var->bits_per_pixel / 8;
44162306a36Sopenharmony_ci		mmp_overlay_set_addr(fbi->overlay, &addr);
44262306a36Sopenharmony_ci	}
44362306a36Sopenharmony_ci	mmp_overlay_set_onoff(fbi->overlay, power);
44462306a36Sopenharmony_ci}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_cistatic int mmpfb_blank(int blank, struct fb_info *info)
44762306a36Sopenharmony_ci{
44862306a36Sopenharmony_ci	struct mmpfb_info *fbi = info->par;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	mmpfb_power(fbi, (blank == FB_BLANK_UNBLANK));
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	return 0;
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_cistatic const struct fb_ops mmpfb_ops = {
45662306a36Sopenharmony_ci	.owner		= THIS_MODULE,
45762306a36Sopenharmony_ci	FB_DEFAULT_IOMEM_OPS,
45862306a36Sopenharmony_ci	.fb_blank	= mmpfb_blank,
45962306a36Sopenharmony_ci	.fb_check_var	= mmpfb_check_var,
46062306a36Sopenharmony_ci	.fb_set_par	= mmpfb_set_par,
46162306a36Sopenharmony_ci	.fb_setcolreg	= mmpfb_setcolreg,
46262306a36Sopenharmony_ci	.fb_pan_display	= mmpfb_pan_display,
46362306a36Sopenharmony_ci};
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_cistatic int modes_setup(struct mmpfb_info *fbi)
46662306a36Sopenharmony_ci{
46762306a36Sopenharmony_ci	struct fb_videomode *videomodes;
46862306a36Sopenharmony_ci	struct mmp_mode *mmp_modes;
46962306a36Sopenharmony_ci	struct fb_info *info = fbi->fb_info;
47062306a36Sopenharmony_ci	int videomode_num, i;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	/* get videomodes from path */
47362306a36Sopenharmony_ci	videomode_num = mmp_path_get_modelist(fbi->path, &mmp_modes);
47462306a36Sopenharmony_ci	if (!videomode_num) {
47562306a36Sopenharmony_ci		dev_warn(fbi->dev, "can't get videomode num\n");
47662306a36Sopenharmony_ci		return 0;
47762306a36Sopenharmony_ci	}
47862306a36Sopenharmony_ci	/* put videomode list to info structure */
47962306a36Sopenharmony_ci	videomodes = kcalloc(videomode_num, sizeof(struct fb_videomode),
48062306a36Sopenharmony_ci			     GFP_KERNEL);
48162306a36Sopenharmony_ci	if (!videomodes)
48262306a36Sopenharmony_ci		return -ENOMEM;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	for (i = 0; i < videomode_num; i++)
48562306a36Sopenharmony_ci		mmpmode_to_fbmode(&videomodes[i], &mmp_modes[i]);
48662306a36Sopenharmony_ci	fb_videomode_to_modelist(videomodes, videomode_num, &info->modelist);
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	/* set videomode[0] as default mode */
48962306a36Sopenharmony_ci	memcpy(&fbi->mode, &videomodes[0], sizeof(struct fb_videomode));
49062306a36Sopenharmony_ci	fbi->output_fmt = mmp_modes[0].pix_fmt_out;
49162306a36Sopenharmony_ci	fb_videomode_to_var(&info->var, &fbi->mode);
49262306a36Sopenharmony_ci	mmp_path_set_mode(fbi->path, &mmp_modes[0]);
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	kfree(videomodes);
49562306a36Sopenharmony_ci	return videomode_num;
49662306a36Sopenharmony_ci}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_cistatic int fb_info_setup(struct fb_info *info,
49962306a36Sopenharmony_ci			struct mmpfb_info *fbi)
50062306a36Sopenharmony_ci{
50162306a36Sopenharmony_ci	int ret = 0;
50262306a36Sopenharmony_ci	/* Initialise static fb parameters.*/
50362306a36Sopenharmony_ci	info->flags = FBINFO_PARTIAL_PAN_OK |
50462306a36Sopenharmony_ci		FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN;
50562306a36Sopenharmony_ci	info->node = -1;
50662306a36Sopenharmony_ci	strcpy(info->fix.id, fbi->name);
50762306a36Sopenharmony_ci	info->fix.type = FB_TYPE_PACKED_PIXELS;
50862306a36Sopenharmony_ci	info->fix.type_aux = 0;
50962306a36Sopenharmony_ci	info->fix.xpanstep = 0;
51062306a36Sopenharmony_ci	info->fix.ypanstep = info->var.yres;
51162306a36Sopenharmony_ci	info->fix.ywrapstep = 0;
51262306a36Sopenharmony_ci	info->fix.accel = FB_ACCEL_NONE;
51362306a36Sopenharmony_ci	info->fix.smem_start = fbi->fb_start_dma;
51462306a36Sopenharmony_ci	info->fix.smem_len = fbi->fb_size;
51562306a36Sopenharmony_ci	info->fix.visual = (fbi->pix_fmt == PIXFMT_PSEUDOCOLOR) ?
51662306a36Sopenharmony_ci		FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
51762306a36Sopenharmony_ci	info->fix.line_length = info->var.xres_virtual *
51862306a36Sopenharmony_ci		info->var.bits_per_pixel / 8;
51962306a36Sopenharmony_ci	info->fbops = &mmpfb_ops;
52062306a36Sopenharmony_ci	info->pseudo_palette = fbi->pseudo_palette;
52162306a36Sopenharmony_ci	info->screen_buffer = fbi->fb_start;
52262306a36Sopenharmony_ci	info->screen_size = fbi->fb_size;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	/* For FB framework: Allocate color map and Register framebuffer*/
52562306a36Sopenharmony_ci	if (fb_alloc_cmap(&info->cmap, 256, 0) < 0)
52662306a36Sopenharmony_ci		ret = -ENOMEM;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	return ret;
52962306a36Sopenharmony_ci}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_cistatic void fb_info_clear(struct fb_info *info)
53262306a36Sopenharmony_ci{
53362306a36Sopenharmony_ci	fb_dealloc_cmap(&info->cmap);
53462306a36Sopenharmony_ci}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_cistatic int mmpfb_probe(struct platform_device *pdev)
53762306a36Sopenharmony_ci{
53862306a36Sopenharmony_ci	struct mmp_buffer_driver_mach_info *mi;
53962306a36Sopenharmony_ci	struct fb_info *info;
54062306a36Sopenharmony_ci	struct mmpfb_info *fbi;
54162306a36Sopenharmony_ci	int ret, modes_num;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	mi = pdev->dev.platform_data;
54462306a36Sopenharmony_ci	if (mi == NULL) {
54562306a36Sopenharmony_ci		dev_err(&pdev->dev, "no platform data defined\n");
54662306a36Sopenharmony_ci		return -EINVAL;
54762306a36Sopenharmony_ci	}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	/* initialize fb */
55062306a36Sopenharmony_ci	info = framebuffer_alloc(sizeof(struct mmpfb_info), &pdev->dev);
55162306a36Sopenharmony_ci	if (info == NULL)
55262306a36Sopenharmony_ci		return -ENOMEM;
55362306a36Sopenharmony_ci	fbi = info->par;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	/* init fb */
55662306a36Sopenharmony_ci	fbi->fb_info = info;
55762306a36Sopenharmony_ci	platform_set_drvdata(pdev, fbi);
55862306a36Sopenharmony_ci	fbi->dev = &pdev->dev;
55962306a36Sopenharmony_ci	fbi->name = mi->name;
56062306a36Sopenharmony_ci	fbi->pix_fmt = mi->default_pixfmt;
56162306a36Sopenharmony_ci	pixfmt_to_var(&info->var, fbi->pix_fmt);
56262306a36Sopenharmony_ci	mutex_init(&fbi->access_ok);
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	/* get display path by name */
56562306a36Sopenharmony_ci	fbi->path = mmp_get_path(mi->path_name);
56662306a36Sopenharmony_ci	if (!fbi->path) {
56762306a36Sopenharmony_ci		dev_err(&pdev->dev, "can't get the path %s\n", mi->path_name);
56862306a36Sopenharmony_ci		ret = -EINVAL;
56962306a36Sopenharmony_ci		goto failed_destroy_mutex;
57062306a36Sopenharmony_ci	}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	dev_info(fbi->dev, "path %s get\n", fbi->path->name);
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	/* get overlay */
57562306a36Sopenharmony_ci	fbi->overlay = mmp_path_get_overlay(fbi->path, mi->overlay_id);
57662306a36Sopenharmony_ci	if (!fbi->overlay) {
57762306a36Sopenharmony_ci		ret = -EINVAL;
57862306a36Sopenharmony_ci		goto failed_destroy_mutex;
57962306a36Sopenharmony_ci	}
58062306a36Sopenharmony_ci	/* set fetch used */
58162306a36Sopenharmony_ci	mmp_overlay_set_fetch(fbi->overlay, mi->dmafetch_id);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	modes_num = modes_setup(fbi);
58462306a36Sopenharmony_ci	if (modes_num < 0) {
58562306a36Sopenharmony_ci		ret = modes_num;
58662306a36Sopenharmony_ci		goto failed_destroy_mutex;
58762306a36Sopenharmony_ci	}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	/*
59062306a36Sopenharmony_ci	 * if get modes success, means not hotplug panels, use caculated buffer
59162306a36Sopenharmony_ci	 * or use default size
59262306a36Sopenharmony_ci	 */
59362306a36Sopenharmony_ci	if (modes_num > 0) {
59462306a36Sopenharmony_ci		/* fix to 2* yres */
59562306a36Sopenharmony_ci		info->var.yres_virtual = info->var.yres * 2;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci		/* Allocate framebuffer memory: size = modes xy *4 */
59862306a36Sopenharmony_ci		fbi->fb_size = info->var.xres_virtual * info->var.yres_virtual
59962306a36Sopenharmony_ci				* info->var.bits_per_pixel / 8;
60062306a36Sopenharmony_ci	} else {
60162306a36Sopenharmony_ci		fbi->fb_size = MMPFB_DEFAULT_SIZE;
60262306a36Sopenharmony_ci	}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	fbi->fb_start = dma_alloc_coherent(&pdev->dev, PAGE_ALIGN(fbi->fb_size),
60562306a36Sopenharmony_ci				&fbi->fb_start_dma, GFP_KERNEL);
60662306a36Sopenharmony_ci	if (fbi->fb_start == NULL) {
60762306a36Sopenharmony_ci		dev_err(&pdev->dev, "can't alloc framebuffer\n");
60862306a36Sopenharmony_ci		ret = -ENOMEM;
60962306a36Sopenharmony_ci		goto failed_destroy_mutex;
61062306a36Sopenharmony_ci	}
61162306a36Sopenharmony_ci	dev_info(fbi->dev, "fb %dk allocated\n", fbi->fb_size/1024);
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	/* fb power on */
61462306a36Sopenharmony_ci	if (modes_num > 0)
61562306a36Sopenharmony_ci		mmpfb_power(fbi, 1);
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	ret = fb_info_setup(info, fbi);
61862306a36Sopenharmony_ci	if (ret < 0)
61962306a36Sopenharmony_ci		goto failed_free_buff;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	ret = register_framebuffer(info);
62262306a36Sopenharmony_ci	if (ret < 0) {
62362306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to register fb: %d\n", ret);
62462306a36Sopenharmony_ci		ret = -ENXIO;
62562306a36Sopenharmony_ci		goto failed_clear_info;
62662306a36Sopenharmony_ci	}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	dev_info(fbi->dev, "loaded to /dev/fb%d <%s>.\n",
62962306a36Sopenharmony_ci		info->node, info->fix.id);
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci#ifdef CONFIG_LOGO
63262306a36Sopenharmony_ci	if (fbi->fb_start) {
63362306a36Sopenharmony_ci		fb_prepare_logo(info, 0);
63462306a36Sopenharmony_ci		fb_show_logo(info, 0);
63562306a36Sopenharmony_ci	}
63662306a36Sopenharmony_ci#endif
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	return 0;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_cifailed_clear_info:
64162306a36Sopenharmony_ci	fb_info_clear(info);
64262306a36Sopenharmony_cifailed_free_buff:
64362306a36Sopenharmony_ci	dma_free_coherent(&pdev->dev, PAGE_ALIGN(fbi->fb_size), fbi->fb_start,
64462306a36Sopenharmony_ci		fbi->fb_start_dma);
64562306a36Sopenharmony_cifailed_destroy_mutex:
64662306a36Sopenharmony_ci	mutex_destroy(&fbi->access_ok);
64762306a36Sopenharmony_ci	dev_err(fbi->dev, "mmp-fb: frame buffer device init failed\n");
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	framebuffer_release(info);
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	return ret;
65262306a36Sopenharmony_ci}
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_cistatic struct platform_driver mmpfb_driver = {
65562306a36Sopenharmony_ci	.driver		= {
65662306a36Sopenharmony_ci		.name	= "mmp-fb",
65762306a36Sopenharmony_ci	},
65862306a36Sopenharmony_ci	.probe		= mmpfb_probe,
65962306a36Sopenharmony_ci};
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_cistatic int mmpfb_init(void)
66262306a36Sopenharmony_ci{
66362306a36Sopenharmony_ci	return platform_driver_register(&mmpfb_driver);
66462306a36Sopenharmony_ci}
66562306a36Sopenharmony_cimodule_init(mmpfb_init);
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ciMODULE_AUTHOR("Zhou Zhu <zhou.zhu@marvell.com>");
66862306a36Sopenharmony_ciMODULE_DESCRIPTION("Framebuffer driver for Marvell displays");
66962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
670