1106 lines · 25.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* *  Freescale i.MX Frame Buffer device driver * *  Copyright (C) 2004 Sascha Hauer, Pengutronix *   Based on acornfb.c Copyright (C) Russell King. * * Please direct your questions and comments on this driver to the following * email address: * *	linux-arm-kernel@lists.arm.linux.org.uk */ #include <linux/module.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/interrupt.h>#include <linux/slab.h>#include <linux/mm.h>#include <linux/fb.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/ioport.h>#include <linux/cpufreq.h>#include <linux/clk.h>#include <linux/platform_device.h>#include <linux/dma-mapping.h>#include <linux/io.h>#include <linux/lcd.h>#include <linux/math64.h>#include <linux/of.h>#include <linux/bitfield.h> #include <linux/regulator/consumer.h> #include <video/of_display_timing.h>#include <video/of_videomode.h>#include <video/videomode.h> struct imx_fb_videomode {	struct fb_videomode mode;	u32 pcr;	bool aus_mode;	unsigned char	bpp;}; /* * Complain if VAR is out of range. */#define DEBUG_VAR 1 #define DRIVER_NAME "imx-fb" #define LCDC_SSA	0x00 #define LCDC_SIZE	0x04#define SIZE_XMAX_MASK	GENMASK(25, 20) #define YMAX_MASK_IMX1	GENMASK(8, 0)#define YMAX_MASK_IMX21	GENMASK(9, 0) #define LCDC_VPW	0x08#define VPW_VPW_MASK	GENMASK(9, 0) #define LCDC_CPOS	0x0C#define CPOS_CC1	BIT(31)#define CPOS_CC0	BIT(30)#define CPOS_OP		BIT(28)#define CPOS_CXP_MASK	GENMASK(25, 16) #define LCDC_LCWHB	0x10#define LCWHB_BK_EN	BIT(31)#define LCWHB_CW_MASK	GENMASK(28, 24)#define LCWHB_CH_MASK	GENMASK(20, 16)#define LCWHB_BD_MASK	GENMASK(7, 0) #define LCDC_LCHCC	0x14 #define LCDC_PCR	0x18#define PCR_TFT		BIT(31)#define PCR_COLOR	BIT(30)#define PCR_BPIX_MASK	GENMASK(27, 25)#define PCR_BPIX_8	3#define PCR_BPIX_12	4#define PCR_BPIX_16	5#define PCR_BPIX_18	6#define PCR_PCD_MASK	GENMASK(5, 0) #define LCDC_HCR	0x1C#define HCR_H_WIDTH_MASK	GENMASK(31, 26)#define HCR_H_WAIT_1_MASK	GENMASK(15, 8)#define HCR_H_WAIT_2_MASK	GENMASK(7, 0) #define LCDC_VCR	0x20#define VCR_V_WIDTH_MASK	GENMASK(31, 26)#define VCR_V_WAIT_1_MASK	GENMASK(15, 8)#define VCR_V_WAIT_2_MASK	GENMASK(7, 0) #define LCDC_POS	0x24#define POS_POS_MASK	GENMASK(4, 0) #define LCDC_LSCR1	0x28/* bit fields in imxfb.h */ #define LCDC_PWMR	0x2C/* bit fields in imxfb.h */ #define LCDC_DMACR	0x30/* bit fields in imxfb.h */ #define LCDC_RMCR	0x34 #define RMCR_LCDC_EN_MX1	BIT(1) #define RMCR_SELF_REF	BIT(0) #define LCDC_LCDICR	0x38#define LCDICR_INT_SYN	BIT(2)#define LCDICR_INT_CON	BIT(0) #define LCDC_LCDISR	0x40#define LCDISR_UDR_ERR	BIT(3)#define LCDISR_ERR_RES	BIT(2)#define LCDISR_EOF	BIT(1)#define LCDISR_BOF	BIT(0) #define IMXFB_LSCR1_DEFAULT 0x00120300 #define LCDC_LAUSCR	0x80#define LAUSCR_AUS_MODE	BIT(31) /* Used fb-mode. Can be set on kernel command line, therefore file-static. */static const char *fb_mode; /* * These are the bitfields for each * display depth that we support. */struct imxfb_rgb {	struct fb_bitfield	red;	struct fb_bitfield	green;	struct fb_bitfield	blue;	struct fb_bitfield	transp;}; enum imxfb_type {	IMX1_FB,	IMX21_FB,}; enum imxfb_panel_type {	PANEL_TYPE_MONOCHROME,	PANEL_TYPE_CSTN,	PANEL_TYPE_TFT,}; struct imxfb_info {	struct platform_device  *pdev;	void __iomem		*regs;	struct clk		*clk_ipg;	struct clk		*clk_ahb;	struct clk		*clk_per;	enum imxfb_type		devtype;	enum imxfb_panel_type	panel_type;	bool			enabled; 	/*	 * These are the addresses we mapped	 * the framebuffer memory region to.	 */	dma_addr_t		map_dma;	u_int			map_size; 	u_int			palette_size; 	dma_addr_t		dbar1;	dma_addr_t		dbar2; 	u_int			pcr;	u_int			lauscr;	u_int			pwmr;	u_int			lscr1;	u_int			dmacr;	bool			cmap_inverse;	bool			cmap_static; 	struct imx_fb_videomode *mode;	int			num_modes; 	struct regulator	*lcd_pwr;	int			lcd_pwr_enabled;}; static const struct platform_device_id imxfb_devtype[] = {	{		.name = "imx1-fb",		.driver_data = IMX1_FB,	}, {		.name = "imx21-fb",		.driver_data = IMX21_FB,	}, {		/* sentinel */	}};MODULE_DEVICE_TABLE(platform, imxfb_devtype); static const struct of_device_id imxfb_of_dev_id[] = {	{		.compatible = "fsl,imx1-fb",		.data = &imxfb_devtype[IMX1_FB],	}, {		.compatible = "fsl,imx21-fb",		.data = &imxfb_devtype[IMX21_FB],	}, {		/* sentinel */	}};MODULE_DEVICE_TABLE(of, imxfb_of_dev_id); static inline int is_imx1_fb(struct imxfb_info *fbi){	return fbi->devtype == IMX1_FB;} #define IMX_NAME	"IMX" /* * Minimum X and Y resolutions */#define MIN_XRES	64#define MIN_YRES	64 /* Actually this really is 18bit support, the lowest 2 bits of each colour * are unused in hardware. We claim to have 24bit support to make software * like X work, which does not support 18bit. */static struct imxfb_rgb def_rgb_18 = {	.red	= {.offset = 16, .length = 8,},	.green	= {.offset = 8, .length = 8,},	.blue	= {.offset = 0, .length = 8,},	.transp = {.offset = 0, .length = 0,},}; static struct imxfb_rgb def_rgb_16_tft = {	.red	= {.offset = 11, .length = 5,},	.green	= {.offset = 5, .length = 6,},	.blue	= {.offset = 0, .length = 5,},	.transp = {.offset = 0, .length = 0,},}; static struct imxfb_rgb def_rgb_16_stn = {	.red	= {.offset = 8, .length = 4,},	.green	= {.offset = 4, .length = 4,},	.blue	= {.offset = 0, .length = 4,},	.transp = {.offset = 0, .length = 0,},}; static struct imxfb_rgb def_rgb_8 = {	.red	= {.offset = 0, .length = 8,},	.green	= {.offset = 0, .length = 8,},	.blue	= {.offset = 0, .length = 8,},	.transp = {.offset = 0, .length = 0,},}; static int imxfb_activate_var(struct fb_var_screeninfo *var,		struct fb_info *info); static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf){	chan &= 0xffff;	chan >>= 16 - bf->length;	return chan << bf->offset;} static int imxfb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue,		u_int trans, struct fb_info *info){	struct imxfb_info *fbi = info->par;	u_int val, ret = 1; #define CNVT_TOHW(val, width) ((((val)<<(width))+0x7FFF-(val))>>16)	if (regno < fbi->palette_size) {		val = (CNVT_TOHW(red, 4) << 8) |		      (CNVT_TOHW(green, 4) << 4) |		      CNVT_TOHW(blue,  4); 		writel(val, fbi->regs + 0x800 + (regno << 2));		ret = 0;	}	return ret;} static int imxfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,		   u_int trans, struct fb_info *info){	struct imxfb_info *fbi = info->par;	unsigned int val;	int ret = 1; 	/*	 * If inverse mode was selected, invert all the colours	 * rather than the register number.  The register number	 * is what you poke into the framebuffer to produce the	 * colour you requested.	 */	if (fbi->cmap_inverse) {		red   = 0xffff - red;		green = 0xffff - green;		blue  = 0xffff - blue;	} 	/*	 * If greyscale is true, then we convert the RGB value	 * to greyscale no mater what visual we are using.	 */	if (info->var.grayscale)		red = green = blue = (19595 * red + 38470 * green +					7471 * blue) >> 16; 	switch (info->fix.visual) {	case FB_VISUAL_TRUECOLOR:		/*		 * 12 or 16-bit True Colour.  We encode the RGB value		 * according to the RGB bitfield information.		 */		if (regno < 16) {			u32 *pal = info->pseudo_palette; 			val  = chan_to_field(red, &info->var.red);			val |= chan_to_field(green, &info->var.green);			val |= chan_to_field(blue, &info->var.blue); 			pal[regno] = val;			ret = 0;		}		break; 	case FB_VISUAL_STATIC_PSEUDOCOLOR:	case FB_VISUAL_PSEUDOCOLOR:		ret = imxfb_setpalettereg(regno, red, green, blue, trans, info);		break;	} 	return ret;} static const struct imx_fb_videomode *imxfb_find_mode(struct imxfb_info *fbi){	struct imx_fb_videomode *m;	int i; 	if (!fb_mode)		return &fbi->mode[0]; 	for (i = 0, m = &fbi->mode[0]; i < fbi->num_modes; i++, m++) {		if (!strcmp(m->mode.name, fb_mode))			return m;	}	return NULL;} /* *  imxfb_check_var(): *    Round up in the following order: bits_per_pixel, xres, *    yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale, *    bitfields, horizontal timing, vertical timing. */static int imxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info){	struct imxfb_info *fbi = info->par;	struct imxfb_rgb *rgb;	const struct imx_fb_videomode *imxfb_mode;	unsigned long lcd_clk;	unsigned long long tmp;	u32 pcr = 0; 	if (var->xres < MIN_XRES)		var->xres = MIN_XRES;	if (var->yres < MIN_YRES)		var->yres = MIN_YRES; 	imxfb_mode = imxfb_find_mode(fbi);	if (!imxfb_mode)		return -EINVAL; 	var->xres		= imxfb_mode->mode.xres;	var->yres		= imxfb_mode->mode.yres;	var->bits_per_pixel	= imxfb_mode->bpp;	var->pixclock		= imxfb_mode->mode.pixclock;	var->hsync_len		= imxfb_mode->mode.hsync_len;	var->left_margin	= imxfb_mode->mode.left_margin;	var->right_margin	= imxfb_mode->mode.right_margin;	var->vsync_len		= imxfb_mode->mode.vsync_len;	var->upper_margin	= imxfb_mode->mode.upper_margin;	var->lower_margin	= imxfb_mode->mode.lower_margin;	var->sync		= imxfb_mode->mode.sync;	var->xres_virtual	= max(var->xres_virtual, var->xres);	var->yres_virtual	= max(var->yres_virtual, var->yres); 	pr_debug("var->bits_per_pixel=%d\n", var->bits_per_pixel); 	lcd_clk = clk_get_rate(fbi->clk_per); 	tmp = var->pixclock * (unsigned long long)lcd_clk; 	do_div(tmp, 1000000); 	if (do_div(tmp, 1000000) > 500000)		tmp++; 	pcr = (unsigned int)tmp; 	if (--pcr > PCR_PCD_MASK) {		pcr = PCR_PCD_MASK;		dev_warn(&fbi->pdev->dev, "Must limit pixel clock to %luHz\n",			 lcd_clk / pcr);	} 	switch (var->bits_per_pixel) {	case 32:		pcr |= FIELD_PREP(PCR_BPIX_MASK, PCR_BPIX_18);		rgb = &def_rgb_18;		break;	case 16:	default:		if (is_imx1_fb(fbi))			pcr |= FIELD_PREP(PCR_BPIX_MASK, PCR_BPIX_12);		else			pcr |= FIELD_PREP(PCR_BPIX_MASK, PCR_BPIX_16); 		if (imxfb_mode->pcr & PCR_TFT)			rgb = &def_rgb_16_tft;		else			rgb = &def_rgb_16_stn;		break;	case 8:		pcr |= FIELD_PREP(PCR_BPIX_MASK, PCR_BPIX_8);		rgb = &def_rgb_8;		break;	} 	/* add sync polarities */	pcr |= imxfb_mode->pcr & ~(PCR_PCD_MASK | PCR_BPIX_MASK); 	fbi->pcr = pcr;	/*	 * The LCDC AUS Mode Control Register does not exist on imx1.	 */	if (!is_imx1_fb(fbi) && imxfb_mode->aus_mode)		fbi->lauscr = LAUSCR_AUS_MODE; 	if (imxfb_mode->pcr & PCR_TFT)		fbi->panel_type = PANEL_TYPE_TFT;	else if (imxfb_mode->pcr & PCR_COLOR)		fbi->panel_type = PANEL_TYPE_CSTN;	else		fbi->panel_type = PANEL_TYPE_MONOCHROME; 	/*	 * Copy the RGB parameters for this display	 * from the machine specific parameters.	 */	var->red    = rgb->red;	var->green  = rgb->green;	var->blue   = rgb->blue;	var->transp = rgb->transp; 	pr_debug("RGBT length = %d:%d:%d:%d\n",		var->red.length, var->green.length, var->blue.length,		var->transp.length); 	pr_debug("RGBT offset = %d:%d:%d:%d\n",		var->red.offset, var->green.offset, var->blue.offset,		var->transp.offset); 	return 0;} /* * imxfb_set_par(): *	Set the user defined part of the display for the specified console */static int imxfb_set_par(struct fb_info *info){	struct imxfb_info *fbi = info->par;	struct fb_var_screeninfo *var = &info->var; 	if (var->bits_per_pixel == 16 || var->bits_per_pixel == 32)		info->fix.visual = FB_VISUAL_TRUECOLOR;	else if (!fbi->cmap_static)		info->fix.visual = FB_VISUAL_PSEUDOCOLOR;	else {		/*		 * Some people have weird ideas about wanting static		 * pseudocolor maps.  I suspect their user space		 * applications are broken.		 */		info->fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR;	}

Lines 1–500 of 1106.