cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

sm501fb.c (53834B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/* linux/drivers/video/sm501fb.c
      3 *
      4 * Copyright (c) 2006 Simtec Electronics
      5 *	Vincent Sanders <vince@simtec.co.uk>
      6 *	Ben Dooks <ben@simtec.co.uk>
      7 *
      8 * Framebuffer driver for the Silicon Motion SM501
      9 */
     10
     11#include <linux/module.h>
     12#include <linux/kernel.h>
     13#include <linux/errno.h>
     14#include <linux/string.h>
     15#include <linux/mm.h>
     16#include <linux/tty.h>
     17#include <linux/slab.h>
     18#include <linux/delay.h>
     19#include <linux/fb.h>
     20#include <linux/init.h>
     21#include <linux/vmalloc.h>
     22#include <linux/dma-mapping.h>
     23#include <linux/interrupt.h>
     24#include <linux/workqueue.h>
     25#include <linux/wait.h>
     26#include <linux/platform_device.h>
     27#include <linux/clk.h>
     28#include <linux/console.h>
     29#include <linux/io.h>
     30
     31#include <linux/uaccess.h>
     32#include <asm/div64.h>
     33
     34#ifdef CONFIG_PM
     35#include <linux/pm.h>
     36#endif
     37
     38#include <linux/sm501.h>
     39#include <linux/sm501-regs.h>
     40
     41#include "edid.h"
     42
     43static char *fb_mode = "640x480-16@60";
     44static unsigned long default_bpp = 16;
     45
     46static const struct fb_videomode sm501_default_mode = {
     47	.refresh	= 60,
     48	.xres		= 640,
     49	.yres		= 480,
     50	.pixclock	= 20833,
     51	.left_margin	= 142,
     52	.right_margin	= 13,
     53	.upper_margin	= 21,
     54	.lower_margin	= 1,
     55	.hsync_len	= 69,
     56	.vsync_len	= 3,
     57	.sync		= FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
     58	.vmode		= FB_VMODE_NONINTERLACED
     59};
     60
     61#define NR_PALETTE	256
     62
     63enum sm501_controller {
     64	HEAD_CRT	= 0,
     65	HEAD_PANEL	= 1,
     66};
     67
     68/* SM501 memory address.
     69 *
     70 * This structure is used to track memory usage within the SM501 framebuffer
     71 * allocation. The sm_addr field is stored as an offset as it is often used
     72 * against both the physical and mapped addresses.
     73 */
     74struct sm501_mem {
     75	unsigned long	 size;
     76	unsigned long	 sm_addr;	/* offset from base of sm501 fb. */
     77	void __iomem	*k_addr;
     78};
     79
     80/* private data that is shared between all frambuffers* */
     81struct sm501fb_info {
     82	struct device		*dev;
     83	struct fb_info		*fb[2];		/* fb info for both heads */
     84	struct resource		*fbmem_res;	/* framebuffer resource */
     85	struct resource		*regs_res;	/* registers resource */
     86	struct resource		*regs2d_res;	/* 2d registers resource */
     87	struct sm501_platdata_fb *pdata;	/* our platform data */
     88
     89	unsigned long		 pm_crt_ctrl;	/* pm: crt ctrl save */
     90
     91	int			 irq;
     92	int			 swap_endian;	/* set to swap rgb=>bgr */
     93	void __iomem		*regs;		/* remapped registers */
     94	void __iomem		*regs2d;	/* 2d remapped registers */
     95	void __iomem		*fbmem;		/* remapped framebuffer */
     96	size_t			 fbmem_len;	/* length of remapped region */
     97	u8 *edid_data;
     98};
     99
    100/* per-framebuffer private data */
    101struct sm501fb_par {
    102	u32			 pseudo_palette[16];
    103
    104	enum sm501_controller	 head;
    105	struct sm501_mem	 cursor;
    106	struct sm501_mem	 screen;
    107	struct fb_ops		 ops;
    108
    109	void			*store_fb;
    110	void			*store_cursor;
    111	void __iomem		*cursor_regs;
    112	struct sm501fb_info	*info;
    113};
    114
    115/* Helper functions */
    116
    117static inline int h_total(struct fb_var_screeninfo *var)
    118{
    119	return var->xres + var->left_margin +
    120		var->right_margin + var->hsync_len;
    121}
    122
    123static inline int v_total(struct fb_var_screeninfo *var)
    124{
    125	return var->yres + var->upper_margin +
    126		var->lower_margin + var->vsync_len;
    127}
    128
    129/* sm501fb_sync_regs()
    130 *
    131 * This call is mainly for PCI bus systems where we need to
    132 * ensure that any writes to the bus are completed before the
    133 * next phase, or after completing a function.
    134*/
    135
    136static inline void sm501fb_sync_regs(struct sm501fb_info *info)
    137{
    138	smc501_readl(info->regs);
    139}
    140
    141/* sm501_alloc_mem
    142 *
    143 * This is an attempt to lay out memory for the two framebuffers and
    144 * everything else
    145 *
    146 * |fbmem_res->start					       fbmem_res->end|
    147 * |									     |
    148 * |fb[0].fix.smem_start    |	      |fb[1].fix.smem_start    |     2K	     |
    149 * |-> fb[0].fix.smem_len <-| spare   |-> fb[1].fix.smem_len <-|-> cursors <-|
    150 *
    151 * The "spare" space is for the 2d engine data
    152 * the fixed is space for the cursors (2x1Kbyte)
    153 *
    154 * we need to allocate memory for the 2D acceleration engine
    155 * command list and the data for the engine to deal with.
    156 *
    157 * - all allocations must be 128bit aligned
    158 * - cursors are 64x64x2 bits (1Kbyte)
    159 *
    160 */
    161
    162#define SM501_MEMF_CURSOR		(1)
    163#define SM501_MEMF_PANEL		(2)
    164#define SM501_MEMF_CRT			(4)
    165#define SM501_MEMF_ACCEL		(8)
    166
    167static int sm501_alloc_mem(struct sm501fb_info *inf, struct sm501_mem *mem,
    168			   unsigned int why, size_t size, u32 smem_len)
    169{
    170	struct sm501fb_par *par;
    171	struct fb_info *fbi;
    172	unsigned int ptr;
    173	unsigned int end;
    174
    175	switch (why) {
    176	case SM501_MEMF_CURSOR:
    177		ptr = inf->fbmem_len - size;
    178		inf->fbmem_len = ptr;	/* adjust available memory. */
    179		break;
    180
    181	case SM501_MEMF_PANEL:
    182		if (size > inf->fbmem_len)
    183			return -ENOMEM;
    184
    185		ptr = inf->fbmem_len - size;
    186		fbi = inf->fb[HEAD_CRT];
    187
    188		/* round down, some programs such as directfb do not draw
    189		 * 0,0 correctly unless the start is aligned to a page start.
    190		 */
    191
    192		if (ptr > 0)
    193			ptr &= ~(PAGE_SIZE - 1);
    194
    195		if (fbi && ptr < smem_len)
    196			return -ENOMEM;
    197
    198		break;
    199
    200	case SM501_MEMF_CRT:
    201		ptr = 0;
    202
    203		/* check to see if we have panel memory allocated
    204		 * which would put an limit on available memory. */
    205
    206		fbi = inf->fb[HEAD_PANEL];
    207		if (fbi) {
    208			par = fbi->par;
    209			end = par->screen.k_addr ? par->screen.sm_addr : inf->fbmem_len;
    210		} else
    211			end = inf->fbmem_len;
    212
    213		if ((ptr + size) > end)
    214			return -ENOMEM;
    215
    216		break;
    217
    218	case SM501_MEMF_ACCEL:
    219		fbi = inf->fb[HEAD_CRT];
    220		ptr = fbi ? smem_len : 0;
    221
    222		fbi = inf->fb[HEAD_PANEL];
    223		if (fbi) {
    224			par = fbi->par;
    225			end = par->screen.sm_addr;
    226		} else
    227			end = inf->fbmem_len;
    228
    229		if ((ptr + size) > end)
    230			return -ENOMEM;
    231
    232		break;
    233
    234	default:
    235		return -EINVAL;
    236	}
    237
    238	mem->size    = size;
    239	mem->sm_addr = ptr;
    240	mem->k_addr  = inf->fbmem + ptr;
    241
    242	dev_dbg(inf->dev, "%s: result %08lx, %p - %u, %zd\n",
    243		__func__, mem->sm_addr, mem->k_addr, why, size);
    244
    245	return 0;
    246}
    247
    248/* sm501fb_ps_to_hz
    249 *
    250 * Converts a period in picoseconds to Hz.
    251 *
    252 * Note, we try to keep this in Hz to minimise rounding with
    253 * the limited PLL settings on the SM501.
    254*/
    255
    256static unsigned long sm501fb_ps_to_hz(unsigned long psvalue)
    257{
    258	unsigned long long numerator=1000000000000ULL;
    259
    260	/* 10^12 / picosecond period gives frequency in Hz */
    261	do_div(numerator, psvalue);
    262	return (unsigned long)numerator;
    263}
    264
    265/* sm501fb_hz_to_ps is identical to the opposite transform */
    266
    267#define sm501fb_hz_to_ps(x) sm501fb_ps_to_hz(x)
    268
    269/* sm501fb_setup_gamma
    270 *
    271 * Programs a linear 1.0 gamma ramp in case the gamma
    272 * correction is enabled without programming anything else.
    273*/
    274
    275static void sm501fb_setup_gamma(struct sm501fb_info *fbi,
    276				unsigned long palette)
    277{
    278	unsigned long value = 0;
    279	int offset;
    280
    281	/* set gamma values */
    282	for (offset = 0; offset < 256 * 4; offset += 4) {
    283		smc501_writel(value, fbi->regs + palette + offset);
    284		value += 0x010101; 	/* Advance RGB by 1,1,1.*/
    285	}
    286}
    287
    288/* sm501fb_check_var
    289 *
    290 * check common variables for both panel and crt
    291*/
    292
    293static int sm501fb_check_var(struct fb_var_screeninfo *var,
    294			     struct fb_info *info)
    295{
    296	struct sm501fb_par  *par = info->par;
    297	struct sm501fb_info *sm  = par->info;
    298	unsigned long tmp;
    299
    300	/* check we can fit these values into the registers */
    301
    302	if (var->hsync_len > 255 || var->vsync_len > 63)
    303		return -EINVAL;
    304
    305	/* hdisplay end and hsync start */
    306	if ((var->xres + var->right_margin) > 4096)
    307		return -EINVAL;
    308
    309	/* vdisplay end and vsync start */
    310	if ((var->yres + var->lower_margin) > 2048)
    311		return -EINVAL;
    312
    313	/* hard limits of device */
    314
    315	if (h_total(var) > 4096 || v_total(var) > 2048)
    316		return -EINVAL;
    317
    318	/* check our line length is going to be 128 bit aligned */
    319
    320	tmp = (var->xres * var->bits_per_pixel) / 8;
    321	if ((tmp & 15) != 0)
    322		return -EINVAL;
    323
    324	/* check the virtual size */
    325
    326	if (var->xres_virtual > 4096 || var->yres_virtual > 2048)
    327		return -EINVAL;
    328
    329	/* can cope with 8,16 or 32bpp */
    330
    331	if (var->bits_per_pixel <= 8)
    332		var->bits_per_pixel = 8;
    333	else if (var->bits_per_pixel <= 16)
    334		var->bits_per_pixel = 16;
    335	else if (var->bits_per_pixel == 24)
    336		var->bits_per_pixel = 32;
    337
    338	/* set r/g/b positions and validate bpp */
    339	switch(var->bits_per_pixel) {
    340	case 8:
    341		var->red.length		= var->bits_per_pixel;
    342		var->red.offset		= 0;
    343		var->green.length	= var->bits_per_pixel;
    344		var->green.offset	= 0;
    345		var->blue.length	= var->bits_per_pixel;
    346		var->blue.offset	= 0;
    347		var->transp.length	= 0;
    348		var->transp.offset	= 0;
    349
    350		break;
    351
    352	case 16:
    353		if (sm->pdata->flags & SM501_FBPD_SWAP_FB_ENDIAN) {
    354			var->blue.offset	= 11;
    355			var->green.offset	= 5;
    356			var->red.offset		= 0;
    357		} else {
    358			var->red.offset		= 11;
    359			var->green.offset	= 5;
    360			var->blue.offset	= 0;
    361		}
    362		var->transp.offset	= 0;
    363
    364		var->red.length		= 5;
    365		var->green.length	= 6;
    366		var->blue.length	= 5;
    367		var->transp.length	= 0;
    368		break;
    369
    370	case 32:
    371		if (sm->pdata->flags & SM501_FBPD_SWAP_FB_ENDIAN) {
    372			var->transp.offset	= 0;
    373			var->red.offset		= 8;
    374			var->green.offset	= 16;
    375			var->blue.offset	= 24;
    376		} else {
    377			var->transp.offset	= 24;
    378			var->red.offset		= 16;
    379			var->green.offset	= 8;
    380			var->blue.offset	= 0;
    381		}
    382
    383		var->red.length		= 8;
    384		var->green.length	= 8;
    385		var->blue.length	= 8;
    386		var->transp.length	= 0;
    387		break;
    388
    389	default:
    390		return -EINVAL;
    391	}
    392
    393	return 0;
    394}
    395
    396/*
    397 * sm501fb_check_var_crt():
    398 *
    399 * check the parameters for the CRT head, and either bring them
    400 * back into range, or return -EINVAL.
    401*/
    402
    403static int sm501fb_check_var_crt(struct fb_var_screeninfo *var,
    404				 struct fb_info *info)
    405{
    406	return sm501fb_check_var(var, info);
    407}
    408
    409/* sm501fb_check_var_pnl():
    410 *
    411 * check the parameters for the CRT head, and either bring them
    412 * back into range, or return -EINVAL.
    413*/
    414
    415static int sm501fb_check_var_pnl(struct fb_var_screeninfo *var,
    416				 struct fb_info *info)
    417{
    418	return sm501fb_check_var(var, info);
    419}
    420
    421/* sm501fb_set_par_common
    422 *
    423 * set common registers for framebuffers
    424*/
    425
    426static int sm501fb_set_par_common(struct fb_info *info,
    427				  struct fb_var_screeninfo *var)
    428{
    429	struct sm501fb_par  *par = info->par;
    430	struct sm501fb_info *fbi = par->info;
    431	unsigned long pixclock;      /* pixelclock in Hz */
    432	unsigned long sm501pixclock; /* pixelclock the 501 can achieve in Hz */
    433	unsigned int mem_type;
    434	unsigned int clock_type;
    435	unsigned int head_addr;
    436	unsigned int smem_len;
    437
    438	dev_dbg(fbi->dev, "%s: %dx%d, bpp = %d, virtual %dx%d\n",
    439		__func__, var->xres, var->yres, var->bits_per_pixel,
    440		var->xres_virtual, var->yres_virtual);
    441
    442	switch (par->head) {
    443	case HEAD_CRT:
    444		mem_type = SM501_MEMF_CRT;
    445		clock_type = SM501_CLOCK_V2XCLK;
    446		head_addr = SM501_DC_CRT_FB_ADDR;
    447		break;
    448
    449	case HEAD_PANEL:
    450		mem_type = SM501_MEMF_PANEL;
    451		clock_type = SM501_CLOCK_P2XCLK;
    452		head_addr = SM501_DC_PANEL_FB_ADDR;
    453		break;
    454
    455	default:
    456		mem_type = 0;		/* stop compiler warnings */
    457		head_addr = 0;
    458		clock_type = 0;
    459	}
    460
    461	switch (var->bits_per_pixel) {
    462	case 8:
    463		info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
    464		break;
    465
    466	case 16:
    467		info->fix.visual = FB_VISUAL_TRUECOLOR;
    468		break;
    469
    470	case 32:
    471		info->fix.visual = FB_VISUAL_TRUECOLOR;
    472		break;
    473	}
    474
    475	/* allocate fb memory within 501 */
    476	info->fix.line_length = (var->xres_virtual * var->bits_per_pixel)/8;
    477	smem_len = info->fix.line_length * var->yres_virtual;
    478
    479	dev_dbg(fbi->dev, "%s: line length = %u\n", __func__,
    480		info->fix.line_length);
    481
    482	if (sm501_alloc_mem(fbi, &par->screen, mem_type, smem_len, smem_len)) {
    483		dev_err(fbi->dev, "no memory available\n");
    484		return -ENOMEM;
    485	}
    486
    487	mutex_lock(&info->mm_lock);
    488	info->fix.smem_start = fbi->fbmem_res->start + par->screen.sm_addr;
    489	info->fix.smem_len   = smem_len;
    490	mutex_unlock(&info->mm_lock);
    491
    492	info->screen_base = fbi->fbmem + par->screen.sm_addr;
    493	info->screen_size = info->fix.smem_len;
    494
    495	/* set start of framebuffer to the screen */
    496
    497	smc501_writel(par->screen.sm_addr | SM501_ADDR_FLIP,
    498			fbi->regs + head_addr);
    499
    500	/* program CRT clock  */
    501
    502	pixclock = sm501fb_ps_to_hz(var->pixclock);
    503
    504	sm501pixclock = sm501_set_clock(fbi->dev->parent, clock_type,
    505					pixclock);
    506
    507	/* update fb layer with actual clock used */
    508	var->pixclock = sm501fb_hz_to_ps(sm501pixclock);
    509
    510	dev_dbg(fbi->dev, "%s: pixclock(ps) = %u, pixclock(Hz)  = %lu, "
    511	       "sm501pixclock = %lu,  error = %ld%%\n",
    512	       __func__, var->pixclock, pixclock, sm501pixclock,
    513	       ((pixclock - sm501pixclock)*100)/pixclock);
    514
    515	return 0;
    516}
    517
    518/* sm501fb_set_par_geometry
    519 *
    520 * set the geometry registers for specified framebuffer.
    521*/
    522
    523static void sm501fb_set_par_geometry(struct fb_info *info,
    524				     struct fb_var_screeninfo *var)
    525{
    526	struct sm501fb_par  *par = info->par;
    527	struct sm501fb_info *fbi = par->info;
    528	void __iomem *base = fbi->regs;
    529	unsigned long reg;
    530
    531	if (par->head == HEAD_CRT)
    532		base += SM501_DC_CRT_H_TOT;
    533	else
    534		base += SM501_DC_PANEL_H_TOT;
    535
    536	/* set framebuffer width and display width */
    537
    538	reg = info->fix.line_length;
    539	reg |= ((var->xres * var->bits_per_pixel)/8) << 16;
    540
    541	smc501_writel(reg, fbi->regs + (par->head == HEAD_CRT ?
    542		    SM501_DC_CRT_FB_OFFSET :  SM501_DC_PANEL_FB_OFFSET));
    543
    544	/* program horizontal total */
    545
    546	reg  = (h_total(var) - 1) << 16;
    547	reg |= (var->xres - 1);
    548
    549	smc501_writel(reg, base + SM501_OFF_DC_H_TOT);
    550
    551	/* program horizontal sync */
    552
    553	reg  = var->hsync_len << 16;
    554	reg |= var->xres + var->right_margin - 1;
    555
    556	smc501_writel(reg, base + SM501_OFF_DC_H_SYNC);
    557
    558	/* program vertical total */
    559
    560	reg  = (v_total(var) - 1) << 16;
    561	reg |= (var->yres - 1);
    562
    563	smc501_writel(reg, base + SM501_OFF_DC_V_TOT);
    564
    565	/* program vertical sync */
    566	reg  = var->vsync_len << 16;
    567	reg |= var->yres + var->lower_margin - 1;
    568
    569	smc501_writel(reg, base + SM501_OFF_DC_V_SYNC);
    570}
    571
    572/* sm501fb_pan_crt
    573 *
    574 * pan the CRT display output within an virtual framebuffer
    575*/
    576
    577static int sm501fb_pan_crt(struct fb_var_screeninfo *var,
    578			   struct fb_info *info)
    579{
    580	struct sm501fb_par  *par = info->par;
    581	struct sm501fb_info *fbi = par->info;
    582	unsigned int bytes_pixel = info->var.bits_per_pixel / 8;
    583	unsigned long reg;
    584	unsigned long xoffs;
    585
    586	xoffs = var->xoffset * bytes_pixel;
    587
    588	reg = smc501_readl(fbi->regs + SM501_DC_CRT_CONTROL);
    589
    590	reg &= ~SM501_DC_CRT_CONTROL_PIXEL_MASK;
    591	reg |= ((xoffs & 15) / bytes_pixel) << 4;
    592	smc501_writel(reg, fbi->regs + SM501_DC_CRT_CONTROL);
    593
    594	reg = (par->screen.sm_addr + xoffs +
    595	       var->yoffset * info->fix.line_length);
    596	smc501_writel(reg | SM501_ADDR_FLIP, fbi->regs + SM501_DC_CRT_FB_ADDR);
    597
    598	sm501fb_sync_regs(fbi);
    599	return 0;
    600}
    601
    602/* sm501fb_pan_pnl
    603 *
    604 * pan the panel display output within an virtual framebuffer
    605*/
    606
    607static int sm501fb_pan_pnl(struct fb_var_screeninfo *var,
    608			   struct fb_info *info)
    609{
    610	struct sm501fb_par  *par = info->par;
    611	struct sm501fb_info *fbi = par->info;
    612	unsigned long reg;
    613
    614	reg = var->xoffset | (info->var.xres_virtual << 16);
    615	smc501_writel(reg, fbi->regs + SM501_DC_PANEL_FB_WIDTH);
    616
    617	reg = var->yoffset | (info->var.yres_virtual << 16);
    618	smc501_writel(reg, fbi->regs + SM501_DC_PANEL_FB_HEIGHT);
    619
    620	sm501fb_sync_regs(fbi);
    621	return 0;
    622}
    623
    624/* sm501fb_set_par_crt
    625 *
    626 * Set the CRT video mode from the fb_info structure
    627*/
    628
    629static int sm501fb_set_par_crt(struct fb_info *info)
    630{
    631	struct sm501fb_par  *par = info->par;
    632	struct sm501fb_info *fbi = par->info;
    633	struct fb_var_screeninfo *var = &info->var;
    634	unsigned long control;       /* control register */
    635	int ret;
    636
    637	/* activate new configuration */
    638
    639	dev_dbg(fbi->dev, "%s(%p)\n", __func__, info);
    640
    641	/* enable CRT DAC - note 0 is on!*/
    642	sm501_misc_control(fbi->dev->parent, 0, SM501_MISC_DAC_POWER);
    643
    644	control = smc501_readl(fbi->regs + SM501_DC_CRT_CONTROL);
    645
    646	control &= (SM501_DC_CRT_CONTROL_PIXEL_MASK |
    647		    SM501_DC_CRT_CONTROL_GAMMA |
    648		    SM501_DC_CRT_CONTROL_BLANK |
    649		    SM501_DC_CRT_CONTROL_SEL |
    650		    SM501_DC_CRT_CONTROL_CP |
    651		    SM501_DC_CRT_CONTROL_TVP);
    652
    653	/* set the sync polarities before we check data source  */
    654
    655	if ((var->sync & FB_SYNC_HOR_HIGH_ACT) == 0)
    656		control |= SM501_DC_CRT_CONTROL_HSP;
    657
    658	if ((var->sync & FB_SYNC_VERT_HIGH_ACT) == 0)
    659		control |= SM501_DC_CRT_CONTROL_VSP;
    660
    661	if ((control & SM501_DC_CRT_CONTROL_SEL) == 0) {
    662		/* the head is displaying panel data... */
    663
    664		sm501_alloc_mem(fbi, &par->screen, SM501_MEMF_CRT, 0,
    665				info->fix.smem_len);
    666		goto out_update;
    667	}
    668
    669	ret = sm501fb_set_par_common(info, var);
    670	if (ret) {
    671		dev_err(fbi->dev, "failed to set common parameters\n");
    672		return ret;
    673	}
    674
    675	sm501fb_pan_crt(var, info);
    676	sm501fb_set_par_geometry(info, var);
    677
    678	control |= SM501_FIFO_3;	/* fill if >3 free slots */
    679
    680	switch(var->bits_per_pixel) {
    681	case 8:
    682		control |= SM501_DC_CRT_CONTROL_8BPP;
    683		break;
    684
    685	case 16:
    686		control |= SM501_DC_CRT_CONTROL_16BPP;
    687		sm501fb_setup_gamma(fbi, SM501_DC_CRT_PALETTE);
    688		break;
    689
    690	case 32:
    691		control |= SM501_DC_CRT_CONTROL_32BPP;
    692		sm501fb_setup_gamma(fbi, SM501_DC_CRT_PALETTE);
    693		break;
    694
    695	default:
    696		BUG();
    697	}
    698
    699	control |= SM501_DC_CRT_CONTROL_SEL;	/* CRT displays CRT data */
    700	control |= SM501_DC_CRT_CONTROL_TE;	/* enable CRT timing */
    701	control |= SM501_DC_CRT_CONTROL_ENABLE;	/* enable CRT plane */
    702
    703 out_update:
    704	dev_dbg(fbi->dev, "new control is %08lx\n", control);
    705
    706	smc501_writel(control, fbi->regs + SM501_DC_CRT_CONTROL);
    707	sm501fb_sync_regs(fbi);
    708
    709	return 0;
    710}
    711
    712static void sm501fb_panel_power(struct sm501fb_info *fbi, int to)
    713{
    714	unsigned long control;
    715	void __iomem *ctrl_reg = fbi->regs + SM501_DC_PANEL_CONTROL;
    716	struct sm501_platdata_fbsub *pd = fbi->pdata->fb_pnl;
    717
    718	control = smc501_readl(ctrl_reg);
    719
    720	if (to && (control & SM501_DC_PANEL_CONTROL_VDD) == 0) {
    721		/* enable panel power */
    722
    723		control |= SM501_DC_PANEL_CONTROL_VDD;	/* FPVDDEN */
    724		smc501_writel(control, ctrl_reg);
    725		sm501fb_sync_regs(fbi);
    726		mdelay(10);
    727
    728		control |= SM501_DC_PANEL_CONTROL_DATA;	/* DATA */
    729		smc501_writel(control, ctrl_reg);
    730		sm501fb_sync_regs(fbi);
    731		mdelay(10);
    732
    733		/* VBIASEN */
    734
    735		if (!(pd->flags & SM501FB_FLAG_PANEL_NO_VBIASEN)) {
    736			if (pd->flags & SM501FB_FLAG_PANEL_INV_VBIASEN)
    737				control &= ~SM501_DC_PANEL_CONTROL_BIAS;
    738			else
    739				control |= SM501_DC_PANEL_CONTROL_BIAS;
    740
    741			smc501_writel(control, ctrl_reg);
    742			sm501fb_sync_regs(fbi);
    743			mdelay(10);
    744		}
    745
    746		if (!(pd->flags & SM501FB_FLAG_PANEL_NO_FPEN)) {
    747			if (pd->flags & SM501FB_FLAG_PANEL_INV_FPEN)
    748				control &= ~SM501_DC_PANEL_CONTROL_FPEN;
    749			else
    750				control |= SM501_DC_PANEL_CONTROL_FPEN;
    751
    752			smc501_writel(control, ctrl_reg);
    753			sm501fb_sync_regs(fbi);
    754			mdelay(10);
    755		}
    756	} else if (!to && (control & SM501_DC_PANEL_CONTROL_VDD) != 0) {
    757		/* disable panel power */
    758		if (!(pd->flags & SM501FB_FLAG_PANEL_NO_FPEN)) {
    759			if (pd->flags & SM501FB_FLAG_PANEL_INV_FPEN)
    760				control |= SM501_DC_PANEL_CONTROL_FPEN;
    761			else
    762				control &= ~SM501_DC_PANEL_CONTROL_FPEN;
    763
    764			smc501_writel(control, ctrl_reg);
    765			sm501fb_sync_regs(fbi);
    766			mdelay(10);
    767		}
    768
    769		if (!(pd->flags & SM501FB_FLAG_PANEL_NO_VBIASEN)) {
    770			if (pd->flags & SM501FB_FLAG_PANEL_INV_VBIASEN)
    771				control |= SM501_DC_PANEL_CONTROL_BIAS;
    772			else
    773				control &= ~SM501_DC_PANEL_CONTROL_BIAS;
    774
    775			smc501_writel(control, ctrl_reg);
    776			sm501fb_sync_regs(fbi);
    777			mdelay(10);
    778		}
    779
    780		control &= ~SM501_DC_PANEL_CONTROL_DATA;
    781		smc501_writel(control, ctrl_reg);
    782		sm501fb_sync_regs(fbi);
    783		mdelay(10);
    784
    785		control &= ~SM501_DC_PANEL_CONTROL_VDD;
    786		smc501_writel(control, ctrl_reg);
    787		sm501fb_sync_regs(fbi);
    788		mdelay(10);
    789	}
    790
    791	sm501fb_sync_regs(fbi);
    792}
    793
    794/* sm501fb_set_par_pnl
    795 *
    796 * Set the panel video mode from the fb_info structure
    797*/
    798
    799static int sm501fb_set_par_pnl(struct fb_info *info)
    800{
    801	struct sm501fb_par  *par = info->par;
    802	struct sm501fb_info *fbi = par->info;
    803	struct fb_var_screeninfo *var = &info->var;
    804	unsigned long control;
    805	unsigned long reg;
    806	int ret;
    807
    808	dev_dbg(fbi->dev, "%s(%p)\n", __func__, info);
    809
    810	/* activate this new configuration */
    811
    812	ret = sm501fb_set_par_common(info, var);
    813	if (ret)
    814		return ret;
    815
    816	sm501fb_pan_pnl(var, info);
    817	sm501fb_set_par_geometry(info, var);
    818
    819	/* update control register */
    820
    821	control = smc501_readl(fbi->regs + SM501_DC_PANEL_CONTROL);
    822	control &= (SM501_DC_PANEL_CONTROL_GAMMA |
    823		    SM501_DC_PANEL_CONTROL_VDD  |
    824		    SM501_DC_PANEL_CONTROL_DATA |
    825		    SM501_DC_PANEL_CONTROL_BIAS |
    826		    SM501_DC_PANEL_CONTROL_FPEN |
    827		    SM501_DC_PANEL_CONTROL_CP |
    828		    SM501_DC_PANEL_CONTROL_CK |
    829		    SM501_DC_PANEL_CONTROL_HP |
    830		    SM501_DC_PANEL_CONTROL_VP |
    831		    SM501_DC_PANEL_CONTROL_HPD |
    832		    SM501_DC_PANEL_CONTROL_VPD);
    833
    834	control |= SM501_FIFO_3;	/* fill if >3 free slots */
    835
    836	switch(var->bits_per_pixel) {
    837	case 8:
    838		control |= SM501_DC_PANEL_CONTROL_8BPP;
    839		break;
    840
    841	case 16:
    842		control |= SM501_DC_PANEL_CONTROL_16BPP;
    843		sm501fb_setup_gamma(fbi, SM501_DC_PANEL_PALETTE);
    844		break;
    845
    846	case 32:
    847		control |= SM501_DC_PANEL_CONTROL_32BPP;
    848		sm501fb_setup_gamma(fbi, SM501_DC_PANEL_PALETTE);
    849		break;
    850
    851	default:
    852		BUG();
    853	}
    854
    855	smc501_writel(0x0, fbi->regs + SM501_DC_PANEL_PANNING_CONTROL);
    856
    857	/* panel plane top left and bottom right location */
    858
    859	smc501_writel(0x00, fbi->regs + SM501_DC_PANEL_TL_LOC);
    860
    861	reg  = var->xres - 1;
    862	reg |= (var->yres - 1) << 16;
    863
    864	smc501_writel(reg, fbi->regs + SM501_DC_PANEL_BR_LOC);
    865
    866	/* program panel control register */
    867
    868	control |= SM501_DC_PANEL_CONTROL_TE;	/* enable PANEL timing */
    869	control |= SM501_DC_PANEL_CONTROL_EN;	/* enable PANEL gfx plane */
    870
    871	if ((var->sync & FB_SYNC_HOR_HIGH_ACT) == 0)
    872		control |= SM501_DC_PANEL_CONTROL_HSP;
    873
    874	if ((var->sync & FB_SYNC_VERT_HIGH_ACT) == 0)
    875		control |= SM501_DC_PANEL_CONTROL_VSP;
    876
    877	smc501_writel(control, fbi->regs + SM501_DC_PANEL_CONTROL);
    878	sm501fb_sync_regs(fbi);
    879
    880	/* ensure the panel interface is not tristated at this point */
    881
    882	sm501_modify_reg(fbi->dev->parent, SM501_SYSTEM_CONTROL,
    883			 0, SM501_SYSCTRL_PANEL_TRISTATE);
    884
    885	/* power the panel up */
    886	sm501fb_panel_power(fbi, 1);
    887	return 0;
    888}
    889
    890
    891/* chan_to_field
    892 *
    893 * convert a colour value into a field position
    894 *
    895 * from pxafb.c
    896*/
    897
    898static inline unsigned int chan_to_field(unsigned int chan,
    899					 struct fb_bitfield *bf)
    900{
    901	chan &= 0xffff;
    902	chan >>= 16 - bf->length;
    903	return chan << bf->offset;
    904}
    905
    906/* sm501fb_setcolreg
    907 *
    908 * set the colour mapping for modes that support palettised data
    909*/
    910
    911static int sm501fb_setcolreg(unsigned regno,
    912			     unsigned red, unsigned green, unsigned blue,
    913			     unsigned transp, struct fb_info *info)
    914{
    915	struct sm501fb_par  *par = info->par;
    916	struct sm501fb_info *fbi = par->info;
    917	void __iomem *base = fbi->regs;
    918	unsigned int val;
    919
    920	if (par->head == HEAD_CRT)
    921		base += SM501_DC_CRT_PALETTE;
    922	else
    923		base += SM501_DC_PANEL_PALETTE;
    924
    925	switch (info->fix.visual) {
    926	case FB_VISUAL_TRUECOLOR:
    927		/* true-colour, use pseuo-palette */
    928
    929		if (regno < 16) {
    930			u32 *pal = par->pseudo_palette;
    931
    932			val  = chan_to_field(red,   &info->var.red);
    933			val |= chan_to_field(green, &info->var.green);
    934			val |= chan_to_field(blue,  &info->var.blue);
    935
    936			pal[regno] = val;
    937		}
    938		break;
    939
    940	case FB_VISUAL_PSEUDOCOLOR:
    941		if (regno < 256) {
    942			val = (red >> 8) << 16;
    943			val |= (green >> 8) << 8;
    944			val |= blue >> 8;
    945
    946			smc501_writel(val, base + (regno * 4));
    947		}
    948
    949		break;
    950
    951	default:
    952		return 1;   /* unknown type */
    953	}
    954
    955	return 0;
    956}
    957
    958/* sm501fb_blank_pnl
    959 *
    960 * Blank or un-blank the panel interface
    961*/
    962
    963static int sm501fb_blank_pnl(int blank_mode, struct fb_info *info)
    964{
    965	struct sm501fb_par  *par = info->par;
    966	struct sm501fb_info *fbi = par->info;
    967
    968	dev_dbg(fbi->dev, "%s(mode=%d, %p)\n", __func__, blank_mode, info);
    969
    970	switch (blank_mode) {
    971	case FB_BLANK_POWERDOWN:
    972		sm501fb_panel_power(fbi, 0);
    973		break;
    974
    975	case FB_BLANK_UNBLANK:
    976		sm501fb_panel_power(fbi, 1);
    977		break;
    978
    979	case FB_BLANK_NORMAL:
    980	case FB_BLANK_VSYNC_SUSPEND:
    981	case FB_BLANK_HSYNC_SUSPEND:
    982	default:
    983		return 1;
    984	}
    985
    986	return 0;
    987}
    988
    989/* sm501fb_blank_crt
    990 *
    991 * Blank or un-blank the crt interface
    992*/
    993
    994static int sm501fb_blank_crt(int blank_mode, struct fb_info *info)
    995{
    996	struct sm501fb_par  *par = info->par;
    997	struct sm501fb_info *fbi = par->info;
    998	unsigned long ctrl;
    999
   1000	dev_dbg(fbi->dev, "%s(mode=%d, %p)\n", __func__, blank_mode, info);
   1001
   1002	ctrl = smc501_readl(fbi->regs + SM501_DC_CRT_CONTROL);
   1003
   1004	switch (blank_mode) {
   1005	case FB_BLANK_POWERDOWN:
   1006		ctrl &= ~SM501_DC_CRT_CONTROL_ENABLE;
   1007		sm501_misc_control(fbi->dev->parent, SM501_MISC_DAC_POWER, 0);
   1008		fallthrough;
   1009
   1010	case FB_BLANK_NORMAL:
   1011		ctrl |= SM501_DC_CRT_CONTROL_BLANK;
   1012		break;
   1013
   1014	case FB_BLANK_UNBLANK:
   1015		ctrl &= ~SM501_DC_CRT_CONTROL_BLANK;
   1016		ctrl |=  SM501_DC_CRT_CONTROL_ENABLE;
   1017		sm501_misc_control(fbi->dev->parent, 0, SM501_MISC_DAC_POWER);
   1018		break;
   1019
   1020	case FB_BLANK_VSYNC_SUSPEND:
   1021	case FB_BLANK_HSYNC_SUSPEND:
   1022	default:
   1023		return 1;
   1024
   1025	}
   1026
   1027	smc501_writel(ctrl, fbi->regs + SM501_DC_CRT_CONTROL);
   1028	sm501fb_sync_regs(fbi);
   1029
   1030	return 0;
   1031}
   1032
   1033/* sm501fb_cursor
   1034 *
   1035 * set or change the hardware cursor parameters
   1036*/
   1037
   1038static int sm501fb_cursor(struct fb_info *info, struct fb_cursor *cursor)
   1039{
   1040	struct sm501fb_par  *par = info->par;
   1041	struct sm501fb_info *fbi = par->info;
   1042	void __iomem *base = fbi->regs;
   1043	unsigned long hwc_addr;
   1044	unsigned long fg, bg;
   1045
   1046	dev_dbg(fbi->dev, "%s(%p,%p)\n", __func__, info, cursor);
   1047
   1048	if (par->head == HEAD_CRT)
   1049		base += SM501_DC_CRT_HWC_BASE;
   1050	else
   1051		base += SM501_DC_PANEL_HWC_BASE;
   1052
   1053	/* check not being asked to exceed capabilities */
   1054
   1055	if (cursor->image.width > 64)
   1056		return -EINVAL;
   1057
   1058	if (cursor->image.height > 64)
   1059		return -EINVAL;
   1060
   1061	if (cursor->image.depth > 1)
   1062		return -EINVAL;
   1063
   1064	hwc_addr = smc501_readl(base + SM501_OFF_HWC_ADDR);
   1065
   1066	if (cursor->enable)
   1067		smc501_writel(hwc_addr | SM501_HWC_EN,
   1068				base + SM501_OFF_HWC_ADDR);
   1069	else
   1070		smc501_writel(hwc_addr & ~SM501_HWC_EN,
   1071				base + SM501_OFF_HWC_ADDR);
   1072
   1073	/* set data */
   1074	if (cursor->set & FB_CUR_SETPOS) {
   1075		unsigned int x = cursor->image.dx;
   1076		unsigned int y = cursor->image.dy;
   1077
   1078		if (x >= 2048 || y >= 2048 )
   1079			return -EINVAL;
   1080
   1081		dev_dbg(fbi->dev, "set position %d,%d\n", x, y);
   1082
   1083		//y += cursor->image.height;
   1084
   1085		smc501_writel(x | (y << 16), base + SM501_OFF_HWC_LOC);
   1086	}
   1087
   1088	if (cursor->set & FB_CUR_SETCMAP) {
   1089		unsigned int bg_col = cursor->image.bg_color;
   1090		unsigned int fg_col = cursor->image.fg_color;
   1091
   1092		dev_dbg(fbi->dev, "%s: update cmap (%08x,%08x)\n",
   1093			__func__, bg_col, fg_col);
   1094
   1095		bg = ((info->cmap.red[bg_col] & 0xF8) << 8) |
   1096			((info->cmap.green[bg_col] & 0xFC) << 3) |
   1097			((info->cmap.blue[bg_col] & 0xF8) >> 3);
   1098
   1099		fg = ((info->cmap.red[fg_col] & 0xF8) << 8) |
   1100			((info->cmap.green[fg_col] & 0xFC) << 3) |
   1101			((info->cmap.blue[fg_col] & 0xF8) >> 3);
   1102
   1103		dev_dbg(fbi->dev, "fgcol %08lx, bgcol %08lx\n", fg, bg);
   1104
   1105		smc501_writel(bg, base + SM501_OFF_HWC_COLOR_1_2);
   1106		smc501_writel(fg, base + SM501_OFF_HWC_COLOR_3);
   1107	}
   1108
   1109	if (cursor->set & FB_CUR_SETSIZE ||
   1110	    cursor->set & (FB_CUR_SETIMAGE | FB_CUR_SETSHAPE)) {
   1111		/* SM501 cursor is a two bpp 64x64 bitmap this routine
   1112		 * clears it to transparent then combines the cursor
   1113		 * shape plane with the colour plane to set the
   1114		 * cursor */
   1115		int x, y;
   1116		const unsigned char *pcol = cursor->image.data;
   1117		const unsigned char *pmsk = cursor->mask;
   1118		void __iomem   *dst = par->cursor.k_addr;
   1119		unsigned char  dcol = 0;
   1120		unsigned char  dmsk = 0;
   1121		unsigned int   op;
   1122
   1123		dev_dbg(fbi->dev, "%s: setting shape (%d,%d)\n",
   1124			__func__, cursor->image.width, cursor->image.height);
   1125
   1126		for (op = 0; op < (64*64*2)/8; op+=4)
   1127			smc501_writel(0x0, dst + op);
   1128
   1129		for (y = 0; y < cursor->image.height; y++) {
   1130			for (x = 0; x < cursor->image.width; x++) {
   1131				if ((x % 8) == 0) {
   1132					dcol = *pcol++;
   1133					dmsk = *pmsk++;
   1134				} else {
   1135					dcol >>= 1;
   1136					dmsk >>= 1;
   1137				}
   1138
   1139				if (dmsk & 1) {
   1140					op = (dcol & 1) ? 1 : 3;
   1141					op <<= ((x % 4) * 2);
   1142
   1143					op |= readb(dst + (x / 4));
   1144					writeb(op, dst + (x / 4));
   1145				}
   1146			}
   1147			dst += (64*2)/8;
   1148		}
   1149	}
   1150
   1151	sm501fb_sync_regs(fbi);	/* ensure cursor data flushed */
   1152	return 0;
   1153}
   1154
   1155/* sm501fb_crtsrc_show
   1156 *
   1157 * device attribute code to show where the crt output is sourced from
   1158*/
   1159
   1160static ssize_t sm501fb_crtsrc_show(struct device *dev,
   1161			       struct device_attribute *attr, char *buf)
   1162{
   1163	struct sm501fb_info *info = dev_get_drvdata(dev);
   1164	unsigned long ctrl;
   1165
   1166	ctrl = smc501_readl(info->regs + SM501_DC_CRT_CONTROL);
   1167	ctrl &= SM501_DC_CRT_CONTROL_SEL;
   1168
   1169	return snprintf(buf, PAGE_SIZE, "%s\n", ctrl ? "crt" : "panel");
   1170}
   1171
   1172/* sm501fb_crtsrc_show
   1173 *
   1174 * device attribute code to set where the crt output is sourced from
   1175*/
   1176
   1177static ssize_t sm501fb_crtsrc_store(struct device *dev,
   1178				struct device_attribute *attr,
   1179				const char *buf, size_t len)
   1180{
   1181	struct sm501fb_info *info = dev_get_drvdata(dev);
   1182	enum sm501_controller head;
   1183	unsigned long ctrl;
   1184
   1185	if (len < 1)
   1186		return -EINVAL;
   1187
   1188	if (strncasecmp(buf, "crt", 3) == 0)
   1189		head = HEAD_CRT;
   1190	else if (strncasecmp(buf, "panel", 5) == 0)
   1191		head = HEAD_PANEL;
   1192	else
   1193		return -EINVAL;
   1194
   1195	dev_info(dev, "setting crt source to head %d\n", head);
   1196
   1197	ctrl = smc501_readl(info->regs + SM501_DC_CRT_CONTROL);
   1198
   1199	if (head == HEAD_CRT) {
   1200		ctrl |= SM501_DC_CRT_CONTROL_SEL;
   1201		ctrl |= SM501_DC_CRT_CONTROL_ENABLE;
   1202		ctrl |= SM501_DC_CRT_CONTROL_TE;
   1203	} else {
   1204		ctrl &= ~SM501_DC_CRT_CONTROL_SEL;
   1205		ctrl &= ~SM501_DC_CRT_CONTROL_ENABLE;
   1206		ctrl &= ~SM501_DC_CRT_CONTROL_TE;
   1207	}
   1208
   1209	smc501_writel(ctrl, info->regs + SM501_DC_CRT_CONTROL);
   1210	sm501fb_sync_regs(info);
   1211
   1212	return len;
   1213}
   1214
   1215/* Prepare the device_attr for registration with sysfs later */
   1216static DEVICE_ATTR(crt_src, 0664, sm501fb_crtsrc_show, sm501fb_crtsrc_store);
   1217
   1218/* sm501fb_show_regs
   1219 *
   1220 * show the primary sm501 registers
   1221*/
   1222static int sm501fb_show_regs(struct sm501fb_info *info, char *ptr,
   1223			     unsigned int start, unsigned int len)
   1224{
   1225	void __iomem *mem = info->regs;
   1226	char *buf = ptr;
   1227	unsigned int reg;
   1228
   1229	for (reg = start; reg < (len + start); reg += 4)
   1230		ptr += sprintf(ptr, "%08x = %08x\n", reg,
   1231				smc501_readl(mem + reg));
   1232
   1233	return ptr - buf;
   1234}
   1235
   1236/* sm501fb_debug_show_crt
   1237 *
   1238 * show the crt control and cursor registers
   1239*/
   1240
   1241static ssize_t sm501fb_debug_show_crt(struct device *dev,
   1242				  struct device_attribute *attr, char *buf)
   1243{
   1244	struct sm501fb_info *info = dev_get_drvdata(dev);
   1245	char *ptr = buf;
   1246
   1247	ptr += sm501fb_show_regs(info, ptr, SM501_DC_CRT_CONTROL, 0x40);
   1248	ptr += sm501fb_show_regs(info, ptr, SM501_DC_CRT_HWC_BASE, 0x10);
   1249
   1250	return ptr - buf;
   1251}
   1252
   1253static DEVICE_ATTR(fbregs_crt, 0444, sm501fb_debug_show_crt, NULL);
   1254
   1255/* sm501fb_debug_show_pnl
   1256 *
   1257 * show the panel control and cursor registers
   1258*/
   1259
   1260static ssize_t sm501fb_debug_show_pnl(struct device *dev,
   1261				  struct device_attribute *attr, char *buf)
   1262{
   1263	struct sm501fb_info *info = dev_get_drvdata(dev);
   1264	char *ptr = buf;
   1265
   1266	ptr += sm501fb_show_regs(info, ptr, 0x0, 0x40);
   1267	ptr += sm501fb_show_regs(info, ptr, SM501_DC_PANEL_HWC_BASE, 0x10);
   1268
   1269	return ptr - buf;
   1270}
   1271
   1272static DEVICE_ATTR(fbregs_pnl, 0444, sm501fb_debug_show_pnl, NULL);
   1273
   1274static struct attribute *sm501fb_attrs[] = {
   1275	&dev_attr_crt_src.attr,
   1276	&dev_attr_fbregs_pnl.attr,
   1277	&dev_attr_fbregs_crt.attr,
   1278	NULL,
   1279};
   1280ATTRIBUTE_GROUPS(sm501fb);
   1281
   1282/* acceleration operations */
   1283static int sm501fb_sync(struct fb_info *info)
   1284{
   1285	int count = 1000000;
   1286	struct sm501fb_par  *par = info->par;
   1287	struct sm501fb_info *fbi = par->info;
   1288
   1289	/* wait for the 2d engine to be ready */
   1290	while ((count > 0) &&
   1291	       (smc501_readl(fbi->regs + SM501_SYSTEM_CONTROL) &
   1292		SM501_SYSCTRL_2D_ENGINE_STATUS) != 0)
   1293		count--;
   1294
   1295	if (count <= 0) {
   1296		dev_err(info->dev, "Timeout waiting for 2d engine sync\n");
   1297		return 1;
   1298	}
   1299	return 0;
   1300}
   1301
   1302static void sm501fb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
   1303{
   1304	struct sm501fb_par  *par = info->par;
   1305	struct sm501fb_info *fbi = par->info;
   1306	int width = area->width;
   1307	int height = area->height;
   1308	int sx = area->sx;
   1309	int sy = area->sy;
   1310	int dx = area->dx;
   1311	int dy = area->dy;
   1312	unsigned long rtl = 0;
   1313
   1314	/* source clip */
   1315	if ((sx >= info->var.xres_virtual) ||
   1316	    (sy >= info->var.yres_virtual))
   1317		/* source Area not within virtual screen, skipping */
   1318		return;
   1319	if ((sx + width) >= info->var.xres_virtual)
   1320		width = info->var.xres_virtual - sx - 1;
   1321	if ((sy + height) >= info->var.yres_virtual)
   1322		height = info->var.yres_virtual - sy - 1;
   1323
   1324	/* dest clip */
   1325	if ((dx >= info->var.xres_virtual) ||
   1326	    (dy >= info->var.yres_virtual))
   1327		/* Destination Area not within virtual screen, skipping */
   1328		return;
   1329	if ((dx + width) >= info->var.xres_virtual)
   1330		width = info->var.xres_virtual - dx - 1;
   1331	if ((dy + height) >= info->var.yres_virtual)
   1332		height = info->var.yres_virtual - dy - 1;
   1333
   1334	if ((sx < dx) || (sy < dy)) {
   1335		rtl = 1 << 27;
   1336		sx += width - 1;
   1337		dx += width - 1;
   1338		sy += height - 1;
   1339		dy += height - 1;
   1340	}
   1341
   1342	if (sm501fb_sync(info))
   1343		return;
   1344
   1345	/* set the base addresses */
   1346	smc501_writel(par->screen.sm_addr, fbi->regs2d + SM501_2D_SOURCE_BASE);
   1347	smc501_writel(par->screen.sm_addr,
   1348			fbi->regs2d + SM501_2D_DESTINATION_BASE);
   1349
   1350	/* set the window width */
   1351	smc501_writel((info->var.xres << 16) | info->var.xres,
   1352	       fbi->regs2d + SM501_2D_WINDOW_WIDTH);
   1353
   1354	/* set window stride */
   1355	smc501_writel((info->var.xres_virtual << 16) | info->var.xres_virtual,
   1356	       fbi->regs2d + SM501_2D_PITCH);
   1357
   1358	/* set data format */
   1359	switch (info->var.bits_per_pixel) {
   1360	case 8:
   1361		smc501_writel(0, fbi->regs2d + SM501_2D_STRETCH);
   1362		break;
   1363	case 16:
   1364		smc501_writel(0x00100000, fbi->regs2d + SM501_2D_STRETCH);
   1365		break;
   1366	case 32:
   1367		smc501_writel(0x00200000, fbi->regs2d + SM501_2D_STRETCH);
   1368		break;
   1369	}
   1370
   1371	/* 2d compare mask */
   1372	smc501_writel(0xffffffff, fbi->regs2d + SM501_2D_COLOR_COMPARE_MASK);
   1373
   1374	/* 2d mask */
   1375	smc501_writel(0xffffffff, fbi->regs2d + SM501_2D_MASK);
   1376
   1377	/* source and destination x y */
   1378	smc501_writel((sx << 16) | sy, fbi->regs2d + SM501_2D_SOURCE);
   1379	smc501_writel((dx << 16) | dy, fbi->regs2d + SM501_2D_DESTINATION);
   1380
   1381	/* w/h */
   1382	smc501_writel((width << 16) | height, fbi->regs2d + SM501_2D_DIMENSION);
   1383
   1384	/* do area move */
   1385	smc501_writel(0x800000cc | rtl, fbi->regs2d + SM501_2D_CONTROL);
   1386}
   1387
   1388static void sm501fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
   1389{
   1390	struct sm501fb_par  *par = info->par;
   1391	struct sm501fb_info *fbi = par->info;
   1392	int width = rect->width, height = rect->height;
   1393
   1394	if ((rect->dx >= info->var.xres_virtual) ||
   1395	    (rect->dy >= info->var.yres_virtual))
   1396		/* Rectangle not within virtual screen, skipping */
   1397		return;
   1398	if ((rect->dx + width) >= info->var.xres_virtual)
   1399		width = info->var.xres_virtual - rect->dx - 1;
   1400	if ((rect->dy + height) >= info->var.yres_virtual)
   1401		height = info->var.yres_virtual - rect->dy - 1;
   1402
   1403	if (sm501fb_sync(info))
   1404		return;
   1405
   1406	/* set the base addresses */
   1407	smc501_writel(par->screen.sm_addr, fbi->regs2d + SM501_2D_SOURCE_BASE);
   1408	smc501_writel(par->screen.sm_addr,
   1409			fbi->regs2d + SM501_2D_DESTINATION_BASE);
   1410
   1411	/* set the window width */
   1412	smc501_writel((info->var.xres << 16) | info->var.xres,
   1413	       fbi->regs2d + SM501_2D_WINDOW_WIDTH);
   1414
   1415	/* set window stride */
   1416	smc501_writel((info->var.xres_virtual << 16) | info->var.xres_virtual,
   1417	       fbi->regs2d + SM501_2D_PITCH);
   1418
   1419	/* set data format */
   1420	switch (info->var.bits_per_pixel) {
   1421	case 8:
   1422		smc501_writel(0, fbi->regs2d + SM501_2D_STRETCH);
   1423		break;
   1424	case 16:
   1425		smc501_writel(0x00100000, fbi->regs2d + SM501_2D_STRETCH);
   1426		break;
   1427	case 32:
   1428		smc501_writel(0x00200000, fbi->regs2d + SM501_2D_STRETCH);
   1429		break;
   1430	}
   1431
   1432	/* 2d compare mask */
   1433	smc501_writel(0xffffffff, fbi->regs2d + SM501_2D_COLOR_COMPARE_MASK);
   1434
   1435	/* 2d mask */
   1436	smc501_writel(0xffffffff, fbi->regs2d + SM501_2D_MASK);
   1437
   1438	/* colour */
   1439	smc501_writel(rect->color, fbi->regs2d + SM501_2D_FOREGROUND);
   1440
   1441	/* x y */
   1442	smc501_writel((rect->dx << 16) | rect->dy,
   1443			fbi->regs2d + SM501_2D_DESTINATION);
   1444
   1445	/* w/h */
   1446	smc501_writel((width << 16) | height, fbi->regs2d + SM501_2D_DIMENSION);
   1447
   1448	/* do rectangle fill */
   1449	smc501_writel(0x800100cc, fbi->regs2d + SM501_2D_CONTROL);
   1450}
   1451
   1452
   1453static struct fb_ops sm501fb_ops_crt = {
   1454	.owner		= THIS_MODULE,
   1455	.fb_check_var	= sm501fb_check_var_crt,
   1456	.fb_set_par	= sm501fb_set_par_crt,
   1457	.fb_blank	= sm501fb_blank_crt,
   1458	.fb_setcolreg	= sm501fb_setcolreg,
   1459	.fb_pan_display	= sm501fb_pan_crt,
   1460	.fb_cursor	= sm501fb_cursor,
   1461	.fb_fillrect	= sm501fb_fillrect,
   1462	.fb_copyarea	= sm501fb_copyarea,
   1463	.fb_imageblit	= cfb_imageblit,
   1464	.fb_sync	= sm501fb_sync,
   1465};
   1466
   1467static struct fb_ops sm501fb_ops_pnl = {
   1468	.owner		= THIS_MODULE,
   1469	.fb_check_var	= sm501fb_check_var_pnl,
   1470	.fb_set_par	= sm501fb_set_par_pnl,
   1471	.fb_pan_display	= sm501fb_pan_pnl,
   1472	.fb_blank	= sm501fb_blank_pnl,
   1473	.fb_setcolreg	= sm501fb_setcolreg,
   1474	.fb_cursor	= sm501fb_cursor,
   1475	.fb_fillrect	= sm501fb_fillrect,
   1476	.fb_copyarea	= sm501fb_copyarea,
   1477	.fb_imageblit	= cfb_imageblit,
   1478	.fb_sync	= sm501fb_sync,
   1479};
   1480
   1481/* sm501_init_cursor
   1482 *
   1483 * initialise hw cursor parameters
   1484*/
   1485
   1486static int sm501_init_cursor(struct fb_info *fbi, unsigned int reg_base)
   1487{
   1488	struct sm501fb_par *par;
   1489	struct sm501fb_info *info;
   1490	int ret;
   1491
   1492	if (fbi == NULL)
   1493		return 0;
   1494
   1495	par = fbi->par;
   1496	info = par->info;
   1497
   1498	par->cursor_regs = info->regs + reg_base;
   1499
   1500	ret = sm501_alloc_mem(info, &par->cursor, SM501_MEMF_CURSOR, 1024,
   1501			      fbi->fix.smem_len);
   1502	if (ret < 0)
   1503		return ret;
   1504
   1505	/* initialise the colour registers */
   1506
   1507	smc501_writel(par->cursor.sm_addr,
   1508			par->cursor_regs + SM501_OFF_HWC_ADDR);
   1509
   1510	smc501_writel(0x00, par->cursor_regs + SM501_OFF_HWC_LOC);
   1511	smc501_writel(0x00, par->cursor_regs + SM501_OFF_HWC_COLOR_1_2);
   1512	smc501_writel(0x00, par->cursor_regs + SM501_OFF_HWC_COLOR_3);
   1513	sm501fb_sync_regs(info);
   1514
   1515	return 0;
   1516}
   1517
   1518/* sm501fb_info_start
   1519 *
   1520 * fills the par structure claiming resources and remapping etc.
   1521*/
   1522
   1523static int sm501fb_start(struct sm501fb_info *info,
   1524			 struct platform_device *pdev)
   1525{
   1526	struct resource	*res;
   1527	struct device *dev = &pdev->dev;
   1528	int k;
   1529	int ret;
   1530
   1531	info->irq = ret = platform_get_irq(pdev, 0);
   1532	if (ret < 0) {
   1533		/* we currently do not use the IRQ */
   1534		dev_warn(dev, "no irq for device\n");
   1535	}
   1536
   1537	/* allocate, reserve and remap resources for display
   1538	 * controller registers */
   1539	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
   1540	if (res == NULL) {
   1541		dev_err(dev, "no resource definition for registers\n");
   1542		ret = -ENOENT;
   1543		goto err_release;
   1544	}
   1545
   1546	info->regs_res = request_mem_region(res->start,
   1547					    resource_size(res),
   1548					    pdev->name);
   1549
   1550	if (info->regs_res == NULL) {
   1551		dev_err(dev, "cannot claim registers\n");
   1552		ret = -ENXIO;
   1553		goto err_release;
   1554	}
   1555
   1556	info->regs = ioremap(res->start, resource_size(res));
   1557	if (info->regs == NULL) {
   1558		dev_err(dev, "cannot remap registers\n");
   1559		ret = -ENXIO;
   1560		goto err_regs_res;
   1561	}
   1562
   1563	/* allocate, reserve and remap resources for 2d
   1564	 * controller registers */
   1565	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
   1566	if (res == NULL) {
   1567		dev_err(dev, "no resource definition for 2d registers\n");
   1568		ret = -ENOENT;
   1569		goto err_regs_map;
   1570	}
   1571
   1572	info->regs2d_res = request_mem_region(res->start,
   1573					      resource_size(res),
   1574					      pdev->name);
   1575
   1576	if (info->regs2d_res == NULL) {
   1577		dev_err(dev, "cannot claim registers\n");
   1578		ret = -ENXIO;
   1579		goto err_regs_map;
   1580	}
   1581
   1582	info->regs2d = ioremap(res->start, resource_size(res));
   1583	if (info->regs2d == NULL) {
   1584		dev_err(dev, "cannot remap registers\n");
   1585		ret = -ENXIO;
   1586		goto err_regs2d_res;
   1587	}
   1588
   1589	/* allocate, reserve resources for framebuffer */
   1590	res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
   1591	if (res == NULL) {
   1592		dev_err(dev, "no memory resource defined\n");
   1593		ret = -ENXIO;
   1594		goto err_regs2d_map;
   1595	}
   1596
   1597	info->fbmem_res = request_mem_region(res->start,
   1598					     resource_size(res),
   1599					     pdev->name);
   1600	if (info->fbmem_res == NULL) {
   1601		dev_err(dev, "cannot claim framebuffer\n");
   1602		ret = -ENXIO;
   1603		goto err_regs2d_map;
   1604	}
   1605
   1606	info->fbmem = ioremap(res->start, resource_size(res));
   1607	if (info->fbmem == NULL) {
   1608		dev_err(dev, "cannot remap framebuffer\n");
   1609		ret = -ENXIO;
   1610		goto err_mem_res;
   1611	}
   1612
   1613	info->fbmem_len = resource_size(res);
   1614
   1615	/* clear framebuffer memory - avoids garbage data on unused fb */
   1616	memset_io(info->fbmem, 0, info->fbmem_len);
   1617
   1618	/* clear palette ram - undefined at power on */
   1619	for (k = 0; k < (256 * 3); k++)
   1620		smc501_writel(0, info->regs + SM501_DC_PANEL_PALETTE + (k * 4));
   1621
   1622	/* enable display controller */
   1623	sm501_unit_power(dev->parent, SM501_GATE_DISPLAY, 1);
   1624
   1625	/* enable 2d controller */
   1626	sm501_unit_power(dev->parent, SM501_GATE_2D_ENGINE, 1);
   1627
   1628	/* setup cursors */
   1629	sm501_init_cursor(info->fb[HEAD_CRT], SM501_DC_CRT_HWC_ADDR);
   1630	sm501_init_cursor(info->fb[HEAD_PANEL], SM501_DC_PANEL_HWC_ADDR);
   1631
   1632	return 0; /* everything is setup */
   1633
   1634 err_mem_res:
   1635	release_mem_region(info->fbmem_res->start,
   1636			   resource_size(info->fbmem_res));
   1637
   1638 err_regs2d_map:
   1639	iounmap(info->regs2d);
   1640
   1641 err_regs2d_res:
   1642	release_mem_region(info->regs2d_res->start,
   1643			   resource_size(info->regs2d_res));
   1644
   1645 err_regs_map:
   1646	iounmap(info->regs);
   1647
   1648 err_regs_res:
   1649	release_mem_region(info->regs_res->start,
   1650			   resource_size(info->regs_res));
   1651
   1652 err_release:
   1653	return ret;
   1654}
   1655
   1656static void sm501fb_stop(struct sm501fb_info *info)
   1657{
   1658	/* disable display controller */
   1659	sm501_unit_power(info->dev->parent, SM501_GATE_DISPLAY, 0);
   1660
   1661	iounmap(info->fbmem);
   1662	release_mem_region(info->fbmem_res->start,
   1663			   resource_size(info->fbmem_res));
   1664
   1665	iounmap(info->regs2d);
   1666	release_mem_region(info->regs2d_res->start,
   1667			   resource_size(info->regs2d_res));
   1668
   1669	iounmap(info->regs);
   1670	release_mem_region(info->regs_res->start,
   1671			   resource_size(info->regs_res));
   1672}
   1673
   1674static int sm501fb_init_fb(struct fb_info *fb, enum sm501_controller head,
   1675			   const char *fbname)
   1676{
   1677	struct sm501_platdata_fbsub *pd;
   1678	struct sm501fb_par *par = fb->par;
   1679	struct sm501fb_info *info = par->info;
   1680	unsigned long ctrl;
   1681	unsigned int enable;
   1682	int ret;
   1683
   1684	switch (head) {
   1685	case HEAD_CRT:
   1686		pd = info->pdata->fb_crt;
   1687		ctrl = smc501_readl(info->regs + SM501_DC_CRT_CONTROL);
   1688		enable = (ctrl & SM501_DC_CRT_CONTROL_ENABLE) ? 1 : 0;
   1689
   1690		/* ensure we set the correct source register */
   1691		if (info->pdata->fb_route != SM501_FB_CRT_PANEL) {
   1692			ctrl |= SM501_DC_CRT_CONTROL_SEL;
   1693			smc501_writel(ctrl, info->regs + SM501_DC_CRT_CONTROL);
   1694		}
   1695
   1696		break;
   1697
   1698	case HEAD_PANEL:
   1699		pd = info->pdata->fb_pnl;
   1700		ctrl = smc501_readl(info->regs + SM501_DC_PANEL_CONTROL);
   1701		enable = (ctrl & SM501_DC_PANEL_CONTROL_EN) ? 1 : 0;
   1702		break;
   1703
   1704	default:
   1705		pd = NULL;		/* stop compiler warnings */
   1706		ctrl = 0;
   1707		enable = 0;
   1708		BUG();
   1709	}
   1710
   1711	dev_info(info->dev, "fb %s %sabled at start\n",
   1712		 fbname, enable ? "en" : "dis");
   1713
   1714	/* check to see if our routing allows this */
   1715
   1716	if (head == HEAD_CRT && info->pdata->fb_route == SM501_FB_CRT_PANEL) {
   1717		ctrl &= ~SM501_DC_CRT_CONTROL_SEL;
   1718		smc501_writel(ctrl, info->regs + SM501_DC_CRT_CONTROL);
   1719		enable = 0;
   1720	}
   1721
   1722	strlcpy(fb->fix.id, fbname, sizeof(fb->fix.id));
   1723
   1724	memcpy(&par->ops,
   1725	       (head == HEAD_CRT) ? &sm501fb_ops_crt : &sm501fb_ops_pnl,
   1726	       sizeof(struct fb_ops));
   1727
   1728	/* update ops dependent on what we've been passed */
   1729
   1730	if ((pd->flags & SM501FB_FLAG_USE_HWCURSOR) == 0)
   1731		par->ops.fb_cursor = NULL;
   1732
   1733	fb->fbops = &par->ops;
   1734	fb->flags = FBINFO_FLAG_DEFAULT | FBINFO_READS_FAST |
   1735		FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT |
   1736		FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN;
   1737
   1738#if defined(CONFIG_OF)
   1739#ifdef __BIG_ENDIAN
   1740	if (of_get_property(info->dev->parent->of_node, "little-endian", NULL))
   1741		fb->flags |= FBINFO_FOREIGN_ENDIAN;
   1742#else
   1743	if (of_get_property(info->dev->parent->of_node, "big-endian", NULL))
   1744		fb->flags |= FBINFO_FOREIGN_ENDIAN;
   1745#endif
   1746#endif
   1747	/* fixed data */
   1748
   1749	fb->fix.type		= FB_TYPE_PACKED_PIXELS;
   1750	fb->fix.type_aux	= 0;
   1751	fb->fix.xpanstep	= 1;
   1752	fb->fix.ypanstep	= 1;
   1753	fb->fix.ywrapstep	= 0;
   1754	fb->fix.accel		= FB_ACCEL_NONE;
   1755
   1756	/* screenmode */
   1757
   1758	fb->var.nonstd		= 0;
   1759	fb->var.activate	= FB_ACTIVATE_NOW;
   1760	fb->var.accel_flags	= 0;
   1761	fb->var.vmode		= FB_VMODE_NONINTERLACED;
   1762	fb->var.bits_per_pixel  = 16;
   1763
   1764	if (info->edid_data) {
   1765			/* Now build modedb from EDID */
   1766			fb_edid_to_monspecs(info->edid_data, &fb->monspecs);
   1767			fb_videomode_to_modelist(fb->monspecs.modedb,
   1768						 fb->monspecs.modedb_len,
   1769						 &fb->modelist);
   1770	}
   1771
   1772	if (enable && (pd->flags & SM501FB_FLAG_USE_INIT_MODE) && 0) {
   1773		/* TODO read the mode from the current display */
   1774	} else {
   1775		if (pd->def_mode) {
   1776			dev_info(info->dev, "using supplied mode\n");
   1777			fb_videomode_to_var(&fb->var, pd->def_mode);
   1778
   1779			fb->var.bits_per_pixel = pd->def_bpp ? pd->def_bpp : 8;
   1780			fb->var.xres_virtual = fb->var.xres;
   1781			fb->var.yres_virtual = fb->var.yres;
   1782		} else {
   1783			if (info->edid_data) {
   1784				ret = fb_find_mode(&fb->var, fb, fb_mode,
   1785					fb->monspecs.modedb,
   1786					fb->monspecs.modedb_len,
   1787					&sm501_default_mode, default_bpp);
   1788				/* edid_data is no longer needed, free it */
   1789				kfree(info->edid_data);
   1790			} else {
   1791				ret = fb_find_mode(&fb->var, fb,
   1792					   NULL, NULL, 0, NULL, 8);
   1793			}
   1794
   1795			switch (ret) {
   1796			case 1:
   1797				dev_info(info->dev, "using mode specified in "
   1798						"@mode\n");
   1799				break;
   1800			case 2:
   1801				dev_info(info->dev, "using mode specified in "
   1802					"@mode with ignored refresh rate\n");
   1803				break;
   1804			case 3:
   1805				dev_info(info->dev, "using mode default "
   1806					"mode\n");
   1807				break;
   1808			case 4:
   1809				dev_info(info->dev, "using mode from list\n");
   1810				break;
   1811			default:
   1812				dev_info(info->dev, "ret = %d\n", ret);
   1813				dev_info(info->dev, "failed to find mode\n");
   1814				return -EINVAL;
   1815			}
   1816		}
   1817	}
   1818
   1819	/* initialise and set the palette */
   1820	if (fb_alloc_cmap(&fb->cmap, NR_PALETTE, 0)) {
   1821		dev_err(info->dev, "failed to allocate cmap memory\n");
   1822		return -ENOMEM;
   1823	}
   1824	fb_set_cmap(&fb->cmap, fb);
   1825
   1826	ret = (fb->fbops->fb_check_var)(&fb->var, fb);
   1827	if (ret)
   1828		dev_err(info->dev, "check_var() failed on initial setup?\n");
   1829
   1830	return 0;
   1831}
   1832
   1833/* default platform data if none is supplied (ie, PCI device) */
   1834
   1835static struct sm501_platdata_fbsub sm501fb_pdata_crt = {
   1836	.flags		= (SM501FB_FLAG_USE_INIT_MODE |
   1837			   SM501FB_FLAG_USE_HWCURSOR |
   1838			   SM501FB_FLAG_USE_HWACCEL |
   1839			   SM501FB_FLAG_DISABLE_AT_EXIT),
   1840
   1841};
   1842
   1843static struct sm501_platdata_fbsub sm501fb_pdata_pnl = {
   1844	.flags		= (SM501FB_FLAG_USE_INIT_MODE |
   1845			   SM501FB_FLAG_USE_HWCURSOR |
   1846			   SM501FB_FLAG_USE_HWACCEL |
   1847			   SM501FB_FLAG_DISABLE_AT_EXIT),
   1848};
   1849
   1850static struct sm501_platdata_fb sm501fb_def_pdata = {
   1851	.fb_route		= SM501_FB_OWN,
   1852	.fb_crt			= &sm501fb_pdata_crt,
   1853	.fb_pnl			= &sm501fb_pdata_pnl,
   1854};
   1855
   1856static char driver_name_crt[] = "sm501fb-crt";
   1857static char driver_name_pnl[] = "sm501fb-panel";
   1858
   1859static int sm501fb_probe_one(struct sm501fb_info *info,
   1860			     enum sm501_controller head)
   1861{
   1862	unsigned char *name = (head == HEAD_CRT) ? "crt" : "panel";
   1863	struct sm501_platdata_fbsub *pd;
   1864	struct sm501fb_par *par;
   1865	struct fb_info *fbi;
   1866
   1867	pd = (head == HEAD_CRT) ? info->pdata->fb_crt : info->pdata->fb_pnl;
   1868
   1869	/* Do not initialise if we've not been given any platform data */
   1870	if (pd == NULL) {
   1871		dev_info(info->dev, "no data for fb %s (disabled)\n", name);
   1872		return 0;
   1873	}
   1874
   1875	fbi = framebuffer_alloc(sizeof(struct sm501fb_par), info->dev);
   1876	if (!fbi)
   1877		return -ENOMEM;
   1878
   1879	par = fbi->par;
   1880	par->info = info;
   1881	par->head = head;
   1882	fbi->pseudo_palette = &par->pseudo_palette;
   1883
   1884	info->fb[head] = fbi;
   1885
   1886	return 0;
   1887}
   1888
   1889/* Free up anything allocated by sm501fb_init_fb */
   1890
   1891static void sm501_free_init_fb(struct sm501fb_info *info,
   1892				enum sm501_controller head)
   1893{
   1894	struct fb_info *fbi = info->fb[head];
   1895
   1896	if (!fbi)
   1897		return;
   1898
   1899	fb_dealloc_cmap(&fbi->cmap);
   1900}
   1901
   1902static int sm501fb_start_one(struct sm501fb_info *info,
   1903			     enum sm501_controller head, const char *drvname)
   1904{
   1905	struct fb_info *fbi = info->fb[head];
   1906	int ret;
   1907
   1908	if (!fbi)
   1909		return 0;
   1910
   1911	mutex_init(&info->fb[head]->mm_lock);
   1912
   1913	ret = sm501fb_init_fb(info->fb[head], head, drvname);
   1914	if (ret) {
   1915		dev_err(info->dev, "cannot initialise fb %s\n", drvname);
   1916		return ret;
   1917	}
   1918
   1919	ret = register_framebuffer(info->fb[head]);
   1920	if (ret) {
   1921		dev_err(info->dev, "failed to register fb %s\n", drvname);
   1922		sm501_free_init_fb(info, head);
   1923		return ret;
   1924	}
   1925
   1926	dev_info(info->dev, "fb%d: %s frame buffer\n", fbi->node, fbi->fix.id);
   1927
   1928	return 0;
   1929}
   1930
   1931static int sm501fb_probe(struct platform_device *pdev)
   1932{
   1933	struct sm501fb_info *info;
   1934	struct device *dev = &pdev->dev;
   1935	int ret;
   1936
   1937	/* allocate our framebuffers */
   1938	info = kzalloc(sizeof(*info), GFP_KERNEL);
   1939	if (!info) {
   1940		dev_err(dev, "failed to allocate state\n");
   1941		return -ENOMEM;
   1942	}
   1943
   1944	info->dev = dev = &pdev->dev;
   1945	platform_set_drvdata(pdev, info);
   1946
   1947	if (dev->parent->platform_data) {
   1948		struct sm501_platdata *pd = dev->parent->platform_data;
   1949		info->pdata = pd->fb;
   1950	}
   1951
   1952	if (info->pdata == NULL) {
   1953		int found = 0;
   1954#if defined(CONFIG_OF)
   1955		struct device_node *np = pdev->dev.parent->of_node;
   1956		const u8 *prop;
   1957		const char *cp;
   1958		int len;
   1959
   1960		info->pdata = &sm501fb_def_pdata;
   1961		if (np) {
   1962			/* Get EDID */
   1963			cp = of_get_property(np, "mode", &len);
   1964			if (cp)
   1965				strcpy(fb_mode, cp);
   1966			prop = of_get_property(np, "edid", &len);
   1967			if (prop && len == EDID_LENGTH) {
   1968				info->edid_data = kmemdup(prop, EDID_LENGTH,
   1969							  GFP_KERNEL);
   1970				if (info->edid_data)
   1971					found = 1;
   1972			}
   1973		}
   1974#endif
   1975		if (!found) {
   1976			dev_info(dev, "using default configuration data\n");
   1977			info->pdata = &sm501fb_def_pdata;
   1978		}
   1979	}
   1980
   1981	/* probe for the presence of each panel */
   1982
   1983	ret = sm501fb_probe_one(info, HEAD_CRT);
   1984	if (ret < 0) {
   1985		dev_err(dev, "failed to probe CRT\n");
   1986		goto err_alloc;
   1987	}
   1988
   1989	ret = sm501fb_probe_one(info, HEAD_PANEL);
   1990	if (ret < 0) {
   1991		dev_err(dev, "failed to probe PANEL\n");
   1992		goto err_probed_crt;
   1993	}
   1994
   1995	if (info->fb[HEAD_PANEL] == NULL &&
   1996	    info->fb[HEAD_CRT] == NULL) {
   1997		dev_err(dev, "no framebuffers found\n");
   1998		ret = -ENODEV;
   1999		goto err_alloc;
   2000	}
   2001
   2002	/* get the resources for both of the framebuffers */
   2003
   2004	ret = sm501fb_start(info, pdev);
   2005	if (ret) {
   2006		dev_err(dev, "cannot initialise SM501\n");
   2007		goto err_probed_panel;
   2008	}
   2009
   2010	ret = sm501fb_start_one(info, HEAD_CRT, driver_name_crt);
   2011	if (ret) {
   2012		dev_err(dev, "failed to start CRT\n");
   2013		goto err_started;
   2014	}
   2015
   2016	ret = sm501fb_start_one(info, HEAD_PANEL, driver_name_pnl);
   2017	if (ret) {
   2018		dev_err(dev, "failed to start Panel\n");
   2019		goto err_started_crt;
   2020	}
   2021
   2022	/* we registered, return ok */
   2023	return 0;
   2024
   2025err_started_crt:
   2026	unregister_framebuffer(info->fb[HEAD_CRT]);
   2027	sm501_free_init_fb(info, HEAD_CRT);
   2028
   2029err_started:
   2030	sm501fb_stop(info);
   2031
   2032err_probed_panel:
   2033	framebuffer_release(info->fb[HEAD_PANEL]);
   2034
   2035err_probed_crt:
   2036	framebuffer_release(info->fb[HEAD_CRT]);
   2037
   2038err_alloc:
   2039	kfree(info);
   2040
   2041	return ret;
   2042}
   2043
   2044
   2045/*
   2046 *  Cleanup
   2047 */
   2048static int sm501fb_remove(struct platform_device *pdev)
   2049{
   2050	struct sm501fb_info *info = platform_get_drvdata(pdev);
   2051	struct fb_info	   *fbinfo_crt = info->fb[0];
   2052	struct fb_info	   *fbinfo_pnl = info->fb[1];
   2053
   2054	sm501_free_init_fb(info, HEAD_CRT);
   2055	sm501_free_init_fb(info, HEAD_PANEL);
   2056
   2057	if (fbinfo_crt)
   2058		unregister_framebuffer(fbinfo_crt);
   2059	if (fbinfo_pnl)
   2060		unregister_framebuffer(fbinfo_pnl);
   2061
   2062	sm501fb_stop(info);
   2063	kfree(info);
   2064
   2065	framebuffer_release(fbinfo_pnl);
   2066	framebuffer_release(fbinfo_crt);
   2067
   2068	return 0;
   2069}
   2070
   2071#ifdef CONFIG_PM
   2072
   2073static int sm501fb_suspend_fb(struct sm501fb_info *info,
   2074			      enum sm501_controller head)
   2075{
   2076	struct fb_info *fbi = info->fb[head];
   2077	struct sm501fb_par *par;
   2078
   2079	if (!fbi)
   2080		return 0;
   2081
   2082	par = fbi->par;
   2083	if (par->screen.size == 0)
   2084		return 0;
   2085
   2086	/* blank the relevant interface to ensure unit power minimised */
   2087	(par->ops.fb_blank)(FB_BLANK_POWERDOWN, fbi);
   2088
   2089	/* tell console/fb driver we are suspending */
   2090
   2091	console_lock();
   2092	fb_set_suspend(fbi, 1);
   2093	console_unlock();
   2094
   2095	/* backup copies in case chip is powered down over suspend */
   2096
   2097	par->store_fb = vmalloc(par->screen.size);
   2098	if (par->store_fb == NULL) {
   2099		dev_err(info->dev, "no memory to store screen\n");
   2100		return -ENOMEM;
   2101	}
   2102
   2103	par->store_cursor = vmalloc(par->cursor.size);
   2104	if (par->store_cursor == NULL) {
   2105		dev_err(info->dev, "no memory to store cursor\n");
   2106		goto err_nocursor;
   2107	}
   2108
   2109	dev_dbg(info->dev, "suspending screen to %p\n", par->store_fb);
   2110	dev_dbg(info->dev, "suspending cursor to %p\n", par->store_cursor);
   2111
   2112	memcpy_fromio(par->store_fb, par->screen.k_addr, par->screen.size);
   2113	memcpy_fromio(par->store_cursor, par->cursor.k_addr, par->cursor.size);
   2114
   2115	return 0;
   2116
   2117 err_nocursor:
   2118	vfree(par->store_fb);
   2119	par->store_fb = NULL;
   2120
   2121	return -ENOMEM;
   2122}
   2123
   2124static void sm501fb_resume_fb(struct sm501fb_info *info,
   2125			      enum sm501_controller head)
   2126{
   2127	struct fb_info *fbi = info->fb[head];
   2128	struct sm501fb_par *par;
   2129
   2130	if (!fbi)
   2131		return;
   2132
   2133	par = fbi->par;
   2134	if (par->screen.size == 0)
   2135		return;
   2136
   2137	/* re-activate the configuration */
   2138
   2139	(par->ops.fb_set_par)(fbi);
   2140
   2141	/* restore the data */
   2142
   2143	dev_dbg(info->dev, "restoring screen from %p\n", par->store_fb);
   2144	dev_dbg(info->dev, "restoring cursor from %p\n", par->store_cursor);
   2145
   2146	if (par->store_fb)
   2147		memcpy_toio(par->screen.k_addr, par->store_fb,
   2148			    par->screen.size);
   2149
   2150	if (par->store_cursor)
   2151		memcpy_toio(par->cursor.k_addr, par->store_cursor,
   2152			    par->cursor.size);
   2153
   2154	console_lock();
   2155	fb_set_suspend(fbi, 0);
   2156	console_unlock();
   2157
   2158	vfree(par->store_fb);
   2159	vfree(par->store_cursor);
   2160}
   2161
   2162
   2163/* suspend and resume support */
   2164
   2165static int sm501fb_suspend(struct platform_device *pdev, pm_message_t state)
   2166{
   2167	struct sm501fb_info *info = platform_get_drvdata(pdev);
   2168
   2169	/* store crt control to resume with */
   2170	info->pm_crt_ctrl = smc501_readl(info->regs + SM501_DC_CRT_CONTROL);
   2171
   2172	sm501fb_suspend_fb(info, HEAD_CRT);
   2173	sm501fb_suspend_fb(info, HEAD_PANEL);
   2174
   2175	/* turn off the clocks, in case the device is not powered down */
   2176	sm501_unit_power(info->dev->parent, SM501_GATE_DISPLAY, 0);
   2177
   2178	return 0;
   2179}
   2180
   2181#define SM501_CRT_CTRL_SAVE (SM501_DC_CRT_CONTROL_TVP |        \
   2182			     SM501_DC_CRT_CONTROL_SEL)
   2183
   2184
   2185static int sm501fb_resume(struct platform_device *pdev)
   2186{
   2187	struct sm501fb_info *info = platform_get_drvdata(pdev);
   2188	unsigned long crt_ctrl;
   2189
   2190	sm501_unit_power(info->dev->parent, SM501_GATE_DISPLAY, 1);
   2191
   2192	/* restore the items we want to be saved for crt control */
   2193
   2194	crt_ctrl = smc501_readl(info->regs + SM501_DC_CRT_CONTROL);
   2195	crt_ctrl &= ~SM501_CRT_CTRL_SAVE;
   2196	crt_ctrl |= info->pm_crt_ctrl & SM501_CRT_CTRL_SAVE;
   2197	smc501_writel(crt_ctrl, info->regs + SM501_DC_CRT_CONTROL);
   2198
   2199	sm501fb_resume_fb(info, HEAD_CRT);
   2200	sm501fb_resume_fb(info, HEAD_PANEL);
   2201
   2202	return 0;
   2203}
   2204
   2205#else
   2206#define sm501fb_suspend NULL
   2207#define sm501fb_resume  NULL
   2208#endif
   2209
   2210static struct platform_driver sm501fb_driver = {
   2211	.probe		= sm501fb_probe,
   2212	.remove		= sm501fb_remove,
   2213	.suspend	= sm501fb_suspend,
   2214	.resume		= sm501fb_resume,
   2215	.driver		= {
   2216		.name	= "sm501-fb",
   2217		.dev_groups	= sm501fb_groups,
   2218	},
   2219};
   2220
   2221module_platform_driver(sm501fb_driver);
   2222
   2223module_param_named(mode, fb_mode, charp, 0);
   2224MODULE_PARM_DESC(mode,
   2225	"Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" ");
   2226module_param_named(bpp, default_bpp, ulong, 0);
   2227MODULE_PARM_DESC(bpp, "Specify bit-per-pixel if not specified mode");
   2228MODULE_AUTHOR("Ben Dooks, Vincent Sanders");
   2229MODULE_DESCRIPTION("SM501 Framebuffer driver");
   2230MODULE_LICENSE("GPL v2");