brintos

brintos / linux-shallow public Read only

0
0
Text · 51.3 KiB · 5ac8201 Raw
1995 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.4 *5 *  Freescale DIU Frame Buffer device driver6 *7 *  Authors: Hongjun Chen <hong-jun.chen@freescale.com>8 *           Paul Widmer <paul.widmer@freescale.com>9 *           Srikanth Srinivasan <srikanth.srinivasan@freescale.com>10 *           York Sun <yorksun@freescale.com>11 *12 *   Based on imxfb.c Copyright (C) 2004 S.Hauer, Pengutronix13 */14 15#include <linux/module.h>16#include <linux/kernel.h>17#include <linux/errno.h>18#include <linux/string.h>19#include <linux/slab.h>20#include <linux/fb.h>21#include <linux/init.h>22#include <linux/dma-mapping.h>23#include <linux/platform_device.h>24#include <linux/interrupt.h>25#include <linux/clk.h>26#include <linux/uaccess.h>27#include <linux/vmalloc.h>28#include <linux/spinlock.h>29#include <linux/of_address.h>30#include <linux/of_irq.h>31 32#include <sysdev/fsl_soc.h>33#include <linux/fsl-diu-fb.h>34#include "edid.h"35 36#define NUM_AOIS	5	/* 1 for plane 0, 2 for planes 1 & 2 each */37 38/* HW cursor parameters */39#define MAX_CURS		3240 41/* INT_STATUS/INT_MASK field descriptions */42#define INT_VSYNC	0x01	/* Vsync interrupt  */43#define INT_VSYNC_WB	0x02	/* Vsync interrupt for write back operation */44#define INT_UNDRUN	0x04	/* Under run exception interrupt */45#define INT_PARERR	0x08	/* Display parameters error interrupt */46#define INT_LS_BF_VS	0x10	/* Lines before vsync. interrupt */47 48/*49 * List of supported video modes50 *51 * The first entry is the default video mode.  The remain entries are in52 * order if increasing resolution and frequency.  The 320x240-60 mode is53 * the initial AOI for the second and third planes.54 */55static struct fb_videomode fsl_diu_mode_db[] = {56	{57		.refresh	= 60,58		.xres		= 1024,59		.yres		= 768,60		.pixclock	= 15385,61		.left_margin	= 160,62		.right_margin	= 24,63		.upper_margin	= 29,64		.lower_margin	= 3,65		.hsync_len	= 136,66		.vsync_len	= 6,67		.sync		= FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,68		.vmode		= FB_VMODE_NONINTERLACED69	},70	{71		.refresh	= 60,72		.xres		= 320,73		.yres		= 240,74		.pixclock	= 79440,75		.left_margin	= 16,76		.right_margin	= 16,77		.upper_margin	= 16,78		.lower_margin	= 5,79		.hsync_len	= 48,80		.vsync_len	= 1,81		.sync		= FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,82		.vmode		= FB_VMODE_NONINTERLACED83	},84	{85		.refresh        = 60,86		.xres           = 640,87		.yres           = 480,88		.pixclock       = 39722,89		.left_margin    = 48,90		.right_margin   = 16,91		.upper_margin   = 33,92		.lower_margin   = 10,93		.hsync_len      = 96,94		.vsync_len      = 2,95		.sync           = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,96		.vmode          = FB_VMODE_NONINTERLACED97	},98	{99		.refresh        = 72,100		.xres           = 640,101		.yres           = 480,102		.pixclock       = 32052,103		.left_margin    = 128,104		.right_margin   = 24,105		.upper_margin   = 28,106		.lower_margin   = 9,107		.hsync_len      = 40,108		.vsync_len      = 3,109		.sync           = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,110		.vmode          = FB_VMODE_NONINTERLACED111	},112	{113		.refresh        = 75,114		.xres           = 640,115		.yres           = 480,116		.pixclock       = 31747,117		.left_margin    = 120,118		.right_margin   = 16,119		.upper_margin   = 16,120		.lower_margin   = 1,121		.hsync_len      = 64,122		.vsync_len      = 3,123		.sync           = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,124		.vmode          = FB_VMODE_NONINTERLACED125	},126	{127		.refresh        = 90,128		.xres           = 640,129		.yres           = 480,130		.pixclock       = 25057,131		.left_margin    = 120,132		.right_margin   = 32,133		.upper_margin   = 14,134		.lower_margin   = 25,135		.hsync_len      = 40,136		.vsync_len      = 14,137		.sync           = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,138		.vmode          = FB_VMODE_NONINTERLACED139	},140	{141		.refresh        = 100,142		.xres           = 640,143		.yres           = 480,144		.pixclock       = 22272,145		.left_margin    = 48,146		.right_margin   = 32,147		.upper_margin   = 17,148		.lower_margin   = 22,149		.hsync_len      = 128,150		.vsync_len      = 12,151		.sync           = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,152		.vmode          = FB_VMODE_NONINTERLACED153	},154	{155		.refresh	= 60,156		.xres		= 800,157		.yres		= 480,158		.pixclock	= 33805,159		.left_margin	= 96,160		.right_margin	= 24,161		.upper_margin	= 10,162		.lower_margin	= 3,163		.hsync_len	= 72,164		.vsync_len	= 7,165		.sync		= FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,166		.vmode		= FB_VMODE_NONINTERLACED167	},168	{169		.refresh        = 60,170		.xres           = 800,171		.yres           = 600,172		.pixclock       = 25000,173		.left_margin    = 88,174		.right_margin   = 40,175		.upper_margin   = 23,176		.lower_margin   = 1,177		.hsync_len      = 128,178		.vsync_len      = 4,179		.sync           = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,180		.vmode          = FB_VMODE_NONINTERLACED181	},182	{183		.refresh	= 60,184		.xres		= 854,185		.yres		= 480,186		.pixclock	= 31518,187		.left_margin	= 104,188		.right_margin	= 16,189		.upper_margin	= 13,190		.lower_margin	= 1,191		.hsync_len	= 88,192		.vsync_len	= 3,193		.sync		= FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,194		.vmode		= FB_VMODE_NONINTERLACED195	},196	{197		.refresh	= 70,198		.xres		= 1024,199		.yres		= 768,200		.pixclock	= 16886,201		.left_margin	= 3,202		.right_margin	= 3,203		.upper_margin	= 2,204		.lower_margin	= 2,205		.hsync_len	= 40,206		.vsync_len	= 18,207		.sync		= FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,208		.vmode		= FB_VMODE_NONINTERLACED209	},210	{211		.refresh	= 75,212		.xres		= 1024,213		.yres		= 768,214		.pixclock	= 15009,215		.left_margin	= 3,216		.right_margin	= 3,217		.upper_margin	= 2,218		.lower_margin	= 2,219		.hsync_len	= 80,220		.vsync_len	= 32,221		.sync		= FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,222		.vmode		= FB_VMODE_NONINTERLACED223	},224	{225		.refresh	= 60,226		.xres		= 1280,227		.yres		= 480,228		.pixclock	= 18939,229		.left_margin	= 353,230		.right_margin	= 47,231		.upper_margin	= 39,232		.lower_margin	= 4,233		.hsync_len	= 8,234		.vsync_len	= 2,235		.sync		= FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,236		.vmode		= FB_VMODE_NONINTERLACED237	},238	{239		.refresh	= 60,240		.xres		= 1280,241		.yres		= 720,242		.pixclock	= 13426,243		.left_margin	= 192,244		.right_margin	= 64,245		.upper_margin	= 22,246		.lower_margin	= 1,247		.hsync_len	= 136,248		.vsync_len	= 3,249		.sync		= FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,250		.vmode		= FB_VMODE_NONINTERLACED251	},252	{253		.refresh	= 60,254		.xres		= 1280,255		.yres		= 1024,256		.pixclock	= 9375,257		.left_margin	= 38,258		.right_margin	= 128,259		.upper_margin	= 2,260		.lower_margin	= 7,261		.hsync_len	= 216,262		.vsync_len	= 37,263		.sync		= FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,264		.vmode		= FB_VMODE_NONINTERLACED265	},266	{267		.refresh	= 70,268		.xres		= 1280,269		.yres		= 1024,270		.pixclock	= 9380,271		.left_margin	= 6,272		.right_margin	= 6,273		.upper_margin	= 4,274		.lower_margin	= 4,275		.hsync_len	= 60,276		.vsync_len	= 94,277		.sync		= FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,278		.vmode		= FB_VMODE_NONINTERLACED279	},280	{281		.refresh	= 75,282		.xres		= 1280,283		.yres		= 1024,284		.pixclock	= 9380,285		.left_margin	= 6,286		.right_margin	= 6,287		.upper_margin	= 4,288		.lower_margin	= 4,289		.hsync_len	= 60,290		.vsync_len	= 15,291		.sync		= FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,292		.vmode		= FB_VMODE_NONINTERLACED293	},294	{295		.refresh	= 60,296		.xres		= 1920,297		.yres		= 1080,298		.pixclock	= 5787,299		.left_margin	= 328,300		.right_margin	= 120,301		.upper_margin	= 34,302		.lower_margin	= 1,303		.hsync_len	= 208,304		.vsync_len	= 3,305		.sync		= FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,306		.vmode		= FB_VMODE_NONINTERLACED307	},308};309 310static char *fb_mode;311static unsigned long default_bpp = 32;312static enum fsl_diu_monitor_port monitor_port;313static char *monitor_string;314 315#if defined(CONFIG_NOT_COHERENT_CACHE)316static u8 *coherence_data;317static size_t coherence_data_size;318static unsigned int d_cache_line_size;319#endif320 321static DEFINE_SPINLOCK(diu_lock);322 323enum mfb_index {324	PLANE0 = 0,	/* Plane 0, only one AOI that fills the screen */325	PLANE1_AOI0,	/* Plane 1, first AOI */326	PLANE1_AOI1,	/* Plane 1, second AOI */327	PLANE2_AOI0,	/* Plane 2, first AOI */328	PLANE2_AOI1,	/* Plane 2, second AOI */329};330 331struct mfb_info {332	enum mfb_index index;333	char *id;334	int registered;335	unsigned long pseudo_palette[16];336	struct diu_ad *ad;337	unsigned char g_alpha;338	unsigned int count;339	int x_aoi_d;		/* aoi display x offset to physical screen */340	int y_aoi_d;		/* aoi display y offset to physical screen */341	struct fsl_diu_data *parent;342};343 344/**345 * struct fsl_diu_data - per-DIU data structure346 * @dma_addr: DMA address of this structure347 * @fsl_diu_info: fb_info objects, one per AOI348 * @dev_attr: sysfs structure349 * @irq: IRQ350 * @monitor_port: the monitor port this DIU is connected to351 * @diu_reg: pointer to the DIU hardware registers352 * @reg_lock: spinlock for register access353 * @dummy_aoi: video buffer for the 4x4 32-bit dummy AOI354 * dummy_ad: DIU Area Descriptor for the dummy AOI355 * @ad[]: Area Descriptors for each real AOI356 * @gamma: gamma color table357 * @cursor: hardware cursor data358 * @blank_cursor: blank cursor for hiding cursor359 * @next_cursor: scratch space to build load cursor360 * @edid_data: EDID information buffer361 * @has_edid: whether or not the EDID buffer is valid362 *363 * This data structure must be allocated with 32-byte alignment, so that the364 * internal fields can be aligned properly.365 */366struct fsl_diu_data {367	dma_addr_t dma_addr;368	struct fb_info fsl_diu_info[NUM_AOIS];369	struct mfb_info mfb[NUM_AOIS];370	struct device_attribute dev_attr;371	unsigned int irq;372	enum fsl_diu_monitor_port monitor_port;373	struct diu __iomem *diu_reg;374	spinlock_t reg_lock;375	u8 dummy_aoi[4 * 4 * 4];376	struct diu_ad dummy_ad __aligned(8);377	struct diu_ad ad[NUM_AOIS] __aligned(8);378	u8 gamma[256 * 3] __aligned(32);379	/* It's easier to parse the cursor data as little-endian */380	__le16 cursor[MAX_CURS * MAX_CURS] __aligned(32);381	/* Blank cursor data -- used to hide the cursor */382	__le16 blank_cursor[MAX_CURS * MAX_CURS] __aligned(32);383	/* Scratch cursor data -- used to build new cursor */384	__le16 next_cursor[MAX_CURS * MAX_CURS] __aligned(32);385	uint8_t edid_data[EDID_LENGTH];386	bool has_edid;387} __aligned(32);388 389/* Determine the DMA address of a member of the fsl_diu_data structure */390#define DMA_ADDR(p, f) ((p)->dma_addr + offsetof(struct fsl_diu_data, f))391 392static const struct mfb_info mfb_template[] = {393	{394		.index = PLANE0,395		.id = "Panel0",396		.registered = 0,397		.count = 0,398		.x_aoi_d = 0,399		.y_aoi_d = 0,400	},401	{402		.index = PLANE1_AOI0,403		.id = "Panel1 AOI0",404		.registered = 0,405		.g_alpha = 0xff,406		.count = 0,407		.x_aoi_d = 0,408		.y_aoi_d = 0,409	},410	{411		.index = PLANE1_AOI1,412		.id = "Panel1 AOI1",413		.registered = 0,414		.g_alpha = 0xff,415		.count = 0,416		.x_aoi_d = 0,417		.y_aoi_d = 480,418	},419	{420		.index = PLANE2_AOI0,421		.id = "Panel2 AOI0",422		.registered = 0,423		.g_alpha = 0xff,424		.count = 0,425		.x_aoi_d = 640,426		.y_aoi_d = 0,427	},428	{429		.index = PLANE2_AOI1,430		.id = "Panel2 AOI1",431		.registered = 0,432		.g_alpha = 0xff,433		.count = 0,434		.x_aoi_d = 640,435		.y_aoi_d = 480,436	},437};438 439#ifdef DEBUG440static void __attribute__ ((unused)) fsl_diu_dump(struct diu __iomem *hw)441{442	mb();443	pr_debug("DIU: desc=%08x,%08x,%08x, gamma=%08x palette=%08x "444		 "cursor=%08x curs_pos=%08x diu_mode=%08x bgnd=%08x "445		 "disp_size=%08x hsyn_para=%08x vsyn_para=%08x syn_pol=%08x "446		 "thresholds=%08x int_mask=%08x plut=%08x\n",447		 hw->desc[0], hw->desc[1], hw->desc[2], hw->gamma,448		 hw->palette, hw->cursor, hw->curs_pos, hw->diu_mode,449		 hw->bgnd, hw->disp_size, hw->hsyn_para, hw->vsyn_para,450		 hw->syn_pol, hw->thresholds, hw->int_mask, hw->plut);451	rmb();452}453#endif454 455/**456 * fsl_diu_name_to_port - convert a port name to a monitor port enum457 *458 * Takes the name of a monitor port ("dvi", "lvds", or "dlvds") and returns459 * the enum fsl_diu_monitor_port that corresponds to that string.460 *461 * For compatibility with older versions, a number ("0", "1", or "2") is also462 * supported.463 *464 * If the string is unknown, DVI is assumed.465 *466 * If the particular port is not supported by the platform, another port467 * (platform-specific) is chosen instead.468 */469static enum fsl_diu_monitor_port fsl_diu_name_to_port(const char *s)470{471	enum fsl_diu_monitor_port port = FSL_DIU_PORT_DVI;472	unsigned long val;473 474	if (s) {475		if (!kstrtoul(s, 10, &val) && (val <= 2))476			port = (enum fsl_diu_monitor_port) val;477		else if (strncmp(s, "lvds", 4) == 0)478			port = FSL_DIU_PORT_LVDS;479		else if (strncmp(s, "dlvds", 5) == 0)480			port = FSL_DIU_PORT_DLVDS;481	}482 483	if (diu_ops.valid_monitor_port)484		port = diu_ops.valid_monitor_port(port);485 486	return port;487}488 489/*490 * Workaround for failed writing desc register of planes.491 * Needed with MPC5121 DIU rev 2.0 silicon.492 */493static void wr_reg_wa(u32 *reg, u32 val)494{495	do {496		out_be32(reg, val);497	} while (in_be32(reg) != val);498}499 500static void fsl_diu_enable_panel(struct fb_info *info)501{502	struct mfb_info *pmfbi, *cmfbi, *mfbi = info->par;503	struct diu_ad *ad = mfbi->ad;504	struct fsl_diu_data *data = mfbi->parent;505	struct diu __iomem *hw = data->diu_reg;506 507	switch (mfbi->index) {508	case PLANE0:509		wr_reg_wa(&hw->desc[0], ad->paddr);510		break;511	case PLANE1_AOI0:512		cmfbi = &data->mfb[2];513		if (hw->desc[1] != ad->paddr) {	/* AOI0 closed */514			if (cmfbi->count > 0)	/* AOI1 open */515				ad->next_ad =516					cpu_to_le32(cmfbi->ad->paddr);517			else518				ad->next_ad = 0;519			wr_reg_wa(&hw->desc[1], ad->paddr);520		}521		break;522	case PLANE2_AOI0:523		cmfbi = &data->mfb[4];524		if (hw->desc[2] != ad->paddr) {	/* AOI0 closed */525			if (cmfbi->count > 0)	/* AOI1 open */526				ad->next_ad =527					cpu_to_le32(cmfbi->ad->paddr);528			else529				ad->next_ad = 0;530			wr_reg_wa(&hw->desc[2], ad->paddr);531		}532		break;533	case PLANE1_AOI1:534		pmfbi = &data->mfb[1];535		ad->next_ad = 0;536		if (hw->desc[1] == data->dummy_ad.paddr)537			wr_reg_wa(&hw->desc[1], ad->paddr);538		else					/* AOI0 open */539			pmfbi->ad->next_ad = cpu_to_le32(ad->paddr);540		break;541	case PLANE2_AOI1:542		pmfbi = &data->mfb[3];543		ad->next_ad = 0;544		if (hw->desc[2] == data->dummy_ad.paddr)545			wr_reg_wa(&hw->desc[2], ad->paddr);546		else				/* AOI0 was open */547			pmfbi->ad->next_ad = cpu_to_le32(ad->paddr);548		break;549	}550}551 552static void fsl_diu_disable_panel(struct fb_info *info)553{554	struct mfb_info *pmfbi, *cmfbi, *mfbi = info->par;555	struct diu_ad *ad = mfbi->ad;556	struct fsl_diu_data *data = mfbi->parent;557	struct diu __iomem *hw = data->diu_reg;558 559	switch (mfbi->index) {560	case PLANE0:561		wr_reg_wa(&hw->desc[0], 0);562		break;563	case PLANE1_AOI0:564		cmfbi = &data->mfb[2];565		if (cmfbi->count > 0)	/* AOI1 is open */566			wr_reg_wa(&hw->desc[1], cmfbi->ad->paddr);567					/* move AOI1 to the first */568		else			/* AOI1 was closed */569			wr_reg_wa(&hw->desc[1], data->dummy_ad.paddr);570					/* close AOI 0 */571		break;572	case PLANE2_AOI0:573		cmfbi = &data->mfb[4];574		if (cmfbi->count > 0)	/* AOI1 is open */575			wr_reg_wa(&hw->desc[2], cmfbi->ad->paddr);576					/* move AOI1 to the first */577		else			/* AOI1 was closed */578			wr_reg_wa(&hw->desc[2], data->dummy_ad.paddr);579					/* close AOI 0 */580		break;581	case PLANE1_AOI1:582		pmfbi = &data->mfb[1];583		if (hw->desc[1] != ad->paddr) {584				/* AOI1 is not the first in the chain */585			if (pmfbi->count > 0)586					/* AOI0 is open, must be the first */587				pmfbi->ad->next_ad = 0;588		} else			/* AOI1 is the first in the chain */589			wr_reg_wa(&hw->desc[1], data->dummy_ad.paddr);590					/* close AOI 1 */591		break;592	case PLANE2_AOI1:593		pmfbi = &data->mfb[3];594		if (hw->desc[2] != ad->paddr) {595				/* AOI1 is not the first in the chain */596			if (pmfbi->count > 0)597				/* AOI0 is open, must be the first */598				pmfbi->ad->next_ad = 0;599		} else		/* AOI1 is the first in the chain */600			wr_reg_wa(&hw->desc[2], data->dummy_ad.paddr);601				/* close AOI 1 */602		break;603	}604}605 606static void enable_lcdc(struct fb_info *info)607{608	struct mfb_info *mfbi = info->par;609	struct fsl_diu_data *data = mfbi->parent;610	struct diu __iomem *hw = data->diu_reg;611 612	out_be32(&hw->diu_mode, MFB_MODE1);613}614 615static void disable_lcdc(struct fb_info *info)616{617	struct mfb_info *mfbi = info->par;618	struct fsl_diu_data *data = mfbi->parent;619	struct diu __iomem *hw = data->diu_reg;620 621	out_be32(&hw->diu_mode, 0);622}623 624static void adjust_aoi_size_position(struct fb_var_screeninfo *var,625				struct fb_info *info)626{627	struct mfb_info *lower_aoi_mfbi, *upper_aoi_mfbi, *mfbi = info->par;628	struct fsl_diu_data *data = mfbi->parent;629	int available_height, upper_aoi_bottom;630	enum mfb_index index = mfbi->index;631	int lower_aoi_is_open, upper_aoi_is_open;632	__u32 base_plane_width, base_plane_height, upper_aoi_height;633 634	base_plane_width = data->fsl_diu_info[0].var.xres;635	base_plane_height = data->fsl_diu_info[0].var.yres;636 637	if (mfbi->x_aoi_d < 0)638		mfbi->x_aoi_d = 0;639	if (mfbi->y_aoi_d < 0)640		mfbi->y_aoi_d = 0;641	switch (index) {642	case PLANE0:643		if (mfbi->x_aoi_d != 0)644			mfbi->x_aoi_d = 0;645		if (mfbi->y_aoi_d != 0)646			mfbi->y_aoi_d = 0;647		break;648	case PLANE1_AOI0:649	case PLANE2_AOI0:650		lower_aoi_mfbi = data->fsl_diu_info[index+1].par;651		lower_aoi_is_open = lower_aoi_mfbi->count > 0 ? 1 : 0;652		if (var->xres > base_plane_width)653			var->xres = base_plane_width;654		if ((mfbi->x_aoi_d + var->xres) > base_plane_width)655			mfbi->x_aoi_d = base_plane_width - var->xres;656 657		if (lower_aoi_is_open)658			available_height = lower_aoi_mfbi->y_aoi_d;659		else660			available_height = base_plane_height;661		if (var->yres > available_height)662			var->yres = available_height;663		if ((mfbi->y_aoi_d + var->yres) > available_height)664			mfbi->y_aoi_d = available_height - var->yres;665		break;666	case PLANE1_AOI1:667	case PLANE2_AOI1:668		upper_aoi_mfbi = data->fsl_diu_info[index-1].par;669		upper_aoi_height = data->fsl_diu_info[index-1].var.yres;670		upper_aoi_bottom = upper_aoi_mfbi->y_aoi_d + upper_aoi_height;671		upper_aoi_is_open = upper_aoi_mfbi->count > 0 ? 1 : 0;672		if (var->xres > base_plane_width)673			var->xres = base_plane_width;674		if ((mfbi->x_aoi_d + var->xres) > base_plane_width)675			mfbi->x_aoi_d = base_plane_width - var->xres;676		if (mfbi->y_aoi_d < 0)677			mfbi->y_aoi_d = 0;678		if (upper_aoi_is_open) {679			if (mfbi->y_aoi_d < upper_aoi_bottom)680				mfbi->y_aoi_d = upper_aoi_bottom;681			available_height = base_plane_height682						- upper_aoi_bottom;683		} else684			available_height = base_plane_height;685		if (var->yres > available_height)686			var->yres = available_height;687		if ((mfbi->y_aoi_d + var->yres) > base_plane_height)688			mfbi->y_aoi_d = base_plane_height - var->yres;689		break;690	}691}692/*693 * Checks to see if the hardware supports the state requested by var passed694 * in. This function does not alter the hardware state! If the var passed in695 * is slightly off by what the hardware can support then we alter the var696 * PASSED in to what we can do. If the hardware doesn't support mode change697 * a -EINVAL will be returned by the upper layers.698 */699static int fsl_diu_check_var(struct fb_var_screeninfo *var,700				struct fb_info *info)701{702	if (var->xres_virtual < var->xres)703		var->xres_virtual = var->xres;704	if (var->yres_virtual < var->yres)705		var->yres_virtual = var->yres;706 707	if (var->xoffset + info->var.xres > info->var.xres_virtual)708		var->xoffset = info->var.xres_virtual - info->var.xres;709 710	if (var->yoffset + info->var.yres > info->var.yres_virtual)711		var->yoffset = info->var.yres_virtual - info->var.yres;712 713	if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) &&714	    (var->bits_per_pixel != 16))715		var->bits_per_pixel = default_bpp;716 717	switch (var->bits_per_pixel) {718	case 16:719		var->red.length = 5;720		var->red.offset = 11;721		var->red.msb_right = 0;722 723		var->green.length = 6;724		var->green.offset = 5;725		var->green.msb_right = 0;726 727		var->blue.length = 5;728		var->blue.offset = 0;729		var->blue.msb_right = 0;730 731		var->transp.length = 0;732		var->transp.offset = 0;733		var->transp.msb_right = 0;734		break;735	case 24:736		var->red.length = 8;737		var->red.offset = 0;738		var->red.msb_right = 0;739 740		var->green.length = 8;741		var->green.offset = 8;742		var->green.msb_right = 0;743 744		var->blue.length = 8;745		var->blue.offset = 16;746		var->blue.msb_right = 0;747 748		var->transp.length = 0;749		var->transp.offset = 0;750		var->transp.msb_right = 0;751		break;752	case 32:753		var->red.length = 8;754		var->red.offset = 16;755		var->red.msb_right = 0;756 757		var->green.length = 8;758		var->green.offset = 8;759		var->green.msb_right = 0;760 761		var->blue.length = 8;762		var->blue.offset = 0;763		var->blue.msb_right = 0;764 765		var->transp.length = 8;766		var->transp.offset = 24;767		var->transp.msb_right = 0;768 769		break;770	}771 772	var->height = -1;773	var->width = -1;774	var->grayscale = 0;775 776	/* Copy nonstd field to/from sync for fbset usage */777	var->sync |= var->nonstd;778	var->nonstd |= var->sync;779 780	adjust_aoi_size_position(var, info);781	return 0;782}783 784static void set_fix(struct fb_info *info)785{786	struct fb_fix_screeninfo *fix = &info->fix;787	struct fb_var_screeninfo *var = &info->var;788	struct mfb_info *mfbi = info->par;789 790	strscpy_pad(fix->id, mfbi->id);791	fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;792	fix->type = FB_TYPE_PACKED_PIXELS;793	fix->accel = FB_ACCEL_NONE;794	fix->visual = FB_VISUAL_TRUECOLOR;795	fix->xpanstep = 1;796	fix->ypanstep = 1;797}798 799static void update_lcdc(struct fb_info *info)800{801	struct fb_var_screeninfo *var = &info->var;802	struct mfb_info *mfbi = info->par;803	struct fsl_diu_data *data = mfbi->parent;804	struct diu __iomem *hw;805	int i, j;806	u8 *gamma_table_base;807 808	u32 temp;809 810	hw = data->diu_reg;811 812	if (diu_ops.set_monitor_port)813		diu_ops.set_monitor_port(data->monitor_port);814	gamma_table_base = data->gamma;815 816	/* Prep for DIU init  - gamma table, cursor table */817 818	for (i = 0; i <= 2; i++)819		for (j = 0; j <= 255; j++)820			*gamma_table_base++ = j;821 822	if (diu_ops.set_gamma_table)823		diu_ops.set_gamma_table(data->monitor_port, data->gamma);824 825	disable_lcdc(info);826 827	/* Program DIU registers */828 829	out_be32(&hw->gamma, DMA_ADDR(data, gamma));830 831	out_be32(&hw->bgnd, 0x007F7F7F); /* Set background to grey */832	out_be32(&hw->disp_size, (var->yres << 16) | var->xres);833 834	/* Horizontal and vertical configuration register */835	temp = var->left_margin << 22 | /* BP_H */836	       var->hsync_len << 11 |   /* PW_H */837	       var->right_margin;       /* FP_H */838 839	out_be32(&hw->hsyn_para, temp);840 841	temp = var->upper_margin << 22 | /* BP_V */842	       var->vsync_len << 11 |    /* PW_V  */843	       var->lower_margin;        /* FP_V  */844 845	out_be32(&hw->vsyn_para, temp);846 847	diu_ops.set_pixel_clock(var->pixclock);848 849#ifndef CONFIG_PPC_MPC512x850	/*851	 * The PLUT register is defined differently on the MPC5121 than it852	 * is on other SOCs.  Unfortunately, there's no documentation that853	 * explains how it's supposed to be programmed, so for now, we leave854	 * it at the default value on the MPC5121.855	 *856	 * For other SOCs, program it for the highest priority, which will857	 * reduce the chance of underrun. Technically, we should scale the858	 * priority to match the screen resolution, but doing that properly859	 * requires delicate fine-tuning for each use-case.860	 */861	out_be32(&hw->plut, 0x01F5F666);862#endif863 864	/* Enable the DIU */865	enable_lcdc(info);866}867 868static int map_video_memory(struct fb_info *info)869{870	u32 smem_len = info->fix.line_length * info->var.yres_virtual;871	void *p;872 873	p = alloc_pages_exact(smem_len, GFP_DMA | __GFP_ZERO);874	if (!p) {875		fb_err(info, "unable to allocate fb memory\n");876		return -ENOMEM;877	}878	mutex_lock(&info->mm_lock);879	info->screen_base = p;880	info->fix.smem_start = virt_to_phys((__force const void *)info->screen_base);881	info->fix.smem_len = smem_len;882	mutex_unlock(&info->mm_lock);883	info->screen_size = info->fix.smem_len;884 885	return 0;886}887 888static void unmap_video_memory(struct fb_info *info)889{890	void *p = info->screen_base;891	size_t l = info->fix.smem_len;892 893	mutex_lock(&info->mm_lock);894	info->screen_base = NULL;895	info->fix.smem_start = 0;896	info->fix.smem_len = 0;897	mutex_unlock(&info->mm_lock);898 899	if (p)900		free_pages_exact(p, l);901}902 903/*904 * Using the fb_var_screeninfo in fb_info we set the aoi of this905 * particular framebuffer. It is a light version of fsl_diu_set_par.906 */907static int fsl_diu_set_aoi(struct fb_info *info)908{909	struct fb_var_screeninfo *var = &info->var;910	struct mfb_info *mfbi = info->par;911	struct diu_ad *ad = mfbi->ad;912 913	/* AOI should not be greater than display size */914	ad->offset_xyi = cpu_to_le32((var->yoffset << 16) | var->xoffset);915	ad->offset_xyd = cpu_to_le32((mfbi->y_aoi_d << 16) | mfbi->x_aoi_d);916	return 0;917}918 919/**920 * fsl_diu_get_pixel_format: return the pixel format for a given color depth921 *922 * The pixel format is a 32-bit value that determine which bits in each923 * pixel are to be used for each color.  This is the default function used924 * if the platform does not define its own version.925 */926static u32 fsl_diu_get_pixel_format(unsigned int bits_per_pixel)927{928#define PF_BYTE_F		0x10000000929#define PF_ALPHA_C_MASK		0x0E000000930#define PF_ALPHA_C_SHIFT	25931#define PF_BLUE_C_MASK		0x01800000932#define PF_BLUE_C_SHIFT		23933#define PF_GREEN_C_MASK		0x00600000934#define PF_GREEN_C_SHIFT	21935#define PF_RED_C_MASK		0x00180000936#define PF_RED_C_SHIFT		19937#define PF_PALETTE		0x00040000938#define PF_PIXEL_S_MASK		0x00030000939#define PF_PIXEL_S_SHIFT	16940#define PF_COMP_3_MASK		0x0000F000941#define PF_COMP_3_SHIFT		12942#define PF_COMP_2_MASK		0x00000F00943#define PF_COMP_2_SHIFT		8944#define PF_COMP_1_MASK		0x000000F0945#define PF_COMP_1_SHIFT		4946#define PF_COMP_0_MASK		0x0000000F947#define PF_COMP_0_SHIFT		0948 949#define MAKE_PF(alpha, red, green, blue, size, c0, c1, c2, c3) \950	cpu_to_le32(PF_BYTE_F | (alpha << PF_ALPHA_C_SHIFT) | \951	(blue << PF_BLUE_C_SHIFT) | (green << PF_GREEN_C_SHIFT) | \952	(red << PF_RED_C_SHIFT) | (c3 << PF_COMP_3_SHIFT) | \953	(c2 << PF_COMP_2_SHIFT) | (c1 << PF_COMP_1_SHIFT) | \954	(c0 << PF_COMP_0_SHIFT) | (size << PF_PIXEL_S_SHIFT))955 956	switch (bits_per_pixel) {957	case 32:958		/* 0x88883316 */959		return MAKE_PF(3, 2, 1, 0, 3, 8, 8, 8, 8);960	case 24:961		/* 0x88082219 */962		return MAKE_PF(4, 0, 1, 2, 2, 8, 8, 8, 0);963	case 16:964		/* 0x65053118 */965		return MAKE_PF(4, 2, 1, 0, 1, 5, 6, 5, 0);966	default:967		pr_err("fsl-diu: unsupported color depth %u\n", bits_per_pixel);968		return 0;969	}970}971 972/*973 * Copies a cursor image from user space to the proper place in driver974 * memory so that the hardware can display the cursor image.975 *976 * Cursor data is represented as a sequence of 'width' bits packed into bytes.977 * That is, the first 8 bits are in the first byte, the second 8 bits in the978 * second byte, and so on.  Therefore, the each row of the cursor is (width +979 * 7) / 8 bytes of 'data'980 *981 * The DIU only supports cursors up to 32x32 (MAX_CURS).  We reject cursors982 * larger than this, so we already know that 'width' <= 32.  Therefore, we can983 * simplify our code by using a 32-bit big-endian integer ("line") to read in984 * a single line of pixels, and only look at the top 'width' bits of that985 * integer.986 *987 * This could result in an unaligned 32-bit read.  For example, if the cursor988 * is 24x24, then the first three bytes of 'image' contain the pixel data for989 * the top line of the cursor.  We do a 32-bit read of 'image', but we look990 * only at the top 24 bits.  Then we increment 'image' by 3 bytes.  The next991 * read is unaligned.  The only problem is that we might read past the end of992 * 'image' by 1-3 bytes, but that should not cause any problems.993 */994static void fsl_diu_load_cursor_image(struct fb_info *info,995	const void *image, uint16_t bg, uint16_t fg,996	unsigned int width, unsigned int height)997{998	struct mfb_info *mfbi = info->par;999	struct fsl_diu_data *data = mfbi->parent;1000	__le16 *cursor = data->cursor;1001	__le16 _fg = cpu_to_le16(fg);1002	__le16 _bg = cpu_to_le16(bg);1003	unsigned int h, w;1004 1005	for (h = 0; h < height; h++) {1006		uint32_t mask = 1 << 31;1007		uint32_t line = be32_to_cpup(image);1008 1009		for (w = 0; w < width; w++) {1010			cursor[w] = (line & mask) ? _fg : _bg;1011			mask >>= 1;1012		}1013 1014		cursor += MAX_CURS;1015		image += DIV_ROUND_UP(width, 8);1016	}1017}1018 1019/*1020 * Set a hardware cursor.  The image data for the cursor is passed via the1021 * fb_cursor object.1022 */1023static int fsl_diu_cursor(struct fb_info *info, struct fb_cursor *cursor)1024{1025	struct mfb_info *mfbi = info->par;1026	struct fsl_diu_data *data = mfbi->parent;1027	struct diu __iomem *hw = data->diu_reg;1028 1029	if (cursor->image.width > MAX_CURS || cursor->image.height > MAX_CURS)1030		return -EINVAL;1031 1032	/* The cursor size has changed */1033	if (cursor->set & FB_CUR_SETSIZE) {1034		/*1035		 * The DIU cursor is a fixed size, so when we get this1036		 * message, instead of resizing the cursor, we just clear1037		 * all the image data, in expectation of new data.  However,1038		 * in tests this control does not appear to be normally1039		 * called.1040		 */1041		memset(data->cursor, 0, sizeof(data->cursor));1042	}1043 1044	/* The cursor position has changed (cursor->image.dx|dy) */1045	if (cursor->set & FB_CUR_SETPOS) {1046		uint32_t xx, yy;1047 1048		yy = (cursor->image.dy - info->var.yoffset) & 0x7ff;1049		xx = (cursor->image.dx - info->var.xoffset) & 0x7ff;1050 1051		out_be32(&hw->curs_pos, yy << 16 | xx);1052	}1053 1054	/*1055	 * FB_CUR_SETIMAGE - the cursor image has changed1056	 * FB_CUR_SETCMAP  - the cursor colors has changed1057	 * FB_CUR_SETSHAPE - the cursor bitmask has changed1058	 */1059	if (cursor->set & (FB_CUR_SETSHAPE | FB_CUR_SETCMAP | FB_CUR_SETIMAGE)) {1060		/*1061		 * Determine the size of the cursor image data.  Normally,1062		 * it's 8x16.1063		 */1064		unsigned int image_size =1065			DIV_ROUND_UP(cursor->image.width, 8) *1066			cursor->image.height;1067		unsigned int image_words =1068			DIV_ROUND_UP(image_size, sizeof(uint32_t));1069		unsigned int bg_idx = cursor->image.bg_color;1070		unsigned int fg_idx = cursor->image.fg_color;1071		uint32_t *image, *source, *mask;1072		uint16_t fg, bg;1073		unsigned int i;1074 1075		if (info->state != FBINFO_STATE_RUNNING)1076			return 0;1077 1078		bg = ((info->cmap.red[bg_idx] & 0xf8) << 7) |1079		     ((info->cmap.green[bg_idx] & 0xf8) << 2) |1080		     ((info->cmap.blue[bg_idx] & 0xf8) >> 3) |1081		     1 << 15;1082 1083		fg = ((info->cmap.red[fg_idx] & 0xf8) << 7) |1084		     ((info->cmap.green[fg_idx] & 0xf8) << 2) |1085		     ((info->cmap.blue[fg_idx] & 0xf8) >> 3) |1086		     1 << 15;1087 1088		/* Use 32-bit operations on the data to improve performance */1089		image = (uint32_t *)data->next_cursor;1090		source = (uint32_t *)cursor->image.data;1091		mask = (uint32_t *)cursor->mask;1092 1093		if (cursor->rop == ROP_XOR)1094			for (i = 0; i < image_words; i++)1095				image[i] = source[i] ^ mask[i];1096		else1097			for (i = 0; i < image_words; i++)1098				image[i] = source[i] & mask[i];1099 1100		fsl_diu_load_cursor_image(info, image, bg, fg,1101			cursor->image.width, cursor->image.height);1102	}1103 1104	/*1105	 * Show or hide the cursor.  The cursor data is always stored in the1106	 * 'cursor' memory block, and the actual cursor position is always in1107	 * the DIU's CURS_POS register.  To hide the cursor, we redirect the1108	 * CURSOR register to a blank cursor.  The show the cursor, we1109	 * redirect the CURSOR register to the real cursor data.1110	 */1111	if (cursor->enable)1112		out_be32(&hw->cursor, DMA_ADDR(data, cursor));1113	else1114		out_be32(&hw->cursor, DMA_ADDR(data, blank_cursor));1115 1116	return 0;1117}1118 1119/*1120 * Using the fb_var_screeninfo in fb_info we set the resolution of this1121 * particular framebuffer. This function alters the fb_fix_screeninfo stored1122 * in fb_info. It does not alter var in fb_info since we are using that1123 * data. This means we depend on the data in var inside fb_info to be1124 * supported by the hardware. fsl_diu_check_var is always called before1125 * fsl_diu_set_par to ensure this.1126 */1127static int fsl_diu_set_par(struct fb_info *info)1128{1129	unsigned long len;1130	struct fb_var_screeninfo *var = &info->var;1131	struct mfb_info *mfbi = info->par;1132	struct fsl_diu_data *data = mfbi->parent;1133	struct diu_ad *ad = mfbi->ad;1134	struct diu __iomem *hw;1135 1136	hw = data->diu_reg;1137 1138	set_fix(info);1139 1140	len = info->var.yres_virtual * info->fix.line_length;1141	/* Alloc & dealloc each time resolution/bpp change */1142	if (len != info->fix.smem_len) {1143		if (info->fix.smem_start)1144			unmap_video_memory(info);1145 1146		/* Memory allocation for framebuffer */1147		if (map_video_memory(info)) {1148			fb_err(info, "unable to allocate fb memory 1\n");1149			return -ENOMEM;1150		}1151	}1152 1153	if (diu_ops.get_pixel_format)1154		ad->pix_fmt = diu_ops.get_pixel_format(data->monitor_port,1155						       var->bits_per_pixel);1156	else1157		ad->pix_fmt = fsl_diu_get_pixel_format(var->bits_per_pixel);1158 1159	ad->addr    = cpu_to_le32(info->fix.smem_start);1160	ad->src_size_g_alpha = cpu_to_le32((var->yres_virtual << 12) |1161				var->xres_virtual) | mfbi->g_alpha;1162	/* AOI should not be greater than display size */1163	ad->aoi_size 	= cpu_to_le32((var->yres << 16) | var->xres);1164	ad->offset_xyi = cpu_to_le32((var->yoffset << 16) | var->xoffset);1165	ad->offset_xyd = cpu_to_le32((mfbi->y_aoi_d << 16) | mfbi->x_aoi_d);1166 1167	/* Disable chroma keying function */1168	ad->ckmax_r = 0;1169	ad->ckmax_g = 0;1170	ad->ckmax_b = 0;1171 1172	ad->ckmin_r = 255;1173	ad->ckmin_g = 255;1174	ad->ckmin_b = 255;1175 1176	if (mfbi->index == PLANE0)1177		update_lcdc(info);1178	return 0;1179}1180 1181static inline __u32 CNVT_TOHW(__u32 val, __u32 width)1182{1183	return ((val << width) + 0x7FFF - val) >> 16;1184}1185 1186/*1187 * Set a single color register. The values supplied have a 16 bit magnitude1188 * which needs to be scaled in this function for the hardware. Things to take1189 * into consideration are how many color registers, if any, are supported with1190 * the current color visual. With truecolor mode no color palettes are1191 * supported. Here a pseudo palette is created which we store the value in1192 * pseudo_palette in struct fb_info. For pseudocolor mode we have a limited1193 * color palette.1194 */1195static int fsl_diu_setcolreg(unsigned int regno, unsigned int red,1196			     unsigned int green, unsigned int blue,1197			     unsigned int transp, struct fb_info *info)1198{1199	int ret = 1;1200 1201	/*1202	 * If greyscale is true, then we convert the RGB value1203	 * to greyscale no matter what visual we are using.1204	 */1205	if (info->var.grayscale)1206		red = green = blue = (19595 * red + 38470 * green +1207				      7471 * blue) >> 16;1208	switch (info->fix.visual) {1209	case FB_VISUAL_TRUECOLOR:1210		/*1211		 * 16-bit True Colour.  We encode the RGB value1212		 * according to the RGB bitfield information.1213		 */1214		if (regno < 16) {1215			u32 *pal = info->pseudo_palette;1216			u32 v;1217 1218			red = CNVT_TOHW(red, info->var.red.length);1219			green = CNVT_TOHW(green, info->var.green.length);1220			blue = CNVT_TOHW(blue, info->var.blue.length);1221			transp = CNVT_TOHW(transp, info->var.transp.length);1222 1223			v = (red << info->var.red.offset) |1224			    (green << info->var.green.offset) |1225			    (blue << info->var.blue.offset) |1226			    (transp << info->var.transp.offset);1227 1228			pal[regno] = v;1229			ret = 0;1230		}1231		break;1232	}1233 1234	return ret;1235}1236 1237/*1238 * Pan (or wrap, depending on the `vmode' field) the display using the1239 * 'xoffset' and 'yoffset' fields of the 'var' structure. If the values1240 * don't fit, return -EINVAL.1241 */1242static int fsl_diu_pan_display(struct fb_var_screeninfo *var,1243			     struct fb_info *info)1244{1245	if ((info->var.xoffset == var->xoffset) &&1246	    (info->var.yoffset == var->yoffset))1247		return 0;	/* No change, do nothing */1248 1249	if (var->xoffset + info->var.xres > info->var.xres_virtual1250	    || var->yoffset + info->var.yres > info->var.yres_virtual)1251		return -EINVAL;1252 1253	info->var.xoffset = var->xoffset;1254	info->var.yoffset = var->yoffset;1255 1256	if (var->vmode & FB_VMODE_YWRAP)1257		info->var.vmode |= FB_VMODE_YWRAP;1258	else1259		info->var.vmode &= ~FB_VMODE_YWRAP;1260 1261	fsl_diu_set_aoi(info);1262 1263	return 0;1264}1265 1266static int fsl_diu_ioctl(struct fb_info *info, unsigned int cmd,1267		       unsigned long arg)1268{1269	struct mfb_info *mfbi = info->par;1270	struct diu_ad *ad = mfbi->ad;1271	struct mfb_chroma_key ck;1272	unsigned char global_alpha;1273	struct aoi_display_offset aoi_d;1274	__u32 pix_fmt;1275	void __user *buf = (void __user *)arg;1276 1277	if (!arg)1278		return -EINVAL;1279 1280	fb_dbg(info, "ioctl %08x (dir=%s%s type=%u nr=%u size=%u)\n", cmd,1281		_IOC_DIR(cmd) & _IOC_READ ? "R" : "",1282		_IOC_DIR(cmd) & _IOC_WRITE ? "W" : "",1283		_IOC_TYPE(cmd), _IOC_NR(cmd), _IOC_SIZE(cmd));1284 1285	switch (cmd) {1286	case MFB_SET_PIXFMT_OLD:1287		fb_warn(info,1288			"MFB_SET_PIXFMT value of 0x%08x is deprecated.\n",1289			MFB_SET_PIXFMT_OLD);1290		fallthrough;1291	case MFB_SET_PIXFMT:1292		if (copy_from_user(&pix_fmt, buf, sizeof(pix_fmt)))1293			return -EFAULT;1294		ad->pix_fmt = pix_fmt;1295		break;1296	case MFB_GET_PIXFMT_OLD:1297		fb_warn(info,1298			"MFB_GET_PIXFMT value of 0x%08x is deprecated.\n",1299			MFB_GET_PIXFMT_OLD);1300		fallthrough;1301	case MFB_GET_PIXFMT:1302		pix_fmt = ad->pix_fmt;1303		if (copy_to_user(buf, &pix_fmt, sizeof(pix_fmt)))1304			return -EFAULT;1305		break;1306	case MFB_SET_AOID:1307		if (copy_from_user(&aoi_d, buf, sizeof(aoi_d)))1308			return -EFAULT;1309		mfbi->x_aoi_d = aoi_d.x_aoi_d;1310		mfbi->y_aoi_d = aoi_d.y_aoi_d;1311		fsl_diu_check_var(&info->var, info);1312		fsl_diu_set_aoi(info);1313		break;1314	case MFB_GET_AOID:1315		aoi_d.x_aoi_d = mfbi->x_aoi_d;1316		aoi_d.y_aoi_d = mfbi->y_aoi_d;1317		if (copy_to_user(buf, &aoi_d, sizeof(aoi_d)))1318			return -EFAULT;1319		break;1320	case MFB_GET_ALPHA:1321		global_alpha = mfbi->g_alpha;1322		if (copy_to_user(buf, &global_alpha, sizeof(global_alpha)))1323			return -EFAULT;1324		break;1325	case MFB_SET_ALPHA:1326		/* set panel information */1327		if (copy_from_user(&global_alpha, buf, sizeof(global_alpha)))1328			return -EFAULT;1329		ad->src_size_g_alpha = (ad->src_size_g_alpha & (~0xff)) |1330							(global_alpha & 0xff);1331		mfbi->g_alpha = global_alpha;1332		break;1333	case MFB_SET_CHROMA_KEY:1334		/* set panel winformation */1335		if (copy_from_user(&ck, buf, sizeof(ck)))1336			return -EFAULT;1337 1338		if (ck.enable &&1339		   (ck.red_max < ck.red_min ||1340		    ck.green_max < ck.green_min ||1341		    ck.blue_max < ck.blue_min))1342			return -EINVAL;1343 1344		if (!ck.enable) {1345			ad->ckmax_r = 0;1346			ad->ckmax_g = 0;1347			ad->ckmax_b = 0;1348			ad->ckmin_r = 255;1349			ad->ckmin_g = 255;1350			ad->ckmin_b = 255;1351		} else {1352			ad->ckmax_r = ck.red_max;1353			ad->ckmax_g = ck.green_max;1354			ad->ckmax_b = ck.blue_max;1355			ad->ckmin_r = ck.red_min;1356			ad->ckmin_g = ck.green_min;1357			ad->ckmin_b = ck.blue_min;1358		}1359		break;1360#ifdef CONFIG_PPC_MPC512x1361	case MFB_SET_GAMMA: {1362		struct fsl_diu_data *data = mfbi->parent;1363 1364		if (copy_from_user(data->gamma, buf, sizeof(data->gamma)))1365			return -EFAULT;1366		setbits32(&data->diu_reg->gamma, 0); /* Force table reload */1367		break;1368	}1369	case MFB_GET_GAMMA: {1370		struct fsl_diu_data *data = mfbi->parent;1371 1372		if (copy_to_user(buf, data->gamma, sizeof(data->gamma)))1373			return -EFAULT;1374		break;1375	}1376#endif1377	default:1378		fb_err(info, "unknown ioctl command (0x%08X)\n", cmd);1379		return -ENOIOCTLCMD;1380	}1381 1382	return 0;1383}1384 1385static inline void fsl_diu_enable_interrupts(struct fsl_diu_data *data)1386{1387	u32 int_mask = INT_UNDRUN; /* enable underrun detection */1388 1389	if (IS_ENABLED(CONFIG_NOT_COHERENT_CACHE))1390		int_mask |= INT_VSYNC; /* enable vertical sync */1391 1392	clrbits32(&data->diu_reg->int_mask, int_mask);1393}1394 1395/* turn on fb if count == 11396 */1397static int fsl_diu_open(struct fb_info *info, int user)1398{1399	struct mfb_info *mfbi = info->par;1400	int res = 0;1401 1402	/* free boot splash memory on first /dev/fb0 open */1403	if ((mfbi->index == PLANE0) && diu_ops.release_bootmem)1404		diu_ops.release_bootmem();1405 1406	spin_lock(&diu_lock);1407	mfbi->count++;1408	if (mfbi->count == 1) {1409		fsl_diu_check_var(&info->var, info);1410		res = fsl_diu_set_par(info);1411		if (res < 0)1412			mfbi->count--;1413		else {1414			fsl_diu_enable_interrupts(mfbi->parent);1415			fsl_diu_enable_panel(info);1416		}1417	}1418 1419	spin_unlock(&diu_lock);1420	return res;1421}1422 1423/* turn off fb if count == 01424 */1425static int fsl_diu_release(struct fb_info *info, int user)1426{1427	struct mfb_info *mfbi = info->par;1428 1429	spin_lock(&diu_lock);1430	mfbi->count--;1431	if (mfbi->count == 0) {1432		struct fsl_diu_data *data = mfbi->parent;1433		bool disable = true;1434		int i;1435 1436		/* Disable interrupts only if all AOIs are closed */1437		for (i = 0; i < NUM_AOIS; i++) {1438			struct mfb_info *mi = data->fsl_diu_info[i].par;1439 1440			if (mi->count)1441				disable = false;1442		}1443		if (disable)1444			out_be32(&data->diu_reg->int_mask, 0xffffffff);1445		fsl_diu_disable_panel(info);1446	}1447 1448	spin_unlock(&diu_lock);1449	return 0;1450}1451 1452static const struct fb_ops fsl_diu_ops = {1453	.owner = THIS_MODULE,1454	FB_DEFAULT_IOMEM_OPS,1455	.fb_check_var = fsl_diu_check_var,1456	.fb_set_par = fsl_diu_set_par,1457	.fb_setcolreg = fsl_diu_setcolreg,1458	.fb_pan_display = fsl_diu_pan_display,1459	.fb_ioctl = fsl_diu_ioctl,1460	.fb_open = fsl_diu_open,1461	.fb_release = fsl_diu_release,1462	.fb_cursor = fsl_diu_cursor,1463};1464 1465static int install_fb(struct fb_info *info)1466{1467	int rc;1468	struct mfb_info *mfbi = info->par;1469	struct fsl_diu_data *data = mfbi->parent;1470	const char *aoi_mode, *init_aoi_mode = "320x240";1471	struct fb_videomode *db = fsl_diu_mode_db;1472	unsigned int dbsize = ARRAY_SIZE(fsl_diu_mode_db);1473	int has_default_mode = 1;1474 1475	info->var.activate = FB_ACTIVATE_NOW;1476	info->fbops = &fsl_diu_ops;1477	info->flags = FBINFO_VIRTFB | FBINFO_PARTIAL_PAN_OK |1478		FBINFO_READS_FAST;1479	info->pseudo_palette = mfbi->pseudo_palette;1480 1481	rc = fb_alloc_cmap(&info->cmap, 16, 0);1482	if (rc)1483		return rc;1484 1485	if (mfbi->index == PLANE0) {1486		if (data->has_edid) {1487			/* Now build modedb from EDID */1488			fb_edid_to_monspecs(data->edid_data, &info->monspecs);1489			fb_videomode_to_modelist(info->monspecs.modedb,1490						 info->monspecs.modedb_len,1491						 &info->modelist);1492			db = info->monspecs.modedb;1493			dbsize = info->monspecs.modedb_len;1494		}1495		aoi_mode = fb_mode;1496	} else {1497		aoi_mode = init_aoi_mode;1498	}1499	rc = fb_find_mode(&info->var, info, aoi_mode, db, dbsize, NULL,1500			  default_bpp);1501	if (!rc) {1502		/*1503		 * For plane 0 we continue and look into1504		 * driver's internal modedb.1505		 */1506		if ((mfbi->index == PLANE0) && data->has_edid)1507			has_default_mode = 0;1508		else1509			return -EINVAL;1510	}1511 1512	if (!has_default_mode) {1513		rc = fb_find_mode(&info->var, info, aoi_mode, fsl_diu_mode_db,1514			ARRAY_SIZE(fsl_diu_mode_db), NULL, default_bpp);1515		if (rc)1516			has_default_mode = 1;1517	}1518 1519	/* Still not found, use preferred mode from database if any */1520	if (!has_default_mode && info->monspecs.modedb) {1521		struct fb_monspecs *specs = &info->monspecs;1522		struct fb_videomode *modedb = &specs->modedb[0];1523 1524		/*1525		 * Get preferred timing. If not found,1526		 * first mode in database will be used.1527		 */1528		if (specs->misc & FB_MISC_1ST_DETAIL) {1529			int i;1530 1531			for (i = 0; i < specs->modedb_len; i++) {1532				if (specs->modedb[i].flag & FB_MODE_IS_FIRST) {1533					modedb = &specs->modedb[i];1534					break;1535				}1536			}1537		}1538 1539		info->var.bits_per_pixel = default_bpp;1540		fb_videomode_to_var(&info->var, modedb);1541	}1542 1543	if (fsl_diu_check_var(&info->var, info)) {1544		fb_err(info, "fsl_diu_check_var failed\n");1545		unmap_video_memory(info);1546		fb_dealloc_cmap(&info->cmap);1547		return -EINVAL;1548	}1549 1550	if (register_framebuffer(info) < 0) {1551		fb_err(info, "register_framebuffer failed\n");1552		unmap_video_memory(info);1553		fb_dealloc_cmap(&info->cmap);1554		return -EINVAL;1555	}1556 1557	mfbi->registered = 1;1558	fb_info(info, "%s registered successfully\n", mfbi->id);1559 1560	return 0;1561}1562 1563static void uninstall_fb(struct fb_info *info)1564{1565	struct mfb_info *mfbi = info->par;1566 1567	if (!mfbi->registered)1568		return;1569 1570	unregister_framebuffer(info);1571	unmap_video_memory(info);1572	fb_dealloc_cmap(&info->cmap);1573 1574	mfbi->registered = 0;1575}1576 1577static irqreturn_t fsl_diu_isr(int irq, void *dev_id)1578{1579	struct diu __iomem *hw = dev_id;1580	uint32_t status = in_be32(&hw->int_status);1581 1582	if (status) {1583		/* This is the workaround for underrun */1584		if (status & INT_UNDRUN) {1585			out_be32(&hw->diu_mode, 0);1586			udelay(1);1587			out_be32(&hw->diu_mode, 1);1588		}1589#if defined(CONFIG_NOT_COHERENT_CACHE)1590		else if (status & INT_VSYNC) {1591			unsigned int i;1592 1593			for (i = 0; i < coherence_data_size;1594				i += d_cache_line_size)1595				__asm__ __volatile__ (1596					"dcbz 0, %[input]"1597				::[input]"r"(&coherence_data[i]));1598		}1599#endif1600		return IRQ_HANDLED;1601	}1602	return IRQ_NONE;1603}1604 1605#ifdef CONFIG_PM1606/*1607 * Power management hooks. Note that we won't be called from IRQ context,1608 * unlike the blank functions above, so we may sleep.1609 */1610static int fsl_diu_suspend(struct platform_device *ofdev, pm_message_t state)1611{1612	struct fsl_diu_data *data;1613 1614	data = dev_get_drvdata(&ofdev->dev);1615	disable_lcdc(data->fsl_diu_info);1616 1617	return 0;1618}1619 1620static int fsl_diu_resume(struct platform_device *ofdev)1621{1622	struct fsl_diu_data *data;1623	unsigned int i;1624 1625	data = dev_get_drvdata(&ofdev->dev);1626 1627	fsl_diu_enable_interrupts(data);1628	update_lcdc(data->fsl_diu_info);1629	for (i = 0; i < NUM_AOIS; i++) {1630		if (data->mfb[i].count)1631			fsl_diu_enable_panel(&data->fsl_diu_info[i]);1632	}1633 1634	return 0;1635}1636 1637#else1638#define fsl_diu_suspend NULL1639#define fsl_diu_resume NULL1640#endif				/* CONFIG_PM */1641 1642static ssize_t store_monitor(struct device *device,1643	struct device_attribute *attr, const char *buf, size_t count)1644{1645	enum fsl_diu_monitor_port old_monitor_port;1646	struct fsl_diu_data *data =1647		container_of(attr, struct fsl_diu_data, dev_attr);1648 1649	old_monitor_port = data->monitor_port;1650	data->monitor_port = fsl_diu_name_to_port(buf);1651 1652	if (old_monitor_port != data->monitor_port) {1653		/* All AOIs need adjust pixel format1654		 * fsl_diu_set_par only change the pixsel format here1655		 * unlikely to fail. */1656		unsigned int i;1657 1658		for (i=0; i < NUM_AOIS; i++)1659			fsl_diu_set_par(&data->fsl_diu_info[i]);1660	}1661	return count;1662}1663 1664static ssize_t show_monitor(struct device *device,1665	struct device_attribute *attr, char *buf)1666{1667	struct fsl_diu_data *data =1668		container_of(attr, struct fsl_diu_data, dev_attr);1669 1670	switch (data->monitor_port) {1671	case FSL_DIU_PORT_DVI:1672		return sprintf(buf, "DVI\n");1673	case FSL_DIU_PORT_LVDS:1674		return sprintf(buf, "Single-link LVDS\n");1675	case FSL_DIU_PORT_DLVDS:1676		return sprintf(buf, "Dual-link LVDS\n");1677	}1678 1679	return 0;1680}1681 1682static int fsl_diu_probe(struct platform_device *pdev)1683{1684	struct device_node *np = pdev->dev.of_node;1685	struct mfb_info *mfbi;1686	struct fsl_diu_data *data;1687	dma_addr_t dma_addr; /* DMA addr of fsl_diu_data struct */1688	const void *prop;1689	unsigned int i;1690	int ret;1691 1692	data = dmam_alloc_coherent(&pdev->dev, sizeof(struct fsl_diu_data),1693				   &dma_addr, GFP_DMA | __GFP_ZERO);1694	if (!data)1695		return -ENOMEM;1696	data->dma_addr = dma_addr;1697 1698	/*1699	 * dma_alloc_coherent() uses a page allocator, so the address is1700	 * always page-aligned.  We need the memory to be 32-byte aligned,1701	 * so that's good.  However, if one day the allocator changes, we1702	 * need to catch that.  It's not worth the effort to handle unaligned1703	 * alloctions now because it's highly unlikely to ever be a problem.1704	 */1705	if ((unsigned long)data & 31) {1706		dev_err(&pdev->dev, "misaligned allocation");1707		ret = -ENOMEM;1708		goto error;1709	}1710 1711	spin_lock_init(&data->reg_lock);1712 1713	for (i = 0; i < NUM_AOIS; i++) {1714		struct fb_info *info = &data->fsl_diu_info[i];1715 1716		info->device = &pdev->dev;1717		info->par = &data->mfb[i];1718 1719		/*1720		 * We store the physical address of the AD in the reserved1721		 * 'paddr' field of the AD itself.1722		 */1723		data->ad[i].paddr = DMA_ADDR(data, ad[i]);1724 1725		info->fix.smem_start = 0;1726 1727		/* Initialize the AOI data structure */1728		mfbi = info->par;1729		memcpy(mfbi, &mfb_template[i], sizeof(struct mfb_info));1730		mfbi->parent = data;1731		mfbi->ad = &data->ad[i];1732	}1733 1734	/* Get the EDID data from the device tree, if present */1735	prop = of_get_property(np, "edid", &ret);1736	if (prop && ret == EDID_LENGTH) {1737		memcpy(data->edid_data, prop, EDID_LENGTH);1738		data->has_edid = true;1739	}1740 1741	data->diu_reg = of_iomap(np, 0);1742	if (!data->diu_reg) {1743		dev_err(&pdev->dev, "cannot map DIU registers\n");1744		ret = -EFAULT;1745		goto error;1746	}1747 1748	/* Get the IRQ of the DIU */1749	data->irq = irq_of_parse_and_map(np, 0);1750 1751	if (!data->irq) {1752		dev_err(&pdev->dev, "could not get DIU IRQ\n");1753		ret = -EINVAL;1754		goto error;1755	}1756	data->monitor_port = monitor_port;1757 1758	/* Initialize the dummy Area Descriptor */1759	data->dummy_ad.addr = cpu_to_le32(DMA_ADDR(data, dummy_aoi));1760	data->dummy_ad.pix_fmt = 0x88882317;1761	data->dummy_ad.src_size_g_alpha = cpu_to_le32((4 << 12) | 4);1762	data->dummy_ad.aoi_size = cpu_to_le32((4 << 16) |  2);1763	data->dummy_ad.offset_xyi = 0;1764	data->dummy_ad.offset_xyd = 0;1765	data->dummy_ad.next_ad = 0;1766	data->dummy_ad.paddr = DMA_ADDR(data, dummy_ad);1767 1768	/*1769	 * Let DIU continue to display splash screen if it was pre-initialized1770	 * by the bootloader; otherwise, clear the display.1771	 */1772	if (in_be32(&data->diu_reg->diu_mode) == MFB_MODE0)1773		out_be32(&data->diu_reg->desc[0], 0);1774 1775	out_be32(&data->diu_reg->desc[1], data->dummy_ad.paddr);1776	out_be32(&data->diu_reg->desc[2], data->dummy_ad.paddr);1777 1778	/*1779	 * Older versions of U-Boot leave interrupts enabled, so disable1780	 * all of them and clear the status register.1781	 */1782	out_be32(&data->diu_reg->int_mask, 0xffffffff);1783	in_be32(&data->diu_reg->int_status);1784 1785	ret = request_irq(data->irq, fsl_diu_isr, 0, "fsl-diu-fb",1786			  data->diu_reg);1787	if (ret) {1788		dev_err(&pdev->dev, "could not claim irq\n");1789		goto error;1790	}1791 1792	for (i = 0; i < NUM_AOIS; i++) {1793		ret = install_fb(&data->fsl_diu_info[i]);1794		if (ret) {1795			dev_err(&pdev->dev, "could not register fb %d\n", i);1796			free_irq(data->irq, data->diu_reg);1797			goto error;1798		}1799	}1800 1801	sysfs_attr_init(&data->dev_attr.attr);1802	data->dev_attr.attr.name = "monitor";1803	data->dev_attr.attr.mode = S_IRUGO|S_IWUSR;1804	data->dev_attr.show = show_monitor;1805	data->dev_attr.store = store_monitor;1806	ret = device_create_file(&pdev->dev, &data->dev_attr);1807	if (ret) {1808		dev_err(&pdev->dev, "could not create sysfs file %s\n",1809			data->dev_attr.attr.name);1810	}1811 1812	dev_set_drvdata(&pdev->dev, data);1813	return 0;1814 1815error:1816	for (i = 0; i < NUM_AOIS; i++)1817		uninstall_fb(&data->fsl_diu_info[i]);1818 1819	iounmap(data->diu_reg);1820 1821	return ret;1822}1823 1824static void fsl_diu_remove(struct platform_device *pdev)1825{1826	struct fsl_diu_data *data;1827	int i;1828 1829	data = dev_get_drvdata(&pdev->dev);1830	disable_lcdc(&data->fsl_diu_info[0]);1831 1832	free_irq(data->irq, data->diu_reg);1833 1834	for (i = 0; i < NUM_AOIS; i++)1835		uninstall_fb(&data->fsl_diu_info[i]);1836 1837	iounmap(data->diu_reg);1838}1839 1840#ifndef MODULE1841static int __init fsl_diu_setup(char *options)1842{1843	char *opt;1844	unsigned long val;1845 1846	if (!options || !*options)1847		return 0;1848 1849	while ((opt = strsep(&options, ",")) != NULL) {1850		if (!*opt)1851			continue;1852		if (!strncmp(opt, "monitor=", 8)) {1853			monitor_port = fsl_diu_name_to_port(opt + 8);1854		} else if (!strncmp(opt, "bpp=", 4)) {1855			if (!kstrtoul(opt + 4, 10, &val))1856				default_bpp = val;1857		} else1858			fb_mode = opt;1859	}1860 1861	return 0;1862}1863#endif1864 1865static const struct of_device_id fsl_diu_match[] = {1866#ifdef CONFIG_PPC_MPC512x1867	{1868		.compatible = "fsl,mpc5121-diu",1869	},1870#endif1871	{1872		.compatible = "fsl,diu",1873	},1874	{}1875};1876MODULE_DEVICE_TABLE(of, fsl_diu_match);1877 1878static struct platform_driver fsl_diu_driver = {1879	.driver		= {1880		.name = "fsl-diu-fb",1881		.of_match_table = fsl_diu_match,1882	},1883	.probe		= fsl_diu_probe,1884	.remove		= fsl_diu_remove,1885	.suspend	= fsl_diu_suspend,1886	.resume		= fsl_diu_resume,1887};1888 1889static int __init fsl_diu_init(void)1890{1891#ifdef CONFIG_NOT_COHERENT_CACHE1892	struct device_node *np;1893	const u32 *prop;1894#endif1895	int ret;1896#ifndef MODULE1897	char *option;1898 1899	/*1900	 * For kernel boot options (in 'video=xxxfb:<options>' format)1901	 */1902	if (fb_get_options("fslfb", &option))1903		return -ENODEV;1904	fsl_diu_setup(option);1905#else1906	monitor_port = fsl_diu_name_to_port(monitor_string);1907#endif1908 1909	/*1910	 * Must to verify set_pixel_clock. If not implement on platform,1911	 * then that means that there is no platform support for the DIU.1912	 */1913	if (!diu_ops.set_pixel_clock)1914		return -ENODEV;1915 1916	pr_info("Freescale Display Interface Unit (DIU) framebuffer driver\n");1917 1918#ifdef CONFIG_NOT_COHERENT_CACHE1919	np = of_get_cpu_node(0, NULL);1920	if (!np) {1921		pr_err("fsl-diu-fb: can't find 'cpu' device node\n");1922		return -ENODEV;1923	}1924 1925	prop = of_get_property(np, "d-cache-size", NULL);1926	if (prop == NULL) {1927		pr_err("fsl-diu-fb: missing 'd-cache-size' property' "1928		       "in 'cpu' node\n");1929		of_node_put(np);1930		return -ENODEV;1931	}1932 1933	/*1934	 * Freescale PLRU requires 13/8 times the cache size to do a proper1935	 * displacement flush1936	 */1937	coherence_data_size = be32_to_cpup(prop) * 13;1938	coherence_data_size /= 8;1939 1940	pr_debug("fsl-diu-fb: coherence data size is %zu bytes\n",1941		 coherence_data_size);1942 1943	prop = of_get_property(np, "d-cache-line-size", NULL);1944	if (prop == NULL) {1945		pr_err("fsl-diu-fb: missing 'd-cache-line-size' property' "1946		       "in 'cpu' node\n");1947		of_node_put(np);1948		return -ENODEV;1949	}1950	d_cache_line_size = be32_to_cpup(prop);1951 1952	pr_debug("fsl-diu-fb: cache lines size is %u bytes\n",1953		 d_cache_line_size);1954 1955	of_node_put(np);1956	coherence_data = vmalloc(coherence_data_size);1957	if (!coherence_data)1958		return -ENOMEM;1959#endif1960 1961	ret = platform_driver_register(&fsl_diu_driver);1962	if (ret) {1963		pr_err("fsl-diu-fb: failed to register platform driver\n");1964#if defined(CONFIG_NOT_COHERENT_CACHE)1965		vfree(coherence_data);1966#endif1967	}1968	return ret;1969}1970 1971static void __exit fsl_diu_exit(void)1972{1973	platform_driver_unregister(&fsl_diu_driver);1974#if defined(CONFIG_NOT_COHERENT_CACHE)1975	vfree(coherence_data);1976#endif1977}1978 1979module_init(fsl_diu_init);1980module_exit(fsl_diu_exit);1981 1982MODULE_AUTHOR("York Sun <yorksun@freescale.com>");1983MODULE_DESCRIPTION("Freescale DIU framebuffer driver");1984MODULE_LICENSE("GPL");1985 1986module_param_named(mode, fb_mode, charp, 0);1987MODULE_PARM_DESC(mode,1988	"Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" ");1989module_param_named(bpp, default_bpp, ulong, 0);1990MODULE_PARM_DESC(bpp, "Specify bit-per-pixel if not specified in 'mode'");1991module_param_named(monitor, monitor_string, charp, 0);1992MODULE_PARM_DESC(monitor, "Specify the monitor port "1993	"(\"dvi\", \"lvds\", or \"dlvds\") if supported by the platform");1994 1995