162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* linux/drivers/video/sm501fb.c
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (c) 2006 Simtec Electronics
562306a36Sopenharmony_ci *	Vincent Sanders <vince@simtec.co.uk>
662306a36Sopenharmony_ci *	Ben Dooks <ben@simtec.co.uk>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Framebuffer driver for the Silicon Motion SM501
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/errno.h>
1462306a36Sopenharmony_ci#include <linux/string.h>
1562306a36Sopenharmony_ci#include <linux/mm.h>
1662306a36Sopenharmony_ci#include <linux/tty.h>
1762306a36Sopenharmony_ci#include <linux/slab.h>
1862306a36Sopenharmony_ci#include <linux/delay.h>
1962306a36Sopenharmony_ci#include <linux/fb.h>
2062306a36Sopenharmony_ci#include <linux/init.h>
2162306a36Sopenharmony_ci#include <linux/vmalloc.h>
2262306a36Sopenharmony_ci#include <linux/dma-mapping.h>
2362306a36Sopenharmony_ci#include <linux/interrupt.h>
2462306a36Sopenharmony_ci#include <linux/workqueue.h>
2562306a36Sopenharmony_ci#include <linux/wait.h>
2662306a36Sopenharmony_ci#include <linux/platform_device.h>
2762306a36Sopenharmony_ci#include <linux/clk.h>
2862306a36Sopenharmony_ci#include <linux/console.h>
2962306a36Sopenharmony_ci#include <linux/io.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#include <linux/uaccess.h>
3262306a36Sopenharmony_ci#include <asm/div64.h>
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#ifdef CONFIG_PM
3562306a36Sopenharmony_ci#include <linux/pm.h>
3662306a36Sopenharmony_ci#endif
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#include <linux/sm501.h>
3962306a36Sopenharmony_ci#include <linux/sm501-regs.h>
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#include "edid.h"
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic char *fb_mode = "640x480-16@60";
4462306a36Sopenharmony_cistatic unsigned long default_bpp = 16;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic const struct fb_videomode sm501_default_mode = {
4762306a36Sopenharmony_ci	.refresh	= 60,
4862306a36Sopenharmony_ci	.xres		= 640,
4962306a36Sopenharmony_ci	.yres		= 480,
5062306a36Sopenharmony_ci	.pixclock	= 20833,
5162306a36Sopenharmony_ci	.left_margin	= 142,
5262306a36Sopenharmony_ci	.right_margin	= 13,
5362306a36Sopenharmony_ci	.upper_margin	= 21,
5462306a36Sopenharmony_ci	.lower_margin	= 1,
5562306a36Sopenharmony_ci	.hsync_len	= 69,
5662306a36Sopenharmony_ci	.vsync_len	= 3,
5762306a36Sopenharmony_ci	.sync		= FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
5862306a36Sopenharmony_ci	.vmode		= FB_VMODE_NONINTERLACED
5962306a36Sopenharmony_ci};
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci#define NR_PALETTE	256
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cienum sm501_controller {
6462306a36Sopenharmony_ci	HEAD_CRT	= 0,
6562306a36Sopenharmony_ci	HEAD_PANEL	= 1,
6662306a36Sopenharmony_ci};
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci/* SM501 memory address.
6962306a36Sopenharmony_ci *
7062306a36Sopenharmony_ci * This structure is used to track memory usage within the SM501 framebuffer
7162306a36Sopenharmony_ci * allocation. The sm_addr field is stored as an offset as it is often used
7262306a36Sopenharmony_ci * against both the physical and mapped addresses.
7362306a36Sopenharmony_ci */
7462306a36Sopenharmony_cistruct sm501_mem {
7562306a36Sopenharmony_ci	unsigned long	 size;
7662306a36Sopenharmony_ci	unsigned long	 sm_addr;	/* offset from base of sm501 fb. */
7762306a36Sopenharmony_ci	void __iomem	*k_addr;
7862306a36Sopenharmony_ci};
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci/* private data that is shared between all frambuffers* */
8162306a36Sopenharmony_cistruct sm501fb_info {
8262306a36Sopenharmony_ci	struct device		*dev;
8362306a36Sopenharmony_ci	struct fb_info		*fb[2];		/* fb info for both heads */
8462306a36Sopenharmony_ci	struct resource		*fbmem_res;	/* framebuffer resource */
8562306a36Sopenharmony_ci	struct resource		*regs_res;	/* registers resource */
8662306a36Sopenharmony_ci	struct resource		*regs2d_res;	/* 2d registers resource */
8762306a36Sopenharmony_ci	struct sm501_platdata_fb *pdata;	/* our platform data */
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	unsigned long		 pm_crt_ctrl;	/* pm: crt ctrl save */
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	int			 irq;
9262306a36Sopenharmony_ci	int			 swap_endian;	/* set to swap rgb=>bgr */
9362306a36Sopenharmony_ci	void __iomem		*regs;		/* remapped registers */
9462306a36Sopenharmony_ci	void __iomem		*regs2d;	/* 2d remapped registers */
9562306a36Sopenharmony_ci	void __iomem		*fbmem;		/* remapped framebuffer */
9662306a36Sopenharmony_ci	size_t			 fbmem_len;	/* length of remapped region */
9762306a36Sopenharmony_ci	u8 *edid_data;
9862306a36Sopenharmony_ci};
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci/* per-framebuffer private data */
10162306a36Sopenharmony_cistruct sm501fb_par {
10262306a36Sopenharmony_ci	u32			 pseudo_palette[16];
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	enum sm501_controller	 head;
10562306a36Sopenharmony_ci	struct sm501_mem	 cursor;
10662306a36Sopenharmony_ci	struct sm501_mem	 screen;
10762306a36Sopenharmony_ci	struct fb_ops		 ops;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	void			*store_fb;
11062306a36Sopenharmony_ci	void			*store_cursor;
11162306a36Sopenharmony_ci	void __iomem		*cursor_regs;
11262306a36Sopenharmony_ci	struct sm501fb_info	*info;
11362306a36Sopenharmony_ci};
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci/* Helper functions */
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic inline int h_total(struct fb_var_screeninfo *var)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	return var->xres + var->left_margin +
12062306a36Sopenharmony_ci		var->right_margin + var->hsync_len;
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic inline int v_total(struct fb_var_screeninfo *var)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	return var->yres + var->upper_margin +
12662306a36Sopenharmony_ci		var->lower_margin + var->vsync_len;
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci/* sm501fb_sync_regs()
13062306a36Sopenharmony_ci *
13162306a36Sopenharmony_ci * This call is mainly for PCI bus systems where we need to
13262306a36Sopenharmony_ci * ensure that any writes to the bus are completed before the
13362306a36Sopenharmony_ci * next phase, or after completing a function.
13462306a36Sopenharmony_ci*/
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic inline void sm501fb_sync_regs(struct sm501fb_info *info)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	smc501_readl(info->regs);
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci/* sm501_alloc_mem
14262306a36Sopenharmony_ci *
14362306a36Sopenharmony_ci * This is an attempt to lay out memory for the two framebuffers and
14462306a36Sopenharmony_ci * everything else
14562306a36Sopenharmony_ci *
14662306a36Sopenharmony_ci * |fbmem_res->start					       fbmem_res->end|
14762306a36Sopenharmony_ci * |									     |
14862306a36Sopenharmony_ci * |fb[0].fix.smem_start    |	      |fb[1].fix.smem_start    |     2K	     |
14962306a36Sopenharmony_ci * |-> fb[0].fix.smem_len <-| spare   |-> fb[1].fix.smem_len <-|-> cursors <-|
15062306a36Sopenharmony_ci *
15162306a36Sopenharmony_ci * The "spare" space is for the 2d engine data
15262306a36Sopenharmony_ci * the fixed is space for the cursors (2x1Kbyte)
15362306a36Sopenharmony_ci *
15462306a36Sopenharmony_ci * we need to allocate memory for the 2D acceleration engine
15562306a36Sopenharmony_ci * command list and the data for the engine to deal with.
15662306a36Sopenharmony_ci *
15762306a36Sopenharmony_ci * - all allocations must be 128bit aligned
15862306a36Sopenharmony_ci * - cursors are 64x64x2 bits (1Kbyte)
15962306a36Sopenharmony_ci *
16062306a36Sopenharmony_ci */
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci#define SM501_MEMF_CURSOR		(1)
16362306a36Sopenharmony_ci#define SM501_MEMF_PANEL		(2)
16462306a36Sopenharmony_ci#define SM501_MEMF_CRT			(4)
16562306a36Sopenharmony_ci#define SM501_MEMF_ACCEL		(8)
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistatic int sm501_alloc_mem(struct sm501fb_info *inf, struct sm501_mem *mem,
16862306a36Sopenharmony_ci			   unsigned int why, size_t size, u32 smem_len)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	struct sm501fb_par *par;
17162306a36Sopenharmony_ci	struct fb_info *fbi;
17262306a36Sopenharmony_ci	unsigned int ptr;
17362306a36Sopenharmony_ci	unsigned int end;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	switch (why) {
17662306a36Sopenharmony_ci	case SM501_MEMF_CURSOR:
17762306a36Sopenharmony_ci		ptr = inf->fbmem_len - size;
17862306a36Sopenharmony_ci		inf->fbmem_len = ptr;	/* adjust available memory. */
17962306a36Sopenharmony_ci		break;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	case SM501_MEMF_PANEL:
18262306a36Sopenharmony_ci		if (size > inf->fbmem_len)
18362306a36Sopenharmony_ci			return -ENOMEM;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci		ptr = inf->fbmem_len - size;
18662306a36Sopenharmony_ci		fbi = inf->fb[HEAD_CRT];
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci		/* round down, some programs such as directfb do not draw
18962306a36Sopenharmony_ci		 * 0,0 correctly unless the start is aligned to a page start.
19062306a36Sopenharmony_ci		 */
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci		if (ptr > 0)
19362306a36Sopenharmony_ci			ptr &= ~(PAGE_SIZE - 1);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci		if (fbi && ptr < smem_len)
19662306a36Sopenharmony_ci			return -ENOMEM;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci		break;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	case SM501_MEMF_CRT:
20162306a36Sopenharmony_ci		ptr = 0;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci		/* check to see if we have panel memory allocated
20462306a36Sopenharmony_ci		 * which would put an limit on available memory. */
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci		fbi = inf->fb[HEAD_PANEL];
20762306a36Sopenharmony_ci		if (fbi) {
20862306a36Sopenharmony_ci			par = fbi->par;
20962306a36Sopenharmony_ci			end = par->screen.k_addr ? par->screen.sm_addr : inf->fbmem_len;
21062306a36Sopenharmony_ci		} else
21162306a36Sopenharmony_ci			end = inf->fbmem_len;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci		if ((ptr + size) > end)
21462306a36Sopenharmony_ci			return -ENOMEM;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci		break;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	case SM501_MEMF_ACCEL:
21962306a36Sopenharmony_ci		fbi = inf->fb[HEAD_CRT];
22062306a36Sopenharmony_ci		ptr = fbi ? smem_len : 0;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci		fbi = inf->fb[HEAD_PANEL];
22362306a36Sopenharmony_ci		if (fbi) {
22462306a36Sopenharmony_ci			par = fbi->par;
22562306a36Sopenharmony_ci			end = par->screen.sm_addr;
22662306a36Sopenharmony_ci		} else
22762306a36Sopenharmony_ci			end = inf->fbmem_len;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci		if ((ptr + size) > end)
23062306a36Sopenharmony_ci			return -ENOMEM;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci		break;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	default:
23562306a36Sopenharmony_ci		return -EINVAL;
23662306a36Sopenharmony_ci	}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	mem->size    = size;
23962306a36Sopenharmony_ci	mem->sm_addr = ptr;
24062306a36Sopenharmony_ci	mem->k_addr  = inf->fbmem + ptr;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	dev_dbg(inf->dev, "%s: result %08lx, %p - %u, %zd\n",
24362306a36Sopenharmony_ci		__func__, mem->sm_addr, mem->k_addr, why, size);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	return 0;
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci/* sm501fb_ps_to_hz
24962306a36Sopenharmony_ci *
25062306a36Sopenharmony_ci * Converts a period in picoseconds to Hz.
25162306a36Sopenharmony_ci *
25262306a36Sopenharmony_ci * Note, we try to keep this in Hz to minimise rounding with
25362306a36Sopenharmony_ci * the limited PLL settings on the SM501.
25462306a36Sopenharmony_ci*/
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_cistatic unsigned long sm501fb_ps_to_hz(unsigned long psvalue)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	unsigned long long numerator=1000000000000ULL;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	/* 10^12 / picosecond period gives frequency in Hz */
26162306a36Sopenharmony_ci	do_div(numerator, psvalue);
26262306a36Sopenharmony_ci	return (unsigned long)numerator;
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci/* sm501fb_hz_to_ps is identical to the opposite transform */
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci#define sm501fb_hz_to_ps(x) sm501fb_ps_to_hz(x)
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci/* sm501fb_setup_gamma
27062306a36Sopenharmony_ci *
27162306a36Sopenharmony_ci * Programs a linear 1.0 gamma ramp in case the gamma
27262306a36Sopenharmony_ci * correction is enabled without programming anything else.
27362306a36Sopenharmony_ci*/
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cistatic void sm501fb_setup_gamma(struct sm501fb_info *fbi,
27662306a36Sopenharmony_ci				unsigned long palette)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	unsigned long value = 0;
27962306a36Sopenharmony_ci	int offset;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	/* set gamma values */
28262306a36Sopenharmony_ci	for (offset = 0; offset < 256 * 4; offset += 4) {
28362306a36Sopenharmony_ci		smc501_writel(value, fbi->regs + palette + offset);
28462306a36Sopenharmony_ci		value += 0x010101; 	/* Advance RGB by 1,1,1.*/
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci/* sm501fb_check_var
28962306a36Sopenharmony_ci *
29062306a36Sopenharmony_ci * check common variables for both panel and crt
29162306a36Sopenharmony_ci*/
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic int sm501fb_check_var(struct fb_var_screeninfo *var,
29462306a36Sopenharmony_ci			     struct fb_info *info)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	struct sm501fb_par  *par = info->par;
29762306a36Sopenharmony_ci	struct sm501fb_info *sm  = par->info;
29862306a36Sopenharmony_ci	unsigned long tmp;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	/* check we can fit these values into the registers */
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	if (var->hsync_len > 255 || var->vsync_len > 63)
30362306a36Sopenharmony_ci		return -EINVAL;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	/* hdisplay end and hsync start */
30662306a36Sopenharmony_ci	if ((var->xres + var->right_margin) > 4096)
30762306a36Sopenharmony_ci		return -EINVAL;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	/* vdisplay end and vsync start */
31062306a36Sopenharmony_ci	if ((var->yres + var->lower_margin) > 2048)
31162306a36Sopenharmony_ci		return -EINVAL;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	/* hard limits of device */
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	if (h_total(var) > 4096 || v_total(var) > 2048)
31662306a36Sopenharmony_ci		return -EINVAL;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	/* check our line length is going to be 128 bit aligned */
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	tmp = (var->xres * var->bits_per_pixel) / 8;
32162306a36Sopenharmony_ci	if ((tmp & 15) != 0)
32262306a36Sopenharmony_ci		return -EINVAL;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	/* check the virtual size */
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	if (var->xres_virtual > 4096 || var->yres_virtual > 2048)
32762306a36Sopenharmony_ci		return -EINVAL;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	/* can cope with 8,16 or 32bpp */
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	if (var->bits_per_pixel <= 8)
33262306a36Sopenharmony_ci		var->bits_per_pixel = 8;
33362306a36Sopenharmony_ci	else if (var->bits_per_pixel <= 16)
33462306a36Sopenharmony_ci		var->bits_per_pixel = 16;
33562306a36Sopenharmony_ci	else if (var->bits_per_pixel == 24)
33662306a36Sopenharmony_ci		var->bits_per_pixel = 32;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	/* set r/g/b positions and validate bpp */
33962306a36Sopenharmony_ci	switch(var->bits_per_pixel) {
34062306a36Sopenharmony_ci	case 8:
34162306a36Sopenharmony_ci		var->red.length		= var->bits_per_pixel;
34262306a36Sopenharmony_ci		var->red.offset		= 0;
34362306a36Sopenharmony_ci		var->green.length	= var->bits_per_pixel;
34462306a36Sopenharmony_ci		var->green.offset	= 0;
34562306a36Sopenharmony_ci		var->blue.length	= var->bits_per_pixel;
34662306a36Sopenharmony_ci		var->blue.offset	= 0;
34762306a36Sopenharmony_ci		var->transp.length	= 0;
34862306a36Sopenharmony_ci		var->transp.offset	= 0;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci		break;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	case 16:
35362306a36Sopenharmony_ci		if (sm->pdata->flags & SM501_FBPD_SWAP_FB_ENDIAN) {
35462306a36Sopenharmony_ci			var->blue.offset	= 11;
35562306a36Sopenharmony_ci			var->green.offset	= 5;
35662306a36Sopenharmony_ci			var->red.offset		= 0;
35762306a36Sopenharmony_ci		} else {
35862306a36Sopenharmony_ci			var->red.offset		= 11;
35962306a36Sopenharmony_ci			var->green.offset	= 5;
36062306a36Sopenharmony_ci			var->blue.offset	= 0;
36162306a36Sopenharmony_ci		}
36262306a36Sopenharmony_ci		var->transp.offset	= 0;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci		var->red.length		= 5;
36562306a36Sopenharmony_ci		var->green.length	= 6;
36662306a36Sopenharmony_ci		var->blue.length	= 5;
36762306a36Sopenharmony_ci		var->transp.length	= 0;
36862306a36Sopenharmony_ci		break;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	case 32:
37162306a36Sopenharmony_ci		if (sm->pdata->flags & SM501_FBPD_SWAP_FB_ENDIAN) {
37262306a36Sopenharmony_ci			var->transp.offset	= 0;
37362306a36Sopenharmony_ci			var->red.offset		= 8;
37462306a36Sopenharmony_ci			var->green.offset	= 16;
37562306a36Sopenharmony_ci			var->blue.offset	= 24;
37662306a36Sopenharmony_ci		} else {
37762306a36Sopenharmony_ci			var->transp.offset	= 24;
37862306a36Sopenharmony_ci			var->red.offset		= 16;
37962306a36Sopenharmony_ci			var->green.offset	= 8;
38062306a36Sopenharmony_ci			var->blue.offset	= 0;
38162306a36Sopenharmony_ci		}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci		var->red.length		= 8;
38462306a36Sopenharmony_ci		var->green.length	= 8;
38562306a36Sopenharmony_ci		var->blue.length	= 8;
38662306a36Sopenharmony_ci		var->transp.length	= 0;
38762306a36Sopenharmony_ci		break;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	default:
39062306a36Sopenharmony_ci		return -EINVAL;
39162306a36Sopenharmony_ci	}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	return 0;
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci/*
39762306a36Sopenharmony_ci * sm501fb_check_var_crt():
39862306a36Sopenharmony_ci *
39962306a36Sopenharmony_ci * check the parameters for the CRT head, and either bring them
40062306a36Sopenharmony_ci * back into range, or return -EINVAL.
40162306a36Sopenharmony_ci*/
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_cistatic int sm501fb_check_var_crt(struct fb_var_screeninfo *var,
40462306a36Sopenharmony_ci				 struct fb_info *info)
40562306a36Sopenharmony_ci{
40662306a36Sopenharmony_ci	return sm501fb_check_var(var, info);
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci/* sm501fb_check_var_pnl():
41062306a36Sopenharmony_ci *
41162306a36Sopenharmony_ci * check the parameters for the CRT head, and either bring them
41262306a36Sopenharmony_ci * back into range, or return -EINVAL.
41362306a36Sopenharmony_ci*/
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_cistatic int sm501fb_check_var_pnl(struct fb_var_screeninfo *var,
41662306a36Sopenharmony_ci				 struct fb_info *info)
41762306a36Sopenharmony_ci{
41862306a36Sopenharmony_ci	return sm501fb_check_var(var, info);
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci/* sm501fb_set_par_common
42262306a36Sopenharmony_ci *
42362306a36Sopenharmony_ci * set common registers for framebuffers
42462306a36Sopenharmony_ci*/
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_cistatic int sm501fb_set_par_common(struct fb_info *info,
42762306a36Sopenharmony_ci				  struct fb_var_screeninfo *var)
42862306a36Sopenharmony_ci{
42962306a36Sopenharmony_ci	struct sm501fb_par  *par = info->par;
43062306a36Sopenharmony_ci	struct sm501fb_info *fbi = par->info;
43162306a36Sopenharmony_ci	unsigned long pixclock;      /* pixelclock in Hz */
43262306a36Sopenharmony_ci	unsigned long sm501pixclock; /* pixelclock the 501 can achieve in Hz */
43362306a36Sopenharmony_ci	unsigned int mem_type;
43462306a36Sopenharmony_ci	unsigned int clock_type;
43562306a36Sopenharmony_ci	unsigned int head_addr;
43662306a36Sopenharmony_ci	unsigned int smem_len;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	dev_dbg(fbi->dev, "%s: %dx%d, bpp = %d, virtual %dx%d\n",
43962306a36Sopenharmony_ci		__func__, var->xres, var->yres, var->bits_per_pixel,
44062306a36Sopenharmony_ci		var->xres_virtual, var->yres_virtual);
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	switch (par->head) {
44362306a36Sopenharmony_ci	case HEAD_CRT:
44462306a36Sopenharmony_ci		mem_type = SM501_MEMF_CRT;
44562306a36Sopenharmony_ci		clock_type = SM501_CLOCK_V2XCLK;
44662306a36Sopenharmony_ci		head_addr = SM501_DC_CRT_FB_ADDR;
44762306a36Sopenharmony_ci		break;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	case HEAD_PANEL:
45062306a36Sopenharmony_ci		mem_type = SM501_MEMF_PANEL;
45162306a36Sopenharmony_ci		clock_type = SM501_CLOCK_P2XCLK;
45262306a36Sopenharmony_ci		head_addr = SM501_DC_PANEL_FB_ADDR;
45362306a36Sopenharmony_ci		break;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	default:
45662306a36Sopenharmony_ci		mem_type = 0;		/* stop compiler warnings */
45762306a36Sopenharmony_ci		head_addr = 0;
45862306a36Sopenharmony_ci		clock_type = 0;
45962306a36Sopenharmony_ci	}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	switch (var->bits_per_pixel) {
46262306a36Sopenharmony_ci	case 8:
46362306a36Sopenharmony_ci		info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
46462306a36Sopenharmony_ci		break;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	case 16:
46762306a36Sopenharmony_ci		info->fix.visual = FB_VISUAL_TRUECOLOR;
46862306a36Sopenharmony_ci		break;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	case 32:
47162306a36Sopenharmony_ci		info->fix.visual = FB_VISUAL_TRUECOLOR;
47262306a36Sopenharmony_ci		break;
47362306a36Sopenharmony_ci	}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	/* allocate fb memory within 501 */
47662306a36Sopenharmony_ci	info->fix.line_length = (var->xres_virtual * var->bits_per_pixel)/8;
47762306a36Sopenharmony_ci	smem_len = info->fix.line_length * var->yres_virtual;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	dev_dbg(fbi->dev, "%s: line length = %u\n", __func__,
48062306a36Sopenharmony_ci		info->fix.line_length);
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	if (sm501_alloc_mem(fbi, &par->screen, mem_type, smem_len, smem_len)) {
48362306a36Sopenharmony_ci		dev_err(fbi->dev, "no memory available\n");
48462306a36Sopenharmony_ci		return -ENOMEM;
48562306a36Sopenharmony_ci	}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	mutex_lock(&info->mm_lock);
48862306a36Sopenharmony_ci	info->fix.smem_start = fbi->fbmem_res->start + par->screen.sm_addr;
48962306a36Sopenharmony_ci	info->fix.smem_len   = smem_len;
49062306a36Sopenharmony_ci	mutex_unlock(&info->mm_lock);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	info->screen_base = fbi->fbmem + par->screen.sm_addr;
49362306a36Sopenharmony_ci	info->screen_size = info->fix.smem_len;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	/* set start of framebuffer to the screen */
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	smc501_writel(par->screen.sm_addr | SM501_ADDR_FLIP,
49862306a36Sopenharmony_ci			fbi->regs + head_addr);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	/* program CRT clock  */
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	pixclock = sm501fb_ps_to_hz(var->pixclock);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	sm501pixclock = sm501_set_clock(fbi->dev->parent, clock_type,
50562306a36Sopenharmony_ci					pixclock);
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	/* update fb layer with actual clock used */
50862306a36Sopenharmony_ci	var->pixclock = sm501fb_hz_to_ps(sm501pixclock);
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	dev_dbg(fbi->dev, "%s: pixclock(ps) = %u, pixclock(Hz)  = %lu, "
51162306a36Sopenharmony_ci	       "sm501pixclock = %lu,  error = %ld%%\n",
51262306a36Sopenharmony_ci	       __func__, var->pixclock, pixclock, sm501pixclock,
51362306a36Sopenharmony_ci	       ((pixclock - sm501pixclock)*100)/pixclock);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	return 0;
51662306a36Sopenharmony_ci}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci/* sm501fb_set_par_geometry
51962306a36Sopenharmony_ci *
52062306a36Sopenharmony_ci * set the geometry registers for specified framebuffer.
52162306a36Sopenharmony_ci*/
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_cistatic void sm501fb_set_par_geometry(struct fb_info *info,
52462306a36Sopenharmony_ci				     struct fb_var_screeninfo *var)
52562306a36Sopenharmony_ci{
52662306a36Sopenharmony_ci	struct sm501fb_par  *par = info->par;
52762306a36Sopenharmony_ci	struct sm501fb_info *fbi = par->info;
52862306a36Sopenharmony_ci	void __iomem *base = fbi->regs;
52962306a36Sopenharmony_ci	unsigned long reg;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	if (par->head == HEAD_CRT)
53262306a36Sopenharmony_ci		base += SM501_DC_CRT_H_TOT;
53362306a36Sopenharmony_ci	else
53462306a36Sopenharmony_ci		base += SM501_DC_PANEL_H_TOT;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	/* set framebuffer width and display width */
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	reg = info->fix.line_length;
53962306a36Sopenharmony_ci	reg |= ((var->xres * var->bits_per_pixel)/8) << 16;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	smc501_writel(reg, fbi->regs + (par->head == HEAD_CRT ?
54262306a36Sopenharmony_ci		    SM501_DC_CRT_FB_OFFSET :  SM501_DC_PANEL_FB_OFFSET));
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	/* program horizontal total */
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	reg  = (h_total(var) - 1) << 16;
54762306a36Sopenharmony_ci	reg |= (var->xres - 1);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	smc501_writel(reg, base + SM501_OFF_DC_H_TOT);
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	/* program horizontal sync */
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	reg  = var->hsync_len << 16;
55462306a36Sopenharmony_ci	reg |= var->xres + var->right_margin - 1;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	smc501_writel(reg, base + SM501_OFF_DC_H_SYNC);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	/* program vertical total */
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	reg  = (v_total(var) - 1) << 16;
56162306a36Sopenharmony_ci	reg |= (var->yres - 1);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	smc501_writel(reg, base + SM501_OFF_DC_V_TOT);
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	/* program vertical sync */
56662306a36Sopenharmony_ci	reg  = var->vsync_len << 16;
56762306a36Sopenharmony_ci	reg |= var->yres + var->lower_margin - 1;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	smc501_writel(reg, base + SM501_OFF_DC_V_SYNC);
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci/* sm501fb_pan_crt
57362306a36Sopenharmony_ci *
57462306a36Sopenharmony_ci * pan the CRT display output within an virtual framebuffer
57562306a36Sopenharmony_ci*/
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_cistatic int sm501fb_pan_crt(struct fb_var_screeninfo *var,
57862306a36Sopenharmony_ci			   struct fb_info *info)
57962306a36Sopenharmony_ci{
58062306a36Sopenharmony_ci	struct sm501fb_par  *par = info->par;
58162306a36Sopenharmony_ci	struct sm501fb_info *fbi = par->info;
58262306a36Sopenharmony_ci	unsigned int bytes_pixel = info->var.bits_per_pixel / 8;
58362306a36Sopenharmony_ci	unsigned long reg;
58462306a36Sopenharmony_ci	unsigned long xoffs;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	xoffs = var->xoffset * bytes_pixel;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	reg = smc501_readl(fbi->regs + SM501_DC_CRT_CONTROL);
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	reg &= ~SM501_DC_CRT_CONTROL_PIXEL_MASK;
59162306a36Sopenharmony_ci	reg |= ((xoffs & 15) / bytes_pixel) << 4;
59262306a36Sopenharmony_ci	smc501_writel(reg, fbi->regs + SM501_DC_CRT_CONTROL);
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	reg = (par->screen.sm_addr + xoffs +
59562306a36Sopenharmony_ci	       var->yoffset * info->fix.line_length);
59662306a36Sopenharmony_ci	smc501_writel(reg | SM501_ADDR_FLIP, fbi->regs + SM501_DC_CRT_FB_ADDR);
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	sm501fb_sync_regs(fbi);
59962306a36Sopenharmony_ci	return 0;
60062306a36Sopenharmony_ci}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci/* sm501fb_pan_pnl
60362306a36Sopenharmony_ci *
60462306a36Sopenharmony_ci * pan the panel display output within an virtual framebuffer
60562306a36Sopenharmony_ci*/
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cistatic int sm501fb_pan_pnl(struct fb_var_screeninfo *var,
60862306a36Sopenharmony_ci			   struct fb_info *info)
60962306a36Sopenharmony_ci{
61062306a36Sopenharmony_ci	struct sm501fb_par  *par = info->par;
61162306a36Sopenharmony_ci	struct sm501fb_info *fbi = par->info;
61262306a36Sopenharmony_ci	unsigned long reg;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	reg = var->xoffset | (info->var.xres_virtual << 16);
61562306a36Sopenharmony_ci	smc501_writel(reg, fbi->regs + SM501_DC_PANEL_FB_WIDTH);
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	reg = var->yoffset | (info->var.yres_virtual << 16);
61862306a36Sopenharmony_ci	smc501_writel(reg, fbi->regs + SM501_DC_PANEL_FB_HEIGHT);
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	sm501fb_sync_regs(fbi);
62162306a36Sopenharmony_ci	return 0;
62262306a36Sopenharmony_ci}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci/* sm501fb_set_par_crt
62562306a36Sopenharmony_ci *
62662306a36Sopenharmony_ci * Set the CRT video mode from the fb_info structure
62762306a36Sopenharmony_ci*/
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_cistatic int sm501fb_set_par_crt(struct fb_info *info)
63062306a36Sopenharmony_ci{
63162306a36Sopenharmony_ci	struct sm501fb_par  *par = info->par;
63262306a36Sopenharmony_ci	struct sm501fb_info *fbi = par->info;
63362306a36Sopenharmony_ci	struct fb_var_screeninfo *var = &info->var;
63462306a36Sopenharmony_ci	unsigned long control;       /* control register */
63562306a36Sopenharmony_ci	int ret;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	/* activate new configuration */
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	dev_dbg(fbi->dev, "%s(%p)\n", __func__, info);
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	/* enable CRT DAC - note 0 is on!*/
64262306a36Sopenharmony_ci	sm501_misc_control(fbi->dev->parent, 0, SM501_MISC_DAC_POWER);
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	control = smc501_readl(fbi->regs + SM501_DC_CRT_CONTROL);
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	control &= (SM501_DC_CRT_CONTROL_PIXEL_MASK |
64762306a36Sopenharmony_ci		    SM501_DC_CRT_CONTROL_GAMMA |
64862306a36Sopenharmony_ci		    SM501_DC_CRT_CONTROL_BLANK |
64962306a36Sopenharmony_ci		    SM501_DC_CRT_CONTROL_SEL |
65062306a36Sopenharmony_ci		    SM501_DC_CRT_CONTROL_CP |
65162306a36Sopenharmony_ci		    SM501_DC_CRT_CONTROL_TVP);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	/* set the sync polarities before we check data source  */
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	if ((var->sync & FB_SYNC_HOR_HIGH_ACT) == 0)
65662306a36Sopenharmony_ci		control |= SM501_DC_CRT_CONTROL_HSP;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	if ((var->sync & FB_SYNC_VERT_HIGH_ACT) == 0)
65962306a36Sopenharmony_ci		control |= SM501_DC_CRT_CONTROL_VSP;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	if ((control & SM501_DC_CRT_CONTROL_SEL) == 0) {
66262306a36Sopenharmony_ci		/* the head is displaying panel data... */
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci		sm501_alloc_mem(fbi, &par->screen, SM501_MEMF_CRT, 0,
66562306a36Sopenharmony_ci				info->fix.smem_len);
66662306a36Sopenharmony_ci		goto out_update;
66762306a36Sopenharmony_ci	}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	ret = sm501fb_set_par_common(info, var);
67062306a36Sopenharmony_ci	if (ret) {
67162306a36Sopenharmony_ci		dev_err(fbi->dev, "failed to set common parameters\n");
67262306a36Sopenharmony_ci		return ret;
67362306a36Sopenharmony_ci	}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	sm501fb_pan_crt(var, info);
67662306a36Sopenharmony_ci	sm501fb_set_par_geometry(info, var);
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	control |= SM501_FIFO_3;	/* fill if >3 free slots */
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	switch(var->bits_per_pixel) {
68162306a36Sopenharmony_ci	case 8:
68262306a36Sopenharmony_ci		control |= SM501_DC_CRT_CONTROL_8BPP;
68362306a36Sopenharmony_ci		break;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	case 16:
68662306a36Sopenharmony_ci		control |= SM501_DC_CRT_CONTROL_16BPP;
68762306a36Sopenharmony_ci		sm501fb_setup_gamma(fbi, SM501_DC_CRT_PALETTE);
68862306a36Sopenharmony_ci		break;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	case 32:
69162306a36Sopenharmony_ci		control |= SM501_DC_CRT_CONTROL_32BPP;
69262306a36Sopenharmony_ci		sm501fb_setup_gamma(fbi, SM501_DC_CRT_PALETTE);
69362306a36Sopenharmony_ci		break;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	default:
69662306a36Sopenharmony_ci		BUG();
69762306a36Sopenharmony_ci	}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	control |= SM501_DC_CRT_CONTROL_SEL;	/* CRT displays CRT data */
70062306a36Sopenharmony_ci	control |= SM501_DC_CRT_CONTROL_TE;	/* enable CRT timing */
70162306a36Sopenharmony_ci	control |= SM501_DC_CRT_CONTROL_ENABLE;	/* enable CRT plane */
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci out_update:
70462306a36Sopenharmony_ci	dev_dbg(fbi->dev, "new control is %08lx\n", control);
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	smc501_writel(control, fbi->regs + SM501_DC_CRT_CONTROL);
70762306a36Sopenharmony_ci	sm501fb_sync_regs(fbi);
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	return 0;
71062306a36Sopenharmony_ci}
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_cistatic void sm501fb_panel_power(struct sm501fb_info *fbi, int to)
71362306a36Sopenharmony_ci{
71462306a36Sopenharmony_ci	unsigned long control;
71562306a36Sopenharmony_ci	void __iomem *ctrl_reg = fbi->regs + SM501_DC_PANEL_CONTROL;
71662306a36Sopenharmony_ci	struct sm501_platdata_fbsub *pd = fbi->pdata->fb_pnl;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	control = smc501_readl(ctrl_reg);
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	if (to && (control & SM501_DC_PANEL_CONTROL_VDD) == 0) {
72162306a36Sopenharmony_ci		/* enable panel power */
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci		control |= SM501_DC_PANEL_CONTROL_VDD;	/* FPVDDEN */
72462306a36Sopenharmony_ci		smc501_writel(control, ctrl_reg);
72562306a36Sopenharmony_ci		sm501fb_sync_regs(fbi);
72662306a36Sopenharmony_ci		mdelay(10);
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci		control |= SM501_DC_PANEL_CONTROL_DATA;	/* DATA */
72962306a36Sopenharmony_ci		smc501_writel(control, ctrl_reg);
73062306a36Sopenharmony_ci		sm501fb_sync_regs(fbi);
73162306a36Sopenharmony_ci		mdelay(10);
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci		/* VBIASEN */
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci		if (!(pd->flags & SM501FB_FLAG_PANEL_NO_VBIASEN)) {
73662306a36Sopenharmony_ci			if (pd->flags & SM501FB_FLAG_PANEL_INV_VBIASEN)
73762306a36Sopenharmony_ci				control &= ~SM501_DC_PANEL_CONTROL_BIAS;
73862306a36Sopenharmony_ci			else
73962306a36Sopenharmony_ci				control |= SM501_DC_PANEL_CONTROL_BIAS;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci			smc501_writel(control, ctrl_reg);
74262306a36Sopenharmony_ci			sm501fb_sync_regs(fbi);
74362306a36Sopenharmony_ci			mdelay(10);
74462306a36Sopenharmony_ci		}
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci		if (!(pd->flags & SM501FB_FLAG_PANEL_NO_FPEN)) {
74762306a36Sopenharmony_ci			if (pd->flags & SM501FB_FLAG_PANEL_INV_FPEN)
74862306a36Sopenharmony_ci				control &= ~SM501_DC_PANEL_CONTROL_FPEN;
74962306a36Sopenharmony_ci			else
75062306a36Sopenharmony_ci				control |= SM501_DC_PANEL_CONTROL_FPEN;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci			smc501_writel(control, ctrl_reg);
75362306a36Sopenharmony_ci			sm501fb_sync_regs(fbi);
75462306a36Sopenharmony_ci			mdelay(10);
75562306a36Sopenharmony_ci		}
75662306a36Sopenharmony_ci	} else if (!to && (control & SM501_DC_PANEL_CONTROL_VDD) != 0) {
75762306a36Sopenharmony_ci		/* disable panel power */
75862306a36Sopenharmony_ci		if (!(pd->flags & SM501FB_FLAG_PANEL_NO_FPEN)) {
75962306a36Sopenharmony_ci			if (pd->flags & SM501FB_FLAG_PANEL_INV_FPEN)
76062306a36Sopenharmony_ci				control |= SM501_DC_PANEL_CONTROL_FPEN;
76162306a36Sopenharmony_ci			else
76262306a36Sopenharmony_ci				control &= ~SM501_DC_PANEL_CONTROL_FPEN;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci			smc501_writel(control, ctrl_reg);
76562306a36Sopenharmony_ci			sm501fb_sync_regs(fbi);
76662306a36Sopenharmony_ci			mdelay(10);
76762306a36Sopenharmony_ci		}
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci		if (!(pd->flags & SM501FB_FLAG_PANEL_NO_VBIASEN)) {
77062306a36Sopenharmony_ci			if (pd->flags & SM501FB_FLAG_PANEL_INV_VBIASEN)
77162306a36Sopenharmony_ci				control |= SM501_DC_PANEL_CONTROL_BIAS;
77262306a36Sopenharmony_ci			else
77362306a36Sopenharmony_ci				control &= ~SM501_DC_PANEL_CONTROL_BIAS;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci			smc501_writel(control, ctrl_reg);
77662306a36Sopenharmony_ci			sm501fb_sync_regs(fbi);
77762306a36Sopenharmony_ci			mdelay(10);
77862306a36Sopenharmony_ci		}
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci		control &= ~SM501_DC_PANEL_CONTROL_DATA;
78162306a36Sopenharmony_ci		smc501_writel(control, ctrl_reg);
78262306a36Sopenharmony_ci		sm501fb_sync_regs(fbi);
78362306a36Sopenharmony_ci		mdelay(10);
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci		control &= ~SM501_DC_PANEL_CONTROL_VDD;
78662306a36Sopenharmony_ci		smc501_writel(control, ctrl_reg);
78762306a36Sopenharmony_ci		sm501fb_sync_regs(fbi);
78862306a36Sopenharmony_ci		mdelay(10);
78962306a36Sopenharmony_ci	}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	sm501fb_sync_regs(fbi);
79262306a36Sopenharmony_ci}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci/* sm501fb_set_par_pnl
79562306a36Sopenharmony_ci *
79662306a36Sopenharmony_ci * Set the panel video mode from the fb_info structure
79762306a36Sopenharmony_ci*/
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_cistatic int sm501fb_set_par_pnl(struct fb_info *info)
80062306a36Sopenharmony_ci{
80162306a36Sopenharmony_ci	struct sm501fb_par  *par = info->par;
80262306a36Sopenharmony_ci	struct sm501fb_info *fbi = par->info;
80362306a36Sopenharmony_ci	struct fb_var_screeninfo *var = &info->var;
80462306a36Sopenharmony_ci	unsigned long control;
80562306a36Sopenharmony_ci	unsigned long reg;
80662306a36Sopenharmony_ci	int ret;
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	dev_dbg(fbi->dev, "%s(%p)\n", __func__, info);
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	/* activate this new configuration */
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	ret = sm501fb_set_par_common(info, var);
81362306a36Sopenharmony_ci	if (ret)
81462306a36Sopenharmony_ci		return ret;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	sm501fb_pan_pnl(var, info);
81762306a36Sopenharmony_ci	sm501fb_set_par_geometry(info, var);
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	/* update control register */
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	control = smc501_readl(fbi->regs + SM501_DC_PANEL_CONTROL);
82262306a36Sopenharmony_ci	control &= (SM501_DC_PANEL_CONTROL_GAMMA |
82362306a36Sopenharmony_ci		    SM501_DC_PANEL_CONTROL_VDD  |
82462306a36Sopenharmony_ci		    SM501_DC_PANEL_CONTROL_DATA |
82562306a36Sopenharmony_ci		    SM501_DC_PANEL_CONTROL_BIAS |
82662306a36Sopenharmony_ci		    SM501_DC_PANEL_CONTROL_FPEN |
82762306a36Sopenharmony_ci		    SM501_DC_PANEL_CONTROL_CP |
82862306a36Sopenharmony_ci		    SM501_DC_PANEL_CONTROL_CK |
82962306a36Sopenharmony_ci		    SM501_DC_PANEL_CONTROL_HP |
83062306a36Sopenharmony_ci		    SM501_DC_PANEL_CONTROL_VP |
83162306a36Sopenharmony_ci		    SM501_DC_PANEL_CONTROL_HPD |
83262306a36Sopenharmony_ci		    SM501_DC_PANEL_CONTROL_VPD);
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	control |= SM501_FIFO_3;	/* fill if >3 free slots */
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	switch(var->bits_per_pixel) {
83762306a36Sopenharmony_ci	case 8:
83862306a36Sopenharmony_ci		control |= SM501_DC_PANEL_CONTROL_8BPP;
83962306a36Sopenharmony_ci		break;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	case 16:
84262306a36Sopenharmony_ci		control |= SM501_DC_PANEL_CONTROL_16BPP;
84362306a36Sopenharmony_ci		sm501fb_setup_gamma(fbi, SM501_DC_PANEL_PALETTE);
84462306a36Sopenharmony_ci		break;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	case 32:
84762306a36Sopenharmony_ci		control |= SM501_DC_PANEL_CONTROL_32BPP;
84862306a36Sopenharmony_ci		sm501fb_setup_gamma(fbi, SM501_DC_PANEL_PALETTE);
84962306a36Sopenharmony_ci		break;
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	default:
85262306a36Sopenharmony_ci		BUG();
85362306a36Sopenharmony_ci	}
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	smc501_writel(0x0, fbi->regs + SM501_DC_PANEL_PANNING_CONTROL);
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	/* panel plane top left and bottom right location */
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	smc501_writel(0x00, fbi->regs + SM501_DC_PANEL_TL_LOC);
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	reg  = var->xres - 1;
86262306a36Sopenharmony_ci	reg |= (var->yres - 1) << 16;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	smc501_writel(reg, fbi->regs + SM501_DC_PANEL_BR_LOC);
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	/* program panel control register */
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	control |= SM501_DC_PANEL_CONTROL_TE;	/* enable PANEL timing */
86962306a36Sopenharmony_ci	control |= SM501_DC_PANEL_CONTROL_EN;	/* enable PANEL gfx plane */
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	if ((var->sync & FB_SYNC_HOR_HIGH_ACT) == 0)
87262306a36Sopenharmony_ci		control |= SM501_DC_PANEL_CONTROL_HSP;
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	if ((var->sync & FB_SYNC_VERT_HIGH_ACT) == 0)
87562306a36Sopenharmony_ci		control |= SM501_DC_PANEL_CONTROL_VSP;
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	smc501_writel(control, fbi->regs + SM501_DC_PANEL_CONTROL);
87862306a36Sopenharmony_ci	sm501fb_sync_regs(fbi);
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	/* ensure the panel interface is not tristated at this point */
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	sm501_modify_reg(fbi->dev->parent, SM501_SYSTEM_CONTROL,
88362306a36Sopenharmony_ci			 0, SM501_SYSCTRL_PANEL_TRISTATE);
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	/* power the panel up */
88662306a36Sopenharmony_ci	sm501fb_panel_power(fbi, 1);
88762306a36Sopenharmony_ci	return 0;
88862306a36Sopenharmony_ci}
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci/* chan_to_field
89262306a36Sopenharmony_ci *
89362306a36Sopenharmony_ci * convert a colour value into a field position
89462306a36Sopenharmony_ci *
89562306a36Sopenharmony_ci * from pxafb.c
89662306a36Sopenharmony_ci*/
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_cistatic inline unsigned int chan_to_field(unsigned int chan,
89962306a36Sopenharmony_ci					 struct fb_bitfield *bf)
90062306a36Sopenharmony_ci{
90162306a36Sopenharmony_ci	chan &= 0xffff;
90262306a36Sopenharmony_ci	chan >>= 16 - bf->length;
90362306a36Sopenharmony_ci	return chan << bf->offset;
90462306a36Sopenharmony_ci}
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci/* sm501fb_setcolreg
90762306a36Sopenharmony_ci *
90862306a36Sopenharmony_ci * set the colour mapping for modes that support palettised data
90962306a36Sopenharmony_ci*/
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_cistatic int sm501fb_setcolreg(unsigned regno,
91262306a36Sopenharmony_ci			     unsigned red, unsigned green, unsigned blue,
91362306a36Sopenharmony_ci			     unsigned transp, struct fb_info *info)
91462306a36Sopenharmony_ci{
91562306a36Sopenharmony_ci	struct sm501fb_par  *par = info->par;
91662306a36Sopenharmony_ci	struct sm501fb_info *fbi = par->info;
91762306a36Sopenharmony_ci	void __iomem *base = fbi->regs;
91862306a36Sopenharmony_ci	unsigned int val;
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	if (par->head == HEAD_CRT)
92162306a36Sopenharmony_ci		base += SM501_DC_CRT_PALETTE;
92262306a36Sopenharmony_ci	else
92362306a36Sopenharmony_ci		base += SM501_DC_PANEL_PALETTE;
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	switch (info->fix.visual) {
92662306a36Sopenharmony_ci	case FB_VISUAL_TRUECOLOR:
92762306a36Sopenharmony_ci		/* true-colour, use pseuo-palette */
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci		if (regno < 16) {
93062306a36Sopenharmony_ci			u32 *pal = par->pseudo_palette;
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci			val  = chan_to_field(red,   &info->var.red);
93362306a36Sopenharmony_ci			val |= chan_to_field(green, &info->var.green);
93462306a36Sopenharmony_ci			val |= chan_to_field(blue,  &info->var.blue);
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci			pal[regno] = val;
93762306a36Sopenharmony_ci		}
93862306a36Sopenharmony_ci		break;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	case FB_VISUAL_PSEUDOCOLOR:
94162306a36Sopenharmony_ci		if (regno < 256) {
94262306a36Sopenharmony_ci			val = (red >> 8) << 16;
94362306a36Sopenharmony_ci			val |= (green >> 8) << 8;
94462306a36Sopenharmony_ci			val |= blue >> 8;
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci			smc501_writel(val, base + (regno * 4));
94762306a36Sopenharmony_ci		}
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci		break;
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	default:
95262306a36Sopenharmony_ci		return 1;   /* unknown type */
95362306a36Sopenharmony_ci	}
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	return 0;
95662306a36Sopenharmony_ci}
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci/* sm501fb_blank_pnl
95962306a36Sopenharmony_ci *
96062306a36Sopenharmony_ci * Blank or un-blank the panel interface
96162306a36Sopenharmony_ci*/
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_cistatic int sm501fb_blank_pnl(int blank_mode, struct fb_info *info)
96462306a36Sopenharmony_ci{
96562306a36Sopenharmony_ci	struct sm501fb_par  *par = info->par;
96662306a36Sopenharmony_ci	struct sm501fb_info *fbi = par->info;
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	dev_dbg(fbi->dev, "%s(mode=%d, %p)\n", __func__, blank_mode, info);
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	switch (blank_mode) {
97162306a36Sopenharmony_ci	case FB_BLANK_POWERDOWN:
97262306a36Sopenharmony_ci		sm501fb_panel_power(fbi, 0);
97362306a36Sopenharmony_ci		break;
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	case FB_BLANK_UNBLANK:
97662306a36Sopenharmony_ci		sm501fb_panel_power(fbi, 1);
97762306a36Sopenharmony_ci		break;
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	case FB_BLANK_NORMAL:
98062306a36Sopenharmony_ci	case FB_BLANK_VSYNC_SUSPEND:
98162306a36Sopenharmony_ci	case FB_BLANK_HSYNC_SUSPEND:
98262306a36Sopenharmony_ci	default:
98362306a36Sopenharmony_ci		return 1;
98462306a36Sopenharmony_ci	}
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	return 0;
98762306a36Sopenharmony_ci}
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci/* sm501fb_blank_crt
99062306a36Sopenharmony_ci *
99162306a36Sopenharmony_ci * Blank or un-blank the crt interface
99262306a36Sopenharmony_ci*/
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_cistatic int sm501fb_blank_crt(int blank_mode, struct fb_info *info)
99562306a36Sopenharmony_ci{
99662306a36Sopenharmony_ci	struct sm501fb_par  *par = info->par;
99762306a36Sopenharmony_ci	struct sm501fb_info *fbi = par->info;
99862306a36Sopenharmony_ci	unsigned long ctrl;
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	dev_dbg(fbi->dev, "%s(mode=%d, %p)\n", __func__, blank_mode, info);
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	ctrl = smc501_readl(fbi->regs + SM501_DC_CRT_CONTROL);
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	switch (blank_mode) {
100562306a36Sopenharmony_ci	case FB_BLANK_POWERDOWN:
100662306a36Sopenharmony_ci		ctrl &= ~SM501_DC_CRT_CONTROL_ENABLE;
100762306a36Sopenharmony_ci		sm501_misc_control(fbi->dev->parent, SM501_MISC_DAC_POWER, 0);
100862306a36Sopenharmony_ci		fallthrough;
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	case FB_BLANK_NORMAL:
101162306a36Sopenharmony_ci		ctrl |= SM501_DC_CRT_CONTROL_BLANK;
101262306a36Sopenharmony_ci		break;
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	case FB_BLANK_UNBLANK:
101562306a36Sopenharmony_ci		ctrl &= ~SM501_DC_CRT_CONTROL_BLANK;
101662306a36Sopenharmony_ci		ctrl |=  SM501_DC_CRT_CONTROL_ENABLE;
101762306a36Sopenharmony_ci		sm501_misc_control(fbi->dev->parent, 0, SM501_MISC_DAC_POWER);
101862306a36Sopenharmony_ci		break;
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	case FB_BLANK_VSYNC_SUSPEND:
102162306a36Sopenharmony_ci	case FB_BLANK_HSYNC_SUSPEND:
102262306a36Sopenharmony_ci	default:
102362306a36Sopenharmony_ci		return 1;
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	}
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	smc501_writel(ctrl, fbi->regs + SM501_DC_CRT_CONTROL);
102862306a36Sopenharmony_ci	sm501fb_sync_regs(fbi);
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	return 0;
103162306a36Sopenharmony_ci}
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci/* sm501fb_cursor
103462306a36Sopenharmony_ci *
103562306a36Sopenharmony_ci * set or change the hardware cursor parameters
103662306a36Sopenharmony_ci*/
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_cistatic int sm501fb_cursor(struct fb_info *info, struct fb_cursor *cursor)
103962306a36Sopenharmony_ci{
104062306a36Sopenharmony_ci	struct sm501fb_par  *par = info->par;
104162306a36Sopenharmony_ci	struct sm501fb_info *fbi = par->info;
104262306a36Sopenharmony_ci	void __iomem *base = fbi->regs;
104362306a36Sopenharmony_ci	unsigned long hwc_addr;
104462306a36Sopenharmony_ci	unsigned long fg, bg;
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	dev_dbg(fbi->dev, "%s(%p,%p)\n", __func__, info, cursor);
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	if (par->head == HEAD_CRT)
104962306a36Sopenharmony_ci		base += SM501_DC_CRT_HWC_BASE;
105062306a36Sopenharmony_ci	else
105162306a36Sopenharmony_ci		base += SM501_DC_PANEL_HWC_BASE;
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	/* check not being asked to exceed capabilities */
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	if (cursor->image.width > 64)
105662306a36Sopenharmony_ci		return -EINVAL;
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	if (cursor->image.height > 64)
105962306a36Sopenharmony_ci		return -EINVAL;
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	if (cursor->image.depth > 1)
106262306a36Sopenharmony_ci		return -EINVAL;
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	hwc_addr = smc501_readl(base + SM501_OFF_HWC_ADDR);
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	if (cursor->enable)
106762306a36Sopenharmony_ci		smc501_writel(hwc_addr | SM501_HWC_EN,
106862306a36Sopenharmony_ci				base + SM501_OFF_HWC_ADDR);
106962306a36Sopenharmony_ci	else
107062306a36Sopenharmony_ci		smc501_writel(hwc_addr & ~SM501_HWC_EN,
107162306a36Sopenharmony_ci				base + SM501_OFF_HWC_ADDR);
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	/* set data */
107462306a36Sopenharmony_ci	if (cursor->set & FB_CUR_SETPOS) {
107562306a36Sopenharmony_ci		unsigned int x = cursor->image.dx;
107662306a36Sopenharmony_ci		unsigned int y = cursor->image.dy;
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci		if (x >= 2048 || y >= 2048 )
107962306a36Sopenharmony_ci			return -EINVAL;
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci		dev_dbg(fbi->dev, "set position %d,%d\n", x, y);
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci		//y += cursor->image.height;
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci		smc501_writel(x | (y << 16), base + SM501_OFF_HWC_LOC);
108662306a36Sopenharmony_ci	}
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	if (cursor->set & FB_CUR_SETCMAP) {
108962306a36Sopenharmony_ci		unsigned int bg_col = cursor->image.bg_color;
109062306a36Sopenharmony_ci		unsigned int fg_col = cursor->image.fg_color;
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci		dev_dbg(fbi->dev, "%s: update cmap (%08x,%08x)\n",
109362306a36Sopenharmony_ci			__func__, bg_col, fg_col);
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci		bg = ((info->cmap.red[bg_col] & 0xF8) << 8) |
109662306a36Sopenharmony_ci			((info->cmap.green[bg_col] & 0xFC) << 3) |
109762306a36Sopenharmony_ci			((info->cmap.blue[bg_col] & 0xF8) >> 3);
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci		fg = ((info->cmap.red[fg_col] & 0xF8) << 8) |
110062306a36Sopenharmony_ci			((info->cmap.green[fg_col] & 0xFC) << 3) |
110162306a36Sopenharmony_ci			((info->cmap.blue[fg_col] & 0xF8) >> 3);
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci		dev_dbg(fbi->dev, "fgcol %08lx, bgcol %08lx\n", fg, bg);
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci		smc501_writel(bg, base + SM501_OFF_HWC_COLOR_1_2);
110662306a36Sopenharmony_ci		smc501_writel(fg, base + SM501_OFF_HWC_COLOR_3);
110762306a36Sopenharmony_ci	}
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	if (cursor->set & FB_CUR_SETSIZE ||
111062306a36Sopenharmony_ci	    cursor->set & (FB_CUR_SETIMAGE | FB_CUR_SETSHAPE)) {
111162306a36Sopenharmony_ci		/* SM501 cursor is a two bpp 64x64 bitmap this routine
111262306a36Sopenharmony_ci		 * clears it to transparent then combines the cursor
111362306a36Sopenharmony_ci		 * shape plane with the colour plane to set the
111462306a36Sopenharmony_ci		 * cursor */
111562306a36Sopenharmony_ci		int x, y;
111662306a36Sopenharmony_ci		const unsigned char *pcol = cursor->image.data;
111762306a36Sopenharmony_ci		const unsigned char *pmsk = cursor->mask;
111862306a36Sopenharmony_ci		void __iomem   *dst = par->cursor.k_addr;
111962306a36Sopenharmony_ci		unsigned char  dcol = 0;
112062306a36Sopenharmony_ci		unsigned char  dmsk = 0;
112162306a36Sopenharmony_ci		unsigned int   op;
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci		dev_dbg(fbi->dev, "%s: setting shape (%d,%d)\n",
112462306a36Sopenharmony_ci			__func__, cursor->image.width, cursor->image.height);
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci		for (op = 0; op < (64*64*2)/8; op+=4)
112762306a36Sopenharmony_ci			smc501_writel(0x0, dst + op);
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci		for (y = 0; y < cursor->image.height; y++) {
113062306a36Sopenharmony_ci			for (x = 0; x < cursor->image.width; x++) {
113162306a36Sopenharmony_ci				if ((x % 8) == 0) {
113262306a36Sopenharmony_ci					dcol = *pcol++;
113362306a36Sopenharmony_ci					dmsk = *pmsk++;
113462306a36Sopenharmony_ci				} else {
113562306a36Sopenharmony_ci					dcol >>= 1;
113662306a36Sopenharmony_ci					dmsk >>= 1;
113762306a36Sopenharmony_ci				}
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci				if (dmsk & 1) {
114062306a36Sopenharmony_ci					op = (dcol & 1) ? 1 : 3;
114162306a36Sopenharmony_ci					op <<= ((x % 4) * 2);
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci					op |= readb(dst + (x / 4));
114462306a36Sopenharmony_ci					writeb(op, dst + (x / 4));
114562306a36Sopenharmony_ci				}
114662306a36Sopenharmony_ci			}
114762306a36Sopenharmony_ci			dst += (64*2)/8;
114862306a36Sopenharmony_ci		}
114962306a36Sopenharmony_ci	}
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	sm501fb_sync_regs(fbi);	/* ensure cursor data flushed */
115262306a36Sopenharmony_ci	return 0;
115362306a36Sopenharmony_ci}
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci/* sm501fb_crtsrc_show
115662306a36Sopenharmony_ci *
115762306a36Sopenharmony_ci * device attribute code to show where the crt output is sourced from
115862306a36Sopenharmony_ci*/
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_cistatic ssize_t sm501fb_crtsrc_show(struct device *dev,
116162306a36Sopenharmony_ci			       struct device_attribute *attr, char *buf)
116262306a36Sopenharmony_ci{
116362306a36Sopenharmony_ci	struct sm501fb_info *info = dev_get_drvdata(dev);
116462306a36Sopenharmony_ci	unsigned long ctrl;
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	ctrl = smc501_readl(info->regs + SM501_DC_CRT_CONTROL);
116762306a36Sopenharmony_ci	ctrl &= SM501_DC_CRT_CONTROL_SEL;
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	return sysfs_emit(buf, "%s\n", ctrl ? "crt" : "panel");
117062306a36Sopenharmony_ci}
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci/* sm501fb_crtsrc_show
117362306a36Sopenharmony_ci *
117462306a36Sopenharmony_ci * device attribute code to set where the crt output is sourced from
117562306a36Sopenharmony_ci*/
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_cistatic ssize_t sm501fb_crtsrc_store(struct device *dev,
117862306a36Sopenharmony_ci				struct device_attribute *attr,
117962306a36Sopenharmony_ci				const char *buf, size_t len)
118062306a36Sopenharmony_ci{
118162306a36Sopenharmony_ci	struct sm501fb_info *info = dev_get_drvdata(dev);
118262306a36Sopenharmony_ci	enum sm501_controller head;
118362306a36Sopenharmony_ci	unsigned long ctrl;
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	if (len < 1)
118662306a36Sopenharmony_ci		return -EINVAL;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	if (strncasecmp(buf, "crt", 3) == 0)
118962306a36Sopenharmony_ci		head = HEAD_CRT;
119062306a36Sopenharmony_ci	else if (strncasecmp(buf, "panel", 5) == 0)
119162306a36Sopenharmony_ci		head = HEAD_PANEL;
119262306a36Sopenharmony_ci	else
119362306a36Sopenharmony_ci		return -EINVAL;
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci	dev_info(dev, "setting crt source to head %d\n", head);
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	ctrl = smc501_readl(info->regs + SM501_DC_CRT_CONTROL);
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	if (head == HEAD_CRT) {
120062306a36Sopenharmony_ci		ctrl |= SM501_DC_CRT_CONTROL_SEL;
120162306a36Sopenharmony_ci		ctrl |= SM501_DC_CRT_CONTROL_ENABLE;
120262306a36Sopenharmony_ci		ctrl |= SM501_DC_CRT_CONTROL_TE;
120362306a36Sopenharmony_ci	} else {
120462306a36Sopenharmony_ci		ctrl &= ~SM501_DC_CRT_CONTROL_SEL;
120562306a36Sopenharmony_ci		ctrl &= ~SM501_DC_CRT_CONTROL_ENABLE;
120662306a36Sopenharmony_ci		ctrl &= ~SM501_DC_CRT_CONTROL_TE;
120762306a36Sopenharmony_ci	}
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	smc501_writel(ctrl, info->regs + SM501_DC_CRT_CONTROL);
121062306a36Sopenharmony_ci	sm501fb_sync_regs(info);
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	return len;
121362306a36Sopenharmony_ci}
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci/* Prepare the device_attr for registration with sysfs later */
121662306a36Sopenharmony_cistatic DEVICE_ATTR(crt_src, 0664, sm501fb_crtsrc_show, sm501fb_crtsrc_store);
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci/* sm501fb_show_regs
121962306a36Sopenharmony_ci *
122062306a36Sopenharmony_ci * show the primary sm501 registers
122162306a36Sopenharmony_ci*/
122262306a36Sopenharmony_cistatic int sm501fb_show_regs(struct sm501fb_info *info, char *ptr,
122362306a36Sopenharmony_ci			     unsigned int start, unsigned int len)
122462306a36Sopenharmony_ci{
122562306a36Sopenharmony_ci	void __iomem *mem = info->regs;
122662306a36Sopenharmony_ci	char *buf = ptr;
122762306a36Sopenharmony_ci	unsigned int reg;
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	for (reg = start; reg < (len + start); reg += 4)
123062306a36Sopenharmony_ci		ptr += sprintf(ptr, "%08x = %08x\n", reg,
123162306a36Sopenharmony_ci				smc501_readl(mem + reg));
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci	return ptr - buf;
123462306a36Sopenharmony_ci}
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci/* sm501fb_debug_show_crt
123762306a36Sopenharmony_ci *
123862306a36Sopenharmony_ci * show the crt control and cursor registers
123962306a36Sopenharmony_ci*/
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_cistatic ssize_t sm501fb_debug_show_crt(struct device *dev,
124262306a36Sopenharmony_ci				  struct device_attribute *attr, char *buf)
124362306a36Sopenharmony_ci{
124462306a36Sopenharmony_ci	struct sm501fb_info *info = dev_get_drvdata(dev);
124562306a36Sopenharmony_ci	char *ptr = buf;
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	ptr += sm501fb_show_regs(info, ptr, SM501_DC_CRT_CONTROL, 0x40);
124862306a36Sopenharmony_ci	ptr += sm501fb_show_regs(info, ptr, SM501_DC_CRT_HWC_BASE, 0x10);
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci	return ptr - buf;
125162306a36Sopenharmony_ci}
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_cistatic DEVICE_ATTR(fbregs_crt, 0444, sm501fb_debug_show_crt, NULL);
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci/* sm501fb_debug_show_pnl
125662306a36Sopenharmony_ci *
125762306a36Sopenharmony_ci * show the panel control and cursor registers
125862306a36Sopenharmony_ci*/
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_cistatic ssize_t sm501fb_debug_show_pnl(struct device *dev,
126162306a36Sopenharmony_ci				  struct device_attribute *attr, char *buf)
126262306a36Sopenharmony_ci{
126362306a36Sopenharmony_ci	struct sm501fb_info *info = dev_get_drvdata(dev);
126462306a36Sopenharmony_ci	char *ptr = buf;
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	ptr += sm501fb_show_regs(info, ptr, 0x0, 0x40);
126762306a36Sopenharmony_ci	ptr += sm501fb_show_regs(info, ptr, SM501_DC_PANEL_HWC_BASE, 0x10);
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	return ptr - buf;
127062306a36Sopenharmony_ci}
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_cistatic DEVICE_ATTR(fbregs_pnl, 0444, sm501fb_debug_show_pnl, NULL);
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_cistatic struct attribute *sm501fb_attrs[] = {
127562306a36Sopenharmony_ci	&dev_attr_crt_src.attr,
127662306a36Sopenharmony_ci	&dev_attr_fbregs_pnl.attr,
127762306a36Sopenharmony_ci	&dev_attr_fbregs_crt.attr,
127862306a36Sopenharmony_ci	NULL,
127962306a36Sopenharmony_ci};
128062306a36Sopenharmony_ciATTRIBUTE_GROUPS(sm501fb);
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci/* acceleration operations */
128362306a36Sopenharmony_cistatic int sm501fb_sync(struct fb_info *info)
128462306a36Sopenharmony_ci{
128562306a36Sopenharmony_ci	int count = 1000000;
128662306a36Sopenharmony_ci	struct sm501fb_par  *par = info->par;
128762306a36Sopenharmony_ci	struct sm501fb_info *fbi = par->info;
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	/* wait for the 2d engine to be ready */
129062306a36Sopenharmony_ci	while ((count > 0) &&
129162306a36Sopenharmony_ci	       (smc501_readl(fbi->regs + SM501_SYSTEM_CONTROL) &
129262306a36Sopenharmony_ci		SM501_SYSCTRL_2D_ENGINE_STATUS) != 0)
129362306a36Sopenharmony_ci		count--;
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	if (count <= 0) {
129662306a36Sopenharmony_ci		fb_err(info, "Timeout waiting for 2d engine sync\n");
129762306a36Sopenharmony_ci		return 1;
129862306a36Sopenharmony_ci	}
129962306a36Sopenharmony_ci	return 0;
130062306a36Sopenharmony_ci}
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_cistatic void sm501fb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
130362306a36Sopenharmony_ci{
130462306a36Sopenharmony_ci	struct sm501fb_par  *par = info->par;
130562306a36Sopenharmony_ci	struct sm501fb_info *fbi = par->info;
130662306a36Sopenharmony_ci	int width = area->width;
130762306a36Sopenharmony_ci	int height = area->height;
130862306a36Sopenharmony_ci	int sx = area->sx;
130962306a36Sopenharmony_ci	int sy = area->sy;
131062306a36Sopenharmony_ci	int dx = area->dx;
131162306a36Sopenharmony_ci	int dy = area->dy;
131262306a36Sopenharmony_ci	unsigned long rtl = 0;
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	/* source clip */
131562306a36Sopenharmony_ci	if ((sx >= info->var.xres_virtual) ||
131662306a36Sopenharmony_ci	    (sy >= info->var.yres_virtual))
131762306a36Sopenharmony_ci		/* source Area not within virtual screen, skipping */
131862306a36Sopenharmony_ci		return;
131962306a36Sopenharmony_ci	if ((sx + width) >= info->var.xres_virtual)
132062306a36Sopenharmony_ci		width = info->var.xres_virtual - sx - 1;
132162306a36Sopenharmony_ci	if ((sy + height) >= info->var.yres_virtual)
132262306a36Sopenharmony_ci		height = info->var.yres_virtual - sy - 1;
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	/* dest clip */
132562306a36Sopenharmony_ci	if ((dx >= info->var.xres_virtual) ||
132662306a36Sopenharmony_ci	    (dy >= info->var.yres_virtual))
132762306a36Sopenharmony_ci		/* Destination Area not within virtual screen, skipping */
132862306a36Sopenharmony_ci		return;
132962306a36Sopenharmony_ci	if ((dx + width) >= info->var.xres_virtual)
133062306a36Sopenharmony_ci		width = info->var.xres_virtual - dx - 1;
133162306a36Sopenharmony_ci	if ((dy + height) >= info->var.yres_virtual)
133262306a36Sopenharmony_ci		height = info->var.yres_virtual - dy - 1;
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_ci	if ((sx < dx) || (sy < dy)) {
133562306a36Sopenharmony_ci		rtl = 1 << 27;
133662306a36Sopenharmony_ci		sx += width - 1;
133762306a36Sopenharmony_ci		dx += width - 1;
133862306a36Sopenharmony_ci		sy += height - 1;
133962306a36Sopenharmony_ci		dy += height - 1;
134062306a36Sopenharmony_ci	}
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci	if (sm501fb_sync(info))
134362306a36Sopenharmony_ci		return;
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	/* set the base addresses */
134662306a36Sopenharmony_ci	smc501_writel(par->screen.sm_addr, fbi->regs2d + SM501_2D_SOURCE_BASE);
134762306a36Sopenharmony_ci	smc501_writel(par->screen.sm_addr,
134862306a36Sopenharmony_ci			fbi->regs2d + SM501_2D_DESTINATION_BASE);
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	/* set the window width */
135162306a36Sopenharmony_ci	smc501_writel((info->var.xres << 16) | info->var.xres,
135262306a36Sopenharmony_ci	       fbi->regs2d + SM501_2D_WINDOW_WIDTH);
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci	/* set window stride */
135562306a36Sopenharmony_ci	smc501_writel((info->var.xres_virtual << 16) | info->var.xres_virtual,
135662306a36Sopenharmony_ci	       fbi->regs2d + SM501_2D_PITCH);
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	/* set data format */
135962306a36Sopenharmony_ci	switch (info->var.bits_per_pixel) {
136062306a36Sopenharmony_ci	case 8:
136162306a36Sopenharmony_ci		smc501_writel(0, fbi->regs2d + SM501_2D_STRETCH);
136262306a36Sopenharmony_ci		break;
136362306a36Sopenharmony_ci	case 16:
136462306a36Sopenharmony_ci		smc501_writel(0x00100000, fbi->regs2d + SM501_2D_STRETCH);
136562306a36Sopenharmony_ci		break;
136662306a36Sopenharmony_ci	case 32:
136762306a36Sopenharmony_ci		smc501_writel(0x00200000, fbi->regs2d + SM501_2D_STRETCH);
136862306a36Sopenharmony_ci		break;
136962306a36Sopenharmony_ci	}
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci	/* 2d compare mask */
137262306a36Sopenharmony_ci	smc501_writel(0xffffffff, fbi->regs2d + SM501_2D_COLOR_COMPARE_MASK);
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	/* 2d mask */
137562306a36Sopenharmony_ci	smc501_writel(0xffffffff, fbi->regs2d + SM501_2D_MASK);
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci	/* source and destination x y */
137862306a36Sopenharmony_ci	smc501_writel((sx << 16) | sy, fbi->regs2d + SM501_2D_SOURCE);
137962306a36Sopenharmony_ci	smc501_writel((dx << 16) | dy, fbi->regs2d + SM501_2D_DESTINATION);
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	/* w/h */
138262306a36Sopenharmony_ci	smc501_writel((width << 16) | height, fbi->regs2d + SM501_2D_DIMENSION);
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	/* do area move */
138562306a36Sopenharmony_ci	smc501_writel(0x800000cc | rtl, fbi->regs2d + SM501_2D_CONTROL);
138662306a36Sopenharmony_ci}
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_cistatic void sm501fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
138962306a36Sopenharmony_ci{
139062306a36Sopenharmony_ci	struct sm501fb_par  *par = info->par;
139162306a36Sopenharmony_ci	struct sm501fb_info *fbi = par->info;
139262306a36Sopenharmony_ci	int width = rect->width, height = rect->height;
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	if ((rect->dx >= info->var.xres_virtual) ||
139562306a36Sopenharmony_ci	    (rect->dy >= info->var.yres_virtual))
139662306a36Sopenharmony_ci		/* Rectangle not within virtual screen, skipping */
139762306a36Sopenharmony_ci		return;
139862306a36Sopenharmony_ci	if ((rect->dx + width) >= info->var.xres_virtual)
139962306a36Sopenharmony_ci		width = info->var.xres_virtual - rect->dx - 1;
140062306a36Sopenharmony_ci	if ((rect->dy + height) >= info->var.yres_virtual)
140162306a36Sopenharmony_ci		height = info->var.yres_virtual - rect->dy - 1;
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	if (sm501fb_sync(info))
140462306a36Sopenharmony_ci		return;
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci	/* set the base addresses */
140762306a36Sopenharmony_ci	smc501_writel(par->screen.sm_addr, fbi->regs2d + SM501_2D_SOURCE_BASE);
140862306a36Sopenharmony_ci	smc501_writel(par->screen.sm_addr,
140962306a36Sopenharmony_ci			fbi->regs2d + SM501_2D_DESTINATION_BASE);
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci	/* set the window width */
141262306a36Sopenharmony_ci	smc501_writel((info->var.xres << 16) | info->var.xres,
141362306a36Sopenharmony_ci	       fbi->regs2d + SM501_2D_WINDOW_WIDTH);
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci	/* set window stride */
141662306a36Sopenharmony_ci	smc501_writel((info->var.xres_virtual << 16) | info->var.xres_virtual,
141762306a36Sopenharmony_ci	       fbi->regs2d + SM501_2D_PITCH);
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci	/* set data format */
142062306a36Sopenharmony_ci	switch (info->var.bits_per_pixel) {
142162306a36Sopenharmony_ci	case 8:
142262306a36Sopenharmony_ci		smc501_writel(0, fbi->regs2d + SM501_2D_STRETCH);
142362306a36Sopenharmony_ci		break;
142462306a36Sopenharmony_ci	case 16:
142562306a36Sopenharmony_ci		smc501_writel(0x00100000, fbi->regs2d + SM501_2D_STRETCH);
142662306a36Sopenharmony_ci		break;
142762306a36Sopenharmony_ci	case 32:
142862306a36Sopenharmony_ci		smc501_writel(0x00200000, fbi->regs2d + SM501_2D_STRETCH);
142962306a36Sopenharmony_ci		break;
143062306a36Sopenharmony_ci	}
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	/* 2d compare mask */
143362306a36Sopenharmony_ci	smc501_writel(0xffffffff, fbi->regs2d + SM501_2D_COLOR_COMPARE_MASK);
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci	/* 2d mask */
143662306a36Sopenharmony_ci	smc501_writel(0xffffffff, fbi->regs2d + SM501_2D_MASK);
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	/* colour */
143962306a36Sopenharmony_ci	smc501_writel(rect->color, fbi->regs2d + SM501_2D_FOREGROUND);
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci	/* x y */
144262306a36Sopenharmony_ci	smc501_writel((rect->dx << 16) | rect->dy,
144362306a36Sopenharmony_ci			fbi->regs2d + SM501_2D_DESTINATION);
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_ci	/* w/h */
144662306a36Sopenharmony_ci	smc501_writel((width << 16) | height, fbi->regs2d + SM501_2D_DIMENSION);
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci	/* do rectangle fill */
144962306a36Sopenharmony_ci	smc501_writel(0x800100cc, fbi->regs2d + SM501_2D_CONTROL);
145062306a36Sopenharmony_ci}
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_cistatic struct fb_ops sm501fb_ops_crt = {
145462306a36Sopenharmony_ci	.owner		= THIS_MODULE,
145562306a36Sopenharmony_ci	.fb_check_var	= sm501fb_check_var_crt,
145662306a36Sopenharmony_ci	.fb_set_par	= sm501fb_set_par_crt,
145762306a36Sopenharmony_ci	.fb_blank	= sm501fb_blank_crt,
145862306a36Sopenharmony_ci	.fb_setcolreg	= sm501fb_setcolreg,
145962306a36Sopenharmony_ci	.fb_pan_display	= sm501fb_pan_crt,
146062306a36Sopenharmony_ci	.fb_cursor	= sm501fb_cursor,
146162306a36Sopenharmony_ci	.fb_fillrect	= sm501fb_fillrect,
146262306a36Sopenharmony_ci	.fb_copyarea	= sm501fb_copyarea,
146362306a36Sopenharmony_ci	.fb_imageblit	= cfb_imageblit,
146462306a36Sopenharmony_ci	.fb_sync	= sm501fb_sync,
146562306a36Sopenharmony_ci};
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_cistatic struct fb_ops sm501fb_ops_pnl = {
146862306a36Sopenharmony_ci	.owner		= THIS_MODULE,
146962306a36Sopenharmony_ci	.fb_check_var	= sm501fb_check_var_pnl,
147062306a36Sopenharmony_ci	.fb_set_par	= sm501fb_set_par_pnl,
147162306a36Sopenharmony_ci	.fb_pan_display	= sm501fb_pan_pnl,
147262306a36Sopenharmony_ci	.fb_blank	= sm501fb_blank_pnl,
147362306a36Sopenharmony_ci	.fb_setcolreg	= sm501fb_setcolreg,
147462306a36Sopenharmony_ci	.fb_cursor	= sm501fb_cursor,
147562306a36Sopenharmony_ci	.fb_fillrect	= sm501fb_fillrect,
147662306a36Sopenharmony_ci	.fb_copyarea	= sm501fb_copyarea,
147762306a36Sopenharmony_ci	.fb_imageblit	= cfb_imageblit,
147862306a36Sopenharmony_ci	.fb_sync	= sm501fb_sync,
147962306a36Sopenharmony_ci};
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci/* sm501_init_cursor
148262306a36Sopenharmony_ci *
148362306a36Sopenharmony_ci * initialise hw cursor parameters
148462306a36Sopenharmony_ci*/
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_cistatic int sm501_init_cursor(struct fb_info *fbi, unsigned int reg_base)
148762306a36Sopenharmony_ci{
148862306a36Sopenharmony_ci	struct sm501fb_par *par;
148962306a36Sopenharmony_ci	struct sm501fb_info *info;
149062306a36Sopenharmony_ci	int ret;
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci	if (fbi == NULL)
149362306a36Sopenharmony_ci		return 0;
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci	par = fbi->par;
149662306a36Sopenharmony_ci	info = par->info;
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	par->cursor_regs = info->regs + reg_base;
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci	ret = sm501_alloc_mem(info, &par->cursor, SM501_MEMF_CURSOR, 1024,
150162306a36Sopenharmony_ci			      fbi->fix.smem_len);
150262306a36Sopenharmony_ci	if (ret < 0)
150362306a36Sopenharmony_ci		return ret;
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci	/* initialise the colour registers */
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_ci	smc501_writel(par->cursor.sm_addr,
150862306a36Sopenharmony_ci			par->cursor_regs + SM501_OFF_HWC_ADDR);
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	smc501_writel(0x00, par->cursor_regs + SM501_OFF_HWC_LOC);
151162306a36Sopenharmony_ci	smc501_writel(0x00, par->cursor_regs + SM501_OFF_HWC_COLOR_1_2);
151262306a36Sopenharmony_ci	smc501_writel(0x00, par->cursor_regs + SM501_OFF_HWC_COLOR_3);
151362306a36Sopenharmony_ci	sm501fb_sync_regs(info);
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci	return 0;
151662306a36Sopenharmony_ci}
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci/* sm501fb_info_start
151962306a36Sopenharmony_ci *
152062306a36Sopenharmony_ci * fills the par structure claiming resources and remapping etc.
152162306a36Sopenharmony_ci*/
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_cistatic int sm501fb_start(struct sm501fb_info *info,
152462306a36Sopenharmony_ci			 struct platform_device *pdev)
152562306a36Sopenharmony_ci{
152662306a36Sopenharmony_ci	struct resource	*res;
152762306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
152862306a36Sopenharmony_ci	int k;
152962306a36Sopenharmony_ci	int ret;
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci	info->irq = ret = platform_get_irq(pdev, 0);
153262306a36Sopenharmony_ci	if (ret < 0) {
153362306a36Sopenharmony_ci		/* we currently do not use the IRQ */
153462306a36Sopenharmony_ci		dev_warn(dev, "no irq for device\n");
153562306a36Sopenharmony_ci	}
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	/* allocate, reserve and remap resources for display
153862306a36Sopenharmony_ci	 * controller registers */
153962306a36Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
154062306a36Sopenharmony_ci	if (res == NULL) {
154162306a36Sopenharmony_ci		dev_err(dev, "no resource definition for registers\n");
154262306a36Sopenharmony_ci		ret = -ENOENT;
154362306a36Sopenharmony_ci		goto err_release;
154462306a36Sopenharmony_ci	}
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci	info->regs_res = request_mem_region(res->start,
154762306a36Sopenharmony_ci					    resource_size(res),
154862306a36Sopenharmony_ci					    pdev->name);
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci	if (info->regs_res == NULL) {
155162306a36Sopenharmony_ci		dev_err(dev, "cannot claim registers\n");
155262306a36Sopenharmony_ci		ret = -ENXIO;
155362306a36Sopenharmony_ci		goto err_release;
155462306a36Sopenharmony_ci	}
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci	info->regs = ioremap(res->start, resource_size(res));
155762306a36Sopenharmony_ci	if (info->regs == NULL) {
155862306a36Sopenharmony_ci		dev_err(dev, "cannot remap registers\n");
155962306a36Sopenharmony_ci		ret = -ENXIO;
156062306a36Sopenharmony_ci		goto err_regs_res;
156162306a36Sopenharmony_ci	}
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci	/* allocate, reserve and remap resources for 2d
156462306a36Sopenharmony_ci	 * controller registers */
156562306a36Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
156662306a36Sopenharmony_ci	if (res == NULL) {
156762306a36Sopenharmony_ci		dev_err(dev, "no resource definition for 2d registers\n");
156862306a36Sopenharmony_ci		ret = -ENOENT;
156962306a36Sopenharmony_ci		goto err_regs_map;
157062306a36Sopenharmony_ci	}
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci	info->regs2d_res = request_mem_region(res->start,
157362306a36Sopenharmony_ci					      resource_size(res),
157462306a36Sopenharmony_ci					      pdev->name);
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci	if (info->regs2d_res == NULL) {
157762306a36Sopenharmony_ci		dev_err(dev, "cannot claim registers\n");
157862306a36Sopenharmony_ci		ret = -ENXIO;
157962306a36Sopenharmony_ci		goto err_regs_map;
158062306a36Sopenharmony_ci	}
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci	info->regs2d = ioremap(res->start, resource_size(res));
158362306a36Sopenharmony_ci	if (info->regs2d == NULL) {
158462306a36Sopenharmony_ci		dev_err(dev, "cannot remap registers\n");
158562306a36Sopenharmony_ci		ret = -ENXIO;
158662306a36Sopenharmony_ci		goto err_regs2d_res;
158762306a36Sopenharmony_ci	}
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	/* allocate, reserve resources for framebuffer */
159062306a36Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
159162306a36Sopenharmony_ci	if (res == NULL) {
159262306a36Sopenharmony_ci		dev_err(dev, "no memory resource defined\n");
159362306a36Sopenharmony_ci		ret = -ENXIO;
159462306a36Sopenharmony_ci		goto err_regs2d_map;
159562306a36Sopenharmony_ci	}
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci	info->fbmem_res = request_mem_region(res->start,
159862306a36Sopenharmony_ci					     resource_size(res),
159962306a36Sopenharmony_ci					     pdev->name);
160062306a36Sopenharmony_ci	if (info->fbmem_res == NULL) {
160162306a36Sopenharmony_ci		dev_err(dev, "cannot claim framebuffer\n");
160262306a36Sopenharmony_ci		ret = -ENXIO;
160362306a36Sopenharmony_ci		goto err_regs2d_map;
160462306a36Sopenharmony_ci	}
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ci	info->fbmem = ioremap(res->start, resource_size(res));
160762306a36Sopenharmony_ci	if (info->fbmem == NULL) {
160862306a36Sopenharmony_ci		dev_err(dev, "cannot remap framebuffer\n");
160962306a36Sopenharmony_ci		ret = -ENXIO;
161062306a36Sopenharmony_ci		goto err_mem_res;
161162306a36Sopenharmony_ci	}
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci	info->fbmem_len = resource_size(res);
161462306a36Sopenharmony_ci
161562306a36Sopenharmony_ci	/* clear framebuffer memory - avoids garbage data on unused fb */
161662306a36Sopenharmony_ci	memset_io(info->fbmem, 0, info->fbmem_len);
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci	/* clear palette ram - undefined at power on */
161962306a36Sopenharmony_ci	for (k = 0; k < (256 * 3); k++)
162062306a36Sopenharmony_ci		smc501_writel(0, info->regs + SM501_DC_PANEL_PALETTE + (k * 4));
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci	/* enable display controller */
162362306a36Sopenharmony_ci	sm501_unit_power(dev->parent, SM501_GATE_DISPLAY, 1);
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	/* enable 2d controller */
162662306a36Sopenharmony_ci	sm501_unit_power(dev->parent, SM501_GATE_2D_ENGINE, 1);
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci	/* setup cursors */
162962306a36Sopenharmony_ci	sm501_init_cursor(info->fb[HEAD_CRT], SM501_DC_CRT_HWC_ADDR);
163062306a36Sopenharmony_ci	sm501_init_cursor(info->fb[HEAD_PANEL], SM501_DC_PANEL_HWC_ADDR);
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_ci	return 0; /* everything is setup */
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_ci err_mem_res:
163562306a36Sopenharmony_ci	release_mem_region(info->fbmem_res->start,
163662306a36Sopenharmony_ci			   resource_size(info->fbmem_res));
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci err_regs2d_map:
163962306a36Sopenharmony_ci	iounmap(info->regs2d);
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci err_regs2d_res:
164262306a36Sopenharmony_ci	release_mem_region(info->regs2d_res->start,
164362306a36Sopenharmony_ci			   resource_size(info->regs2d_res));
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci err_regs_map:
164662306a36Sopenharmony_ci	iounmap(info->regs);
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci err_regs_res:
164962306a36Sopenharmony_ci	release_mem_region(info->regs_res->start,
165062306a36Sopenharmony_ci			   resource_size(info->regs_res));
165162306a36Sopenharmony_ci
165262306a36Sopenharmony_ci err_release:
165362306a36Sopenharmony_ci	return ret;
165462306a36Sopenharmony_ci}
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_cistatic void sm501fb_stop(struct sm501fb_info *info)
165762306a36Sopenharmony_ci{
165862306a36Sopenharmony_ci	/* disable display controller */
165962306a36Sopenharmony_ci	sm501_unit_power(info->dev->parent, SM501_GATE_DISPLAY, 0);
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci	iounmap(info->fbmem);
166262306a36Sopenharmony_ci	release_mem_region(info->fbmem_res->start,
166362306a36Sopenharmony_ci			   resource_size(info->fbmem_res));
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_ci	iounmap(info->regs2d);
166662306a36Sopenharmony_ci	release_mem_region(info->regs2d_res->start,
166762306a36Sopenharmony_ci			   resource_size(info->regs2d_res));
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_ci	iounmap(info->regs);
167062306a36Sopenharmony_ci	release_mem_region(info->regs_res->start,
167162306a36Sopenharmony_ci			   resource_size(info->regs_res));
167262306a36Sopenharmony_ci}
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_cistatic int sm501fb_init_fb(struct fb_info *fb, enum sm501_controller head,
167562306a36Sopenharmony_ci			   const char *fbname)
167662306a36Sopenharmony_ci{
167762306a36Sopenharmony_ci	struct sm501_platdata_fbsub *pd;
167862306a36Sopenharmony_ci	struct sm501fb_par *par = fb->par;
167962306a36Sopenharmony_ci	struct sm501fb_info *info = par->info;
168062306a36Sopenharmony_ci	unsigned long ctrl;
168162306a36Sopenharmony_ci	unsigned int enable;
168262306a36Sopenharmony_ci	int ret;
168362306a36Sopenharmony_ci
168462306a36Sopenharmony_ci	switch (head) {
168562306a36Sopenharmony_ci	case HEAD_CRT:
168662306a36Sopenharmony_ci		pd = info->pdata->fb_crt;
168762306a36Sopenharmony_ci		ctrl = smc501_readl(info->regs + SM501_DC_CRT_CONTROL);
168862306a36Sopenharmony_ci		enable = (ctrl & SM501_DC_CRT_CONTROL_ENABLE) ? 1 : 0;
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci		/* ensure we set the correct source register */
169162306a36Sopenharmony_ci		if (info->pdata->fb_route != SM501_FB_CRT_PANEL) {
169262306a36Sopenharmony_ci			ctrl |= SM501_DC_CRT_CONTROL_SEL;
169362306a36Sopenharmony_ci			smc501_writel(ctrl, info->regs + SM501_DC_CRT_CONTROL);
169462306a36Sopenharmony_ci		}
169562306a36Sopenharmony_ci
169662306a36Sopenharmony_ci		break;
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_ci	case HEAD_PANEL:
169962306a36Sopenharmony_ci		pd = info->pdata->fb_pnl;
170062306a36Sopenharmony_ci		ctrl = smc501_readl(info->regs + SM501_DC_PANEL_CONTROL);
170162306a36Sopenharmony_ci		enable = (ctrl & SM501_DC_PANEL_CONTROL_EN) ? 1 : 0;
170262306a36Sopenharmony_ci		break;
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_ci	default:
170562306a36Sopenharmony_ci		pd = NULL;		/* stop compiler warnings */
170662306a36Sopenharmony_ci		ctrl = 0;
170762306a36Sopenharmony_ci		enable = 0;
170862306a36Sopenharmony_ci		BUG();
170962306a36Sopenharmony_ci	}
171062306a36Sopenharmony_ci
171162306a36Sopenharmony_ci	dev_info(info->dev, "fb %s %sabled at start\n",
171262306a36Sopenharmony_ci		 fbname, enable ? "en" : "dis");
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_ci	/* check to see if our routing allows this */
171562306a36Sopenharmony_ci
171662306a36Sopenharmony_ci	if (head == HEAD_CRT && info->pdata->fb_route == SM501_FB_CRT_PANEL) {
171762306a36Sopenharmony_ci		ctrl &= ~SM501_DC_CRT_CONTROL_SEL;
171862306a36Sopenharmony_ci		smc501_writel(ctrl, info->regs + SM501_DC_CRT_CONTROL);
171962306a36Sopenharmony_ci		enable = 0;
172062306a36Sopenharmony_ci	}
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ci	strscpy(fb->fix.id, fbname, sizeof(fb->fix.id));
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci	memcpy(&par->ops,
172562306a36Sopenharmony_ci	       (head == HEAD_CRT) ? &sm501fb_ops_crt : &sm501fb_ops_pnl,
172662306a36Sopenharmony_ci	       sizeof(struct fb_ops));
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_ci	/* update ops dependent on what we've been passed */
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_ci	if ((pd->flags & SM501FB_FLAG_USE_HWCURSOR) == 0)
173162306a36Sopenharmony_ci		par->ops.fb_cursor = NULL;
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_ci	fb->fbops = &par->ops;
173462306a36Sopenharmony_ci	fb->flags = FBINFO_READS_FAST |
173562306a36Sopenharmony_ci		FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT |
173662306a36Sopenharmony_ci		FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN;
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_ci#if defined(CONFIG_OF)
173962306a36Sopenharmony_ci#ifdef __BIG_ENDIAN
174062306a36Sopenharmony_ci	if (of_property_read_bool(info->dev->parent->of_node, "little-endian"))
174162306a36Sopenharmony_ci		fb->flags |= FBINFO_FOREIGN_ENDIAN;
174262306a36Sopenharmony_ci#else
174362306a36Sopenharmony_ci	if (of_property_read_bool(info->dev->parent->of_node, "big-endian"))
174462306a36Sopenharmony_ci		fb->flags |= FBINFO_FOREIGN_ENDIAN;
174562306a36Sopenharmony_ci#endif
174662306a36Sopenharmony_ci#endif
174762306a36Sopenharmony_ci	/* fixed data */
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_ci	fb->fix.type		= FB_TYPE_PACKED_PIXELS;
175062306a36Sopenharmony_ci	fb->fix.type_aux	= 0;
175162306a36Sopenharmony_ci	fb->fix.xpanstep	= 1;
175262306a36Sopenharmony_ci	fb->fix.ypanstep	= 1;
175362306a36Sopenharmony_ci	fb->fix.ywrapstep	= 0;
175462306a36Sopenharmony_ci	fb->fix.accel		= FB_ACCEL_NONE;
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_ci	/* screenmode */
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci	fb->var.nonstd		= 0;
175962306a36Sopenharmony_ci	fb->var.activate	= FB_ACTIVATE_NOW;
176062306a36Sopenharmony_ci	fb->var.accel_flags	= 0;
176162306a36Sopenharmony_ci	fb->var.vmode		= FB_VMODE_NONINTERLACED;
176262306a36Sopenharmony_ci	fb->var.bits_per_pixel  = 16;
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci	if (info->edid_data) {
176562306a36Sopenharmony_ci			/* Now build modedb from EDID */
176662306a36Sopenharmony_ci			fb_edid_to_monspecs(info->edid_data, &fb->monspecs);
176762306a36Sopenharmony_ci			fb_videomode_to_modelist(fb->monspecs.modedb,
176862306a36Sopenharmony_ci						 fb->monspecs.modedb_len,
176962306a36Sopenharmony_ci						 &fb->modelist);
177062306a36Sopenharmony_ci	}
177162306a36Sopenharmony_ci
177262306a36Sopenharmony_ci	if (enable && (pd->flags & SM501FB_FLAG_USE_INIT_MODE) && 0) {
177362306a36Sopenharmony_ci		/* TODO read the mode from the current display */
177462306a36Sopenharmony_ci	} else {
177562306a36Sopenharmony_ci		if (pd->def_mode) {
177662306a36Sopenharmony_ci			dev_info(info->dev, "using supplied mode\n");
177762306a36Sopenharmony_ci			fb_videomode_to_var(&fb->var, pd->def_mode);
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_ci			fb->var.bits_per_pixel = pd->def_bpp ? pd->def_bpp : 8;
178062306a36Sopenharmony_ci			fb->var.xres_virtual = fb->var.xres;
178162306a36Sopenharmony_ci			fb->var.yres_virtual = fb->var.yres;
178262306a36Sopenharmony_ci		} else {
178362306a36Sopenharmony_ci			if (info->edid_data) {
178462306a36Sopenharmony_ci				ret = fb_find_mode(&fb->var, fb, fb_mode,
178562306a36Sopenharmony_ci					fb->monspecs.modedb,
178662306a36Sopenharmony_ci					fb->monspecs.modedb_len,
178762306a36Sopenharmony_ci					&sm501_default_mode, default_bpp);
178862306a36Sopenharmony_ci				/* edid_data is no longer needed, free it */
178962306a36Sopenharmony_ci				kfree(info->edid_data);
179062306a36Sopenharmony_ci			} else {
179162306a36Sopenharmony_ci				ret = fb_find_mode(&fb->var, fb,
179262306a36Sopenharmony_ci					   NULL, NULL, 0, NULL, 8);
179362306a36Sopenharmony_ci			}
179462306a36Sopenharmony_ci
179562306a36Sopenharmony_ci			switch (ret) {
179662306a36Sopenharmony_ci			case 1:
179762306a36Sopenharmony_ci				dev_info(info->dev, "using mode specified in "
179862306a36Sopenharmony_ci						"@mode\n");
179962306a36Sopenharmony_ci				break;
180062306a36Sopenharmony_ci			case 2:
180162306a36Sopenharmony_ci				dev_info(info->dev, "using mode specified in "
180262306a36Sopenharmony_ci					"@mode with ignored refresh rate\n");
180362306a36Sopenharmony_ci				break;
180462306a36Sopenharmony_ci			case 3:
180562306a36Sopenharmony_ci				dev_info(info->dev, "using mode default "
180662306a36Sopenharmony_ci					"mode\n");
180762306a36Sopenharmony_ci				break;
180862306a36Sopenharmony_ci			case 4:
180962306a36Sopenharmony_ci				dev_info(info->dev, "using mode from list\n");
181062306a36Sopenharmony_ci				break;
181162306a36Sopenharmony_ci			default:
181262306a36Sopenharmony_ci				dev_info(info->dev, "ret = %d\n", ret);
181362306a36Sopenharmony_ci				dev_info(info->dev, "failed to find mode\n");
181462306a36Sopenharmony_ci				return -EINVAL;
181562306a36Sopenharmony_ci			}
181662306a36Sopenharmony_ci		}
181762306a36Sopenharmony_ci	}
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_ci	/* initialise and set the palette */
182062306a36Sopenharmony_ci	if (fb_alloc_cmap(&fb->cmap, NR_PALETTE, 0)) {
182162306a36Sopenharmony_ci		dev_err(info->dev, "failed to allocate cmap memory\n");
182262306a36Sopenharmony_ci		return -ENOMEM;
182362306a36Sopenharmony_ci	}
182462306a36Sopenharmony_ci	fb_set_cmap(&fb->cmap, fb);
182562306a36Sopenharmony_ci
182662306a36Sopenharmony_ci	ret = (fb->fbops->fb_check_var)(&fb->var, fb);
182762306a36Sopenharmony_ci	if (ret)
182862306a36Sopenharmony_ci		dev_err(info->dev, "check_var() failed on initial setup?\n");
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_ci	return 0;
183162306a36Sopenharmony_ci}
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_ci/* default platform data if none is supplied (ie, PCI device) */
183462306a36Sopenharmony_ci
183562306a36Sopenharmony_cistatic struct sm501_platdata_fbsub sm501fb_pdata_crt = {
183662306a36Sopenharmony_ci	.flags		= (SM501FB_FLAG_USE_INIT_MODE |
183762306a36Sopenharmony_ci			   SM501FB_FLAG_USE_HWCURSOR |
183862306a36Sopenharmony_ci			   SM501FB_FLAG_USE_HWACCEL |
183962306a36Sopenharmony_ci			   SM501FB_FLAG_DISABLE_AT_EXIT),
184062306a36Sopenharmony_ci
184162306a36Sopenharmony_ci};
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_cistatic struct sm501_platdata_fbsub sm501fb_pdata_pnl = {
184462306a36Sopenharmony_ci	.flags		= (SM501FB_FLAG_USE_INIT_MODE |
184562306a36Sopenharmony_ci			   SM501FB_FLAG_USE_HWCURSOR |
184662306a36Sopenharmony_ci			   SM501FB_FLAG_USE_HWACCEL |
184762306a36Sopenharmony_ci			   SM501FB_FLAG_DISABLE_AT_EXIT),
184862306a36Sopenharmony_ci};
184962306a36Sopenharmony_ci
185062306a36Sopenharmony_cistatic struct sm501_platdata_fb sm501fb_def_pdata = {
185162306a36Sopenharmony_ci	.fb_route		= SM501_FB_OWN,
185262306a36Sopenharmony_ci	.fb_crt			= &sm501fb_pdata_crt,
185362306a36Sopenharmony_ci	.fb_pnl			= &sm501fb_pdata_pnl,
185462306a36Sopenharmony_ci};
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_cistatic char driver_name_crt[] = "sm501fb-crt";
185762306a36Sopenharmony_cistatic char driver_name_pnl[] = "sm501fb-panel";
185862306a36Sopenharmony_ci
185962306a36Sopenharmony_cistatic int sm501fb_probe_one(struct sm501fb_info *info,
186062306a36Sopenharmony_ci			     enum sm501_controller head)
186162306a36Sopenharmony_ci{
186262306a36Sopenharmony_ci	unsigned char *name = (head == HEAD_CRT) ? "crt" : "panel";
186362306a36Sopenharmony_ci	struct sm501_platdata_fbsub *pd;
186462306a36Sopenharmony_ci	struct sm501fb_par *par;
186562306a36Sopenharmony_ci	struct fb_info *fbi;
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ci	pd = (head == HEAD_CRT) ? info->pdata->fb_crt : info->pdata->fb_pnl;
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_ci	/* Do not initialise if we've not been given any platform data */
187062306a36Sopenharmony_ci	if (pd == NULL) {
187162306a36Sopenharmony_ci		dev_info(info->dev, "no data for fb %s (disabled)\n", name);
187262306a36Sopenharmony_ci		return 0;
187362306a36Sopenharmony_ci	}
187462306a36Sopenharmony_ci
187562306a36Sopenharmony_ci	fbi = framebuffer_alloc(sizeof(struct sm501fb_par), info->dev);
187662306a36Sopenharmony_ci	if (!fbi)
187762306a36Sopenharmony_ci		return -ENOMEM;
187862306a36Sopenharmony_ci
187962306a36Sopenharmony_ci	par = fbi->par;
188062306a36Sopenharmony_ci	par->info = info;
188162306a36Sopenharmony_ci	par->head = head;
188262306a36Sopenharmony_ci	fbi->pseudo_palette = &par->pseudo_palette;
188362306a36Sopenharmony_ci
188462306a36Sopenharmony_ci	info->fb[head] = fbi;
188562306a36Sopenharmony_ci
188662306a36Sopenharmony_ci	return 0;
188762306a36Sopenharmony_ci}
188862306a36Sopenharmony_ci
188962306a36Sopenharmony_ci/* Free up anything allocated by sm501fb_init_fb */
189062306a36Sopenharmony_ci
189162306a36Sopenharmony_cistatic void sm501_free_init_fb(struct sm501fb_info *info,
189262306a36Sopenharmony_ci				enum sm501_controller head)
189362306a36Sopenharmony_ci{
189462306a36Sopenharmony_ci	struct fb_info *fbi = info->fb[head];
189562306a36Sopenharmony_ci
189662306a36Sopenharmony_ci	if (!fbi)
189762306a36Sopenharmony_ci		return;
189862306a36Sopenharmony_ci
189962306a36Sopenharmony_ci	fb_dealloc_cmap(&fbi->cmap);
190062306a36Sopenharmony_ci}
190162306a36Sopenharmony_ci
190262306a36Sopenharmony_cistatic int sm501fb_start_one(struct sm501fb_info *info,
190362306a36Sopenharmony_ci			     enum sm501_controller head, const char *drvname)
190462306a36Sopenharmony_ci{
190562306a36Sopenharmony_ci	struct fb_info *fbi = info->fb[head];
190662306a36Sopenharmony_ci	int ret;
190762306a36Sopenharmony_ci
190862306a36Sopenharmony_ci	if (!fbi)
190962306a36Sopenharmony_ci		return 0;
191062306a36Sopenharmony_ci
191162306a36Sopenharmony_ci	mutex_init(&info->fb[head]->mm_lock);
191262306a36Sopenharmony_ci
191362306a36Sopenharmony_ci	ret = sm501fb_init_fb(info->fb[head], head, drvname);
191462306a36Sopenharmony_ci	if (ret) {
191562306a36Sopenharmony_ci		dev_err(info->dev, "cannot initialise fb %s\n", drvname);
191662306a36Sopenharmony_ci		return ret;
191762306a36Sopenharmony_ci	}
191862306a36Sopenharmony_ci
191962306a36Sopenharmony_ci	ret = register_framebuffer(info->fb[head]);
192062306a36Sopenharmony_ci	if (ret) {
192162306a36Sopenharmony_ci		dev_err(info->dev, "failed to register fb %s\n", drvname);
192262306a36Sopenharmony_ci		sm501_free_init_fb(info, head);
192362306a36Sopenharmony_ci		return ret;
192462306a36Sopenharmony_ci	}
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_ci	dev_info(info->dev, "fb%d: %s frame buffer\n", fbi->node, fbi->fix.id);
192762306a36Sopenharmony_ci
192862306a36Sopenharmony_ci	return 0;
192962306a36Sopenharmony_ci}
193062306a36Sopenharmony_ci
193162306a36Sopenharmony_cistatic int sm501fb_probe(struct platform_device *pdev)
193262306a36Sopenharmony_ci{
193362306a36Sopenharmony_ci	struct sm501fb_info *info;
193462306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
193562306a36Sopenharmony_ci	int ret;
193662306a36Sopenharmony_ci
193762306a36Sopenharmony_ci	/* allocate our framebuffers */
193862306a36Sopenharmony_ci	info = kzalloc(sizeof(*info), GFP_KERNEL);
193962306a36Sopenharmony_ci	if (!info) {
194062306a36Sopenharmony_ci		dev_err(dev, "failed to allocate state\n");
194162306a36Sopenharmony_ci		return -ENOMEM;
194262306a36Sopenharmony_ci	}
194362306a36Sopenharmony_ci
194462306a36Sopenharmony_ci	info->dev = dev = &pdev->dev;
194562306a36Sopenharmony_ci	platform_set_drvdata(pdev, info);
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_ci	if (dev->parent->platform_data) {
194862306a36Sopenharmony_ci		struct sm501_platdata *pd = dev->parent->platform_data;
194962306a36Sopenharmony_ci		info->pdata = pd->fb;
195062306a36Sopenharmony_ci	}
195162306a36Sopenharmony_ci
195262306a36Sopenharmony_ci	if (info->pdata == NULL) {
195362306a36Sopenharmony_ci		int found = 0;
195462306a36Sopenharmony_ci#if defined(CONFIG_OF)
195562306a36Sopenharmony_ci		struct device_node *np = pdev->dev.parent->of_node;
195662306a36Sopenharmony_ci		const u8 *prop;
195762306a36Sopenharmony_ci		const char *cp;
195862306a36Sopenharmony_ci		int len;
195962306a36Sopenharmony_ci
196062306a36Sopenharmony_ci		info->pdata = &sm501fb_def_pdata;
196162306a36Sopenharmony_ci		if (np) {
196262306a36Sopenharmony_ci			/* Get EDID */
196362306a36Sopenharmony_ci			cp = of_get_property(np, "mode", &len);
196462306a36Sopenharmony_ci			if (cp)
196562306a36Sopenharmony_ci				strcpy(fb_mode, cp);
196662306a36Sopenharmony_ci			prop = of_get_property(np, "edid", &len);
196762306a36Sopenharmony_ci			if (prop && len == EDID_LENGTH) {
196862306a36Sopenharmony_ci				info->edid_data = kmemdup(prop, EDID_LENGTH,
196962306a36Sopenharmony_ci							  GFP_KERNEL);
197062306a36Sopenharmony_ci				if (info->edid_data)
197162306a36Sopenharmony_ci					found = 1;
197262306a36Sopenharmony_ci			}
197362306a36Sopenharmony_ci		}
197462306a36Sopenharmony_ci#endif
197562306a36Sopenharmony_ci		if (!found) {
197662306a36Sopenharmony_ci			dev_info(dev, "using default configuration data\n");
197762306a36Sopenharmony_ci			info->pdata = &sm501fb_def_pdata;
197862306a36Sopenharmony_ci		}
197962306a36Sopenharmony_ci	}
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci	/* probe for the presence of each panel */
198262306a36Sopenharmony_ci
198362306a36Sopenharmony_ci	ret = sm501fb_probe_one(info, HEAD_CRT);
198462306a36Sopenharmony_ci	if (ret < 0) {
198562306a36Sopenharmony_ci		dev_err(dev, "failed to probe CRT\n");
198662306a36Sopenharmony_ci		goto err_alloc;
198762306a36Sopenharmony_ci	}
198862306a36Sopenharmony_ci
198962306a36Sopenharmony_ci	ret = sm501fb_probe_one(info, HEAD_PANEL);
199062306a36Sopenharmony_ci	if (ret < 0) {
199162306a36Sopenharmony_ci		dev_err(dev, "failed to probe PANEL\n");
199262306a36Sopenharmony_ci		goto err_probed_crt;
199362306a36Sopenharmony_ci	}
199462306a36Sopenharmony_ci
199562306a36Sopenharmony_ci	if (info->fb[HEAD_PANEL] == NULL &&
199662306a36Sopenharmony_ci	    info->fb[HEAD_CRT] == NULL) {
199762306a36Sopenharmony_ci		dev_err(dev, "no framebuffers found\n");
199862306a36Sopenharmony_ci		ret = -ENODEV;
199962306a36Sopenharmony_ci		goto err_alloc;
200062306a36Sopenharmony_ci	}
200162306a36Sopenharmony_ci
200262306a36Sopenharmony_ci	/* get the resources for both of the framebuffers */
200362306a36Sopenharmony_ci
200462306a36Sopenharmony_ci	ret = sm501fb_start(info, pdev);
200562306a36Sopenharmony_ci	if (ret) {
200662306a36Sopenharmony_ci		dev_err(dev, "cannot initialise SM501\n");
200762306a36Sopenharmony_ci		goto err_probed_panel;
200862306a36Sopenharmony_ci	}
200962306a36Sopenharmony_ci
201062306a36Sopenharmony_ci	ret = sm501fb_start_one(info, HEAD_CRT, driver_name_crt);
201162306a36Sopenharmony_ci	if (ret) {
201262306a36Sopenharmony_ci		dev_err(dev, "failed to start CRT\n");
201362306a36Sopenharmony_ci		goto err_started;
201462306a36Sopenharmony_ci	}
201562306a36Sopenharmony_ci
201662306a36Sopenharmony_ci	ret = sm501fb_start_one(info, HEAD_PANEL, driver_name_pnl);
201762306a36Sopenharmony_ci	if (ret) {
201862306a36Sopenharmony_ci		dev_err(dev, "failed to start Panel\n");
201962306a36Sopenharmony_ci		goto err_started_crt;
202062306a36Sopenharmony_ci	}
202162306a36Sopenharmony_ci
202262306a36Sopenharmony_ci	/* we registered, return ok */
202362306a36Sopenharmony_ci	return 0;
202462306a36Sopenharmony_ci
202562306a36Sopenharmony_cierr_started_crt:
202662306a36Sopenharmony_ci	unregister_framebuffer(info->fb[HEAD_CRT]);
202762306a36Sopenharmony_ci	sm501_free_init_fb(info, HEAD_CRT);
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_cierr_started:
203062306a36Sopenharmony_ci	sm501fb_stop(info);
203162306a36Sopenharmony_ci
203262306a36Sopenharmony_cierr_probed_panel:
203362306a36Sopenharmony_ci	framebuffer_release(info->fb[HEAD_PANEL]);
203462306a36Sopenharmony_ci
203562306a36Sopenharmony_cierr_probed_crt:
203662306a36Sopenharmony_ci	framebuffer_release(info->fb[HEAD_CRT]);
203762306a36Sopenharmony_ci
203862306a36Sopenharmony_cierr_alloc:
203962306a36Sopenharmony_ci	kfree(info);
204062306a36Sopenharmony_ci
204162306a36Sopenharmony_ci	return ret;
204262306a36Sopenharmony_ci}
204362306a36Sopenharmony_ci
204462306a36Sopenharmony_ci
204562306a36Sopenharmony_ci/*
204662306a36Sopenharmony_ci *  Cleanup
204762306a36Sopenharmony_ci */
204862306a36Sopenharmony_cistatic void sm501fb_remove(struct platform_device *pdev)
204962306a36Sopenharmony_ci{
205062306a36Sopenharmony_ci	struct sm501fb_info *info = platform_get_drvdata(pdev);
205162306a36Sopenharmony_ci	struct fb_info	   *fbinfo_crt = info->fb[0];
205262306a36Sopenharmony_ci	struct fb_info	   *fbinfo_pnl = info->fb[1];
205362306a36Sopenharmony_ci
205462306a36Sopenharmony_ci	sm501_free_init_fb(info, HEAD_CRT);
205562306a36Sopenharmony_ci	sm501_free_init_fb(info, HEAD_PANEL);
205662306a36Sopenharmony_ci
205762306a36Sopenharmony_ci	if (fbinfo_crt)
205862306a36Sopenharmony_ci		unregister_framebuffer(fbinfo_crt);
205962306a36Sopenharmony_ci	if (fbinfo_pnl)
206062306a36Sopenharmony_ci		unregister_framebuffer(fbinfo_pnl);
206162306a36Sopenharmony_ci
206262306a36Sopenharmony_ci	sm501fb_stop(info);
206362306a36Sopenharmony_ci	kfree(info);
206462306a36Sopenharmony_ci
206562306a36Sopenharmony_ci	framebuffer_release(fbinfo_pnl);
206662306a36Sopenharmony_ci	framebuffer_release(fbinfo_crt);
206762306a36Sopenharmony_ci}
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci#ifdef CONFIG_PM
207062306a36Sopenharmony_ci
207162306a36Sopenharmony_cistatic int sm501fb_suspend_fb(struct sm501fb_info *info,
207262306a36Sopenharmony_ci			      enum sm501_controller head)
207362306a36Sopenharmony_ci{
207462306a36Sopenharmony_ci	struct fb_info *fbi = info->fb[head];
207562306a36Sopenharmony_ci	struct sm501fb_par *par;
207662306a36Sopenharmony_ci
207762306a36Sopenharmony_ci	if (!fbi)
207862306a36Sopenharmony_ci		return 0;
207962306a36Sopenharmony_ci
208062306a36Sopenharmony_ci	par = fbi->par;
208162306a36Sopenharmony_ci	if (par->screen.size == 0)
208262306a36Sopenharmony_ci		return 0;
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_ci	/* blank the relevant interface to ensure unit power minimised */
208562306a36Sopenharmony_ci	(par->ops.fb_blank)(FB_BLANK_POWERDOWN, fbi);
208662306a36Sopenharmony_ci
208762306a36Sopenharmony_ci	/* tell console/fb driver we are suspending */
208862306a36Sopenharmony_ci
208962306a36Sopenharmony_ci	console_lock();
209062306a36Sopenharmony_ci	fb_set_suspend(fbi, 1);
209162306a36Sopenharmony_ci	console_unlock();
209262306a36Sopenharmony_ci
209362306a36Sopenharmony_ci	/* backup copies in case chip is powered down over suspend */
209462306a36Sopenharmony_ci
209562306a36Sopenharmony_ci	par->store_fb = vmalloc(par->screen.size);
209662306a36Sopenharmony_ci	if (par->store_fb == NULL) {
209762306a36Sopenharmony_ci		dev_err(info->dev, "no memory to store screen\n");
209862306a36Sopenharmony_ci		return -ENOMEM;
209962306a36Sopenharmony_ci	}
210062306a36Sopenharmony_ci
210162306a36Sopenharmony_ci	par->store_cursor = vmalloc(par->cursor.size);
210262306a36Sopenharmony_ci	if (par->store_cursor == NULL) {
210362306a36Sopenharmony_ci		dev_err(info->dev, "no memory to store cursor\n");
210462306a36Sopenharmony_ci		goto err_nocursor;
210562306a36Sopenharmony_ci	}
210662306a36Sopenharmony_ci
210762306a36Sopenharmony_ci	dev_dbg(info->dev, "suspending screen to %p\n", par->store_fb);
210862306a36Sopenharmony_ci	dev_dbg(info->dev, "suspending cursor to %p\n", par->store_cursor);
210962306a36Sopenharmony_ci
211062306a36Sopenharmony_ci	memcpy_fromio(par->store_fb, par->screen.k_addr, par->screen.size);
211162306a36Sopenharmony_ci	memcpy_fromio(par->store_cursor, par->cursor.k_addr, par->cursor.size);
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_ci	return 0;
211462306a36Sopenharmony_ci
211562306a36Sopenharmony_ci err_nocursor:
211662306a36Sopenharmony_ci	vfree(par->store_fb);
211762306a36Sopenharmony_ci	par->store_fb = NULL;
211862306a36Sopenharmony_ci
211962306a36Sopenharmony_ci	return -ENOMEM;
212062306a36Sopenharmony_ci}
212162306a36Sopenharmony_ci
212262306a36Sopenharmony_cistatic void sm501fb_resume_fb(struct sm501fb_info *info,
212362306a36Sopenharmony_ci			      enum sm501_controller head)
212462306a36Sopenharmony_ci{
212562306a36Sopenharmony_ci	struct fb_info *fbi = info->fb[head];
212662306a36Sopenharmony_ci	struct sm501fb_par *par;
212762306a36Sopenharmony_ci
212862306a36Sopenharmony_ci	if (!fbi)
212962306a36Sopenharmony_ci		return;
213062306a36Sopenharmony_ci
213162306a36Sopenharmony_ci	par = fbi->par;
213262306a36Sopenharmony_ci	if (par->screen.size == 0)
213362306a36Sopenharmony_ci		return;
213462306a36Sopenharmony_ci
213562306a36Sopenharmony_ci	/* re-activate the configuration */
213662306a36Sopenharmony_ci
213762306a36Sopenharmony_ci	(par->ops.fb_set_par)(fbi);
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ci	/* restore the data */
214062306a36Sopenharmony_ci
214162306a36Sopenharmony_ci	dev_dbg(info->dev, "restoring screen from %p\n", par->store_fb);
214262306a36Sopenharmony_ci	dev_dbg(info->dev, "restoring cursor from %p\n", par->store_cursor);
214362306a36Sopenharmony_ci
214462306a36Sopenharmony_ci	if (par->store_fb)
214562306a36Sopenharmony_ci		memcpy_toio(par->screen.k_addr, par->store_fb,
214662306a36Sopenharmony_ci			    par->screen.size);
214762306a36Sopenharmony_ci
214862306a36Sopenharmony_ci	if (par->store_cursor)
214962306a36Sopenharmony_ci		memcpy_toio(par->cursor.k_addr, par->store_cursor,
215062306a36Sopenharmony_ci			    par->cursor.size);
215162306a36Sopenharmony_ci
215262306a36Sopenharmony_ci	console_lock();
215362306a36Sopenharmony_ci	fb_set_suspend(fbi, 0);
215462306a36Sopenharmony_ci	console_unlock();
215562306a36Sopenharmony_ci
215662306a36Sopenharmony_ci	vfree(par->store_fb);
215762306a36Sopenharmony_ci	vfree(par->store_cursor);
215862306a36Sopenharmony_ci}
215962306a36Sopenharmony_ci
216062306a36Sopenharmony_ci
216162306a36Sopenharmony_ci/* suspend and resume support */
216262306a36Sopenharmony_ci
216362306a36Sopenharmony_cistatic int sm501fb_suspend(struct platform_device *pdev, pm_message_t state)
216462306a36Sopenharmony_ci{
216562306a36Sopenharmony_ci	struct sm501fb_info *info = platform_get_drvdata(pdev);
216662306a36Sopenharmony_ci
216762306a36Sopenharmony_ci	/* store crt control to resume with */
216862306a36Sopenharmony_ci	info->pm_crt_ctrl = smc501_readl(info->regs + SM501_DC_CRT_CONTROL);
216962306a36Sopenharmony_ci
217062306a36Sopenharmony_ci	sm501fb_suspend_fb(info, HEAD_CRT);
217162306a36Sopenharmony_ci	sm501fb_suspend_fb(info, HEAD_PANEL);
217262306a36Sopenharmony_ci
217362306a36Sopenharmony_ci	/* turn off the clocks, in case the device is not powered down */
217462306a36Sopenharmony_ci	sm501_unit_power(info->dev->parent, SM501_GATE_DISPLAY, 0);
217562306a36Sopenharmony_ci
217662306a36Sopenharmony_ci	return 0;
217762306a36Sopenharmony_ci}
217862306a36Sopenharmony_ci
217962306a36Sopenharmony_ci#define SM501_CRT_CTRL_SAVE (SM501_DC_CRT_CONTROL_TVP |        \
218062306a36Sopenharmony_ci			     SM501_DC_CRT_CONTROL_SEL)
218162306a36Sopenharmony_ci
218262306a36Sopenharmony_ci
218362306a36Sopenharmony_cistatic int sm501fb_resume(struct platform_device *pdev)
218462306a36Sopenharmony_ci{
218562306a36Sopenharmony_ci	struct sm501fb_info *info = platform_get_drvdata(pdev);
218662306a36Sopenharmony_ci	unsigned long crt_ctrl;
218762306a36Sopenharmony_ci
218862306a36Sopenharmony_ci	sm501_unit_power(info->dev->parent, SM501_GATE_DISPLAY, 1);
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_ci	/* restore the items we want to be saved for crt control */
219162306a36Sopenharmony_ci
219262306a36Sopenharmony_ci	crt_ctrl = smc501_readl(info->regs + SM501_DC_CRT_CONTROL);
219362306a36Sopenharmony_ci	crt_ctrl &= ~SM501_CRT_CTRL_SAVE;
219462306a36Sopenharmony_ci	crt_ctrl |= info->pm_crt_ctrl & SM501_CRT_CTRL_SAVE;
219562306a36Sopenharmony_ci	smc501_writel(crt_ctrl, info->regs + SM501_DC_CRT_CONTROL);
219662306a36Sopenharmony_ci
219762306a36Sopenharmony_ci	sm501fb_resume_fb(info, HEAD_CRT);
219862306a36Sopenharmony_ci	sm501fb_resume_fb(info, HEAD_PANEL);
219962306a36Sopenharmony_ci
220062306a36Sopenharmony_ci	return 0;
220162306a36Sopenharmony_ci}
220262306a36Sopenharmony_ci
220362306a36Sopenharmony_ci#else
220462306a36Sopenharmony_ci#define sm501fb_suspend NULL
220562306a36Sopenharmony_ci#define sm501fb_resume  NULL
220662306a36Sopenharmony_ci#endif
220762306a36Sopenharmony_ci
220862306a36Sopenharmony_cistatic struct platform_driver sm501fb_driver = {
220962306a36Sopenharmony_ci	.probe		= sm501fb_probe,
221062306a36Sopenharmony_ci	.remove_new	= sm501fb_remove,
221162306a36Sopenharmony_ci	.suspend	= sm501fb_suspend,
221262306a36Sopenharmony_ci	.resume		= sm501fb_resume,
221362306a36Sopenharmony_ci	.driver		= {
221462306a36Sopenharmony_ci		.name	= "sm501-fb",
221562306a36Sopenharmony_ci		.dev_groups	= sm501fb_groups,
221662306a36Sopenharmony_ci	},
221762306a36Sopenharmony_ci};
221862306a36Sopenharmony_ci
221962306a36Sopenharmony_cimodule_platform_driver(sm501fb_driver);
222062306a36Sopenharmony_ci
222162306a36Sopenharmony_cimodule_param_named(mode, fb_mode, charp, 0);
222262306a36Sopenharmony_ciMODULE_PARM_DESC(mode,
222362306a36Sopenharmony_ci	"Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" ");
222462306a36Sopenharmony_cimodule_param_named(bpp, default_bpp, ulong, 0);
222562306a36Sopenharmony_ciMODULE_PARM_DESC(bpp, "Specify bit-per-pixel if not specified mode");
222662306a36Sopenharmony_ciMODULE_AUTHOR("Ben Dooks, Vincent Sanders");
222762306a36Sopenharmony_ciMODULE_DESCRIPTION("SM501 Framebuffer driver");
222862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
2229