brintos

brintos / linux-shallow public Read only

0
0
Text · 17.8 KiB · 2d9797c Raw
663 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * linux/drivers/video/mmp/fb/mmpfb.c4 * Framebuffer driver for Marvell Display controller.5 *6 * Copyright (C) 2012 Marvell Technology Group Ltd.7 * Authors: Zhou Zhu <zzhu3@marvell.com>8 */9#include <linux/module.h>10#include <linux/dma-mapping.h>11#include <linux/platform_device.h>12#include "mmpfb.h"13 14static int var_to_pixfmt(struct fb_var_screeninfo *var)15{16	/*17	 * Pseudocolor mode?18	 */19	if (var->bits_per_pixel == 8)20		return PIXFMT_PSEUDOCOLOR;21 22	/*23	 * Check for YUV422PLANAR.24	 */25	if (var->bits_per_pixel == 16 && var->red.length == 8 &&26			var->green.length == 4 && var->blue.length == 4) {27		if (var->green.offset >= var->blue.offset)28			return PIXFMT_YUV422P;29		else30			return PIXFMT_YVU422P;31	}32 33	/*34	 * Check for YUV420PLANAR.35	 */36	if (var->bits_per_pixel == 12 && var->red.length == 8 &&37			var->green.length == 2 && var->blue.length == 2) {38		if (var->green.offset >= var->blue.offset)39			return PIXFMT_YUV420P;40		else41			return PIXFMT_YVU420P;42	}43 44	/*45	 * Check for YUV422PACK.46	 */47	if (var->bits_per_pixel == 16 && var->red.length == 16 &&48			var->green.length == 16 && var->blue.length == 16) {49		if (var->red.offset == 0)50			return PIXFMT_YUYV;51		else if (var->green.offset >= var->blue.offset)52			return PIXFMT_UYVY;53		else54			return PIXFMT_VYUY;55	}56 57	/*58	 * Check for 565/1555.59	 */60	if (var->bits_per_pixel == 16 && var->red.length <= 5 &&61			var->green.length <= 6 && var->blue.length <= 5) {62		if (var->transp.length == 0) {63			if (var->red.offset >= var->blue.offset)64				return PIXFMT_RGB565;65			else66				return PIXFMT_BGR565;67		}68	}69 70	/*71	 * Check for 888/A888.72	 */73	if (var->bits_per_pixel <= 32 && var->red.length <= 8 &&74			var->green.length <= 8 && var->blue.length <= 8) {75		if (var->bits_per_pixel == 24 && var->transp.length == 0) {76			if (var->red.offset >= var->blue.offset)77				return PIXFMT_RGB888PACK;78			else79				return PIXFMT_BGR888PACK;80		}81 82		if (var->bits_per_pixel == 32 && var->transp.offset == 24) {83			if (var->red.offset >= var->blue.offset)84				return PIXFMT_RGBA888;85			else86				return PIXFMT_BGRA888;87		} else {88			if (var->red.offset >= var->blue.offset)89				return PIXFMT_RGB888UNPACK;90			else91				return PIXFMT_BGR888UNPACK;92		}93	}94 95	return -EINVAL;96}97 98static void pixfmt_to_var(struct fb_var_screeninfo *var, int pix_fmt)99{100	switch (pix_fmt) {101	case PIXFMT_RGB565:102		var->bits_per_pixel = 16;103		var->red.offset = 11;	var->red.length = 5;104		var->green.offset = 5;   var->green.length = 6;105		var->blue.offset = 0;	var->blue.length = 5;106		var->transp.offset = 0;  var->transp.length = 0;107		break;108	case PIXFMT_BGR565:109		var->bits_per_pixel = 16;110		var->red.offset = 0;	var->red.length = 5;111		var->green.offset = 5;	 var->green.length = 6;112		var->blue.offset = 11;	var->blue.length = 5;113		var->transp.offset = 0;  var->transp.length = 0;114		break;115	case PIXFMT_RGB888UNPACK:116		var->bits_per_pixel = 32;117		var->red.offset = 16;	var->red.length = 8;118		var->green.offset = 8;   var->green.length = 8;119		var->blue.offset = 0;	var->blue.length = 8;120		var->transp.offset = 0;  var->transp.length = 0;121		break;122	case PIXFMT_BGR888UNPACK:123		var->bits_per_pixel = 32;124		var->red.offset = 0;	var->red.length = 8;125		var->green.offset = 8;	 var->green.length = 8;126		var->blue.offset = 16;	var->blue.length = 8;127		var->transp.offset = 0;  var->transp.length = 0;128		break;129	case PIXFMT_RGBA888:130		var->bits_per_pixel = 32;131		var->red.offset = 16;	var->red.length = 8;132		var->green.offset = 8;   var->green.length = 8;133		var->blue.offset = 0;	var->blue.length = 8;134		var->transp.offset = 24; var->transp.length = 8;135		break;136	case PIXFMT_BGRA888:137		var->bits_per_pixel = 32;138		var->red.offset = 0;	var->red.length = 8;139		var->green.offset = 8;	 var->green.length = 8;140		var->blue.offset = 16;	var->blue.length = 8;141		var->transp.offset = 24; var->transp.length = 8;142		break;143	case PIXFMT_RGB888PACK:144		var->bits_per_pixel = 24;145		var->red.offset = 16;	var->red.length = 8;146		var->green.offset = 8;   var->green.length = 8;147		var->blue.offset = 0;	var->blue.length = 8;148		var->transp.offset = 0;  var->transp.length = 0;149		break;150	case PIXFMT_BGR888PACK:151		var->bits_per_pixel = 24;152		var->red.offset = 0;	var->red.length = 8;153		var->green.offset = 8;	 var->green.length = 8;154		var->blue.offset = 16;	var->blue.length = 8;155		var->transp.offset = 0;  var->transp.length = 0;156		break;157	case PIXFMT_YUV420P:158		var->bits_per_pixel = 12;159		var->red.offset = 4;	 var->red.length = 8;160		var->green.offset = 2;   var->green.length = 2;161		var->blue.offset = 0;   var->blue.length = 2;162		var->transp.offset = 0;  var->transp.length = 0;163		break;164	case PIXFMT_YVU420P:165		var->bits_per_pixel = 12;166		var->red.offset = 4;	 var->red.length = 8;167		var->green.offset = 0;	 var->green.length = 2;168		var->blue.offset = 2;	var->blue.length = 2;169		var->transp.offset = 0;  var->transp.length = 0;170		break;171	case PIXFMT_YUV422P:172		var->bits_per_pixel = 16;173		var->red.offset = 8;	 var->red.length = 8;174		var->green.offset = 4;   var->green.length = 4;175		var->blue.offset = 0;   var->blue.length = 4;176		var->transp.offset = 0;  var->transp.length = 0;177		break;178	case PIXFMT_YVU422P:179		var->bits_per_pixel = 16;180		var->red.offset = 8;	 var->red.length = 8;181		var->green.offset = 0;	 var->green.length = 4;182		var->blue.offset = 4;	var->blue.length = 4;183		var->transp.offset = 0;  var->transp.length = 0;184		break;185	case PIXFMT_UYVY:186		var->bits_per_pixel = 16;187		var->red.offset = 8;	 var->red.length = 16;188		var->green.offset = 4;   var->green.length = 16;189		var->blue.offset = 0;   var->blue.length = 16;190		var->transp.offset = 0;  var->transp.length = 0;191		break;192	case PIXFMT_VYUY:193		var->bits_per_pixel = 16;194		var->red.offset = 8;	 var->red.length = 16;195		var->green.offset = 0;	 var->green.length = 16;196		var->blue.offset = 4;	var->blue.length = 16;197		var->transp.offset = 0;  var->transp.length = 0;198		break;199	case PIXFMT_YUYV:200		var->bits_per_pixel = 16;201		var->red.offset = 0;	 var->red.length = 16;202		var->green.offset = 4;	 var->green.length = 16;203		var->blue.offset = 8;	var->blue.length = 16;204		var->transp.offset = 0;  var->transp.length = 0;205		break;206	case PIXFMT_PSEUDOCOLOR:207		var->bits_per_pixel = 8;208		var->red.offset = 0;	 var->red.length = 8;209		var->green.offset = 0;   var->green.length = 8;210		var->blue.offset = 0;	var->blue.length = 8;211		var->transp.offset = 0;  var->transp.length = 0;212		break;213	}214}215 216/*217 * fb framework has its limitation:218 * 1. input color/output color is not seprated219 * 2. fb_videomode not include output color220 * so for fb usage, we keep a output format which is not changed221 *  then it's added for mmpmode222 */223static void fbmode_to_mmpmode(struct mmp_mode *mode,224		struct fb_videomode *videomode, int output_fmt)225{226	u64 div_result = 1000000000000ll;227	mode->name = videomode->name;228	mode->refresh = videomode->refresh;229	mode->xres = videomode->xres;230	mode->yres = videomode->yres;231 232	do_div(div_result, videomode->pixclock);233	mode->pixclock_freq = (u32)div_result;234 235	mode->left_margin = videomode->left_margin;236	mode->right_margin = videomode->right_margin;237	mode->upper_margin = videomode->upper_margin;238	mode->lower_margin = videomode->lower_margin;239	mode->hsync_len = videomode->hsync_len;240	mode->vsync_len = videomode->vsync_len;241	mode->hsync_invert = !!(videomode->sync & FB_SYNC_HOR_HIGH_ACT);242	mode->vsync_invert = !!(videomode->sync & FB_SYNC_VERT_HIGH_ACT);243	/* no defined flag in fb, use vmode>>3*/244	mode->invert_pixclock = !!(videomode->vmode & 8);245	mode->pix_fmt_out = output_fmt;246}247 248static void mmpmode_to_fbmode(struct fb_videomode *videomode,249		struct mmp_mode *mode)250{251	u64 div_result = 1000000000000ll;252 253	videomode->name = mode->name;254	videomode->refresh = mode->refresh;255	videomode->xres = mode->xres;256	videomode->yres = mode->yres;257 258	do_div(div_result, mode->pixclock_freq);259	videomode->pixclock = (u32)div_result;260 261	videomode->left_margin = mode->left_margin;262	videomode->right_margin = mode->right_margin;263	videomode->upper_margin = mode->upper_margin;264	videomode->lower_margin = mode->lower_margin;265	videomode->hsync_len = mode->hsync_len;266	videomode->vsync_len = mode->vsync_len;267	videomode->sync = (mode->hsync_invert ? FB_SYNC_HOR_HIGH_ACT : 0)268		| (mode->vsync_invert ? FB_SYNC_VERT_HIGH_ACT : 0);269	videomode->vmode = mode->invert_pixclock ? 8 : 0;270}271 272static int mmpfb_check_var(struct fb_var_screeninfo *var,273		struct fb_info *info)274{275	struct mmpfb_info *fbi = info->par;276 277	if (var->bits_per_pixel == 8)278		return -EINVAL;279	/*280	 * Basic geometry sanity checks.281	 */282	if (var->xoffset + var->xres > var->xres_virtual)283		return -EINVAL;284	if (var->yoffset + var->yres > var->yres_virtual)285		return -EINVAL;286 287	/*288	 * Check size of framebuffer.289	 */290	if (var->xres_virtual * var->yres_virtual *291			(var->bits_per_pixel >> 3) > fbi->fb_size)292		return -EINVAL;293 294	return 0;295}296 297static unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)298{299	return ((chan & 0xffff) >> (16 - bf->length)) << bf->offset;300}301 302static u32 to_rgb(u16 red, u16 green, u16 blue)303{304	red >>= 8;305	green >>= 8;306	blue >>= 8;307 308	return (red << 16) | (green << 8) | blue;309}310 311static int mmpfb_setcolreg(unsigned int regno, unsigned int red,312		unsigned int green, unsigned int blue,313		unsigned int trans, struct fb_info *info)314{315	struct mmpfb_info *fbi = info->par;316	u32 val;317 318	if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 16) {319		val =  chan_to_field(red,   &info->var.red);320		val |= chan_to_field(green, &info->var.green);321		val |= chan_to_field(blue , &info->var.blue);322		fbi->pseudo_palette[regno] = val;323	}324 325	if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR && regno < 256) {326		val = to_rgb(red, green, blue);327		/* TODO */328	}329 330	return 0;331}332 333static int mmpfb_pan_display(struct fb_var_screeninfo *var,334		struct fb_info *info)335{336	struct mmpfb_info *fbi = info->par;337	struct mmp_addr addr;338 339	memset(&addr, 0, sizeof(addr));340	addr.phys[0] = (var->yoffset * var->xres_virtual + var->xoffset)341		* var->bits_per_pixel / 8 + fbi->fb_start_dma;342	mmp_overlay_set_addr(fbi->overlay, &addr);343 344	return 0;345}346 347static int var_update(struct fb_info *info)348{349	struct mmpfb_info *fbi = info->par;350	struct fb_var_screeninfo *var = &info->var;351	struct fb_videomode *m;352	int pix_fmt;353 354	/* set pix_fmt */355	pix_fmt = var_to_pixfmt(var);356	if (pix_fmt < 0)357		return -EINVAL;358	pixfmt_to_var(var, pix_fmt);359	fbi->pix_fmt = pix_fmt;360 361	/* set var according to best video mode*/362	m = (struct fb_videomode *)fb_match_mode(var, &info->modelist);363	if (!m) {364		dev_err(fbi->dev, "set par: no match mode, use best mode\n");365		m = (struct fb_videomode *)fb_find_best_mode(var,366				&info->modelist);367		fb_videomode_to_var(var, m);368	}369	memcpy(&fbi->mode, m, sizeof(struct fb_videomode));370 371	/* fix to 2* yres */372	var->yres_virtual = var->yres * 2;373	info->fix.visual = (pix_fmt == PIXFMT_PSEUDOCOLOR) ?374		FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;375	info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8;376	info->fix.ypanstep = var->yres;377	return 0;378}379 380static void mmpfb_set_win(struct fb_info *info)381{382	struct mmpfb_info *fbi = info->par;383	struct fb_var_screeninfo *var = &info->var;384	struct mmp_win win;385	u32 stride;386 387	memset(&win, 0, sizeof(win));388	win.xsrc = win.xdst = fbi->mode.xres;389	win.ysrc = win.ydst = fbi->mode.yres;390	win.pix_fmt = fbi->pix_fmt;391	stride = pixfmt_to_stride(win.pix_fmt);392	win.pitch[0] = var->xres_virtual * stride;393	win.pitch[1] = win.pitch[2] =394		(stride == 1) ? (var->xres_virtual >> 1) : 0;395	mmp_overlay_set_win(fbi->overlay, &win);396}397 398static int mmpfb_set_par(struct fb_info *info)399{400	struct mmpfb_info *fbi = info->par;401	struct fb_var_screeninfo *var = &info->var;402	struct mmp_addr addr;403	struct mmp_mode mode;404	int ret;405 406	ret = var_update(info);407	if (ret != 0)408		return ret;409 410	/* set window/path according to new videomode */411	fbmode_to_mmpmode(&mode, &fbi->mode, fbi->output_fmt);412	mmp_path_set_mode(fbi->path, &mode);413 414	/* set window related info */415	mmpfb_set_win(info);416 417	/* set address always */418	memset(&addr, 0, sizeof(addr));419	addr.phys[0] = (var->yoffset * var->xres_virtual + var->xoffset)420		* var->bits_per_pixel / 8 + fbi->fb_start_dma;421	mmp_overlay_set_addr(fbi->overlay, &addr);422 423	return 0;424}425 426static void mmpfb_power(struct mmpfb_info *fbi, int power)427{428	struct mmp_addr addr;429	struct fb_var_screeninfo *var = &fbi->fb_info->var;430 431	/* for power on, always set address/window again */432	if (power) {433		/* set window related info */434		mmpfb_set_win(fbi->fb_info);435 436		/* set address always */437		memset(&addr, 0, sizeof(addr));438		addr.phys[0] = fbi->fb_start_dma +439			(var->yoffset * var->xres_virtual + var->xoffset)440			* var->bits_per_pixel / 8;441		mmp_overlay_set_addr(fbi->overlay, &addr);442	}443	mmp_overlay_set_onoff(fbi->overlay, power);444}445 446static int mmpfb_blank(int blank, struct fb_info *info)447{448	struct mmpfb_info *fbi = info->par;449 450	mmpfb_power(fbi, (blank == FB_BLANK_UNBLANK));451 452	return 0;453}454 455static const struct fb_ops mmpfb_ops = {456	.owner		= THIS_MODULE,457	FB_DEFAULT_IOMEM_OPS,458	.fb_blank	= mmpfb_blank,459	.fb_check_var	= mmpfb_check_var,460	.fb_set_par	= mmpfb_set_par,461	.fb_setcolreg	= mmpfb_setcolreg,462	.fb_pan_display	= mmpfb_pan_display,463};464 465static int modes_setup(struct mmpfb_info *fbi)466{467	struct fb_videomode *videomodes;468	struct mmp_mode *mmp_modes;469	struct fb_info *info = fbi->fb_info;470	int videomode_num, i;471 472	/* get videomodes from path */473	videomode_num = mmp_path_get_modelist(fbi->path, &mmp_modes);474	if (!videomode_num) {475		dev_warn(fbi->dev, "can't get videomode num\n");476		return 0;477	}478	/* put videomode list to info structure */479	videomodes = kcalloc(videomode_num, sizeof(struct fb_videomode),480			     GFP_KERNEL);481	if (!videomodes)482		return -ENOMEM;483 484	for (i = 0; i < videomode_num; i++)485		mmpmode_to_fbmode(&videomodes[i], &mmp_modes[i]);486	fb_videomode_to_modelist(videomodes, videomode_num, &info->modelist);487 488	/* set videomode[0] as default mode */489	memcpy(&fbi->mode, &videomodes[0], sizeof(struct fb_videomode));490	fbi->output_fmt = mmp_modes[0].pix_fmt_out;491	fb_videomode_to_var(&info->var, &fbi->mode);492	mmp_path_set_mode(fbi->path, &mmp_modes[0]);493 494	kfree(videomodes);495	return videomode_num;496}497 498static int fb_info_setup(struct fb_info *info,499			struct mmpfb_info *fbi)500{501	int ret = 0;502	/* Initialise static fb parameters.*/503	info->flags = FBINFO_PARTIAL_PAN_OK |504		FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN;505	info->node = -1;506	strcpy(info->fix.id, fbi->name);507	info->fix.type = FB_TYPE_PACKED_PIXELS;508	info->fix.type_aux = 0;509	info->fix.xpanstep = 0;510	info->fix.ypanstep = info->var.yres;511	info->fix.ywrapstep = 0;512	info->fix.accel = FB_ACCEL_NONE;513	info->fix.smem_start = fbi->fb_start_dma;514	info->fix.smem_len = fbi->fb_size;515	info->fix.visual = (fbi->pix_fmt == PIXFMT_PSEUDOCOLOR) ?516		FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;517	info->fix.line_length = info->var.xres_virtual *518		info->var.bits_per_pixel / 8;519	info->fbops = &mmpfb_ops;520	info->pseudo_palette = fbi->pseudo_palette;521	info->screen_buffer = fbi->fb_start;522	info->screen_size = fbi->fb_size;523 524	/* For FB framework: Allocate color map and Register framebuffer*/525	if (fb_alloc_cmap(&info->cmap, 256, 0) < 0)526		ret = -ENOMEM;527 528	return ret;529}530 531static void fb_info_clear(struct fb_info *info)532{533	fb_dealloc_cmap(&info->cmap);534}535 536static int mmpfb_probe(struct platform_device *pdev)537{538	struct mmp_buffer_driver_mach_info *mi;539	struct fb_info *info;540	struct mmpfb_info *fbi;541	int ret, modes_num;542 543	mi = pdev->dev.platform_data;544	if (mi == NULL) {545		dev_err(&pdev->dev, "no platform data defined\n");546		return -EINVAL;547	}548 549	/* initialize fb */550	info = framebuffer_alloc(sizeof(struct mmpfb_info), &pdev->dev);551	if (info == NULL)552		return -ENOMEM;553	fbi = info->par;554 555	/* init fb */556	fbi->fb_info = info;557	platform_set_drvdata(pdev, fbi);558	fbi->dev = &pdev->dev;559	fbi->name = mi->name;560	fbi->pix_fmt = mi->default_pixfmt;561	pixfmt_to_var(&info->var, fbi->pix_fmt);562	mutex_init(&fbi->access_ok);563 564	/* get display path by name */565	fbi->path = mmp_get_path(mi->path_name);566	if (!fbi->path) {567		dev_err(&pdev->dev, "can't get the path %s\n", mi->path_name);568		ret = -EINVAL;569		goto failed_destroy_mutex;570	}571 572	dev_info(fbi->dev, "path %s get\n", fbi->path->name);573 574	/* get overlay */575	fbi->overlay = mmp_path_get_overlay(fbi->path, mi->overlay_id);576	if (!fbi->overlay) {577		ret = -EINVAL;578		goto failed_destroy_mutex;579	}580	/* set fetch used */581	mmp_overlay_set_fetch(fbi->overlay, mi->dmafetch_id);582 583	modes_num = modes_setup(fbi);584	if (modes_num < 0) {585		ret = modes_num;586		goto failed_destroy_mutex;587	}588 589	/*590	 * if get modes success, means not hotplug panels, use caculated buffer591	 * or use default size592	 */593	if (modes_num > 0) {594		/* fix to 2* yres */595		info->var.yres_virtual = info->var.yres * 2;596 597		/* Allocate framebuffer memory: size = modes xy *4 */598		fbi->fb_size = info->var.xres_virtual * info->var.yres_virtual599				* info->var.bits_per_pixel / 8;600	} else {601		fbi->fb_size = MMPFB_DEFAULT_SIZE;602	}603 604	fbi->fb_start = dma_alloc_coherent(&pdev->dev, PAGE_ALIGN(fbi->fb_size),605				&fbi->fb_start_dma, GFP_KERNEL);606	if (fbi->fb_start == NULL) {607		dev_err(&pdev->dev, "can't alloc framebuffer\n");608		ret = -ENOMEM;609		goto failed_destroy_mutex;610	}611	dev_info(fbi->dev, "fb %dk allocated\n", fbi->fb_size/1024);612 613	/* fb power on */614	if (modes_num > 0)615		mmpfb_power(fbi, 1);616 617	ret = fb_info_setup(info, fbi);618	if (ret < 0)619		goto failed_free_buff;620 621	ret = register_framebuffer(info);622	if (ret < 0) {623		dev_err(&pdev->dev, "Failed to register fb: %d\n", ret);624		ret = -ENXIO;625		goto failed_clear_info;626	}627 628	dev_info(fbi->dev, "loaded to /dev/fb%d <%s>.\n",629		info->node, info->fix.id);630 631	return 0;632 633failed_clear_info:634	fb_info_clear(info);635failed_free_buff:636	dma_free_coherent(&pdev->dev, PAGE_ALIGN(fbi->fb_size), fbi->fb_start,637		fbi->fb_start_dma);638failed_destroy_mutex:639	mutex_destroy(&fbi->access_ok);640	dev_err(fbi->dev, "mmp-fb: frame buffer device init failed\n");641 642	framebuffer_release(info);643 644	return ret;645}646 647static struct platform_driver mmpfb_driver = {648	.driver		= {649		.name	= "mmp-fb",650	},651	.probe		= mmpfb_probe,652};653 654static int mmpfb_init(void)655{656	return platform_driver_register(&mmpfb_driver);657}658module_init(mmpfb_init);659 660MODULE_AUTHOR("Zhou Zhu <zhou.zhu@marvell.com>");661MODULE_DESCRIPTION("Framebuffer driver for Marvell displays");662MODULE_LICENSE("GPL");663