18c2ecf20Sopenharmony_ci/* linux/drivers/video/s3c2410fb.c
28c2ecf20Sopenharmony_ci *	Copyright (c) 2004,2005 Arnaud Patard
38c2ecf20Sopenharmony_ci *	Copyright (c) 2004-2008 Ben Dooks
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * S3C2410 LCD Framebuffer Driver
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
88c2ecf20Sopenharmony_ci * License.  See the file COPYING in the main directory of this archive for
98c2ecf20Sopenharmony_ci * more details.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Driver based on skeletonfb.c, sa1100fb.c and others.
128c2ecf20Sopenharmony_ci*/
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/module.h>
178c2ecf20Sopenharmony_ci#include <linux/kernel.h>
188c2ecf20Sopenharmony_ci#include <linux/err.h>
198c2ecf20Sopenharmony_ci#include <linux/errno.h>
208c2ecf20Sopenharmony_ci#include <linux/string.h>
218c2ecf20Sopenharmony_ci#include <linux/mm.h>
228c2ecf20Sopenharmony_ci#include <linux/slab.h>
238c2ecf20Sopenharmony_ci#include <linux/delay.h>
248c2ecf20Sopenharmony_ci#include <linux/fb.h>
258c2ecf20Sopenharmony_ci#include <linux/init.h>
268c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
278c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
288c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
298c2ecf20Sopenharmony_ci#include <linux/clk.h>
308c2ecf20Sopenharmony_ci#include <linux/cpufreq.h>
318c2ecf20Sopenharmony_ci#include <linux/io.h>
328c2ecf20Sopenharmony_ci#include <linux/platform_data/fb-s3c2410.h>
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#include <asm/div64.h>
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#include <asm/mach/map.h>
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
398c2ecf20Sopenharmony_ci#include <linux/pm.h>
408c2ecf20Sopenharmony_ci#endif
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#include "s3c2410fb.h"
438c2ecf20Sopenharmony_ci#include "s3c2410fb-regs-lcd.h"
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci/* Debugging stuff */
468c2ecf20Sopenharmony_cistatic int debug = IS_BUILTIN(CONFIG_FB_S3C2410_DEBUG);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#define dprintk(msg...) \
498c2ecf20Sopenharmony_cido { \
508c2ecf20Sopenharmony_ci	if (debug) \
518c2ecf20Sopenharmony_ci		pr_debug(msg); \
528c2ecf20Sopenharmony_ci} while (0)
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci/* useful functions */
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic int is_s3c2412(struct s3c2410fb_info *fbi)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	return (fbi->drv_type == DRV_S3C2412);
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci/* s3c2410fb_set_lcdaddr
628c2ecf20Sopenharmony_ci *
638c2ecf20Sopenharmony_ci * initialise lcd controller address pointers
648c2ecf20Sopenharmony_ci */
658c2ecf20Sopenharmony_cistatic void s3c2410fb_set_lcdaddr(struct fb_info *info)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	unsigned long saddr1, saddr2, saddr3;
688c2ecf20Sopenharmony_ci	struct s3c2410fb_info *fbi = info->par;
698c2ecf20Sopenharmony_ci	void __iomem *regs = fbi->io;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	saddr1  = info->fix.smem_start >> 1;
728c2ecf20Sopenharmony_ci	saddr2  = info->fix.smem_start;
738c2ecf20Sopenharmony_ci	saddr2 += info->fix.line_length * info->var.yres;
748c2ecf20Sopenharmony_ci	saddr2 >>= 1;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	saddr3 = S3C2410_OFFSIZE(0) |
778c2ecf20Sopenharmony_ci		 S3C2410_PAGEWIDTH((info->fix.line_length / 2) & 0x3ff);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	dprintk("LCDSADDR1 = 0x%08lx\n", saddr1);
808c2ecf20Sopenharmony_ci	dprintk("LCDSADDR2 = 0x%08lx\n", saddr2);
818c2ecf20Sopenharmony_ci	dprintk("LCDSADDR3 = 0x%08lx\n", saddr3);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	writel(saddr1, regs + S3C2410_LCDSADDR1);
848c2ecf20Sopenharmony_ci	writel(saddr2, regs + S3C2410_LCDSADDR2);
858c2ecf20Sopenharmony_ci	writel(saddr3, regs + S3C2410_LCDSADDR3);
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci/* s3c2410fb_calc_pixclk()
898c2ecf20Sopenharmony_ci *
908c2ecf20Sopenharmony_ci * calculate divisor for clk->pixclk
918c2ecf20Sopenharmony_ci */
928c2ecf20Sopenharmony_cistatic unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi,
938c2ecf20Sopenharmony_ci					  unsigned long pixclk)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	unsigned long clk = fbi->clk_rate;
968c2ecf20Sopenharmony_ci	unsigned long long div;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	/* pixclk is in picoseconds, our clock is in Hz
998c2ecf20Sopenharmony_ci	 *
1008c2ecf20Sopenharmony_ci	 * Hz -> picoseconds is / 10^-12
1018c2ecf20Sopenharmony_ci	 */
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	div = (unsigned long long)clk * pixclk;
1048c2ecf20Sopenharmony_ci	div >>= 12;			/* div / 2^12 */
1058c2ecf20Sopenharmony_ci	do_div(div, 625 * 625UL * 625); /* div / 5^12 */
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	dprintk("pixclk %ld, divisor is %ld\n", pixclk, (long)div);
1088c2ecf20Sopenharmony_ci	return div;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci/*
1128c2ecf20Sopenharmony_ci *	s3c2410fb_check_var():
1138c2ecf20Sopenharmony_ci *	Get the video params out of 'var'. If a value doesn't fit, round it up,
1148c2ecf20Sopenharmony_ci *	if it's too big, return -EINVAL.
1158c2ecf20Sopenharmony_ci *
1168c2ecf20Sopenharmony_ci */
1178c2ecf20Sopenharmony_cistatic int s3c2410fb_check_var(struct fb_var_screeninfo *var,
1188c2ecf20Sopenharmony_ci			       struct fb_info *info)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	struct s3c2410fb_info *fbi = info->par;
1218c2ecf20Sopenharmony_ci	struct s3c2410fb_mach_info *mach_info = dev_get_platdata(fbi->dev);
1228c2ecf20Sopenharmony_ci	struct s3c2410fb_display *display = NULL;
1238c2ecf20Sopenharmony_ci	struct s3c2410fb_display *default_display = mach_info->displays +
1248c2ecf20Sopenharmony_ci						    mach_info->default_display;
1258c2ecf20Sopenharmony_ci	int type = default_display->type;
1268c2ecf20Sopenharmony_ci	unsigned i;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	dprintk("check_var(var=%p, info=%p)\n", var, info);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	/* validate x/y resolution */
1318c2ecf20Sopenharmony_ci	/* choose default mode if possible */
1328c2ecf20Sopenharmony_ci	if (var->yres == default_display->yres &&
1338c2ecf20Sopenharmony_ci	    var->xres == default_display->xres &&
1348c2ecf20Sopenharmony_ci	    var->bits_per_pixel == default_display->bpp)
1358c2ecf20Sopenharmony_ci		display = default_display;
1368c2ecf20Sopenharmony_ci	else
1378c2ecf20Sopenharmony_ci		for (i = 0; i < mach_info->num_displays; i++)
1388c2ecf20Sopenharmony_ci			if (type == mach_info->displays[i].type &&
1398c2ecf20Sopenharmony_ci			    var->yres == mach_info->displays[i].yres &&
1408c2ecf20Sopenharmony_ci			    var->xres == mach_info->displays[i].xres &&
1418c2ecf20Sopenharmony_ci			    var->bits_per_pixel == mach_info->displays[i].bpp) {
1428c2ecf20Sopenharmony_ci				display = mach_info->displays + i;
1438c2ecf20Sopenharmony_ci				break;
1448c2ecf20Sopenharmony_ci			}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	if (!display) {
1478c2ecf20Sopenharmony_ci		dprintk("wrong resolution or depth %dx%d at %d bpp\n",
1488c2ecf20Sopenharmony_ci			var->xres, var->yres, var->bits_per_pixel);
1498c2ecf20Sopenharmony_ci		return -EINVAL;
1508c2ecf20Sopenharmony_ci	}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	/* it is always the size as the display */
1538c2ecf20Sopenharmony_ci	var->xres_virtual = display->xres;
1548c2ecf20Sopenharmony_ci	var->yres_virtual = display->yres;
1558c2ecf20Sopenharmony_ci	var->height = display->height;
1568c2ecf20Sopenharmony_ci	var->width = display->width;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	/* copy lcd settings */
1598c2ecf20Sopenharmony_ci	var->pixclock = display->pixclock;
1608c2ecf20Sopenharmony_ci	var->left_margin = display->left_margin;
1618c2ecf20Sopenharmony_ci	var->right_margin = display->right_margin;
1628c2ecf20Sopenharmony_ci	var->upper_margin = display->upper_margin;
1638c2ecf20Sopenharmony_ci	var->lower_margin = display->lower_margin;
1648c2ecf20Sopenharmony_ci	var->vsync_len = display->vsync_len;
1658c2ecf20Sopenharmony_ci	var->hsync_len = display->hsync_len;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	fbi->regs.lcdcon5 = display->lcdcon5;
1688c2ecf20Sopenharmony_ci	/* set display type */
1698c2ecf20Sopenharmony_ci	fbi->regs.lcdcon1 = display->type;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	var->transp.offset = 0;
1728c2ecf20Sopenharmony_ci	var->transp.length = 0;
1738c2ecf20Sopenharmony_ci	/* set r/g/b positions */
1748c2ecf20Sopenharmony_ci	switch (var->bits_per_pixel) {
1758c2ecf20Sopenharmony_ci	case 1:
1768c2ecf20Sopenharmony_ci	case 2:
1778c2ecf20Sopenharmony_ci	case 4:
1788c2ecf20Sopenharmony_ci		var->red.offset	= 0;
1798c2ecf20Sopenharmony_ci		var->red.length	= var->bits_per_pixel;
1808c2ecf20Sopenharmony_ci		var->green	= var->red;
1818c2ecf20Sopenharmony_ci		var->blue	= var->red;
1828c2ecf20Sopenharmony_ci		break;
1838c2ecf20Sopenharmony_ci	case 8:
1848c2ecf20Sopenharmony_ci		if (display->type != S3C2410_LCDCON1_TFT) {
1858c2ecf20Sopenharmony_ci			/* 8 bpp 332 */
1868c2ecf20Sopenharmony_ci			var->red.length		= 3;
1878c2ecf20Sopenharmony_ci			var->red.offset		= 5;
1888c2ecf20Sopenharmony_ci			var->green.length	= 3;
1898c2ecf20Sopenharmony_ci			var->green.offset	= 2;
1908c2ecf20Sopenharmony_ci			var->blue.length	= 2;
1918c2ecf20Sopenharmony_ci			var->blue.offset	= 0;
1928c2ecf20Sopenharmony_ci		} else {
1938c2ecf20Sopenharmony_ci			var->red.offset		= 0;
1948c2ecf20Sopenharmony_ci			var->red.length		= 8;
1958c2ecf20Sopenharmony_ci			var->green		= var->red;
1968c2ecf20Sopenharmony_ci			var->blue		= var->red;
1978c2ecf20Sopenharmony_ci		}
1988c2ecf20Sopenharmony_ci		break;
1998c2ecf20Sopenharmony_ci	case 12:
2008c2ecf20Sopenharmony_ci		/* 12 bpp 444 */
2018c2ecf20Sopenharmony_ci		var->red.length		= 4;
2028c2ecf20Sopenharmony_ci		var->red.offset		= 8;
2038c2ecf20Sopenharmony_ci		var->green.length	= 4;
2048c2ecf20Sopenharmony_ci		var->green.offset	= 4;
2058c2ecf20Sopenharmony_ci		var->blue.length	= 4;
2068c2ecf20Sopenharmony_ci		var->blue.offset	= 0;
2078c2ecf20Sopenharmony_ci		break;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	default:
2108c2ecf20Sopenharmony_ci	case 16:
2118c2ecf20Sopenharmony_ci		if (display->lcdcon5 & S3C2410_LCDCON5_FRM565) {
2128c2ecf20Sopenharmony_ci			/* 16 bpp, 565 format */
2138c2ecf20Sopenharmony_ci			var->red.offset		= 11;
2148c2ecf20Sopenharmony_ci			var->green.offset	= 5;
2158c2ecf20Sopenharmony_ci			var->blue.offset	= 0;
2168c2ecf20Sopenharmony_ci			var->red.length		= 5;
2178c2ecf20Sopenharmony_ci			var->green.length	= 6;
2188c2ecf20Sopenharmony_ci			var->blue.length	= 5;
2198c2ecf20Sopenharmony_ci		} else {
2208c2ecf20Sopenharmony_ci			/* 16 bpp, 5551 format */
2218c2ecf20Sopenharmony_ci			var->red.offset		= 11;
2228c2ecf20Sopenharmony_ci			var->green.offset	= 6;
2238c2ecf20Sopenharmony_ci			var->blue.offset	= 1;
2248c2ecf20Sopenharmony_ci			var->red.length		= 5;
2258c2ecf20Sopenharmony_ci			var->green.length	= 5;
2268c2ecf20Sopenharmony_ci			var->blue.length	= 5;
2278c2ecf20Sopenharmony_ci		}
2288c2ecf20Sopenharmony_ci		break;
2298c2ecf20Sopenharmony_ci	case 32:
2308c2ecf20Sopenharmony_ci		/* 24 bpp 888 and 8 dummy */
2318c2ecf20Sopenharmony_ci		var->red.length		= 8;
2328c2ecf20Sopenharmony_ci		var->red.offset		= 16;
2338c2ecf20Sopenharmony_ci		var->green.length	= 8;
2348c2ecf20Sopenharmony_ci		var->green.offset	= 8;
2358c2ecf20Sopenharmony_ci		var->blue.length	= 8;
2368c2ecf20Sopenharmony_ci		var->blue.offset	= 0;
2378c2ecf20Sopenharmony_ci		break;
2388c2ecf20Sopenharmony_ci	}
2398c2ecf20Sopenharmony_ci	return 0;
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci/* s3c2410fb_calculate_stn_lcd_regs
2438c2ecf20Sopenharmony_ci *
2448c2ecf20Sopenharmony_ci * calculate register values from var settings
2458c2ecf20Sopenharmony_ci */
2468c2ecf20Sopenharmony_cistatic void s3c2410fb_calculate_stn_lcd_regs(const struct fb_info *info,
2478c2ecf20Sopenharmony_ci					     struct s3c2410fb_hw *regs)
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	const struct s3c2410fb_info *fbi = info->par;
2508c2ecf20Sopenharmony_ci	const struct fb_var_screeninfo *var = &info->var;
2518c2ecf20Sopenharmony_ci	int type = regs->lcdcon1 & ~S3C2410_LCDCON1_TFT;
2528c2ecf20Sopenharmony_ci	int hs = var->xres >> 2;
2538c2ecf20Sopenharmony_ci	unsigned wdly = (var->left_margin >> 4) - 1;
2548c2ecf20Sopenharmony_ci	unsigned wlh = (var->hsync_len >> 4) - 1;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	if (type != S3C2410_LCDCON1_STN4)
2578c2ecf20Sopenharmony_ci		hs >>= 1;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	switch (var->bits_per_pixel) {
2608c2ecf20Sopenharmony_ci	case 1:
2618c2ecf20Sopenharmony_ci		regs->lcdcon1 |= S3C2410_LCDCON1_STN1BPP;
2628c2ecf20Sopenharmony_ci		break;
2638c2ecf20Sopenharmony_ci	case 2:
2648c2ecf20Sopenharmony_ci		regs->lcdcon1 |= S3C2410_LCDCON1_STN2GREY;
2658c2ecf20Sopenharmony_ci		break;
2668c2ecf20Sopenharmony_ci	case 4:
2678c2ecf20Sopenharmony_ci		regs->lcdcon1 |= S3C2410_LCDCON1_STN4GREY;
2688c2ecf20Sopenharmony_ci		break;
2698c2ecf20Sopenharmony_ci	case 8:
2708c2ecf20Sopenharmony_ci		regs->lcdcon1 |= S3C2410_LCDCON1_STN8BPP;
2718c2ecf20Sopenharmony_ci		hs *= 3;
2728c2ecf20Sopenharmony_ci		break;
2738c2ecf20Sopenharmony_ci	case 12:
2748c2ecf20Sopenharmony_ci		regs->lcdcon1 |= S3C2410_LCDCON1_STN12BPP;
2758c2ecf20Sopenharmony_ci		hs *= 3;
2768c2ecf20Sopenharmony_ci		break;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	default:
2798c2ecf20Sopenharmony_ci		/* invalid pixel depth */
2808c2ecf20Sopenharmony_ci		dev_err(fbi->dev, "invalid bpp %d\n",
2818c2ecf20Sopenharmony_ci			var->bits_per_pixel);
2828c2ecf20Sopenharmony_ci	}
2838c2ecf20Sopenharmony_ci	/* update X/Y info */
2848c2ecf20Sopenharmony_ci	dprintk("setting horz: lft=%d, rt=%d, sync=%d\n",
2858c2ecf20Sopenharmony_ci		var->left_margin, var->right_margin, var->hsync_len);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	regs->lcdcon2 = S3C2410_LCDCON2_LINEVAL(var->yres - 1);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	if (wdly > 3)
2908c2ecf20Sopenharmony_ci		wdly = 3;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	if (wlh > 3)
2938c2ecf20Sopenharmony_ci		wlh = 3;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	regs->lcdcon3 =	S3C2410_LCDCON3_WDLY(wdly) |
2968c2ecf20Sopenharmony_ci			S3C2410_LCDCON3_LINEBLANK(var->right_margin / 8) |
2978c2ecf20Sopenharmony_ci			S3C2410_LCDCON3_HOZVAL(hs - 1);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	regs->lcdcon4 = S3C2410_LCDCON4_WLH(wlh);
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci/* s3c2410fb_calculate_tft_lcd_regs
3038c2ecf20Sopenharmony_ci *
3048c2ecf20Sopenharmony_ci * calculate register values from var settings
3058c2ecf20Sopenharmony_ci */
3068c2ecf20Sopenharmony_cistatic void s3c2410fb_calculate_tft_lcd_regs(const struct fb_info *info,
3078c2ecf20Sopenharmony_ci					     struct s3c2410fb_hw *regs)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	const struct s3c2410fb_info *fbi = info->par;
3108c2ecf20Sopenharmony_ci	const struct fb_var_screeninfo *var = &info->var;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	switch (var->bits_per_pixel) {
3138c2ecf20Sopenharmony_ci	case 1:
3148c2ecf20Sopenharmony_ci		regs->lcdcon1 |= S3C2410_LCDCON1_TFT1BPP;
3158c2ecf20Sopenharmony_ci		break;
3168c2ecf20Sopenharmony_ci	case 2:
3178c2ecf20Sopenharmony_ci		regs->lcdcon1 |= S3C2410_LCDCON1_TFT2BPP;
3188c2ecf20Sopenharmony_ci		break;
3198c2ecf20Sopenharmony_ci	case 4:
3208c2ecf20Sopenharmony_ci		regs->lcdcon1 |= S3C2410_LCDCON1_TFT4BPP;
3218c2ecf20Sopenharmony_ci		break;
3228c2ecf20Sopenharmony_ci	case 8:
3238c2ecf20Sopenharmony_ci		regs->lcdcon1 |= S3C2410_LCDCON1_TFT8BPP;
3248c2ecf20Sopenharmony_ci		regs->lcdcon5 |= S3C2410_LCDCON5_BSWP |
3258c2ecf20Sopenharmony_ci				 S3C2410_LCDCON5_FRM565;
3268c2ecf20Sopenharmony_ci		regs->lcdcon5 &= ~S3C2410_LCDCON5_HWSWP;
3278c2ecf20Sopenharmony_ci		break;
3288c2ecf20Sopenharmony_ci	case 16:
3298c2ecf20Sopenharmony_ci		regs->lcdcon1 |= S3C2410_LCDCON1_TFT16BPP;
3308c2ecf20Sopenharmony_ci		regs->lcdcon5 &= ~S3C2410_LCDCON5_BSWP;
3318c2ecf20Sopenharmony_ci		regs->lcdcon5 |= S3C2410_LCDCON5_HWSWP;
3328c2ecf20Sopenharmony_ci		break;
3338c2ecf20Sopenharmony_ci	case 32:
3348c2ecf20Sopenharmony_ci		regs->lcdcon1 |= S3C2410_LCDCON1_TFT24BPP;
3358c2ecf20Sopenharmony_ci		regs->lcdcon5 &= ~(S3C2410_LCDCON5_BSWP |
3368c2ecf20Sopenharmony_ci				   S3C2410_LCDCON5_HWSWP |
3378c2ecf20Sopenharmony_ci				   S3C2410_LCDCON5_BPP24BL);
3388c2ecf20Sopenharmony_ci		break;
3398c2ecf20Sopenharmony_ci	default:
3408c2ecf20Sopenharmony_ci		/* invalid pixel depth */
3418c2ecf20Sopenharmony_ci		dev_err(fbi->dev, "invalid bpp %d\n",
3428c2ecf20Sopenharmony_ci			var->bits_per_pixel);
3438c2ecf20Sopenharmony_ci	}
3448c2ecf20Sopenharmony_ci	/* update X/Y info */
3458c2ecf20Sopenharmony_ci	dprintk("setting vert: up=%d, low=%d, sync=%d\n",
3468c2ecf20Sopenharmony_ci		var->upper_margin, var->lower_margin, var->vsync_len);
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	dprintk("setting horz: lft=%d, rt=%d, sync=%d\n",
3498c2ecf20Sopenharmony_ci		var->left_margin, var->right_margin, var->hsync_len);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	regs->lcdcon2 = S3C2410_LCDCON2_LINEVAL(var->yres - 1) |
3528c2ecf20Sopenharmony_ci			S3C2410_LCDCON2_VBPD(var->upper_margin - 1) |
3538c2ecf20Sopenharmony_ci			S3C2410_LCDCON2_VFPD(var->lower_margin - 1) |
3548c2ecf20Sopenharmony_ci			S3C2410_LCDCON2_VSPW(var->vsync_len - 1);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	regs->lcdcon3 = S3C2410_LCDCON3_HBPD(var->right_margin - 1) |
3578c2ecf20Sopenharmony_ci			S3C2410_LCDCON3_HFPD(var->left_margin - 1) |
3588c2ecf20Sopenharmony_ci			S3C2410_LCDCON3_HOZVAL(var->xres - 1);
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	regs->lcdcon4 = S3C2410_LCDCON4_HSPW(var->hsync_len - 1);
3618c2ecf20Sopenharmony_ci}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci/* s3c2410fb_activate_var
3648c2ecf20Sopenharmony_ci *
3658c2ecf20Sopenharmony_ci * activate (set) the controller from the given framebuffer
3668c2ecf20Sopenharmony_ci * information
3678c2ecf20Sopenharmony_ci */
3688c2ecf20Sopenharmony_cistatic void s3c2410fb_activate_var(struct fb_info *info)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	struct s3c2410fb_info *fbi = info->par;
3718c2ecf20Sopenharmony_ci	void __iomem *regs = fbi->io;
3728c2ecf20Sopenharmony_ci	int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT;
3738c2ecf20Sopenharmony_ci	struct fb_var_screeninfo *var = &info->var;
3748c2ecf20Sopenharmony_ci	int clkdiv;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	clkdiv = DIV_ROUND_UP(s3c2410fb_calc_pixclk(fbi, var->pixclock), 2);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	dprintk("%s: var->xres  = %d\n", __func__, var->xres);
3798c2ecf20Sopenharmony_ci	dprintk("%s: var->yres  = %d\n", __func__, var->yres);
3808c2ecf20Sopenharmony_ci	dprintk("%s: var->bpp   = %d\n", __func__, var->bits_per_pixel);
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	if (type == S3C2410_LCDCON1_TFT) {
3838c2ecf20Sopenharmony_ci		s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs);
3848c2ecf20Sopenharmony_ci		--clkdiv;
3858c2ecf20Sopenharmony_ci		if (clkdiv < 0)
3868c2ecf20Sopenharmony_ci			clkdiv = 0;
3878c2ecf20Sopenharmony_ci	} else {
3888c2ecf20Sopenharmony_ci		s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs);
3898c2ecf20Sopenharmony_ci		if (clkdiv < 2)
3908c2ecf20Sopenharmony_ci			clkdiv = 2;
3918c2ecf20Sopenharmony_ci	}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	fbi->regs.lcdcon1 |=  S3C2410_LCDCON1_CLKVAL(clkdiv);
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	/* write new registers */
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	dprintk("new register set:\n");
3988c2ecf20Sopenharmony_ci	dprintk("lcdcon[1] = 0x%08lx\n", fbi->regs.lcdcon1);
3998c2ecf20Sopenharmony_ci	dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2);
4008c2ecf20Sopenharmony_ci	dprintk("lcdcon[3] = 0x%08lx\n", fbi->regs.lcdcon3);
4018c2ecf20Sopenharmony_ci	dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4);
4028c2ecf20Sopenharmony_ci	dprintk("lcdcon[5] = 0x%08lx\n", fbi->regs.lcdcon5);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID,
4058c2ecf20Sopenharmony_ci		regs + S3C2410_LCDCON1);
4068c2ecf20Sopenharmony_ci	writel(fbi->regs.lcdcon2, regs + S3C2410_LCDCON2);
4078c2ecf20Sopenharmony_ci	writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3);
4088c2ecf20Sopenharmony_ci	writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4);
4098c2ecf20Sopenharmony_ci	writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5);
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	/* set lcd address pointers */
4128c2ecf20Sopenharmony_ci	s3c2410fb_set_lcdaddr(info);
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,
4158c2ecf20Sopenharmony_ci	writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1);
4168c2ecf20Sopenharmony_ci}
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci/*
4198c2ecf20Sopenharmony_ci *      s3c2410fb_set_par - Alters the hardware state.
4208c2ecf20Sopenharmony_ci *      @info: frame buffer structure that represents a single frame buffer
4218c2ecf20Sopenharmony_ci *
4228c2ecf20Sopenharmony_ci */
4238c2ecf20Sopenharmony_cistatic int s3c2410fb_set_par(struct fb_info *info)
4248c2ecf20Sopenharmony_ci{
4258c2ecf20Sopenharmony_ci	struct fb_var_screeninfo *var = &info->var;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	switch (var->bits_per_pixel) {
4288c2ecf20Sopenharmony_ci	case 32:
4298c2ecf20Sopenharmony_ci	case 16:
4308c2ecf20Sopenharmony_ci	case 12:
4318c2ecf20Sopenharmony_ci		info->fix.visual = FB_VISUAL_TRUECOLOR;
4328c2ecf20Sopenharmony_ci		break;
4338c2ecf20Sopenharmony_ci	case 1:
4348c2ecf20Sopenharmony_ci		info->fix.visual = FB_VISUAL_MONO01;
4358c2ecf20Sopenharmony_ci		break;
4368c2ecf20Sopenharmony_ci	default:
4378c2ecf20Sopenharmony_ci		info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
4388c2ecf20Sopenharmony_ci		break;
4398c2ecf20Sopenharmony_ci	}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	/* activate this new configuration */
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	s3c2410fb_activate_var(info);
4468c2ecf20Sopenharmony_ci	return 0;
4478c2ecf20Sopenharmony_ci}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_cistatic void schedule_palette_update(struct s3c2410fb_info *fbi,
4508c2ecf20Sopenharmony_ci				    unsigned int regno, unsigned int val)
4518c2ecf20Sopenharmony_ci{
4528c2ecf20Sopenharmony_ci	unsigned long flags;
4538c2ecf20Sopenharmony_ci	unsigned long irqen;
4548c2ecf20Sopenharmony_ci	void __iomem *irq_base = fbi->irq_base;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	local_irq_save(flags);
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	fbi->palette_buffer[regno] = val;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	if (!fbi->palette_ready) {
4618c2ecf20Sopenharmony_ci		fbi->palette_ready = 1;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci		/* enable IRQ */
4648c2ecf20Sopenharmony_ci		irqen = readl(irq_base + S3C24XX_LCDINTMSK);
4658c2ecf20Sopenharmony_ci		irqen &= ~S3C2410_LCDINT_FRSYNC;
4668c2ecf20Sopenharmony_ci		writel(irqen, irq_base + S3C24XX_LCDINTMSK);
4678c2ecf20Sopenharmony_ci	}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	local_irq_restore(flags);
4708c2ecf20Sopenharmony_ci}
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci/* from pxafb.c */
4738c2ecf20Sopenharmony_cistatic inline unsigned int chan_to_field(unsigned int chan,
4748c2ecf20Sopenharmony_ci					 struct fb_bitfield *bf)
4758c2ecf20Sopenharmony_ci{
4768c2ecf20Sopenharmony_ci	chan &= 0xffff;
4778c2ecf20Sopenharmony_ci	chan >>= 16 - bf->length;
4788c2ecf20Sopenharmony_ci	return chan << bf->offset;
4798c2ecf20Sopenharmony_ci}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_cistatic int s3c2410fb_setcolreg(unsigned regno,
4828c2ecf20Sopenharmony_ci			       unsigned red, unsigned green, unsigned blue,
4838c2ecf20Sopenharmony_ci			       unsigned transp, struct fb_info *info)
4848c2ecf20Sopenharmony_ci{
4858c2ecf20Sopenharmony_ci	struct s3c2410fb_info *fbi = info->par;
4868c2ecf20Sopenharmony_ci	void __iomem *regs = fbi->io;
4878c2ecf20Sopenharmony_ci	unsigned int val;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	/* dprintk("setcol: regno=%d, rgb=%d,%d,%d\n",
4908c2ecf20Sopenharmony_ci		   regno, red, green, blue); */
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	switch (info->fix.visual) {
4938c2ecf20Sopenharmony_ci	case FB_VISUAL_TRUECOLOR:
4948c2ecf20Sopenharmony_ci		/* true-colour, use pseudo-palette */
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci		if (regno < 16) {
4978c2ecf20Sopenharmony_ci			u32 *pal = info->pseudo_palette;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci			val  = chan_to_field(red,   &info->var.red);
5008c2ecf20Sopenharmony_ci			val |= chan_to_field(green, &info->var.green);
5018c2ecf20Sopenharmony_ci			val |= chan_to_field(blue,  &info->var.blue);
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci			pal[regno] = val;
5048c2ecf20Sopenharmony_ci		}
5058c2ecf20Sopenharmony_ci		break;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	case FB_VISUAL_PSEUDOCOLOR:
5088c2ecf20Sopenharmony_ci		if (regno < 256) {
5098c2ecf20Sopenharmony_ci			/* currently assume RGB 5-6-5 mode */
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci			val  = (red   >>  0) & 0xf800;
5128c2ecf20Sopenharmony_ci			val |= (green >>  5) & 0x07e0;
5138c2ecf20Sopenharmony_ci			val |= (blue  >> 11) & 0x001f;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci			writel(val, regs + S3C2410_TFTPAL(regno));
5168c2ecf20Sopenharmony_ci			schedule_palette_update(fbi, regno, val);
5178c2ecf20Sopenharmony_ci		}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci		break;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	default:
5228c2ecf20Sopenharmony_ci		return 1;	/* unknown type */
5238c2ecf20Sopenharmony_ci	}
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	return 0;
5268c2ecf20Sopenharmony_ci}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci/* s3c2410fb_lcd_enable
5298c2ecf20Sopenharmony_ci *
5308c2ecf20Sopenharmony_ci * shutdown the lcd controller
5318c2ecf20Sopenharmony_ci */
5328c2ecf20Sopenharmony_cistatic void s3c2410fb_lcd_enable(struct s3c2410fb_info *fbi, int enable)
5338c2ecf20Sopenharmony_ci{
5348c2ecf20Sopenharmony_ci	unsigned long flags;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	local_irq_save(flags);
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	if (enable)
5398c2ecf20Sopenharmony_ci		fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID;
5408c2ecf20Sopenharmony_ci	else
5418c2ecf20Sopenharmony_ci		fbi->regs.lcdcon1 &= ~S3C2410_LCDCON1_ENVID;
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	writel(fbi->regs.lcdcon1, fbi->io + S3C2410_LCDCON1);
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	local_irq_restore(flags);
5468c2ecf20Sopenharmony_ci}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci/*
5508c2ecf20Sopenharmony_ci *      s3c2410fb_blank
5518c2ecf20Sopenharmony_ci *	@blank_mode: the blank mode we want.
5528c2ecf20Sopenharmony_ci *	@info: frame buffer structure that represents a single frame buffer
5538c2ecf20Sopenharmony_ci *
5548c2ecf20Sopenharmony_ci *	Blank the screen if blank_mode != 0, else unblank. Return 0 if
5558c2ecf20Sopenharmony_ci *	blanking succeeded, != 0 if un-/blanking failed due to e.g. a
5568c2ecf20Sopenharmony_ci *	video mode which doesn't support it. Implements VESA suspend
5578c2ecf20Sopenharmony_ci *	and powerdown modes on hardware that supports disabling hsync/vsync:
5588c2ecf20Sopenharmony_ci *
5598c2ecf20Sopenharmony_ci *	Returns negative errno on error, or zero on success.
5608c2ecf20Sopenharmony_ci *
5618c2ecf20Sopenharmony_ci */
5628c2ecf20Sopenharmony_cistatic int s3c2410fb_blank(int blank_mode, struct fb_info *info)
5638c2ecf20Sopenharmony_ci{
5648c2ecf20Sopenharmony_ci	struct s3c2410fb_info *fbi = info->par;
5658c2ecf20Sopenharmony_ci	void __iomem *tpal_reg = fbi->io;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	dprintk("blank(mode=%d, info=%p)\n", blank_mode, info);
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	tpal_reg += is_s3c2412(fbi) ? S3C2412_TPAL : S3C2410_TPAL;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	if (blank_mode == FB_BLANK_POWERDOWN)
5728c2ecf20Sopenharmony_ci		s3c2410fb_lcd_enable(fbi, 0);
5738c2ecf20Sopenharmony_ci	else
5748c2ecf20Sopenharmony_ci		s3c2410fb_lcd_enable(fbi, 1);
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	if (blank_mode == FB_BLANK_UNBLANK)
5778c2ecf20Sopenharmony_ci		writel(0x0, tpal_reg);
5788c2ecf20Sopenharmony_ci	else {
5798c2ecf20Sopenharmony_ci		dprintk("setting TPAL to output 0x000000\n");
5808c2ecf20Sopenharmony_ci		writel(S3C2410_TPAL_EN, tpal_reg);
5818c2ecf20Sopenharmony_ci	}
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	return 0;
5848c2ecf20Sopenharmony_ci}
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_cistatic int s3c2410fb_debug_show(struct device *dev,
5878c2ecf20Sopenharmony_ci				struct device_attribute *attr, char *buf)
5888c2ecf20Sopenharmony_ci{
5898c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%s\n", debug ? "on" : "off");
5908c2ecf20Sopenharmony_ci}
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_cistatic int s3c2410fb_debug_store(struct device *dev,
5938c2ecf20Sopenharmony_ci				 struct device_attribute *attr,
5948c2ecf20Sopenharmony_ci				 const char *buf, size_t len)
5958c2ecf20Sopenharmony_ci{
5968c2ecf20Sopenharmony_ci	if (len < 1)
5978c2ecf20Sopenharmony_ci		return -EINVAL;
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	if (strncasecmp(buf, "on", 2) == 0 ||
6008c2ecf20Sopenharmony_ci	    strncasecmp(buf, "1", 1) == 0) {
6018c2ecf20Sopenharmony_ci		debug = 1;
6028c2ecf20Sopenharmony_ci		dev_dbg(dev, "s3c2410fb: Debug On");
6038c2ecf20Sopenharmony_ci	} else if (strncasecmp(buf, "off", 3) == 0 ||
6048c2ecf20Sopenharmony_ci		   strncasecmp(buf, "0", 1) == 0) {
6058c2ecf20Sopenharmony_ci		debug = 0;
6068c2ecf20Sopenharmony_ci		dev_dbg(dev, "s3c2410fb: Debug Off");
6078c2ecf20Sopenharmony_ci	} else {
6088c2ecf20Sopenharmony_ci		return -EINVAL;
6098c2ecf20Sopenharmony_ci	}
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	return len;
6128c2ecf20Sopenharmony_ci}
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_cistatic DEVICE_ATTR(debug, 0664, s3c2410fb_debug_show, s3c2410fb_debug_store);
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_cistatic const struct fb_ops s3c2410fb_ops = {
6178c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
6188c2ecf20Sopenharmony_ci	.fb_check_var	= s3c2410fb_check_var,
6198c2ecf20Sopenharmony_ci	.fb_set_par	= s3c2410fb_set_par,
6208c2ecf20Sopenharmony_ci	.fb_blank	= s3c2410fb_blank,
6218c2ecf20Sopenharmony_ci	.fb_setcolreg	= s3c2410fb_setcolreg,
6228c2ecf20Sopenharmony_ci	.fb_fillrect	= cfb_fillrect,
6238c2ecf20Sopenharmony_ci	.fb_copyarea	= cfb_copyarea,
6248c2ecf20Sopenharmony_ci	.fb_imageblit	= cfb_imageblit,
6258c2ecf20Sopenharmony_ci};
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci/*
6288c2ecf20Sopenharmony_ci * s3c2410fb_map_video_memory():
6298c2ecf20Sopenharmony_ci *	Allocates the DRAM memory for the frame buffer.  This buffer is
6308c2ecf20Sopenharmony_ci *	remapped into a non-cached, non-buffered, memory region to
6318c2ecf20Sopenharmony_ci *	allow palette and pixel writes to occur without flushing the
6328c2ecf20Sopenharmony_ci *	cache.  Once this area is remapped, all virtual memory
6338c2ecf20Sopenharmony_ci *	access to the video memory should occur at the new region.
6348c2ecf20Sopenharmony_ci */
6358c2ecf20Sopenharmony_cistatic int s3c2410fb_map_video_memory(struct fb_info *info)
6368c2ecf20Sopenharmony_ci{
6378c2ecf20Sopenharmony_ci	struct s3c2410fb_info *fbi = info->par;
6388c2ecf20Sopenharmony_ci	dma_addr_t map_dma;
6398c2ecf20Sopenharmony_ci	unsigned map_size = PAGE_ALIGN(info->fix.smem_len);
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	dprintk("map_video_memory(fbi=%p) map_size %u\n", fbi, map_size);
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	info->screen_base = dma_alloc_wc(fbi->dev, map_size, &map_dma,
6448c2ecf20Sopenharmony_ci					 GFP_KERNEL);
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	if (info->screen_base) {
6478c2ecf20Sopenharmony_ci		/* prevent initial garbage on screen */
6488c2ecf20Sopenharmony_ci		dprintk("map_video_memory: clear %p:%08x\n",
6498c2ecf20Sopenharmony_ci			info->screen_base, map_size);
6508c2ecf20Sopenharmony_ci		memset(info->screen_base, 0x00, map_size);
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci		info->fix.smem_start = map_dma;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci		dprintk("map_video_memory: dma=%08lx cpu=%p size=%08x\n",
6558c2ecf20Sopenharmony_ci			info->fix.smem_start, info->screen_base, map_size);
6568c2ecf20Sopenharmony_ci	}
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	return info->screen_base ? 0 : -ENOMEM;
6598c2ecf20Sopenharmony_ci}
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_cistatic inline void s3c2410fb_unmap_video_memory(struct fb_info *info)
6628c2ecf20Sopenharmony_ci{
6638c2ecf20Sopenharmony_ci	struct s3c2410fb_info *fbi = info->par;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	dma_free_wc(fbi->dev, PAGE_ALIGN(info->fix.smem_len),
6668c2ecf20Sopenharmony_ci		    info->screen_base, info->fix.smem_start);
6678c2ecf20Sopenharmony_ci}
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_cistatic inline void modify_gpio(void __iomem *reg,
6708c2ecf20Sopenharmony_ci			       unsigned long set, unsigned long mask)
6718c2ecf20Sopenharmony_ci{
6728c2ecf20Sopenharmony_ci	unsigned long tmp;
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	if (!reg)
6758c2ecf20Sopenharmony_ci		return;
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	tmp = readl(reg) & ~mask;
6788c2ecf20Sopenharmony_ci	writel(tmp | set, reg);
6798c2ecf20Sopenharmony_ci}
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci/*
6828c2ecf20Sopenharmony_ci * s3c2410fb_init_registers - Initialise all LCD-related registers
6838c2ecf20Sopenharmony_ci */
6848c2ecf20Sopenharmony_cistatic int s3c2410fb_init_registers(struct fb_info *info)
6858c2ecf20Sopenharmony_ci{
6868c2ecf20Sopenharmony_ci	struct s3c2410fb_info *fbi = info->par;
6878c2ecf20Sopenharmony_ci	struct s3c2410fb_mach_info *mach_info = dev_get_platdata(fbi->dev);
6888c2ecf20Sopenharmony_ci	unsigned long flags;
6898c2ecf20Sopenharmony_ci	void __iomem *regs = fbi->io;
6908c2ecf20Sopenharmony_ci	void __iomem *tpal;
6918c2ecf20Sopenharmony_ci	void __iomem *lpcsel;
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	if (is_s3c2412(fbi)) {
6948c2ecf20Sopenharmony_ci		tpal = regs + S3C2412_TPAL;
6958c2ecf20Sopenharmony_ci		lpcsel = regs + S3C2412_TCONSEL;
6968c2ecf20Sopenharmony_ci	} else {
6978c2ecf20Sopenharmony_ci		tpal = regs + S3C2410_TPAL;
6988c2ecf20Sopenharmony_ci		lpcsel = regs + S3C2410_LPCSEL;
6998c2ecf20Sopenharmony_ci	}
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	/* Initialise LCD with values from haret */
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	local_irq_save(flags);
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	/* modify the gpio(s) with interrupts set (bjd) */
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	modify_gpio(mach_info->gpcup_reg,  mach_info->gpcup,  mach_info->gpcup_mask);
7088c2ecf20Sopenharmony_ci	modify_gpio(mach_info->gpccon_reg, mach_info->gpccon, mach_info->gpccon_mask);
7098c2ecf20Sopenharmony_ci	modify_gpio(mach_info->gpdup_reg,  mach_info->gpdup,  mach_info->gpdup_mask);
7108c2ecf20Sopenharmony_ci	modify_gpio(mach_info->gpdcon_reg, mach_info->gpdcon, mach_info->gpdcon_mask);
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	local_irq_restore(flags);
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	dprintk("LPCSEL    = 0x%08lx\n", mach_info->lpcsel);
7158c2ecf20Sopenharmony_ci	writel(mach_info->lpcsel, lpcsel);
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	dprintk("replacing TPAL %08x\n", readl(tpal));
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	/* ensure temporary palette disabled */
7208c2ecf20Sopenharmony_ci	writel(0x00, tpal);
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	return 0;
7238c2ecf20Sopenharmony_ci}
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_cistatic void s3c2410fb_write_palette(struct s3c2410fb_info *fbi)
7268c2ecf20Sopenharmony_ci{
7278c2ecf20Sopenharmony_ci	unsigned int i;
7288c2ecf20Sopenharmony_ci	void __iomem *regs = fbi->io;
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	fbi->palette_ready = 0;
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	for (i = 0; i < 256; i++) {
7338c2ecf20Sopenharmony_ci		unsigned long ent = fbi->palette_buffer[i];
7348c2ecf20Sopenharmony_ci		if (ent == PALETTE_BUFF_CLEAR)
7358c2ecf20Sopenharmony_ci			continue;
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci		writel(ent, regs + S3C2410_TFTPAL(i));
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci		/* it seems the only way to know exactly
7408c2ecf20Sopenharmony_ci		 * if the palette wrote ok, is to check
7418c2ecf20Sopenharmony_ci		 * to see if the value verifies ok
7428c2ecf20Sopenharmony_ci		 */
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci		if (readw(regs + S3C2410_TFTPAL(i)) == ent)
7458c2ecf20Sopenharmony_ci			fbi->palette_buffer[i] = PALETTE_BUFF_CLEAR;
7468c2ecf20Sopenharmony_ci		else
7478c2ecf20Sopenharmony_ci			fbi->palette_ready = 1;   /* retry */
7488c2ecf20Sopenharmony_ci	}
7498c2ecf20Sopenharmony_ci}
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_cistatic irqreturn_t s3c2410fb_irq(int irq, void *dev_id)
7528c2ecf20Sopenharmony_ci{
7538c2ecf20Sopenharmony_ci	struct s3c2410fb_info *fbi = dev_id;
7548c2ecf20Sopenharmony_ci	void __iomem *irq_base = fbi->irq_base;
7558c2ecf20Sopenharmony_ci	unsigned long lcdirq = readl(irq_base + S3C24XX_LCDINTPND);
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	if (lcdirq & S3C2410_LCDINT_FRSYNC) {
7588c2ecf20Sopenharmony_ci		if (fbi->palette_ready)
7598c2ecf20Sopenharmony_ci			s3c2410fb_write_palette(fbi);
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci		writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDINTPND);
7628c2ecf20Sopenharmony_ci		writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDSRCPND);
7638c2ecf20Sopenharmony_ci	}
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
7668c2ecf20Sopenharmony_ci}
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM_S3C24XX_CPUFREQ
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_cistatic int s3c2410fb_cpufreq_transition(struct notifier_block *nb,
7718c2ecf20Sopenharmony_ci					unsigned long val, void *data)
7728c2ecf20Sopenharmony_ci{
7738c2ecf20Sopenharmony_ci	struct s3c2410fb_info *info;
7748c2ecf20Sopenharmony_ci	struct fb_info *fbinfo;
7758c2ecf20Sopenharmony_ci	long delta_f;
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	info = container_of(nb, struct s3c2410fb_info, freq_transition);
7788c2ecf20Sopenharmony_ci	fbinfo = dev_get_drvdata(info->dev);
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	/* work out change, <0 for speed-up */
7818c2ecf20Sopenharmony_ci	delta_f = info->clk_rate - clk_get_rate(info->clk);
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	if ((val == CPUFREQ_POSTCHANGE && delta_f > 0) ||
7848c2ecf20Sopenharmony_ci	    (val == CPUFREQ_PRECHANGE && delta_f < 0)) {
7858c2ecf20Sopenharmony_ci		info->clk_rate = clk_get_rate(info->clk);
7868c2ecf20Sopenharmony_ci		s3c2410fb_activate_var(fbinfo);
7878c2ecf20Sopenharmony_ci	}
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	return 0;
7908c2ecf20Sopenharmony_ci}
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_cistatic inline int s3c2410fb_cpufreq_register(struct s3c2410fb_info *info)
7938c2ecf20Sopenharmony_ci{
7948c2ecf20Sopenharmony_ci	info->freq_transition.notifier_call = s3c2410fb_cpufreq_transition;
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	return cpufreq_register_notifier(&info->freq_transition,
7978c2ecf20Sopenharmony_ci					 CPUFREQ_TRANSITION_NOTIFIER);
7988c2ecf20Sopenharmony_ci}
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_cistatic inline void s3c2410fb_cpufreq_deregister(struct s3c2410fb_info *info)
8018c2ecf20Sopenharmony_ci{
8028c2ecf20Sopenharmony_ci	cpufreq_unregister_notifier(&info->freq_transition,
8038c2ecf20Sopenharmony_ci				    CPUFREQ_TRANSITION_NOTIFIER);
8048c2ecf20Sopenharmony_ci}
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci#else
8078c2ecf20Sopenharmony_cistatic inline int s3c2410fb_cpufreq_register(struct s3c2410fb_info *info)
8088c2ecf20Sopenharmony_ci{
8098c2ecf20Sopenharmony_ci	return 0;
8108c2ecf20Sopenharmony_ci}
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_cistatic inline void s3c2410fb_cpufreq_deregister(struct s3c2410fb_info *info)
8138c2ecf20Sopenharmony_ci{
8148c2ecf20Sopenharmony_ci}
8158c2ecf20Sopenharmony_ci#endif
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_cistatic const char driver_name[] = "s3c2410fb";
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_cistatic int s3c24xxfb_probe(struct platform_device *pdev,
8218c2ecf20Sopenharmony_ci			   enum s3c_drv_type drv_type)
8228c2ecf20Sopenharmony_ci{
8238c2ecf20Sopenharmony_ci	struct s3c2410fb_info *info;
8248c2ecf20Sopenharmony_ci	struct s3c2410fb_display *display;
8258c2ecf20Sopenharmony_ci	struct fb_info *fbinfo;
8268c2ecf20Sopenharmony_ci	struct s3c2410fb_mach_info *mach_info;
8278c2ecf20Sopenharmony_ci	struct resource *res;
8288c2ecf20Sopenharmony_ci	int ret;
8298c2ecf20Sopenharmony_ci	int irq;
8308c2ecf20Sopenharmony_ci	int i;
8318c2ecf20Sopenharmony_ci	int size;
8328c2ecf20Sopenharmony_ci	u32 lcdcon1;
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	mach_info = dev_get_platdata(&pdev->dev);
8358c2ecf20Sopenharmony_ci	if (mach_info == NULL) {
8368c2ecf20Sopenharmony_ci		dev_err(&pdev->dev,
8378c2ecf20Sopenharmony_ci			"no platform data for lcd, cannot attach\n");
8388c2ecf20Sopenharmony_ci		return -EINVAL;
8398c2ecf20Sopenharmony_ci	}
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	if (mach_info->default_display >= mach_info->num_displays) {
8428c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "default is %d but only %d displays\n",
8438c2ecf20Sopenharmony_ci			mach_info->default_display, mach_info->num_displays);
8448c2ecf20Sopenharmony_ci		return -EINVAL;
8458c2ecf20Sopenharmony_ci	}
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	display = mach_info->displays + mach_info->default_display;
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
8508c2ecf20Sopenharmony_ci	if (irq < 0) {
8518c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "no irq for device\n");
8528c2ecf20Sopenharmony_ci		return -ENOENT;
8538c2ecf20Sopenharmony_ci	}
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
8568c2ecf20Sopenharmony_ci	if (!fbinfo)
8578c2ecf20Sopenharmony_ci		return -ENOMEM;
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, fbinfo);
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	info = fbinfo->par;
8628c2ecf20Sopenharmony_ci	info->dev = &pdev->dev;
8638c2ecf20Sopenharmony_ci	info->drv_type = drv_type;
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
8668c2ecf20Sopenharmony_ci	if (res == NULL) {
8678c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to get memory registers\n");
8688c2ecf20Sopenharmony_ci		ret = -ENXIO;
8698c2ecf20Sopenharmony_ci		goto dealloc_fb;
8708c2ecf20Sopenharmony_ci	}
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci	size = resource_size(res);
8738c2ecf20Sopenharmony_ci	info->mem = request_mem_region(res->start, size, pdev->name);
8748c2ecf20Sopenharmony_ci	if (info->mem == NULL) {
8758c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to get memory region\n");
8768c2ecf20Sopenharmony_ci		ret = -ENOENT;
8778c2ecf20Sopenharmony_ci		goto dealloc_fb;
8788c2ecf20Sopenharmony_ci	}
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci	info->io = ioremap(res->start, size);
8818c2ecf20Sopenharmony_ci	if (info->io == NULL) {
8828c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "ioremap() of registers failed\n");
8838c2ecf20Sopenharmony_ci		ret = -ENXIO;
8848c2ecf20Sopenharmony_ci		goto release_mem;
8858c2ecf20Sopenharmony_ci	}
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	if (drv_type == DRV_S3C2412)
8888c2ecf20Sopenharmony_ci		info->irq_base = info->io + S3C2412_LCDINTBASE;
8898c2ecf20Sopenharmony_ci	else
8908c2ecf20Sopenharmony_ci		info->irq_base = info->io + S3C2410_LCDINTBASE;
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	dprintk("devinit\n");
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	strcpy(fbinfo->fix.id, driver_name);
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	/* Stop the video */
8978c2ecf20Sopenharmony_ci	lcdcon1 = readl(info->io + S3C2410_LCDCON1);
8988c2ecf20Sopenharmony_ci	writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci	fbinfo->fix.type	    = FB_TYPE_PACKED_PIXELS;
9018c2ecf20Sopenharmony_ci	fbinfo->fix.type_aux	    = 0;
9028c2ecf20Sopenharmony_ci	fbinfo->fix.xpanstep	    = 0;
9038c2ecf20Sopenharmony_ci	fbinfo->fix.ypanstep	    = 0;
9048c2ecf20Sopenharmony_ci	fbinfo->fix.ywrapstep	    = 0;
9058c2ecf20Sopenharmony_ci	fbinfo->fix.accel	    = FB_ACCEL_NONE;
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci	fbinfo->var.nonstd	    = 0;
9088c2ecf20Sopenharmony_ci	fbinfo->var.activate	    = FB_ACTIVATE_NOW;
9098c2ecf20Sopenharmony_ci	fbinfo->var.accel_flags     = 0;
9108c2ecf20Sopenharmony_ci	fbinfo->var.vmode	    = FB_VMODE_NONINTERLACED;
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	fbinfo->fbops		    = &s3c2410fb_ops;
9138c2ecf20Sopenharmony_ci	fbinfo->flags		    = FBINFO_FLAG_DEFAULT;
9148c2ecf20Sopenharmony_ci	fbinfo->pseudo_palette      = &info->pseudo_pal;
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	for (i = 0; i < 256; i++)
9178c2ecf20Sopenharmony_ci		info->palette_buffer[i] = PALETTE_BUFF_CLEAR;
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	ret = request_irq(irq, s3c2410fb_irq, 0, pdev->name, info);
9208c2ecf20Sopenharmony_ci	if (ret) {
9218c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);
9228c2ecf20Sopenharmony_ci		ret = -EBUSY;
9238c2ecf20Sopenharmony_ci		goto release_regs;
9248c2ecf20Sopenharmony_ci	}
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	info->clk = clk_get(NULL, "lcd");
9278c2ecf20Sopenharmony_ci	if (IS_ERR(info->clk)) {
9288c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to get lcd clock source\n");
9298c2ecf20Sopenharmony_ci		ret = PTR_ERR(info->clk);
9308c2ecf20Sopenharmony_ci		goto release_irq;
9318c2ecf20Sopenharmony_ci	}
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	clk_prepare_enable(info->clk);
9348c2ecf20Sopenharmony_ci	dprintk("got and enabled clock\n");
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	usleep_range(1000, 1100);
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	info->clk_rate = clk_get_rate(info->clk);
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	/* find maximum required memory size for display */
9418c2ecf20Sopenharmony_ci	for (i = 0; i < mach_info->num_displays; i++) {
9428c2ecf20Sopenharmony_ci		unsigned long smem_len = mach_info->displays[i].xres;
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci		smem_len *= mach_info->displays[i].yres;
9458c2ecf20Sopenharmony_ci		smem_len *= mach_info->displays[i].bpp;
9468c2ecf20Sopenharmony_ci		smem_len >>= 3;
9478c2ecf20Sopenharmony_ci		if (fbinfo->fix.smem_len < smem_len)
9488c2ecf20Sopenharmony_ci			fbinfo->fix.smem_len = smem_len;
9498c2ecf20Sopenharmony_ci	}
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci	/* Initialize video memory */
9528c2ecf20Sopenharmony_ci	ret = s3c2410fb_map_video_memory(fbinfo);
9538c2ecf20Sopenharmony_ci	if (ret) {
9548c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to allocate video RAM: %d\n", ret);
9558c2ecf20Sopenharmony_ci		ret = -ENOMEM;
9568c2ecf20Sopenharmony_ci		goto release_clock;
9578c2ecf20Sopenharmony_ci	}
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	dprintk("got video memory\n");
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci	fbinfo->var.xres = display->xres;
9628c2ecf20Sopenharmony_ci	fbinfo->var.yres = display->yres;
9638c2ecf20Sopenharmony_ci	fbinfo->var.bits_per_pixel = display->bpp;
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	s3c2410fb_init_registers(fbinfo);
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	s3c2410fb_check_var(&fbinfo->var, fbinfo);
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci	ret = s3c2410fb_cpufreq_register(info);
9708c2ecf20Sopenharmony_ci	if (ret < 0) {
9718c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to register cpufreq\n");
9728c2ecf20Sopenharmony_ci		goto free_video_memory;
9738c2ecf20Sopenharmony_ci	}
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_ci	ret = register_framebuffer(fbinfo);
9768c2ecf20Sopenharmony_ci	if (ret < 0) {
9778c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to register framebuffer device: %d\n",
9788c2ecf20Sopenharmony_ci			ret);
9798c2ecf20Sopenharmony_ci		goto free_cpufreq;
9808c2ecf20Sopenharmony_ci	}
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	/* create device files */
9838c2ecf20Sopenharmony_ci	ret = device_create_file(&pdev->dev, &dev_attr_debug);
9848c2ecf20Sopenharmony_ci	if (ret)
9858c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to add debug attribute\n");
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	dev_info(&pdev->dev, "fb%d: %s frame buffer device\n",
9888c2ecf20Sopenharmony_ci		fbinfo->node, fbinfo->fix.id);
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	return 0;
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_ci free_cpufreq:
9938c2ecf20Sopenharmony_ci	s3c2410fb_cpufreq_deregister(info);
9948c2ecf20Sopenharmony_cifree_video_memory:
9958c2ecf20Sopenharmony_ci	s3c2410fb_unmap_video_memory(fbinfo);
9968c2ecf20Sopenharmony_cirelease_clock:
9978c2ecf20Sopenharmony_ci	clk_disable_unprepare(info->clk);
9988c2ecf20Sopenharmony_ci	clk_put(info->clk);
9998c2ecf20Sopenharmony_cirelease_irq:
10008c2ecf20Sopenharmony_ci	free_irq(irq, info);
10018c2ecf20Sopenharmony_cirelease_regs:
10028c2ecf20Sopenharmony_ci	iounmap(info->io);
10038c2ecf20Sopenharmony_cirelease_mem:
10048c2ecf20Sopenharmony_ci	release_mem_region(res->start, size);
10058c2ecf20Sopenharmony_cidealloc_fb:
10068c2ecf20Sopenharmony_ci	framebuffer_release(fbinfo);
10078c2ecf20Sopenharmony_ci	return ret;
10088c2ecf20Sopenharmony_ci}
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_cistatic int s3c2410fb_probe(struct platform_device *pdev)
10118c2ecf20Sopenharmony_ci{
10128c2ecf20Sopenharmony_ci	return s3c24xxfb_probe(pdev, DRV_S3C2410);
10138c2ecf20Sopenharmony_ci}
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_cistatic int s3c2412fb_probe(struct platform_device *pdev)
10168c2ecf20Sopenharmony_ci{
10178c2ecf20Sopenharmony_ci	return s3c24xxfb_probe(pdev, DRV_S3C2412);
10188c2ecf20Sopenharmony_ci}
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci/*
10228c2ecf20Sopenharmony_ci *  Cleanup
10238c2ecf20Sopenharmony_ci */
10248c2ecf20Sopenharmony_cistatic int s3c2410fb_remove(struct platform_device *pdev)
10258c2ecf20Sopenharmony_ci{
10268c2ecf20Sopenharmony_ci	struct fb_info *fbinfo = platform_get_drvdata(pdev);
10278c2ecf20Sopenharmony_ci	struct s3c2410fb_info *info = fbinfo->par;
10288c2ecf20Sopenharmony_ci	int irq;
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	unregister_framebuffer(fbinfo);
10318c2ecf20Sopenharmony_ci	s3c2410fb_cpufreq_deregister(info);
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	s3c2410fb_lcd_enable(info, 0);
10348c2ecf20Sopenharmony_ci	usleep_range(1000, 1100);
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci	s3c2410fb_unmap_video_memory(fbinfo);
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci	if (info->clk) {
10398c2ecf20Sopenharmony_ci		clk_disable_unprepare(info->clk);
10408c2ecf20Sopenharmony_ci		clk_put(info->clk);
10418c2ecf20Sopenharmony_ci		info->clk = NULL;
10428c2ecf20Sopenharmony_ci	}
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
10458c2ecf20Sopenharmony_ci	free_irq(irq, info);
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci	iounmap(info->io);
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci	release_mem_region(info->mem->start, resource_size(info->mem));
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ci	framebuffer_release(fbinfo);
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	return 0;
10548c2ecf20Sopenharmony_ci}
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci/* suspend and resume support for the lcd controller */
10598c2ecf20Sopenharmony_cistatic int s3c2410fb_suspend(struct platform_device *dev, pm_message_t state)
10608c2ecf20Sopenharmony_ci{
10618c2ecf20Sopenharmony_ci	struct fb_info	   *fbinfo = platform_get_drvdata(dev);
10628c2ecf20Sopenharmony_ci	struct s3c2410fb_info *info = fbinfo->par;
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci	s3c2410fb_lcd_enable(info, 0);
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci	/* sleep before disabling the clock, we need to ensure
10678c2ecf20Sopenharmony_ci	 * the LCD DMA engine is not going to get back on the bus
10688c2ecf20Sopenharmony_ci	 * before the clock goes off again (bjd) */
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci	usleep_range(1000, 1100);
10718c2ecf20Sopenharmony_ci	clk_disable_unprepare(info->clk);
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci	return 0;
10748c2ecf20Sopenharmony_ci}
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_cistatic int s3c2410fb_resume(struct platform_device *dev)
10778c2ecf20Sopenharmony_ci{
10788c2ecf20Sopenharmony_ci	struct fb_info	   *fbinfo = platform_get_drvdata(dev);
10798c2ecf20Sopenharmony_ci	struct s3c2410fb_info *info = fbinfo->par;
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci	clk_prepare_enable(info->clk);
10828c2ecf20Sopenharmony_ci	usleep_range(1000, 1100);
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci	s3c2410fb_init_registers(fbinfo);
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_ci	/* re-activate our display after resume */
10878c2ecf20Sopenharmony_ci	s3c2410fb_activate_var(fbinfo);
10888c2ecf20Sopenharmony_ci	s3c2410fb_blank(FB_BLANK_UNBLANK, fbinfo);
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci	return 0;
10918c2ecf20Sopenharmony_ci}
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci#else
10948c2ecf20Sopenharmony_ci#define s3c2410fb_suspend NULL
10958c2ecf20Sopenharmony_ci#define s3c2410fb_resume  NULL
10968c2ecf20Sopenharmony_ci#endif
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_cistatic struct platform_driver s3c2410fb_driver = {
10998c2ecf20Sopenharmony_ci	.probe		= s3c2410fb_probe,
11008c2ecf20Sopenharmony_ci	.remove		= s3c2410fb_remove,
11018c2ecf20Sopenharmony_ci	.suspend	= s3c2410fb_suspend,
11028c2ecf20Sopenharmony_ci	.resume		= s3c2410fb_resume,
11038c2ecf20Sopenharmony_ci	.driver		= {
11048c2ecf20Sopenharmony_ci		.name	= "s3c2410-lcd",
11058c2ecf20Sopenharmony_ci	},
11068c2ecf20Sopenharmony_ci};
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_cistatic struct platform_driver s3c2412fb_driver = {
11098c2ecf20Sopenharmony_ci	.probe		= s3c2412fb_probe,
11108c2ecf20Sopenharmony_ci	.remove		= s3c2410fb_remove,
11118c2ecf20Sopenharmony_ci	.suspend	= s3c2410fb_suspend,
11128c2ecf20Sopenharmony_ci	.resume		= s3c2410fb_resume,
11138c2ecf20Sopenharmony_ci	.driver		= {
11148c2ecf20Sopenharmony_ci		.name	= "s3c2412-lcd",
11158c2ecf20Sopenharmony_ci	},
11168c2ecf20Sopenharmony_ci};
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ciint __init s3c2410fb_init(void)
11198c2ecf20Sopenharmony_ci{
11208c2ecf20Sopenharmony_ci	int ret = platform_driver_register(&s3c2410fb_driver);
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci	if (ret == 0)
11238c2ecf20Sopenharmony_ci		ret = platform_driver_register(&s3c2412fb_driver);
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci	return ret;
11268c2ecf20Sopenharmony_ci}
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_cistatic void __exit s3c2410fb_cleanup(void)
11298c2ecf20Sopenharmony_ci{
11308c2ecf20Sopenharmony_ci	platform_driver_unregister(&s3c2410fb_driver);
11318c2ecf20Sopenharmony_ci	platform_driver_unregister(&s3c2412fb_driver);
11328c2ecf20Sopenharmony_ci}
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_cimodule_init(s3c2410fb_init);
11358c2ecf20Sopenharmony_cimodule_exit(s3c2410fb_cleanup);
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ciMODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
11388c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ben Dooks <ben-linux@fluff.org>");
11398c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Framebuffer driver for the s3c2410");
11408c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
11418c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:s3c2410-lcd");
11428c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:s3c2412-lcd");
1143