18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * BRIEF MODULE DESCRIPTION
38c2ecf20Sopenharmony_ci *	Au1100 LCD Driver.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Rewritten for 2.6 by Embedded Alley Solutions
68c2ecf20Sopenharmony_ci * 	<source@embeddedalley.com>, based on submissions by
78c2ecf20Sopenharmony_ci *  	Karl Lessard <klessard@sunrisetelecom.com>
88c2ecf20Sopenharmony_ci *  	<c.pellegrin@exadron.com>
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * PM support added by Rodolfo Giometti <giometti@linux.it>
118c2ecf20Sopenharmony_ci * Cursor enable/disable by Rodolfo Giometti <giometti@linux.it>
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * Copyright 2002 MontaVista Software
148c2ecf20Sopenharmony_ci * Author: MontaVista Software, Inc.
158c2ecf20Sopenharmony_ci *		ppopov@mvista.com or source@mvista.com
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * Copyright 2002 Alchemy Semiconductor
188c2ecf20Sopenharmony_ci * Author: Alchemy Semiconductor
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci * Based on:
218c2ecf20Sopenharmony_ci * linux/drivers/video/skeletonfb.c -- Skeleton for a frame buffer device
228c2ecf20Sopenharmony_ci *  Created 28 Dec 1997 by Geert Uytterhoeven
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci *  This program is free software; you can redistribute	 it and/or modify it
258c2ecf20Sopenharmony_ci *  under  the terms of	 the GNU General  Public License as published by the
268c2ecf20Sopenharmony_ci *  Free Software Foundation;  either version 2 of the	License, or (at your
278c2ecf20Sopenharmony_ci *  option) any later version.
288c2ecf20Sopenharmony_ci *
298c2ecf20Sopenharmony_ci *  THIS  SOFTWARE  IS PROVIDED	  ``AS	IS'' AND   ANY	EXPRESS OR IMPLIED
308c2ecf20Sopenharmony_ci *  WARRANTIES,	  INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
318c2ecf20Sopenharmony_ci *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
328c2ecf20Sopenharmony_ci *  NO	EVENT  SHALL   THE AUTHOR  BE	 LIABLE FOR ANY	  DIRECT, INDIRECT,
338c2ecf20Sopenharmony_ci *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
348c2ecf20Sopenharmony_ci *  NOT LIMITED	  TO, PROCUREMENT OF  SUBSTITUTE GOODS	OR SERVICES; LOSS OF
358c2ecf20Sopenharmony_ci *  USE, DATA,	OR PROFITS; OR	BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
368c2ecf20Sopenharmony_ci *  ANY THEORY OF LIABILITY, WHETHER IN	 CONTRACT, STRICT LIABILITY, OR TORT
378c2ecf20Sopenharmony_ci *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
388c2ecf20Sopenharmony_ci *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
398c2ecf20Sopenharmony_ci *
408c2ecf20Sopenharmony_ci *  You should have received a copy of the  GNU General Public License along
418c2ecf20Sopenharmony_ci *  with this program; if not, write  to the Free Software Foundation, Inc.,
428c2ecf20Sopenharmony_ci *  675 Mass Ave, Cambridge, MA 02139, USA.
438c2ecf20Sopenharmony_ci */
448c2ecf20Sopenharmony_ci#include <linux/clk.h>
458c2ecf20Sopenharmony_ci#include <linux/module.h>
468c2ecf20Sopenharmony_ci#include <linux/kernel.h>
478c2ecf20Sopenharmony_ci#include <linux/errno.h>
488c2ecf20Sopenharmony_ci#include <linux/string.h>
498c2ecf20Sopenharmony_ci#include <linux/mm.h>
508c2ecf20Sopenharmony_ci#include <linux/fb.h>
518c2ecf20Sopenharmony_ci#include <linux/init.h>
528c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
538c2ecf20Sopenharmony_ci#include <linux/ctype.h>
548c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
558c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
568c2ecf20Sopenharmony_ci#include <linux/slab.h>
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci#include <asm/mach-au1x00/au1000.h>
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci#define DEBUG 0
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci#include "au1100fb.h"
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci#define DRIVER_NAME "au1100fb"
658c2ecf20Sopenharmony_ci#define DRIVER_DESC "LCD controller driver for AU1100 processors"
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci#define to_au1100fb_device(_info) \
688c2ecf20Sopenharmony_ci	  (_info ? container_of(_info, struct au1100fb_device, info) : NULL);
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci/* Bitfields format supported by the controller. Note that the order of formats
718c2ecf20Sopenharmony_ci * SHOULD be the same as in the LCD_CONTROL_SBPPF field, so we can retrieve the
728c2ecf20Sopenharmony_ci * right pixel format by doing rgb_bitfields[LCD_CONTROL_SBPPF_XXX >> LCD_CONTROL_SBPPF]
738c2ecf20Sopenharmony_ci */
748c2ecf20Sopenharmony_cistruct fb_bitfield rgb_bitfields[][4] =
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci  	/*     Red, 	   Green, 	 Blue, 	     Transp   */
778c2ecf20Sopenharmony_ci	{ { 10, 6, 0 }, { 5, 5, 0 }, { 0, 5, 0 }, { 0, 0, 0 } },
788c2ecf20Sopenharmony_ci	{ { 11, 5, 0 }, { 5, 6, 0 }, { 0, 5, 0 }, { 0, 0, 0 } },
798c2ecf20Sopenharmony_ci	{ { 11, 5, 0 }, { 6, 5, 0 }, { 0, 6, 0 }, { 0, 0, 0 } },
808c2ecf20Sopenharmony_ci	{ { 10, 5, 0 }, { 5, 5, 0 }, { 0, 5, 0 }, { 15, 1, 0 } },
818c2ecf20Sopenharmony_ci	{ { 11, 5, 0 }, { 6, 5, 0 }, { 1, 5, 0 }, { 0, 1, 0 } },
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	/* The last is used to describe 12bpp format */
848c2ecf20Sopenharmony_ci	{ { 8, 4, 0 },  { 4, 4, 0 }, { 0, 4, 0 }, { 0, 0, 0 } },
858c2ecf20Sopenharmony_ci};
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic struct fb_fix_screeninfo au1100fb_fix = {
888c2ecf20Sopenharmony_ci	.id		= "AU1100 FB",
898c2ecf20Sopenharmony_ci	.xpanstep 	= 1,
908c2ecf20Sopenharmony_ci	.ypanstep 	= 1,
918c2ecf20Sopenharmony_ci	.type		= FB_TYPE_PACKED_PIXELS,
928c2ecf20Sopenharmony_ci	.accel		= FB_ACCEL_NONE,
938c2ecf20Sopenharmony_ci};
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic struct fb_var_screeninfo au1100fb_var = {
968c2ecf20Sopenharmony_ci	.activate	= FB_ACTIVATE_NOW,
978c2ecf20Sopenharmony_ci	.height		= -1,
988c2ecf20Sopenharmony_ci	.width		= -1,
998c2ecf20Sopenharmony_ci	.vmode		= FB_VMODE_NONINTERLACED,
1008c2ecf20Sopenharmony_ci};
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci/* fb_blank
1038c2ecf20Sopenharmony_ci * Blank the screen. Depending on the mode, the screen will be
1048c2ecf20Sopenharmony_ci * activated with the backlight color, or desactivated
1058c2ecf20Sopenharmony_ci */
1068c2ecf20Sopenharmony_cistatic int au1100fb_fb_blank(int blank_mode, struct fb_info *fbi)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	struct au1100fb_device *fbdev = to_au1100fb_device(fbi);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	print_dbg("fb_blank %d %p", blank_mode, fbi);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	switch (blank_mode) {
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	case VESA_NO_BLANKING:
1158c2ecf20Sopenharmony_ci		/* Turn on panel */
1168c2ecf20Sopenharmony_ci		fbdev->regs->lcd_control |= LCD_CONTROL_GO;
1178c2ecf20Sopenharmony_ci		wmb(); /* drain writebuffer */
1188c2ecf20Sopenharmony_ci		break;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	case VESA_VSYNC_SUSPEND:
1218c2ecf20Sopenharmony_ci	case VESA_HSYNC_SUSPEND:
1228c2ecf20Sopenharmony_ci	case VESA_POWERDOWN:
1238c2ecf20Sopenharmony_ci		/* Turn off panel */
1248c2ecf20Sopenharmony_ci		fbdev->regs->lcd_control &= ~LCD_CONTROL_GO;
1258c2ecf20Sopenharmony_ci		wmb(); /* drain writebuffer */
1268c2ecf20Sopenharmony_ci		break;
1278c2ecf20Sopenharmony_ci	default:
1288c2ecf20Sopenharmony_ci		break;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci	return 0;
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci/*
1358c2ecf20Sopenharmony_ci * Set hardware with var settings. This will enable the controller with a specific
1368c2ecf20Sopenharmony_ci * mode, normally validated with the fb_check_var method
1378c2ecf20Sopenharmony_ci	 */
1388c2ecf20Sopenharmony_ciint au1100fb_setmode(struct au1100fb_device *fbdev)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	struct fb_info *info = &fbdev->info;
1418c2ecf20Sopenharmony_ci	u32 words;
1428c2ecf20Sopenharmony_ci	int index;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (!fbdev)
1458c2ecf20Sopenharmony_ci		return -EINVAL;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	/* Update var-dependent FB info */
1488c2ecf20Sopenharmony_ci	if (panel_is_active(fbdev->panel) || panel_is_color(fbdev->panel)) {
1498c2ecf20Sopenharmony_ci		if (info->var.bits_per_pixel <= 8) {
1508c2ecf20Sopenharmony_ci			/* palettized */
1518c2ecf20Sopenharmony_ci			info->var.red.offset    = 0;
1528c2ecf20Sopenharmony_ci			info->var.red.length    = info->var.bits_per_pixel;
1538c2ecf20Sopenharmony_ci			info->var.red.msb_right = 0;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci			info->var.green.offset  = 0;
1568c2ecf20Sopenharmony_ci			info->var.green.length  = info->var.bits_per_pixel;
1578c2ecf20Sopenharmony_ci			info->var.green.msb_right = 0;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci			info->var.blue.offset   = 0;
1608c2ecf20Sopenharmony_ci			info->var.blue.length   = info->var.bits_per_pixel;
1618c2ecf20Sopenharmony_ci			info->var.blue.msb_right = 0;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci			info->var.transp.offset = 0;
1648c2ecf20Sopenharmony_ci			info->var.transp.length = 0;
1658c2ecf20Sopenharmony_ci			info->var.transp.msb_right = 0;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci			info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
1688c2ecf20Sopenharmony_ci			info->fix.line_length = info->var.xres_virtual /
1698c2ecf20Sopenharmony_ci							(8/info->var.bits_per_pixel);
1708c2ecf20Sopenharmony_ci		} else {
1718c2ecf20Sopenharmony_ci			/* non-palettized */
1728c2ecf20Sopenharmony_ci			index = (fbdev->panel->control_base & LCD_CONTROL_SBPPF_MASK) >> LCD_CONTROL_SBPPF_BIT;
1738c2ecf20Sopenharmony_ci			info->var.red = rgb_bitfields[index][0];
1748c2ecf20Sopenharmony_ci			info->var.green = rgb_bitfields[index][1];
1758c2ecf20Sopenharmony_ci			info->var.blue = rgb_bitfields[index][2];
1768c2ecf20Sopenharmony_ci			info->var.transp = rgb_bitfields[index][3];
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci			info->fix.visual = FB_VISUAL_TRUECOLOR;
1798c2ecf20Sopenharmony_ci			info->fix.line_length = info->var.xres_virtual << 1; /* depth=16 */
1808c2ecf20Sopenharmony_ci		}
1818c2ecf20Sopenharmony_ci	} else {
1828c2ecf20Sopenharmony_ci		/* mono */
1838c2ecf20Sopenharmony_ci		info->fix.visual = FB_VISUAL_MONO10;
1848c2ecf20Sopenharmony_ci		info->fix.line_length = info->var.xres_virtual / 8;
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	info->screen_size = info->fix.line_length * info->var.yres_virtual;
1888c2ecf20Sopenharmony_ci	info->var.rotate = ((fbdev->panel->control_base&LCD_CONTROL_SM_MASK) \
1898c2ecf20Sopenharmony_ci				>> LCD_CONTROL_SM_BIT) * 90;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	/* Determine BPP mode and format */
1928c2ecf20Sopenharmony_ci	fbdev->regs->lcd_control = fbdev->panel->control_base;
1938c2ecf20Sopenharmony_ci	fbdev->regs->lcd_horztiming = fbdev->panel->horztiming;
1948c2ecf20Sopenharmony_ci	fbdev->regs->lcd_verttiming = fbdev->panel->verttiming;
1958c2ecf20Sopenharmony_ci	fbdev->regs->lcd_clkcontrol = fbdev->panel->clkcontrol_base;
1968c2ecf20Sopenharmony_ci	fbdev->regs->lcd_intenable = 0;
1978c2ecf20Sopenharmony_ci	fbdev->regs->lcd_intstatus = 0;
1988c2ecf20Sopenharmony_ci	fbdev->regs->lcd_dmaaddr0 = LCD_DMA_SA_N(fbdev->fb_phys);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	if (panel_is_dual(fbdev->panel)) {
2018c2ecf20Sopenharmony_ci		/* Second panel display seconf half of screen if possible,
2028c2ecf20Sopenharmony_ci		 * otherwise display the same as the first panel */
2038c2ecf20Sopenharmony_ci		if (info->var.yres_virtual >= (info->var.yres << 1)) {
2048c2ecf20Sopenharmony_ci			fbdev->regs->lcd_dmaaddr1 = LCD_DMA_SA_N(fbdev->fb_phys +
2058c2ecf20Sopenharmony_ci							  (info->fix.line_length *
2068c2ecf20Sopenharmony_ci						          (info->var.yres_virtual >> 1)));
2078c2ecf20Sopenharmony_ci		} else {
2088c2ecf20Sopenharmony_ci			fbdev->regs->lcd_dmaaddr1 = LCD_DMA_SA_N(fbdev->fb_phys);
2098c2ecf20Sopenharmony_ci		}
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	words = info->fix.line_length / sizeof(u32);
2138c2ecf20Sopenharmony_ci	if (!info->var.rotate || (info->var.rotate == 180)) {
2148c2ecf20Sopenharmony_ci		words *= info->var.yres_virtual;
2158c2ecf20Sopenharmony_ci		if (info->var.rotate /* 180 */) {
2168c2ecf20Sopenharmony_ci			words -= (words % 8); /* should be divisable by 8 */
2178c2ecf20Sopenharmony_ci		}
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci	fbdev->regs->lcd_words = LCD_WRD_WRDS_N(words);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	fbdev->regs->lcd_pwmdiv = 0;
2228c2ecf20Sopenharmony_ci	fbdev->regs->lcd_pwmhi = 0;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	/* Resume controller */
2258c2ecf20Sopenharmony_ci	fbdev->regs->lcd_control |= LCD_CONTROL_GO;
2268c2ecf20Sopenharmony_ci	mdelay(10);
2278c2ecf20Sopenharmony_ci	au1100fb_fb_blank(VESA_NO_BLANKING, info);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	return 0;
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci/* fb_setcolreg
2338c2ecf20Sopenharmony_ci * Set color in LCD palette.
2348c2ecf20Sopenharmony_ci */
2358c2ecf20Sopenharmony_ciint au1100fb_fb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *fbi)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	struct au1100fb_device *fbdev;
2388c2ecf20Sopenharmony_ci	u32 *palette;
2398c2ecf20Sopenharmony_ci	u32 value;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	fbdev = to_au1100fb_device(fbi);
2428c2ecf20Sopenharmony_ci	palette = fbdev->regs->lcd_pallettebase;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	if (regno > (AU1100_LCD_NBR_PALETTE_ENTRIES - 1))
2458c2ecf20Sopenharmony_ci		return -EINVAL;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	if (fbi->var.grayscale) {
2488c2ecf20Sopenharmony_ci		/* Convert color to grayscale */
2498c2ecf20Sopenharmony_ci		red = green = blue =
2508c2ecf20Sopenharmony_ci			(19595 * red + 38470 * green + 7471 * blue) >> 16;
2518c2ecf20Sopenharmony_ci	}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	if (fbi->fix.visual == FB_VISUAL_TRUECOLOR) {
2548c2ecf20Sopenharmony_ci		/* Place color in the pseudopalette */
2558c2ecf20Sopenharmony_ci		if (regno > 16)
2568c2ecf20Sopenharmony_ci			return -EINVAL;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci		palette = (u32*)fbi->pseudo_palette;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci		red   >>= (16 - fbi->var.red.length);
2618c2ecf20Sopenharmony_ci		green >>= (16 - fbi->var.green.length);
2628c2ecf20Sopenharmony_ci		blue  >>= (16 - fbi->var.blue.length);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci		value = (red   << fbi->var.red.offset) 	|
2658c2ecf20Sopenharmony_ci			(green << fbi->var.green.offset)|
2668c2ecf20Sopenharmony_ci			(blue  << fbi->var.blue.offset);
2678c2ecf20Sopenharmony_ci		value &= 0xFFFF;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	} else if (panel_is_active(fbdev->panel)) {
2708c2ecf20Sopenharmony_ci		/* COLOR TFT PALLETTIZED (use RGB 565) */
2718c2ecf20Sopenharmony_ci		value = (red & 0xF800)|((green >> 5) & 0x07E0)|((blue >> 11) & 0x001F);
2728c2ecf20Sopenharmony_ci		value &= 0xFFFF;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	} else if (panel_is_color(fbdev->panel)) {
2758c2ecf20Sopenharmony_ci		/* COLOR STN MODE */
2768c2ecf20Sopenharmony_ci		value = (((panel_swap_rgb(fbdev->panel) ? blue : red) >> 12) & 0x000F) |
2778c2ecf20Sopenharmony_ci			((green >> 8) & 0x00F0) |
2788c2ecf20Sopenharmony_ci			(((panel_swap_rgb(fbdev->panel) ? red : blue) >> 4) & 0x0F00);
2798c2ecf20Sopenharmony_ci		value &= 0xFFF;
2808c2ecf20Sopenharmony_ci	} else {
2818c2ecf20Sopenharmony_ci		/* MONOCHROME MODE */
2828c2ecf20Sopenharmony_ci		value = (green >> 12) & 0x000F;
2838c2ecf20Sopenharmony_ci		value &= 0xF;
2848c2ecf20Sopenharmony_ci	}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	palette[regno] = value;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	return 0;
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci/* fb_pan_display
2928c2ecf20Sopenharmony_ci * Pan display in x and/or y as specified
2938c2ecf20Sopenharmony_ci */
2948c2ecf20Sopenharmony_ciint au1100fb_fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *fbi)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	struct au1100fb_device *fbdev;
2978c2ecf20Sopenharmony_ci	int dy;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	fbdev = to_au1100fb_device(fbi);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	print_dbg("fb_pan_display %p %p", var, fbi);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	if (!var || !fbdev) {
3048c2ecf20Sopenharmony_ci		return -EINVAL;
3058c2ecf20Sopenharmony_ci	}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	if (var->xoffset - fbi->var.xoffset) {
3088c2ecf20Sopenharmony_ci		/* No support for X panning for now! */
3098c2ecf20Sopenharmony_ci		return -EINVAL;
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	print_dbg("fb_pan_display 2 %p %p", var, fbi);
3138c2ecf20Sopenharmony_ci	dy = var->yoffset - fbi->var.yoffset;
3148c2ecf20Sopenharmony_ci	if (dy) {
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci		u32 dmaaddr;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci		print_dbg("Panning screen of %d lines", dy);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci		dmaaddr = fbdev->regs->lcd_dmaaddr0;
3218c2ecf20Sopenharmony_ci		dmaaddr += (fbi->fix.line_length * dy);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci		/* TODO: Wait for current frame to finished */
3248c2ecf20Sopenharmony_ci		fbdev->regs->lcd_dmaaddr0 = LCD_DMA_SA_N(dmaaddr);
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci		if (panel_is_dual(fbdev->panel)) {
3278c2ecf20Sopenharmony_ci			dmaaddr = fbdev->regs->lcd_dmaaddr1;
3288c2ecf20Sopenharmony_ci			dmaaddr += (fbi->fix.line_length * dy);
3298c2ecf20Sopenharmony_ci			fbdev->regs->lcd_dmaaddr0 = LCD_DMA_SA_N(dmaaddr);
3308c2ecf20Sopenharmony_ci	}
3318c2ecf20Sopenharmony_ci	}
3328c2ecf20Sopenharmony_ci	print_dbg("fb_pan_display 3 %p %p", var, fbi);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	return 0;
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci/* fb_mmap
3388c2ecf20Sopenharmony_ci * Map video memory in user space. We don't use the generic fb_mmap method mainly
3398c2ecf20Sopenharmony_ci * to allow the use of the TLB streaming flag (CCA=6)
3408c2ecf20Sopenharmony_ci */
3418c2ecf20Sopenharmony_ciint au1100fb_fb_mmap(struct fb_info *fbi, struct vm_area_struct *vma)
3428c2ecf20Sopenharmony_ci{
3438c2ecf20Sopenharmony_ci	struct au1100fb_device *fbdev = to_au1100fb_device(fbi);
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	pgprot_val(vma->vm_page_prot) |= (6 << 9); //CCA=6
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	return dma_mmap_coherent(fbdev->dev, vma, fbdev->fb_mem, fbdev->fb_phys,
3488c2ecf20Sopenharmony_ci			fbdev->fb_len);
3498c2ecf20Sopenharmony_ci}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_cistatic const struct fb_ops au1100fb_ops =
3528c2ecf20Sopenharmony_ci{
3538c2ecf20Sopenharmony_ci	.owner			= THIS_MODULE,
3548c2ecf20Sopenharmony_ci	.fb_setcolreg		= au1100fb_fb_setcolreg,
3558c2ecf20Sopenharmony_ci	.fb_blank		= au1100fb_fb_blank,
3568c2ecf20Sopenharmony_ci	.fb_pan_display		= au1100fb_fb_pan_display,
3578c2ecf20Sopenharmony_ci	.fb_fillrect		= cfb_fillrect,
3588c2ecf20Sopenharmony_ci	.fb_copyarea		= cfb_copyarea,
3598c2ecf20Sopenharmony_ci	.fb_imageblit		= cfb_imageblit,
3608c2ecf20Sopenharmony_ci	.fb_mmap		= au1100fb_fb_mmap,
3618c2ecf20Sopenharmony_ci};
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_cistatic int au1100fb_setup(struct au1100fb_device *fbdev)
3678c2ecf20Sopenharmony_ci{
3688c2ecf20Sopenharmony_ci	char *this_opt, *options;
3698c2ecf20Sopenharmony_ci	int num_panels = ARRAY_SIZE(known_lcd_panels);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	if (num_panels <= 0) {
3728c2ecf20Sopenharmony_ci		print_err("No LCD panels supported by driver!");
3738c2ecf20Sopenharmony_ci		return -ENODEV;
3748c2ecf20Sopenharmony_ci	}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	if (fb_get_options(DRIVER_NAME, &options))
3778c2ecf20Sopenharmony_ci		return -ENODEV;
3788c2ecf20Sopenharmony_ci	if (!options)
3798c2ecf20Sopenharmony_ci		return -ENODEV;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	while ((this_opt = strsep(&options, ",")) != NULL) {
3828c2ecf20Sopenharmony_ci		/* Panel option */
3838c2ecf20Sopenharmony_ci		if (!strncmp(this_opt, "panel:", 6)) {
3848c2ecf20Sopenharmony_ci			int i;
3858c2ecf20Sopenharmony_ci			this_opt += 6;
3868c2ecf20Sopenharmony_ci			for (i = 0; i < num_panels; i++) {
3878c2ecf20Sopenharmony_ci				if (!strncmp(this_opt, known_lcd_panels[i].name,
3888c2ecf20Sopenharmony_ci					     strlen(this_opt))) {
3898c2ecf20Sopenharmony_ci					fbdev->panel = &known_lcd_panels[i];
3908c2ecf20Sopenharmony_ci					fbdev->panel_idx = i;
3918c2ecf20Sopenharmony_ci					break;
3928c2ecf20Sopenharmony_ci				}
3938c2ecf20Sopenharmony_ci			}
3948c2ecf20Sopenharmony_ci			if (i >= num_panels) {
3958c2ecf20Sopenharmony_ci				print_warn("Panel '%s' not supported!", this_opt);
3968c2ecf20Sopenharmony_ci				return -ENODEV;
3978c2ecf20Sopenharmony_ci			}
3988c2ecf20Sopenharmony_ci		}
3998c2ecf20Sopenharmony_ci		/* Unsupported option */
4008c2ecf20Sopenharmony_ci		else
4018c2ecf20Sopenharmony_ci			print_warn("Unsupported option \"%s\"", this_opt);
4028c2ecf20Sopenharmony_ci	}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	print_info("Panel=%s", fbdev->panel->name);
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	return 0;
4078c2ecf20Sopenharmony_ci}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_cistatic int au1100fb_drv_probe(struct platform_device *dev)
4108c2ecf20Sopenharmony_ci{
4118c2ecf20Sopenharmony_ci	struct au1100fb_device *fbdev;
4128c2ecf20Sopenharmony_ci	struct resource *regs_res;
4138c2ecf20Sopenharmony_ci	struct clk *c;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	/* Allocate new device private */
4168c2ecf20Sopenharmony_ci	fbdev = devm_kzalloc(&dev->dev, sizeof(*fbdev), GFP_KERNEL);
4178c2ecf20Sopenharmony_ci	if (!fbdev)
4188c2ecf20Sopenharmony_ci		return -ENOMEM;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	if (au1100fb_setup(fbdev))
4218c2ecf20Sopenharmony_ci		goto failed;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	platform_set_drvdata(dev, (void *)fbdev);
4248c2ecf20Sopenharmony_ci	fbdev->dev = &dev->dev;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	/* Allocate region for our registers and map them */
4278c2ecf20Sopenharmony_ci	regs_res = platform_get_resource(dev, IORESOURCE_MEM, 0);
4288c2ecf20Sopenharmony_ci	if (!regs_res) {
4298c2ecf20Sopenharmony_ci		print_err("fail to retrieve registers resource");
4308c2ecf20Sopenharmony_ci		return -EFAULT;
4318c2ecf20Sopenharmony_ci	}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	au1100fb_fix.mmio_start = regs_res->start;
4348c2ecf20Sopenharmony_ci	au1100fb_fix.mmio_len = resource_size(regs_res);
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	if (!devm_request_mem_region(&dev->dev,
4378c2ecf20Sopenharmony_ci				     au1100fb_fix.mmio_start,
4388c2ecf20Sopenharmony_ci				     au1100fb_fix.mmio_len,
4398c2ecf20Sopenharmony_ci				     DRIVER_NAME)) {
4408c2ecf20Sopenharmony_ci		print_err("fail to lock memory region at 0x%08lx",
4418c2ecf20Sopenharmony_ci				au1100fb_fix.mmio_start);
4428c2ecf20Sopenharmony_ci		return -EBUSY;
4438c2ecf20Sopenharmony_ci	}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	fbdev->regs = (struct au1100fb_regs*)KSEG1ADDR(au1100fb_fix.mmio_start);
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	print_dbg("Register memory map at %p", fbdev->regs);
4488c2ecf20Sopenharmony_ci	print_dbg("phys=0x%08x, size=%d", fbdev->regs_phys, fbdev->regs_len);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	c = clk_get(NULL, "lcd_intclk");
4518c2ecf20Sopenharmony_ci	if (!IS_ERR(c)) {
4528c2ecf20Sopenharmony_ci		fbdev->lcdclk = c;
4538c2ecf20Sopenharmony_ci		clk_set_rate(c, 48000000);
4548c2ecf20Sopenharmony_ci		clk_prepare_enable(c);
4558c2ecf20Sopenharmony_ci	}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	/* Allocate the framebuffer to the maximum screen size * nbr of video buffers */
4588c2ecf20Sopenharmony_ci	fbdev->fb_len = fbdev->panel->xres * fbdev->panel->yres *
4598c2ecf20Sopenharmony_ci		  	(fbdev->panel->bpp >> 3) * AU1100FB_NBR_VIDEO_BUFFERS;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	fbdev->fb_mem = dmam_alloc_coherent(&dev->dev,
4628c2ecf20Sopenharmony_ci					    PAGE_ALIGN(fbdev->fb_len),
4638c2ecf20Sopenharmony_ci					    &fbdev->fb_phys, GFP_KERNEL);
4648c2ecf20Sopenharmony_ci	if (!fbdev->fb_mem) {
4658c2ecf20Sopenharmony_ci		print_err("fail to allocate framebuffer (size: %dK))",
4668c2ecf20Sopenharmony_ci			  fbdev->fb_len / 1024);
4678c2ecf20Sopenharmony_ci		return -ENOMEM;
4688c2ecf20Sopenharmony_ci	}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	au1100fb_fix.smem_start = fbdev->fb_phys;
4718c2ecf20Sopenharmony_ci	au1100fb_fix.smem_len = fbdev->fb_len;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	print_dbg("Framebuffer memory map at %p", fbdev->fb_mem);
4748c2ecf20Sopenharmony_ci	print_dbg("phys=0x%08x, size=%dK", fbdev->fb_phys, fbdev->fb_len / 1024);
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	/* load the panel info into the var struct */
4778c2ecf20Sopenharmony_ci	au1100fb_var.bits_per_pixel = fbdev->panel->bpp;
4788c2ecf20Sopenharmony_ci	au1100fb_var.xres = fbdev->panel->xres;
4798c2ecf20Sopenharmony_ci	au1100fb_var.xres_virtual = au1100fb_var.xres;
4808c2ecf20Sopenharmony_ci	au1100fb_var.yres = fbdev->panel->yres;
4818c2ecf20Sopenharmony_ci	au1100fb_var.yres_virtual = au1100fb_var.yres;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	fbdev->info.screen_base = fbdev->fb_mem;
4848c2ecf20Sopenharmony_ci	fbdev->info.fbops = &au1100fb_ops;
4858c2ecf20Sopenharmony_ci	fbdev->info.fix = au1100fb_fix;
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	fbdev->info.pseudo_palette =
4888c2ecf20Sopenharmony_ci		devm_kcalloc(&dev->dev, 16, sizeof(u32), GFP_KERNEL);
4898c2ecf20Sopenharmony_ci	if (!fbdev->info.pseudo_palette)
4908c2ecf20Sopenharmony_ci		return -ENOMEM;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	if (fb_alloc_cmap(&fbdev->info.cmap, AU1100_LCD_NBR_PALETTE_ENTRIES, 0) < 0) {
4938c2ecf20Sopenharmony_ci		print_err("Fail to allocate colormap (%d entries)",
4948c2ecf20Sopenharmony_ci			   AU1100_LCD_NBR_PALETTE_ENTRIES);
4958c2ecf20Sopenharmony_ci		return -EFAULT;
4968c2ecf20Sopenharmony_ci	}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	fbdev->info.var = au1100fb_var;
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	/* Set h/w registers */
5018c2ecf20Sopenharmony_ci	au1100fb_setmode(fbdev);
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	/* Register new framebuffer */
5048c2ecf20Sopenharmony_ci	if (register_framebuffer(&fbdev->info) < 0) {
5058c2ecf20Sopenharmony_ci		print_err("cannot register new framebuffer");
5068c2ecf20Sopenharmony_ci		goto failed;
5078c2ecf20Sopenharmony_ci	}
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	return 0;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_cifailed:
5128c2ecf20Sopenharmony_ci	if (fbdev->lcdclk) {
5138c2ecf20Sopenharmony_ci		clk_disable_unprepare(fbdev->lcdclk);
5148c2ecf20Sopenharmony_ci		clk_put(fbdev->lcdclk);
5158c2ecf20Sopenharmony_ci	}
5168c2ecf20Sopenharmony_ci	if (fbdev->info.cmap.len != 0) {
5178c2ecf20Sopenharmony_ci		fb_dealloc_cmap(&fbdev->info.cmap);
5188c2ecf20Sopenharmony_ci	}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	return -ENODEV;
5218c2ecf20Sopenharmony_ci}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ciint au1100fb_drv_remove(struct platform_device *dev)
5248c2ecf20Sopenharmony_ci{
5258c2ecf20Sopenharmony_ci	struct au1100fb_device *fbdev = NULL;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	if (!dev)
5288c2ecf20Sopenharmony_ci		return -ENODEV;
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	fbdev = platform_get_drvdata(dev);
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci#if !defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_LOGO)
5338c2ecf20Sopenharmony_ci	au1100fb_fb_blank(VESA_POWERDOWN, &fbdev->info);
5348c2ecf20Sopenharmony_ci#endif
5358c2ecf20Sopenharmony_ci	fbdev->regs->lcd_control &= ~LCD_CONTROL_GO;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	/* Clean up all probe data */
5388c2ecf20Sopenharmony_ci	unregister_framebuffer(&fbdev->info);
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	fb_dealloc_cmap(&fbdev->info.cmap);
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	if (fbdev->lcdclk) {
5438c2ecf20Sopenharmony_ci		clk_disable_unprepare(fbdev->lcdclk);
5448c2ecf20Sopenharmony_ci		clk_put(fbdev->lcdclk);
5458c2ecf20Sopenharmony_ci	}
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	return 0;
5488c2ecf20Sopenharmony_ci}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
5518c2ecf20Sopenharmony_cistatic struct au1100fb_regs fbregs;
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ciint au1100fb_drv_suspend(struct platform_device *dev, pm_message_t state)
5548c2ecf20Sopenharmony_ci{
5558c2ecf20Sopenharmony_ci	struct au1100fb_device *fbdev = platform_get_drvdata(dev);
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	if (!fbdev)
5588c2ecf20Sopenharmony_ci		return 0;
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	/* Blank the LCD */
5618c2ecf20Sopenharmony_ci	au1100fb_fb_blank(VESA_POWERDOWN, &fbdev->info);
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	if (fbdev->lcdclk)
5648c2ecf20Sopenharmony_ci		clk_disable(fbdev->lcdclk);
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	memcpy(&fbregs, fbdev->regs, sizeof(struct au1100fb_regs));
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	return 0;
5698c2ecf20Sopenharmony_ci}
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ciint au1100fb_drv_resume(struct platform_device *dev)
5728c2ecf20Sopenharmony_ci{
5738c2ecf20Sopenharmony_ci	struct au1100fb_device *fbdev = platform_get_drvdata(dev);
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	if (!fbdev)
5768c2ecf20Sopenharmony_ci		return 0;
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	memcpy(fbdev->regs, &fbregs, sizeof(struct au1100fb_regs));
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	if (fbdev->lcdclk)
5818c2ecf20Sopenharmony_ci		clk_enable(fbdev->lcdclk);
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	/* Unblank the LCD */
5848c2ecf20Sopenharmony_ci	au1100fb_fb_blank(VESA_NO_BLANKING, &fbdev->info);
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	return 0;
5878c2ecf20Sopenharmony_ci}
5888c2ecf20Sopenharmony_ci#else
5898c2ecf20Sopenharmony_ci#define au1100fb_drv_suspend NULL
5908c2ecf20Sopenharmony_ci#define au1100fb_drv_resume NULL
5918c2ecf20Sopenharmony_ci#endif
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_cistatic struct platform_driver au1100fb_driver = {
5948c2ecf20Sopenharmony_ci	.driver = {
5958c2ecf20Sopenharmony_ci		.name		= "au1100-lcd",
5968c2ecf20Sopenharmony_ci	},
5978c2ecf20Sopenharmony_ci	.probe		= au1100fb_drv_probe,
5988c2ecf20Sopenharmony_ci        .remove		= au1100fb_drv_remove,
5998c2ecf20Sopenharmony_ci	.suspend	= au1100fb_drv_suspend,
6008c2ecf20Sopenharmony_ci        .resume		= au1100fb_drv_resume,
6018c2ecf20Sopenharmony_ci};
6028c2ecf20Sopenharmony_cimodule_platform_driver(au1100fb_driver);
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
6058c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
606