162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci *  Freescale i.MX Frame Buffer device driver
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci *  Copyright (C) 2004 Sascha Hauer, Pengutronix
562306a36Sopenharmony_ci *   Based on acornfb.c Copyright (C) Russell King.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
862306a36Sopenharmony_ci * License.  See the file COPYING in the main directory of this archive for
962306a36Sopenharmony_ci * more details.
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Please direct your questions and comments on this driver to the following
1262306a36Sopenharmony_ci * email address:
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci *	linux-arm-kernel@lists.arm.linux.org.uk
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <linux/module.h>
1862306a36Sopenharmony_ci#include <linux/kernel.h>
1962306a36Sopenharmony_ci#include <linux/errno.h>
2062306a36Sopenharmony_ci#include <linux/string.h>
2162306a36Sopenharmony_ci#include <linux/interrupt.h>
2262306a36Sopenharmony_ci#include <linux/slab.h>
2362306a36Sopenharmony_ci#include <linux/mm.h>
2462306a36Sopenharmony_ci#include <linux/fb.h>
2562306a36Sopenharmony_ci#include <linux/delay.h>
2662306a36Sopenharmony_ci#include <linux/init.h>
2762306a36Sopenharmony_ci#include <linux/ioport.h>
2862306a36Sopenharmony_ci#include <linux/cpufreq.h>
2962306a36Sopenharmony_ci#include <linux/clk.h>
3062306a36Sopenharmony_ci#include <linux/platform_device.h>
3162306a36Sopenharmony_ci#include <linux/dma-mapping.h>
3262306a36Sopenharmony_ci#include <linux/io.h>
3362306a36Sopenharmony_ci#include <linux/lcd.h>
3462306a36Sopenharmony_ci#include <linux/math64.h>
3562306a36Sopenharmony_ci#include <linux/of.h>
3662306a36Sopenharmony_ci#include <linux/of_device.h>
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#include <video/of_display_timing.h>
4162306a36Sopenharmony_ci#include <video/of_videomode.h>
4262306a36Sopenharmony_ci#include <video/videomode.h>
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#define PCR_TFT		(1 << 31)
4562306a36Sopenharmony_ci#define PCR_COLOR	(1 << 30)
4662306a36Sopenharmony_ci#define PCR_BPIX_8	(3 << 25)
4762306a36Sopenharmony_ci#define PCR_BPIX_12	(4 << 25)
4862306a36Sopenharmony_ci#define PCR_BPIX_16	(5 << 25)
4962306a36Sopenharmony_ci#define PCR_BPIX_18	(6 << 25)
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistruct imx_fb_videomode {
5262306a36Sopenharmony_ci	struct fb_videomode mode;
5362306a36Sopenharmony_ci	u32 pcr;
5462306a36Sopenharmony_ci	bool aus_mode;
5562306a36Sopenharmony_ci	unsigned char	bpp;
5662306a36Sopenharmony_ci};
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/*
5962306a36Sopenharmony_ci * Complain if VAR is out of range.
6062306a36Sopenharmony_ci */
6162306a36Sopenharmony_ci#define DEBUG_VAR 1
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci#define DRIVER_NAME "imx-fb"
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci#define LCDC_SSA	0x00
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci#define LCDC_SIZE	0x04
6862306a36Sopenharmony_ci#define SIZE_XMAX(x)	((((x) >> 4) & 0x3f) << 20)
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci#define YMAX_MASK_IMX1	0x1ff
7162306a36Sopenharmony_ci#define YMAX_MASK_IMX21	0x3ff
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci#define LCDC_VPW	0x08
7462306a36Sopenharmony_ci#define VPW_VPW(x)	((x) & 0x3ff)
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci#define LCDC_CPOS	0x0C
7762306a36Sopenharmony_ci#define CPOS_CC1	(1<<31)
7862306a36Sopenharmony_ci#define CPOS_CC0	(1<<30)
7962306a36Sopenharmony_ci#define CPOS_OP		(1<<28)
8062306a36Sopenharmony_ci#define CPOS_CXP(x)	(((x) & 3ff) << 16)
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci#define LCDC_LCWHB	0x10
8362306a36Sopenharmony_ci#define LCWHB_BK_EN	(1<<31)
8462306a36Sopenharmony_ci#define LCWHB_CW(w)	(((w) & 0x1f) << 24)
8562306a36Sopenharmony_ci#define LCWHB_CH(h)	(((h) & 0x1f) << 16)
8662306a36Sopenharmony_ci#define LCWHB_BD(x)	((x) & 0xff)
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci#define LCDC_LCHCC	0x14
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci#define LCDC_PCR	0x18
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci#define LCDC_HCR	0x1C
9362306a36Sopenharmony_ci#define HCR_H_WIDTH(x)	(((x) & 0x3f) << 26)
9462306a36Sopenharmony_ci#define HCR_H_WAIT_1(x)	(((x) & 0xff) << 8)
9562306a36Sopenharmony_ci#define HCR_H_WAIT_2(x)	((x) & 0xff)
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci#define LCDC_VCR	0x20
9862306a36Sopenharmony_ci#define VCR_V_WIDTH(x)	(((x) & 0x3f) << 26)
9962306a36Sopenharmony_ci#define VCR_V_WAIT_1(x)	(((x) & 0xff) << 8)
10062306a36Sopenharmony_ci#define VCR_V_WAIT_2(x)	((x) & 0xff)
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci#define LCDC_POS	0x24
10362306a36Sopenharmony_ci#define POS_POS(x)	((x) & 1f)
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci#define LCDC_LSCR1	0x28
10662306a36Sopenharmony_ci/* bit fields in imxfb.h */
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci#define LCDC_PWMR	0x2C
10962306a36Sopenharmony_ci/* bit fields in imxfb.h */
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci#define LCDC_DMACR	0x30
11262306a36Sopenharmony_ci/* bit fields in imxfb.h */
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci#define LCDC_RMCR	0x34
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci#define RMCR_LCDC_EN_MX1	(1<<1)
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci#define RMCR_SELF_REF	(1<<0)
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci#define LCDC_LCDICR	0x38
12162306a36Sopenharmony_ci#define LCDICR_INT_SYN	(1<<2)
12262306a36Sopenharmony_ci#define LCDICR_INT_CON	(1)
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci#define LCDC_LCDISR	0x40
12562306a36Sopenharmony_ci#define LCDISR_UDR_ERR	(1<<3)
12662306a36Sopenharmony_ci#define LCDISR_ERR_RES	(1<<2)
12762306a36Sopenharmony_ci#define LCDISR_EOF	(1<<1)
12862306a36Sopenharmony_ci#define LCDISR_BOF	(1<<0)
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci#define IMXFB_LSCR1_DEFAULT 0x00120300
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci#define LCDC_LAUSCR	0x80
13362306a36Sopenharmony_ci#define LAUSCR_AUS_MODE	(1<<31)
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci/* Used fb-mode. Can be set on kernel command line, therefore file-static. */
13662306a36Sopenharmony_cistatic const char *fb_mode;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci/*
13962306a36Sopenharmony_ci * These are the bitfields for each
14062306a36Sopenharmony_ci * display depth that we support.
14162306a36Sopenharmony_ci */
14262306a36Sopenharmony_cistruct imxfb_rgb {
14362306a36Sopenharmony_ci	struct fb_bitfield	red;
14462306a36Sopenharmony_ci	struct fb_bitfield	green;
14562306a36Sopenharmony_ci	struct fb_bitfield	blue;
14662306a36Sopenharmony_ci	struct fb_bitfield	transp;
14762306a36Sopenharmony_ci};
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cienum imxfb_type {
15062306a36Sopenharmony_ci	IMX1_FB,
15162306a36Sopenharmony_ci	IMX21_FB,
15262306a36Sopenharmony_ci};
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cienum imxfb_panel_type {
15562306a36Sopenharmony_ci	PANEL_TYPE_MONOCHROME,
15662306a36Sopenharmony_ci	PANEL_TYPE_CSTN,
15762306a36Sopenharmony_ci	PANEL_TYPE_TFT,
15862306a36Sopenharmony_ci};
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistruct imxfb_info {
16162306a36Sopenharmony_ci	struct platform_device  *pdev;
16262306a36Sopenharmony_ci	void __iomem		*regs;
16362306a36Sopenharmony_ci	struct clk		*clk_ipg;
16462306a36Sopenharmony_ci	struct clk		*clk_ahb;
16562306a36Sopenharmony_ci	struct clk		*clk_per;
16662306a36Sopenharmony_ci	enum imxfb_type		devtype;
16762306a36Sopenharmony_ci	enum imxfb_panel_type	panel_type;
16862306a36Sopenharmony_ci	bool			enabled;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	/*
17162306a36Sopenharmony_ci	 * These are the addresses we mapped
17262306a36Sopenharmony_ci	 * the framebuffer memory region to.
17362306a36Sopenharmony_ci	 */
17462306a36Sopenharmony_ci	dma_addr_t		map_dma;
17562306a36Sopenharmony_ci	u_int			map_size;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	u_int			palette_size;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	dma_addr_t		dbar1;
18062306a36Sopenharmony_ci	dma_addr_t		dbar2;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	u_int			pcr;
18362306a36Sopenharmony_ci	u_int			lauscr;
18462306a36Sopenharmony_ci	u_int			pwmr;
18562306a36Sopenharmony_ci	u_int			lscr1;
18662306a36Sopenharmony_ci	u_int			dmacr;
18762306a36Sopenharmony_ci	bool			cmap_inverse;
18862306a36Sopenharmony_ci	bool			cmap_static;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	struct imx_fb_videomode *mode;
19162306a36Sopenharmony_ci	int			num_modes;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	struct regulator	*lcd_pwr;
19462306a36Sopenharmony_ci	int			lcd_pwr_enabled;
19562306a36Sopenharmony_ci};
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic const struct platform_device_id imxfb_devtype[] = {
19862306a36Sopenharmony_ci	{
19962306a36Sopenharmony_ci		.name = "imx1-fb",
20062306a36Sopenharmony_ci		.driver_data = IMX1_FB,
20162306a36Sopenharmony_ci	}, {
20262306a36Sopenharmony_ci		.name = "imx21-fb",
20362306a36Sopenharmony_ci		.driver_data = IMX21_FB,
20462306a36Sopenharmony_ci	}, {
20562306a36Sopenharmony_ci		/* sentinel */
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci};
20862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(platform, imxfb_devtype);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistatic const struct of_device_id imxfb_of_dev_id[] = {
21162306a36Sopenharmony_ci	{
21262306a36Sopenharmony_ci		.compatible = "fsl,imx1-fb",
21362306a36Sopenharmony_ci		.data = &imxfb_devtype[IMX1_FB],
21462306a36Sopenharmony_ci	}, {
21562306a36Sopenharmony_ci		.compatible = "fsl,imx21-fb",
21662306a36Sopenharmony_ci		.data = &imxfb_devtype[IMX21_FB],
21762306a36Sopenharmony_ci	}, {
21862306a36Sopenharmony_ci		/* sentinel */
21962306a36Sopenharmony_ci	}
22062306a36Sopenharmony_ci};
22162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, imxfb_of_dev_id);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic inline int is_imx1_fb(struct imxfb_info *fbi)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	return fbi->devtype == IMX1_FB;
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci#define IMX_NAME	"IMX"
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci/*
23162306a36Sopenharmony_ci * Minimum X and Y resolutions
23262306a36Sopenharmony_ci */
23362306a36Sopenharmony_ci#define MIN_XRES	64
23462306a36Sopenharmony_ci#define MIN_YRES	64
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci/* Actually this really is 18bit support, the lowest 2 bits of each colour
23762306a36Sopenharmony_ci * are unused in hardware. We claim to have 24bit support to make software
23862306a36Sopenharmony_ci * like X work, which does not support 18bit.
23962306a36Sopenharmony_ci */
24062306a36Sopenharmony_cistatic struct imxfb_rgb def_rgb_18 = {
24162306a36Sopenharmony_ci	.red	= {.offset = 16, .length = 8,},
24262306a36Sopenharmony_ci	.green	= {.offset = 8, .length = 8,},
24362306a36Sopenharmony_ci	.blue	= {.offset = 0, .length = 8,},
24462306a36Sopenharmony_ci	.transp = {.offset = 0, .length = 0,},
24562306a36Sopenharmony_ci};
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic struct imxfb_rgb def_rgb_16_tft = {
24862306a36Sopenharmony_ci	.red	= {.offset = 11, .length = 5,},
24962306a36Sopenharmony_ci	.green	= {.offset = 5, .length = 6,},
25062306a36Sopenharmony_ci	.blue	= {.offset = 0, .length = 5,},
25162306a36Sopenharmony_ci	.transp = {.offset = 0, .length = 0,},
25262306a36Sopenharmony_ci};
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic struct imxfb_rgb def_rgb_16_stn = {
25562306a36Sopenharmony_ci	.red	= {.offset = 8, .length = 4,},
25662306a36Sopenharmony_ci	.green	= {.offset = 4, .length = 4,},
25762306a36Sopenharmony_ci	.blue	= {.offset = 0, .length = 4,},
25862306a36Sopenharmony_ci	.transp = {.offset = 0, .length = 0,},
25962306a36Sopenharmony_ci};
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_cistatic struct imxfb_rgb def_rgb_8 = {
26262306a36Sopenharmony_ci	.red	= {.offset = 0, .length = 8,},
26362306a36Sopenharmony_ci	.green	= {.offset = 0, .length = 8,},
26462306a36Sopenharmony_ci	.blue	= {.offset = 0, .length = 8,},
26562306a36Sopenharmony_ci	.transp = {.offset = 0, .length = 0,},
26662306a36Sopenharmony_ci};
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_cistatic int imxfb_activate_var(struct fb_var_screeninfo *var,
26962306a36Sopenharmony_ci		struct fb_info *info);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	chan &= 0xffff;
27462306a36Sopenharmony_ci	chan >>= 16 - bf->length;
27562306a36Sopenharmony_ci	return chan << bf->offset;
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cistatic int imxfb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue,
27962306a36Sopenharmony_ci		u_int trans, struct fb_info *info)
28062306a36Sopenharmony_ci{
28162306a36Sopenharmony_ci	struct imxfb_info *fbi = info->par;
28262306a36Sopenharmony_ci	u_int val, ret = 1;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci#define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16)
28562306a36Sopenharmony_ci	if (regno < fbi->palette_size) {
28662306a36Sopenharmony_ci		val = (CNVT_TOHW(red, 4) << 8) |
28762306a36Sopenharmony_ci		      (CNVT_TOHW(green,4) << 4) |
28862306a36Sopenharmony_ci		      CNVT_TOHW(blue,  4);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci		writel(val, fbi->regs + 0x800 + (regno << 2));
29162306a36Sopenharmony_ci		ret = 0;
29262306a36Sopenharmony_ci	}
29362306a36Sopenharmony_ci	return ret;
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistatic int imxfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
29762306a36Sopenharmony_ci		   u_int trans, struct fb_info *info)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	struct imxfb_info *fbi = info->par;
30062306a36Sopenharmony_ci	unsigned int val;
30162306a36Sopenharmony_ci	int ret = 1;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	/*
30462306a36Sopenharmony_ci	 * If inverse mode was selected, invert all the colours
30562306a36Sopenharmony_ci	 * rather than the register number.  The register number
30662306a36Sopenharmony_ci	 * is what you poke into the framebuffer to produce the
30762306a36Sopenharmony_ci	 * colour you requested.
30862306a36Sopenharmony_ci	 */
30962306a36Sopenharmony_ci	if (fbi->cmap_inverse) {
31062306a36Sopenharmony_ci		red   = 0xffff - red;
31162306a36Sopenharmony_ci		green = 0xffff - green;
31262306a36Sopenharmony_ci		blue  = 0xffff - blue;
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	/*
31662306a36Sopenharmony_ci	 * If greyscale is true, then we convert the RGB value
31762306a36Sopenharmony_ci	 * to greyscale no mater what visual we are using.
31862306a36Sopenharmony_ci	 */
31962306a36Sopenharmony_ci	if (info->var.grayscale)
32062306a36Sopenharmony_ci		red = green = blue = (19595 * red + 38470 * green +
32162306a36Sopenharmony_ci					7471 * blue) >> 16;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	switch (info->fix.visual) {
32462306a36Sopenharmony_ci	case FB_VISUAL_TRUECOLOR:
32562306a36Sopenharmony_ci		/*
32662306a36Sopenharmony_ci		 * 12 or 16-bit True Colour.  We encode the RGB value
32762306a36Sopenharmony_ci		 * according to the RGB bitfield information.
32862306a36Sopenharmony_ci		 */
32962306a36Sopenharmony_ci		if (regno < 16) {
33062306a36Sopenharmony_ci			u32 *pal = info->pseudo_palette;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci			val  = chan_to_field(red, &info->var.red);
33362306a36Sopenharmony_ci			val |= chan_to_field(green, &info->var.green);
33462306a36Sopenharmony_ci			val |= chan_to_field(blue, &info->var.blue);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci			pal[regno] = val;
33762306a36Sopenharmony_ci			ret = 0;
33862306a36Sopenharmony_ci		}
33962306a36Sopenharmony_ci		break;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	case FB_VISUAL_STATIC_PSEUDOCOLOR:
34262306a36Sopenharmony_ci	case FB_VISUAL_PSEUDOCOLOR:
34362306a36Sopenharmony_ci		ret = imxfb_setpalettereg(regno, red, green, blue, trans, info);
34462306a36Sopenharmony_ci		break;
34562306a36Sopenharmony_ci	}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	return ret;
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic const struct imx_fb_videomode *imxfb_find_mode(struct imxfb_info *fbi)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	struct imx_fb_videomode *m;
35362306a36Sopenharmony_ci	int i;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	if (!fb_mode)
35662306a36Sopenharmony_ci		return &fbi->mode[0];
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	for (i = 0, m = &fbi->mode[0]; i < fbi->num_modes; i++, m++) {
35962306a36Sopenharmony_ci		if (!strcmp(m->mode.name, fb_mode))
36062306a36Sopenharmony_ci			return m;
36162306a36Sopenharmony_ci	}
36262306a36Sopenharmony_ci	return NULL;
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci/*
36662306a36Sopenharmony_ci *  imxfb_check_var():
36762306a36Sopenharmony_ci *    Round up in the following order: bits_per_pixel, xres,
36862306a36Sopenharmony_ci *    yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale,
36962306a36Sopenharmony_ci *    bitfields, horizontal timing, vertical timing.
37062306a36Sopenharmony_ci */
37162306a36Sopenharmony_cistatic int imxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
37262306a36Sopenharmony_ci{
37362306a36Sopenharmony_ci	struct imxfb_info *fbi = info->par;
37462306a36Sopenharmony_ci	struct imxfb_rgb *rgb;
37562306a36Sopenharmony_ci	const struct imx_fb_videomode *imxfb_mode;
37662306a36Sopenharmony_ci	unsigned long lcd_clk;
37762306a36Sopenharmony_ci	unsigned long long tmp;
37862306a36Sopenharmony_ci	u32 pcr = 0;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	if (var->xres < MIN_XRES)
38162306a36Sopenharmony_ci		var->xres = MIN_XRES;
38262306a36Sopenharmony_ci	if (var->yres < MIN_YRES)
38362306a36Sopenharmony_ci		var->yres = MIN_YRES;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	imxfb_mode = imxfb_find_mode(fbi);
38662306a36Sopenharmony_ci	if (!imxfb_mode)
38762306a36Sopenharmony_ci		return -EINVAL;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	var->xres		= imxfb_mode->mode.xres;
39062306a36Sopenharmony_ci	var->yres		= imxfb_mode->mode.yres;
39162306a36Sopenharmony_ci	var->bits_per_pixel	= imxfb_mode->bpp;
39262306a36Sopenharmony_ci	var->pixclock		= imxfb_mode->mode.pixclock;
39362306a36Sopenharmony_ci	var->hsync_len		= imxfb_mode->mode.hsync_len;
39462306a36Sopenharmony_ci	var->left_margin	= imxfb_mode->mode.left_margin;
39562306a36Sopenharmony_ci	var->right_margin	= imxfb_mode->mode.right_margin;
39662306a36Sopenharmony_ci	var->vsync_len		= imxfb_mode->mode.vsync_len;
39762306a36Sopenharmony_ci	var->upper_margin	= imxfb_mode->mode.upper_margin;
39862306a36Sopenharmony_ci	var->lower_margin	= imxfb_mode->mode.lower_margin;
39962306a36Sopenharmony_ci	var->sync		= imxfb_mode->mode.sync;
40062306a36Sopenharmony_ci	var->xres_virtual	= max(var->xres_virtual, var->xres);
40162306a36Sopenharmony_ci	var->yres_virtual	= max(var->yres_virtual, var->yres);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	pr_debug("var->bits_per_pixel=%d\n", var->bits_per_pixel);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	lcd_clk = clk_get_rate(fbi->clk_per);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	tmp = var->pixclock * (unsigned long long)lcd_clk;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	do_div(tmp, 1000000);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	if (do_div(tmp, 1000000) > 500000)
41262306a36Sopenharmony_ci		tmp++;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	pcr = (unsigned int)tmp;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	if (--pcr > 0x3F) {
41762306a36Sopenharmony_ci		pcr = 0x3F;
41862306a36Sopenharmony_ci		printk(KERN_WARNING "Must limit pixel clock to %luHz\n",
41962306a36Sopenharmony_ci				lcd_clk / pcr);
42062306a36Sopenharmony_ci	}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	switch (var->bits_per_pixel) {
42362306a36Sopenharmony_ci	case 32:
42462306a36Sopenharmony_ci		pcr |= PCR_BPIX_18;
42562306a36Sopenharmony_ci		rgb = &def_rgb_18;
42662306a36Sopenharmony_ci		break;
42762306a36Sopenharmony_ci	case 16:
42862306a36Sopenharmony_ci	default:
42962306a36Sopenharmony_ci		if (is_imx1_fb(fbi))
43062306a36Sopenharmony_ci			pcr |= PCR_BPIX_12;
43162306a36Sopenharmony_ci		else
43262306a36Sopenharmony_ci			pcr |= PCR_BPIX_16;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci		if (imxfb_mode->pcr & PCR_TFT)
43562306a36Sopenharmony_ci			rgb = &def_rgb_16_tft;
43662306a36Sopenharmony_ci		else
43762306a36Sopenharmony_ci			rgb = &def_rgb_16_stn;
43862306a36Sopenharmony_ci		break;
43962306a36Sopenharmony_ci	case 8:
44062306a36Sopenharmony_ci		pcr |= PCR_BPIX_8;
44162306a36Sopenharmony_ci		rgb = &def_rgb_8;
44262306a36Sopenharmony_ci		break;
44362306a36Sopenharmony_ci	}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	/* add sync polarities */
44662306a36Sopenharmony_ci	pcr |= imxfb_mode->pcr & ~(0x3f | (7 << 25));
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	fbi->pcr = pcr;
44962306a36Sopenharmony_ci	/*
45062306a36Sopenharmony_ci	 * The LCDC AUS Mode Control Register does not exist on imx1.
45162306a36Sopenharmony_ci	 */
45262306a36Sopenharmony_ci	if (!is_imx1_fb(fbi) && imxfb_mode->aus_mode)
45362306a36Sopenharmony_ci		fbi->lauscr = LAUSCR_AUS_MODE;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	if (imxfb_mode->pcr & PCR_TFT)
45662306a36Sopenharmony_ci		fbi->panel_type = PANEL_TYPE_TFT;
45762306a36Sopenharmony_ci	else if (imxfb_mode->pcr & PCR_COLOR)
45862306a36Sopenharmony_ci		fbi->panel_type = PANEL_TYPE_CSTN;
45962306a36Sopenharmony_ci	else
46062306a36Sopenharmony_ci		fbi->panel_type = PANEL_TYPE_MONOCHROME;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	/*
46362306a36Sopenharmony_ci	 * Copy the RGB parameters for this display
46462306a36Sopenharmony_ci	 * from the machine specific parameters.
46562306a36Sopenharmony_ci	 */
46662306a36Sopenharmony_ci	var->red    = rgb->red;
46762306a36Sopenharmony_ci	var->green  = rgb->green;
46862306a36Sopenharmony_ci	var->blue   = rgb->blue;
46962306a36Sopenharmony_ci	var->transp = rgb->transp;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	pr_debug("RGBT length = %d:%d:%d:%d\n",
47262306a36Sopenharmony_ci		var->red.length, var->green.length, var->blue.length,
47362306a36Sopenharmony_ci		var->transp.length);
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	pr_debug("RGBT offset = %d:%d:%d:%d\n",
47662306a36Sopenharmony_ci		var->red.offset, var->green.offset, var->blue.offset,
47762306a36Sopenharmony_ci		var->transp.offset);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	return 0;
48062306a36Sopenharmony_ci}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci/*
48362306a36Sopenharmony_ci * imxfb_set_par():
48462306a36Sopenharmony_ci *	Set the user defined part of the display for the specified console
48562306a36Sopenharmony_ci */
48662306a36Sopenharmony_cistatic int imxfb_set_par(struct fb_info *info)
48762306a36Sopenharmony_ci{
48862306a36Sopenharmony_ci	struct imxfb_info *fbi = info->par;
48962306a36Sopenharmony_ci	struct fb_var_screeninfo *var = &info->var;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	if (var->bits_per_pixel == 16 || var->bits_per_pixel == 32)
49262306a36Sopenharmony_ci		info->fix.visual = FB_VISUAL_TRUECOLOR;
49362306a36Sopenharmony_ci	else if (!fbi->cmap_static)
49462306a36Sopenharmony_ci		info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
49562306a36Sopenharmony_ci	else {
49662306a36Sopenharmony_ci		/*
49762306a36Sopenharmony_ci		 * Some people have weird ideas about wanting static
49862306a36Sopenharmony_ci		 * pseudocolor maps.  I suspect their user space
49962306a36Sopenharmony_ci		 * applications are broken.
50062306a36Sopenharmony_ci		 */
50162306a36Sopenharmony_ci		info->fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR;
50262306a36Sopenharmony_ci	}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8;
50562306a36Sopenharmony_ci	fbi->palette_size = var->bits_per_pixel == 8 ? 256 : 16;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	imxfb_activate_var(var, info);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	return 0;
51062306a36Sopenharmony_ci}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_cistatic int imxfb_enable_controller(struct imxfb_info *fbi)
51362306a36Sopenharmony_ci{
51462306a36Sopenharmony_ci	int ret;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	if (fbi->enabled)
51762306a36Sopenharmony_ci		return 0;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	pr_debug("Enabling LCD controller\n");
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	writel(fbi->map_dma, fbi->regs + LCDC_SSA);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	/* panning offset 0 (0 pixel offset)        */
52462306a36Sopenharmony_ci	writel(0x00000000, fbi->regs + LCDC_POS);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	/* disable hardware cursor */
52762306a36Sopenharmony_ci	writel(readl(fbi->regs + LCDC_CPOS) & ~(CPOS_CC0 | CPOS_CC1),
52862306a36Sopenharmony_ci		fbi->regs + LCDC_CPOS);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	/*
53162306a36Sopenharmony_ci	 * RMCR_LCDC_EN_MX1 is present on i.MX1 only, but doesn't hurt
53262306a36Sopenharmony_ci	 * on other SoCs
53362306a36Sopenharmony_ci	 */
53462306a36Sopenharmony_ci	writel(RMCR_LCDC_EN_MX1, fbi->regs + LCDC_RMCR);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	ret = clk_prepare_enable(fbi->clk_ipg);
53762306a36Sopenharmony_ci	if (ret)
53862306a36Sopenharmony_ci		goto err_enable_ipg;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	ret = clk_prepare_enable(fbi->clk_ahb);
54162306a36Sopenharmony_ci	if (ret)
54262306a36Sopenharmony_ci		goto err_enable_ahb;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	ret = clk_prepare_enable(fbi->clk_per);
54562306a36Sopenharmony_ci	if (ret)
54662306a36Sopenharmony_ci		goto err_enable_per;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	fbi->enabled = true;
54962306a36Sopenharmony_ci	return 0;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_cierr_enable_per:
55262306a36Sopenharmony_ci	clk_disable_unprepare(fbi->clk_ahb);
55362306a36Sopenharmony_cierr_enable_ahb:
55462306a36Sopenharmony_ci	clk_disable_unprepare(fbi->clk_ipg);
55562306a36Sopenharmony_cierr_enable_ipg:
55662306a36Sopenharmony_ci	writel(0, fbi->regs + LCDC_RMCR);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	return ret;
55962306a36Sopenharmony_ci}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_cistatic void imxfb_disable_controller(struct imxfb_info *fbi)
56262306a36Sopenharmony_ci{
56362306a36Sopenharmony_ci	if (!fbi->enabled)
56462306a36Sopenharmony_ci		return;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	pr_debug("Disabling LCD controller\n");
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	clk_disable_unprepare(fbi->clk_per);
56962306a36Sopenharmony_ci	clk_disable_unprepare(fbi->clk_ahb);
57062306a36Sopenharmony_ci	clk_disable_unprepare(fbi->clk_ipg);
57162306a36Sopenharmony_ci	fbi->enabled = false;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	writel(0, fbi->regs + LCDC_RMCR);
57462306a36Sopenharmony_ci}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_cistatic int imxfb_blank(int blank, struct fb_info *info)
57762306a36Sopenharmony_ci{
57862306a36Sopenharmony_ci	struct imxfb_info *fbi = info->par;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	pr_debug("imxfb_blank: blank=%d\n", blank);
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	switch (blank) {
58362306a36Sopenharmony_ci	case FB_BLANK_POWERDOWN:
58462306a36Sopenharmony_ci	case FB_BLANK_VSYNC_SUSPEND:
58562306a36Sopenharmony_ci	case FB_BLANK_HSYNC_SUSPEND:
58662306a36Sopenharmony_ci	case FB_BLANK_NORMAL:
58762306a36Sopenharmony_ci		imxfb_disable_controller(fbi);
58862306a36Sopenharmony_ci		break;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	case FB_BLANK_UNBLANK:
59162306a36Sopenharmony_ci		return imxfb_enable_controller(fbi);
59262306a36Sopenharmony_ci	}
59362306a36Sopenharmony_ci	return 0;
59462306a36Sopenharmony_ci}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_cistatic const struct fb_ops imxfb_ops = {
59762306a36Sopenharmony_ci	.owner		= THIS_MODULE,
59862306a36Sopenharmony_ci	FB_DEFAULT_IOMEM_OPS,
59962306a36Sopenharmony_ci	.fb_check_var	= imxfb_check_var,
60062306a36Sopenharmony_ci	.fb_set_par	= imxfb_set_par,
60162306a36Sopenharmony_ci	.fb_setcolreg	= imxfb_setcolreg,
60262306a36Sopenharmony_ci	.fb_blank	= imxfb_blank,
60362306a36Sopenharmony_ci};
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci/*
60662306a36Sopenharmony_ci * imxfb_activate_var():
60762306a36Sopenharmony_ci *	Configures LCD Controller based on entries in var parameter.  Settings are
60862306a36Sopenharmony_ci *	only written to the controller if changes were made.
60962306a36Sopenharmony_ci */
61062306a36Sopenharmony_cistatic int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *info)
61162306a36Sopenharmony_ci{
61262306a36Sopenharmony_ci	struct imxfb_info *fbi = info->par;
61362306a36Sopenharmony_ci	u32 ymax_mask = is_imx1_fb(fbi) ? YMAX_MASK_IMX1 : YMAX_MASK_IMX21;
61462306a36Sopenharmony_ci	u8 left_margin_low;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	pr_debug("var: xres=%d hslen=%d lm=%d rm=%d\n",
61762306a36Sopenharmony_ci		var->xres, var->hsync_len,
61862306a36Sopenharmony_ci		var->left_margin, var->right_margin);
61962306a36Sopenharmony_ci	pr_debug("var: yres=%d vslen=%d um=%d bm=%d\n",
62062306a36Sopenharmony_ci		var->yres, var->vsync_len,
62162306a36Sopenharmony_ci		var->upper_margin, var->lower_margin);
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	if (fbi->panel_type == PANEL_TYPE_TFT)
62462306a36Sopenharmony_ci		left_margin_low = 3;
62562306a36Sopenharmony_ci	else if (fbi->panel_type == PANEL_TYPE_CSTN)
62662306a36Sopenharmony_ci		left_margin_low = 2;
62762306a36Sopenharmony_ci	else
62862306a36Sopenharmony_ci		left_margin_low = 0;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci#if DEBUG_VAR
63162306a36Sopenharmony_ci	if (var->xres < 16        || var->xres > 1024)
63262306a36Sopenharmony_ci		printk(KERN_ERR "%s: invalid xres %d\n",
63362306a36Sopenharmony_ci			info->fix.id, var->xres);
63462306a36Sopenharmony_ci	if (var->hsync_len < 1    || var->hsync_len > 64)
63562306a36Sopenharmony_ci		printk(KERN_ERR "%s: invalid hsync_len %d\n",
63662306a36Sopenharmony_ci			info->fix.id, var->hsync_len);
63762306a36Sopenharmony_ci	if (var->left_margin < left_margin_low  || var->left_margin > 255)
63862306a36Sopenharmony_ci		printk(KERN_ERR "%s: invalid left_margin %d\n",
63962306a36Sopenharmony_ci			info->fix.id, var->left_margin);
64062306a36Sopenharmony_ci	if (var->right_margin < 1 || var->right_margin > 255)
64162306a36Sopenharmony_ci		printk(KERN_ERR "%s: invalid right_margin %d\n",
64262306a36Sopenharmony_ci			info->fix.id, var->right_margin);
64362306a36Sopenharmony_ci	if (var->yres < 1 || var->yres > ymax_mask)
64462306a36Sopenharmony_ci		printk(KERN_ERR "%s: invalid yres %d\n",
64562306a36Sopenharmony_ci			info->fix.id, var->yres);
64662306a36Sopenharmony_ci	if (var->vsync_len > 100)
64762306a36Sopenharmony_ci		printk(KERN_ERR "%s: invalid vsync_len %d\n",
64862306a36Sopenharmony_ci			info->fix.id, var->vsync_len);
64962306a36Sopenharmony_ci	if (var->upper_margin > 63)
65062306a36Sopenharmony_ci		printk(KERN_ERR "%s: invalid upper_margin %d\n",
65162306a36Sopenharmony_ci			info->fix.id, var->upper_margin);
65262306a36Sopenharmony_ci	if (var->lower_margin > 255)
65362306a36Sopenharmony_ci		printk(KERN_ERR "%s: invalid lower_margin %d\n",
65462306a36Sopenharmony_ci			info->fix.id, var->lower_margin);
65562306a36Sopenharmony_ci#endif
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	/* physical screen start address	    */
65862306a36Sopenharmony_ci	writel(VPW_VPW(var->xres * var->bits_per_pixel / 8 / 4),
65962306a36Sopenharmony_ci		fbi->regs + LCDC_VPW);
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	writel(HCR_H_WIDTH(var->hsync_len - 1) |
66262306a36Sopenharmony_ci		HCR_H_WAIT_1(var->right_margin - 1) |
66362306a36Sopenharmony_ci		HCR_H_WAIT_2(var->left_margin - left_margin_low),
66462306a36Sopenharmony_ci		fbi->regs + LCDC_HCR);
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	writel(VCR_V_WIDTH(var->vsync_len) |
66762306a36Sopenharmony_ci		VCR_V_WAIT_1(var->lower_margin) |
66862306a36Sopenharmony_ci		VCR_V_WAIT_2(var->upper_margin),
66962306a36Sopenharmony_ci		fbi->regs + LCDC_VCR);
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	writel(SIZE_XMAX(var->xres) | (var->yres & ymax_mask),
67262306a36Sopenharmony_ci			fbi->regs + LCDC_SIZE);
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	writel(fbi->pcr, fbi->regs + LCDC_PCR);
67562306a36Sopenharmony_ci	if (fbi->pwmr)
67662306a36Sopenharmony_ci		writel(fbi->pwmr, fbi->regs + LCDC_PWMR);
67762306a36Sopenharmony_ci	writel(fbi->lscr1, fbi->regs + LCDC_LSCR1);
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	/* dmacr = 0 is no valid value, as we need DMA control marks. */
68062306a36Sopenharmony_ci	if (fbi->dmacr)
68162306a36Sopenharmony_ci		writel(fbi->dmacr, fbi->regs + LCDC_DMACR);
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	if (fbi->lauscr)
68462306a36Sopenharmony_ci		writel(fbi->lauscr, fbi->regs + LCDC_LAUSCR);
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	return 0;
68762306a36Sopenharmony_ci}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_cistatic int imxfb_init_fbinfo(struct platform_device *pdev)
69062306a36Sopenharmony_ci{
69162306a36Sopenharmony_ci	struct fb_info *info = platform_get_drvdata(pdev);
69262306a36Sopenharmony_ci	struct imxfb_info *fbi = info->par;
69362306a36Sopenharmony_ci	struct device_node *np;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	pr_debug("%s\n",__func__);
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	info->pseudo_palette = devm_kmalloc_array(&pdev->dev, 16,
69862306a36Sopenharmony_ci						  sizeof(u32), GFP_KERNEL);
69962306a36Sopenharmony_ci	if (!info->pseudo_palette)
70062306a36Sopenharmony_ci		return -ENOMEM;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	memset(fbi, 0, sizeof(struct imxfb_info));
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	fbi->devtype = pdev->id_entry->driver_data;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	strscpy(info->fix.id, IMX_NAME, sizeof(info->fix.id));
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	info->fix.type			= FB_TYPE_PACKED_PIXELS;
70962306a36Sopenharmony_ci	info->fix.type_aux		= 0;
71062306a36Sopenharmony_ci	info->fix.xpanstep		= 0;
71162306a36Sopenharmony_ci	info->fix.ypanstep		= 0;
71262306a36Sopenharmony_ci	info->fix.ywrapstep		= 0;
71362306a36Sopenharmony_ci	info->fix.accel			= FB_ACCEL_NONE;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	info->var.nonstd		= 0;
71662306a36Sopenharmony_ci	info->var.activate		= FB_ACTIVATE_NOW;
71762306a36Sopenharmony_ci	info->var.height		= -1;
71862306a36Sopenharmony_ci	info->var.width	= -1;
71962306a36Sopenharmony_ci	info->var.accel_flags		= 0;
72062306a36Sopenharmony_ci	info->var.vmode			= FB_VMODE_NONINTERLACED;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	info->fbops			= &imxfb_ops;
72362306a36Sopenharmony_ci	info->flags			= FBINFO_READS_FAST;
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	np = pdev->dev.of_node;
72662306a36Sopenharmony_ci	info->var.grayscale = of_property_read_bool(np,
72762306a36Sopenharmony_ci					"cmap-greyscale");
72862306a36Sopenharmony_ci	fbi->cmap_inverse = of_property_read_bool(np, "cmap-inverse");
72962306a36Sopenharmony_ci	fbi->cmap_static = of_property_read_bool(np, "cmap-static");
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	fbi->lscr1 = IMXFB_LSCR1_DEFAULT;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	of_property_read_u32(np, "fsl,lpccr", &fbi->pwmr);
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	of_property_read_u32(np, "fsl,lscr1", &fbi->lscr1);
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	of_property_read_u32(np, "fsl,dmacr", &fbi->dmacr);
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	return 0;
74062306a36Sopenharmony_ci}
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_cistatic int imxfb_of_read_mode(struct device *dev, struct device_node *np,
74362306a36Sopenharmony_ci		struct imx_fb_videomode *imxfb_mode)
74462306a36Sopenharmony_ci{
74562306a36Sopenharmony_ci	int ret;
74662306a36Sopenharmony_ci	struct fb_videomode *of_mode = &imxfb_mode->mode;
74762306a36Sopenharmony_ci	u32 bpp;
74862306a36Sopenharmony_ci	u32 pcr;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	ret = of_property_read_string(np, "model", &of_mode->name);
75162306a36Sopenharmony_ci	if (ret)
75262306a36Sopenharmony_ci		of_mode->name = NULL;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	ret = of_get_fb_videomode(np, of_mode, OF_USE_NATIVE_MODE);
75562306a36Sopenharmony_ci	if (ret) {
75662306a36Sopenharmony_ci		dev_err(dev, "Failed to get videomode from DT\n");
75762306a36Sopenharmony_ci		return ret;
75862306a36Sopenharmony_ci	}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	ret = of_property_read_u32(np, "bits-per-pixel", &bpp);
76162306a36Sopenharmony_ci	ret |= of_property_read_u32(np, "fsl,pcr", &pcr);
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	if (ret) {
76462306a36Sopenharmony_ci		dev_err(dev, "Failed to read bpp and pcr from DT\n");
76562306a36Sopenharmony_ci		return -EINVAL;
76662306a36Sopenharmony_ci	}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	if (bpp < 1 || bpp > 255) {
76962306a36Sopenharmony_ci		dev_err(dev, "Bits per pixel have to be between 1 and 255\n");
77062306a36Sopenharmony_ci		return -EINVAL;
77162306a36Sopenharmony_ci	}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	imxfb_mode->bpp = bpp;
77462306a36Sopenharmony_ci	imxfb_mode->pcr = pcr;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	/*
77762306a36Sopenharmony_ci	 * fsl,aus-mode is optional
77862306a36Sopenharmony_ci	 */
77962306a36Sopenharmony_ci	imxfb_mode->aus_mode = of_property_read_bool(np, "fsl,aus-mode");
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	return 0;
78262306a36Sopenharmony_ci}
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_cistatic int imxfb_lcd_check_fb(struct lcd_device *lcddev, struct fb_info *fi)
78562306a36Sopenharmony_ci{
78662306a36Sopenharmony_ci	struct imxfb_info *fbi = dev_get_drvdata(&lcddev->dev);
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	if (!fi || fi->par == fbi)
78962306a36Sopenharmony_ci		return 1;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	return 0;
79262306a36Sopenharmony_ci}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_cistatic int imxfb_lcd_get_contrast(struct lcd_device *lcddev)
79562306a36Sopenharmony_ci{
79662306a36Sopenharmony_ci	struct imxfb_info *fbi = dev_get_drvdata(&lcddev->dev);
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	return fbi->pwmr & 0xff;
79962306a36Sopenharmony_ci}
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_cistatic int imxfb_lcd_set_contrast(struct lcd_device *lcddev, int contrast)
80262306a36Sopenharmony_ci{
80362306a36Sopenharmony_ci	struct imxfb_info *fbi = dev_get_drvdata(&lcddev->dev);
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	if (fbi->pwmr && fbi->enabled) {
80662306a36Sopenharmony_ci		if (contrast > 255)
80762306a36Sopenharmony_ci			contrast = 255;
80862306a36Sopenharmony_ci		else if (contrast < 0)
80962306a36Sopenharmony_ci			contrast = 0;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci		fbi->pwmr &= ~0xff;
81262306a36Sopenharmony_ci		fbi->pwmr |= contrast;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci		writel(fbi->pwmr, fbi->regs + LCDC_PWMR);
81562306a36Sopenharmony_ci	}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	return 0;
81862306a36Sopenharmony_ci}
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_cistatic int imxfb_lcd_get_power(struct lcd_device *lcddev)
82162306a36Sopenharmony_ci{
82262306a36Sopenharmony_ci	struct imxfb_info *fbi = dev_get_drvdata(&lcddev->dev);
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	if (!IS_ERR(fbi->lcd_pwr) &&
82562306a36Sopenharmony_ci	    !regulator_is_enabled(fbi->lcd_pwr))
82662306a36Sopenharmony_ci		return FB_BLANK_POWERDOWN;
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	return FB_BLANK_UNBLANK;
82962306a36Sopenharmony_ci}
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_cistatic int imxfb_regulator_set(struct imxfb_info *fbi, int enable)
83262306a36Sopenharmony_ci{
83362306a36Sopenharmony_ci	int ret;
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	if (enable == fbi->lcd_pwr_enabled)
83662306a36Sopenharmony_ci		return 0;
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	if (enable)
83962306a36Sopenharmony_ci		ret = regulator_enable(fbi->lcd_pwr);
84062306a36Sopenharmony_ci	else
84162306a36Sopenharmony_ci		ret = regulator_disable(fbi->lcd_pwr);
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	if (ret == 0)
84462306a36Sopenharmony_ci		fbi->lcd_pwr_enabled = enable;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	return ret;
84762306a36Sopenharmony_ci}
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_cistatic int imxfb_lcd_set_power(struct lcd_device *lcddev, int power)
85062306a36Sopenharmony_ci{
85162306a36Sopenharmony_ci	struct imxfb_info *fbi = dev_get_drvdata(&lcddev->dev);
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	if (!IS_ERR(fbi->lcd_pwr))
85462306a36Sopenharmony_ci		return imxfb_regulator_set(fbi, power == FB_BLANK_UNBLANK);
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	return 0;
85762306a36Sopenharmony_ci}
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_cistatic struct lcd_ops imxfb_lcd_ops = {
86062306a36Sopenharmony_ci	.check_fb	= imxfb_lcd_check_fb,
86162306a36Sopenharmony_ci	.get_contrast	= imxfb_lcd_get_contrast,
86262306a36Sopenharmony_ci	.set_contrast	= imxfb_lcd_set_contrast,
86362306a36Sopenharmony_ci	.get_power	= imxfb_lcd_get_power,
86462306a36Sopenharmony_ci	.set_power	= imxfb_lcd_set_power,
86562306a36Sopenharmony_ci};
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_cistatic int imxfb_setup(void)
86862306a36Sopenharmony_ci{
86962306a36Sopenharmony_ci	char *opt, *options = NULL;
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	if (fb_get_options("imxfb", &options))
87262306a36Sopenharmony_ci		return -ENODEV;
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	if (!options || !*options)
87562306a36Sopenharmony_ci		return 0;
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	while ((opt = strsep(&options, ",")) != NULL) {
87862306a36Sopenharmony_ci		if (!*opt)
87962306a36Sopenharmony_ci			continue;
88062306a36Sopenharmony_ci		else
88162306a36Sopenharmony_ci			fb_mode = opt;
88262306a36Sopenharmony_ci	}
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	return 0;
88562306a36Sopenharmony_ci}
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_cistatic int imxfb_probe(struct platform_device *pdev)
88862306a36Sopenharmony_ci{
88962306a36Sopenharmony_ci	struct imxfb_info *fbi;
89062306a36Sopenharmony_ci	struct lcd_device *lcd;
89162306a36Sopenharmony_ci	struct fb_info *info;
89262306a36Sopenharmony_ci	struct imx_fb_videomode *m;
89362306a36Sopenharmony_ci	const struct of_device_id *of_id;
89462306a36Sopenharmony_ci	struct device_node *display_np;
89562306a36Sopenharmony_ci	int ret, i;
89662306a36Sopenharmony_ci	int bytes_per_pixel;
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	dev_info(&pdev->dev, "i.MX Framebuffer driver\n");
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	ret = imxfb_setup();
90162306a36Sopenharmony_ci	if (ret < 0)
90262306a36Sopenharmony_ci		return ret;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	of_id = of_match_device(imxfb_of_dev_id, &pdev->dev);
90562306a36Sopenharmony_ci	if (of_id)
90662306a36Sopenharmony_ci		pdev->id_entry = of_id->data;
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	info = framebuffer_alloc(sizeof(struct imxfb_info), &pdev->dev);
90962306a36Sopenharmony_ci	if (!info)
91062306a36Sopenharmony_ci		return -ENOMEM;
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	fbi = info->par;
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	platform_set_drvdata(pdev, info);
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	ret = imxfb_init_fbinfo(pdev);
91762306a36Sopenharmony_ci	if (ret < 0)
91862306a36Sopenharmony_ci		goto failed_init;
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	fb_mode = NULL;
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	display_np = of_parse_phandle(pdev->dev.of_node, "display", 0);
92362306a36Sopenharmony_ci	if (!display_np) {
92462306a36Sopenharmony_ci		dev_err(&pdev->dev, "No display defined in devicetree\n");
92562306a36Sopenharmony_ci		ret = -EINVAL;
92662306a36Sopenharmony_ci		goto failed_init;
92762306a36Sopenharmony_ci	}
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	/*
93062306a36Sopenharmony_ci	 * imxfb does not support more modes, we choose only the native
93162306a36Sopenharmony_ci	 * mode.
93262306a36Sopenharmony_ci	 */
93362306a36Sopenharmony_ci	fbi->num_modes = 1;
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	fbi->mode = devm_kzalloc(&pdev->dev,
93662306a36Sopenharmony_ci			sizeof(struct imx_fb_videomode), GFP_KERNEL);
93762306a36Sopenharmony_ci	if (!fbi->mode) {
93862306a36Sopenharmony_ci		ret = -ENOMEM;
93962306a36Sopenharmony_ci		of_node_put(display_np);
94062306a36Sopenharmony_ci		goto failed_init;
94162306a36Sopenharmony_ci	}
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	ret = imxfb_of_read_mode(&pdev->dev, display_np, fbi->mode);
94462306a36Sopenharmony_ci	of_node_put(display_np);
94562306a36Sopenharmony_ci	if (ret)
94662306a36Sopenharmony_ci		goto failed_init;
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	/* Calculate maximum bytes used per pixel. In most cases this should
94962306a36Sopenharmony_ci	 * be the same as m->bpp/8 */
95062306a36Sopenharmony_ci	m = &fbi->mode[0];
95162306a36Sopenharmony_ci	bytes_per_pixel = (m->bpp + 7) / 8;
95262306a36Sopenharmony_ci	for (i = 0; i < fbi->num_modes; i++, m++)
95362306a36Sopenharmony_ci		info->fix.smem_len = max_t(size_t, info->fix.smem_len,
95462306a36Sopenharmony_ci				m->mode.xres * m->mode.yres * bytes_per_pixel);
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	fbi->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
95762306a36Sopenharmony_ci	if (IS_ERR(fbi->clk_ipg)) {
95862306a36Sopenharmony_ci		ret = PTR_ERR(fbi->clk_ipg);
95962306a36Sopenharmony_ci		goto failed_init;
96062306a36Sopenharmony_ci	}
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	/*
96362306a36Sopenharmony_ci	 * The LCDC controller does not have an enable bit. The
96462306a36Sopenharmony_ci	 * controller starts directly when the clocks are enabled.
96562306a36Sopenharmony_ci	 * If the clocks are enabled when the controller is not yet
96662306a36Sopenharmony_ci	 * programmed with proper register values (enabled at the
96762306a36Sopenharmony_ci	 * bootloader, for example) then it just goes into some undefined
96862306a36Sopenharmony_ci	 * state.
96962306a36Sopenharmony_ci	 * To avoid this issue, let's enable and disable LCDC IPG clock
97062306a36Sopenharmony_ci	 * so that we force some kind of 'reset' to the LCDC block.
97162306a36Sopenharmony_ci	 */
97262306a36Sopenharmony_ci	ret = clk_prepare_enable(fbi->clk_ipg);
97362306a36Sopenharmony_ci	if (ret)
97462306a36Sopenharmony_ci		goto failed_init;
97562306a36Sopenharmony_ci	clk_disable_unprepare(fbi->clk_ipg);
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	fbi->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
97862306a36Sopenharmony_ci	if (IS_ERR(fbi->clk_ahb)) {
97962306a36Sopenharmony_ci		ret = PTR_ERR(fbi->clk_ahb);
98062306a36Sopenharmony_ci		goto failed_init;
98162306a36Sopenharmony_ci	}
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	fbi->clk_per = devm_clk_get(&pdev->dev, "per");
98462306a36Sopenharmony_ci	if (IS_ERR(fbi->clk_per)) {
98562306a36Sopenharmony_ci		ret = PTR_ERR(fbi->clk_per);
98662306a36Sopenharmony_ci		goto failed_init;
98762306a36Sopenharmony_ci	}
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	fbi->regs = devm_platform_ioremap_resource(pdev, 0);
99062306a36Sopenharmony_ci	if (IS_ERR(fbi->regs)) {
99162306a36Sopenharmony_ci		ret = PTR_ERR(fbi->regs);
99262306a36Sopenharmony_ci		goto failed_init;
99362306a36Sopenharmony_ci	}
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	fbi->map_size = PAGE_ALIGN(info->fix.smem_len);
99662306a36Sopenharmony_ci	info->screen_buffer = dma_alloc_wc(&pdev->dev, fbi->map_size,
99762306a36Sopenharmony_ci					   &fbi->map_dma, GFP_KERNEL);
99862306a36Sopenharmony_ci	if (!info->screen_buffer) {
99962306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to allocate video RAM\n");
100062306a36Sopenharmony_ci		ret = -ENOMEM;
100162306a36Sopenharmony_ci		goto failed_init;
100262306a36Sopenharmony_ci	}
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	info->fix.smem_start = fbi->map_dma;
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	INIT_LIST_HEAD(&info->modelist);
100762306a36Sopenharmony_ci	for (i = 0; i < fbi->num_modes; i++)
100862306a36Sopenharmony_ci		fb_add_videomode(&fbi->mode[i].mode, &info->modelist);
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	/*
101162306a36Sopenharmony_ci	 * This makes sure that our colour bitfield
101262306a36Sopenharmony_ci	 * descriptors are correctly initialised.
101362306a36Sopenharmony_ci	 */
101462306a36Sopenharmony_ci	imxfb_check_var(&info->var, info);
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	/*
101762306a36Sopenharmony_ci	 * For modes > 8bpp, the color map is bypassed.
101862306a36Sopenharmony_ci	 * Therefore, 256 entries are enough.
101962306a36Sopenharmony_ci	 */
102062306a36Sopenharmony_ci	ret = fb_alloc_cmap(&info->cmap, 256, 0);
102162306a36Sopenharmony_ci	if (ret < 0)
102262306a36Sopenharmony_ci		goto failed_cmap;
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	imxfb_set_par(info);
102562306a36Sopenharmony_ci	ret = register_framebuffer(info);
102662306a36Sopenharmony_ci	if (ret < 0) {
102762306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to register framebuffer\n");
102862306a36Sopenharmony_ci		goto failed_register;
102962306a36Sopenharmony_ci	}
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	fbi->lcd_pwr = devm_regulator_get(&pdev->dev, "lcd");
103262306a36Sopenharmony_ci	if (PTR_ERR(fbi->lcd_pwr) == -EPROBE_DEFER) {
103362306a36Sopenharmony_ci		ret = -EPROBE_DEFER;
103462306a36Sopenharmony_ci		goto failed_lcd;
103562306a36Sopenharmony_ci	}
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	lcd = devm_lcd_device_register(&pdev->dev, "imxfb-lcd", &pdev->dev, fbi,
103862306a36Sopenharmony_ci				       &imxfb_lcd_ops);
103962306a36Sopenharmony_ci	if (IS_ERR(lcd)) {
104062306a36Sopenharmony_ci		ret = PTR_ERR(lcd);
104162306a36Sopenharmony_ci		goto failed_lcd;
104262306a36Sopenharmony_ci	}
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	lcd->props.max_contrast = 0xff;
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	imxfb_enable_controller(fbi);
104762306a36Sopenharmony_ci	fbi->pdev = pdev;
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	return 0;
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_cifailed_lcd:
105262306a36Sopenharmony_ci	unregister_framebuffer(info);
105362306a36Sopenharmony_cifailed_register:
105462306a36Sopenharmony_ci	fb_dealloc_cmap(&info->cmap);
105562306a36Sopenharmony_cifailed_cmap:
105662306a36Sopenharmony_ci	dma_free_wc(&pdev->dev, fbi->map_size, info->screen_buffer,
105762306a36Sopenharmony_ci		    fbi->map_dma);
105862306a36Sopenharmony_cifailed_init:
105962306a36Sopenharmony_ci	framebuffer_release(info);
106062306a36Sopenharmony_ci	return ret;
106162306a36Sopenharmony_ci}
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_cistatic void imxfb_remove(struct platform_device *pdev)
106462306a36Sopenharmony_ci{
106562306a36Sopenharmony_ci	struct fb_info *info = platform_get_drvdata(pdev);
106662306a36Sopenharmony_ci	struct imxfb_info *fbi = info->par;
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	imxfb_disable_controller(fbi);
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	unregister_framebuffer(info);
107162306a36Sopenharmony_ci	fb_dealloc_cmap(&info->cmap);
107262306a36Sopenharmony_ci	dma_free_wc(&pdev->dev, fbi->map_size, info->screen_buffer,
107362306a36Sopenharmony_ci		    fbi->map_dma);
107462306a36Sopenharmony_ci	framebuffer_release(info);
107562306a36Sopenharmony_ci}
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_cistatic int imxfb_suspend(struct device *dev)
107862306a36Sopenharmony_ci{
107962306a36Sopenharmony_ci	struct fb_info *info = dev_get_drvdata(dev);
108062306a36Sopenharmony_ci	struct imxfb_info *fbi = info->par;
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	imxfb_disable_controller(fbi);
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	return 0;
108562306a36Sopenharmony_ci}
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_cistatic int imxfb_resume(struct device *dev)
108862306a36Sopenharmony_ci{
108962306a36Sopenharmony_ci	struct fb_info *info = dev_get_drvdata(dev);
109062306a36Sopenharmony_ci	struct imxfb_info *fbi = info->par;
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	imxfb_enable_controller(fbi);
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	return 0;
109562306a36Sopenharmony_ci}
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(imxfb_pm_ops, imxfb_suspend, imxfb_resume);
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_cistatic struct platform_driver imxfb_driver = {
110062306a36Sopenharmony_ci	.driver		= {
110162306a36Sopenharmony_ci		.name	= DRIVER_NAME,
110262306a36Sopenharmony_ci		.of_match_table = imxfb_of_dev_id,
110362306a36Sopenharmony_ci		.pm	= pm_sleep_ptr(&imxfb_pm_ops),
110462306a36Sopenharmony_ci	},
110562306a36Sopenharmony_ci	.probe		= imxfb_probe,
110662306a36Sopenharmony_ci	.remove_new	= imxfb_remove,
110762306a36Sopenharmony_ci	.id_table	= imxfb_devtype,
110862306a36Sopenharmony_ci};
110962306a36Sopenharmony_cimodule_platform_driver(imxfb_driver);
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ciMODULE_DESCRIPTION("Freescale i.MX framebuffer driver");
111262306a36Sopenharmony_ciMODULE_AUTHOR("Sascha Hauer, Pengutronix");
111362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1114