18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * linux/drivers/video/pxa168fb.c -- Marvell PXA168 LCD Controller
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci *  Copyright (C) 2008 Marvell International Ltd.
58c2ecf20Sopenharmony_ci *  All rights reserved.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  2009-02-16  adapted from original version for PXA168/910
88c2ecf20Sopenharmony_ci *              Jun Nie <njun@marvell.com>
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
118c2ecf20Sopenharmony_ci * License. See the file COPYING in the main directory of this archive for
128c2ecf20Sopenharmony_ci * more details.
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/kernel.h>
178c2ecf20Sopenharmony_ci#include <linux/sched.h>
188c2ecf20Sopenharmony_ci#include <linux/string.h>
198c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
208c2ecf20Sopenharmony_ci#include <linux/slab.h>
218c2ecf20Sopenharmony_ci#include <linux/fb.h>
228c2ecf20Sopenharmony_ci#include <linux/delay.h>
238c2ecf20Sopenharmony_ci#include <linux/init.h>
248c2ecf20Sopenharmony_ci#include <linux/io.h>
258c2ecf20Sopenharmony_ci#include <linux/ioport.h>
268c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
278c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
288c2ecf20Sopenharmony_ci#include <linux/clk.h>
298c2ecf20Sopenharmony_ci#include <linux/err.h>
308c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
318c2ecf20Sopenharmony_ci#include <video/pxa168fb.h>
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#include "pxa168fb.h"
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define DEFAULT_REFRESH		60	/* Hz */
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic int determine_best_pix_fmt(struct fb_var_screeninfo *var)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	/*
408c2ecf20Sopenharmony_ci	 * Pseudocolor mode?
418c2ecf20Sopenharmony_ci	 */
428c2ecf20Sopenharmony_ci	if (var->bits_per_pixel == 8)
438c2ecf20Sopenharmony_ci		return PIX_FMT_PSEUDOCOLOR;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	/*
468c2ecf20Sopenharmony_ci	 * Check for 565/1555.
478c2ecf20Sopenharmony_ci	 */
488c2ecf20Sopenharmony_ci	if (var->bits_per_pixel == 16 && var->red.length <= 5 &&
498c2ecf20Sopenharmony_ci	    var->green.length <= 6 && var->blue.length <= 5) {
508c2ecf20Sopenharmony_ci		if (var->transp.length == 0) {
518c2ecf20Sopenharmony_ci			if (var->red.offset >= var->blue.offset)
528c2ecf20Sopenharmony_ci				return PIX_FMT_RGB565;
538c2ecf20Sopenharmony_ci			else
548c2ecf20Sopenharmony_ci				return PIX_FMT_BGR565;
558c2ecf20Sopenharmony_ci		}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci		if (var->transp.length == 1 && var->green.length <= 5) {
588c2ecf20Sopenharmony_ci			if (var->red.offset >= var->blue.offset)
598c2ecf20Sopenharmony_ci				return PIX_FMT_RGB1555;
608c2ecf20Sopenharmony_ci			else
618c2ecf20Sopenharmony_ci				return PIX_FMT_BGR1555;
628c2ecf20Sopenharmony_ci		}
638c2ecf20Sopenharmony_ci	}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	/*
668c2ecf20Sopenharmony_ci	 * Check for 888/A888.
678c2ecf20Sopenharmony_ci	 */
688c2ecf20Sopenharmony_ci	if (var->bits_per_pixel <= 32 && var->red.length <= 8 &&
698c2ecf20Sopenharmony_ci	    var->green.length <= 8 && var->blue.length <= 8) {
708c2ecf20Sopenharmony_ci		if (var->bits_per_pixel == 24 && var->transp.length == 0) {
718c2ecf20Sopenharmony_ci			if (var->red.offset >= var->blue.offset)
728c2ecf20Sopenharmony_ci				return PIX_FMT_RGB888PACK;
738c2ecf20Sopenharmony_ci			else
748c2ecf20Sopenharmony_ci				return PIX_FMT_BGR888PACK;
758c2ecf20Sopenharmony_ci		}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci		if (var->bits_per_pixel == 32 && var->transp.length == 8) {
788c2ecf20Sopenharmony_ci			if (var->red.offset >= var->blue.offset)
798c2ecf20Sopenharmony_ci				return PIX_FMT_RGBA888;
808c2ecf20Sopenharmony_ci			else
818c2ecf20Sopenharmony_ci				return PIX_FMT_BGRA888;
828c2ecf20Sopenharmony_ci		} else {
838c2ecf20Sopenharmony_ci			if (var->red.offset >= var->blue.offset)
848c2ecf20Sopenharmony_ci				return PIX_FMT_RGB888UNPACK;
858c2ecf20Sopenharmony_ci			else
868c2ecf20Sopenharmony_ci				return PIX_FMT_BGR888UNPACK;
878c2ecf20Sopenharmony_ci		}
888c2ecf20Sopenharmony_ci	}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	return -EINVAL;
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic void set_pix_fmt(struct fb_var_screeninfo *var, int pix_fmt)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	switch (pix_fmt) {
968c2ecf20Sopenharmony_ci	case PIX_FMT_RGB565:
978c2ecf20Sopenharmony_ci		var->bits_per_pixel = 16;
988c2ecf20Sopenharmony_ci		var->red.offset = 11;    var->red.length = 5;
998c2ecf20Sopenharmony_ci		var->green.offset = 5;   var->green.length = 6;
1008c2ecf20Sopenharmony_ci		var->blue.offset = 0;    var->blue.length = 5;
1018c2ecf20Sopenharmony_ci		var->transp.offset = 0;  var->transp.length = 0;
1028c2ecf20Sopenharmony_ci		break;
1038c2ecf20Sopenharmony_ci	case PIX_FMT_BGR565:
1048c2ecf20Sopenharmony_ci		var->bits_per_pixel = 16;
1058c2ecf20Sopenharmony_ci		var->red.offset = 0;     var->red.length = 5;
1068c2ecf20Sopenharmony_ci		var->green.offset = 5;   var->green.length = 6;
1078c2ecf20Sopenharmony_ci		var->blue.offset = 11;   var->blue.length = 5;
1088c2ecf20Sopenharmony_ci		var->transp.offset = 0;  var->transp.length = 0;
1098c2ecf20Sopenharmony_ci		break;
1108c2ecf20Sopenharmony_ci	case PIX_FMT_RGB1555:
1118c2ecf20Sopenharmony_ci		var->bits_per_pixel = 16;
1128c2ecf20Sopenharmony_ci		var->red.offset = 10;    var->red.length = 5;
1138c2ecf20Sopenharmony_ci		var->green.offset = 5;   var->green.length = 5;
1148c2ecf20Sopenharmony_ci		var->blue.offset = 0;    var->blue.length = 5;
1158c2ecf20Sopenharmony_ci		var->transp.offset = 15; var->transp.length = 1;
1168c2ecf20Sopenharmony_ci		break;
1178c2ecf20Sopenharmony_ci	case PIX_FMT_BGR1555:
1188c2ecf20Sopenharmony_ci		var->bits_per_pixel = 16;
1198c2ecf20Sopenharmony_ci		var->red.offset = 0;     var->red.length = 5;
1208c2ecf20Sopenharmony_ci		var->green.offset = 5;   var->green.length = 5;
1218c2ecf20Sopenharmony_ci		var->blue.offset = 10;   var->blue.length = 5;
1228c2ecf20Sopenharmony_ci		var->transp.offset = 15; var->transp.length = 1;
1238c2ecf20Sopenharmony_ci		break;
1248c2ecf20Sopenharmony_ci	case PIX_FMT_RGB888PACK:
1258c2ecf20Sopenharmony_ci		var->bits_per_pixel = 24;
1268c2ecf20Sopenharmony_ci		var->red.offset = 16;    var->red.length = 8;
1278c2ecf20Sopenharmony_ci		var->green.offset = 8;   var->green.length = 8;
1288c2ecf20Sopenharmony_ci		var->blue.offset = 0;    var->blue.length = 8;
1298c2ecf20Sopenharmony_ci		var->transp.offset = 0;  var->transp.length = 0;
1308c2ecf20Sopenharmony_ci		break;
1318c2ecf20Sopenharmony_ci	case PIX_FMT_BGR888PACK:
1328c2ecf20Sopenharmony_ci		var->bits_per_pixel = 24;
1338c2ecf20Sopenharmony_ci		var->red.offset = 0;     var->red.length = 8;
1348c2ecf20Sopenharmony_ci		var->green.offset = 8;   var->green.length = 8;
1358c2ecf20Sopenharmony_ci		var->blue.offset = 16;   var->blue.length = 8;
1368c2ecf20Sopenharmony_ci		var->transp.offset = 0;  var->transp.length = 0;
1378c2ecf20Sopenharmony_ci		break;
1388c2ecf20Sopenharmony_ci	case PIX_FMT_RGBA888:
1398c2ecf20Sopenharmony_ci		var->bits_per_pixel = 32;
1408c2ecf20Sopenharmony_ci		var->red.offset = 16;    var->red.length = 8;
1418c2ecf20Sopenharmony_ci		var->green.offset = 8;   var->green.length = 8;
1428c2ecf20Sopenharmony_ci		var->blue.offset = 0;    var->blue.length = 8;
1438c2ecf20Sopenharmony_ci		var->transp.offset = 24; var->transp.length = 8;
1448c2ecf20Sopenharmony_ci		break;
1458c2ecf20Sopenharmony_ci	case PIX_FMT_BGRA888:
1468c2ecf20Sopenharmony_ci		var->bits_per_pixel = 32;
1478c2ecf20Sopenharmony_ci		var->red.offset = 0;     var->red.length = 8;
1488c2ecf20Sopenharmony_ci		var->green.offset = 8;   var->green.length = 8;
1498c2ecf20Sopenharmony_ci		var->blue.offset = 16;   var->blue.length = 8;
1508c2ecf20Sopenharmony_ci		var->transp.offset = 24; var->transp.length = 8;
1518c2ecf20Sopenharmony_ci		break;
1528c2ecf20Sopenharmony_ci	case PIX_FMT_PSEUDOCOLOR:
1538c2ecf20Sopenharmony_ci		var->bits_per_pixel = 8;
1548c2ecf20Sopenharmony_ci		var->red.offset = 0;     var->red.length = 8;
1558c2ecf20Sopenharmony_ci		var->green.offset = 0;   var->green.length = 8;
1568c2ecf20Sopenharmony_ci		var->blue.offset = 0;    var->blue.length = 8;
1578c2ecf20Sopenharmony_ci		var->transp.offset = 0;  var->transp.length = 0;
1588c2ecf20Sopenharmony_ci		break;
1598c2ecf20Sopenharmony_ci	}
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic void set_mode(struct pxa168fb_info *fbi, struct fb_var_screeninfo *var,
1638c2ecf20Sopenharmony_ci		     struct fb_videomode *mode, int pix_fmt, int ystretch)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci	struct fb_info *info = fbi->info;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	set_pix_fmt(var, pix_fmt);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	var->xres = mode->xres;
1708c2ecf20Sopenharmony_ci	var->yres = mode->yres;
1718c2ecf20Sopenharmony_ci	var->xres_virtual = max(var->xres, var->xres_virtual);
1728c2ecf20Sopenharmony_ci	if (ystretch)
1738c2ecf20Sopenharmony_ci		var->yres_virtual = info->fix.smem_len /
1748c2ecf20Sopenharmony_ci			(var->xres_virtual * (var->bits_per_pixel >> 3));
1758c2ecf20Sopenharmony_ci	else
1768c2ecf20Sopenharmony_ci		var->yres_virtual = max(var->yres, var->yres_virtual);
1778c2ecf20Sopenharmony_ci	var->grayscale = 0;
1788c2ecf20Sopenharmony_ci	var->accel_flags = FB_ACCEL_NONE;
1798c2ecf20Sopenharmony_ci	var->pixclock = mode->pixclock;
1808c2ecf20Sopenharmony_ci	var->left_margin = mode->left_margin;
1818c2ecf20Sopenharmony_ci	var->right_margin = mode->right_margin;
1828c2ecf20Sopenharmony_ci	var->upper_margin = mode->upper_margin;
1838c2ecf20Sopenharmony_ci	var->lower_margin = mode->lower_margin;
1848c2ecf20Sopenharmony_ci	var->hsync_len = mode->hsync_len;
1858c2ecf20Sopenharmony_ci	var->vsync_len = mode->vsync_len;
1868c2ecf20Sopenharmony_ci	var->sync = mode->sync;
1878c2ecf20Sopenharmony_ci	var->vmode = FB_VMODE_NONINTERLACED;
1888c2ecf20Sopenharmony_ci	var->rotate = FB_ROTATE_UR;
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic int pxa168fb_check_var(struct fb_var_screeninfo *var,
1928c2ecf20Sopenharmony_ci			      struct fb_info *info)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	struct pxa168fb_info *fbi = info->par;
1958c2ecf20Sopenharmony_ci	int pix_fmt;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	/*
1988c2ecf20Sopenharmony_ci	 * Determine which pixel format we're going to use.
1998c2ecf20Sopenharmony_ci	 */
2008c2ecf20Sopenharmony_ci	pix_fmt = determine_best_pix_fmt(var);
2018c2ecf20Sopenharmony_ci	if (pix_fmt < 0)
2028c2ecf20Sopenharmony_ci		return pix_fmt;
2038c2ecf20Sopenharmony_ci	set_pix_fmt(var, pix_fmt);
2048c2ecf20Sopenharmony_ci	fbi->pix_fmt = pix_fmt;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	/*
2078c2ecf20Sopenharmony_ci	 * Basic geometry sanity checks.
2088c2ecf20Sopenharmony_ci	 */
2098c2ecf20Sopenharmony_ci	if (var->xoffset + var->xres > var->xres_virtual)
2108c2ecf20Sopenharmony_ci		return -EINVAL;
2118c2ecf20Sopenharmony_ci	if (var->yoffset + var->yres > var->yres_virtual)
2128c2ecf20Sopenharmony_ci		return -EINVAL;
2138c2ecf20Sopenharmony_ci	if (var->xres + var->right_margin +
2148c2ecf20Sopenharmony_ci	    var->hsync_len + var->left_margin > 2048)
2158c2ecf20Sopenharmony_ci		return -EINVAL;
2168c2ecf20Sopenharmony_ci	if (var->yres + var->lower_margin +
2178c2ecf20Sopenharmony_ci	    var->vsync_len + var->upper_margin > 2048)
2188c2ecf20Sopenharmony_ci		return -EINVAL;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	/*
2218c2ecf20Sopenharmony_ci	 * Check size of framebuffer.
2228c2ecf20Sopenharmony_ci	 */
2238c2ecf20Sopenharmony_ci	if (var->xres_virtual * var->yres_virtual *
2248c2ecf20Sopenharmony_ci	    (var->bits_per_pixel >> 3) > info->fix.smem_len)
2258c2ecf20Sopenharmony_ci		return -EINVAL;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	return 0;
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci/*
2318c2ecf20Sopenharmony_ci * The hardware clock divider has an integer and a fractional
2328c2ecf20Sopenharmony_ci * stage:
2338c2ecf20Sopenharmony_ci *
2348c2ecf20Sopenharmony_ci *	clk2 = clk_in / integer_divider
2358c2ecf20Sopenharmony_ci *	clk_out = clk2 * (1 - (fractional_divider >> 12))
2368c2ecf20Sopenharmony_ci *
2378c2ecf20Sopenharmony_ci * Calculate integer and fractional divider for given clk_in
2388c2ecf20Sopenharmony_ci * and clk_out.
2398c2ecf20Sopenharmony_ci */
2408c2ecf20Sopenharmony_cistatic void set_clock_divider(struct pxa168fb_info *fbi,
2418c2ecf20Sopenharmony_ci			      const struct fb_videomode *m)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	int divider_int;
2448c2ecf20Sopenharmony_ci	int needed_pixclk;
2458c2ecf20Sopenharmony_ci	u64 div_result;
2468c2ecf20Sopenharmony_ci	u32 x = 0;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	/*
2498c2ecf20Sopenharmony_ci	 * Notice: The field pixclock is used by linux fb
2508c2ecf20Sopenharmony_ci	 * is in pixel second. E.g. struct fb_videomode &
2518c2ecf20Sopenharmony_ci	 * struct fb_var_screeninfo
2528c2ecf20Sopenharmony_ci	 */
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	/*
2558c2ecf20Sopenharmony_ci	 * Check input values.
2568c2ecf20Sopenharmony_ci	 */
2578c2ecf20Sopenharmony_ci	if (!m || !m->pixclock || !m->refresh) {
2588c2ecf20Sopenharmony_ci		dev_err(fbi->dev, "Input refresh or pixclock is wrong.\n");
2598c2ecf20Sopenharmony_ci		return;
2608c2ecf20Sopenharmony_ci	}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	/*
2638c2ecf20Sopenharmony_ci	 * Using PLL/AXI clock.
2648c2ecf20Sopenharmony_ci	 */
2658c2ecf20Sopenharmony_ci	x = 0x80000000;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	/*
2688c2ecf20Sopenharmony_ci	 * Calc divider according to refresh rate.
2698c2ecf20Sopenharmony_ci	 */
2708c2ecf20Sopenharmony_ci	div_result = 1000000000000ll;
2718c2ecf20Sopenharmony_ci	do_div(div_result, m->pixclock);
2728c2ecf20Sopenharmony_ci	needed_pixclk = (u32)div_result;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	divider_int = clk_get_rate(fbi->clk) / needed_pixclk;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	/* check whether divisor is too small. */
2778c2ecf20Sopenharmony_ci	if (divider_int < 2) {
2788c2ecf20Sopenharmony_ci		dev_warn(fbi->dev, "Warning: clock source is too slow. "
2798c2ecf20Sopenharmony_ci				"Try smaller resolution\n");
2808c2ecf20Sopenharmony_ci		divider_int = 2;
2818c2ecf20Sopenharmony_ci	}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	/*
2848c2ecf20Sopenharmony_ci	 * Set setting to reg.
2858c2ecf20Sopenharmony_ci	 */
2868c2ecf20Sopenharmony_ci	x |= divider_int;
2878c2ecf20Sopenharmony_ci	writel(x, fbi->reg_base + LCD_CFG_SCLK_DIV);
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistatic void set_dma_control0(struct pxa168fb_info *fbi)
2918c2ecf20Sopenharmony_ci{
2928c2ecf20Sopenharmony_ci	u32 x;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	/*
2958c2ecf20Sopenharmony_ci	 * Set bit to enable graphics DMA.
2968c2ecf20Sopenharmony_ci	 */
2978c2ecf20Sopenharmony_ci	x = readl(fbi->reg_base + LCD_SPU_DMA_CTRL0);
2988c2ecf20Sopenharmony_ci	x &= ~CFG_GRA_ENA_MASK;
2998c2ecf20Sopenharmony_ci	x |= fbi->active ? CFG_GRA_ENA(1) : CFG_GRA_ENA(0);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	/*
3028c2ecf20Sopenharmony_ci	 * If we are in a pseudo-color mode, we need to enable
3038c2ecf20Sopenharmony_ci	 * palette lookup.
3048c2ecf20Sopenharmony_ci	 */
3058c2ecf20Sopenharmony_ci	if (fbi->pix_fmt == PIX_FMT_PSEUDOCOLOR)
3068c2ecf20Sopenharmony_ci		x |= 0x10000000;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	/*
3098c2ecf20Sopenharmony_ci	 * Configure hardware pixel format.
3108c2ecf20Sopenharmony_ci	 */
3118c2ecf20Sopenharmony_ci	x &= ~(0xF << 16);
3128c2ecf20Sopenharmony_ci	x |= (fbi->pix_fmt >> 1) << 16;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	/*
3158c2ecf20Sopenharmony_ci	 * Check red and blue pixel swap.
3168c2ecf20Sopenharmony_ci	 * 1. source data swap
3178c2ecf20Sopenharmony_ci	 * 2. panel output data swap
3188c2ecf20Sopenharmony_ci	 */
3198c2ecf20Sopenharmony_ci	x &= ~(1 << 12);
3208c2ecf20Sopenharmony_ci	x |= ((fbi->pix_fmt & 1) ^ (fbi->panel_rbswap)) << 12;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	writel(x, fbi->reg_base + LCD_SPU_DMA_CTRL0);
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cistatic void set_dma_control1(struct pxa168fb_info *fbi, int sync)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	u32 x;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	/*
3308c2ecf20Sopenharmony_ci	 * Configure default bits: vsync triggers DMA, gated clock
3318c2ecf20Sopenharmony_ci	 * enable, power save enable, configure alpha registers to
3328c2ecf20Sopenharmony_ci	 * display 100% graphics, and set pixel command.
3338c2ecf20Sopenharmony_ci	 */
3348c2ecf20Sopenharmony_ci	x = readl(fbi->reg_base + LCD_SPU_DMA_CTRL1);
3358c2ecf20Sopenharmony_ci	x |= 0x2032ff81;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	/*
3388c2ecf20Sopenharmony_ci	 * We trigger DMA on the falling edge of vsync if vsync is
3398c2ecf20Sopenharmony_ci	 * active low, or on the rising edge if vsync is active high.
3408c2ecf20Sopenharmony_ci	 */
3418c2ecf20Sopenharmony_ci	if (!(sync & FB_SYNC_VERT_HIGH_ACT))
3428c2ecf20Sopenharmony_ci		x |= 0x08000000;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	writel(x, fbi->reg_base + LCD_SPU_DMA_CTRL1);
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cistatic void set_graphics_start(struct fb_info *info, int xoffset, int yoffset)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	struct pxa168fb_info *fbi = info->par;
3508c2ecf20Sopenharmony_ci	struct fb_var_screeninfo *var = &info->var;
3518c2ecf20Sopenharmony_ci	int pixel_offset;
3528c2ecf20Sopenharmony_ci	unsigned long addr;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	pixel_offset = (yoffset * var->xres_virtual) + xoffset;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	addr = fbi->fb_start_dma + (pixel_offset * (var->bits_per_pixel >> 3));
3578c2ecf20Sopenharmony_ci	writel(addr, fbi->reg_base + LCD_CFG_GRA_START_ADDR0);
3588c2ecf20Sopenharmony_ci}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic void set_dumb_panel_control(struct fb_info *info)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	struct pxa168fb_info *fbi = info->par;
3638c2ecf20Sopenharmony_ci	struct pxa168fb_mach_info *mi = dev_get_platdata(fbi->dev);
3648c2ecf20Sopenharmony_ci	u32 x;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	/*
3678c2ecf20Sopenharmony_ci	 * Preserve enable flag.
3688c2ecf20Sopenharmony_ci	 */
3698c2ecf20Sopenharmony_ci	x = readl(fbi->reg_base + LCD_SPU_DUMB_CTRL) & 0x00000001;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	x |= (fbi->is_blanked ? 0x7 : mi->dumb_mode) << 28;
3728c2ecf20Sopenharmony_ci	x |= mi->gpio_output_data << 20;
3738c2ecf20Sopenharmony_ci	x |= mi->gpio_output_mask << 12;
3748c2ecf20Sopenharmony_ci	x |= mi->panel_rgb_reverse_lanes ? 0x00000080 : 0;
3758c2ecf20Sopenharmony_ci	x |= mi->invert_composite_blank ? 0x00000040 : 0;
3768c2ecf20Sopenharmony_ci	x |= (info->var.sync & FB_SYNC_COMP_HIGH_ACT) ? 0x00000020 : 0;
3778c2ecf20Sopenharmony_ci	x |= mi->invert_pix_val_ena ? 0x00000010 : 0;
3788c2ecf20Sopenharmony_ci	x |= (info->var.sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 0x00000008;
3798c2ecf20Sopenharmony_ci	x |= (info->var.sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 0x00000004;
3808c2ecf20Sopenharmony_ci	x |= mi->invert_pixclock ? 0x00000002 : 0;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	writel(x, fbi->reg_base + LCD_SPU_DUMB_CTRL);
3838c2ecf20Sopenharmony_ci}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_cistatic void set_dumb_screen_dimensions(struct fb_info *info)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	struct pxa168fb_info *fbi = info->par;
3888c2ecf20Sopenharmony_ci	struct fb_var_screeninfo *v = &info->var;
3898c2ecf20Sopenharmony_ci	int x;
3908c2ecf20Sopenharmony_ci	int y;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	x = v->xres + v->right_margin + v->hsync_len + v->left_margin;
3938c2ecf20Sopenharmony_ci	y = v->yres + v->lower_margin + v->vsync_len + v->upper_margin;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	writel((y << 16) | x, fbi->reg_base + LCD_SPUT_V_H_TOTAL);
3968c2ecf20Sopenharmony_ci}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_cistatic int pxa168fb_set_par(struct fb_info *info)
3998c2ecf20Sopenharmony_ci{
4008c2ecf20Sopenharmony_ci	struct pxa168fb_info *fbi = info->par;
4018c2ecf20Sopenharmony_ci	struct fb_var_screeninfo *var = &info->var;
4028c2ecf20Sopenharmony_ci	struct fb_videomode mode;
4038c2ecf20Sopenharmony_ci	u32 x;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	/*
4068c2ecf20Sopenharmony_ci	 * Set additional mode info.
4078c2ecf20Sopenharmony_ci	 */
4088c2ecf20Sopenharmony_ci	if (fbi->pix_fmt == PIX_FMT_PSEUDOCOLOR)
4098c2ecf20Sopenharmony_ci		info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
4108c2ecf20Sopenharmony_ci	else
4118c2ecf20Sopenharmony_ci		info->fix.visual = FB_VISUAL_TRUECOLOR;
4128c2ecf20Sopenharmony_ci	info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8;
4138c2ecf20Sopenharmony_ci	info->fix.ypanstep = var->yres;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	/*
4168c2ecf20Sopenharmony_ci	 * Disable panel output while we setup the display.
4178c2ecf20Sopenharmony_ci	 */
4188c2ecf20Sopenharmony_ci	x = readl(fbi->reg_base + LCD_SPU_DUMB_CTRL);
4198c2ecf20Sopenharmony_ci	writel(x & ~1, fbi->reg_base + LCD_SPU_DUMB_CTRL);
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	/*
4228c2ecf20Sopenharmony_ci	 * Configure global panel parameters.
4238c2ecf20Sopenharmony_ci	 */
4248c2ecf20Sopenharmony_ci	writel((var->yres << 16) | var->xres,
4258c2ecf20Sopenharmony_ci		fbi->reg_base + LCD_SPU_V_H_ACTIVE);
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	/*
4288c2ecf20Sopenharmony_ci	 * convet var to video mode
4298c2ecf20Sopenharmony_ci	 */
4308c2ecf20Sopenharmony_ci	fb_var_to_videomode(&mode, &info->var);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	/* Calculate clock divisor. */
4338c2ecf20Sopenharmony_ci	set_clock_divider(fbi, &mode);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	/* Configure dma ctrl regs. */
4368c2ecf20Sopenharmony_ci	set_dma_control0(fbi);
4378c2ecf20Sopenharmony_ci	set_dma_control1(fbi, info->var.sync);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	/*
4408c2ecf20Sopenharmony_ci	 * Configure graphics DMA parameters.
4418c2ecf20Sopenharmony_ci	 */
4428c2ecf20Sopenharmony_ci	x = readl(fbi->reg_base + LCD_CFG_GRA_PITCH);
4438c2ecf20Sopenharmony_ci	x = (x & ~0xFFFF) | ((var->xres_virtual * var->bits_per_pixel) >> 3);
4448c2ecf20Sopenharmony_ci	writel(x, fbi->reg_base + LCD_CFG_GRA_PITCH);
4458c2ecf20Sopenharmony_ci	writel((var->yres << 16) | var->xres,
4468c2ecf20Sopenharmony_ci		fbi->reg_base + LCD_SPU_GRA_HPXL_VLN);
4478c2ecf20Sopenharmony_ci	writel((var->yres << 16) | var->xres,
4488c2ecf20Sopenharmony_ci		fbi->reg_base + LCD_SPU_GZM_HPXL_VLN);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	/*
4518c2ecf20Sopenharmony_ci	 * Configure dumb panel ctrl regs & timings.
4528c2ecf20Sopenharmony_ci	 */
4538c2ecf20Sopenharmony_ci	set_dumb_panel_control(info);
4548c2ecf20Sopenharmony_ci	set_dumb_screen_dimensions(info);
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	writel((var->left_margin << 16) | var->right_margin,
4578c2ecf20Sopenharmony_ci			fbi->reg_base + LCD_SPU_H_PORCH);
4588c2ecf20Sopenharmony_ci	writel((var->upper_margin << 16) | var->lower_margin,
4598c2ecf20Sopenharmony_ci			fbi->reg_base + LCD_SPU_V_PORCH);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	/*
4628c2ecf20Sopenharmony_ci	 * Re-enable panel output.
4638c2ecf20Sopenharmony_ci	 */
4648c2ecf20Sopenharmony_ci	x = readl(fbi->reg_base + LCD_SPU_DUMB_CTRL);
4658c2ecf20Sopenharmony_ci	writel(x | 1, fbi->reg_base + LCD_SPU_DUMB_CTRL);
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	return 0;
4688c2ecf20Sopenharmony_ci}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_cistatic unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
4718c2ecf20Sopenharmony_ci{
4728c2ecf20Sopenharmony_ci	return ((chan & 0xffff) >> (16 - bf->length)) << bf->offset;
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_cistatic u32 to_rgb(u16 red, u16 green, u16 blue)
4768c2ecf20Sopenharmony_ci{
4778c2ecf20Sopenharmony_ci	red >>= 8;
4788c2ecf20Sopenharmony_ci	green >>= 8;
4798c2ecf20Sopenharmony_ci	blue >>= 8;
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	return (red << 16) | (green << 8) | blue;
4828c2ecf20Sopenharmony_ci}
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_cistatic int
4858c2ecf20Sopenharmony_cipxa168fb_setcolreg(unsigned int regno, unsigned int red, unsigned int green,
4868c2ecf20Sopenharmony_ci		 unsigned int blue, unsigned int trans, struct fb_info *info)
4878c2ecf20Sopenharmony_ci{
4888c2ecf20Sopenharmony_ci	struct pxa168fb_info *fbi = info->par;
4898c2ecf20Sopenharmony_ci	u32 val;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	if (info->var.grayscale)
4928c2ecf20Sopenharmony_ci		red = green = blue = (19595 * red + 38470 * green +
4938c2ecf20Sopenharmony_ci					7471 * blue) >> 16;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 16) {
4968c2ecf20Sopenharmony_ci		val =  chan_to_field(red,   &info->var.red);
4978c2ecf20Sopenharmony_ci		val |= chan_to_field(green, &info->var.green);
4988c2ecf20Sopenharmony_ci		val |= chan_to_field(blue , &info->var.blue);
4998c2ecf20Sopenharmony_ci		fbi->pseudo_palette[regno] = val;
5008c2ecf20Sopenharmony_ci	}
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR && regno < 256) {
5038c2ecf20Sopenharmony_ci		val = to_rgb(red, green, blue);
5048c2ecf20Sopenharmony_ci		writel(val, fbi->reg_base + LCD_SPU_SRAM_WRDAT);
5058c2ecf20Sopenharmony_ci		writel(0x8300 | regno, fbi->reg_base + LCD_SPU_SRAM_CTRL);
5068c2ecf20Sopenharmony_ci	}
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	return 0;
5098c2ecf20Sopenharmony_ci}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_cistatic int pxa168fb_blank(int blank, struct fb_info *info)
5128c2ecf20Sopenharmony_ci{
5138c2ecf20Sopenharmony_ci	struct pxa168fb_info *fbi = info->par;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	fbi->is_blanked = (blank == FB_BLANK_UNBLANK) ? 0 : 1;
5168c2ecf20Sopenharmony_ci	set_dumb_panel_control(info);
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	return 0;
5198c2ecf20Sopenharmony_ci}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_cistatic int pxa168fb_pan_display(struct fb_var_screeninfo *var,
5228c2ecf20Sopenharmony_ci				struct fb_info *info)
5238c2ecf20Sopenharmony_ci{
5248c2ecf20Sopenharmony_ci	set_graphics_start(info, var->xoffset, var->yoffset);
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	return 0;
5278c2ecf20Sopenharmony_ci}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_cistatic irqreturn_t pxa168fb_handle_irq(int irq, void *dev_id)
5308c2ecf20Sopenharmony_ci{
5318c2ecf20Sopenharmony_ci	struct pxa168fb_info *fbi = dev_id;
5328c2ecf20Sopenharmony_ci	u32 isr = readl(fbi->reg_base + SPU_IRQ_ISR);
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	if ((isr & GRA_FRAME_IRQ0_ENA_MASK)) {
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci		writel(isr & (~GRA_FRAME_IRQ0_ENA_MASK),
5378c2ecf20Sopenharmony_ci			fbi->reg_base + SPU_IRQ_ISR);
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
5408c2ecf20Sopenharmony_ci	}
5418c2ecf20Sopenharmony_ci	return IRQ_NONE;
5428c2ecf20Sopenharmony_ci}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_cistatic const struct fb_ops pxa168fb_ops = {
5458c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
5468c2ecf20Sopenharmony_ci	.fb_check_var	= pxa168fb_check_var,
5478c2ecf20Sopenharmony_ci	.fb_set_par	= pxa168fb_set_par,
5488c2ecf20Sopenharmony_ci	.fb_setcolreg	= pxa168fb_setcolreg,
5498c2ecf20Sopenharmony_ci	.fb_blank	= pxa168fb_blank,
5508c2ecf20Sopenharmony_ci	.fb_pan_display	= pxa168fb_pan_display,
5518c2ecf20Sopenharmony_ci	.fb_fillrect	= cfb_fillrect,
5528c2ecf20Sopenharmony_ci	.fb_copyarea	= cfb_copyarea,
5538c2ecf20Sopenharmony_ci	.fb_imageblit	= cfb_imageblit,
5548c2ecf20Sopenharmony_ci};
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_cistatic void pxa168fb_init_mode(struct fb_info *info,
5578c2ecf20Sopenharmony_ci			      struct pxa168fb_mach_info *mi)
5588c2ecf20Sopenharmony_ci{
5598c2ecf20Sopenharmony_ci	struct pxa168fb_info *fbi = info->par;
5608c2ecf20Sopenharmony_ci	struct fb_var_screeninfo *var = &info->var;
5618c2ecf20Sopenharmony_ci	u32 total_w, total_h, refresh;
5628c2ecf20Sopenharmony_ci	u64 div_result;
5638c2ecf20Sopenharmony_ci	const struct fb_videomode *m;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	/*
5668c2ecf20Sopenharmony_ci	 * Set default value
5678c2ecf20Sopenharmony_ci	 */
5688c2ecf20Sopenharmony_ci	refresh = DEFAULT_REFRESH;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	/* try to find best video mode. */
5718c2ecf20Sopenharmony_ci	m = fb_find_best_mode(&info->var, &info->modelist);
5728c2ecf20Sopenharmony_ci	if (m)
5738c2ecf20Sopenharmony_ci		fb_videomode_to_var(&info->var, m);
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	/* Init settings. */
5768c2ecf20Sopenharmony_ci	var->xres_virtual = var->xres;
5778c2ecf20Sopenharmony_ci	var->yres_virtual = info->fix.smem_len /
5788c2ecf20Sopenharmony_ci		(var->xres_virtual * (var->bits_per_pixel >> 3));
5798c2ecf20Sopenharmony_ci	dev_dbg(fbi->dev, "pxa168fb: find best mode: res = %dx%d\n",
5808c2ecf20Sopenharmony_ci				var->xres, var->yres);
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	/* correct pixclock. */
5838c2ecf20Sopenharmony_ci	total_w = var->xres + var->left_margin + var->right_margin +
5848c2ecf20Sopenharmony_ci		  var->hsync_len;
5858c2ecf20Sopenharmony_ci	total_h = var->yres + var->upper_margin + var->lower_margin +
5868c2ecf20Sopenharmony_ci		  var->vsync_len;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	div_result = 1000000000000ll;
5898c2ecf20Sopenharmony_ci	do_div(div_result, total_w * total_h * refresh);
5908c2ecf20Sopenharmony_ci	var->pixclock = (u32)div_result;
5918c2ecf20Sopenharmony_ci}
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_cistatic int pxa168fb_probe(struct platform_device *pdev)
5948c2ecf20Sopenharmony_ci{
5958c2ecf20Sopenharmony_ci	struct pxa168fb_mach_info *mi;
5968c2ecf20Sopenharmony_ci	struct fb_info *info = 0;
5978c2ecf20Sopenharmony_ci	struct pxa168fb_info *fbi = 0;
5988c2ecf20Sopenharmony_ci	struct resource *res;
5998c2ecf20Sopenharmony_ci	struct clk *clk;
6008c2ecf20Sopenharmony_ci	int irq, ret;
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	mi = dev_get_platdata(&pdev->dev);
6038c2ecf20Sopenharmony_ci	if (mi == NULL) {
6048c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "no platform data defined\n");
6058c2ecf20Sopenharmony_ci		return -EINVAL;
6068c2ecf20Sopenharmony_ci	}
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	clk = devm_clk_get(&pdev->dev, "LCDCLK");
6098c2ecf20Sopenharmony_ci	if (IS_ERR(clk)) {
6108c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "unable to get LCDCLK");
6118c2ecf20Sopenharmony_ci		return PTR_ERR(clk);
6128c2ecf20Sopenharmony_ci	}
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
6158c2ecf20Sopenharmony_ci	if (res == NULL) {
6168c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "no IO memory defined\n");
6178c2ecf20Sopenharmony_ci		return -ENOENT;
6188c2ecf20Sopenharmony_ci	}
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
6218c2ecf20Sopenharmony_ci	if (irq < 0) {
6228c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "no IRQ defined\n");
6238c2ecf20Sopenharmony_ci		return -ENOENT;
6248c2ecf20Sopenharmony_ci	}
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	info = framebuffer_alloc(sizeof(struct pxa168fb_info), &pdev->dev);
6278c2ecf20Sopenharmony_ci	if (info == NULL) {
6288c2ecf20Sopenharmony_ci		return -ENOMEM;
6298c2ecf20Sopenharmony_ci	}
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	/* Initialize private data */
6328c2ecf20Sopenharmony_ci	fbi = info->par;
6338c2ecf20Sopenharmony_ci	fbi->info = info;
6348c2ecf20Sopenharmony_ci	fbi->clk = clk;
6358c2ecf20Sopenharmony_ci	fbi->dev = info->dev = &pdev->dev;
6368c2ecf20Sopenharmony_ci	fbi->panel_rbswap = mi->panel_rbswap;
6378c2ecf20Sopenharmony_ci	fbi->is_blanked = 0;
6388c2ecf20Sopenharmony_ci	fbi->active = mi->active;
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	/*
6418c2ecf20Sopenharmony_ci	 * Initialise static fb parameters.
6428c2ecf20Sopenharmony_ci	 */
6438c2ecf20Sopenharmony_ci	info->flags = FBINFO_DEFAULT | FBINFO_PARTIAL_PAN_OK |
6448c2ecf20Sopenharmony_ci		      FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN;
6458c2ecf20Sopenharmony_ci	info->node = -1;
6468c2ecf20Sopenharmony_ci	strlcpy(info->fix.id, mi->id, 16);
6478c2ecf20Sopenharmony_ci	info->fix.type = FB_TYPE_PACKED_PIXELS;
6488c2ecf20Sopenharmony_ci	info->fix.type_aux = 0;
6498c2ecf20Sopenharmony_ci	info->fix.xpanstep = 0;
6508c2ecf20Sopenharmony_ci	info->fix.ypanstep = 0;
6518c2ecf20Sopenharmony_ci	info->fix.ywrapstep = 0;
6528c2ecf20Sopenharmony_ci	info->fix.mmio_start = res->start;
6538c2ecf20Sopenharmony_ci	info->fix.mmio_len = resource_size(res);
6548c2ecf20Sopenharmony_ci	info->fix.accel = FB_ACCEL_NONE;
6558c2ecf20Sopenharmony_ci	info->fbops = &pxa168fb_ops;
6568c2ecf20Sopenharmony_ci	info->pseudo_palette = fbi->pseudo_palette;
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	/*
6598c2ecf20Sopenharmony_ci	 * Map LCD controller registers.
6608c2ecf20Sopenharmony_ci	 */
6618c2ecf20Sopenharmony_ci	fbi->reg_base = devm_ioremap(&pdev->dev, res->start,
6628c2ecf20Sopenharmony_ci					     resource_size(res));
6638c2ecf20Sopenharmony_ci	if (fbi->reg_base == NULL) {
6648c2ecf20Sopenharmony_ci		ret = -ENOMEM;
6658c2ecf20Sopenharmony_ci		goto failed_free_info;
6668c2ecf20Sopenharmony_ci	}
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	/*
6698c2ecf20Sopenharmony_ci	 * Allocate framebuffer memory.
6708c2ecf20Sopenharmony_ci	 */
6718c2ecf20Sopenharmony_ci	info->fix.smem_len = PAGE_ALIGN(DEFAULT_FB_SIZE);
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	info->screen_base = dma_alloc_wc(fbi->dev, info->fix.smem_len,
6748c2ecf20Sopenharmony_ci					 &fbi->fb_start_dma, GFP_KERNEL);
6758c2ecf20Sopenharmony_ci	if (info->screen_base == NULL) {
6768c2ecf20Sopenharmony_ci		ret = -ENOMEM;
6778c2ecf20Sopenharmony_ci		goto failed_free_info;
6788c2ecf20Sopenharmony_ci	}
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	info->fix.smem_start = (unsigned long)fbi->fb_start_dma;
6818c2ecf20Sopenharmony_ci	set_graphics_start(info, 0, 0);
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	/*
6848c2ecf20Sopenharmony_ci	 * Set video mode according to platform data.
6858c2ecf20Sopenharmony_ci	 */
6868c2ecf20Sopenharmony_ci	set_mode(fbi, &info->var, mi->modes, mi->pix_fmt, 1);
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	fb_videomode_to_modelist(mi->modes, mi->num_modes, &info->modelist);
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	/*
6918c2ecf20Sopenharmony_ci	 * init video mode data.
6928c2ecf20Sopenharmony_ci	 */
6938c2ecf20Sopenharmony_ci	pxa168fb_init_mode(info, mi);
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	/*
6968c2ecf20Sopenharmony_ci	 * Fill in sane defaults.
6978c2ecf20Sopenharmony_ci	 */
6988c2ecf20Sopenharmony_ci	ret = pxa168fb_check_var(&info->var, info);
6998c2ecf20Sopenharmony_ci	if (ret)
7008c2ecf20Sopenharmony_ci		goto failed_free_fbmem;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	/*
7038c2ecf20Sopenharmony_ci	 * enable controller clock
7048c2ecf20Sopenharmony_ci	 */
7058c2ecf20Sopenharmony_ci	clk_prepare_enable(fbi->clk);
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	pxa168fb_set_par(info);
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	/*
7108c2ecf20Sopenharmony_ci	 * Configure default register values.
7118c2ecf20Sopenharmony_ci	 */
7128c2ecf20Sopenharmony_ci	writel(0, fbi->reg_base + LCD_SPU_BLANKCOLOR);
7138c2ecf20Sopenharmony_ci	writel(mi->io_pin_allocation_mode, fbi->reg_base + SPU_IOPAD_CONTROL);
7148c2ecf20Sopenharmony_ci	writel(0, fbi->reg_base + LCD_CFG_GRA_START_ADDR1);
7158c2ecf20Sopenharmony_ci	writel(0, fbi->reg_base + LCD_SPU_GRA_OVSA_HPXL_VLN);
7168c2ecf20Sopenharmony_ci	writel(0, fbi->reg_base + LCD_SPU_SRAM_PARA0);
7178c2ecf20Sopenharmony_ci	writel(CFG_CSB_256x32(0x1)|CFG_CSB_256x24(0x1)|CFG_CSB_256x8(0x1),
7188c2ecf20Sopenharmony_ci		fbi->reg_base + LCD_SPU_SRAM_PARA1);
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	/*
7218c2ecf20Sopenharmony_ci	 * Allocate color map.
7228c2ecf20Sopenharmony_ci	 */
7238c2ecf20Sopenharmony_ci	if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
7248c2ecf20Sopenharmony_ci		ret = -ENOMEM;
7258c2ecf20Sopenharmony_ci		goto failed_free_clk;
7268c2ecf20Sopenharmony_ci	}
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	/*
7298c2ecf20Sopenharmony_ci	 * Register irq handler.
7308c2ecf20Sopenharmony_ci	 */
7318c2ecf20Sopenharmony_ci	ret = devm_request_irq(&pdev->dev, irq, pxa168fb_handle_irq,
7328c2ecf20Sopenharmony_ci			       IRQF_SHARED, info->fix.id, fbi);
7338c2ecf20Sopenharmony_ci	if (ret < 0) {
7348c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "unable to request IRQ\n");
7358c2ecf20Sopenharmony_ci		ret = -ENXIO;
7368c2ecf20Sopenharmony_ci		goto failed_free_cmap;
7378c2ecf20Sopenharmony_ci	}
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	/*
7408c2ecf20Sopenharmony_ci	 * Enable GFX interrupt
7418c2ecf20Sopenharmony_ci	 */
7428c2ecf20Sopenharmony_ci	writel(GRA_FRAME_IRQ0_ENA(0x1), fbi->reg_base + SPU_IRQ_ENA);
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	/*
7458c2ecf20Sopenharmony_ci	 * Register framebuffer.
7468c2ecf20Sopenharmony_ci	 */
7478c2ecf20Sopenharmony_ci	ret = register_framebuffer(info);
7488c2ecf20Sopenharmony_ci	if (ret < 0) {
7498c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to register pxa168-fb: %d\n", ret);
7508c2ecf20Sopenharmony_ci		ret = -ENXIO;
7518c2ecf20Sopenharmony_ci		goto failed_free_cmap;
7528c2ecf20Sopenharmony_ci	}
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, fbi);
7558c2ecf20Sopenharmony_ci	return 0;
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_cifailed_free_cmap:
7588c2ecf20Sopenharmony_ci	fb_dealloc_cmap(&info->cmap);
7598c2ecf20Sopenharmony_cifailed_free_clk:
7608c2ecf20Sopenharmony_ci	clk_disable_unprepare(fbi->clk);
7618c2ecf20Sopenharmony_cifailed_free_fbmem:
7628c2ecf20Sopenharmony_ci	dma_free_wc(fbi->dev, info->fix.smem_len,
7638c2ecf20Sopenharmony_ci		    info->screen_base, fbi->fb_start_dma);
7648c2ecf20Sopenharmony_cifailed_free_info:
7658c2ecf20Sopenharmony_ci	framebuffer_release(info);
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	dev_err(&pdev->dev, "frame buffer device init failed with %d\n", ret);
7688c2ecf20Sopenharmony_ci	return ret;
7698c2ecf20Sopenharmony_ci}
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_cistatic int pxa168fb_remove(struct platform_device *pdev)
7728c2ecf20Sopenharmony_ci{
7738c2ecf20Sopenharmony_ci	struct pxa168fb_info *fbi = platform_get_drvdata(pdev);
7748c2ecf20Sopenharmony_ci	struct fb_info *info;
7758c2ecf20Sopenharmony_ci	unsigned int data;
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	if (!fbi)
7788c2ecf20Sopenharmony_ci		return 0;
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	/* disable DMA transfer */
7818c2ecf20Sopenharmony_ci	data = readl(fbi->reg_base + LCD_SPU_DMA_CTRL0);
7828c2ecf20Sopenharmony_ci	data &= ~CFG_GRA_ENA_MASK;
7838c2ecf20Sopenharmony_ci	writel(data, fbi->reg_base + LCD_SPU_DMA_CTRL0);
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	info = fbi->info;
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	unregister_framebuffer(info);
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	writel(GRA_FRAME_IRQ0_ENA(0x0), fbi->reg_base + SPU_IRQ_ENA);
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	if (info->cmap.len)
7928c2ecf20Sopenharmony_ci		fb_dealloc_cmap(&info->cmap);
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	dma_free_wc(fbi->dev, info->fix.smem_len,
7958c2ecf20Sopenharmony_ci		    info->screen_base, info->fix.smem_start);
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	clk_disable_unprepare(fbi->clk);
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	framebuffer_release(info);
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	return 0;
8028c2ecf20Sopenharmony_ci}
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_cistatic struct platform_driver pxa168fb_driver = {
8058c2ecf20Sopenharmony_ci	.driver		= {
8068c2ecf20Sopenharmony_ci		.name	= "pxa168-fb",
8078c2ecf20Sopenharmony_ci	},
8088c2ecf20Sopenharmony_ci	.probe		= pxa168fb_probe,
8098c2ecf20Sopenharmony_ci	.remove		= pxa168fb_remove,
8108c2ecf20Sopenharmony_ci};
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_cimodule_platform_driver(pxa168fb_driver);
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ciMODULE_AUTHOR("Lennert Buytenhek <buytenh@marvell.com> "
8158c2ecf20Sopenharmony_ci	      "Green Wan <gwan@marvell.com>");
8168c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Framebuffer driver for PXA168/910");
8178c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
818