aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/mxc
diff options
context:
space:
mode:
authorroot <root@artemis.panaceas.org>2015-12-25 04:40:36 +0000
committerroot <root@artemis.panaceas.org>2015-12-25 04:40:36 +0000
commit849369d6c66d3054688672f97d31fceb8e8230fb (patch)
tree6135abc790ca67dedbe07c39806591e70eda81ce /drivers/video/mxc
downloadlinux-3.0.35-kobo-849369d6c66d3054688672f97d31fceb8e8230fb.tar.gz
linux-3.0.35-kobo-849369d6c66d3054688672f97d31fceb8e8230fb.tar.bz2
linux-3.0.35-kobo-849369d6c66d3054688672f97d31fceb8e8230fb.zip
initial_commit
Diffstat (limited to 'drivers/video/mxc')
-rw-r--r--drivers/video/mxc/DataType.h111
-rw-r--r--drivers/video/mxc/Kconfig153
-rw-r--r--drivers/video/mxc/Makefile39
-rw-r--r--drivers/video/mxc/ch7024.c866
-rw-r--r--drivers/video/mxc/eink_processing2.c103
-rw-r--r--drivers/video/mxc/eink_processing2.h197
-rw-r--r--drivers/video/mxc/elcdif_regs.h678
-rw-r--r--drivers/video/mxc/epdc_regs.h443
-rw-r--r--drivers/video/mxc/epdfb_dc.c1837
-rw-r--r--drivers/video/mxc/epdfb_dc.h184
-rw-r--r--drivers/video/mxc/fake_s1d13522.c3149
-rw-r--r--drivers/video/mxc/fake_s1d13522.h67
-rw-r--r--drivers/video/mxc/ldb.c974
l---------drivers/video/mxc/lib.a_shipped1
-rwxr-xr-xdrivers/video/mxc/lib.a_shipped-imx6sl-aabin0 -> 12650 bytes
-rwxr-xr-xdrivers/video/mxc/lib.a_shipped-imx6sl-noaabin0 -> 2790 bytes
-rw-r--r--drivers/video/mxc/lk_fp9928.c1070
-rw-r--r--drivers/video/mxc/lk_fp9928.h34
-rw-r--r--drivers/video/mxc/lk_tps65185.c2311
-rw-r--r--drivers/video/mxc/lk_tps65185.h53
-rw-r--r--drivers/video/mxc/mipi_dsi.c1010
-rw-r--r--drivers/video/mxc/mipi_dsi.h109
-rw-r--r--drivers/video/mxc/mx2fb.c1349
-rw-r--r--drivers/video/mxc/mx2fb.h141
-rw-r--r--drivers/video/mxc/mxc_dispdrv.c163
-rw-r--r--drivers/video/mxc/mxc_dispdrv.h54
-rw-r--r--drivers/video/mxc/mxc_dvi.c380
-rw-r--r--drivers/video/mxc/mxc_edid.c763
-rw-r--r--drivers/video/mxc/mxc_elcdif_fb.c1723
-rw-r--r--drivers/video/mxc/mxc_epdc_eink_module.c44
-rw-r--r--drivers/video/mxc/mxc_epdc_fake_s1d13522.c1123
-rw-r--r--drivers/video/mxc/mxc_epdc_fb.c8045
-rw-r--r--drivers/video/mxc/mxc_ipuv3_fb.c2647
-rw-r--r--drivers/video/mxc/mxc_lcdif.c145
-rw-r--r--drivers/video/mxc/mxc_spdc_fb.c4180
-rw-r--r--drivers/video/mxc/mxc_spdc_fb.h352
-rw-r--r--drivers/video/mxc/mxcfb.c1373
-rw-r--r--drivers/video/mxc/mxcfb_ch7026.c370
-rw-r--r--drivers/video/mxc/mxcfb_claa_wvga.c240
-rw-r--r--drivers/video/mxc/mxcfb_epson.c1153
-rw-r--r--drivers/video/mxc/mxcfb_epson_vga.c362
-rw-r--r--drivers/video/mxc/mxcfb_hx8369_wvga.c454
-rw-r--r--drivers/video/mxc/mxcfb_modedb.c69
-rw-r--r--drivers/video/mxc/mxcfb_seiko_wvga.c236
-rw-r--r--drivers/video/mxc/mxcfb_sii902x.c1312
-rw-r--r--drivers/video/mxc/mxcfb_sii902x_elcdif.c538
l---------drivers/video/mxc/ntx_hwconfig.h1
-rw-r--r--drivers/video/mxc/ntx_s1d13521fb.h169
-rw-r--r--drivers/video/mxc/s1d13521.h268
-rw-r--r--drivers/video/mxc/s1d13521_debug.h53
-rw-r--r--drivers/video/mxc/s1d13521fb.h214
-rw-r--r--drivers/video/mxc/s1d13521ioctl.h409
-rw-r--r--drivers/video/mxc/tve.c1310
53 files changed, 43029 insertions, 0 deletions
diff --git a/drivers/video/mxc/DataType.h b/drivers/video/mxc/DataType.h
new file mode 100644
index 00000000..87f4aa48
--- /dev/null
+++ b/drivers/video/mxc/DataType.h
@@ -0,0 +1,111 @@
+/*
+ * DataType.h
+ *
+ */
+
+#ifndef __DATA_TYPE_H__
+ #define __DATA_TYPE_H__
+
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ //
+ //
+ //
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ #define _BIT0 0x00000001
+ #define _BIT1 0x00000002
+ #define _BIT2 0x00000004
+ #define _BIT3 0x00000008
+ #define _BIT4 0x00000010
+ #define _BIT5 0x00000010
+ #define _BIT6 0x00000040
+ #define _BIT7 0x00000080
+ #define _BIT8 0x00000100
+ #define _BIT9 0x00000200
+ #define _BIT10 0x00000400
+ #define _BIT11 0x00000800
+ #define _BIT12 0x00001000
+ #define _BIT13 0x00002000
+ #define _BIT14 0x00004000
+ #define _BIT15 0x00008000
+ #define _BIT16 0x00010000
+ #define _BIT17 0x00020000
+ #define _BIT18 0x00040000
+ #define _BIT19 0x00080000
+ #define _BIT20 0x00100000
+ #define _BIT21 0x00200000
+ #define _BIT22 0x00400000
+ #define _BIT23 0x00800000
+ #define _BIT24 0x01000000
+ #define _BIT25 0x02000000
+ #define _BIT26 0x04000000
+ #define _BIT27 0x08000000
+ #define _BIT28 0x10000000
+ #define _BIT29 0x20000000
+ #define _BIT30 0x40000000
+ #define _BIT31 0x80000000
+
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ //
+ //
+ //
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ typedef char S8;
+ typedef char PS8;
+ typedef unsigned char U8;
+ typedef unsigned char *PU8;
+ typedef unsigned char BYTE;
+ typedef unsigned char *PBYTE;
+
+ typedef short int SINT16;
+ typedef short int *PSINT16;
+ typedef unsigned short int UINT16;
+ typedef unsigned short int *PUINT16;
+ typedef unsigned short int WORD;
+ typedef unsigned short int *PWORD;
+
+ typedef int SINT32;
+ typedef int *PSINT32;
+
+ typedef unsigned int UINT;
+ typedef unsigned int *PUINT;
+ typedef unsigned int UINT32;
+ typedef unsigned int *PUINT32;
+ typedef unsigned int DWORD;
+ typedef unsigned int *PDWORD;
+
+ typedef void VOID;
+ typedef void *PVOID;
+
+ typedef unsigned long ULONG;
+ typedef unsigned long *PULONG;
+
+ typedef long LONG;
+ typedef long *PLONG;
+
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ //
+ //
+ //
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#if 1
+ typedef int BOOL;
+#endif
+
+ #define TRUE 1
+ #define FALSE 0
+
+ typedef int HW_LOGIC;
+
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ //
+ //
+ //
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ #define _HW_LOGIC_HIGH 1
+ #define _HW_LOGIC_LOW 0
+
+ #define K_BYTES(x) (1024*(x))
+ #define M_BYTES(x) (1024*1024*(x))
+ #define MEM_PAGE_SIZE K_BYTES(4)
+
+#endif /* __DATA_TYPE_H__ */
diff --git a/drivers/video/mxc/Kconfig b/drivers/video/mxc/Kconfig
new file mode 100644
index 00000000..73427a82
--- /dev/null
+++ b/drivers/video/mxc/Kconfig
@@ -0,0 +1,153 @@
+config FB_MXC
+ tristate "MXC Framebuffer support"
+ depends on FB && (MXC_IPU || ARCH_MX21 || ARCH_MX27 || ARCH_MX25 || ARCH_MX6)
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ select FB_MODE_HELPERS
+ default y
+ help
+ This is a framebuffer device for the MXC LCD Controller.
+ See <http://www.linux-fbdev.org/> for information on framebuffer
+ devices.
+
+ If you plan to use the LCD display with your MXC system, say
+ Y here.
+
+config FB_MXC_EDID
+ depends on FB_MXC && I2C
+ tristate "MXC EDID support"
+ default y
+
+config FB_MXC_SYNC_PANEL
+ depends on FB_MXC
+ tristate "Synchronous Panel Framebuffer"
+ default y
+
+config FB_MXC_EPSON_VGA_SYNC_PANEL
+ depends on FB_MXC_SYNC_PANEL
+ tristate "Epson VGA Panel"
+ default n
+
+config FB_MXC_TVOUT_TVE
+ tristate "MXC TVE TV Out Encoder"
+ depends on FB_MXC_SYNC_PANEL
+ depends on MXC_IPU_V3 && !ARCH_MX6Q
+
+config FB_MXC_LDB
+ tristate "MXC LDB"
+ depends on FB_MXC_SYNC_PANEL
+ depends on MXC_IPU_V3
+
+config FB_MXC_MIPI_DSI
+ tristate "MXC MIPI_DSI"
+ depends on FB_MXC_SYNC_PANEL
+ depends on MXC_IPU_V3
+
+config FB_MXC_TRULY_WVGA_SYNC_PANEL
+ tristate "TRULY WVGA Panel"
+ depends on FB_MXC_SYNC_PANEL && FB_MXC_MIPI_DSI
+
+config FB_MXC_CLAA_WVGA_SYNC_PANEL
+ depends on FB_MXC_SYNC_PANEL
+ tristate "CLAA WVGA Panel"
+
+config FB_MXC_SEIKO_WVGA_SYNC_PANEL
+ depends on FB_MXC_SYNC_PANEL
+ tristate "SEIKO WVGA Panel"
+
+config FB_MXC_SII902X
+ depends on FB_MXC_SYNC_PANEL && I2C
+ tristate "Si Image SII9022 DVI/HDMI Interface Chip"
+
+config FB_MXC_SII902X_ELCDIF
+ depends on FB_MXC_ELCDIF_FB && FB_MXC_SYNC_PANEL && I2C
+ tristate "Si Image SII9022 DVI/HDMI Interface Chip for ELCDIF FB"
+
+config FB_MXC_CH7026
+ depends on FB_MXC_SYNC_PANEL
+ tristate "Chrontel CH7026 VGA Interface Chip"
+
+config FB_MXC_TVOUT_CH7024
+ tristate "CH7024 TV Out Encoder"
+ depends on FB_MXC_SYNC_PANEL
+
+config FB_MXC_LOW_PWR_DISPLAY
+ bool "Low Power Display Refresh Mode"
+ depends on FB_MXC_SYNC_PANEL && MXC_FB_IRAM
+ default y
+
+config FB_MXC_INTERNAL_MEM
+ bool "Framebuffer in Internal RAM"
+ depends on FB_MXC_SYNC_PANEL && MXC_FB_IRAM
+ default y
+
+config FB_MXC_ASYNC_PANEL
+ depends on FB_MXC
+ bool "Asynchronous Panels"
+ default n
+
+menu "Asynchronous Panel Type"
+ depends on FB_MXC_ASYNC_PANEL && FB_MXC
+
+config FB_MXC_EPSON_PANEL
+ depends on FB_MXC_ASYNC_PANEL
+ default n
+ bool "Epson 176x220 Panel"
+
+endmenu
+
+config FB_MXC_EINK_PANEL
+ depends on FB_MXC
+ depends on DMA_ENGINE
+ select FB_DEFERRED_IO
+ tristate "E-Ink Panel Framebuffer"
+
+config FB_MXC_EINK_REGAL
+ depends on FB_MXC_EINK_PANEL
+ bool "E-Ink REGAL support"
+
+config FB_MXC_EINK_AUTO_UPDATE_MODE
+ bool "E-Ink Auto-update Mode Support"
+ default n
+ depends on FB_MXC_EINK_PANEL
+
+config FB_MXC_SIPIX_PANEL
+ depends on FB_MXC
+ depends on DMA_ENGINE
+ select FB_DEFERRED_IO
+ tristate "SIPIX Panel Framebuffer"
+
+config FB_MXC_SIPIX_AUTO_UPDATE_MODE
+ bool "SIPIX Auto-update Mode Support"
+ default n
+ depends on FB_MXC_SIPIX_PANEL
+
+config FB_MXC_ELCDIF_FB
+ depends on FB && ARCH_MXC
+ tristate "Support MXC ELCDIF framebuffer"
+ select IMX_HAVE_PLATFORM_IMX_ELCDIF
+
+config IMX_HAVE_PLATFORM_IMX_ELCDIF
+ bool "IMX_HAVE_PLATFORM_IMX_ELCDIF"
+ default n
+ depends on FB_MXC_ELCDIF_FB
+ help
+
+
+
+choice
+ prompt "Async Panel Interface Type"
+ depends on FB_MXC_ASYNC_PANEL && FB_MXC
+ default FB_MXC_ASYNC_PANEL_IFC_16_BIT
+
+config FB_MXC_ASYNC_PANEL_IFC_8_BIT
+ bool "8-bit Parallel Bus Interface"
+
+config FB_MXC_ASYNC_PANEL_IFC_16_BIT
+ bool "16-bit Parallel Bus Interface"
+
+config FB_MXC_ASYNC_PANEL_IFC_SERIAL
+ bool "Serial Bus Interface"
+
+endchoice
diff --git a/drivers/video/mxc/Makefile b/drivers/video/mxc/Makefile
new file mode 100644
index 00000000..f186b7f8
--- /dev/null
+++ b/drivers/video/mxc/Makefile
@@ -0,0 +1,39 @@
+obj-$(CONFIG_FB_MXC_TVOUT_TVE) += tve.o
+obj-$(CONFIG_FB_MXC_SII902X) += mxcfb_sii902x.o
+obj-$(CONFIG_FB_MXC_SII902X_ELCDIF) += mxcfb_sii902x_elcdif.o
+obj-$(CONFIG_FB_MXC_LDB) += ldb.o
+obj-$(CONFIG_FB_MXC_MIPI_DSI) += mipi_dsi.o
+obj-$(CONFIG_FB_MXC_TRULY_WVGA_SYNC_PANEL) += mxcfb_hx8369_wvga.o
+obj-$(CONFIG_FB_MXC_EDID) += mxc_edid.o
+ifeq ($(CONFIG_ARCH_MX21)$(CONFIG_ARCH_MX27)$(CONFIG_ARCH_MX25),y)
+ obj-$(CONFIG_FB_MXC_TVOUT) += fs453.o
+ obj-$(CONFIG_FB_MXC_SYNC_PANEL) += mx2fb.o mxcfb_modedb.o
+ obj-$(CONFIG_FB_MXC_EPSON_PANEL) += mx2fb_epson.o
+else
+ifeq ($(CONFIG_MXC_IPU_V1),y)
+ obj-$(CONFIG_FB_MXC_SYNC_PANEL) += mxcfb.o mxcfb_modedb.o
+endif
+ifeq ($(CONFIG_MXC_IPU_V3),y)
+ obj-$(CONFIG_FB_MXC_SYNC_PANEL) += mxc_dispdrv.o mxc_lcdif.o mxc_ipuv3_fb.o
+ obj-$(CONFIG_FB_MXC_EDID) += mxc_dvi.o
+endif
+ obj-$(CONFIG_FB_MXC_EPSON_PANEL) += mxcfb_epson.o
+ obj-$(CONFIG_FB_MXC_EPSON_QVGA_PANEL) += mxcfb_epson_qvga.o
+ obj-$(CONFIG_FB_MXC_TOSHIBA_QVGA_PANEL) += mxcfb_toshiba_qvga.o
+ obj-$(CONFIG_FB_MXC_SHARP_128_PANEL) += mxcfb_sharp_128x128.o
+endif
+obj-$(CONFIG_FB_MXC_EPSON_VGA_SYNC_PANEL) += mxcfb_epson_vga.o
+obj-$(CONFIG_FB_MXC_CLAA_WVGA_SYNC_PANEL) += mxcfb_claa_wvga.o
+obj-$(CONFIG_FB_MXC_SEIKO_WVGA_SYNC_PANEL) += mxcfb_seiko_wvga.o
+obj-$(CONFIG_FB_MXC_TVOUT_CH7024) += ch7024.o
+obj-$(CONFIG_FB_MXC_CH7026) += mxcfb_ch7026.o
+obj-$(CONFIG_FB_MXC_EINK_PANEL) += mxc_epdc_fb.o mxc_epdc_eink.o
+ifeq ($(CONFIG_FB_MXC_EINK_REGAL),y)
+mxc_epdc_eink-objs := mxc_epdc_eink_module.o lib.a
+else
+mxc_epdc_eink-objs := mxc_epdc_eink_module.o eink_processing2.o
+endif
+obj-$(CONFIG_FB_MXC_SIPIX_PANEL) += mxc_spdc_fb.o
+obj-$(CONFIG_FB_MXC_ELCDIF_FB) += mxc_elcdif_fb.o
+obj-y += lk_tps65185.o lk_fp9928.o fake_s1d13522.o epdfb_dc.o
+
diff --git a/drivers/video/mxc/ch7024.c b/drivers/video/mxc/ch7024.c
new file mode 100644
index 00000000..32ca332d
--- /dev/null
+++ b/drivers/video/mxc/ch7024.c
@@ -0,0 +1,866 @@
+/*
+ * Copyright 2007-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file ch7024.c
+ * @brief Driver for CH7024 TV encoder
+ *
+ * @ingroup Framebuffer
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/sysfs.h>
+#include <linux/mxcfb.h>
+#include <linux/regulator/consumer.h>
+#include <asm/uaccess.h>
+#include <asm/atomic.h>
+#include <mach/gpio.h>
+#include <mach/hw_events.h>
+
+/*!
+ * CH7024 registers
+ */
+#define CH7024_DEVID 0x00
+#define CH7024_REVID 0x01
+#define CH7024_PG 0x02
+
+#define CH7024_RESET 0x03
+#define CH7024_POWER 0x04
+#define CH7024_TVHUE 0x05
+#define CH7024_TVSAT 0x06
+#define CH7024_TVCTA 0x07
+#define CH7024_TVBRI 0x08
+#define CH7024_TVSHARP 0x09
+#define CH7024_OUT_FMT 0x0A
+#define CH7024_XTAL 0x0B
+#define CH7024_IDF1 0x0C
+#define CH7024_IDF2 0x0D
+#define CH7024_SYNC 0x0E
+#define CH7024_TVFILTER1 0x0F
+#define CH7024_TVFILTER2 0x10
+#define CH7024_IN_TIMING1 0x11
+#define CH7024_IN_TIMING2 0x12
+#define CH7024_IN_TIMING3 0x13
+#define CH7024_IN_TIMING4 0x14
+#define CH7024_IN_TIMING5 0x15
+#define CH7024_IN_TIMING6 0x16
+#define CH7024_IN_TIMING7 0x17
+#define CH7024_IN_TIMING8 0x18
+#define CH7024_IN_TIMING9 0x19
+#define CH7024_IN_TIMING10 0x1A
+#define CH7024_IN_TIMING11 0x1B
+#define CH7024_ACIV 0x1C
+#define CH7024_CLK_TREE 0x1D
+#define CH7024_OUT_TIMING1 0x1E
+#define CH7024_OUT_TIMING2 0x1F
+#define CH7024_V_POS1 0x20
+#define CH7024_V_POS2 0x21
+#define CH7024_H_POS1 0x22
+#define CH7024_H_POS2 0x23
+#define CH7024_PCLK_A1 0x24
+#define CH7024_PCLK_A2 0x25
+#define CH7024_PCLK_A3 0x26
+#define CH7024_PCLK_A4 0x27
+#define CH7024_CLK_P1 0x28
+#define CH7024_CLK_P2 0x29
+#define CH7024_CLK_P3 0x2A
+#define CH7024_CLK_N1 0x2B
+#define CH7024_CLK_N2 0x2C
+#define CH7024_CLK_N3 0x2D
+#define CH7024_CLK_T 0x2E
+#define CH7024_PLL1 0x2F
+#define CH7024_PLL2 0x30
+#define CH7024_PLL3 0x31
+#define CH7024_SC_FREQ1 0x34
+#define CH7024_SC_FREQ2 0x35
+#define CH7024_SC_FREQ3 0x36
+#define CH7024_SC_FREQ4 0x37
+#define CH7024_DAC_TRIM 0x62
+#define CH7024_DATA_IO 0x63
+#define CH7024_ATT_DISP 0x7E
+
+/*!
+ * CH7024 register values
+ */
+/* video output formats */
+#define CH7024_VOS_NTSC_M 0x0
+#define CH7024_VOS_NTSC_J 0x1
+#define CH7024_VOS_NTSC_443 0x2
+#define CH7024_VOS_PAL_BDGHKI 0x3
+#define CH7024_VOS_PAL_M 0x4
+#define CH7024_VOS_PAL_N 0x5
+#define CH7024_VOS_PAL_NC 0x6
+#define CH7024_VOS_PAL_60 0x7
+/* crystal predefined */
+#define CH7024_XTAL_13MHZ 0x4
+#define CH7024_XTAL_26MHZ 0xB
+
+/* chip ID */
+#define CH7024_DEVICE_ID 0x45
+
+/* clock source define */
+#define CLK_HIGH 0
+#define CLK_LOW 1
+
+/* CH7024 presets structs */
+struct ch7024_clock {
+ u32 A;
+ u32 P;
+ u32 N;
+ u32 T;
+ u8 PLLN1;
+ u8 PLLN2;
+ u8 PLLN3;
+};
+
+struct ch7024_input_timing {
+ u32 HTI;
+ u32 VTI;
+ u32 HAI;
+ u32 VAI;
+ u32 HW;
+ u32 HO;
+ u32 VW;
+ u32 VO;
+ u32 VOS;
+};
+
+#define TVOUT_FMT_OFF 0
+#define TVOUT_FMT_NTSC 1
+#define TVOUT_FMT_PAL 2
+
+static int enabled; /* enable power on or not */
+static int pm_status; /* status before suspend */
+
+static struct i2c_client *ch7024_client;
+static struct fb_info *ch7024_fbi;
+static int ch7024_cur_mode;
+static u32 detect_gpio;
+static struct regulator *io_reg;
+static struct regulator *core_reg;
+static struct regulator *analog_reg;
+
+static void hp_detect_wq_handler(struct work_struct *);
+DECLARE_DELAYED_WORK(ch7024_wq, hp_detect_wq_handler);
+
+static inline int ch7024_read_reg(u8 reg)
+{
+ return i2c_smbus_read_byte_data(ch7024_client, reg);
+}
+
+static inline int ch7024_write_reg(u8 reg, u8 word)
+{
+ return i2c_smbus_write_byte_data(ch7024_client, reg, word);
+}
+
+/**
+ * PAL B/D/G/H/K/I clock and timting structures
+ */
+static struct ch7024_clock ch7024_clk_pal = {
+ .A = 0x0,
+ .P = 0x36b00,
+ .N = 0x41eb00,
+ .T = 0x3f,
+ .PLLN1 = 0x0,
+ .PLLN2 = 0x1b,
+ .PLLN3 = 0x12,
+};
+
+static struct ch7024_input_timing ch7024_timing_pal = {
+ .HTI = 950,
+ .VTI = 560,
+ .HAI = 640,
+ .VAI = 480,
+ .HW = 60,
+ .HO = 250,
+ .VW = 40,
+ .VO = 40,
+ .VOS = CH7024_VOS_PAL_BDGHKI,
+};
+
+/**
+ * NTSC_M clock and timting structures
+ * TODO: change values to work well.
+ */
+static struct ch7024_clock ch7024_clk_ntsc = {
+ .A = 0x0,
+ .P = 0x2ac90,
+ .N = 0x36fc90,
+ .T = 0x3f,
+ .PLLN1 = 0x0,
+ .PLLN2 = 0x1b,
+ .PLLN3 = 0x12,
+};
+
+static struct ch7024_input_timing ch7024_timing_ntsc = {
+ .HTI = 801,
+ .VTI = 554,
+ .HAI = 640,
+ .VAI = 480,
+ .HW = 60,
+ .HO = 101,
+ .VW = 20,
+ .VO = 54,
+ .VOS = CH7024_VOS_NTSC_M,
+};
+
+static struct fb_videomode video_modes[] = {
+ {
+ /* NTSC TV output */
+ "TV-NTSC", 60, 640, 480, 37594,
+ 0, 101,
+ 0, 54,
+ 60, 20,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED,
+ 0,},
+ {
+ /* PAL TV output */
+ "TV-PAL", 50, 640, 480, 37594,
+ 0, 250,
+ 0, 40,
+ 60, 40,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED,
+ 0,},
+};
+
+/**
+ * ch7024_setup
+ * initial the CH7024 chipset by setting register
+ * @param:
+ * vos: output video format
+ * @return:
+ * 0 successful
+ * otherwise failed
+ */
+static int ch7024_setup(int vos)
+{
+ struct ch7024_input_timing *ch_timing;
+ struct ch7024_clock *ch_clk;
+#ifdef DEBUG_CH7024
+ int i, val;
+#endif
+
+ /* select output video format */
+ if (vos == TVOUT_FMT_PAL) {
+ ch_timing = &ch7024_timing_pal;
+ ch_clk = &ch7024_clk_pal;
+ pr_debug("CH7024: change to PAL video\n");
+ } else if (vos == TVOUT_FMT_NTSC) {
+ ch_timing = &ch7024_timing_ntsc;
+ ch_clk = &ch7024_clk_ntsc;
+ pr_debug("CH7024: change to NTSC video\n");
+ } else {
+
+ pr_debug("CH7024: no such video format.\n");
+ return -EINVAL;
+ }
+ ch7024_write_reg(CH7024_RESET, 0x0);
+ ch7024_write_reg(CH7024_RESET, 0x3);
+
+ ch7024_write_reg(CH7024_POWER, 0x0C); /* power on, disable DAC */
+ ch7024_write_reg(CH7024_XTAL, CH7024_XTAL_26MHZ);
+ ch7024_write_reg(CH7024_SYNC, 0x0D); /* SLAVE mode, and TTL */
+ ch7024_write_reg(CH7024_IDF1, 0x00);
+ ch7024_write_reg(CH7024_TVFILTER1, 0x00); /* set XCH=0 */
+ ch7024_write_reg(CH7024_CLK_TREE, 0x9E); /* Invert input clk */
+
+ /* set input clock and divider */
+ /* set PLL */
+ ch7024_write_reg(CH7024_PLL1, ch_clk->PLLN1);
+ ch7024_write_reg(CH7024_PLL2, ch_clk->PLLN2);
+ ch7024_write_reg(CH7024_PLL3, ch_clk->PLLN3);
+ /* set A register */
+ ch7024_write_reg(CH7024_PCLK_A1, (ch_clk->A >> 24) & 0xFF);
+ ch7024_write_reg(CH7024_PCLK_A2, (ch_clk->A >> 16) & 0xFF);
+ ch7024_write_reg(CH7024_PCLK_A3, (ch_clk->A >> 8) & 0xFF);
+ ch7024_write_reg(CH7024_PCLK_A4, ch_clk->A & 0xFF);
+ /* set P register */
+ ch7024_write_reg(CH7024_CLK_P1, (ch_clk->P >> 16) & 0xFF);
+ ch7024_write_reg(CH7024_CLK_P2, (ch_clk->P >> 8) & 0xFF);
+ ch7024_write_reg(CH7024_CLK_P3, ch_clk->P & 0xFF);
+ /* set N register */
+ ch7024_write_reg(CH7024_CLK_N1, (ch_clk->N >> 16) & 0xFF);
+ ch7024_write_reg(CH7024_CLK_N2, (ch_clk->N >> 8) & 0xFF);
+ ch7024_write_reg(CH7024_CLK_N3, ch_clk->N & 0xFF);
+ /* set T register */
+ ch7024_write_reg(CH7024_CLK_T, ch_clk->T & 0xFF);
+
+ /* set sub-carrier frequency generation method */
+ ch7024_write_reg(CH7024_ACIV, 0x00); /* ACIV = 0, automatical SCF */
+ /* TV out pattern and DAC switch */
+ ch7024_write_reg(CH7024_OUT_FMT, (0x10 | ch_timing->VOS) & 0xFF);
+
+ /* input settings */
+ /* input format, RGB666 */
+ ch7024_write_reg(CH7024_IDF2, 0x02);
+ /* HAI/HTI VAI */
+ ch7024_write_reg(CH7024_IN_TIMING1, ((ch_timing->HTI >> 5) & 0x38) |
+ ((ch_timing->HAI >> 8) & 0x07));
+ ch7024_write_reg(CH7024_IN_TIMING2, ch_timing->HAI & 0xFF);
+ ch7024_write_reg(CH7024_IN_TIMING8, ch_timing->VAI & 0xFF);
+ /* HTI VTI */
+ ch7024_write_reg(CH7024_IN_TIMING3, ch_timing->HTI & 0xFF);
+ ch7024_write_reg(CH7024_IN_TIMING9, ch_timing->VTI & 0xFF);
+ /* HW/HO(h) VW */
+ ch7024_write_reg(CH7024_IN_TIMING4, ((ch_timing->HW >> 5) & 0x18) |
+ ((ch_timing->HO >> 8) & 0x7));
+ ch7024_write_reg(CH7024_IN_TIMING6, ch_timing->HW & 0xFF);
+ ch7024_write_reg(CH7024_IN_TIMING11, ch_timing->VW & 0x3F);
+ /* HO(l) VO/VAI/VTI */
+ ch7024_write_reg(CH7024_IN_TIMING5, ch_timing->HO & 0xFF);
+ ch7024_write_reg(CH7024_IN_TIMING7, ((ch_timing->VO >> 4) & 0x30) |
+ ((ch_timing->VTI >> 6) & 0x0C) |
+ ((ch_timing->VAI >> 8) & 0x03));
+ ch7024_write_reg(CH7024_IN_TIMING10, ch_timing->VO & 0xFF);
+
+ /* adjust the brightness */
+ ch7024_write_reg(CH7024_TVBRI, 0x90);
+
+ ch7024_write_reg(CH7024_OUT_TIMING1, 0x4);
+ ch7024_write_reg(CH7024_OUT_TIMING2, 0xe0);
+
+ if (vos == TVOUT_FMT_PAL) {
+ ch7024_write_reg(CH7024_V_POS1, 0x03);
+ ch7024_write_reg(CH7024_V_POS2, 0x7d);
+ } else {
+ ch7024_write_reg(CH7024_V_POS1, 0x02);
+ ch7024_write_reg(CH7024_V_POS2, 0x7b);
+ }
+
+ ch7024_write_reg(CH7024_POWER, 0x00);
+
+#ifdef DEBUG_CH7024
+ for (i = 0; i < CH7024_SC_FREQ4; i++) {
+
+ val = ch7024_read_reg(i);
+ pr_debug("CH7024, reg[0x%x] = %x\n", i, val);
+ }
+#endif
+ return 0;
+}
+
+/**
+ * ch7024_enable
+ * Enable the ch7024 Power to begin TV encoder
+ */
+static int ch7024_enable(void)
+{
+ int en = enabled;
+
+ if (!enabled) {
+ regulator_enable(core_reg);
+ regulator_enable(io_reg);
+ regulator_enable(analog_reg);
+ msleep(200);
+ enabled = 1;
+ ch7024_write_reg(CH7024_POWER, 0x00);
+ pr_debug("CH7024 power on.\n");
+ }
+ return en;
+}
+
+/**
+ * ch7024_disable
+ * Disable the ch7024 Power to stop TV encoder
+ */
+static void ch7024_disable(void)
+{
+ if (enabled) {
+ enabled = 0;
+ ch7024_write_reg(CH7024_POWER, 0x0D);
+ regulator_disable(analog_reg);
+ regulator_disable(io_reg);
+ regulator_disable(core_reg);
+ pr_debug("CH7024 power off.\n");
+ }
+}
+
+static int ch7024_detect(void)
+{
+ int en;
+ int detect = 0;
+
+ if (gpio_get_value(detect_gpio) == 1) {
+ set_irq_type(ch7024_client->irq, IRQF_TRIGGER_FALLING);
+
+ en = ch7024_enable();
+
+ ch7024_write_reg(CH7024_DAC_TRIM, 0xB4);
+ msleep(50);
+ detect = ch7024_read_reg(CH7024_ATT_DISP) & 0x3;
+ ch7024_write_reg(CH7024_DAC_TRIM, 0x34);
+
+ if (!en)
+ ch7024_disable();
+ } else {
+ set_irq_type(ch7024_client->irq, IRQF_TRIGGER_RISING);
+ }
+ dev_dbg(&ch7024_client->dev, "detect = %d\n", detect);
+ return detect;
+}
+
+static irqreturn_t hp_detect_handler(int irq, void *data)
+{
+ disable_irq(irq);
+ schedule_delayed_work(&ch7024_wq, 50);
+
+ return IRQ_HANDLED;
+}
+
+static void hp_detect_wq_handler(struct work_struct *work)
+{
+ int detect;
+ struct mxc_hw_event event = { HWE_PHONEJACK_PLUG, 0 };
+
+ detect = ch7024_detect();
+
+ enable_irq(ch7024_client->irq);
+
+ sysfs_notify(&ch7024_client->dev.kobj, NULL, "headphone");
+
+ /* send hw event by netlink */
+ event.args = detect;
+ hw_event_send(1, &event);
+}
+
+int ch7024_fb_event(struct notifier_block *nb, unsigned long val, void *v)
+{
+ struct fb_event *event = v;
+ struct fb_info *fbi = event->info;
+
+ switch (val) {
+ case FB_EVENT_FB_REGISTERED:
+ if ((ch7024_fbi != NULL) || strcmp(fbi->fix.id, "DISP3 BG"))
+ break;
+
+ ch7024_fbi = fbi;
+ fb_add_videomode(&video_modes[0], &ch7024_fbi->modelist);
+ fb_add_videomode(&video_modes[1], &ch7024_fbi->modelist);
+ break;
+ case FB_EVENT_MODE_CHANGE:
+ if (ch7024_fbi != fbi)
+ break;
+
+ if (!fbi->mode) {
+ ch7024_disable();
+ ch7024_cur_mode = TVOUT_FMT_OFF;
+ return 0;
+ }
+
+ if (fb_mode_is_equal(fbi->mode, &video_modes[0])) {
+ ch7024_cur_mode = TVOUT_FMT_NTSC;
+ ch7024_enable();
+ ch7024_setup(TVOUT_FMT_NTSC);
+ } else if (fb_mode_is_equal(fbi->mode, &video_modes[1])) {
+ ch7024_cur_mode = TVOUT_FMT_PAL;
+ ch7024_enable();
+ ch7024_setup(TVOUT_FMT_PAL);
+ } else {
+ ch7024_disable();
+ ch7024_cur_mode = TVOUT_FMT_OFF;
+ return 0;
+ }
+ break;
+ case FB_EVENT_BLANK:
+ if ((ch7024_fbi != fbi) || (ch7024_cur_mode == TVOUT_FMT_OFF))
+ return 0;
+
+ if (*((int *)event->data) == FB_BLANK_UNBLANK) {
+ ch7024_enable();
+ ch7024_setup(ch7024_cur_mode);
+ } else {
+ ch7024_disable();
+ }
+ break;
+ }
+ return 0;
+}
+
+static struct notifier_block nb = {
+ .notifier_call = ch7024_fb_event,
+};
+
+static ssize_t show_headphone(struct device_driver *dev, char *buf)
+{
+ int detect;
+
+ detect = ch7024_detect();
+
+ if (detect == 0) {
+ strcpy(buf, "none\n");
+ } else if (detect == 1) {
+ strcpy(buf, "cvbs\n");
+ } else {
+ strcpy(buf, "headset\n");
+ }
+
+ return strlen(buf);
+}
+
+DRIVER_ATTR(headphone, 0644, show_headphone, NULL);
+
+static ssize_t show_brightness(struct device_driver *dev, char *buf)
+{
+ u32 reg;
+ reg = ch7024_read_reg(CH7024_TVBRI);
+ return snprintf(buf, PAGE_SIZE, "%u", reg);
+}
+
+static ssize_t store_brightness(struct device_driver *dev, const char *buf,
+ size_t count)
+{
+ char *endp;
+ int brightness = simple_strtoul(buf, &endp, 0);
+ size_t size = endp - buf;
+
+ if (*endp && isspace(*endp))
+ size++;
+ if (size != count)
+ return -EINVAL;
+
+ if (brightness > 255)
+ brightness = 255;
+
+ ch7024_write_reg(CH7024_TVBRI, brightness);
+
+ return count;
+}
+
+DRIVER_ATTR(brightness, 0644, show_brightness, store_brightness);
+
+static ssize_t show_contrast(struct device_driver *dev, char *buf)
+{
+ u32 reg;
+ reg = ch7024_read_reg(CH7024_TVCTA);
+
+ reg *= 2; /* Scale to 0 - 255 */
+
+ return snprintf(buf, PAGE_SIZE, "%u", reg);
+}
+
+static ssize_t store_contrast(struct device_driver *dev, const char *buf,
+ size_t count)
+{
+ char *endp;
+ int contrast = simple_strtoul(buf, &endp, 0);
+ size_t size = endp - buf;
+
+ if (*endp && isspace(*endp))
+ size++;
+ if (size != count)
+ return -EINVAL;
+
+ contrast /= 2;
+ if (contrast > 127)
+ contrast = 127;
+
+ ch7024_write_reg(CH7024_TVCTA, contrast);
+
+ return count;
+}
+
+DRIVER_ATTR(contrast, 0644, show_contrast, store_contrast);
+
+static ssize_t show_hue(struct device_driver *dev, char *buf)
+{
+ u32 reg;
+ reg = ch7024_read_reg(CH7024_TVHUE);
+
+ reg *= 2; /* Scale to 0 - 255 */
+
+ return snprintf(buf, PAGE_SIZE, "%u", reg);
+}
+
+static ssize_t store_hue(struct device_driver *dev, const char *buf,
+ size_t count)
+{
+ char *endp;
+ int hue = simple_strtoul(buf, &endp, 0);
+ size_t size = endp - buf;
+
+ if (*endp && isspace(*endp))
+ size++;
+ if (size != count)
+ return -EINVAL;
+
+ hue /= 2;
+ if (hue > 127)
+ hue = 127;
+
+ ch7024_write_reg(CH7024_TVHUE, hue);
+
+ return count;
+}
+
+DRIVER_ATTR(hue, 0644, show_hue, store_hue);
+
+static ssize_t show_saturation(struct device_driver *dev, char *buf)
+{
+ u32 reg;
+ reg = ch7024_read_reg(CH7024_TVSAT);
+
+ reg *= 2; /* Scale to 0 - 255 */
+
+ return snprintf(buf, PAGE_SIZE, "%u", reg);
+}
+
+static ssize_t store_saturation(struct device_driver *dev, const char *buf,
+ size_t count)
+{
+ char *endp;
+ int saturation = simple_strtoul(buf, &endp, 0);
+ size_t size = endp - buf;
+
+ if (*endp && isspace(*endp))
+ size++;
+ if (size != count)
+ return -EINVAL;
+
+ saturation /= 2;
+ if (saturation > 127)
+ saturation = 127;
+
+ ch7024_write_reg(CH7024_TVSAT, saturation);
+
+ return count;
+}
+
+DRIVER_ATTR(saturation, 0644, show_saturation, store_saturation);
+
+static ssize_t show_sharpness(struct device_driver *dev, char *buf)
+{
+ u32 reg;
+ reg = ch7024_read_reg(CH7024_TVSHARP);
+
+ reg *= 32; /* Scale to 0 - 255 */
+
+ return snprintf(buf, PAGE_SIZE, "%u", reg);
+}
+
+static ssize_t store_sharpness(struct device_driver *dev, const char *buf,
+ size_t count)
+{
+ char *endp;
+ int sharpness = simple_strtoul(buf, &endp, 0);
+ size_t size = endp - buf;
+
+ if (*endp && isspace(*endp))
+ size++;
+ if (size != count)
+ return -EINVAL;
+
+ sharpness /= 32; /* Scale to 0 - 7 */
+ if (sharpness > 7)
+ sharpness = 7;
+
+ ch7024_write_reg(CH7024_TVSHARP, sharpness);
+
+ return count;
+}
+
+DRIVER_ATTR(sharpness, 0644, show_sharpness, store_sharpness);
+
+static int ch7024_probe(struct i2c_client *client, const struct i2c_device_id *dev_id)
+{
+ int ret, i;
+ u32 id;
+ u32 irqtype;
+ struct mxc_tvout_platform_data *plat_data = client->dev.platform_data;
+
+ ch7024_client = client;
+
+ io_reg = regulator_get(&client->dev, plat_data->io_reg);
+ core_reg = regulator_get(&client->dev, plat_data->core_reg);
+ analog_reg = regulator_get(&client->dev, plat_data->analog_reg);
+
+ regulator_enable(io_reg);
+ regulator_enable(core_reg);
+ regulator_enable(analog_reg);
+ msleep(200);
+
+ id = ch7024_read_reg(CH7024_DEVID);
+
+ regulator_disable(core_reg);
+ regulator_disable(io_reg);
+ regulator_disable(analog_reg);
+
+ if (id < 0 || id != CH7024_DEVICE_ID) {
+ printk(KERN_ERR
+ "ch7024: TV encoder not present: id = %x\n", id);
+ return -ENODEV;
+ }
+ printk(KERN_ERR "ch7024: TV encoder present: id = %x\n", id);
+
+ detect_gpio = plat_data->detect_line;
+
+ if (client->irq > 0) {
+ if (ch7024_detect() == 0)
+ irqtype = IRQF_TRIGGER_RISING;
+ else
+ irqtype = IRQF_TRIGGER_FALLING;
+
+ ret = request_irq(client->irq, hp_detect_handler, irqtype,
+ client->name, client);
+ if (ret < 0)
+ goto err0;
+
+ ret = driver_create_file(&client->driver->driver,
+ &driver_attr_headphone);
+ if (ret < 0)
+ goto err1;
+ }
+
+ ret = driver_create_file(&client->driver->driver,
+ &driver_attr_brightness);
+ if (ret)
+ goto err2;
+
+ ret = driver_create_file(&client->driver->driver,
+ &driver_attr_contrast);
+ if (ret)
+ goto err3;
+ ret = driver_create_file(&client->driver->driver, &driver_attr_hue);
+ if (ret)
+ goto err4;
+ ret = driver_create_file(&client->driver->driver,
+ &driver_attr_saturation);
+ if (ret)
+ goto err5;
+ ret = driver_create_file(&client->driver->driver,
+ &driver_attr_sharpness);
+ if (ret)
+ goto err6;
+
+ for (i = 0; i < num_registered_fb; i++) {
+ if (strcmp(registered_fb[i]->fix.id, "DISP3 BG") == 0) {
+ ch7024_fbi = registered_fb[i];
+ break;
+ }
+ }
+ if (ch7024_fbi != NULL) {
+ fb_add_videomode(&video_modes[0], &ch7024_fbi->modelist);
+ fb_add_videomode(&video_modes[1], &ch7024_fbi->modelist);
+ }
+ fb_register_client(&nb);
+
+ return 0;
+ err6:
+ driver_remove_file(&client->driver->driver, &driver_attr_saturation);
+ err5:
+ driver_remove_file(&client->driver->driver, &driver_attr_hue);
+ err4:
+ driver_remove_file(&client->driver->driver, &driver_attr_contrast);
+ err3:
+ driver_remove_file(&client->driver->driver, &driver_attr_brightness);
+ err2:
+ driver_remove_file(&client->driver->driver, &driver_attr_headphone);
+ err1:
+ free_irq(client->irq, client);
+ err0:
+ return ret;
+}
+
+static int ch7024_remove(struct i2c_client *client)
+{
+ free_irq(client->irq, client);
+
+ regulator_put(io_reg);
+ regulator_put(core_reg);
+ regulator_put(analog_reg);
+
+ driver_remove_file(&client->driver->driver, &driver_attr_headphone);
+ driver_remove_file(&client->driver->driver, &driver_attr_brightness);
+ driver_remove_file(&client->driver->driver, &driver_attr_contrast);
+ driver_remove_file(&client->driver->driver, &driver_attr_hue);
+ driver_remove_file(&client->driver->driver, &driver_attr_saturation);
+ driver_remove_file(&client->driver->driver, &driver_attr_sharpness);
+
+ fb_unregister_client(&nb);
+
+ ch7024_client = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/*!
+ * PM suspend/resume routing
+ */
+static int ch7024_suspend(struct i2c_client *client, pm_message_t state)
+{
+ pr_debug("Ch7024 suspend routing..\n");
+ if (enabled) {
+ ch7024_disable();
+ pm_status = 1;
+ } else {
+ pm_status = 0;
+ }
+ return 0;
+}
+
+static int ch7024_resume(struct i2c_client *client)
+{
+ pr_debug("Ch7024 resume routing..\n");
+ if (pm_status) {
+ ch7024_enable();
+ ch7024_setup(ch7024_cur_mode);
+ }
+ return 0;
+}
+#else
+#define ch7024_suspend NULL
+#define ch7024_resume NULL
+#endif
+
+static const struct i2c_device_id ch7024_id[] = {
+ { "ch7024", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, ch7024_id);
+
+static struct i2c_driver ch7024_driver = {
+ .driver = {
+ .name = "ch7024",
+ },
+ .probe = ch7024_probe,
+ .remove = ch7024_remove,
+ .suspend = ch7024_suspend,
+ .resume = ch7024_resume,
+ .id_table = ch7024_id,
+};
+
+static int __init ch7024_init(void)
+{
+ return i2c_add_driver(&ch7024_driver);
+}
+
+static void __exit ch7024_exit(void)
+{
+ i2c_del_driver(&ch7024_driver);
+}
+
+module_init(ch7024_init);
+module_exit(ch7024_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("CH7024 TV encoder driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/mxc/eink_processing2.c b/drivers/video/mxc/eink_processing2.c
new file mode 100644
index 00000000..bb814793
--- /dev/null
+++ b/drivers/video/mxc/eink_processing2.c
@@ -0,0 +1,103 @@
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/mxcfb.h>
+#include <linux/mxcfb_epdc_kernel.h>
+
+
+int do_aa_processing_v2_2_1(
+ unsigned short *working_buffer_ptr_in,
+ unsigned short *working_buffer_ptr_out,
+ struct mxcfb_rect *update_region,
+ long working_buffer_width,
+ long working_buffer_height)
+{
+ return -1;
+}
+int do_aad_processing_v2_1_1(
+ unsigned short *working_buffer_ptr_in,
+ unsigned short *working_buffer_ptr_out,
+ struct mxcfb_rect *update_region,
+ long working_buffer_width,
+ long working_buffer_height)
+{
+ return -1;
+}
+int do_aa_processing_v2_2_0(
+ unsigned char *update_region_ptr, struct mxcfb_rect *update_region,
+ unsigned long update_region_stride, unsigned short *working_buffer_ptr,
+ long working_buffer_width, long working_buffer_height)
+{
+ return -1;
+}
+int do_aad_processing_v2_1_0(
+ unsigned char *update_region_ptr, struct mxcfb_rect *update_region,
+ unsigned long update_region_stride, unsigned short *working_buffer_ptr,
+ long working_buffer_width, long working_buffer_height)
+{
+ return -1;
+}
+void set_aad_update_counter(unsigned long value)
+{
+ return ;
+}
+
+int mxc_epdc_fb_prep_algorithm_data( unsigned char *waveform_acd_buffer,
+ unsigned wMode,
+ unsigned wTempRange,
+ unsigned waveform_mc,
+ unsigned waveform_trc,
+ unsigned waveform_magic_number,
+ unsigned ctrlFlags)
+{
+ return 0;
+}
+
+/* function for handing the extra waveform information */
+int mxc_epdc_fb_fetch_wxi_data( unsigned char *waveform_xwi_buffer, char *xwiBuffer)
+{
+ if (xwiBuffer != NULL) {
+ memcpy(xwiBuffer, &waveform_xwi_buffer[1], (size_t) waveform_xwi_buffer[0]);
+ }
+ return (int) waveform_xwi_buffer[0];
+}
+
+/* function for copying the voltage control data to the allocated buffer */
+int mxc_epdc_fb_fetch_vc_data( unsigned char *waveform_vcd_buffer,
+ unsigned wMode,
+ unsigned wTempRange,
+ unsigned waveform_mc,
+ unsigned waveform_trc,
+ unsigned char *VoltageCtrlData)
+{
+ if ((wMode >= waveform_mc) || (wTempRange >= waveform_trc))
+ return -1;
+ /* copy the waveform voltage control data */
+ memcpy(VoltageCtrlData, waveform_vcd_buffer + 16 * (wMode * waveform_trc + wTempRange),16);
+ {
+ unsigned i;
+ unsigned char cs = 0;
+ for (i=0; i< 15; i++)
+ cs += VoltageCtrlData[i];
+ if (cs != VoltageCtrlData[15]) return -1;
+ }
+ return 0;
+}
+
+
+
+/* About advance algorithms in this device driver */
+
+int mxc_epdc_fb_aa_info( void )
+{
+#if REAGL_ENABLED
+ printk(" This version contains GLR16 and GLD16 advance algorithms.\n");
+ return 2;
+#else
+ printk(" This version contains no advance algorithm.\n");
+ return 0;
+#endif
+}
+
diff --git a/drivers/video/mxc/eink_processing2.h b/drivers/video/mxc/eink_processing2.h
new file mode 100644
index 00000000..7278d7ac
--- /dev/null
+++ b/drivers/video/mxc/eink_processing2.h
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2012 - 2013 Freescale Semiconductor, Inc. All Rights Reserved.
+ * THIS SOURCE CODE IS CONFIDENTIAL AND PROPRIETARY AND MAY NOT
+ * BE USED OR DISTRIBUTED WITHOUT THE WRITTEN PERMISSION OF
+ * Freescale Semiconductor, Inc.
+ */
+
+#ifndef __EINK_PROCESSING_H__
+#define __EINK_PROCESSING_H__
+
+#include <linux/mxcfb.h>
+
+#define WAVEFORM_SET_ALGO_DATA 1
+#define WAVEFORM_RESTORE_DEFAULT_PMM 2
+
+/*
+ * select a version according to SoCs on the platform
+ * i.MX6SL has Cortex A9 core
+ * i.MX508 has Cortex A8 core
+ * The algorithms are not compatible
+ * so use care to select proper lib.1_shipped for the correct core
+ * for the target platform.
+ */
+#undef IMX6SL_AA
+#define IMX508_AA
+
+/*
+ * E-ink Gen II waveform algorithm aa implementation - version 2.2.1
+ *
+ * Function Name:
+ * int do_aa_processing ( .. )
+ * input parameters:
+ * unsigned short *working_buffer_ptr_in,
+ * unsigned short *working_buffer_ptr_out,
+ * struct mxcfb_rect *update_region,
+ * long working_buffer_width
+ * long working_buffer_height
+ * return:
+ * 0 - normal exit
+ * -1 - no advance algorithm
+ */
+
+int do_aa_processing_v2_2_1(
+ unsigned short *working_buffer_ptr_in,
+ unsigned short *working_buffer_ptr_out,
+ struct mxcfb_rect *update_region,
+ long working_buffer_width,
+ long working_buffer_height);
+
+/*
+ * E-ink Gen II waveform algorithm AA-D implementation version 2.1.1
+ * for i.MX50 (508: 86ms for 1024x768, 52ms for 800x600)
+ * Function Name:
+ * int do_aad_processing ( .. )
+ * input parameters:
+ * unsigned short *working_buffer_ptr_in,
+ * unsigned short *working_buffer_ptr_out,
+ * struct mxcfb_rect *update_region,
+ * long working_buffer_width
+ * long working_buffer_height
+ * return:
+ * 0 - normal exit
+ * -1 - no advance algorithm
+ */
+
+int do_aad_processing_v2_1_1(
+ unsigned short *working_buffer_ptr_in,
+ unsigned short *working_buffer_ptr_out,
+ struct mxcfb_rect *update_region,
+ long working_buffer_width,
+ long working_buffer_height);
+/*
+ * E-ink Gen II waveform algorithm aa implementation - version 2.2.0
+ *
+ * Function Name:
+ * int do_aa_processing( .. )
+ * input parameters:
+ * unsigned char* update_region_ptr
+ * struct mxcfb_rect* update_region
+ * unsigned long update_region_stride
+ * unsigned short* working_buffer_ptr
+ * long working_buffer_width
+ * long working_buffer_height
+ * output:
+ * unsigned char* update_region_ptr
+ * return:
+ * 0 - normal exit
+ * -1 - no advance algorithm
+ */
+
+int do_aa_processing_v2_2_0(
+ unsigned char *update_region_ptr, struct mxcfb_rect *update_region,
+ unsigned long update_region_stride, unsigned short *working_buffer_ptr,
+ long working_buffer_width, long working_buffer_height);
+
+
+/*
+ * E-ink Gen II waveform algorithm AA-D implementation version 2.1.0
+ * Function Name:
+ * int do_aad_processing( .. )
+ * input parameters:
+ * unsigned char* update_region_ptr
+ * struct mxcfb_rect* update_region
+ * unsigned long update_region_stride
+ * unsigned short* working_buffer_ptr
+ * long working_buffer_width
+ * long working_buffer_height
+ * output parameters:
+ * unsigned char* update_region_ptr
+ * return:
+ * 0 - normal exit
+ * -1 - no advance algorithm
+ */
+
+int do_aad_processing_v2_1_0(
+ unsigned char *update_region_ptr, struct mxcfb_rect *update_region,
+ unsigned long update_region_stride, unsigned short *working_buffer_ptr,
+ long working_buffer_width, long working_buffer_height);
+
+/*
+ * API for setting the update counter
+ * Function Name:
+ * void set_aad_update_counter( .. )
+ * input parameters:
+ * unsigned long value
+ * return:
+ * none
+ */
+void set_aad_update_counter(unsigned long value);
+
+/*
+ * Function for fetching the voltage control data for EPDC PMIC
+ * name: int mxc_epdc_fb_fetch_vc_data ( .. )
+ * return: 0 - normal exit, -1 - invalide waveform mode or invalid temperature range, -2 - data checksum error
+ * parameters: *waveform_vcd_buffer holds the voltage control data table for waveform modes, typically fb_data->waveform_vcd_buffer
+ * wMode - waveform mode for the interested vc data
+ * wTempRange - specific temperature range of the waveform mode for vc data
+ * waveform_mc - the number of waveform modes in the waveform file, typically it is fb_data-waveform_mc
+ * waveform_trc - the number of temperature ranges in each waveform mode, typically fb_data->waveform_trc
+ * *VoltageCtrlData - 16-byte data array for returning vc data
+ */
+int mxc_epdc_fb_fetch_vc_data( unsigned char *waveform_vcd_buffer,
+ unsigned wMode,
+ unsigned wTempRange,
+ unsigned waveform_mc,
+ unsigned waveform_trc,
+ unsigned char *VoltageCtrlData);
+
+/*
+ * function for extracting the extra waveform information data (usually the waveform name)
+ * name: int mxc_epdc_fb_fetch_wxi_data(( .. )
+ * return: number of bytes in the extra information
+ * parameters: *waveform_xwi_buffer holds the extra waveform data, typically fb_data->waveform_xwi_buffer
+ * *ExtraWaveformString - NULL if no buffer allocated, the function will return the number of bytes in string
+ * and it can be used for allocating the string buffer
+ * Note: typically fetching the extra waveform information requires two function calls.
+ * The first call with NULL pointer to get the length of the information then allocate a string buffer with one byte larger than the size of the xwi
+ * The second call with the allocated buffer to get the data then set the null termination for completing the string.
+ */
+int mxc_epdc_fb_fetch_wxi_data( unsigned char *waveform_xwi_buffer,
+ char *ExtraWaveformString);
+
+/*
+ * Function for preparing the advance algorithm control data
+ * name: int mxc_epdc_fb_prep_algorithm_data( ( .. )
+ * return: 0 - no advance algorithm data
+ * 1 - version 1 algorithm data (AA algorithm)
+ * 2 - version 2 algorithm data (AA/AA-D)
+ * -1 - no advance waveform support algorithms in this version of library
+ * -2 - invalid waveform mode or invalid temperature range
+ * < -2 values - data checksum errors
+ * parameters: *waveform_acd_buffer holds the algorithm control data table for waveform modes, typically fb_data->waveform_acd_buffer
+ * wMode - waveform mode for the interested vc data
+ * wTempRange - specific temperature range of the waveform mode for vc data
+ * waveform_mc - the number of waveform modes in the waveform file, typically it is fb_data-waveform_mc
+ * waveform_trc - the number of temperature ranges in each waveform mode, typically fb_data->waveform_trc
+ * waveform_magic_number - algorithm data code from the waveform file, typically fb_data->waveform_magic_number
+ * ctrlFlags - 0 just look up the version of the algorithm data,
+ * 1 - transfer the data to be used by algorithm if defined,
+ * 3 - same as 1 but restore the default data if there is no algorithm data.
+ */
+int mxc_epdc_fb_prep_algorithm_data( unsigned char *waveform_acd_buffer,
+ unsigned wMode,
+ unsigned wTempRange,
+ unsigned waveform_mc,
+ unsigned waveform_trc,
+ unsigned waveform_magic_number,
+ unsigned ctrlFlags);
+
+/* Function about advance algoritms in this device driver
+ * name: int mxc_epdc_fb_aa_info( void )
+ * return: number of advance algorithms built into this device driver
+ * parameters: none
+ */
+int mxc_epdc_fb_aa_info( void );
+
+#endif
diff --git a/drivers/video/mxc/elcdif_regs.h b/drivers/video/mxc/elcdif_regs.h
new file mode 100644
index 00000000..42e377de
--- /dev/null
+++ b/drivers/video/mxc/elcdif_regs.h
@@ -0,0 +1,678 @@
+/*
+ * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+/*
+ * Based on arch/arm/mach-mx28/include/mach/regs-lcdif.h.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+#ifndef __ELCDIF_REGS_INCLUDED_
+#define __ELCDIF_REGS_INCLUDED_
+
+#define HW_ELCDIF_CTRL (0x00000000)
+#define HW_ELCDIF_CTRL_SET (0x00000004)
+#define HW_ELCDIF_CTRL_CLR (0x00000008)
+#define HW_ELCDIF_CTRL_TOG (0x0000000c)
+
+#define BM_ELCDIF_CTRL_SFTRST 0x80000000
+#define BM_ELCDIF_CTRL_CLKGATE 0x40000000
+#define BM_ELCDIF_CTRL_YCBCR422_INPUT 0x20000000
+#define BM_ELCDIF_CTRL_READ_WRITEB 0x10000000
+#define BM_ELCDIF_CTRL_WAIT_FOR_VSYNC_EDGE 0x08000000
+#define BM_ELCDIF_CTRL_DATA_SHIFT_DIR 0x04000000
+#define BV_ELCDIF_CTRL_DATA_SHIFT_DIR__TXDATA_SHIFT_LEFT 0x0
+#define BV_ELCDIF_CTRL_DATA_SHIFT_DIR__TXDATA_SHIFT_RIGHT 0x1
+#define BP_ELCDIF_CTRL_SHIFT_NUM_BITS 21
+#define BM_ELCDIF_CTRL_SHIFT_NUM_BITS 0x03E00000
+#define BF_ELCDIF_CTRL_SHIFT_NUM_BITS(v) \
+ (((v) << 21) & BM_ELCDIF_CTRL_SHIFT_NUM_BITS)
+#define BM_ELCDIF_CTRL_DVI_MODE 0x00100000
+#define BM_ELCDIF_CTRL_BYPASS_COUNT 0x00080000
+#define BM_ELCDIF_CTRL_VSYNC_MODE 0x00040000
+#define BM_ELCDIF_CTRL_DOTCLK_MODE 0x00020000
+#define BM_ELCDIF_CTRL_DATA_SELECT 0x00010000
+#define BV_ELCDIF_CTRL_DATA_SELECT__CMD_MODE 0x0
+#define BV_ELCDIF_CTRL_DATA_SELECT__DATA_MODE 0x1
+#define BP_ELCDIF_CTRL_INPUT_DATA_SWIZZLE 14
+#define BM_ELCDIF_CTRL_INPUT_DATA_SWIZZLE 0x0000C000
+#define BF_ELCDIF_CTRL_INPUT_DATA_SWIZZLE(v) \
+ (((v) << 14) & BM_ELCDIF_CTRL_INPUT_DATA_SWIZZLE)
+#define BV_ELCDIF_CTRL_INPUT_DATA_SWIZZLE__NO_SWAP 0x0
+#define BV_ELCDIF_CTRL_INPUT_DATA_SWIZZLE__LITTLE_ENDIAN 0x0
+#define BV_ELCDIF_CTRL_INPUT_DATA_SWIZZLE__BIG_ENDIAN_SWAP 0x1
+#define BV_ELCDIF_CTRL_INPUT_DATA_SWIZZLE__SWAP_ALL_BYTES 0x1
+#define BV_ELCDIF_CTRL_INPUT_DATA_SWIZZLE__HWD_SWAP 0x2
+#define BV_ELCDIF_CTRL_INPUT_DATA_SWIZZLE__HWD_BYTE_SWAP 0x3
+#define BP_ELCDIF_CTRL_CSC_DATA_SWIZZLE 12
+#define BM_ELCDIF_CTRL_CSC_DATA_SWIZZLE 0x00003000
+#define BF_ELCDIF_CTRL_CSC_DATA_SWIZZLE(v) \
+ (((v) << 12) & BM_ELCDIF_CTRL_CSC_DATA_SWIZZLE)
+#define BV_ELCDIF_CTRL_CSC_DATA_SWIZZLE__NO_SWAP 0x0
+#define BV_ELCDIF_CTRL_CSC_DATA_SWIZZLE__LITTLE_ENDIAN 0x0
+#define BV_ELCDIF_CTRL_CSC_DATA_SWIZZLE__BIG_ENDIAN_SWAP 0x1
+#define BV_ELCDIF_CTRL_CSC_DATA_SWIZZLE__SWAP_ALL_BYTES 0x1
+#define BV_ELCDIF_CTRL_CSC_DATA_SWIZZLE__HWD_SWAP 0x2
+#define BV_ELCDIF_CTRL_CSC_DATA_SWIZZLE__HWD_BYTE_SWAP 0x3
+#define BP_ELCDIF_CTRL_LCD_DATABUS_WIDTH 10
+#define BM_ELCDIF_CTRL_LCD_DATABUS_WIDTH 0x00000C00
+#define BF_ELCDIF_CTRL_LCD_DATABUS_WIDTH(v) \
+ (((v) << 10) & BM_ELCDIF_CTRL_LCD_DATABUS_WIDTH)
+#define BV_ELCDIF_CTRL_LCD_DATABUS_WIDTH__16_BIT 0x0
+#define BV_ELCDIF_CTRL_LCD_DATABUS_WIDTH__8_BIT 0x1
+#define BV_ELCDIF_CTRL_LCD_DATABUS_WIDTH__18_BIT 0x2
+#define BV_ELCDIF_CTRL_LCD_DATABUS_WIDTH__24_BIT 0x3
+#define BP_ELCDIF_CTRL_WORD_LENGTH 8
+#define BM_ELCDIF_CTRL_WORD_LENGTH 0x00000300
+#define BF_ELCDIF_CTRL_WORD_LENGTH(v) \
+ (((v) << 8) & BM_ELCDIF_CTRL_WORD_LENGTH)
+#define BV_ELCDIF_CTRL_WORD_LENGTH__16_BIT 0x0
+#define BV_ELCDIF_CTRL_WORD_LENGTH__8_BIT 0x1
+#define BV_ELCDIF_CTRL_WORD_LENGTH__18_BIT 0x2
+#define BV_ELCDIF_CTRL_WORD_LENGTH__24_BIT 0x3
+#define BM_ELCDIF_CTRL_RGB_TO_YCBCR422_CSC 0x00000080
+#define BM_ELCDIF_CTRL_ENABLE_PXP_HANDSHAKE 0x00000040
+#define BM_ELCDIF_CTRL_ELCDIF_MASTER 0x00000020
+#define BM_ELCDIF_CTRL_RSRVD0 0x00000010
+#define BM_ELCDIF_CTRL_DATA_FORMAT_16_BIT 0x00000008
+#define BM_ELCDIF_CTRL_DATA_FORMAT_18_BIT 0x00000004
+#define BV_ELCDIF_CTRL_DATA_FORMAT_18_BIT__LOWER_18_BITS_VALID 0x0
+#define BV_ELCDIF_CTRL_DATA_FORMAT_18_BIT__UPPER_18_BITS_VALID 0x1
+#define BM_ELCDIF_CTRL_DATA_FORMAT_24_BIT 0x00000002
+#define BV_ELCDIF_CTRL_DATA_FORMAT_24_BIT__ALL_24_BITS_VALID 0x0
+#define BV_ELCDIF_CTRL_DATA_FORMAT_24_BIT__DROP_UPPER_2_BITS_PER_BYTE 0x1
+#define BM_ELCDIF_CTRL_RUN 0x00000001
+
+#define HW_ELCDIF_CTRL1 (0x00000010)
+#define HW_ELCDIF_CTRL1_SET (0x00000014)
+#define HW_ELCDIF_CTRL1_CLR (0x00000018)
+#define HW_ELCDIF_CTRL1_TOG (0x0000001c)
+
+#define BP_ELCDIF_CTRL1_RSRVD1 28
+#define BM_ELCDIF_CTRL1_RSRVD1 0xF0000000
+#define BF_ELCDIF_CTRL1_RSRVD1(v) \
+ (((v) << 28) & BM_ELCDIF_CTRL1_RSRVD1)
+#define BM_ELCDIF_CTRL1_COMBINE_MPU_WR_STRB 0x08000000
+#define BM_ELCDIF_CTRL1_BM_ERROR_IRQ_EN 0x04000000
+#define BM_ELCDIF_CTRL1_BM_ERROR_IRQ 0x02000000
+#define BV_ELCDIF_CTRL1_BM_ERROR_IRQ__NO_REQUEST 0x0
+#define BV_ELCDIF_CTRL1_BM_ERROR_IRQ__REQUEST 0x1
+#define BM_ELCDIF_CTRL1_RECOVER_ON_UNDERFLOW 0x01000000
+#define BM_ELCDIF_CTRL1_INTERLACE_FIELDS 0x00800000
+#define BM_ELCDIF_CTRL1_START_INTERLACE_FROM_SECOND_FIELD 0x00400000
+#define BM_ELCDIF_CTRL1_FIFO_CLEAR 0x00200000
+#define BM_ELCDIF_CTRL1_IRQ_ON_ALTERNATE_FIELDS 0x00100000
+#define BP_ELCDIF_CTRL1_BYTE_PACKING_FORMAT 16
+#define BM_ELCDIF_CTRL1_BYTE_PACKING_FORMAT 0x000F0000
+#define BF_ELCDIF_CTRL1_BYTE_PACKING_FORMAT(v) \
+ (((v) << 16) & BM_ELCDIF_CTRL1_BYTE_PACKING_FORMAT)
+#define BM_ELCDIF_CTRL1_OVERFLOW_IRQ_EN 0x00008000
+#define BM_ELCDIF_CTRL1_UNDERFLOW_IRQ_EN 0x00004000
+#define BM_ELCDIF_CTRL1_CUR_FRAME_DONE_IRQ_EN 0x00002000
+#define BM_ELCDIF_CTRL1_VSYNC_EDGE_IRQ_EN 0x00001000
+#define BM_ELCDIF_CTRL1_OVERFLOW_IRQ 0x00000800
+#define BV_ELCDIF_CTRL1_OVERFLOW_IRQ__NO_REQUEST 0x0
+#define BV_ELCDIF_CTRL1_OVERFLOW_IRQ__REQUEST 0x1
+#define BM_ELCDIF_CTRL1_UNDERFLOW_IRQ 0x00000400
+#define BV_ELCDIF_CTRL1_UNDERFLOW_IRQ__NO_REQUEST 0x0
+#define BV_ELCDIF_CTRL1_UNDERFLOW_IRQ__REQUEST 0x1
+#define BM_ELCDIF_CTRL1_CUR_FRAME_DONE_IRQ 0x00000200
+#define BV_ELCDIF_CTRL1_CUR_FRAME_DONE_IRQ__NO_REQUEST 0x0
+#define BV_ELCDIF_CTRL1_CUR_FRAME_DONE_IRQ__REQUEST 0x1
+#define BM_ELCDIF_CTRL1_VSYNC_EDGE_IRQ 0x00000100
+#define BV_ELCDIF_CTRL1_VSYNC_EDGE_IRQ__NO_REQUEST 0x0
+#define BV_ELCDIF_CTRL1_VSYNC_EDGE_IRQ__REQUEST 0x1
+#define BP_ELCDIF_CTRL1_RSRVD0 3
+#define BM_ELCDIF_CTRL1_RSRVD0 0x000000F8
+#define BF_ELCDIF_CTRL1_RSRVD0(v) \
+ (((v) << 3) & BM_ELCDIF_CTRL1_RSRVD0)
+#define BM_ELCDIF_CTRL1_BUSY_ENABLE 0x00000004
+#define BV_ELCDIF_CTRL1_BUSY_ENABLE__BUSY_DISABLED 0x0
+#define BV_ELCDIF_CTRL1_BUSY_ENABLE__BUSY_ENABLED 0x1
+#define BM_ELCDIF_CTRL1_MODE86 0x00000002
+#define BV_ELCDIF_CTRL1_MODE86__8080_MODE 0x0
+#define BV_ELCDIF_CTRL1_MODE86__6800_MODE 0x1
+#define BM_ELCDIF_CTRL1_RESET 0x00000001
+#define BV_ELCDIF_CTRL1_RESET__LCDRESET_LOW 0x0
+#define BV_ELCDIF_CTRL1_RESET__LCDRESET_HIGH 0x1
+
+#define HW_ELCDIF_CTRL2 (0x00000020)
+#define HW_ELCDIF_CTRL2_SET (0x00000024)
+#define HW_ELCDIF_CTRL2_CLR (0x00000028)
+#define HW_ELCDIF_CTRL2_TOG (0x0000002c)
+
+#define BP_ELCDIF_CTRL2_RSRVD5 24
+#define BM_ELCDIF_CTRL2_RSRVD5 0xFF000000
+#define BF_ELCDIF_CTRL2_RSRVD5(v) \
+ (((v) << 24) & BM_ELCDIF_CTRL2_RSRVD5)
+#define BP_ELCDIF_CTRL2_OUTSTANDING_REQS 21
+#define BM_ELCDIF_CTRL2_OUTSTANDING_REQS 0x00E00000
+#define BF_ELCDIF_CTRL2_OUTSTANDING_REQS(v) \
+ (((v) << 21) & BM_ELCDIF_CTRL2_OUTSTANDING_REQS)
+#define BV_ELCDIF_CTRL2_OUTSTANDING_REQS__REQ_1 0x0
+#define BV_ELCDIF_CTRL2_OUTSTANDING_REQS__REQ_2 0x1
+#define BV_ELCDIF_CTRL2_OUTSTANDING_REQS__REQ_4 0x2
+#define BV_ELCDIF_CTRL2_OUTSTANDING_REQS__REQ_8 0x3
+#define BV_ELCDIF_CTRL2_OUTSTANDING_REQS__REQ_16 0x4
+#define BM_ELCDIF_CTRL2_BURST_LEN_8 0x00100000
+#define BM_ELCDIF_CTRL2_RSRVD4 0x00080000
+#define BP_ELCDIF_CTRL2_ODD_LINE_PATTERN 16
+#define BM_ELCDIF_CTRL2_ODD_LINE_PATTERN 0x00070000
+#define BF_ELCDIF_CTRL2_ODD_LINE_PATTERN(v) \
+ (((v) << 16) & BM_ELCDIF_CTRL2_ODD_LINE_PATTERN)
+#define BV_ELCDIF_CTRL2_ODD_LINE_PATTERN__RGB 0x0
+#define BV_ELCDIF_CTRL2_ODD_LINE_PATTERN__RBG 0x1
+#define BV_ELCDIF_CTRL2_ODD_LINE_PATTERN__GBR 0x2
+#define BV_ELCDIF_CTRL2_ODD_LINE_PATTERN__GRB 0x3
+#define BV_ELCDIF_CTRL2_ODD_LINE_PATTERN__BRG 0x4
+#define BV_ELCDIF_CTRL2_ODD_LINE_PATTERN__BGR 0x5
+#define BM_ELCDIF_CTRL2_RSRVD3 0x00008000
+#define BP_ELCDIF_CTRL2_EVEN_LINE_PATTERN 12
+#define BM_ELCDIF_CTRL2_EVEN_LINE_PATTERN 0x00007000
+#define BF_ELCDIF_CTRL2_EVEN_LINE_PATTERN(v) \
+ (((v) << 12) & BM_ELCDIF_CTRL2_EVEN_LINE_PATTERN)
+#define BV_ELCDIF_CTRL2_EVEN_LINE_PATTERN__RGB 0x0
+#define BV_ELCDIF_CTRL2_EVEN_LINE_PATTERN__RBG 0x1
+#define BV_ELCDIF_CTRL2_EVEN_LINE_PATTERN__GBR 0x2
+#define BV_ELCDIF_CTRL2_EVEN_LINE_PATTERN__GRB 0x3
+#define BV_ELCDIF_CTRL2_EVEN_LINE_PATTERN__BRG 0x4
+#define BV_ELCDIF_CTRL2_EVEN_LINE_PATTERN__BGR 0x5
+#define BM_ELCDIF_CTRL2_RSRVD2 0x00000800
+#define BM_ELCDIF_CTRL2_READ_PACK_DIR 0x00000400
+#define BM_ELCDIF_CTRL2_READ_MODE_OUTPUT_IN_RGB_FORMAT 0x00000200
+#define BM_ELCDIF_CTRL2_READ_MODE_6_BIT_INPUT 0x00000100
+#define BM_ELCDIF_CTRL2_RSRVD1 0x00000080
+#define BP_ELCDIF_CTRL2_READ_MODE_NUM_PACKED_SUBWORDS 4
+#define BM_ELCDIF_CTRL2_READ_MODE_NUM_PACKED_SUBWORDS 0x00000070
+#define BF_ELCDIF_CTRL2_READ_MODE_NUM_PACKED_SUBWORDS(v) \
+ (((v) << 4) & BM_ELCDIF_CTRL2_READ_MODE_NUM_PACKED_SUBWORDS)
+#define BP_ELCDIF_CTRL2_INITIAL_DUMMY_READ 1
+#define BM_ELCDIF_CTRL2_INITIAL_DUMMY_READ 0x0000000E
+#define BF_ELCDIF_CTRL2_INITIAL_DUMMY_READ(v) \
+ (((v) << 1) & BM_ELCDIF_CTRL2_INITIAL_DUMMY_READ)
+#define BM_ELCDIF_CTRL2_RSRVD0 0x00000001
+
+#define HW_ELCDIF_TRANSFER_COUNT (0x00000030)
+
+#define BP_ELCDIF_TRANSFER_COUNT_V_COUNT 16
+#define BM_ELCDIF_TRANSFER_COUNT_V_COUNT 0xFFFF0000
+#define BF_ELCDIF_TRANSFER_COUNT_V_COUNT(v) \
+ (((v) << 16) & BM_ELCDIF_TRANSFER_COUNT_V_COUNT)
+#define BP_ELCDIF_TRANSFER_COUNT_H_COUNT 0
+#define BM_ELCDIF_TRANSFER_COUNT_H_COUNT 0x0000FFFF
+#define BF_ELCDIF_TRANSFER_COUNT_H_COUNT(v) \
+ (((v) << 0) & BM_ELCDIF_TRANSFER_COUNT_H_COUNT)
+
+#define HW_ELCDIF_CUR_BUF (0x00000040)
+
+#define BP_ELCDIF_CUR_BUF_ADDR 0
+#define BM_ELCDIF_CUR_BUF_ADDR 0xFFFFFFFF
+#define BF_ELCDIF_CUR_BUF_ADDR(v) (v)
+
+#define HW_ELCDIF_NEXT_BUF (0x00000050)
+
+#define BP_ELCDIF_NEXT_BUF_ADDR 0
+#define BM_ELCDIF_NEXT_BUF_ADDR 0xFFFFFFFF
+#define BF_ELCDIF_NEXT_BUF_ADDR(v) (v)
+
+#define HW_ELCDIF_TIMING (0x00000060)
+
+#define BP_ELCDIF_TIMING_CMD_HOLD 24
+#define BM_ELCDIF_TIMING_CMD_HOLD 0xFF000000
+#define BF_ELCDIF_TIMING_CMD_HOLD(v) \
+ (((v) << 24) & BM_ELCDIF_TIMING_CMD_HOLD)
+#define BP_ELCDIF_TIMING_CMD_SETUP 16
+#define BM_ELCDIF_TIMING_CMD_SETUP 0x00FF0000
+#define BF_ELCDIF_TIMING_CMD_SETUP(v) \
+ (((v) << 16) & BM_ELCDIF_TIMING_CMD_SETUP)
+#define BP_ELCDIF_TIMING_DATA_HOLD 8
+#define BM_ELCDIF_TIMING_DATA_HOLD 0x0000FF00
+#define BF_ELCDIF_TIMING_DATA_HOLD(v) \
+ (((v) << 8) & BM_ELCDIF_TIMING_DATA_HOLD)
+#define BP_ELCDIF_TIMING_DATA_SETUP 0
+#define BM_ELCDIF_TIMING_DATA_SETUP 0x000000FF
+#define BF_ELCDIF_TIMING_DATA_SETUP(v) \
+ (((v) << 0) & BM_ELCDIF_TIMING_DATA_SETUP)
+
+#define HW_ELCDIF_VDCTRL0 (0x00000070)
+#define HW_ELCDIF_VDCTRL0_SET (0x00000074)
+#define HW_ELCDIF_VDCTRL0_CLR (0x00000078)
+#define HW_ELCDIF_VDCTRL0_TOG (0x0000007c)
+
+#define BP_ELCDIF_VDCTRL0_RSRVD2 30
+#define BM_ELCDIF_VDCTRL0_RSRVD2 0xC0000000
+#define BF_ELCDIF_VDCTRL0_RSRVD2(v) \
+ (((v) << 30) & BM_ELCDIF_VDCTRL0_RSRVD2)
+#define BM_ELCDIF_VDCTRL0_VSYNC_OEB 0x20000000
+#define BV_ELCDIF_VDCTRL0_VSYNC_OEB__VSYNC_OUTPUT 0x0
+#define BV_ELCDIF_VDCTRL0_VSYNC_OEB__VSYNC_INPUT 0x1
+#define BM_ELCDIF_VDCTRL0_ENABLE_PRESENT 0x10000000
+#define BM_ELCDIF_VDCTRL0_VSYNC_POL 0x08000000
+#define BM_ELCDIF_VDCTRL0_HSYNC_POL 0x04000000
+#define BM_ELCDIF_VDCTRL0_DOTCLK_POL 0x02000000
+#define BM_ELCDIF_VDCTRL0_ENABLE_POL 0x01000000
+#define BP_ELCDIF_VDCTRL0_RSRVD1 22
+#define BM_ELCDIF_VDCTRL0_RSRVD1 0x00C00000
+#define BF_ELCDIF_VDCTRL0_RSRVD1(v) \
+ (((v) << 22) & BM_ELCDIF_VDCTRL0_RSRVD1)
+#define BM_ELCDIF_VDCTRL0_VSYNC_PERIOD_UNIT 0x00200000
+#define BM_ELCDIF_VDCTRL0_VSYNC_PULSE_WIDTH_UNIT 0x00100000
+#define BM_ELCDIF_VDCTRL0_HALF_LINE 0x00080000
+#define BM_ELCDIF_VDCTRL0_HALF_LINE_MODE 0x00040000
+#define BP_ELCDIF_VDCTRL0_VSYNC_PULSE_WIDTH 0
+#define BM_ELCDIF_VDCTRL0_VSYNC_PULSE_WIDTH 0x0003FFFF
+#define BF_ELCDIF_VDCTRL0_VSYNC_PULSE_WIDTH(v) \
+ (((v) << 0) & BM_ELCDIF_VDCTRL0_VSYNC_PULSE_WIDTH)
+
+#define HW_ELCDIF_VDCTRL1 (0x00000080)
+
+#define BP_ELCDIF_VDCTRL1_VSYNC_PERIOD 0
+#define BM_ELCDIF_VDCTRL1_VSYNC_PERIOD 0xFFFFFFFF
+#define BF_ELCDIF_VDCTRL1_VSYNC_PERIOD(v) (v)
+
+#define HW_ELCDIF_VDCTRL2 (0x00000090)
+
+#define BP_ELCDIF_VDCTRL2_HSYNC_PULSE_WIDTH 18
+#define BM_ELCDIF_VDCTRL2_HSYNC_PULSE_WIDTH 0xFFFC0000
+#define BF_ELCDIF_VDCTRL2_HSYNC_PULSE_WIDTH(v) \
+ (((v) << 18) & BM_ELCDIF_VDCTRL2_HSYNC_PULSE_WIDTH)
+#define BP_ELCDIF_VDCTRL2_HSYNC_PERIOD 0
+#define BM_ELCDIF_VDCTRL2_HSYNC_PERIOD 0x0003FFFF
+#define BF_ELCDIF_VDCTRL2_HSYNC_PERIOD(v) \
+ (((v) << 0) & BM_ELCDIF_VDCTRL2_HSYNC_PERIOD)
+
+#define HW_ELCDIF_VDCTRL3 (0x000000a0)
+
+#define BP_ELCDIF_VDCTRL3_RSRVD0 30
+#define BM_ELCDIF_VDCTRL3_RSRVD0 0xC0000000
+#define BF_ELCDIF_VDCTRL3_RSRVD0(v) \
+ (((v) << 30) & BM_ELCDIF_VDCTRL3_RSRVD0)
+#define BM_ELCDIF_VDCTRL3_MUX_SYNC_SIGNALS 0x20000000
+#define BM_ELCDIF_VDCTRL3_VSYNC_ONLY 0x10000000
+#define BP_ELCDIF_VDCTRL3_HORIZONTAL_WAIT_CNT 16
+#define BM_ELCDIF_VDCTRL3_HORIZONTAL_WAIT_CNT 0x0FFF0000
+#define BF_ELCDIF_VDCTRL3_HORIZONTAL_WAIT_CNT(v) \
+ (((v) << 16) & BM_ELCDIF_VDCTRL3_HORIZONTAL_WAIT_CNT)
+#define BP_ELCDIF_VDCTRL3_VERTICAL_WAIT_CNT 0
+#define BM_ELCDIF_VDCTRL3_VERTICAL_WAIT_CNT 0x0000FFFF
+#define BF_ELCDIF_VDCTRL3_VERTICAL_WAIT_CNT(v) \
+ (((v) << 0) & BM_ELCDIF_VDCTRL3_VERTICAL_WAIT_CNT)
+
+#define HW_ELCDIF_VDCTRL4 (0x000000b0)
+
+#define BP_ELCDIF_VDCTRL4_DOTCLK_DLY_SEL 29
+#define BM_ELCDIF_VDCTRL4_DOTCLK_DLY_SEL 0xE0000000
+#define BF_ELCDIF_VDCTRL4_DOTCLK_DLY_SEL(v) \
+ (((v) << 29) & BM_ELCDIF_VDCTRL4_DOTCLK_DLY_SEL)
+#define BP_ELCDIF_VDCTRL4_RSRVD0 19
+#define BM_ELCDIF_VDCTRL4_RSRVD0 0x1FF80000
+#define BF_ELCDIF_VDCTRL4_RSRVD0(v) \
+ (((v) << 19) & BM_ELCDIF_VDCTRL4_RSRVD0)
+#define BM_ELCDIF_VDCTRL4_SYNC_SIGNALS_ON 0x00040000
+#define BP_ELCDIF_VDCTRL4_DOTCLK_H_VALID_DATA_CNT 0
+#define BM_ELCDIF_VDCTRL4_DOTCLK_H_VALID_DATA_CNT 0x0003FFFF
+#define BF_ELCDIF_VDCTRL4_DOTCLK_H_VALID_DATA_CNT(v) \
+ (((v) << 0) & BM_ELCDIF_VDCTRL4_DOTCLK_H_VALID_DATA_CNT)
+
+#define HW_ELCDIF_DVICTRL0 (0x000000c0)
+
+#define BP_ELCDIF_DVICTRL0_RSRVD1 28
+#define BM_ELCDIF_DVICTRL0_RSRVD1 0xF0000000
+#define BF_ELCDIF_DVICTRL0_RSRVD1(v) \
+ (((v) << 28) & BM_ELCDIF_DVICTRL0_RSRVD1)
+#define BP_ELCDIF_DVICTRL0_H_ACTIVE_CNT 16
+#define BM_ELCDIF_DVICTRL0_H_ACTIVE_CNT 0x0FFF0000
+#define BF_ELCDIF_DVICTRL0_H_ACTIVE_CNT(v) \
+ (((v) << 16) & BM_ELCDIF_DVICTRL0_H_ACTIVE_CNT)
+#define BP_ELCDIF_DVICTRL0_RSRVD0 12
+#define BM_ELCDIF_DVICTRL0_RSRVD0 0x0000F000
+#define BF_ELCDIF_DVICTRL0_RSRVD0(v) \
+ (((v) << 12) & BM_ELCDIF_DVICTRL0_RSRVD0)
+#define BP_ELCDIF_DVICTRL0_H_BLANKING_CNT 0
+#define BM_ELCDIF_DVICTRL0_H_BLANKING_CNT 0x00000FFF
+#define BF_ELCDIF_DVICTRL0_H_BLANKING_CNT(v) \
+ (((v) << 0) & BM_ELCDIF_DVICTRL0_H_BLANKING_CNT)
+
+#define HW_ELCDIF_DVICTRL1 (0x000000d0)
+
+#define BP_ELCDIF_DVICTRL1_RSRVD0 30
+#define BM_ELCDIF_DVICTRL1_RSRVD0 0xC0000000
+#define BF_ELCDIF_DVICTRL1_RSRVD0(v) \
+ (((v) << 30) & BM_ELCDIF_DVICTRL1_RSRVD0)
+#define BP_ELCDIF_DVICTRL1_F1_START_LINE 20
+#define BM_ELCDIF_DVICTRL1_F1_START_LINE 0x3FF00000
+#define BF_ELCDIF_DVICTRL1_F1_START_LINE(v) \
+ (((v) << 20) & BM_ELCDIF_DVICTRL1_F1_START_LINE)
+#define BP_ELCDIF_DVICTRL1_F1_END_LINE 10
+#define BM_ELCDIF_DVICTRL1_F1_END_LINE 0x000FFC00
+#define BF_ELCDIF_DVICTRL1_F1_END_LINE(v) \
+ (((v) << 10) & BM_ELCDIF_DVICTRL1_F1_END_LINE)
+#define BP_ELCDIF_DVICTRL1_F2_START_LINE 0
+#define BM_ELCDIF_DVICTRL1_F2_START_LINE 0x000003FF
+#define BF_ELCDIF_DVICTRL1_F2_START_LINE(v) \
+ (((v) << 0) & BM_ELCDIF_DVICTRL1_F2_START_LINE)
+
+#define HW_ELCDIF_DVICTRL2 (0x000000e0)
+
+#define BP_ELCDIF_DVICTRL2_RSRVD0 30
+#define BM_ELCDIF_DVICTRL2_RSRVD0 0xC0000000
+#define BF_ELCDIF_DVICTRL2_RSRVD0(v) \
+ (((v) << 30) & BM_ELCDIF_DVICTRL2_RSRVD0)
+#define BP_ELCDIF_DVICTRL2_F2_END_LINE 20
+#define BM_ELCDIF_DVICTRL2_F2_END_LINE 0x3FF00000
+#define BF_ELCDIF_DVICTRL2_F2_END_LINE(v) \
+ (((v) << 20) & BM_ELCDIF_DVICTRL2_F2_END_LINE)
+#define BP_ELCDIF_DVICTRL2_V1_BLANK_START_LINE 10
+#define BM_ELCDIF_DVICTRL2_V1_BLANK_START_LINE 0x000FFC00
+#define BF_ELCDIF_DVICTRL2_V1_BLANK_START_LINE(v) \
+ (((v) << 10) & BM_ELCDIF_DVICTRL2_V1_BLANK_START_LINE)
+#define BP_ELCDIF_DVICTRL2_V1_BLANK_END_LINE 0
+#define BM_ELCDIF_DVICTRL2_V1_BLANK_END_LINE 0x000003FF
+#define BF_ELCDIF_DVICTRL2_V1_BLANK_END_LINE(v) \
+ (((v) << 0) & BM_ELCDIF_DVICTRL2_V1_BLANK_END_LINE)
+
+#define HW_ELCDIF_DVICTRL3 (0x000000f0)
+
+#define BP_ELCDIF_DVICTRL3_RSRVD0 30
+#define BM_ELCDIF_DVICTRL3_RSRVD0 0xC0000000
+#define BF_ELCDIF_DVICTRL3_RSRVD0(v) \
+ (((v) << 30) & BM_ELCDIF_DVICTRL3_RSRVD0)
+#define BP_ELCDIF_DVICTRL3_V2_BLANK_START_LINE 20
+#define BM_ELCDIF_DVICTRL3_V2_BLANK_START_LINE 0x3FF00000
+#define BF_ELCDIF_DVICTRL3_V2_BLANK_START_LINE(v) \
+ (((v) << 20) & BM_ELCDIF_DVICTRL3_V2_BLANK_START_LINE)
+#define BP_ELCDIF_DVICTRL3_V2_BLANK_END_LINE 10
+#define BM_ELCDIF_DVICTRL3_V2_BLANK_END_LINE 0x000FFC00
+#define BF_ELCDIF_DVICTRL3_V2_BLANK_END_LINE(v) \
+ (((v) << 10) & BM_ELCDIF_DVICTRL3_V2_BLANK_END_LINE)
+#define BP_ELCDIF_DVICTRL3_V_LINES_CNT 0
+#define BM_ELCDIF_DVICTRL3_V_LINES_CNT 0x000003FF
+#define BF_ELCDIF_DVICTRL3_V_LINES_CNT(v) \
+ (((v) << 0) & BM_ELCDIF_DVICTRL3_V_LINES_CNT)
+
+#define HW_ELCDIF_DVICTRL4 (0x00000100)
+
+#define BP_ELCDIF_DVICTRL4_Y_FILL_VALUE 24
+#define BM_ELCDIF_DVICTRL4_Y_FILL_VALUE 0xFF000000
+#define BF_ELCDIF_DVICTRL4_Y_FILL_VALUE(v) \
+ (((v) << 24) & BM_ELCDIF_DVICTRL4_Y_FILL_VALUE)
+#define BP_ELCDIF_DVICTRL4_CB_FILL_VALUE 16
+#define BM_ELCDIF_DVICTRL4_CB_FILL_VALUE 0x00FF0000
+#define BF_ELCDIF_DVICTRL4_CB_FILL_VALUE(v) \
+ (((v) << 16) & BM_ELCDIF_DVICTRL4_CB_FILL_VALUE)
+#define BP_ELCDIF_DVICTRL4_CR_FILL_VALUE 8
+#define BM_ELCDIF_DVICTRL4_CR_FILL_VALUE 0x0000FF00
+#define BF_ELCDIF_DVICTRL4_CR_FILL_VALUE(v) \
+ (((v) << 8) & BM_ELCDIF_DVICTRL4_CR_FILL_VALUE)
+#define BP_ELCDIF_DVICTRL4_H_FILL_CNT 0
+#define BM_ELCDIF_DVICTRL4_H_FILL_CNT 0x000000FF
+#define BF_ELCDIF_DVICTRL4_H_FILL_CNT(v) \
+ (((v) << 0) & BM_ELCDIF_DVICTRL4_H_FILL_CNT)
+
+#define HW_ELCDIF_CSC_COEFF0 (0x00000110)
+
+#define BP_ELCDIF_CSC_COEFF0_RSRVD1 26
+#define BM_ELCDIF_CSC_COEFF0_RSRVD1 0xFC000000
+#define BF_ELCDIF_CSC_COEFF0_RSRVD1(v) \
+ (((v) << 26) & BM_ELCDIF_CSC_COEFF0_RSRVD1)
+#define BP_ELCDIF_CSC_COEFF0_C0 16
+#define BM_ELCDIF_CSC_COEFF0_C0 0x03FF0000
+#define BF_ELCDIF_CSC_COEFF0_C0(v) \
+ (((v) << 16) & BM_ELCDIF_CSC_COEFF0_C0)
+#define BP_ELCDIF_CSC_COEFF0_RSRVD0 2
+#define BM_ELCDIF_CSC_COEFF0_RSRVD0 0x0000FFFC
+#define BF_ELCDIF_CSC_COEFF0_RSRVD0(v) \
+ (((v) << 2) & BM_ELCDIF_CSC_COEFF0_RSRVD0)
+#define BP_ELCDIF_CSC_COEFF0_CSC_SUBSAMPLE_FILTER 0
+#define BM_ELCDIF_CSC_COEFF0_CSC_SUBSAMPLE_FILTER 0x00000003
+#define BF_ELCDIF_CSC_COEFF0_CSC_SUBSAMPLE_FILTER(v) \
+ (((v) << 0) & BM_ELCDIF_CSC_COEFF0_CSC_SUBSAMPLE_FILTER)
+#define BV_ELCDIF_CSC_COEFF0_CSC_SUBSAMPLE_FILTER__SAMPLE_AND_HOLD 0x0
+#define BV_ELCDIF_CSC_COEFF0_CSC_SUBSAMPLE_FILTER__RSRVD 0x1
+#define BV_ELCDIF_CSC_COEFF0_CSC_SUBSAMPLE_FILTER__INTERSTITIAL 0x2
+#define BV_ELCDIF_CSC_COEFF0_CSC_SUBSAMPLE_FILTER__COSITED 0x3
+
+#define HW_ELCDIF_CSC_COEFF1 (0x00000120)
+
+#define BP_ELCDIF_CSC_COEFF1_RSRVD1 26
+#define BM_ELCDIF_CSC_COEFF1_RSRVD1 0xFC000000
+#define BF_ELCDIF_CSC_COEFF1_RSRVD1(v) \
+ (((v) << 26) & BM_ELCDIF_CSC_COEFF1_RSRVD1)
+#define BP_ELCDIF_CSC_COEFF1_C2 16
+#define BM_ELCDIF_CSC_COEFF1_C2 0x03FF0000
+#define BF_ELCDIF_CSC_COEFF1_C2(v) \
+ (((v) << 16) & BM_ELCDIF_CSC_COEFF1_C2)
+#define BP_ELCDIF_CSC_COEFF1_RSRVD0 10
+#define BM_ELCDIF_CSC_COEFF1_RSRVD0 0x0000FC00
+#define BF_ELCDIF_CSC_COEFF1_RSRVD0(v) \
+ (((v) << 10) & BM_ELCDIF_CSC_COEFF1_RSRVD0)
+#define BP_ELCDIF_CSC_COEFF1_C1 0
+#define BM_ELCDIF_CSC_COEFF1_C1 0x000003FF
+#define BF_ELCDIF_CSC_COEFF1_C1(v) \
+ (((v) << 0) & BM_ELCDIF_CSC_COEFF1_C1)
+
+#define HW_ELCDIF_CSC_COEFF2 (0x00000130)
+
+#define BP_ELCDIF_CSC_COEFF2_RSRVD1 26
+#define BM_ELCDIF_CSC_COEFF2_RSRVD1 0xFC000000
+#define BF_ELCDIF_CSC_COEFF2_RSRVD1(v) \
+ (((v) << 26) & BM_ELCDIF_CSC_COEFF2_RSRVD1)
+#define BP_ELCDIF_CSC_COEFF2_C4 16
+#define BM_ELCDIF_CSC_COEFF2_C4 0x03FF0000
+#define BF_ELCDIF_CSC_COEFF2_C4(v) \
+ (((v) << 16) & BM_ELCDIF_CSC_COEFF2_C4)
+#define BP_ELCDIF_CSC_COEFF2_RSRVD0 10
+#define BM_ELCDIF_CSC_COEFF2_RSRVD0 0x0000FC00
+#define BF_ELCDIF_CSC_COEFF2_RSRVD0(v) \
+ (((v) << 10) & BM_ELCDIF_CSC_COEFF2_RSRVD0)
+#define BP_ELCDIF_CSC_COEFF2_C3 0
+#define BM_ELCDIF_CSC_COEFF2_C3 0x000003FF
+#define BF_ELCDIF_CSC_COEFF2_C3(v) \
+ (((v) << 0) & BM_ELCDIF_CSC_COEFF2_C3)
+
+#define HW_ELCDIF_CSC_COEFF3 (0x00000140)
+
+#define BP_ELCDIF_CSC_COEFF3_RSRVD1 26
+#define BM_ELCDIF_CSC_COEFF3_RSRVD1 0xFC000000
+#define BF_ELCDIF_CSC_COEFF3_RSRVD1(v) \
+ (((v) << 26) & BM_ELCDIF_CSC_COEFF3_RSRVD1)
+#define BP_ELCDIF_CSC_COEFF3_C6 16
+#define BM_ELCDIF_CSC_COEFF3_C6 0x03FF0000
+#define BF_ELCDIF_CSC_COEFF3_C6(v) \
+ (((v) << 16) & BM_ELCDIF_CSC_COEFF3_C6)
+#define BP_ELCDIF_CSC_COEFF3_RSRVD0 10
+#define BM_ELCDIF_CSC_COEFF3_RSRVD0 0x0000FC00
+#define BF_ELCDIF_CSC_COEFF3_RSRVD0(v) \
+ (((v) << 10) & BM_ELCDIF_CSC_COEFF3_RSRVD0)
+#define BP_ELCDIF_CSC_COEFF3_C5 0
+#define BM_ELCDIF_CSC_COEFF3_C5 0x000003FF
+#define BF_ELCDIF_CSC_COEFF3_C5(v) \
+ (((v) << 0) & BM_ELCDIF_CSC_COEFF3_C5)
+
+#define HW_ELCDIF_CSC_COEFF4 (0x00000150)
+
+#define BP_ELCDIF_CSC_COEFF4_RSRVD1 26
+#define BM_ELCDIF_CSC_COEFF4_RSRVD1 0xFC000000
+#define BF_ELCDIF_CSC_COEFF4_RSRVD1(v) \
+ (((v) << 26) & BM_ELCDIF_CSC_COEFF4_RSRVD1)
+#define BP_ELCDIF_CSC_COEFF4_C8 16
+#define BM_ELCDIF_CSC_COEFF4_C8 0x03FF0000
+#define BF_ELCDIF_CSC_COEFF4_C8(v) \
+ (((v) << 16) & BM_ELCDIF_CSC_COEFF4_C8)
+#define BP_ELCDIF_CSC_COEFF4_RSRVD0 10
+#define BM_ELCDIF_CSC_COEFF4_RSRVD0 0x0000FC00
+#define BF_ELCDIF_CSC_COEFF4_RSRVD0(v) \
+ (((v) << 10) & BM_ELCDIF_CSC_COEFF4_RSRVD0)
+#define BP_ELCDIF_CSC_COEFF4_C7 0
+#define BM_ELCDIF_CSC_COEFF4_C7 0x000003FF
+#define BF_ELCDIF_CSC_COEFF4_C7(v) \
+ (((v) << 0) & BM_ELCDIF_CSC_COEFF4_C7)
+
+#define HW_ELCDIF_CSC_OFFSET (0x00000160)
+
+#define BP_ELCDIF_CSC_OFFSET_RSRVD1 25
+#define BM_ELCDIF_CSC_OFFSET_RSRVD1 0xFE000000
+#define BF_ELCDIF_CSC_OFFSET_RSRVD1(v) \
+ (((v) << 25) & BM_ELCDIF_CSC_OFFSET_RSRVD1)
+#define BP_ELCDIF_CSC_OFFSET_CBCR_OFFSET 16
+#define BM_ELCDIF_CSC_OFFSET_CBCR_OFFSET 0x01FF0000
+#define BF_ELCDIF_CSC_OFFSET_CBCR_OFFSET(v) \
+ (((v) << 16) & BM_ELCDIF_CSC_OFFSET_CBCR_OFFSET)
+#define BP_ELCDIF_CSC_OFFSET_RSRVD0 9
+#define BM_ELCDIF_CSC_OFFSET_RSRVD0 0x0000FE00
+#define BF_ELCDIF_CSC_OFFSET_RSRVD0(v) \
+ (((v) << 9) & BM_ELCDIF_CSC_OFFSET_RSRVD0)
+#define BP_ELCDIF_CSC_OFFSET_Y_OFFSET 0
+#define BM_ELCDIF_CSC_OFFSET_Y_OFFSET 0x000001FF
+#define BF_ELCDIF_CSC_OFFSET_Y_OFFSET(v) \
+ (((v) << 0) & BM_ELCDIF_CSC_OFFSET_Y_OFFSET)
+
+#define HW_ELCDIF_CSC_LIMIT (0x00000170)
+
+#define BP_ELCDIF_CSC_LIMIT_CBCR_MIN 24
+#define BM_ELCDIF_CSC_LIMIT_CBCR_MIN 0xFF000000
+#define BF_ELCDIF_CSC_LIMIT_CBCR_MIN(v) \
+ (((v) << 24) & BM_ELCDIF_CSC_LIMIT_CBCR_MIN)
+#define BP_ELCDIF_CSC_LIMIT_CBCR_MAX 16
+#define BM_ELCDIF_CSC_LIMIT_CBCR_MAX 0x00FF0000
+#define BF_ELCDIF_CSC_LIMIT_CBCR_MAX(v) \
+ (((v) << 16) & BM_ELCDIF_CSC_LIMIT_CBCR_MAX)
+#define BP_ELCDIF_CSC_LIMIT_Y_MIN 8
+#define BM_ELCDIF_CSC_LIMIT_Y_MIN 0x0000FF00
+#define BF_ELCDIF_CSC_LIMIT_Y_MIN(v) \
+ (((v) << 8) & BM_ELCDIF_CSC_LIMIT_Y_MIN)
+#define BP_ELCDIF_CSC_LIMIT_Y_MAX 0
+#define BM_ELCDIF_CSC_LIMIT_Y_MAX 0x000000FF
+#define BF_ELCDIF_CSC_LIMIT_Y_MAX(v) \
+ (((v) << 0) & BM_ELCDIF_CSC_LIMIT_Y_MAX)
+
+#define HW_ELCDIF_DATA (0x00000180)
+
+#define BP_ELCDIF_DATA_DATA_THREE 24
+#define BM_ELCDIF_DATA_DATA_THREE 0xFF000000
+#define BF_ELCDIF_DATA_DATA_THREE(v) \
+ (((v) << 24) & BM_ELCDIF_DATA_DATA_THREE)
+#define BP_ELCDIF_DATA_DATA_TWO 16
+#define BM_ELCDIF_DATA_DATA_TWO 0x00FF0000
+#define BF_ELCDIF_DATA_DATA_TWO(v) \
+ (((v) << 16) & BM_ELCDIF_DATA_DATA_TWO)
+#define BP_ELCDIF_DATA_DATA_ONE 8
+#define BM_ELCDIF_DATA_DATA_ONE 0x0000FF00
+#define BF_ELCDIF_DATA_DATA_ONE(v) \
+ (((v) << 8) & BM_ELCDIF_DATA_DATA_ONE)
+#define BP_ELCDIF_DATA_DATA_ZERO 0
+#define BM_ELCDIF_DATA_DATA_ZERO 0x000000FF
+#define BF_ELCDIF_DATA_DATA_ZERO(v) \
+ (((v) << 0) & BM_ELCDIF_DATA_DATA_ZERO)
+
+#define HW_ELCDIF_BM_ERROR_STAT (0x00000190)
+
+#define BP_ELCDIF_BM_ERROR_STAT_ADDR 0
+#define BM_ELCDIF_BM_ERROR_STAT_ADDR 0xFFFFFFFF
+#define BF_ELCDIF_BM_ERROR_STAT_ADDR(v) (v)
+
+#define HW_ELCDIF_CRC_STAT (0x000001a0)
+
+#define BP_ELCDIF_CRC_STAT_CRC_VALUE 0
+#define BM_ELCDIF_CRC_STAT_CRC_VALUE 0xFFFFFFFF
+#define BF_ELCDIF_CRC_STAT_CRC_VALUE(v) (v)
+
+#define HW_ELCDIF_STAT (0x000001b0)
+
+#define BM_ELCDIF_STAT_PRESENT 0x80000000
+#define BM_ELCDIF_STAT_DMA_REQ 0x40000000
+#define BM_ELCDIF_STAT_LFIFO_FULL 0x20000000
+#define BM_ELCDIF_STAT_LFIFO_EMPTY 0x10000000
+#define BM_ELCDIF_STAT_TXFIFO_FULL 0x08000000
+#define BM_ELCDIF_STAT_TXFIFO_EMPTY 0x04000000
+#define BM_ELCDIF_STAT_BUSY 0x02000000
+#define BM_ELCDIF_STAT_DVI_CURRENT_FIELD 0x01000000
+#define BP_ELCDIF_STAT_RSRVD0 9
+#define BM_ELCDIF_STAT_RSRVD0 0x00FFFE00
+#define BF_ELCDIF_STAT_RSRVD0(v) \
+ (((v) << 9) & BM_ELCDIF_STAT_RSRVD0)
+#define BP_ELCDIF_STAT_LFIFO_COUNT 0
+#define BM_ELCDIF_STAT_LFIFO_COUNT 0x000001FF
+#define BF_ELCDIF_STAT_LFIFO_COUNT(v) \
+ (((v) << 0) & BM_ELCDIF_STAT_LFIFO_COUNT)
+
+#define HW_ELCDIF_VERSION (0x000001c0)
+
+#define BP_ELCDIF_VERSION_MAJOR 24
+#define BM_ELCDIF_VERSION_MAJOR 0xFF000000
+#define BF_ELCDIF_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_ELCDIF_VERSION_MAJOR)
+#define BP_ELCDIF_VERSION_MINOR 16
+#define BM_ELCDIF_VERSION_MINOR 0x00FF0000
+#define BF_ELCDIF_VERSION_MINOR(v) \
+ (((v) << 16) & BM_ELCDIF_VERSION_MINOR)
+#define BP_ELCDIF_VERSION_STEP 0
+#define BM_ELCDIF_VERSION_STEP 0x0000FFFF
+#define BF_ELCDIF_VERSION_STEP(v) \
+ (((v) << 0) & BM_ELCDIF_VERSION_STEP)
+
+#define HW_ELCDIF_DEBUG0 (0x000001d0)
+
+#define BM_ELCDIF_DEBUG0_STREAMING_END_DETECTED 0x80000000
+#define BM_ELCDIF_DEBUG0_WAIT_FOR_VSYNC_EDGE_OUT 0x40000000
+#define BM_ELCDIF_DEBUG0_SYNC_SIGNALS_ON_REG 0x20000000
+#define BM_ELCDIF_DEBUG0_DMACMDKICK 0x10000000
+#define BM_ELCDIF_DEBUG0_ENABLE 0x08000000
+#define BM_ELCDIF_DEBUG0_HSYNC 0x04000000
+#define BM_ELCDIF_DEBUG0_VSYNC 0x02000000
+#define BM_ELCDIF_DEBUG0_CUR_FRAME_TX 0x01000000
+#define BM_ELCDIF_DEBUG0_EMPTY_WORD 0x00800000
+#define BP_ELCDIF_DEBUG0_CUR_STATE 16
+#define BM_ELCDIF_DEBUG0_CUR_STATE 0x007F0000
+#define BF_ELCDIF_DEBUG0_CUR_STATE(v) \
+ (((v) << 16) & BM_ELCDIF_DEBUG0_CUR_STATE)
+#define BM_ELCDIF_DEBUG0_PXP_ELCDIF_B0_READY 0x00008000
+#define BM_ELCDIF_DEBUG0_ELCDIF_PXP_B0_DONE 0x00004000
+#define BM_ELCDIF_DEBUG0_PXP_ELCDIF_B1_READY 0x00002000
+#define BM_ELCDIF_DEBUG0_ELCDIF_PXP_B1_DONE 0x00001000
+#define BP_ELCDIF_DEBUG0_CUR_REQ_STATE 10
+#define BM_ELCDIF_DEBUG0_CUR_REQ_STATE 0x00000C00
+#define BF_ELCDIF_DEBUG0_CUR_REQ_STATE(v) \
+ (((v) << 10) & BM_ELCDIF_DEBUG0_CUR_REQ_STATE)
+#define BM_ELCDIF_DEBUG0_MST_AVALID 0x00000200
+#define BP_ELCDIF_DEBUG0_MST_OUTSTANDING_REQS 4
+#define BM_ELCDIF_DEBUG0_MST_OUTSTANDING_REQS 0x000001F0
+#define BF_ELCDIF_DEBUG0_MST_OUTSTANDING_REQS(v) \
+ (((v) << 4) & BM_ELCDIF_DEBUG0_MST_OUTSTANDING_REQS)
+#define BP_ELCDIF_DEBUG0_MST_WORDS 0
+#define BM_ELCDIF_DEBUG0_MST_WORDS 0x0000000F
+#define BF_ELCDIF_DEBUG0_MST_WORDS(v) \
+ (((v) << 0) & BM_ELCDIF_DEBUG0_MST_WORDS)
+
+#define HW_ELCDIF_DEBUG1 (0x000001e0)
+
+#define BP_ELCDIF_DEBUG1_H_DATA_COUNT 16
+#define BM_ELCDIF_DEBUG1_H_DATA_COUNT 0xFFFF0000
+#define BF_ELCDIF_DEBUG1_H_DATA_COUNT(v) \
+ (((v) << 16) & BM_ELCDIF_DEBUG1_H_DATA_COUNT)
+#define BP_ELCDIF_DEBUG1_V_DATA_COUNT 0
+#define BM_ELCDIF_DEBUG1_V_DATA_COUNT 0x0000FFFF
+#define BF_ELCDIF_DEBUG1_V_DATA_COUNT(v) \
+ (((v) << 0) & BM_ELCDIF_DEBUG1_V_DATA_COUNT)
+
+#define HW_ELCDIF_DEBUG2 (0x000001f0)
+
+#define BP_ELCDIF_DEBUG2_MST_ADDRESS 0
+#define BM_ELCDIF_DEBUG2_MST_ADDRESS 0xFFFFFFFF
+#define BF_ELCDIF_DEBUG2_MST_ADDRESS(v) (v)
+#endif /* __ELCDIF_REGS_INCLUDED_ */
diff --git a/drivers/video/mxc/epdc_regs.h b/drivers/video/mxc/epdc_regs.h
new file mode 100644
index 00000000..59c0f348
--- /dev/null
+++ b/drivers/video/mxc/epdc_regs.h
@@ -0,0 +1,443 @@
+/*
+ * Copyright (C) 2010-2012 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef __EPDC_REGS_INCLUDED__
+#define __EPDC_REGS_INCLUDED__
+
+extern void __iomem *epdc_base;
+
+/*************************************
+ * Register addresses
+ **************************************/
+
+#define EPDC_CTRL (epdc_base + 0x000)
+#define EPDC_CTRL_SET (epdc_base + 0x004)
+#define EPDC_CTRL_CLEAR (epdc_base + 0x008)
+#define EPDC_CTRL_TOGGLE (epdc_base + 0x00C)
+#define EPDC_WVADDR (epdc_base + 0x020)
+#define EPDC_WB_ADDR (epdc_base + 0x030)
+#define EPDC_RES (epdc_base + 0x040)
+#define EPDC_FORMAT (epdc_base + 0x050)
+#define EPDC_FORMAT_SET (epdc_base + 0x054)
+#define EPDC_FORMAT_CLEAR (epdc_base + 0x058)
+#define EPDC_FORMAT_TOGGLE (epdc_base + 0x05C)
+#define EPDC_FIFOCTRL (epdc_base + 0x0A0)
+#define EPDC_FIFOCTRL_SET (epdc_base + 0x0A4)
+#define EPDC_FIFOCTRL_CLEAR (epdc_base + 0x0A8)
+#define EPDC_FIFOCTRL_TOGGLE (epdc_base + 0x0AC)
+#define EPDC_UPD_ADDR (epdc_base + 0x100)
+#define EPDC_UPD_STRIDE (epdc_base + 0x110)
+#define EPDC_UPD_CORD (epdc_base + 0x120)
+#define EPDC_UPD_SIZE (epdc_base + 0x140)
+#define EPDC_UPD_CTRL (epdc_base + 0x160)
+#define EPDC_UPD_FIXED (epdc_base + 0x180)
+#define EPDC_TEMP (epdc_base + 0x1A0)
+#define EPDC_AUTOWV_LUT (epdc_base + 0x1C0)
+#define EPDC_TCE_CTRL (epdc_base + 0x200)
+#define EPDC_TCE_SDCFG (epdc_base + 0x220)
+#define EPDC_TCE_GDCFG (epdc_base + 0x240)
+#define EPDC_TCE_HSCAN1 (epdc_base + 0x260)
+#define EPDC_TCE_HSCAN2 (epdc_base + 0x280)
+#define EPDC_TCE_VSCAN (epdc_base + 0x2A0)
+#define EPDC_TCE_OE (epdc_base + 0x2C0)
+#define EPDC_TCE_POLARITY (epdc_base + 0x2E0)
+#define EPDC_TCE_TIMING1 (epdc_base + 0x300)
+#define EPDC_TCE_TIMING2 (epdc_base + 0x310)
+#define EPDC_TCE_TIMING3 (epdc_base + 0x320)
+#define EPDC_PIGEON_CTRL0 (epdc_base + 0x380)
+#define EPDC_PIGEON_CTRL1 (epdc_base + 0x390)
+#define EPDC_IRQ_MASK1 (epdc_base + 0x3C0)
+#define EPDC_IRQ_MASK1_SET (epdc_base + 0x3C4)
+#define EPDC_IRQ_MASK1_CLEAR (epdc_base + 0x3C8)
+#define EPDC_IRQ_MASK1_TOGGLE (epdc_base + 0x3CC)
+#define EPDC_IRQ_MASK2 (epdc_base + 0x3D0)
+#define EPDC_IRQ_MASK2_SET (epdc_base + 0x3D4)
+#define EPDC_IRQ_MASK2_CLEAR (epdc_base + 0x3D8)
+#define EPDC_IRQ_MASK2_TOGGLE (epdc_base + 0x3DC)
+#define EPDC_IRQ1 (epdc_base + 0x3E0)
+#define EPDC_IRQ1_SET (epdc_base + 0x3E4)
+#define EPDC_IRQ1_CLEAR (epdc_base + 0x3E8)
+#define EPDC_IRQ1_TOGGLE (epdc_base + 0x3EC)
+#define EPDC_IRQ2 (epdc_base + 0x3F0)
+#define EPDC_IRQ2_SET (epdc_base + 0x3F4)
+#define EPDC_IRQ2_CLEAR (epdc_base + 0x3F8)
+#define EPDC_IRQ2_TOGGLE (epdc_base + 0x3FC)
+#define EPDC_IRQ_MASK (epdc_base + 0x400)
+#define EPDC_IRQ_MASK_SET (epdc_base + 0x404)
+#define EPDC_IRQ_MASK_CLEAR (epdc_base + 0x408)
+#define EPDC_IRQ_MASK_TOGGLE (epdc_base + 0x40C)
+#define EPDC_IRQ (epdc_base + 0x420)
+#define EPDC_IRQ_SET (epdc_base + 0x424)
+#define EPDC_IRQ_CLEAR (epdc_base + 0x428)
+#define EPDC_IRQ_TOGGLE (epdc_base + 0x42C)
+#define EPDC_STATUS_LUTS (epdc_base + 0x440)
+#define EPDC_STATUS_LUTS_SET (epdc_base + 0x444)
+#define EPDC_STATUS_LUTS_CLEAR (epdc_base + 0x448)
+#define EPDC_STATUS_LUTS_TOGGLE (epdc_base + 0x44C)
+#define EPDC_STATUS_LUTS2 (epdc_base + 0x450)
+#define EPDC_STATUS_LUTS2_SET (epdc_base + 0x454)
+#define EPDC_STATUS_LUTS2_CLEAR (epdc_base + 0x458)
+#define EPDC_STATUS_LUTS2_TOGGLE (epdc_base + 0x45C)
+#define EPDC_STATUS_NEXTLUT (epdc_base + 0x460)
+#define EPDC_STATUS_COL (epdc_base + 0x480)
+#define EPDC_STATUS_COL2 (epdc_base + 0x490)
+#define EPDC_STATUS (epdc_base + 0x4A0)
+#define EPDC_STATUS_SET (epdc_base + 0x4A4)
+#define EPDC_STATUS_CLEAR (epdc_base + 0x4A8)
+#define EPDC_STATUS_TOGGLE (epdc_base + 0x4AC)
+#define EPDC_UPD_COL_CORD (epdc_base + 0x4C0)
+#define EPDC_UPD_COL_SIZE (epdc_base + 0x4E0)
+#define EPDC_DEBUG (epdc_base + 0x500)
+#define EPDC_DEBUG_LUT (epdc_base + 0x530)
+#define EPDC_HIST1_PARAM (epdc_base + 0x600)
+#define EPDC_HIST2_PARAM (epdc_base + 0x610)
+#define EPDC_HIST4_PARAM (epdc_base + 0x620)
+#define EPDC_HIST8_PARAM0 (epdc_base + 0x630)
+#define EPDC_HIST8_PARAM1 (epdc_base + 0x640)
+#define EPDC_HIST16_PARAM0 (epdc_base + 0x650)
+#define EPDC_HIST16_PARAM1 (epdc_base + 0x660)
+#define EPDC_HIST16_PARAM2 (epdc_base + 0x670)
+#define EPDC_HIST16_PARAM3 (epdc_base + 0x680)
+#define EPDC_GPIO (epdc_base + 0x700)
+#define EPDC_VERSION (epdc_base + 0x7F0)
+#define EPDC_PIGEON_0_0 (epdc_base + 0x800)
+#define EPDC_PIGEON_0_1 (epdc_base + 0x810)
+#define EPDC_PIGEON_0_2 (epdc_base + 0x820)
+#define EPDC_PIGEON_1_0 (epdc_base + 0x840)
+#define EPDC_PIGEON_1_1 (epdc_base + 0x850)
+#define EPDC_PIGEON_1_2 (epdc_base + 0x860)
+#define EPDC_PIGEON_2_0 (epdc_base + 0x880)
+#define EPDC_PIGEON_2_1 (epdc_base + 0x890)
+#define EPDC_PIGEON_2_2 (epdc_base + 0x8A0)
+#define EPDC_PIGEON_3_0 (epdc_base + 0x8C0)
+#define EPDC_PIGEON_3_1 (epdc_base + 0x8D0)
+#define EPDC_PIGEON_3_2 (epdc_base + 0x8E0)
+#define EPDC_PIGEON_4_0 (epdc_base + 0x900)
+#define EPDC_PIGEON_4_1 (epdc_base + 0x910)
+#define EPDC_PIGEON_4_2 (epdc_base + 0x920)
+#define EPDC_PIGEON_5_0 (epdc_base + 0x940)
+#define EPDC_PIGEON_5_1 (epdc_base + 0x950)
+#define EPDC_PIGEON_5_2 (epdc_base + 0x960)
+#define EPDC_PIGEON_6_0 (epdc_base + 0x980)
+#define EPDC_PIGEON_6_1 (epdc_base + 0x990)
+#define EPDC_PIGEON_6_2 (epdc_base + 0x9A0)
+#define EPDC_PIGEON_7_0 (epdc_base + 0x9C0)
+#define EPDC_PIGEON_7_1 (epdc_base + 0x9D0)
+#define EPDC_PIGEON_7_2 (epdc_base + 0x9E0)
+#define EPDC_PIGEON_8_0 (epdc_base + 0xA00)
+#define EPDC_PIGEON_8_1 (epdc_base + 0xA10)
+#define EPDC_PIGEON_8_2 (epdc_base + 0xA20)
+#define EPDC_PIGEON_9_0 (epdc_base + 0xA40)
+#define EPDC_PIGEON_9_1 (epdc_base + 0xA50)
+#define EPDC_PIGEON_9_2 (epdc_base + 0xA60)
+#define EPDC_PIGEON_10_0 (epdc_base + 0xA80)
+#define EPDC_PIGEON_10_1 (epdc_base + 0xA90)
+#define EPDC_PIGEON_10_2 (epdc_base + 0xAA0)
+#define EPDC_PIGEON_11_0 (epdc_base + 0xAC0)
+#define EPDC_PIGEON_11_1 (epdc_base + 0xAD0)
+#define EPDC_PIGEON_11_2 (epdc_base + 0xAE0)
+#define EPDC_PIGEON_12_0 (epdc_base + 0xB00)
+#define EPDC_PIGEON_12_1 (epdc_base + 0xB10)
+#define EPDC_PIGEON_12_2 (epdc_base + 0xB20)
+#define EPDC_PIGEON_13_0 (epdc_base + 0xB40)
+#define EPDC_PIGEON_13_1 (epdc_base + 0xB50)
+#define EPDC_PIGEON_13_2 (epdc_base + 0xB60)
+#define EPDC_PIGEON_14_0 (epdc_base + 0xB80)
+#define EPDC_PIGEON_14_1 (epdc_base + 0xB90)
+#define EPDC_PIGEON_14_2 (epdc_base + 0xBA0)
+#define EPDC_PIGEON_15_0 (epdc_base + 0xBC0)
+#define EPDC_PIGEON_15_1 (epdc_base + 0xBD0)
+#define EPDC_PIGEON_15_2 (epdc_base + 0xBE0)
+#define EPDC_WB_ADDR_TCE (epdc_base + 0xC10)
+
+/*
+ * Register field definitions
+ */
+
+enum {
+/* EPDC_CTRL field values */
+ EPDC_CTRL_SFTRST = 0x80000000,
+ EPDC_CTRL_CLKGATE = 0x40000000,
+ EPDC_CTRL_SRAM_POWERDOWN = 0x100,
+ EPDC_CTRL_UPD_DATA_SWIZZLE_MASK = 0xC0,
+ EPDC_CTRL_UPD_DATA_SWIZZLE_NO_SWAP = 0,
+ EPDC_CTRL_UPD_DATA_SWIZZLE_ALL_BYTES_SWAP = 0x40,
+ EPDC_CTRL_UPD_DATA_SWIZZLE_HWD_SWAP = 0x80,
+ EPDC_CTRL_UPD_DATA_SWIZZLE_HWD_BYTE_SWAP = 0xC0,
+ EPDC_CTRL_LUT_DATA_SWIZZLE_MASK = 0x30,
+ EPDC_CTRL_LUT_DATA_SWIZZLE_NO_SWAP = 0,
+ EPDC_CTRL_LUT_DATA_SWIZZLE_ALL_BYTES_SWAP = 0x10,
+ EPDC_CTRL_LUT_DATA_SWIZZLE_HWD_SWAP = 0x20,
+ EPDC_CTRL_LUT_DATA_SWIZZLE_HWD_BYTE_SWAP = 0x30,
+ EPDC_CTRL_BURST_LEN_8_8 = 0x1,
+ EPDC_CTRL_BURST_LEN_8_16 = 0,
+
+/* EPDC_RES field values */
+ EPDC_RES_VERTICAL_MASK = 0x1FFF0000,
+ EPDC_RES_VERTICAL_OFFSET = 16,
+ EPDC_RES_HORIZONTAL_MASK = 0x1FFF,
+ EPDC_RES_HORIZONTAL_OFFSET = 0,
+
+/* EPDC_FORMAT field values */
+ EPDC_FORMAT_BUF_PIXEL_SCALE_ROUND = 0x1000000,
+ EPDC_FORMAT_DEFAULT_TFT_PIXEL_MASK = 0xFF0000,
+ EPDC_FORMAT_DEFAULT_TFT_PIXEL_OFFSET = 16,
+ EPDC_FORMAT_BUF_PIXEL_FORMAT_MASK = 0x700,
+ EPDC_FORMAT_BUF_PIXEL_FORMAT_OFFSET = 8,
+ EPDC_FORMAT_BUF_PIXEL_FORMAT_P2N = 0x200,
+ EPDC_FORMAT_BUF_PIXEL_FORMAT_P3N = 0x300,
+ EPDC_FORMAT_BUF_PIXEL_FORMAT_P4N = 0x400,
+ EPDC_FORMAT_BUF_PIXEL_FORMAT_P5N = 0x500,
+ EPDC_FORMAT_TFT_PIXEL_FORMAT_2BIT = 0x0,
+ EPDC_FORMAT_TFT_PIXEL_FORMAT_2BIT_VCOM = 0x1,
+ EPDC_FORMAT_TFT_PIXEL_FORMAT_4BIT = 0x2,
+ EPDC_FORMAT_TFT_PIXEL_FORMAT_4BIT_VCOM = 0x3,
+
+/* EPDC_FIFOCTRL field values */
+ EPDC_FIFOCTRL_ENABLE_PRIORITY = 0x80000000,
+ EPDC_FIFOCTRL_FIFO_INIT_LEVEL_MASK = 0xFF0000,
+ EPDC_FIFOCTRL_FIFO_INIT_LEVEL_OFFSET = 16,
+ EPDC_FIFOCTRL_FIFO_H_LEVEL_MASK = 0xFF00,
+ EPDC_FIFOCTRL_FIFO_H_LEVEL_OFFSET = 8,
+ EPDC_FIFOCTRL_FIFO_L_LEVEL_MASK = 0xFF,
+ EPDC_FIFOCTRL_FIFO_L_LEVEL_OFFSET = 0,
+
+/* EPDC_UPD_CORD field values */
+ EPDC_UPD_CORD_YCORD_MASK = 0x1FFF0000,
+ EPDC_UPD_CORD_YCORD_OFFSET = 16,
+ EPDC_UPD_CORD_XCORD_MASK = 0x1FFF,
+ EPDC_UPD_CORD_XCORD_OFFSET = 0,
+
+/* EPDC_UPD_SIZE field values */
+ EPDC_UPD_SIZE_HEIGHT_MASK = 0x1FFF0000,
+ EPDC_UPD_SIZE_HEIGHT_OFFSET = 16,
+ EPDC_UPD_SIZE_WIDTH_MASK = 0x1FFF,
+ EPDC_UPD_SIZE_WIDTH_OFFSET = 0,
+
+/* EPDC_UPD_CTRL field values */
+ EPDC_UPD_CTRL_USE_FIXED = 0x80000000,
+ EPDC_UPD_CTRL_LUT_SEL_MASK = 0x3F0000,
+ EPDC_UPD_CTRL_LUT_SEL_OFFSET = 16,
+ EPDC_UPD_CTRL_WAVEFORM_MODE_MASK = 0xFF00,
+ EPDC_UPD_CTRL_WAVEFORM_MODE_OFFSET = 8,
+ EPDC_UPD_CTRL_AUTOWV_PAUSE = 0x8,
+ EPDC_UPD_CTRL_AUTOWV = 0x4,
+ EPDC_UPD_CTRL_DRY_RUN = 0x2,
+ EPDC_UPD_CTRL_UPDATE_MODE_FULL = 0x1,
+
+/* EPDC_UPD_FIXED field values */
+ EPDC_UPD_FIXED_FIXNP_EN = 0x80000000,
+ EPDC_UPD_FIXED_FIXCP_EN = 0x40000000,
+ EPDC_UPD_FIXED_FIXNP_MASK = 0xFF00,
+ EPDC_UPD_FIXED_FIXNP_OFFSET = 8,
+ EPDC_UPD_FIXED_FIXCP_MASK = 0xFF,
+ EPDC_UPD_FIXED_FIXCP_OFFSET = 0,
+
+/* EPDC_AUTOWV_LUT field values */
+ EPDC_AUTOWV_LUT_DATA_MASK = 0xFF0000,
+ EPDC_AUTOWV_LUT_DATA_OFFSET = 16,
+ EPDC_AUTOWV_LUT_ADDR_MASK = 0xFF,
+ EPDC_AUTOWV_LUT_ADDR_OFFSET = 0,
+
+/* EPDC_TCE_CTRL field values */
+ EPDC_TCE_CTRL_VSCAN_HOLDOFF_MASK = 0x1FF0000,
+ EPDC_TCE_CTRL_VSCAN_HOLDOFF_OFFSET = 16,
+ EPDC_TCE_CTRL_VCOM_VAL_MASK = 0xC00,
+ EPDC_TCE_CTRL_VCOM_VAL_OFFSET = 10,
+ EPDC_TCE_CTRL_VCOM_MODE_AUTO = 0x200,
+ EPDC_TCE_CTRL_VCOM_MODE_MANUAL = 0x000,
+ EPDC_TCE_CTRL_DDR_MODE_ENABLE = 0x100,
+ EPDC_TCE_CTRL_LVDS_MODE_CE_ENABLE = 0x80,
+ EPDC_TCE_CTRL_LVDS_MODE_ENABLE = 0x40,
+ EPDC_TCE_CTRL_SCAN_DIR_1_UP = 0x20,
+ EPDC_TCE_CTRL_SCAN_DIR_0_UP = 0x10,
+ EPDC_TCE_CTRL_DUAL_SCAN_ENABLE = 0x8,
+ EPDC_TCE_CTRL_SDDO_WIDTH_16BIT = 0x4,
+ EPDC_TCE_CTRL_PIXELS_PER_SDCLK_2 = 1,
+ EPDC_TCE_CTRL_PIXELS_PER_SDCLK_4 = 2,
+ EPDC_TCE_CTRL_PIXELS_PER_SDCLK_8 = 3,
+
+/* EPDC_TCE_SDCFG field values */
+ EPDC_TCE_SDCFG_SDCLK_HOLD = 0x200000,
+ EPDC_TCE_SDCFG_SDSHR = 0x100000,
+ EPDC_TCE_SDCFG_NUM_CE_MASK = 0xF0000,
+ EPDC_TCE_SDCFG_NUM_CE_OFFSET = 16,
+ EPDC_TCE_SDCFG_SDDO_REFORMAT_STANDARD = 0,
+ EPDC_TCE_SDCFG_SDDO_REFORMAT_FLIP_PIXELS = 0x4000,
+ EPDC_TCE_SDCFG_SDDO_INVERT_ENABLE = 0x2000,
+ EPDC_TCE_SDCFG_PIXELS_PER_CE_MASK = 0x1FFF,
+ EPDC_TCE_SDCFG_PIXELS_PER_CE_OFFSET = 0,
+
+/* EPDC_TCE_GDCFG field values */
+ EPDC_TCE_SDCFG_GDRL = 0x10,
+ EPDC_TCE_SDCFG_GDOE_MODE_DELAYED_GDCLK = 0x2,
+ EPDC_TCE_SDCFG_GDSP_MODE_FRAME_SYNC = 0x1,
+ EPDC_TCE_SDCFG_GDSP_MODE_ONE_LINE = 0x0,
+
+/* EPDC_TCE_HSCAN1 field values */
+ EPDC_TCE_HSCAN1_LINE_SYNC_WIDTH_MASK = 0xFFF0000,
+ EPDC_TCE_HSCAN1_LINE_SYNC_WIDTH_OFFSET = 16,
+ EPDC_TCE_HSCAN1_LINE_SYNC_MASK = 0xFFF,
+ EPDC_TCE_HSCAN1_LINE_SYNC_OFFSET = 0,
+
+/* EPDC_TCE_HSCAN2 field values */
+ EPDC_TCE_HSCAN2_LINE_END_MASK = 0xFFF0000,
+ EPDC_TCE_HSCAN2_LINE_END_OFFSET = 16,
+ EPDC_TCE_HSCAN2_LINE_BEGIN_MASK = 0xFFF,
+ EPDC_TCE_HSCAN2_LINE_BEGIN_OFFSET = 0,
+
+/* EPDC_TCE_VSCAN field values */
+ EPDC_TCE_VSCAN_FRAME_END_MASK = 0xFF0000,
+ EPDC_TCE_VSCAN_FRAME_END_OFFSET = 16,
+ EPDC_TCE_VSCAN_FRAME_BEGIN_MASK = 0xFF00,
+ EPDC_TCE_VSCAN_FRAME_BEGIN_OFFSET = 8,
+ EPDC_TCE_VSCAN_FRAME_SYNC_MASK = 0xFF,
+ EPDC_TCE_VSCAN_FRAME_SYNC_OFFSET = 0,
+
+/* EPDC_TCE_OE field values */
+ EPDC_TCE_OE_SDOED_WIDTH_MASK = 0xFF000000,
+ EPDC_TCE_OE_SDOED_WIDTH_OFFSET = 24,
+ EPDC_TCE_OE_SDOED_DLY_MASK = 0xFF0000,
+ EPDC_TCE_OE_SDOED_DLY_OFFSET = 16,
+ EPDC_TCE_OE_SDOEZ_WIDTH_MASK = 0xFF00,
+ EPDC_TCE_OE_SDOEZ_WIDTH_OFFSET = 8,
+ EPDC_TCE_OE_SDOEZ_DLY_MASK = 0xFF,
+ EPDC_TCE_OE_SDOEZ_DLY_OFFSET = 0,
+
+/* EPDC_TCE_POLARITY field values */
+ EPDC_TCE_POLARITY_GDSP_POL_ACTIVE_HIGH = 0x10,
+ EPDC_TCE_POLARITY_GDOE_POL_ACTIVE_HIGH = 0x8,
+ EPDC_TCE_POLARITY_SDOE_POL_ACTIVE_HIGH = 0x4,
+ EPDC_TCE_POLARITY_SDLE_POL_ACTIVE_HIGH = 0x2,
+ EPDC_TCE_POLARITY_SDCE_POL_ACTIVE_HIGH = 0x1,
+
+/* EPDC_TCE_TIMING1 field values */
+ EPDC_TCE_TIMING1_SDLE_SHIFT_NONE = 0x00,
+ EPDC_TCE_TIMING1_SDLE_SHIFT_1 = 0x10,
+ EPDC_TCE_TIMING1_SDLE_SHIFT_2 = 0x20,
+ EPDC_TCE_TIMING1_SDLE_SHIFT_3 = 0x30,
+ EPDC_TCE_TIMING1_SDCLK_INVERT = 0x8,
+ EPDC_TCE_TIMING1_SDCLK_SHIFT_NONE = 0,
+ EPDC_TCE_TIMING1_SDCLK_SHIFT_1CYCLE = 1,
+ EPDC_TCE_TIMING1_SDCLK_SHIFT_2CYCLES = 2,
+ EPDC_TCE_TIMING1_SDCLK_SHIFT_3CYCLES = 3,
+
+/* EPDC_TCE_TIMING2 field values */
+ EPDC_TCE_TIMING2_GDCLK_HP_MASK = 0xFFFF0000,
+ EPDC_TCE_TIMING2_GDCLK_HP_OFFSET = 16,
+ EPDC_TCE_TIMING2_GDSP_OFFSET_MASK = 0xFFFF,
+ EPDC_TCE_TIMING2_GDSP_OFFSET_OFFSET = 0,
+
+/* EPDC_TCE_TIMING3 field values */
+ EPDC_TCE_TIMING3_GDOE_OFFSET_MASK = 0xFFFF0000,
+ EPDC_TCE_TIMING3_GDOE_OFFSET_OFFSET = 16,
+ EPDC_TCE_TIMING3_GDCLK_OFFSET_MASK = 0xFFFF,
+ EPDC_TCE_TIMING3_GDCLK_OFFSET_OFFSET = 0,
+
+/* EPDC_IRQ_MASK/EPDC_IRQ field values */
+ EPDC_IRQ_WB_CMPLT_IRQ = 0x10000,
+ EPDC_IRQ_LUT_COL_IRQ = 0x20000,
+ EPDC_IRQ_TCE_UNDERRUN_IRQ = 0x40000,
+ EPDC_IRQ_FRAME_END_IRQ = 0x80000,
+ EPDC_IRQ_BUS_ERROR_IRQ = 0x100000,
+ EPDC_IRQ_TCE_IDLE_IRQ = 0x200000,
+ EPDC_IRQ_UPD_DONE_IRQ = 0x400000,
+ EPDC_IRQ_PWR_IRQ = 0x800000,
+
+/* EPDC_STATUS_NEXTLUT field values */
+ EPDC_STATUS_NEXTLUT_NEXT_LUT_VALID = 0x100,
+ EPDC_STATUS_NEXTLUT_NEXT_LUT_MASK = 0x3F,
+ EPDC_STATUS_NEXTLUT_NEXT_LUT_OFFSET = 0,
+
+/* EPDC_STATUS field values */
+ EPDC_STATUS_HISTOGRAM_CP_MASK = 0x1F0000,
+ EPDC_STATUS_HISTOGRAM_CP_OFFSET = 16,
+ EPDC_STATUS_HISTOGRAM_NP_MASK = 0x1F00,
+ EPDC_STATUS_HISTOGRAM_NP_OFFSET = 8,
+ EPDC_STATUS_UPD_VOID = 0x8,
+ EPDC_STATUS_LUTS_UNDERRUN = 0x4,
+ EPDC_STATUS_LUTS_BUSY = 0x2,
+ EPDC_STATUS_WB_BUSY = 0x1,
+
+/* EPDC_UPD_COL_CORD field values */
+ EPDC_UPD_COL_CORD_YCORD_MASK = 0x1FFF0000,
+ EPDC_UPD_COL_CORD_YCORD_OFFSET = 16,
+ EPDC_UPD_COL_CORD_XCORD_MASK = 0x1FFF,
+ EPDC_UPD_COL_CORD_XCORD_OFFSET = 0,
+
+/* EPDC_UPD_COL_SIZE field values */
+ EPDC_UPD_COL_SIZE_HEIGHT_MASK = 0x1FFF0000,
+ EPDC_UPD_COL_SIZE_HEIGHT_OFFSET = 16,
+ EPDC_UPD_COL_SIZE_WIDTH_MASK = 0x1FFF,
+ EPDC_UPD_COL_SIZE_WIDTH_OFFSET = 0,
+
+/* EPDC_DEBUG field values */
+ EPDC_DEBUG_UNDERRUN_RECOVER = 0x2,
+ EPDC_DEBUG_COLLISION_OFF = 0x1,
+
+/* EPDC_HISTx_PARAM field values */
+ EPDC_HIST_PARAM_VALUE0_MASK = 0x1F,
+ EPDC_HIST_PARAM_VALUE0_OFFSET = 0,
+ EPDC_HIST_PARAM_VALUE1_MASK = 0x1F00,
+ EPDC_HIST_PARAM_VALUE1_OFFSET = 8,
+ EPDC_HIST_PARAM_VALUE2_MASK = 0x1F0000,
+ EPDC_HIST_PARAM_VALUE2_OFFSET = 16,
+ EPDC_HIST_PARAM_VALUE3_MASK = 0x1F000000,
+ EPDC_HIST_PARAM_VALUE3_OFFSET = 24,
+ EPDC_HIST_PARAM_VALUE4_MASK = 0x1F,
+ EPDC_HIST_PARAM_VALUE4_OFFSET = 0,
+ EPDC_HIST_PARAM_VALUE5_MASK = 0x1F00,
+ EPDC_HIST_PARAM_VALUE5_OFFSET = 8,
+ EPDC_HIST_PARAM_VALUE6_MASK = 0x1F0000,
+ EPDC_HIST_PARAM_VALUE6_OFFSET = 16,
+ EPDC_HIST_PARAM_VALUE7_MASK = 0x1F000000,
+ EPDC_HIST_PARAM_VALUE7_OFFSET = 24,
+ EPDC_HIST_PARAM_VALUE8_MASK = 0x1F,
+ EPDC_HIST_PARAM_VALUE8_OFFSET = 0,
+ EPDC_HIST_PARAM_VALUE9_MASK = 0x1F00,
+ EPDC_HIST_PARAM_VALUE9_OFFSET = 8,
+ EPDC_HIST_PARAM_VALUE10_MASK = 0x1F0000,
+ EPDC_HIST_PARAM_VALUE10_OFFSET = 16,
+ EPDC_HIST_PARAM_VALUE11_MASK = 0x1F000000,
+ EPDC_HIST_PARAM_VALUE11_OFFSET = 24,
+ EPDC_HIST_PARAM_VALUE12_MASK = 0x1F,
+ EPDC_HIST_PARAM_VALUE12_OFFSET = 0,
+ EPDC_HIST_PARAM_VALUE13_MASK = 0x1F00,
+ EPDC_HIST_PARAM_VALUE13_OFFSET = 8,
+ EPDC_HIST_PARAM_VALUE14_MASK = 0x1F0000,
+ EPDC_HIST_PARAM_VALUE14_OFFSET = 16,
+ EPDC_HIST_PARAM_VALUE15_MASK = 0x1F000000,
+ EPDC_HIST_PARAM_VALUE15_OFFSET = 24,
+
+/* EPDC_GPIO field values */
+ EPDC_GPIO_PWRCOM = 0x40,
+ EPDC_GPIO_PWRCTRL_MASK = 0x3C,
+ EPDC_GPIO_PWRCTRL_OFFSET = 2,
+ EPDC_GPIO_BDR_MASK = 0x3,
+ EPDC_GPIO_BDR_OFFSET = 0,
+
+/* EPDC_VERSION field values */
+ EPDC_VERSION_MAJOR_MASK = 0xFF000000,
+ EPDC_VERSION_MAJOR_OFFSET = 24,
+ EPDC_VERSION_MINOR_MASK = 0xFF0000,
+ EPDC_VERSION_MINOR_OFFSET = 16,
+ EPDC_VERSION_STEP_MASK = 0xFFFF,
+ EPDC_VERSION_STEP_OFFSET = 0,
+};
+
+#endif /* __EPDC_REGS_INCLUDED__ */
diff --git a/drivers/video/mxc/epdfb_dc.c b/drivers/video/mxc/epdfb_dc.c
new file mode 100644
index 00000000..ac1679a7
--- /dev/null
+++ b/drivers/video/mxc/epdfb_dc.c
@@ -0,0 +1,1837 @@
+/***************************************************
+ * EPD frame buffer device content manager (4 bits).
+ *
+ * Author : Gallen Lin
+ * Data : 2011/01/20
+ * Revision : 1.0
+ * File Name : epdfb_dc.c
+ *
+****************************************************/
+
+#include "epdfb_dc.h"
+
+
+
+
+#ifdef __KERNEL__//[
+ #include <linux/module.h>
+ #include <linux/kernel.h>
+ #include <linux/errno.h>
+ #include <linux/string.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+ #include <linux/fb.h>
+
+ #define GDEBUG 0
+ #include <linux/gallen_dbg.h>
+ #define my_malloc(sz) kmalloc(sz,GFP_KERNEL)
+ #define my_free(p) kfree(p)
+ #define my_memcpy(dest,src,size) memcpy(dest,src,size)
+
+#else //][!__KERNEL__
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #define GDEBUG 0
+ #include "gallen_dbg.h"
+ #define my_malloc(sz) malloc(sz)
+ #define my_free(p) free(p)
+ #define my_memcpy(dest,src,size) memcpy(dest,src,size)
+#endif//] __KERNEL__
+
+
+#define EPDFB_MAGIC 0x15a815a8
+
+
+#define CHK_EPDFB_DC(pEPD_DC) \
+(pEPD_DC->dwMagicPrivateBegin==EPDFB_MAGIC)&&(pEPD_DC->dwMagicPrivateEnd==EPDFB_MAGIC)
+
+static const int giDither8[16][16] = {
+ { 1,235, 59,219, 15,231, 55,215, 2,232, 56,216, 12,228, 52,212},
+ { 129, 65,187,123,143, 79,183,119,130, 66,184,120,140, 76,180,116},
+ { 33,193, 17,251, 47,207, 31,247, 34,194, 18,248, 44,204, 28,244},
+ { 161, 97,145, 81,175,111,159, 95,162, 98,146, 82,172,108,156, 92},
+ { 9,225, 49,209, 5,239, 63,223, 10,226, 50,210, 6,236, 60,220},
+ { 137, 73,177,113,133, 69,191,127,138, 74,178,114,134, 70,188,124},
+ { 41,201, 25,241, 37,197, 21,255, 42,202, 26,242, 38,198, 22,252},
+ { 169,105,153, 89,165,101,149, 85,170,106,154, 90,166,102,150, 86},
+ { 3,233, 57,217, 13,229, 53,213, 0,234, 58,218, 14,230, 54,214},
+ { 131, 67,185,121,141, 77,181,117,128, 64,186,122,142, 78,182,118},
+ { 35,195, 19,249, 45,205, 29,245, 32,192, 16,250, 46,206, 30,246},
+ { 163, 99,147, 83,173,109,157, 93,160, 96,144, 80,174,110,158, 94},
+ { 11,227, 51,211, 7,237, 61,221, 8,224, 48,208, 4,238, 62,222},
+ { 139, 75,179,115,135, 71,189,125,136, 72,176,112,132, 68,190,126},
+ { 43,203, 27,243, 39,199, 23,253, 40,200, 24,240, 36,196, 20,254},
+ { 171,107,155, 91,167,103,151, 87,168,104,152, 88,164,100,148, 84}
+};
+
+
+
+static void _fb_gray_8to4(unsigned char *data, unsigned long len)
+{
+ unsigned char *pb = (unsigned char *)data ;
+ unsigned long dwByteRdIdx,dwByteWrIdx;
+ unsigned char bTemp;
+ unsigned long dwLenNew = len;
+
+ DBG_MSG("%s(%d):data=%p,len=%d\n",\
+ __FUNCTION__,__LINE__,data,len);
+
+ for(dwByteRdIdx=0,dwByteWrIdx=0;1;)
+ {
+ bTemp = (pb[dwByteRdIdx]>>4)&0x0f;
+ bTemp = bTemp<<4;
+ if(++dwByteRdIdx>=len){
+ break;
+ }
+ bTemp |= (pb[dwByteRdIdx]>>4)&0x0f;
+ dwLenNew-=1;
+ pb[++dwByteWrIdx] = bTemp;
+
+ if(++dwByteRdIdx>=len) {
+ break;
+ }
+ }
+
+}
+
+// get blue value to gray value .
+#if 1
+#define _RGB565_to_Gray8(_wRGB_565) (((unsigned char)(_wRGB_565)&0x001f)<<3)
+#define _RGB565_to_Gray4(_wRGB_565) (((unsigned char)(_wRGB_565)&0x001f)>>1)
+#else
+#define _RGB565_to_Gray8(_wRGB_565) \
+ (unsigned char)(( \
+ (((unsigned long)((unsigned char)(_wRGB_565)&0xf800)>>11)*299) + \
+ (((unsigned long)((unsigned char)(_wRGB_565)&0x07e0)>>5)*587) + \
+ ((unsigned long)((unsigned char)(_wRGB_565)&0x001f)*114) ) >> 8 )
+
+
+#define _RGB565_to_Gray4(_wRGB_565) \
+ _RGB565_to_Gray8(_wRGB_565)>>4
+#endif
+#define _RGB565_to_RGB888(_wRGB_565)({\
+ unsigned long dwR,dwG,dwB;\
+ unsigned long dwRGB888;\
+ dwB=((_wRGB_565)&0x1f);\
+ dwG=((_wRGB_565>>6)&0x3f);\
+ dwR=((_wRGB_565>>11)&0x1f);\
+ dwRGB888=dwR<<16|dwG<<8|dwB;\
+ dwRGB888;\
+})
+
+#define _RGB565_to_RGB8888(_wRGB_565) ({\
+ unsigned long dwR,dwG,dwB,dwT;\
+ unsigned long dwRGB8888;\
+ dwB=((_wRGB_565)&0x1f);\
+ dwG=((_wRGB_565>>6)&0x3f);\
+ dwR=((_wRGB_565>>11)&0x1f);\
+ dwT=0;\
+ dwRGB8888=dwR<<24|dwG<<16|dwB<<8|dwT;\
+ dwRGB8888;\
+})
+
+#define _RGB565(_r,_g,_b) (unsigned short)((_r)<<11|(_g)<<6|(_b))
+
+static const unsigned short gwGray4toRGB565_TableA[] = {
+ _RGB565(0x00,0x00,0x00),
+ _RGB565(0x02,0x02,0x02),
+ _RGB565(0x04,0x04,0x04),
+ _RGB565(0x06,0x06,0x06),
+ _RGB565(0x08,0x08,0x08),
+ _RGB565(0x0a,0x0a,0x0a),
+ _RGB565(0x0c,0x0c,0x0c),
+ _RGB565(0x0e,0x0e,0x0e),
+ _RGB565(0x10,0x10,0x10),
+ _RGB565(0x12,0x12,0x12),
+ _RGB565(0x14,0x14,0x14),
+ _RGB565(0x16,0x16,0x16),
+ _RGB565(0x18,0x18,0x18),
+ _RGB565(0x1a,0x1a,0x1a),
+ _RGB565(0x1c,0x1c,0x1c),
+ _RGB565(0x1e,0x1e,0x1e),
+};
+static const unsigned short gwGray2toRGB565_TableA[] = {
+ _RGB565(0x00,0x00,0x00),
+ _RGB565(0x0a,0x0a,0x0a),
+ _RGB565(0x14,0x14,0x14),
+ _RGB565(0x1e,0x1e,0x1e),
+};
+
+static const unsigned char gbGray4toGray8_TableA[] = {
+ 0x00,
+ 0x10,
+ 0x20,
+ 0x30,
+ 0x40,
+ 0x50,
+ 0x60,
+ 0x70,
+ 0x80,
+ 0x90,
+ 0xa0,
+ 0xb0,
+ 0xc0,
+ 0xd0,
+ 0xe0,
+ 0xf0,
+};
+
+static void _fb_Gray4toRGB565(
+ unsigned char *IO_pbRGB565Buf,unsigned long I_dwRGB565BufSize,
+ unsigned char *I_pb4BitsSrc,unsigned long I_dw4BitsBufSize,int iIsPixelSwap)
+{
+ unsigned long dwRd;
+ unsigned short *pwWrBuf = (unsigned short *)IO_pbRGB565Buf;
+ unsigned char *pbRdSrc = I_pb4BitsSrc;
+
+ int iIdxDot1,iIdxDot2;
+
+
+ for(dwRd=0;dwRd<I_dw4BitsBufSize;dwRd++)
+ {
+
+ if(iIsPixelSwap) {
+ iIdxDot1 = (pbRdSrc[dwRd]>>4)&0xf;
+ iIdxDot2 = (pbRdSrc[dwRd])&0xf;
+ }
+ else {
+ iIdxDot1 = (pbRdSrc[dwRd])&0xf;
+ iIdxDot2 = (pbRdSrc[dwRd]>>4)&0xf;
+ }
+
+ *pwWrBuf++ = gwGray4toRGB565_TableA[iIdxDot1];
+ *pwWrBuf++ = gwGray4toRGB565_TableA[iIdxDot2];
+ }
+}
+
+static void _fb_Gray8toRGB565(
+ unsigned char *IO_pbRGB565Buf,unsigned long I_dwRGB565BufSize,
+ unsigned char *I_pb8BitsSrc,unsigned long I_dw8BitsBufSize)
+{
+ unsigned long dwRd,dwWr;
+ unsigned short *pwWrBuf = (unsigned short *)IO_pbRGB565Buf;
+ unsigned char *pbRdSrc = I_pb8BitsSrc;
+
+
+
+ for(dwRd=0;dwRd<I_dw8BitsBufSize;dwRd++)
+ {
+ *pwWrBuf++ = (*pbRdSrc++)<<8;
+ if(++dwWr>=I_dwRGB565BufSize) {
+ ERR_MSG("%s(%d):write buffer too small \n",__FUNCTION__,__LINE__);
+ break;
+ }
+ }
+}
+
+static void _fb_RGB565toGray4(
+ unsigned char *IO_pbGray4Buf,unsigned long I_dwGray4BufSize,
+ unsigned char *I_pbRGB565Buf,unsigned long I_dwRGB565BufSize,int iIsPixelSwap)
+{
+ unsigned long dwRd,dwWr;
+ unsigned char *pbWrBuf = (unsigned char *)IO_pbGray4Buf;
+ unsigned short *pwRdSrc = (unsigned short *)I_pbRGB565Buf;
+
+ unsigned char bDot1,bDot2;
+
+ ASSERT(!(I_dwRGB565BufSize&1));
+ for(dwRd=0;dwRd<I_dwRGB565BufSize;dwRd+=2)
+ {
+ bDot1 = (*pwRdSrc) >> 12 ;
+ pwRdSrc ++ ;
+ bDot2 = (*pwRdSrc) >> 12 ;
+ pwRdSrc ++ ;
+
+ if(iIsPixelSwap) {
+ *pbWrBuf = bDot2&(bDot1<<4) ;
+ }
+ else {
+ *pbWrBuf = (bDot2<<4)&bDot1 ;
+ }
+ pbWrBuf++;
+ if(++dwWr >= I_dwGray4BufSize) {
+ ERR_MSG("%s(%d):write buffer too small \n",__FUNCTION__,__LINE__);
+ break;
+ }
+ }
+}
+
+
+static void _fb_gray_4to8(
+ unsigned char *IO_pb8BitsBuf,unsigned long I_dw8BitsBufSize,
+ unsigned char *I_pb4BitsSrc,unsigned long I_dw4BitsBufSize,int iIsPixelSwap)
+{
+ unsigned long dwWr,dwRd;
+
+ for(dwWr=0,dwRd=0;dwRd<I_dw4BitsBufSize;dwRd++)
+ {
+ if(iIsPixelSwap) {
+ if(dwWr>=I_dw8BitsBufSize) {
+ ERR_MSG("%s(%d):write buffer too small \n",__FUNCTION__,__LINE__);
+ break;
+ }
+ IO_pb8BitsBuf[dwWr++] = (I_pb4BitsSrc[dwRd]<<4)&0xf0;
+
+ if(dwWr>=I_dw8BitsBufSize) {
+ ERR_MSG("%s(%d):write buffer too small \n",__FUNCTION__,__LINE__);
+ break;
+ }
+ IO_pb8BitsBuf[dwWr++] = I_pb4BitsSrc[dwRd]&0xf0;
+ }
+ else {
+ if(dwWr>=I_dw8BitsBufSize) {
+ ERR_MSG("%s(%d):write buffer too small \n",__FUNCTION__,__LINE__);
+ break;
+ }
+ IO_pb8BitsBuf[dwWr++] = I_pb4BitsSrc[dwRd]&0xf0;
+
+ if(dwWr>=I_dw8BitsBufSize) {
+ ERR_MSG("%s(%d):write buffer too small \n",__FUNCTION__,__LINE__);
+ break;
+ }
+ IO_pb8BitsBuf[dwWr++] = (I_pb4BitsSrc[dwRd]<<4)&0xf0;
+ }
+ }
+}
+
+EPDFB_DC *epdfbdc_create_ex2(unsigned long dwFBW,unsigned long dwFBH,\
+ unsigned long dwW,unsigned long dwH,\
+ unsigned char bPixelBits,unsigned char *pbDCbuf,unsigned long dwCreateFlag)
+{
+ EPDFB_DC *pDC = 0;
+ unsigned long dwFBBits = (dwFBW*dwFBH*bPixelBits);
+ unsigned long dwFBWBits = (dwFBW*bPixelBits);
+ unsigned long dwDCSize ,dwDCWidthBytes;
+
+ dwDCSize=dwFBBits>>3;
+ dwDCWidthBytes=dwFBWBits>>3;
+
+ if(dwFBWBits&0x7) {
+ dwDCWidthBytes+=1;
+ dwDCSize+=dwFBH;
+ }
+
+ DBG_MSG("%s(%d):w=%d,h=%d,bits=%d,bufp=%p\n",__FUNCTION__,__LINE__,\
+ dwW,dwH,bPixelBits,pbDCbuf);
+
+ pDC = my_malloc(sizeof(EPDFB_DC));
+ if(0==pDC) {
+ return 0;
+ }
+
+
+ if(pbDCbuf) {
+ pDC->dwBufSize = 0;
+ pDC->pbDCbuf = pbDCbuf;
+
+ DBG_MSG("%s(%d):pDC malloc=%p\n",__FUNCTION__,__LINE__,pDC);
+ }
+ else {
+ pbDCbuf = my_malloc(dwDCSize);
+ if(0==pbDCbuf) {
+ my_free(pDC);
+ return 0;
+ }
+ DBG_MSG("%s(%d):pDC malloc=%p,pbDCbuf=%p,sz=%u\n",
+ __FUNCTION__,__LINE__,pDC,pbDCbuf,dwDCSize);
+ memset(pbDCbuf,0,dwDCSize);
+ pDC->pbDCbuf = pbDCbuf;
+ pDC->dwBufSize = dwDCSize;
+ }
+ ASSERT(dwFBW>=dwW);
+ pDC->dwFBWExtra = dwFBW-dwW ;
+ ASSERT(dwFBH>=dwH);
+ pDC->dwFBHExtra = dwFBH-dwH ;
+ pDC->dwWidth = dwW;
+ pDC->dwHeight = dwH;
+ pDC->dwFBXOffset = 0;
+ pDC->dwFBYOffset = 0;
+
+ {
+ int maxval = 255;
+ int row,col;
+
+ switch(bPixelBits) {
+ case 1:
+ maxval = 1;
+ break;
+ case 2:
+ maxval = 3;
+ break;
+ case 4:
+ maxval = 15;
+ break;
+ case 8:
+ maxval = 255;
+ break;
+ case 16:
+ maxval = 31;
+ break;
+ case 32:
+ maxval = 255;
+ break;
+ }
+ //printk("D8 Array[][] = {");
+ for ( row = 0; row < 16; ++row ) {
+ //printk("{");
+ for ( col = 0; col < 16; ++col ) {
+ pDC->dither8[row][col] = giDither8[row][col] * ( maxval + 1 ) / 256;
+ //printk("%d,",pDC->dither8[row][col]);
+ }
+ //printk("}\n");
+ }
+ //printk("\n");
+ }
+
+
+ pDC->bPixelBits = bPixelBits;
+ pDC->dwDCSize = dwDCSize;
+
+ pDC->dwDCWidthBytes = dwDCWidthBytes;
+
+ pDC->dwDirtyOffsetStart = 0;
+ pDC->dwDirtyOffsetEnd = dwDCSize;
+
+ pDC->dwMagicPrivateBegin = EPDFB_MAGIC;
+ pDC->dwMagicPrivateEnd = EPDFB_MAGIC;
+ pDC->dwFlags = dwCreateFlag;
+
+ pDC->pfnGetWaveformBpp = 0;
+ pDC->pfnVcomEnable = 0;
+ pDC->pfnSetPartialUpdate = 0;
+ pDC->pfnGetRealFrameBuf = 0;
+ pDC->pfnGetRealFrameBufEx = 0;
+ pDC->pfnDispStart = 0;
+ pDC->pfnGetWaveformMode = 0;
+ pDC->pfnSetWaveformMode = 0;
+ pDC->pfnIsUpdating = 0;
+ pDC->pfnWaitUpdateComplete = 0;
+ pDC->pfnSetUpdateRect = 0;
+ pDC->pfnPwrAutoOffIntervalMax = 0;
+ pDC->pfnAutoOffEnable = 0;
+ pDC->pfnPutImg = 0;
+ pDC->pfnSetVCOM = 0;
+ pDC->pfnSetVCOMToFlash = 0;
+ pDC->pfnGetVCOM = 0;
+
+ pDC->iIsForceWaitUpdateFinished = 1;
+
+ pDC->pfnPwrOnOff = 0;
+
+ pDC->iWFMode = -1;
+ pDC->iLastWFMode = -1;
+
+ return pDC;
+}
+
+EPDFB_DC *epdfbdc_create_ex(unsigned long dwW,unsigned long dwH,\
+ unsigned char bPixelBits,unsigned char *pbDCbuf,unsigned long dwCreateFlag)
+{
+ return epdfbdc_create_ex2(dwW,dwH,dwW,dwH,bPixelBits,pbDCbuf,dwCreateFlag);
+}
+
+
+
+EPDFB_DC *epdfbdc_create(unsigned long dwW,unsigned long dwH,\
+ unsigned char bPixelBits,unsigned char *pbDCbuf)
+{
+ return epdfbdc_create_ex(dwW,dwH,bPixelBits,pbDCbuf,EPDFB_DC_FLAG_DEFAUT);
+}
+
+
+EPDFB_DC_RET epdfbdc_delete(EPDFB_DC *pEPD_dc)
+{
+ DBG_MSG("%s\n",__FUNCTION__);
+ if(!CHK_EPDFB_DC(pEPD_dc)) {
+ ERR_MSG("%s(%d): object handle error !\n",__FUNCTION__,__LINE__);
+ return EPDFB_DC_OBJECTERR;
+ }
+
+ if(pEPD_dc->dwBufSize>0) {
+ my_free((void *)pEPD_dc->pbDCbuf);
+ pEPD_dc->pbDCbuf = 0;
+ }
+ my_free(pEPD_dc);
+ return EPDFB_DC_SUCCESS;
+}
+
+/*
+ *
+ *
+ *
+ */
+EPDFB_DC_RET epdfbdc_fbimg_normallize(EPDFB_DC *I_pEPD_dc,\
+ EPDFB_IMG *IO_pEPD_img)
+{
+ EPDFB_DC_RET tRet=EPDFB_DC_PIXELBITSNOTSUPPORT;
+ unsigned char *pbBuf = 0;
+ unsigned long dwBufSize ;
+
+ if(!CHK_EPDFB_DC(I_pEPD_dc)) {
+ return EPDFB_DC_OBJECTERR;
+ }
+
+ if(IO_pEPD_img->bPixelBits!=I_pEPD_dc->bPixelBits) {
+ switch(IO_pEPD_img->bPixelBits) { // source image
+ case 16:
+ if(4==I_pEPD_dc->bPixelBits) {
+ pbBuf = IO_pEPD_img->pbImgBuf;
+ dwBufSize = IO_pEPD_img->dwW*IO_pEPD_img->dwH*IO_pEPD_img->bPixelBits/8;
+ _fb_RGB565toGray4(IO_pEPD_img->pbImgBuf,dwBufSize,
+ pbBuf,dwBufSize,0);
+ tRet=EPDFB_DC_SUCCESS;
+ }
+ else if(8==I_pEPD_dc->bPixelBits) {
+ }
+ else if (16==I_pEPD_dc->bPixelBits){
+ tRet=EPDFB_DC_SUCCESS;
+ }
+ break;
+ case 8:
+ if(4==I_pEPD_dc->bPixelBits) {
+ _fb_gray_8to4(IO_pEPD_img->pbImgBuf,\
+ IO_pEPD_img->dwW*IO_pEPD_img->dwH );
+ IO_pEPD_img->bPixelBits = 4;
+ tRet=EPDFB_DC_SUCCESS;
+ }
+ else if(16==I_pEPD_dc->bPixelBits) {
+ dwBufSize = IO_pEPD_img->dwW*IO_pEPD_img->dwH*I_pEPD_dc->bPixelBits/8;
+ pbBuf = my_malloc(dwBufSize);
+ if(!pbBuf) {
+ return EPDFB_DC_MEMMALLOCFAIL;
+ }
+ memcpy(pbBuf,IO_pEPD_img->pbImgBuf,IO_pEPD_img->dwW*IO_pEPD_img->dwH);
+ _fb_Gray8toRGB565(IO_pEPD_img->pbImgBuf,\
+ IO_pEPD_img->dwW*IO_pEPD_img->dwH,pbBuf,dwBufSize);
+ my_free(pbBuf);pbBuf=0;
+
+ }
+ else if(8==I_pEPD_dc->bPixelBits) {
+ tRet=EPDFB_DC_SUCCESS;
+ }
+ break;
+ case 4:
+ dwBufSize = IO_pEPD_img->dwW*IO_pEPD_img->dwH*I_pEPD_dc->bPixelBits/8;
+ pbBuf = my_malloc(dwBufSize);
+ if(!pbBuf) {
+ return EPDFB_DC_MEMMALLOCFAIL;
+ }
+ if(8==I_pEPD_dc->bPixelBits) {
+ memcpy(pbBuf,IO_pEPD_img->pbImgBuf, \
+ IO_pEPD_img->dwW*IO_pEPD_img->dwH*IO_pEPD_img->bPixelBits/8);
+ _fb_gray_4to8(IO_pEPD_img->pbImgBuf,IO_pEPD_img->dwW*IO_pEPD_img->dwH,
+ pbBuf,dwBufSize,0);
+ tRet=EPDFB_DC_SUCCESS;
+ }
+ else if(16==I_pEPD_dc->bPixelBits) {
+ memcpy(pbBuf,IO_pEPD_img->pbImgBuf, \
+ IO_pEPD_img->dwW*IO_pEPD_img->dwH*IO_pEPD_img->bPixelBits/8);
+ _fb_Gray4toRGB565(IO_pEPD_img->pbImgBuf,
+ IO_pEPD_img->dwW*IO_pEPD_img->dwH*IO_pEPD_img->bPixelBits/8,
+ pbBuf,dwBufSize,0);
+ tRet=EPDFB_DC_SUCCESS;
+ }
+ else if(4==I_pEPD_dc->bPixelBits) {
+ tRet=EPDFB_DC_SUCCESS;
+ }
+ my_free(pbBuf);pbBuf=0;
+ break;
+ default :
+ //tRet = EPDFB_DC_PIXELBITSNOTSUPPORT;
+ break;
+ }
+ }
+ return tRet;
+}
+
+unsigned long _epdfbdc_pixel_dither(EPDFB_DC *pEPD_dc,unsigned char bSrcBitsPerPixel,
+ unsigned long dwSrcPxlVal,unsigned long I_dwSrcPxlX,unsigned long I_dwSrcPxlY)
+{
+ unsigned long dwPValDitherRet=dwSrcPxlVal;
+
+ if(pEPD_dc->dwFlags & EPDFB_DC_FLAG_DITHER8) {
+ unsigned long dwPVal;
+ switch (bSrcBitsPerPixel) {
+ case 2:
+ dwPVal = dwSrcPxlVal & 0x3;
+ break;
+ case 4:
+ dwPVal = dwSrcPxlVal & 0xf;
+ break;
+ case 8:
+ dwPVal = dwSrcPxlVal & 0xff;
+ break;
+ case 16:
+ dwPVal = dwSrcPxlVal & 0x1f;
+ break;
+ case 24:
+ dwPVal = dwSrcPxlVal & 0xff;
+ break;
+ case 32:
+ dwPVal = dwSrcPxlVal & 0xff;
+ break;
+ }
+ if((int)(dwPVal)>=pEPD_dc->dither8[I_dwSrcPxlY%16][I_dwSrcPxlX%16]) {
+ dwPValDitherRet = 0xffffffff;
+ }
+ else {
+ dwPValDitherRet = 0;
+ }
+ }
+ return dwPValDitherRet;
+}
+
+static inline unsigned long _pixel_value_convert(unsigned long dwPixelVal,
+ unsigned char bSrcBitsPerPixel,unsigned char bDestBitsPerPixel)
+{
+ unsigned long dwRetPixelVal;
+
+ switch(bSrcBitsPerPixel)
+ {
+ case 1:
+ dwRetPixelVal = dwPixelVal?0x0:0xffffffff;
+ break;
+
+ case 2:
+ if(4==bDestBitsPerPixel) {
+ dwRetPixelVal = dwPixelVal<<2;
+ }
+ else if(8==bDestBitsPerPixel) {
+ dwRetPixelVal = dwPixelVal<<6;
+ }
+ else if(16==bDestBitsPerPixel) {
+ dwRetPixelVal = (unsigned long)(gwGray2toRGB565_TableA[(dwPixelVal&0x3)]);
+ //printk("%x->%x.",dwPixelVal,dwRetPixelVal);
+ }
+ else if(32==bDestBitsPerPixel) {
+ dwRetPixelVal = dwPixelVal<<6|dwPixelVal<<14|dwPixelVal<<22|dwPixelVal<<30;
+ //printk("%x->%x.",dwPixelVal,dwRetPixelVal);
+ }
+ else if(24==bDestBitsPerPixel) {
+ dwRetPixelVal = dwPixelVal<<6|dwPixelVal<<14|dwPixelVal<<22;
+ //printk("%x->%x.",dwPixelVal,dwRetPixelVal);
+ }
+ else if(1==bDestBitsPerPixel) {
+ dwRetPixelVal = dwPixelVal>>1;
+ }
+ else if(2==bDestBitsPerPixel) {
+ dwRetPixelVal = dwPixelVal;
+ }
+ break;
+
+ case 4:
+ if(4==bDestBitsPerPixel) {
+ dwRetPixelVal = dwPixelVal;
+ }
+ else if(8==bDestBitsPerPixel) {
+ dwRetPixelVal = dwPixelVal<<4;
+ //dwRetPixelVal = (unsigned long)gbGray4toGray8_TableA[(dwPixelVal&0xf)];
+ }
+ else if(16==bDestBitsPerPixel) {
+ //dwRetPixelVal = dwPixelVal<<12;
+ dwRetPixelVal = (unsigned long)(gwGray4toRGB565_TableA[(dwPixelVal&0xf)]);
+ }
+ else if(32==bDestBitsPerPixel) {
+ //dwRetPixelVal = dwPixelVal<<8;
+ dwRetPixelVal = dwPixelVal<<4|dwPixelVal<<12|dwPixelVal<<20|dwPixelVal<<28;
+ }
+ else if(24==bDestBitsPerPixel) {
+ //dwRetPixelVal = dwPixelVal<<8;
+ dwRetPixelVal = dwPixelVal<<4|dwPixelVal<<12|dwPixelVal<<20;
+ }
+ else if(1==bDestBitsPerPixel) {
+ dwRetPixelVal = dwPixelVal>>3;
+ }
+ else if(2==bDestBitsPerPixel) {
+ dwRetPixelVal = dwPixelVal>>2;
+ }
+ break;
+
+ case 8:
+ if(4==bDestBitsPerPixel) {
+ dwRetPixelVal = dwPixelVal>>4;
+ }
+ else if(8==bDestBitsPerPixel) {
+ dwRetPixelVal = dwPixelVal;
+ }
+ else if(16==bDestBitsPerPixel) {
+ //dwRetPixelVal = dwPixelVal<<8;
+ dwRetPixelVal = (unsigned long)(gwGray4toRGB565_TableA[(dwPixelVal>>4)]);
+ }
+ else if(32==bDestBitsPerPixel) {
+ //dwRetPixelVal = dwPixelVal<<8;
+ dwRetPixelVal = dwPixelVal|dwPixelVal<<8|dwPixelVal<<16|dwPixelVal<<24;
+ }
+ else if(24==bDestBitsPerPixel) {
+ //dwRetPixelVal = dwPixelVal<<8;
+ dwRetPixelVal = dwPixelVal|dwPixelVal<<8|dwPixelVal<<16;
+ }
+ else if(1==bDestBitsPerPixel) {
+ dwRetPixelVal = dwPixelVal>>7;
+ }
+ else if(2==bDestBitsPerPixel) {
+ dwRetPixelVal = dwPixelVal>>6;
+ }
+ break;
+
+ case 16:
+ if(4==bDestBitsPerPixel) {
+ dwRetPixelVal = (unsigned long)_RGB565_to_Gray4(dwPixelVal);
+ }
+ else if(8==bDestBitsPerPixel) {
+ dwRetPixelVal = (unsigned long)_RGB565_to_Gray8(dwPixelVal);
+ }
+ else if(16==bDestBitsPerPixel) {
+ dwRetPixelVal = dwPixelVal;
+ }
+ else if(32==bDestBitsPerPixel) {
+ dwRetPixelVal =_RGB565_to_RGB8888(dwPixelVal);
+ }
+ else if(24==bDestBitsPerPixel) {
+ dwRetPixelVal = _RGB565_to_RGB888(dwPixelVal);
+ }
+ else if(1==bDestBitsPerPixel) {
+ dwRetPixelVal = dwPixelVal>>31;
+ }
+ else if(2==bDestBitsPerPixel) {
+ dwRetPixelVal = dwPixelVal>>30;
+ }
+ break;
+
+ case 24:
+ if(4==bDestBitsPerPixel) {
+ dwRetPixelVal = dwPixelVal>>20;
+ }
+ else if(8==bDestBitsPerPixel) {
+ dwRetPixelVal = dwPixelVal>>18;
+ }
+ else if(16==bDestBitsPerPixel) {
+ dwRetPixelVal = dwPixelVal>>8;
+ }
+ else if(32==bDestBitsPerPixel) {
+ dwRetPixelVal = dwPixelVal;
+ }
+ else if(24==bDestBitsPerPixel) {
+ dwRetPixelVal = dwPixelVal;
+ }
+ else if(1==bDestBitsPerPixel) {
+ dwRetPixelVal = dwPixelVal>>23;
+ }
+ else if(2==bDestBitsPerPixel) {
+ dwRetPixelVal = dwPixelVal>>22;
+ }
+ break;
+
+ case 32:
+ if(4==bDestBitsPerPixel) {
+ dwRetPixelVal = dwPixelVal>>28;
+ }
+ else if(8==bDestBitsPerPixel) {
+ dwRetPixelVal = dwPixelVal>>24;
+ }
+ else if(16==bDestBitsPerPixel) {
+ dwRetPixelVal = dwPixelVal>>16;
+ }
+ else if(32==bDestBitsPerPixel) {
+ dwRetPixelVal = dwPixelVal;
+ }
+ else if(1==bDestBitsPerPixel) {
+ dwRetPixelVal = dwPixelVal>>31;
+ }
+ else if(2==bDestBitsPerPixel) {
+ dwRetPixelVal = dwPixelVal>>30;
+ }
+ break;
+
+ default:
+ WARNING_MSG("convert %d -> %d not supported \n",bSrcBitsPerPixel,bDestBitsPerPixel);
+ dwRetPixelVal = dwPixelVal;
+ break;
+ }
+ //printf("0x%x->0x%x.",dwPixelVal,dwRetPixelVal);
+
+ return dwRetPixelVal;
+}
+
+
+
+static inline unsigned char *_epdfbdc_get_dcimg_ptr(
+ EPDFB_DC *I_pEPD_imgdc, unsigned long I_dwImgX,unsigned long I_dwImgY)
+{
+ unsigned char *pbRet ;
+ unsigned char bBitsPerPixel = I_pEPD_imgdc->bPixelBits;
+
+ if(4==bBitsPerPixel) {
+ pbRet = I_pEPD_imgdc->pbDCbuf +
+ (I_dwImgX>>1)+(I_dwImgY*I_pEPD_imgdc->dwDCWidthBytes);
+ }
+ else if(8==bBitsPerPixel) {
+ pbRet = (unsigned char *)(I_pEPD_imgdc->pbDCbuf+(I_dwImgX)+\
+ (I_dwImgY*I_pEPD_imgdc->dwDCWidthBytes));
+ }
+ else if(16==bBitsPerPixel) {
+ pbRet = (unsigned char *)(I_pEPD_imgdc->pbDCbuf+(I_dwImgX>>1)+\
+ (I_dwImgY*I_pEPD_imgdc->dwDCWidthBytes));
+ }
+ else if(24==bBitsPerPixel) {
+ pbRet = (unsigned char *)(I_pEPD_imgdc->pbDCbuf+(I_dwImgX/3)+\
+ (I_dwImgY*I_pEPD_imgdc->dwDCWidthBytes));
+ }
+ else if(32==bBitsPerPixel) {
+ pbRet = (unsigned char *)(I_pEPD_imgdc->pbDCbuf+(I_dwImgX>>2)+\
+ (I_dwImgY*I_pEPD_imgdc->dwDCWidthBytes));
+ }
+ else if(1==bBitsPerPixel) {
+ pbRet =(unsigned char *)(I_pEPD_imgdc->pbDCbuf+(I_dwImgX>>3)+\
+ (I_dwImgY*I_pEPD_imgdc->dwDCWidthBytes));
+ }
+ else if(2==bBitsPerPixel) {
+ pbRet =(unsigned char *)(I_pEPD_imgdc->pbDCbuf+(I_dwImgX>>2)+\
+ (I_dwImgY*I_pEPD_imgdc->dwDCWidthBytes));
+ }
+ else {
+ pbRet = 0;
+ }
+ return pbRet;
+}
+
+static inline unsigned long _epdfbdc_get_img_pixelvalue_from_ptr(EPDFB_DC *I_pEPD_imgdc,
+ unsigned char **IO_ppbImg,unsigned long I_dwX)
+{
+ unsigned long dwRet;
+ unsigned char bBitsPerPixel=I_pEPD_imgdc->bPixelBits;
+
+ ASSERT(IO_ppbImg);
+ ASSERT(*IO_ppbImg);
+ ASSERT(*IO_ppbImg>=I_pEPD_imgdc->pbDCbuf);
+
+ if((*IO_ppbImg)>I_pEPD_imgdc->pbDCbuf+I_pEPD_imgdc->dwDCSize) {
+ ERR_MSG("[Warning]*IO_ppbImg=%p>pbDCbuf=%p+dwDCSize=%u\n",(*IO_ppbImg),
+ I_pEPD_imgdc->pbDCbuf,I_pEPD_imgdc->dwDCSize);
+ return 0;
+ ASSERT(0);
+ }
+
+ switch(bBitsPerPixel) {
+ case 4:
+ {
+ unsigned char b;
+ if(I_dwX&1) {
+ if(I_pEPD_imgdc->dwFlags&EPDFB_DC_FLAG_REVERSEINPDATA) {
+ b = (**IO_ppbImg)&0xf;
+ }
+ else {
+ b = ((**IO_ppbImg)>>4)&0xf;
+ }
+ (*IO_ppbImg) += 1;
+ }
+ else {
+ if(I_pEPD_imgdc->dwFlags&EPDFB_DC_FLAG_REVERSEINPDATA) {
+ b = ((**IO_ppbImg)>>4)&0xf;
+ }
+ else {
+ b = (**IO_ppbImg)&0xf;
+ }
+ }
+ dwRet = (unsigned long)b;
+
+ break;
+ }
+ case 1:
+ {
+ unsigned char b=(I_dwX&0x7);
+ unsigned char bBitMask=0x80>>b;
+
+ dwRet = (unsigned long)((**IO_ppbImg)&bBitMask);
+ if(7==b) {
+ (*IO_ppbImg) += 1;
+ }
+ break;
+ }
+ case 2:
+ {
+ unsigned char b=(I_dwX&0x3);
+ unsigned char bShift=6-(b<<1);
+
+ /*
+ if(I_pEPD_imgdc->dwFlags&EPDFB_DC_FLAG_REVERSEINPDATA) {
+
+ }
+ else {
+ switch(bShift) {
+ case 0:
+ bShift = 6;
+ break;
+ case 2:
+ bShift = 2;
+ break;
+ case 4:
+ bShift = 4;
+ break;
+ case 6:
+ bShift = 0;
+ break;
+ default :
+ break;
+ }
+ }
+ */
+
+ dwRet = (unsigned long)(((**IO_ppbImg)>>bShift)&0x03);
+
+ if(3==b) {
+ (*IO_ppbImg) += 1;
+ }
+ break;
+ }
+ case 8:
+ {
+ dwRet = (unsigned long)(**((unsigned char **)IO_ppbImg));
+ (*IO_ppbImg) += 1;
+ break;
+ }
+ case 16:
+ {
+ dwRet = (unsigned long)(**((unsigned short **)IO_ppbImg));
+ (*IO_ppbImg) += 2;
+ break;
+ }
+ case 24:
+ {
+ dwRet = (unsigned long)(**((unsigned long **)IO_ppbImg));
+ (*IO_ppbImg) += 3;
+ break;
+ }
+ case 32:
+ {
+ dwRet = (unsigned long)(**((unsigned short **)IO_ppbImg));
+ (*IO_ppbImg) += 4;
+ break;
+ }
+ default:
+ ERR_MSG("%d Bits/Pixel not supported !!\n",bBitsPerPixel);
+ dwRet=0;
+ break;
+ }
+ return dwRet;
+}
+#if 0
+static inline void _epdfbdc_set_dcpixel_at_ptr(EPDFB_DC *I_pEPD_dc,
+ unsigned char **IO_ppbDCDest,unsigned long I_dwSrcPixelVal,unsigned char I_bSrcBitsPerPixel,
+ unsigned short I_dwDestX)
+{
+ unsigned long L_dwPixelVal;
+ unsigned char bDestBitsPerPixel;
+
+ ASSERT(IO_ppbDCDest);
+ ASSERT(*IO_ppbDCDest);
+
+ ASSERT(*IO_ppbDCDest>=I_pEPD_dc->pbDCbuf);
+ ASSERT(*IO_ppbDCDest<I_pEPD_dc->pbDCbuf+I_pEPD_dc->dwDCSize);
+
+ bDestBitsPerPixel = I_pEPD_dc->bPixelBits;
+ L_dwPixelVal = _pixel_value_convert(I_dwSrcPixelVal,I_bSrcBitsPerPixel,bDestBitsPerPixel);
+
+ switch(bDestBitsPerPixel) {
+ case 4:
+ {
+ unsigned char bTemp,bPixelVal;
+
+ bPixelVal=(unsigned char)(L_dwPixelVal&0xf);
+ bTemp = **IO_ppbDCDest;
+
+ if(I_pEPD_dc->dwFlags&EPDFB_DC_FLAG_REVERSEDRVDATA) {
+ if(I_dwDestX&0x1) {
+ bTemp &= 0x0f;
+ bTemp |= (bPixelVal<<4)&0xf0 ;
+ **IO_ppbDCDest = bTemp;
+ (*IO_ppbDCDest) += 1;
+ }
+ else {
+ bTemp &= 0xf0;
+ bTemp |= bPixelVal;
+ **IO_ppbDCDest = bTemp;
+ }
+ }
+ else {
+ if(I_dwDestX&0x1) {
+ bTemp &= 0xf0;
+ bTemp |= bPixelVal;
+ **IO_ppbDCDest = bTemp;
+ (*IO_ppbDCDest) += 1;
+ }
+ else {
+ bTemp &= 0x0f;
+ bTemp |= (bPixelVal<<4)&0xf0 ;
+ **IO_ppbDCDest = bTemp;
+ }
+ }
+
+
+ break;
+ }
+ case 1:
+ {
+ unsigned char bTemp;
+ unsigned char b=(I_dwDestX&0x7);
+
+ bTemp = **IO_ppbDCDest;
+ if(L_dwPixelVal) {
+ bTemp |= 0x01<<b;
+ }
+ else {
+ bTemp &= ~(0x01<<b);
+ }
+ **IO_ppbDCDest = bTemp;
+ if(7==b) {
+ (*IO_ppbDCDest) += 1;
+ }
+ break;
+ }
+ case 2:
+ {
+ unsigned char bTemp;
+ unsigned char b=(I_dwDestX&0x3);
+
+ bTemp = **IO_ppbDCDest;
+ bTemp &= ~(0x3<<(b<<1));
+ bTemp |= L_dwPixelVal<<(b<<1);
+ **IO_ppbDCDest = bTemp;
+ if(3==b) {
+ (*IO_ppbDCDest) += 1;
+ }
+ break;
+ }
+ case 8:
+ {
+ **((unsigned char **)IO_ppbDCDest) = (unsigned char)(L_dwPixelVal&0x000000ff);
+ (*IO_ppbDCDest) += 1;
+ break;
+ }
+ case 16:
+ {
+ **((unsigned short **)IO_ppbDCDest) = (unsigned short)(L_dwPixelVal&0x0000ffff);
+ (*IO_ppbDCDest) += 2;
+ break;
+ }
+ case 24:
+ {
+ **((unsigned long **)IO_ppbDCDest) = (unsigned long)(L_dwPixelVal&0x00ffffff);
+ (*IO_ppbDCDest) += 3;
+ break;
+ }
+ case 32:
+ {
+ **((unsigned long **)IO_ppbDCDest) = (unsigned long)(L_dwPixelVal&0xffffffff);
+ (*IO_ppbDCDest) += 4;
+ break;
+ }
+
+ default:
+ break;
+
+ }
+}
+#endif
+
+
+static inline unsigned long _epdfbdc_get_img_pixelvalue(EPDFB_DC *I_pEPD_dc,
+ EPDFB_IMG *I_pEPD_img,unsigned long dwX,unsigned long dwY,unsigned long dwImgWidthBytes)
+{
+ unsigned long dwRet = 0;
+ unsigned char bBitsPerPixel = I_pEPD_img->bPixelBits;
+ //unsigned long dwImgWidthBytes ;
+
+ if(4==bBitsPerPixel) {
+ unsigned char *pb;
+ unsigned char b;
+
+ pb = I_pEPD_img->pbImgBuf+
+ (dwX>>1)+(dwY*dwImgWidthBytes);
+
+ if(dwX&1) {
+ if(I_pEPD_dc->dwFlags&EPDFB_DC_FLAG_REVERSEINPDATA) {
+ b = (*pb)&0xf;
+ }
+ else {
+ b = ((*pb)>>4)&0xf;
+ }
+ }
+ else {
+ if(I_pEPD_dc->dwFlags&EPDFB_DC_FLAG_REVERSEINPDATA) {
+ b = ((*pb)>>4)&0xf;
+ }
+ else {
+ b = (*pb)&0xf;
+ }
+ }
+ dwRet = (unsigned long)b;
+ }
+ else if(2==bBitsPerPixel) {
+ unsigned char *pb =
+ ((unsigned char *)(I_pEPD_img->pbImgBuf+(dwX>>2)+(dwY*dwImgWidthBytes)));
+ unsigned char bShiftBits = (dwX%4)<<1;
+ dwRet = (unsigned long)((*pb)>>bShiftBits);
+ dwRet &= 0x3;
+ }
+ else if(1==bBitsPerPixel) {
+ unsigned char *pb =
+ ((unsigned char *)(I_pEPD_img->pbImgBuf+(dwX>>3)+(dwY*dwImgWidthBytes)));
+ unsigned char bBitMask=0x80>>(dwX&0x7);
+ dwRet = (unsigned long)((*pb)&bBitMask);
+ }
+ else if(8==bBitsPerPixel) {
+ dwRet = (unsigned long)
+ *((unsigned char *)(I_pEPD_img->pbImgBuf+(dwX)+(dwY*dwImgWidthBytes)));
+ }
+ else if(16==bBitsPerPixel) {
+ dwRet = (unsigned long)
+ *((unsigned short *)(I_pEPD_img->pbImgBuf+(dwX<<1)+(dwY*dwImgWidthBytes)));
+ }
+ else if(24==bBitsPerPixel) {
+ dwRet = (unsigned long)
+ *((unsigned long *)(I_pEPD_img->pbImgBuf+(dwX*3)+(dwY*dwImgWidthBytes)));
+ }
+ else if(32==bBitsPerPixel) {
+ dwRet = (unsigned long)
+ *((unsigned long *)(I_pEPD_img->pbImgBuf+(dwX<<2)+(dwY*dwImgWidthBytes)));
+ }
+ else {
+ ERR_MSG("%s : pixel bits %d not supported !\n",__FUNCTION__,bBitsPerPixel);
+ }
+
+ return dwRet;
+}
+
+static inline void _epdfbdc_set_pixel(EPDFB_DC *pEPD_dc,
+ unsigned long dwX,unsigned long dwY,unsigned long dwPixelVal,unsigned char bSrcBitsPerPixel)
+{
+ unsigned long L_dwPixelVal;
+ unsigned char bDestBitsPerPixel;
+
+ L_dwPixelVal = _pixel_value_convert(dwPixelVal,bSrcBitsPerPixel,pEPD_dc->bPixelBits);
+ bDestBitsPerPixel = pEPD_dc->bPixelBits;
+
+
+ //DBG_MSG("(%d,%d)=0x%x",dwX,dwY,L_dwPixelVal);
+
+ if( 4 == bDestBitsPerPixel ) {
+ volatile unsigned char *pbDCDest;
+ unsigned char bTemp,bPixelVal;
+
+
+ //DBG_MSG("%s(),x=0x%x,y=0x%x,pixel=0x%x\n",__FUNCTION__,dwX,dwY,dwPixelVal);
+
+ pbDCDest = pEPD_dc->pbDCbuf + \
+ (dwX>>1)+(dwY*pEPD_dc->dwDCWidthBytes);
+
+ bPixelVal=(unsigned char)(L_dwPixelVal&0xf);
+ bTemp = *pbDCDest;
+
+ if(pEPD_dc->dwFlags&EPDFB_DC_FLAG_REVERSEDRVDATA) {
+ if(dwX&0x1) {
+ bTemp &= 0x0f;
+ bTemp |= (bPixelVal<<4)&0xf0 ;
+ }
+ else {
+ bTemp &= 0xf0;
+ bTemp |= bPixelVal;
+ }
+ }
+ else {
+ if(dwX&0x1) {
+ bTemp &= 0xf0;
+ bTemp |= bPixelVal;
+ }
+ else {
+ bTemp &= 0x0f;
+ bTemp |= (bPixelVal<<4)&0xf0 ;
+ }
+ }
+
+ *pbDCDest = bTemp;
+ }
+ else if( 16 == bDestBitsPerPixel ) {
+ volatile unsigned short *pwDCDest;
+
+ pwDCDest = (unsigned short *)(pEPD_dc->pbDCbuf + \
+ (dwX<<1)+(dwY*pEPD_dc->dwDCWidthBytes));
+ *pwDCDest = (unsigned short)(L_dwPixelVal&0x0000ffff);
+ }
+ else if( 24 == bDestBitsPerPixel ) {
+ volatile unsigned long *pdwDCDest;
+
+ pdwDCDest = (unsigned long *)(pEPD_dc->pbDCbuf + \
+ (dwX/3)+(dwY*pEPD_dc->dwDCWidthBytes));
+ *pdwDCDest = (unsigned long)(L_dwPixelVal&0x00ffffff);
+ }
+ else if( 32 == bDestBitsPerPixel ) {
+ volatile unsigned long *pdwDCDest;
+
+ pdwDCDest = (unsigned long *)(pEPD_dc->pbDCbuf + \
+ (dwX<<2)+(dwY*pEPD_dc->dwDCWidthBytes));
+ *pdwDCDest = (unsigned long)(L_dwPixelVal&0xffffffff);
+ }
+ else if( 8 == bDestBitsPerPixel ) {
+ volatile unsigned char *pbDCDest;
+
+ pbDCDest = (unsigned short *)(pEPD_dc->pbDCbuf + \
+ (dwX)+(dwY*pEPD_dc->dwDCWidthBytes));
+ *pbDCDest = (unsigned char)(L_dwPixelVal&0x000000ff);
+ }
+ else {
+ }
+}
+
+
+//
+#define GETIMG_PIXEL_METHOD 1
+
+EPDFB_DC_RET epdfbdc_put_dcimg(EPDFB_DC *pEPD_dc,
+ EPDFB_DC *pEPD_dcimg,EPDFB_ROTATE_T tRotateDegree,
+ unsigned long I_dwDCimgX,unsigned long I_dwDCimgY,
+ unsigned long I_dwDCimgW,unsigned long I_dwDCimgH,
+ unsigned long I_dwDCPutX,unsigned long I_dwDCPutY)
+{
+ EPDFB_DC_RET tRet=EPDFB_DC_SUCCESS;
+
+ unsigned long dwDCH,dwDCW;
+
+ //unsigned long dwImgW,dwImgH;
+ //unsigned long dwImgX,dwImgY;
+
+ unsigned char bImgPixelBits;
+ unsigned long dwDCWidthBytes;
+ unsigned long dwX,dwY,dwEPD_dc_flags;
+ unsigned long dwSrcX,dwSrcY;
+
+ unsigned long h,w;// image offset of x,y .
+
+ unsigned long dwWHidden,dwHHidden;
+
+ unsigned char *pbDCRow;
+ unsigned char *pbImgRow;
+
+ //int tick=jiffies;
+ unsigned long dwImgWidthBytes;
+
+
+ #if (GETIMG_PIXEL_METHOD==1)
+ EPDFB_IMG tEPD_img,*pEPD_img=&tEPD_img;
+ pEPD_img->dwX = I_dwDCimgX;
+ pEPD_img->dwY = I_dwDCimgY;
+ pEPD_img->dwW = pEPD_dcimg->dwWidth+pEPD_dcimg->dwFBWExtra;
+ pEPD_img->dwH = pEPD_dcimg->dwHeight+pEPD_dcimg->dwFBHExtra;
+ pEPD_img->bPixelBits = pEPD_dcimg->bPixelBits;
+ pEPD_img->pbImgBuf = pEPD_dcimg->pbDCbuf;
+ #endif //] (GETIMG_PIXEL_METHOD==1)
+
+
+ dwImgWidthBytes = pEPD_dcimg->dwDCWidthBytes;//pEPD_dc->bPixelBits/8
+ GALLEN_DBGLOCAL_BEGIN();
+
+ if(!CHK_EPDFB_DC(pEPD_dc)) {
+ ERR_MSG("%s(%d): object handle error !\n",__FUNCTION__,__LINE__);
+ GALLEN_DBGLOCAL_RUNLOG(0);
+ GALLEN_DBGLOCAL_ESC();
+ return EPDFB_DC_OBJECTERR;
+ }
+
+ I_dwDCPutX += pEPD_dc->dwFBXOffset;
+ I_dwDCPutY += pEPD_dc->dwFBYOffset;
+
+
+ dwWHidden = pEPD_dc->dwFBWExtra;
+ dwHHidden = pEPD_dc->dwFBHExtra;
+ dwDCH=pEPD_dc->dwHeight + dwHHidden;
+ dwDCW=pEPD_dc->dwWidth + dwWHidden;
+ dwEPD_dc_flags = pEPD_dc->dwFlags;
+
+ bImgPixelBits=pEPD_dcimg->bPixelBits;
+
+ //if(pEPD_dc->bPixelBits!=bImgPixelBits) {
+ // GALLEN_DBGLOCAL_ESC();
+ // return EPDFB_DC_PIXELBITSNOTSUPPORT;
+ //}
+
+ //ASSERT(4==pEPD_dc->bPixelBits);
+ //ASSERT(4==bImgPixelBits);
+ //ASSERT(0==(I_dwDCimgW&0x1)); // image width must be even .
+ //ASSERT(0==(I_dwDCimgH&0x1)); // image heigh must be even .
+
+
+ dwDCWidthBytes = pEPD_dc->dwDCWidthBytes;
+
+
+ DBG_MSG("dc_flag=0x%08x,imgdc.w=%u,imgdc.h=%u,img.w=%u,img.h=%u,img pixbits=%d,dc pixbits=%d\n",
+ dwEPD_dc_flags,pEPD_dcimg->dwWidth,pEPD_dcimg->dwHeight,I_dwDCimgW,I_dwDCimgH,pEPD_dcimg->bPixelBits,pEPD_dc->bPixelBits);
+ DBG_MSG("IMG_Wbytes=%u,DC_Wbytes=%u\n",dwImgWidthBytes,dwDCWidthBytes);
+
+ switch(tRotateDegree) {
+ case EPDFB_R_0:GALLEN_DBGLOCAL_RUNLOG(2);
+ // left->right,up->down .
+
+ {
+ unsigned long dwPixelVal;
+
+ pEPD_dc->dwDirtyOffsetStart = I_dwDCimgY*dwDCWidthBytes;
+ pEPD_dc->dwDirtyOffsetEnd = (I_dwDCimgY+I_dwDCimgH)*dwDCWidthBytes;
+
+ //dwImgPixelIdx = 0;
+ for(h=0;h<I_dwDCimgH;h+=1) {
+
+ dwSrcY = I_dwDCimgY+h;
+ dwY = I_dwDCPutY+h;
+ if(dwY>=dwDCH) {
+ continue ;
+ }
+ //GALLEN_DBGLOCAL_PRINTMSG("y=%d,x=",h);
+
+ pbImgRow = _epdfbdc_get_dcimg_ptr(pEPD_dcimg,I_dwDCimgX,dwSrcY);
+
+ for(w=0;w<I_dwDCimgW;w+=1) {
+
+ dwSrcX = I_dwDCimgX+w;
+ dwX = I_dwDCPutX+w;
+ if(dwX>=dwDCW) {
+ continue ;
+ }
+
+ //GALLEN_DBGLOCAL_PRINTMSG("%d,",w,h);
+ #if (GETIMG_PIXEL_METHOD==1)//[
+ dwPixelVal = _epdfbdc_get_img_pixelvalue(pEPD_dc,pEPD_img,dwSrcX,dwSrcY,dwImgWidthBytes);
+ #else //][!(GETIMG_PIXEL_METHOD==1)
+ dwPixelVal = _epdfbdc_get_img_pixelvalue_from_ptr(pEPD_dcimg,&pbImgRow,dwSrcX);
+ #endif //]
+
+ dwPixelVal = _epdfbdc_pixel_dither(pEPD_dc,bImgPixelBits,dwPixelVal,dwSrcX,dwSrcY);
+
+ if(dwEPD_dc_flags&EPDFB_DC_FLAG_SKIPLEFTPIXEL && 0==w) {
+ // skip left pixel output.
+ }
+ else if(dwEPD_dc_flags&EPDFB_DC_FLAG_SKIPRIGHTPIXEL && I_dwDCimgW==w+1) {
+ // skip right pixel output.
+ }
+ else {
+ _epdfbdc_set_pixel(pEPD_dc,dwX,dwY,dwPixelVal,bImgPixelBits);
+ }
+ }
+ //GALLEN_DBGLOCAL_PRINTMSG("\n,",w,h);
+ }
+ }
+ break;
+ case EPDFB_R_90:GALLEN_DBGLOCAL_RUNLOG(3);
+ // up->down,right->left .
+ {
+ unsigned long dwPixelVal;
+
+ pEPD_dc->dwDirtyOffsetStart = I_dwDCimgX*dwDCWidthBytes;
+ pEPD_dc->dwDirtyOffsetEnd = (I_dwDCimgX+I_dwDCimgW)*dwDCWidthBytes;
+
+
+ //dwImgPixelIdx = 0;
+
+ for(h=0;h<I_dwDCimgH;h+=1) {
+ dwSrcY = I_dwDCimgY+h;
+ dwX = dwDCW-h-1-I_dwDCPutY-dwWHidden;
+ if(dwX>=dwDCW) {
+ continue ;
+ }
+
+ pbImgRow = _epdfbdc_get_dcimg_ptr(pEPD_dcimg,I_dwDCimgX,dwSrcY);
+
+ for(w=0;w<I_dwDCimgW;w+=1) {
+
+ dwSrcX = I_dwDCimgX+w;
+ dwY = I_dwDCPutX+w;
+ if(dwY>=dwDCH) {
+ continue ;
+ }
+
+ #if (GETIMG_PIXEL_METHOD==1) //[
+ dwPixelVal = _epdfbdc_get_img_pixelvalue(pEPD_dc,pEPD_img,dwSrcX,dwSrcY,dwImgWidthBytes);
+ #else //][
+ dwPixelVal = _epdfbdc_get_img_pixelvalue_from_ptr(pEPD_dcimg,&pbImgRow,dwSrcX);
+ #endif //]
+
+ dwPixelVal = _epdfbdc_pixel_dither(pEPD_dc,bImgPixelBits,dwPixelVal,dwSrcX,dwSrcY);
+
+ if(dwEPD_dc_flags&EPDFB_DC_FLAG_SKIPLEFTPIXEL && 0==w) {
+ // skip left pixel output.
+ //DBG_MSG("[[%d]]",w);
+ }
+ else if(dwEPD_dc_flags&EPDFB_DC_FLAG_SKIPRIGHTPIXEL && I_dwDCimgW==w+1) {
+ // skip right pixel output.
+ //DBG_MSG("{{%d}}",w);
+ }
+ else {
+ _epdfbdc_set_pixel(pEPD_dc,dwX,dwY,dwPixelVal,bImgPixelBits);
+ }
+ }
+ }// image height loop .
+ }
+ break;
+ case EPDFB_R_180:GALLEN_DBGLOCAL_RUNLOG(4);
+ // right->left,down->up .
+ {
+ volatile unsigned long dwPixelVal;
+
+ pEPD_dc->dwDirtyOffsetStart = (dwDCH-I_dwDCimgY-I_dwDCimgH)*dwDCWidthBytes;
+ pEPD_dc->dwDirtyOffsetEnd = (dwDCH-I_dwDCimgY)*dwDCWidthBytes;
+
+ //dwImgPixelIdx = 0;
+ for(h=0;h<I_dwDCimgH;h+=1) {
+
+ dwSrcY = I_dwDCimgY+h;
+ dwY = dwDCH-h-1-I_dwDCPutY-dwHHidden;
+ if(dwY>=dwDCH) {
+ continue ;
+ }
+
+ pbImgRow = _epdfbdc_get_dcimg_ptr(pEPD_dcimg,I_dwDCimgX,dwSrcY);
+
+ //DBG_MSG("y=%d->",I_dwDCimgH-h-1);
+ for(w=0;w<I_dwDCimgW;w+=1) {
+ dwSrcX = I_dwDCimgX+w;
+ dwX = dwDCW-w-1-I_dwDCPutX-dwWHidden;
+ //DBG_MSG("y=%d->",dwImgH-h-1);
+ if(dwX>=dwDCW) {
+ continue ;
+ }
+
+ #if (GETIMG_PIXEL_METHOD==1)//[
+ dwPixelVal = _epdfbdc_get_img_pixelvalue(pEPD_dc,pEPD_img,dwSrcX,dwSrcY,dwImgWidthBytes);
+ #else //][
+ dwPixelVal = _epdfbdc_get_img_pixelvalue_from_ptr(pEPD_dcimg,&pbImgRow,dwSrcX);
+ #endif //]
+
+ dwPixelVal = _epdfbdc_pixel_dither(pEPD_dc,bImgPixelBits,dwPixelVal,dwSrcX,dwSrcY);
+
+ if(dwEPD_dc_flags&EPDFB_DC_FLAG_SKIPLEFTPIXEL && 0==w) {
+ // skip left pixel output.
+ }
+ else if(dwEPD_dc_flags&EPDFB_DC_FLAG_SKIPRIGHTPIXEL && I_dwDCimgW==w+1) {
+ // skip right pixel output.
+ }
+ else {
+ _epdfbdc_set_pixel(pEPD_dc,dwX,dwY,dwPixelVal,bImgPixelBits);
+ }
+ }
+ }
+ }
+ break;
+ case EPDFB_R_270:GALLEN_DBGLOCAL_RUNLOG(5);
+ // down->up,left->right .
+ {
+ unsigned long dwPixelVal;
+
+ pEPD_dc->dwDirtyOffsetStart = (dwDCH-I_dwDCimgX-I_dwDCimgW)*dwDCWidthBytes;
+ pEPD_dc->dwDirtyOffsetEnd = (dwDCH-I_dwDCimgX)*dwDCWidthBytes;
+
+ //dwImgPixelIdx = 0;
+ for(h=0;h<I_dwDCimgH;h+=1) {
+
+ dwSrcY = I_dwDCimgY+h;
+ dwX = h+I_dwDCPutY;
+ if(dwX>=dwDCW) {
+ continue ;
+ }
+
+ pbImgRow = _epdfbdc_get_dcimg_ptr(pEPD_dcimg,I_dwDCimgX,dwSrcY);
+
+ for(w=0;w<I_dwDCimgW;w+=1) {
+
+ dwSrcX = I_dwDCimgX+w;
+ dwY = dwDCH-w-1-I_dwDCPutX-dwHHidden;
+ if(dwY>=dwDCH) {
+ continue ;
+ }
+
+ #if (GETIMG_PIXEL_METHOD==1) //[
+ dwPixelVal = _epdfbdc_get_img_pixelvalue(pEPD_dc,pEPD_img,dwSrcX,dwSrcY,dwImgWidthBytes);
+ #else //][
+ dwPixelVal = _epdfbdc_get_img_pixelvalue_from_ptr(pEPD_dcimg,&pbImgRow,dwSrcX);
+ #endif //]
+
+ dwPixelVal = _epdfbdc_pixel_dither(pEPD_dc,bImgPixelBits,dwPixelVal,dwSrcX,dwSrcY);
+
+ if(dwEPD_dc_flags&EPDFB_DC_FLAG_SKIPLEFTPIXEL && 0==w) {
+ // skip left pixel output.
+ }
+ else if(dwEPD_dc_flags&EPDFB_DC_FLAG_SKIPRIGHTPIXEL && I_dwDCimgW==w+1) {
+ // skip right pixel output.
+ }
+ else {
+ _epdfbdc_set_pixel(pEPD_dc,dwX,dwY,dwPixelVal,bImgPixelBits);
+ }
+ }
+ }
+ }
+ break;
+ }
+
+//printk ("[%s-%d] %d\n",__func__,__LINE__,jiffies-tick);
+
+ GALLEN_DBGLOCAL_END();
+ return tRet;
+}
+
+EPDFB_DC_RET epdfbdc_put_fbimg(EPDFB_DC *pEPD_dc,
+ EPDFB_IMG *pEPD_img,EPDFB_ROTATE_T tRotateDegree)
+{
+ EPDFB_DC *ptEPD_dcimg ;
+ EPDFB_DC_RET tRet;
+
+ ptEPD_dcimg = epdfbdc_create_ex(pEPD_img->dwW,pEPD_img->dwH,\
+ pEPD_img->bPixelBits,pEPD_img->pbImgBuf,pEPD_dc->dwFlags);
+
+ tRet = epdfbdc_put_dcimg(pEPD_dc,ptEPD_dcimg,tRotateDegree,
+ 0,0,pEPD_img->dwW,pEPD_img->dwH,pEPD_img->dwX,pEPD_img->dwY);
+
+ epdfbdc_delete(ptEPD_dcimg);
+
+ return tRet;
+}
+
+
+EPDFB_DC_RET epdfbdc_get_rotate_active(EPDFB_DC *I_pEPD_dc,
+ unsigned long *IO_pdwX,unsigned long *IO_pdwY,
+ unsigned long *IO_pdwW,unsigned long *IO_pdwH,
+ EPDFB_ROTATE_T I_tRotate)
+{
+ EPDFB_DC_RET tRet=EPDFB_DC_SUCCESS;
+ unsigned long dwX,dwY,dwH,dwW;
+ unsigned long dwDCW,dwDCH;
+ unsigned dwWHidden,dwHHidden;
+
+ if(!CHK_EPDFB_DC(I_pEPD_dc)) {
+ ERR_MSG("%s(%d): object handle error !\n",__FUNCTION__,__LINE__);
+ return EPDFB_DC_OBJECTERR;
+ }
+
+ if(!(IO_pdwX&&IO_pdwY&&IO_pdwW&&IO_pdwH)) {
+ return EPDFB_DC_PARAMERR;
+ }
+
+ dwWHidden = I_pEPD_dc->dwFBWExtra;
+ dwHHidden = I_pEPD_dc->dwFBHExtra;
+ dwDCH=I_pEPD_dc->dwHeight + dwHHidden;
+ dwDCW=I_pEPD_dc->dwWidth + dwWHidden;
+
+ switch(I_tRotate)
+ {
+ default :
+ case EPDFB_R_0:
+ dwX = (*IO_pdwX);
+ dwY = (*IO_pdwY);
+ dwW = (*IO_pdwW);
+ dwH = (*IO_pdwH);
+ return tRet;
+ case EPDFB_R_90:
+ dwX = dwDCW-(*IO_pdwY)-(*IO_pdwH)-dwWHidden;
+ dwY = (*IO_pdwX);
+ dwW = (*IO_pdwH);
+ dwH = (*IO_pdwW);
+ break;
+ case EPDFB_R_180:
+ dwX = dwDCW-(*IO_pdwX)-(*IO_pdwW)-dwWHidden;
+ dwY = dwDCH-(*IO_pdwY)-(*IO_pdwH)-dwHHidden;
+ dwW = (*IO_pdwW);
+ dwH = (*IO_pdwH);
+ break;
+ case EPDFB_R_270:
+ dwX = (*IO_pdwY);
+ dwY = dwDCH-(*IO_pdwW)-(*IO_pdwX)-dwHHidden;
+ dwW = (*IO_pdwH);
+ dwH = (*IO_pdwW);
+ break;
+ }
+ *IO_pdwX = dwX;
+ *IO_pdwY = dwY;
+ *IO_pdwW = dwW;
+ *IO_pdwH = dwH;
+ return tRet;
+}
+
+
+EPDFB_DC_RET epdfbdc_set_pixel(EPDFB_DC *I_pEPD_dc,\
+ unsigned long I_dwX,unsigned long I_dwY,unsigned long I_dwPVal)
+{
+ EPDFB_DC_RET tRet=EPDFB_DC_SUCCESS;
+
+ if(!CHK_EPDFB_DC(I_pEPD_dc)) {
+ ERR_MSG("%s(%d): object handle error !\n",__FUNCTION__,__LINE__);
+ return EPDFB_DC_OBJECTERR;
+ }
+
+ _epdfbdc_set_pixel(I_pEPD_dc,I_dwX,I_dwY,I_dwPVal,4);
+
+ return tRet;
+}
+
+EPDFB_DC_RET epdfbdc_dcbuf_to_RGB565(EPDFB_DC *I_pEPD_dc,\
+ unsigned char *O_pbRGB565Buf,unsigned long I_dwRGB565BufSize)
+{
+ EPDFB_DC_RET tRet=EPDFB_DC_SUCCESS;
+
+ if(!CHK_EPDFB_DC(I_pEPD_dc)) {
+ return EPDFB_DC_OBJECTERR;
+ }
+
+ _fb_Gray4toRGB565(O_pbRGB565Buf,I_dwRGB565BufSize,\
+ I_pEPD_dc->pbDCbuf,I_pEPD_dc->dwDCSize,I_pEPD_dc->dwFlags&EPDFB_DC_FLAG_REVERSEINPDATA);
+
+ return tRet;
+}
+
+EPDFB_DC_RET epdfbdc_get_dirty_region(EPDFB_DC *I_pEPD_dc,\
+ unsigned long *O_pdwDirtyOffsetStart,unsigned long *O_pdwDirtyOffsetEnd)
+{
+ EPDFB_DC_RET tRet=EPDFB_DC_SUCCESS;
+
+ if(!CHK_EPDFB_DC(I_pEPD_dc)) {
+ return EPDFB_DC_OBJECTERR;
+ }
+
+ if(O_pdwDirtyOffsetStart) {
+ *O_pdwDirtyOffsetStart = I_pEPD_dc->dwDirtyOffsetStart ;
+ }
+ I_pEPD_dc->dwDirtyOffsetStart = 0;
+
+ if(O_pdwDirtyOffsetEnd) {
+ *O_pdwDirtyOffsetEnd = I_pEPD_dc->dwDirtyOffsetEnd ;
+ }
+ I_pEPD_dc->dwDirtyOffsetEnd = I_pEPD_dc->dwDCSize;
+
+ return tRet;
+}
+
+
+EPDFB_DC_RET epdfbdc_set_width_height(EPDFB_DC *I_pEPD_dc,
+ unsigned long dwFBW,unsigned long dwFBH,
+ unsigned long dwW,unsigned long dwH)
+{
+ EPDFB_DC_RET tRet = EPDFB_DC_SUCCESS;
+ unsigned char bPixelBits;
+ unsigned long dwFBBits ;
+ unsigned long dwFBWBits;
+ unsigned long dwDCSize ,dwDCWidthBytes;
+
+ if(!CHK_EPDFB_DC(I_pEPD_dc)) {
+ ERR_MSG("%s(%d): object handle error !\n",__FUNCTION__,__LINE__);
+ return EPDFB_DC_OBJECTERR;
+ }
+
+ if(dwFBW<dwW) {
+ ERR_MSG("%s(%d): FBW(%d)<W(%d) !\n",__FUNCTION__,__LINE__,dwFBW,dwW);
+ return EPDFB_DC_PARAMERR;
+ }
+
+ if(dwFBH<dwH) {
+ ERR_MSG("%s(%d): FBH(%d)<H(%d) !\n",__FUNCTION__,__LINE__,dwFBH,dwH);
+ return EPDFB_DC_PARAMERR;
+ }
+
+ bPixelBits = I_pEPD_dc->bPixelBits;
+ dwFBBits = (dwFBW*dwFBH*bPixelBits);
+ dwFBWBits = (dwFBW*bPixelBits);
+ dwDCSize=dwFBBits>>3;
+ dwDCWidthBytes=dwFBWBits>>3;
+
+ if(dwFBWBits&0x7) {
+ dwDCWidthBytes+=1;
+ dwDCSize+=dwFBH;
+ }
+
+ if( I_pEPD_dc->dwBufSize>0 && dwDCSize>I_pEPD_dc->dwDCSize) {
+ ERR_MSG("%s(%d): new DCSize(%d)>original DCSize(%d) !\n",__FUNCTION__,__LINE__,
+ dwDCSize,I_pEPD_dc->dwDCSize);
+ return EPDFB_DC_PARAMERR;
+ }
+
+ I_pEPD_dc->dwDCWidthBytes = dwDCWidthBytes;
+ I_pEPD_dc->dwDirtyOffsetEnd = dwDCSize;
+ I_pEPD_dc->dwDCSize = dwDCSize;
+
+ I_pEPD_dc->dwFBWExtra = dwFBW-dwW ;
+ I_pEPD_dc->dwFBHExtra = dwFBH-dwH ;
+ I_pEPD_dc->dwWidth = dwW;
+ I_pEPD_dc->dwHeight = dwH;
+
+ return tRet;
+}
+EPDFB_DC_RET epdfbdc_set_fbxyoffset(EPDFB_DC *I_pEPD_dc,
+ unsigned long dwFBXOffset,unsigned long dwFBYOffset)
+{
+ EPDFB_DC_RET tRet = EPDFB_DC_SUCCESS;
+ unsigned long dwFBW,dwFBH;
+
+ if(!CHK_EPDFB_DC(I_pEPD_dc)) {
+ ERR_MSG("%s(%d): object handle error !\n",__FUNCTION__,__LINE__);
+ return EPDFB_DC_OBJECTERR;
+ }
+
+ dwFBW = I_pEPD_dc->dwWidth+I_pEPD_dc->dwFBWExtra;
+ dwFBH = I_pEPD_dc->dwHeight+I_pEPD_dc->dwFBHExtra;
+
+ if(dwFBW<dwFBXOffset) {
+ ERR_MSG("%s(%d): FBW(%d)<X(%d) !\n",__FUNCTION__,__LINE__,dwFBW,dwFBXOffset);
+ return EPDFB_DC_PARAMERR;
+ }
+
+ if(dwFBH<dwFBYOffset) {
+ ERR_MSG("%s(%d): FBH(%d)<Y(%d) !\n",__FUNCTION__,__LINE__,dwFBH,dwFBYOffset);
+ return EPDFB_DC_PARAMERR;
+ }
+
+ I_pEPD_dc->dwFBXOffset = dwFBXOffset;
+ I_pEPD_dc->dwFBYOffset = dwFBYOffset;
+
+ return tRet;
+}
+
+EPDFB_DC_RET epdfbdc_rotate(EPDFB_DC *I_pEPD_dc,EPDFB_ROTATE_T I_tRotate)
+{
+ EPDFB_DC_RET tRet = EPDFB_DC_SUCCESS;
+ unsigned char *pbWorkBuf;
+ EPDFB_IMG tEPD_img;
+
+ if(!CHK_EPDFB_DC(I_pEPD_dc)) {
+ ERR_MSG("%s(%d): object handle error !\n",__FUNCTION__,__LINE__);
+ return EPDFB_DC_OBJECTERR;
+ }
+
+ if(I_tRotate == EPDFB_R_0) {
+ // nothing have to do .
+ return EPDFB_DC_SUCCESS;
+ }
+
+ pbWorkBuf = my_malloc(I_pEPD_dc->dwDCSize);
+ if(pbWorkBuf) {
+
+ tEPD_img.dwX = 0;
+ tEPD_img.dwY = 0;
+ tEPD_img.dwW = I_pEPD_dc->dwWidth;
+ tEPD_img.dwH = I_pEPD_dc->dwHeight;
+
+ tEPD_img.bPixelBits = I_pEPD_dc->bPixelBits;
+ tEPD_img.pbImgBuf = pbWorkBuf;
+
+ my_memcpy(pbWorkBuf,I_pEPD_dc->pbDCbuf,I_pEPD_dc->dwDCSize);
+
+ tRet = epdfbdc_put_fbimg(I_pEPD_dc,&tEPD_img,I_tRotate);
+ my_free(pbWorkBuf);pbWorkBuf=0;
+ }
+ else {
+ ERR_MSG("%s(%d): memory not enough !\n",__FUNCTION__,__LINE__);
+ tRet = EPDFB_DC_MEMMALLOCFAIL;
+ }
+
+ return tRet;
+}
+
+EPDFB_DC_RET epdfbdc_set_host_dataswap(EPDFB_DC *I_pEPD_dc,int iIsSet)
+{
+ EPDFB_DC_RET tRet = EPDFB_DC_SUCCESS;
+
+ if(!CHK_EPDFB_DC(I_pEPD_dc)) {
+ ERR_MSG("%s(%d): object handle error !\n",__FUNCTION__,__LINE__);
+ return EPDFB_DC_OBJECTERR;
+ }
+
+ if(iIsSet) {
+ I_pEPD_dc->dwFlags |= EPDFB_DC_FLAG_REVERSEINPDATA;
+ }
+ else {
+ I_pEPD_dc->dwFlags &= ~EPDFB_DC_FLAG_REVERSEINPDATA;
+ }
+
+ return tRet;
+}
+
+EPDFB_DC_RET epdfbdc_set_drive_dataswap(EPDFB_DC *I_pEPD_dc,int iIsSet)
+{
+ EPDFB_DC_RET tRet = EPDFB_DC_SUCCESS;
+
+ if(!CHK_EPDFB_DC(I_pEPD_dc)) {
+ ERR_MSG("%s(%d): object handle error !\n",__FUNCTION__,__LINE__);
+ return EPDFB_DC_OBJECTERR;
+ }
+
+ if(iIsSet) {
+ I_pEPD_dc->dwFlags |= EPDFB_DC_FLAG_SKIPLEFTPIXEL;
+ }
+ else {
+ I_pEPD_dc->dwFlags &= ~EPDFB_DC_FLAG_SKIPLEFTPIXEL;
+ }
+
+ return tRet;
+}
+
+EPDFB_DC_RET epdfbdc_set_skip_pixel(EPDFB_DC *I_pEPD_dc,int iIsSet,int iIsSkipRight)
+{
+ EPDFB_DC_RET tRet = EPDFB_DC_SUCCESS;
+
+ if(!CHK_EPDFB_DC(I_pEPD_dc)) {
+ ERR_MSG("%s(%d): object handle error !\n",__FUNCTION__,__LINE__);
+ return EPDFB_DC_OBJECTERR;
+ }
+
+ if(iIsSkipRight) {
+ if(iIsSet) {
+ I_pEPD_dc->dwFlags |= EPDFB_DC_FLAG_SKIPRIGHTPIXEL;
+ }
+ else {
+ I_pEPD_dc->dwFlags &= ~EPDFB_DC_FLAG_SKIPRIGHTPIXEL;
+ }
+ }
+ else {
+ if(iIsSet) {
+ I_pEPD_dc->dwFlags |= EPDFB_DC_FLAG_SKIPLEFTPIXEL;
+ }
+ else {
+ I_pEPD_dc->dwFlags &= ~EPDFB_DC_FLAG_SKIPLEFTPIXEL;
+ }
+ }
+
+ return tRet;
+}
+
+EPDFB_DC_RET epdfbdc_set_flags(EPDFB_DC *I_pEPD_dc,unsigned long *pIO_dwFlags)
+{
+ unsigned dwOldFlags;
+
+ if(!CHK_EPDFB_DC(I_pEPD_dc)) {
+ ERR_MSG("%s(%d): object handle error !\n",__FUNCTION__,__LINE__);
+ return EPDFB_DC_OBJECTERR;
+ }
+
+ if(!pIO_dwFlags) {
+ return EPDFB_DC_PARAMERR;
+ }
+
+ dwOldFlags = I_pEPD_dc->dwFlags ;
+ I_pEPD_dc->dwFlags = *pIO_dwFlags;
+ *pIO_dwFlags = dwOldFlags;
+ return EPDFB_DC_SUCCESS;
+}
+
+EPDFB_DC_RET epdfbdc_get_flags(EPDFB_DC *I_pEPD_dc,unsigned long *pO_dwFlags)
+{
+
+ if(!CHK_EPDFB_DC(I_pEPD_dc)) {
+ ERR_MSG("%s(%d): object handle error !\n",__FUNCTION__,__LINE__);
+ return EPDFB_DC_OBJECTERR;
+ }
+
+ if(!pO_dwFlags) {
+ return EPDFB_DC_PARAMERR;
+ }
+
+ *pO_dwFlags = I_pEPD_dc->dwFlags;
+ return EPDFB_DC_SUCCESS;
+}
+
+
+
diff --git a/drivers/video/mxc/epdfb_dc.h b/drivers/video/mxc/epdfb_dc.h
new file mode 100644
index 00000000..d25eae07
--- /dev/null
+++ b/drivers/video/mxc/epdfb_dc.h
@@ -0,0 +1,184 @@
+/***************************************************
+ * EPD frame buffer device content manager (4 bits).
+ *
+ * Author : Gallen Lin
+ * Data : 2011/01/20
+ * Revision : 1.0
+ * File Name : epdfb_dc.h
+ *
+****************************************************/
+
+#ifndef __epdfb_dc_h//[
+#define __epdfb_dc_h
+
+
+typedef enum {
+ EPDFB_DC_SUCCESS = 0,
+ EPDFB_DC_PARAMERR = -1,
+ EPDFB_DC_MEMMALLOCFAIL = -2,
+ EPDFB_DC_OBJECTERR = -3, // dc object content error .
+ EPDFB_DC_PIXELBITSNOTSUPPORT = -4,
+} EPDFB_DC_RET;
+
+typedef enum {
+ EPDFB_R_0 = 0,
+ EPDFB_R_90 = 1,
+ EPDFB_R_180 = 2,
+ EPDFB_R_270 = 3,
+} EPDFB_ROTATE_T;
+
+typedef struct tagEPDFB_IMG {
+ unsigned long dwX,dwY;
+ unsigned long dwW,dwH;
+ unsigned char bPixelBits,bReserve;
+ unsigned char *pbImgBuf;
+} EPDFB_IMG;
+
+typedef void (*fnVcomEnable)(int iIsEnable);
+typedef void (*fnPwrOnOff)(int iIsOn);
+typedef void (*fnPwrAutoOffIntervalMax)(int iIsEnable);
+typedef void (*fnAutoOffEnable)(int iIsEnable);
+typedef int (*fnGetWaveformBpp)(void);
+typedef int (*fnSetPartialUpdate)(int iIsPartial);
+typedef unsigned char *(*fnGetRealFrameBuf)(void);
+typedef unsigned char *(*fnGetRealFrameBufEx)(unsigned long *O_pdwFBSize);
+typedef void (*fnDispStart)(int iIsStart);
+typedef int (*fnGetWaveformMode)(void);
+typedef void (*fnSetWaveformMode)(int iWaveformMode);
+typedef int (*fnIsUpdating)(void);
+typedef int (*fnWaitUpdateComplete)(void);
+typedef int (*fnSetUpdateRect)(unsigned short wX,unsigned short wY,
+ unsigned short wW,unsigned short wH);
+typedef int (*fnPutImg)(EPDFB_IMG *I_ptPutImage,EPDFB_ROTATE_T I_tRotate);
+typedef int (*fnSetVCOM)(int iVCOM_set_mV);
+typedef int (*fnSetVCOMToFlash)(int iVCOM_set_mV);
+typedef int (*fnGetVCOM)(int *O_piVCOM_get_mV);
+
+
+// epd framebuffer device content ...
+typedef struct tagEPDFB_DC {
+ // public :
+ unsigned long dwWidth;// visible DC width .
+ unsigned long dwHeight;// visible DC height .
+ unsigned long dwFBWExtra; // frame buffer width extra width .
+ unsigned long dwFBHExtra; // frame buffer height extra height .
+ unsigned long dwFBXOffset; // frame buffer X offset .
+ unsigned long dwFBYOffset; // frame buffer Y offset .
+ unsigned char bPixelBits;
+ unsigned char bReservedA[1];
+ volatile unsigned char *pbDCbuf;// DC buffer .
+
+ fnGetWaveformBpp pfnGetWaveformBpp;
+ fnVcomEnable pfnVcomEnable;
+ fnSetPartialUpdate pfnSetPartialUpdate;
+ fnGetRealFrameBuf pfnGetRealFrameBuf;
+ fnGetRealFrameBufEx pfnGetRealFrameBufEx;
+ fnDispStart pfnDispStart;
+ fnGetWaveformMode pfnGetWaveformMode;
+ fnSetWaveformMode pfnSetWaveformMode;
+ fnIsUpdating pfnIsUpdating;
+ fnWaitUpdateComplete pfnWaitUpdateComplete;
+ fnSetUpdateRect pfnSetUpdateRect;
+ fnPwrAutoOffIntervalMax pfnPwrAutoOffIntervalMax;
+ fnPwrOnOff pfnPwrOnOff;
+ fnAutoOffEnable pfnAutoOffEnable;
+ fnPutImg pfnPutImg;
+ fnSetVCOM pfnSetVCOM;
+ fnSetVCOMToFlash pfnSetVCOMToFlash;
+ fnGetVCOM pfnGetVCOM;
+
+ // private : do not modify these member var .
+ // only for epdfbdc manager .
+ unsigned long dwMagicPrivateBegin;
+
+ unsigned long dwDCSize; // visible dc size .
+ unsigned long dwBufSize; // dc buffer real size .
+
+ unsigned long dwDCWidthBytes;
+ unsigned long dwFlags;
+ int iWFMode,iLastWFMode;
+ int iIsForceWaitUpdateFinished;
+ unsigned long dwDirtyOffsetStart,dwDirtyOffsetEnd;
+ unsigned long dwMagicPrivateEnd;
+ int dither8[16][16];
+} EPDFB_DC;
+
+
+#define epdfbdc_create_e60mt2() epdfbdc_create(800,600,4,0)
+
+EPDFB_DC *epdfbdc_create(unsigned long dwW,unsigned long dwH,\
+ unsigned char bPixelBits,unsigned char *pbDCbuf);
+
+#define EPDFB_DC_FLAG_DEFAUT (0x0)
+
+#define EPDFB_DC_FLAG_REVERSEDRVDATA 0x00000001 // reverse drive data -> pixel 3,pixel 4,pixel 1,pixel 2 .
+#define EPDFB_DC_FLAG_REVERSEINPDATA 0x00000002 // reverse input data -> pixel 3,pixel 4,pixel 1,pixel 2 .
+
+#define EPDFB_DC_FLAG_SKIPLEFTPIXEL 0x00000004 // shift input image data (skip 1 pixel in image left side).
+#define EPDFB_DC_FLAG_SKIPRIGHTPIXEL 0x00000008 // shift input image data (skip 1 pixel in image right side).
+
+
+#define EPDFB_DC_FLAG_DITHER8 0x00000100 //
+
+#define EPDFB_DC_FLAG_OFB_RGB565 0x00010000 // output framebuffer data format is rgb565 .
+#define EPDFB_DC_FLAG_FLASHDIRTY 0x00020000 // only flash dirty image .
+
+
+
+EPDFB_DC *epdfbdc_create_ex(unsigned long dwW,unsigned long dwH,\
+ unsigned char bPixelBits,unsigned char *pbDCbuf,unsigned long dwCreateFlag);
+
+EPDFB_DC *epdfbdc_create_ex2(unsigned long dwFBW,unsigned long dwFBH,\
+ unsigned long dwW,unsigned long dwH,\
+ unsigned char bPixelBits,unsigned char *pbDCbuf,unsigned long dwCreateFlag);
+
+
+EPDFB_DC_RET epdfbdc_delete(EPDFB_DC *I_pEPD_dc);
+
+
+
+
+EPDFB_DC_RET epdfbdc_fbimg_normallize(EPDFB_DC *I_pEPD_dc,\
+ EPDFB_IMG *IO_pEPD_img);
+
+EPDFB_DC_RET epdfbdc_put_fbimg(EPDFB_DC *I_pEPD_dc,\
+ EPDFB_IMG *I_pEPD_img,EPDFB_ROTATE_T I_tRotateDegree);
+
+EPDFB_DC_RET epdfbdc_put_dcimg(EPDFB_DC *pEPD_dc,
+ EPDFB_DC *pEPD_dcimg,EPDFB_ROTATE_T tRotateDegree,
+ unsigned long I_dwDCimgX,unsigned long I_dwDCimgY,
+ unsigned long I_dwDCimgW,unsigned long I_dwDCimgH,
+ unsigned long I_dwDCPutX,unsigned long I_dwDCPutY);
+
+EPDFB_DC_RET epdfbdc_get_dirty_region(EPDFB_DC *I_pEPD_dc,\
+ unsigned long *O_pdwDirtyOffsetStart,unsigned long *O_pdwDirtyOffsetEnd);
+
+EPDFB_DC_RET epdfbdc_set_pixel(EPDFB_DC *I_pEPD_dc,\
+ unsigned long I_dwX,unsigned long I_dwY,unsigned long I_dwPVal);
+
+EPDFB_DC_RET epdfbdc_dcbuf_to_RGB565(EPDFB_DC *I_pEPD_dc,\
+ unsigned char *O_pbRGB565Buf,unsigned long I_dwRGB565BufSize);
+
+EPDFB_DC_RET epdfbdc_rotate(EPDFB_DC *I_pEPD_dc,EPDFB_ROTATE_T I_tRotate);
+EPDFB_DC_RET epdfbdc_set_host_dataswap(EPDFB_DC *I_pEPD_dc,int I_isSet);
+EPDFB_DC_RET epdfbdc_set_drive_dataswap(EPDFB_DC *I_pEPD_dc,int I_isSet);
+EPDFB_DC_RET epdfbdc_set_skip_pixel(EPDFB_DC *I_pEPD_dc,int iIsSet,int iIsSkipRight);
+
+EPDFB_DC_RET epdfbdc_get_rotate_active(EPDFB_DC *I_pEPD_dc,
+ unsigned long *IO_pdwX,unsigned long *IO_pdwY,
+ unsigned long *IO_pdwW,unsigned long *IO_pdwH,
+ EPDFB_ROTATE_T I_tRotate);
+
+EPDFB_DC_RET epdfbdc_set_width_height(EPDFB_DC *I_pEPD_dc,
+ unsigned long dwFBW,unsigned long dwFBH,
+ unsigned long dwW,unsigned long dwH);
+
+EPDFB_DC_RET epdfbdc_set_fbxyoffset(EPDFB_DC *I_pEPD_dc,
+ unsigned long dwFBXOffset,unsigned long dwFBYOffset);
+
+EPDFB_DC_RET epdfbdc_set_flags(EPDFB_DC *I_pEPD_dc,unsigned long *pIO_dwFlags);
+
+EPDFB_DC_RET epdfbdc_get_flags(EPDFB_DC *I_pEPD_dc,unsigned long *pO_dwFlags);
+
+#endif //]__epdfb_dc_h
+
diff --git a/drivers/video/mxc/fake_s1d13522.c b/drivers/video/mxc/fake_s1d13522.c
new file mode 100644
index 00000000..ba0e26cb
--- /dev/null
+++ b/drivers/video/mxc/fake_s1d13522.c
@@ -0,0 +1,3149 @@
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/syscalls.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/fcntl.h>
+#include <asm/uaccess.h>
+#include <linux/delay.h>
+
+
+#include "DataType.h"
+#include "s1d13521ioctl.h"
+
+#include "s1d13521.h"
+#include "s1d13521fb.h"
+
+#ifndef GDEBUG //[
+ #define GDEBUG 0
+#endif //]GDEBUG
+#include <linux/gallen_dbg.h>
+
+#include "epdfb_dc.h"
+#include "fake_s1d13522.h"
+#include "ntx_hwconfig.h"
+
+
+#define _PVI_IOCTL_INTERFACE 1
+#define _QT_SUPPORT 1
+
+
+
+
+#define BusIssueReadReg(wRegIdx) S1D13522_reg_read(wRegIdx)
+#define BusIssueWriteReg(wRegIdx,wValue) S1D13522_reg_write(wRegIdx,wValue)
+
+
+
+extern volatile NTX_HWCONFIG *gptHWCFG;
+
+
+
+// for s1d13522 hardware registers .
+#define S1D13522_REGADDR_PRODUCTCODE 0x0002 // Product code register .
+#define S1D13522_REGADDR_LUTSTATUS 0x0336 // Lookup table status register .
+#define S1D13522_REGADDR_HOSTIFMACONF 0x0140 // Host interface memory access configuration .
+#define S1D13522_REGADDR_GENCONF 0x032C // General configuration register .
+#define S1D13522_REGADDR_HOSTMEMPIXSWAP 0x015E // Host memory access pixel swap configuration .
+#define S1D13522_REGADDR_PUBC 0x0330 // Panel Update Buffer Configuration .
+#define S1D13522_REGADDR_DSPENGINTRSR 0x033A // Display Engine Interrupt Raw Status Register .
+//static uint16_t gwS1d13522_hostifmaconf = 0x0000; //register 0x140, host packed pixel is 4 bits.
+static uint16_t gwS1d13522_GenConf = 0x0000; //register 0x32C, General config configuration register .
+static uint16_t gwS1d13522_hostmempixswapconf = 0x0000; //register 0x15E, host packed pixel is 4 bits.
+static uint16_t gwS1d13522_DspEngIntrRSR = 0x0000;// Display Engine Interrupt Raw Status Register .
+
+
+// for epdfb_dc .
+static epdfb_rotate_t gtRotate = epdfb_rotate_0;
+static int giPixelBits = 4;//
+static EPDFB_DC *gptEPD_dc_current ;// epd device content .
+
+static unsigned short gwScrW=800,gwScrH=600;
+
+//static DECLARE_COMPLETION(fake13522_inited);
+
+//static u32 currHeight=800,currWidth=600;
+#define S1D13522_PRODUCTCODE 0x004d
+
+
+#ifdef _QT_SUPPORT //[
+ // vars ...
+ static int resolve_conflict_step;
+ static unsigned long gLastUpdParm0;
+
+
+ static BOOL bConflictTimerRunning; // KEG 20090825
+ static struct timer_list conflict_resolution_timer; // KEG 20090825
+
+ // functions ...
+ static void s1d13521fb_resolve_conflict(unsigned long dummy);
+
+ #define _ES_LUTNOWAIT_ 1
+
+#endif //] _QT_SUPPORT
+
+#ifdef _PVI_IOCTL_INTERFACE //[
+#define _PVI_IMAGE_SIZE (S1D_DISPLAY_HEIGHT*S1D_DISPLAY_WIDTH/4)
+#define _PVI_IMAGE_SIZE_800x600 (800*600/4)
+
+//static BYTE *ImagePVI_BufferA[_PVI_IMAGE_SIZE];
+//static BYTE *ImagePVI = ImagePVI_BufferA;
+static BYTE *ImagePVI ;
+
+//static BYTE gbTemp4BitsFBA[_PANEL_HEIGHT*_PANEL_WIDTH/(8/S1D_DISPLAY_BPP)];
+
+
+static struct timer_list pvi_refersh_timer;
+
+int pvi_Init(VOID);
+void pvi_Deinit(VOID);
+#endif //] _PVI_IOCTL_INTERFACE
+
+static ST_PVI_CONFIG ConfigPVI={
+ .CurRotateMode =(_INIT_ROTMODE << 8),
+ // .PastRotateMode=(_INIT_ROTMODE << 8),
+ .PastRotateMode=0xffff,
+ .Deepth=2,
+ .StartX=0,
+ .StartY=0,
+ .Width =_PANEL_HEIGHT,
+ .Height=_PANEL_WIDTH,
+
+ .Reg0x140=0,
+ .Reg0x330=0,
+
+ .ReverseGrade=0
+};
+
+
+#ifdef SHOW_PROGRESS_BAR
+static DECLARE_WAIT_QUEUE_HEAD(progress_WaitQueue);
+volatile static int gIsProgessRunning=1;
+static VOID PROGRESS_BAR(EPDFB_DC *pDC);
+#endif
+
+
+
+/*
+*
+* for boot argument .
+*
+*/
+#define _TEST_CMDLINE 0
+#define _MYCMDLINE_PARSE 1
+
+#if (_MYCMDLINE_PARSE==1) //[
+#define _MYINIT_TEXT __init
+#define _MYINIT_DATA __initdata
+#else//][
+#define _MYINIT_TEXT __init
+#define _MYINIT_DATA __initdata
+#endif//]
+
+volatile unsigned char *gpbLOGO_paddr;
+volatile unsigned char *gpbLOGO_vaddr;
+volatile unsigned long gdwLOGO_size;
+
+volatile unsigned char *gpbWF_paddr;
+volatile unsigned char *gpbWF_vaddr;
+volatile unsigned long gdwWF_size;
+
+volatile long glVCOM_uV;
+
+int giIsLogoByPass=0;
+
+
+
+/**
+ * \fn _MemoryRequest
+ * \brief allocate memory region for kermit
+ * \param address,
+ * \param memory length
+ * \param name assigned to memory region
+ * \return none
+ */
+static void * _MemoryRequest(u32 addr, u32 len, const char * name)
+{
+ void * mem = NULL;
+ do {
+ printk(KERN_DEBUG "***%s:%d: request memory region! addr=0x%08x, len=%d***\n",
+ __FUNCTION__, __LINE__, addr, len);
+ if (!request_mem_region(addr, len, name)) {
+ printk(KERN_CRIT "fake_s1d13522: request memory region failed! addr=0x%08x, len %d\n", addr, len);
+ break;
+ }
+ mem = (void *) ioremap_nocache(addr, len);
+ if (!mem) {
+ printk(KERN_CRIT "***%s:%d: could not ioremap %s***\n", __FUNCTION__, __LINE__, name);
+ release_mem_region(addr, len);
+ break;
+ }
+ } while (0);
+ return mem;
+}
+
+/**
+ * \fn _MemoryRelease
+ * \brief free kermit's memory region
+ * \param memory region address
+ * \param address
+ * \param memory length
+ * \return none
+ */
+void _MemoryRelease(void * unmap_addr, unsigned long addr, unsigned long len)
+{
+ iounmap(unmap_addr);
+ release_mem_region(addr, len);
+ printk(KERN_INFO "fake_s1d13522: release memory region!\n");
+}
+
+static int _MYINIT_TEXT vcom_uV_setup(char *str)
+{
+ #if (_TEST_CMDLINE == 0)
+ glVCOM_uV = (long)simple_strtol(str,NULL,0);
+ printk("%s() VCOM uV=%d\n",__FUNCTION__,(int)glVCOM_uV);
+ #else
+ printk("%s() str=%s\n",__FUNCTION__,str);
+ #endif
+ return 1;
+}
+
+
+
+
+static int _MYINIT_TEXT waveform_p_setup(char *str)
+{
+ #if (_TEST_CMDLINE == 0)
+ gpbWF_paddr = (unsigned char *)simple_strtoul(str,NULL,0);
+ if(NULL==gpbWF_vaddr) {
+ gpbWF_vaddr = (u32 *)_MemoryRequest((u32)gpbWF_paddr, gdwWF_size, "waveform_p");
+ if(!gpbWF_vaddr) {
+ return 0;
+ }
+ }
+ printk("%s() wf_p=%p,vaddr=%p,size=%lu\n",__FUNCTION__,
+ gpbWF_paddr,gpbWF_vaddr,gdwWF_size);
+
+ #if 0//[
+ {
+ volatile u8 *pbTemp;
+ int i,irequestsize=32;
+
+ printk("gpbWF_vaddr (%p),size=%u = [\n\t",gpbWF_vaddr,irequestsize);
+ if(gpbWF_vaddr) {
+ pbTemp = (u8*)gpbWF_vaddr;
+ for(i=0;i<irequestsize;i++) {
+ printk("%02X,",pbTemp[i]);
+ }
+ }
+ printk("]\n");
+ }
+ #endif //]
+ #else
+ printk("%s() str=%s\n",__FUNCTION__,str);
+ #endif
+ return 1;
+}
+
+
+static int _MYINIT_TEXT waveform_size_setup(char *str)
+{
+ #if (_TEST_CMDLINE == 0)
+ gdwWF_size = (unsigned int)simple_strtoul(str,NULL,0);
+ printk("%s() wf_size=%lu\n",__FUNCTION__,gdwWF_size);
+ #else
+ printk("%s() str=%s\n",__FUNCTION__,str);
+ #endif
+ return 1;
+}
+
+static int _MYINIT_TEXT logo_setup(char *str)
+{
+ #if (_TEST_CMDLINE == 0)
+ if(0==strcmp(str,"bypass")) {
+ printk("%s() bypass logo\n",__FUNCTION__,str);
+ giIsLogoByPass=1;
+ }
+ #else
+ printk("%s() str=%s\n",__FUNCTION__,str);
+ #endif
+ return 1;
+}
+
+static int _MYINIT_TEXT logo_p_setup(char *str)
+{
+ #if (_TEST_CMDLINE == 0)
+ gpbLOGO_paddr = (unsigned char *)simple_strtoul(str,NULL,0);
+ if(NULL==gpbLOGO_vaddr) {
+ gpbLOGO_vaddr = (u32 *)_MemoryRequest((u32)gpbLOGO_paddr, gdwLOGO_size, "logo_p");
+ if(!gpbWF_vaddr) {
+ return 0;
+ }
+ }
+ printk("%s() logo_p=%p,vaddr=%p,size=%lu\n",__FUNCTION__,
+ gpbLOGO_paddr,gpbLOGO_vaddr,gdwLOGO_size);
+ #else
+ printk("%s() str=%s\n",__FUNCTION__,str);
+ #endif
+ return 1;
+}
+
+static int _MYINIT_TEXT logo_size_setup(char *str)
+{
+ #if (_TEST_CMDLINE == 0)
+ gdwLOGO_size = (int)simple_strtoul(str,NULL,0);
+ printk("%s() logo_szie=%lu\n",__FUNCTION__,gdwLOGO_size);
+ #else
+ printk("%s() str=%s\n",__FUNCTION__,str);
+ #endif
+ return 1;
+}
+
+
+
+
+#if (_MYCMDLINE_PARSE==0) //[
+__setup("vcom=",vcom_uV_setup);
+__setup("waveform_p=",waveform_p_setup);
+__setup("waveform_sz=",waveform_size_setup);
+__setup("logo_p=",logo_p_setup);
+__setup("logo_sz=",logo_size_setup);
+__setup("logo=",logo_setup);
+#else //][
+void fake_s1d13522_parse_epd_cmdline(void)
+{
+ static int iParseCnt = 0;
+ char *pcPatternStart,*pcPatternVal,*pcPatternValEnd,cTempStore='\0';
+ unsigned long ulPatternLen;
+
+ char *szParsePatternA[]={"vcom=","waveform_sz=","waveform_p=","logo_sz=","logo_p=","logo="};
+ int ((*pfnDispatchA[])(char *str))={ \
+ vcom_uV_setup,waveform_size_setup,waveform_p_setup,\
+ logo_size_setup,logo_p_setup,logo_setup};
+
+ int i;
+ char *pszCmdLineBuf;
+
+
+ if(iParseCnt++>0) {
+ WARNING_MSG("%s : epd cmdline parse already done .\n",__FUNCTION__);
+ return ;
+ }
+ //printk("%s():cmdline(%d)=%s\n",__FUNCTION__,strlen(saved_command_line),saved_command_line);
+
+ pszCmdLineBuf = kmalloc(strlen(saved_command_line)+1,GFP_KERNEL);
+ ASSERT(pszCmdLineBuf);
+ strcpy(pszCmdLineBuf,saved_command_line);
+ //printk("%s():cp cmdline=%s\n",__FUNCTION__,pszCmdLineBuf);
+
+ for(i=0;i<sizeof(szParsePatternA)/sizeof(szParsePatternA[0]);i++) {
+ ulPatternLen = strlen(szParsePatternA[i]);
+ pcPatternStart = strstr(pszCmdLineBuf,szParsePatternA[i]);
+ if(pcPatternStart) {
+ pcPatternVal=pcPatternStart + ulPatternLen ;
+ pcPatternValEnd = strchr(pcPatternVal,' ');
+ if(pcPatternValEnd) {
+ cTempStore = *pcPatternValEnd;
+ *pcPatternValEnd = '\0';
+ }
+ //printk("%s():pattern \"%s\" ,val=\"%s\"\n",__FUNCTION__,szParsePatternA[i],pcPatternVal);
+ pfnDispatchA[i](pcPatternVal);
+ if(pcPatternValEnd) {
+ *pcPatternValEnd = cTempStore;
+ }
+ }
+ }
+
+ if(pszCmdLineBuf) {
+ kfree(pszCmdLineBuf);
+ }
+}
+#endif //]
+
+
+
+int fake_s1d13522_is_logo_bypss(void) {
+ return giIsLogoByPass;
+}
+
+
+//
+// file operation to emulate S1D13521 flash read/write .
+//
+static int read_file(char *filename, char *pBuffer)
+{
+ struct file *file;
+ loff_t pos = 0;
+ int result = 0;
+ mm_segment_t old_fs = get_fs();
+
+ file = filp_open(filename, O_RDONLY, 0);
+
+ if (IS_ERR(file)) {
+ printk ("[%s-%d] failed open %s,(%p)\n",__func__,__LINE__,filename,file);
+ }
+ else {
+ ssize_t tSz;
+ set_fs(KERNEL_DS);
+ printk(KERN_DEBUG);
+ tSz = file->f_op->read(file, pBuffer, 4096, &file->f_pos);
+ if(tSz!=4096) {
+ printk ("[%s-%d] failed read %s,%d!=4096\n",__func__,__LINE__,filename,tSz);
+ result = 0;
+ }
+ else {
+ result = 1;
+ }
+ filp_close(file, NULL);
+ set_fs(old_fs);
+ }
+ return result;
+}
+
+int fake_s1d13522_write_file_ex2(char *filename, char *data,unsigned long dwDataSize,unsigned long dwSeekSize)
+{
+ struct file *file;
+ loff_t pos = 0;
+ int fd;
+ int iRet = 1;
+ loff_t fsz_chk,fsz_temp ;
+ mm_segment_t old_fs;
+
+ file = filp_open(filename, O_WRONLY|O_CREAT, 0644);
+
+ if (IS_ERR(file)) {
+ printk ("[%s-%d] failed open %s,(%p)\n",__func__,__LINE__,filename,file);
+ }
+ else {
+ ssize_t tSz;
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ fsz_temp = (loff_t)dwSeekSize;
+ fsz_chk = file->f_op->llseek(file,fsz_temp,SEEK_SET);
+ if(fsz_temp!=fsz_chk) {
+ printk("[WARNING] %s() : seek size check fail %d!=%d\n",__FUNCTION__,fsz_chk,fsz_temp);
+ }
+ tSz = file->f_op->write(file, (char *)data, dwDataSize, &file->f_pos);
+ if(dwDataSize!=tSz) {
+ printk ("[%s-%d] failed write %s,%d!=4096\n",__func__,__LINE__,filename,tSz);
+ iRet = 0;
+ }
+ filp_close(file, NULL);
+ set_fs(old_fs);
+ }
+ return iRet;
+
+}
+
+static int write_file_ex(char *filename, char *data,unsigned long dwSize)
+{
+ return fake_s1d13522_write_file_ex2(filename,data,dwSize,0);
+}
+
+static int write_file(char *filename, char *data)
+{
+ return write_file_ex(filename, data,4096);
+}
+
+static char *gFlashBuffer;
+static BOOL Flash_File_Erase(DWORD FlashAddress, DWORD Length)
+{
+ BOOL fRet = TRUE;
+ unsigned char FilenameA[256];
+
+ if (!gFlashBuffer)
+ gFlashBuffer = __get_free_pages(GFP_KERNEL, 1);
+ sprintf(FilenameA,"/etc/epson_flash_%08X",(FlashAddress&0xFFFFF000));
+ memset (gFlashBuffer, 0xFF, 4096);
+ write_file (FilenameA, gFlashBuffer);
+ return fRet;
+}
+
+static BOOL Flash_File_Write(DWORD FlashAddress, DWORD Length, PBYTE pData)
+{
+ BOOL fRet = TRUE;
+ unsigned char FilenameA[256];
+
+ if (!gFlashBuffer) {
+ gFlashBuffer = __get_free_pages(GFP_KERNEL, 1);
+ ASSERT(gFlashBuffer);
+ }
+ sprintf(FilenameA,"/etc/epson_flash_%08X",(FlashAddress&0xFFFFF000));
+
+ if (!read_file (FilenameA, gFlashBuffer)) {
+ memset (gFlashBuffer, 0xFF, 4096);
+ }
+ memcpy (gFlashBuffer+(FlashAddress&0x0FFF), pData, Length);
+ if(!write_file (FilenameA, gFlashBuffer)) {
+ printk("[kenel]%s-%d : write_file %s fail !!\n",__FUNCTION__,__LINE__,FilenameA);
+ fRet = 0;
+ }
+ return fRet;
+}
+
+static BOOL Flash_File_Read(DWORD FlashAddress, DWORD Length, PBYTE pData)
+{
+ BOOL fRet = TRUE;
+ unsigned char FilenameA[256];
+
+ if (!gFlashBuffer) {
+ gFlashBuffer = __get_free_pages(GFP_KERNEL, 1);
+ ASSERT(gFlashBuffer);
+ }
+
+
+ sprintf(FilenameA,"/etc/epson_flash_%08X",(FlashAddress&0xFFFFF000));
+ if (read_file (FilenameA, gFlashBuffer)) {
+ memcpy (pData, gFlashBuffer+(FlashAddress&0x0FFF), Length);
+ }
+ else {
+ memset (pData, 0xFF, Length);
+ }
+ return fRet;
+}
+
+#define WAVEFORM_4BIT_MARK 0x20000
+static int BusIssueFlashOperation(PS1D13532_FLASH_PACKAGE pFlashControl,EPDFB_DC *pDC)
+{
+int RetCode=-1;
+ //DEBUGMSG(_DUMP_IOCTL_FLASH, "pFlashControl->Command=%u\n", pFlashControl->Command);
+ //DEBUGMSG(_DUMP_IOCTL_FLASH, "pFlashControl->StartAddr=0x%08x\n", pFlashControl->StartAddr);
+ //DEBUGMSG(_DUMP_IOCTL_FLASH, "pFlashControl->DataLength=0x%08x\n", pFlashControl->DataLength);
+ //DEBUGMSG(_DUMP_IOCTL_FLASH, "pFlashControl->Buf=0x%p\n", pFlashControl->Buf);
+
+ switch(pFlashControl->Command) {
+ case _FLASH_CMD_GET_INFO:
+ printk ("[%s-%d] Flash_GetID (0x%08X);\n",__func__, __LINE__, pFlashControl->Buf);
+ //DEBUGMSG(_DUMP_IOCTL_FLASH, "IDCode is 0x%08x\n", *((PDWORD) pFlashControl->Buf));
+ RetCode=4;
+ break;
+
+ case _FLASH_CMD_ERASE:
+ if(Flash_File_Erase(pFlashControl->StartAddr, pFlashControl->DataLength) == FALSE)
+ break;
+ RetCode=0;
+ break;
+
+ case _FLASH_CMD_WRITE:
+ if(Flash_File_Write(pFlashControl->StartAddr, pFlashControl->DataLength, pFlashControl->Buf) == FALSE)
+ break;
+ RetCode=0;
+ break;
+
+ case _FLASH_CMD_READ:
+ Flash_File_Read(pFlashControl->StartAddr, pFlashControl->DataLength, pFlashControl->Buf);
+ if (WAVEFORM_4BIT_MARK == pFlashControl->StartAddr) {
+ ASSERT(pDC);
+
+ if(4==pDC->pfnGetWaveformBpp()) {
+ printk ("[%s-%d] return wf 4 bit flag\n",__func__, __LINE__);
+ pFlashControl->Buf[0] = 0x0a;
+ }
+ else {
+ printk ("[%s-%d] return wf 3 bit flag\n",__func__, __LINE__);
+ pFlashControl->Buf[0] = 0x0f;
+ }
+ }
+ break;
+
+ default:
+ return FALSE;
+ };
+ return RetCode;
+}
+
+
+static void fake_s1d13522_setwfmode(EPDFB_DC *pDC,int iWFMode)
+{
+ DBG_MSG("%s(),wf.mode=%d\n",__FUNCTION__,iWFMode);
+ if(pDC->pfnSetWaveformMode) {
+ pDC->pfnSetWaveformMode(iWFMode);
+ }
+ pDC->iWFMode = iWFMode;
+}
+
+
+void fake_s1d13522_progress_start(EPDFB_DC *pDC)
+{
+#ifdef SHOW_PROGRESS_BAR//[
+ int ret;
+ ret = kernel_thread(PROGRESS_BAR,pDC,CLONE_KERNEL);
+ if(ret < 0){
+ printk("Progress bar thread creat error\n");
+ }
+#endif //]SHOW_PROGRESS_BAR
+}
+
+void fake_s1d13522_progress_stop(void)
+{
+ DBG_MSG("====%s()====!\n",__FUNCTION__);
+ gIsProgessRunning = 0;
+}
+
+/*********************************************************
+ *
+ *
+ *********************************************************/
+EPDFB_DC *fake_s1d13522_initEx3(unsigned char bBitsPerPixel,unsigned char *pbDCBuf,
+ unsigned short wScrW,unsigned short wScrH,unsigned short wFBW,unsigned short wFBH)
+{
+ EPDFB_DC *pDC = 0;
+
+#ifdef _PVI_IOCTL_INTERFACE//[
+ pvi_Init();
+#endif//]_PVI_IOCTL_INTERFACE
+
+#if (_MYCMDLINE_PARSE==1) //[
+ fake_s1d13522_parse_epd_cmdline();
+#endif //]
+
+ if(0==glVCOM_uV) {
+ unsigned short wVCOM_10mV = 0 ;
+ short sVCOM_10mV;
+
+ wVCOM_10mV = gptHWCFG->m_val.bVCOM_10mV_HiByte<<8|gptHWCFG->m_val.bVCOM_10mV_LoByte;
+ sVCOM_10mV = (short) wVCOM_10mV;
+ glVCOM_uV = (long)sVCOM_10mV*10000;
+
+ if(0!=glVCOM_uV) {
+ printk("%s:VCOM from HWCONFIG 0x%x=>%hd 10mV,%ld uV\n",
+ __FUNCTION__,wVCOM_10mV,sVCOM_10mV,glVCOM_uV);
+ }
+ else {
+ // Actually ,we don't need to set VCOM if glVCOM_uV=0
+ }
+ }
+
+ gwScrW = wScrW;
+ gwScrH = wScrH;
+ //pDC = epdfbdc_create_ex2(wFBW,wFBH,gwScrW,gwScrH,bBitsPerPixel,pbDCBuf,\
+ // EPDFB_DC_FLAG_REVERSEDRVDATA|EPDFB_DC_FLAG_SKIPRIGHTPIXEL);
+
+ pDC = epdfbdc_create_ex2(wFBW,wFBH,gwScrW,gwScrH,bBitsPerPixel,pbDCBuf,\
+ EPDFB_DC_FLAG_REVERSEDRVDATA);
+
+ //currWidth = _INIT_HSIZE;
+ //currHeight = _INIT_VSIZE;
+ if(pDC) {
+ //ImagePVI = pDC->pbDCbuf;
+ gptEPD_dc_current = pDC;
+ }
+
+#ifdef _QT_SUPPORT//[
+ #ifndef _ES_LUTNOWAIT_ //[
+ init_timer(&conflict_resolution_timer);
+ conflict_resolution_timer.function = s1d13521fb_resolve_conflict;
+ #endif //]
+ bConflictTimerRunning = FALSE;
+#endif //] _QT_SUPPORT
+
+ if(1==gptHWCFG->m_val.bUIStyle) {
+ gtRotate = epdfb_rotate_90;
+ }
+
+ //complete_all(&fake13522_inited);
+ return pDC;
+}
+EPDFB_DC *fake_s1d13522_initEx2(unsigned char bBitsPerPixel,unsigned char *pbDCBuf,
+ unsigned short wScrW,unsigned short wScrH)
+{
+ return fake_s1d13522_initEx3(bBitsPerPixel,pbDCBuf,wScrW,wScrH,wScrW,wScrH);
+}
+EPDFB_DC *fake_s1d13522_initEx(unsigned char bBitsPerPixel,unsigned char *pbDCBuf)
+{
+ return fake_s1d13522_initEx2(bBitsPerPixel,pbDCBuf,800,600);
+}
+
+
+EPDFB_DC *fake_s1d13522_init(void)
+{
+ return fake_s1d13522_initEx(4,0);
+}
+
+int fake_s1d13522_release(EPDFB_DC *pDC)
+{
+ int iRet = 0;
+
+#ifdef _QT_SUPPORT//[
+ #ifndef _ES_LUTNOWAIT_ //[
+ del_timer(&conflict_resolution_timer); // KEG 20090825
+ #endif //]
+ bConflictTimerRunning = FALSE;
+#endif //] _QT_SUPPORT
+
+ if(pDC) {
+ epdfbdc_delete(pDC);
+ gptEPD_dc_current = 0;
+ }
+ if(gpbLOGO_vaddr) {
+ //kermitMemoryRelease(gpbLOGO_vaddr,,);
+ //gpbLOGO_vaddr = NULL;
+ }
+ if(gpbWF_vaddr) {
+ //gpbWF_vaddr = NULL;
+ }
+ pvi_Deinit();
+ return iRet;
+}
+
+int fake_s1d13522_display_img(u16 wX,u16 wY,u16 wW,u16 wH,u8 *pbImgBuf,
+ EPDFB_DC *pDC,int iPixelBits,epdfb_rotate_t I_tRotate)
+{
+ int iRet = 0;
+ EPDFB_IMG tEPD_img;
+ EPDFB_ROTATE_T tRotate;
+ u16 wTemp ;
+ unsigned int uiCnt;
+ int iIsFullScreen = 0;
+ volatile u8 *pbRealFB = 0;
+
+
+ ASSERT(pDC);
+
+ DBG_MSG("%s() : x=%d,y=%d,w=%d,h=%d,r=%d,bit=%d,p=%p,dc.w=%d,dc.h=%d,dc.fb.w+=%d,dc.fb.h+=%d,dc.wfmode=%d\n",__FUNCTION__,\
+ wX,wY,wW,wH,I_tRotate,iPixelBits,pbImgBuf,
+ pDC->dwWidth,pDC->dwHeight,pDC->dwFBWExtra,
+ pDC->dwFBHExtra,pDC->iWFMode);
+
+ uiCnt = 0 ;
+
+
+
+ if(0==wW||0==wH) {
+ DBG_MSG("[%s] skip !!,W or H =0 !!\n",__FUNCTION__);
+ return -1;
+ }
+
+ if( 1==pDC->iIsForceWaitUpdateFinished || pDC->iWFMode!=pDC->iLastWFMode )
+ {
+ if(pDC->pfnWaitUpdateComplete) {
+ pDC->pfnWaitUpdateComplete();
+ }
+ else {
+ while(pDC->pfnIsUpdating()) {
+ schedule_timeout(1);
+ if(printk_ratelimit()) {
+ printk("EPD updating ... (t=%u,cnt=%lu)\n",jiffies,++uiCnt);
+ }
+ }
+ }
+ }
+
+ //tEPD_img.dwX = wX&0xfffe;
+ //tEPD_img.dwY = wY&0xfffe;
+
+ tEPD_img.dwX = wX;
+ tEPD_img.dwY = wY;
+
+ tEPD_img.dwW = wW;
+ if(wW&0x1) {
+ tEPD_img.dwW += 1;
+ }
+ else {
+ }
+
+ tEPD_img.dwH = wH;
+
+
+ tEPD_img.pbImgBuf = pbImgBuf;
+ tEPD_img.bPixelBits = (unsigned char)(iPixelBits);
+ switch (I_tRotate) {
+ case epdfb_rotate_0:
+ tRotate = EPDFB_R_0;
+ break;
+ case epdfb_rotate_90:
+ tRotate = EPDFB_R_90;
+ break;
+ case epdfb_rotate_180:
+ tRotate = EPDFB_R_180;
+ break;
+ case epdfb_rotate_270:
+ tRotate = EPDFB_R_270;
+ break;
+ default:
+ ASSERT(0);
+ break;
+ }
+
+
+ if(pDC->pfnVcomEnable) {
+ pDC->pfnVcomEnable(1);
+ //VCOM_TRUNON_BY_TIMER(20,pDC);
+ }
+ else {
+ }
+
+ if(pDC->pfnSetPartialUpdate) {
+ #ifdef __KERNEL__//[
+ //if( gptHWCFG && 0==gptHWCFG->m_val.bUIStyle )
+ if( gptHWCFG )
+ {
+ if(wW * wH == pDC->dwWidth * pDC->dwHeight) {
+ DBG_MSG("%s : fullscree update!\n",__FUNCTION__);
+ pDC->pfnSetPartialUpdate(0);
+ }
+ else {
+ pDC->pfnSetPartialUpdate(1);
+ }
+ }
+ #endif //]
+ }
+
+
+
+
+ if(pDC->pfnPutImg) {
+ pDC->pfnPutImg(&tEPD_img,tRotate);
+ }
+ else {
+ //epdfbdc_fbimg_normallize(pDC,&tEPD_img);
+
+ epdfbdc_put_fbimg(pDC,&tEPD_img,tRotate);
+
+ epdfbdc_get_rotate_active(pDC,&tEPD_img.dwX,&tEPD_img.dwY,
+ &tEPD_img.dwW,&tEPD_img.dwH,tRotate);
+
+ if(pDC->pfnSetUpdateRect) {
+ pDC->pfnSetUpdateRect(tEPD_img.dwX,tEPD_img.dwY,tEPD_img.dwW,tEPD_img.dwH);
+ }
+
+ if(pDC->pfnGetRealFrameBufEx) {
+ pbRealFB = (u8 *)pDC->pfnGetRealFrameBufEx(0);
+ }
+ else if(pDC->pfnGetRealFrameBuf) {
+ pbRealFB = (u8 *)pDC->pfnGetRealFrameBuf();
+ }
+
+ if(pDC->pbDCbuf==(unsigned char *)pbRealFB) {
+ }
+ else
+ if(pDC->dwFlags&EPDFB_DC_FLAG_FLASHDIRTY)
+ {
+ // dirty update ...
+ unsigned long dwDirtyOffsetEnd,dwDirtyOffsetStart;
+
+ epdfbdc_get_dirty_region(pDC,&dwDirtyOffsetStart,&dwDirtyOffsetEnd);
+ if(pbRealFB) {
+
+ memcpy((void *)(volatile u32 *)(pbRealFB+dwDirtyOffsetStart),\
+ pDC->pbDCbuf+dwDirtyOffsetStart, dwDirtyOffsetEnd-dwDirtyOffsetStart);
+
+ if(pDC->dwFlags&EPDFB_DC_FLAG_OFB_RGB565) {
+ epdfbdc_dcbuf_to_RGB565(pDC,pbRealFB,(pDC->dwWidth*pDC->dwHeight)>>1);
+ }
+ }
+ }
+ else
+ {
+ // full update ...
+ //extern unsigned int marvell_logo_800x600_size;
+ //extern unsigned const char marvell_logo_800x600[] ;
+ if(pDC->pfnGetRealFrameBufEx) {
+ pbRealFB = (u8 *)pDC->pfnGetRealFrameBufEx(0);
+ }
+ else if(pDC->pfnGetRealFrameBuf) {
+ pbRealFB = (u8 *)pDC->pfnGetRealFrameBuf();
+ }
+
+ if(pbRealFB) {
+ if(pDC->dwFlags&EPDFB_DC_FLAG_OFB_RGB565) {
+ epdfbdc_dcbuf_to_RGB565(pDC,pbRealFB,(pDC->dwWidth*pDC->dwHeight)>>1);
+ }
+ else {
+ memcpy((void *)(volatile u32 *)(pbRealFB),\
+ pDC->pbDCbuf, pDC->dwDCSize);
+
+ //memcpy((void *)(volatile u32 *)((u8 *)pDC->pfnGetRealFrameBuf()),\
+ // marvell_logo_800x600, marvell_logo_800x600_size);
+ }
+ }
+ }
+ }
+
+
+ if(pDC->pfnDispStart) {
+ pDC->pfnDispStart(1);
+ }
+
+ if(pDC->pfnVcomEnable) {
+ pDC->pfnVcomEnable(0);
+ //VCOM_TRUNOFF_BY_TIMER(20,pDC);
+ }
+ else {
+ }
+
+ pDC->iLastWFMode = pDC->iWFMode;
+ pDC->iIsForceWaitUpdateFinished = 0;
+
+ return iRet;
+}
+
+
+int _Epson_displayCMD(PST_IMAGE_PGM pt,EPDFB_DC *pDC)
+{
+ int iRet = 0;
+ u32 newMode ;
+ u32 currMode ;
+ int iCurWfBpp;
+
+ GALLEN_DBGLOCAL_BEGIN();
+
+ ASSERT(pDC->pfnGetWaveformMode);
+ currMode = pDC->pfnGetWaveformMode();
+ ASSERT(pDC->pfnGetWaveformBpp);
+ iCurWfBpp = pDC->pfnGetWaveformBpp();
+
+// setWaveform(par,pt->WaveForm+1);
+ if(257==pt->WaveForm) {
+ // WAVEFORM_MODE_AUTO
+ newMode = pt->WaveForm;
+ }
+ else
+ if (pt->Width*pt->Height == pDC->dwWidth*pDC->dwHeight) {
+ GALLEN_DBGLOCAL_RUNLOG(2);
+ ASSERT(pDC->pfnGetWaveformBpp);
+ if(4==iCurWfBpp)
+ {
+ GALLEN_DBGLOCAL_RUNLOG(3);
+ newMode = 2;// GC16 .
+ }
+ else if(3==iCurWfBpp) {
+ GALLEN_DBGLOCAL_RUNLOG(4);
+ newMode = 3;//
+ }
+ else {
+ GALLEN_DBGLOCAL_RUNLOG(5);
+ ASSERT(0);
+ }
+ }
+ else
+ {
+ GALLEN_DBGLOCAL_RUNLOG(6);
+ newMode = pt->WaveForm;
+ }
+
+ if (newMode != currMode) {
+ GALLEN_DBGLOCAL_RUNLOG(7);
+ /* set wafeforms new mode */
+ ASSERT(pDC->pfnSetWaveformMode);
+ fake_s1d13522_setwfmode(pDC,(int)newMode);
+ }
+
+ fake_s1d13522_display_img(pt->StartX,pt->StartY,pt->Width,pt->Height,\
+ pt->Data,pDC,giPixelBits,gtRotate);
+ GALLEN_DBGLOCAL_END();
+
+ return iRet;
+}
+
+static EN_EPSON_ERROR_CODE S1D13522_reg_write(u16 wIndex, u16 wValue)
+{
+ EN_EPSON_ERROR_CODE tRet = _EPSON_ERROR_SUCCESS;
+
+ GALLEN_DBGLOCAL_BEGIN();
+
+ switch (wIndex) {
+ case S1D13522_REGADDR_HOSTIFMACONF:GALLEN_DBGLOCAL_RUNLOG(10);
+ {
+ unsigned short wRotate;
+ int iPixelBits;
+
+ GALLEN_DBGLOCAL_RUNLOG(11);
+ //gwS1d13522_hostifmaconf = wValue;
+ ConfigPVI.Reg0x140 = wValue;
+
+
+ //wRotate = gwS1d13522_hostifmaconf&0x0300;
+ wRotate = ConfigPVI.Reg0x140&0x0300;
+
+ #if 1 //[ auto rotate by ntx hw config .
+ if(gptHWCFG)
+ {
+
+ if(0x0000==wRotate) {
+ //ASSERT(gptHWCFG);
+ if(0==gptHWCFG->m_val.bDisplayPanel||3==gptHWCFG->m_val.bDisplayPanel||\
+ 6==gptHWCFG->m_val.bDisplayPanel||8==gptHWCFG->m_val.bDisplayPanel||\
+ 9==gptHWCFG->m_val.bDisplayPanel)
+ {
+ // Left Out EPD Panel ...
+ GALLEN_DBGLOCAL_RUNLOG(12);
+ }
+ else {
+ // Right Out EPD Panel (E60MT2,E60820,E60822,E60830,E60832,...)
+ GALLEN_DBGLOCAL_RUNLOG(13);
+ wRotate = 0x0200;// 0->180
+ }
+ }
+ else if(0x0200==wRotate) {
+ ASSERT(gptHWCFG);
+ if(0==gptHWCFG->m_val.bDisplayPanel||3==gptHWCFG->m_val.bDisplayPanel||\
+ 6==gptHWCFG->m_val.bDisplayPanel||8==gptHWCFG->m_val.bDisplayPanel||\
+ 9==gptHWCFG->m_val.bDisplayPanel)
+ {
+ // Left Out EPD Panel ...
+ GALLEN_DBGLOCAL_RUNLOG(12);
+ }
+ else {
+ // Right Out EPD Panel (E60MT2,E60820,E60822,E60830,E60832,...)
+ GALLEN_DBGLOCAL_RUNLOG(13);
+ wRotate = 0x0000;// 180->0
+ }
+ }
+ else if(0x0100==wRotate) {
+ ASSERT(gptHWCFG);
+ if(0==gptHWCFG->m_val.bDisplayPanel||3==gptHWCFG->m_val.bDisplayPanel||\
+ 6==gptHWCFG->m_val.bDisplayPanel||8==gptHWCFG->m_val.bDisplayPanel||\
+ 9==gptHWCFG->m_val.bDisplayPanel)
+ {
+ // Left Out EPD Panel ...
+ GALLEN_DBGLOCAL_RUNLOG(24);
+ wRotate = 0x0300;// 90->270
+ }
+ else {
+ // Right Out EPD Panel (E60MT2,E60820,E60822,E60830,E60832,...)
+ GALLEN_DBGLOCAL_RUNLOG(25);
+ }
+ }
+ else if(0x0300==wRotate) {
+ ASSERT(gptHWCFG);
+ if(0==gptHWCFG->m_val.bDisplayPanel||3==gptHWCFG->m_val.bDisplayPanel||\
+ 6==gptHWCFG->m_val.bDisplayPanel||8==gptHWCFG->m_val.bDisplayPanel||\
+ 9==gptHWCFG->m_val.bDisplayPanel)
+ {
+ // Left Out EPD Panel ...
+ GALLEN_DBGLOCAL_RUNLOG(26);
+ wRotate = 0x0100;// 270->90
+ }
+ else {
+ // Right Out EPD Panel (E60MT2,E60820,E60822,E60830,E60832,...)
+ GALLEN_DBGLOCAL_RUNLOG(27);
+ }
+ }
+ }
+ else {
+ // without ntx hwconfig .
+ if(0x0000==wRotate) {
+ }
+ else if(0x0200==wRotate) {
+ }
+ else if(0x0100==wRotate) {
+ wRotate = 0x0300;// 90->270
+ }
+ else if(0x0300==wRotate) {
+ wRotate = 0x0100;// 270->90
+ }
+ }
+ #endif //]
+
+ wRotate = wRotate >> 8;
+
+ //wRotate = ((gwS1d13522_hostifmaconf>>8) & 0x0003);
+
+ DBG_MSG("%s(%d):rotate change %d=>%d\n",__FILE__,__LINE__,\
+ gtRotate,wRotate);
+ gtRotate = (epdfb_rotate_t)wRotate;
+
+
+ if((0x0020==(ConfigPVI.Reg0x140&0x0030))) {
+ GALLEN_DBGLOCAL_RUNLOG(14);
+ iPixelBits = 4;
+ }
+ else
+ if((0x0030==(ConfigPVI.Reg0x140&0x0030))) {
+ GALLEN_DBGLOCAL_RUNLOG(15);
+ iPixelBits = 8;
+ }
+ else
+ if((0x0010==(ConfigPVI.Reg0x140&0x0030))) {
+ GALLEN_DBGLOCAL_RUNLOG(16);
+ iPixelBits = 2;
+ }
+ else {
+ GALLEN_DBGLOCAL_RUNLOG(17);
+ iPixelBits = 1;
+ }
+ giPixelBits = iPixelBits;
+ }break;
+ case S1D13522_REGADDR_GENCONF:GALLEN_DBGLOCAL_RUNLOG(18);
+ {
+ GALLEN_DBGLOCAL_RUNLOG(19);
+ gwS1d13522_GenConf = wValue;
+
+ //wRotate = ((gwS1d13522_GenConf>>4) & 0x0003);
+ }break;
+ case S1D13522_REGADDR_HOSTMEMPIXSWAP:GALLEN_DBGLOCAL_RUNLOG(20);
+ {
+
+ gwS1d13522_hostmempixswapconf = wValue;
+ if(0x0000==(gwS1d13522_hostmempixswapconf&0x0003)) {
+ GALLEN_DBGLOCAL_RUNLOG(21);
+ epdfbdc_set_host_dataswap(gptEPD_dc_current,0);
+ }
+ else if(0x0003==(gwS1d13522_hostmempixswapconf&0x0003)) {
+ GALLEN_DBGLOCAL_RUNLOG(22);
+ epdfbdc_set_host_dataswap(gptEPD_dc_current,1);
+ }
+ }break;
+ default :
+ WARNING_MSG("%s %d(0x%x) not support !\n",__FUNCTION__,wIndex,wIndex);
+ GALLEN_DBGLOCAL_RUNLOG(23);
+ break;
+ }
+
+ GALLEN_DBGLOCAL_END();
+ return tRet;
+}
+
+static u16 S1D13522_reg_read(u16 wIndex)
+{
+ u16 wRet = 0;
+
+ //DBG_MSG("%s(0x%x)\n",__FUNCTION__,wIndex);
+ switch (wIndex) {
+ case S1D13522_REGADDR_HOSTIFMACONF:
+ wRet = ConfigPVI.Reg0x140;
+ break;
+
+ case S1D13522_REGADDR_PRODUCTCODE:
+ wRet = S1D13522_PRODUCTCODE;
+ break;
+
+ case S1D13522_REGADDR_LUTSTATUS:
+ ASSERT(gptEPD_dc_current);
+ ASSERT(gptEPD_dc_current->pfnIsUpdating);
+ if(gptEPD_dc_current->pfnIsUpdating()) {
+ wRet = 0xffff;
+ }
+ else {
+ wRet = 0x0000;
+ }
+ break;
+
+ case S1D13522_REGADDR_GENCONF:
+ wRet = gwS1d13522_GenConf;
+ break;
+ case S1D13522_REGADDR_HOSTMEMPIXSWAP:
+ wRet = gwS1d13522_hostmempixswapconf;
+ break;
+
+ case S1D13522_REGADDR_PUBC:
+ wRet = ConfigPVI.Reg0x330;
+ break;
+ case S1D13522_REGADDR_DSPENGINTRSR:
+ wRet = gwS1d13522_DspEngIntrRSR;
+ break;
+
+ default :
+ WARNING_MSG("%s %d(0x%x) not support !\n",__FUNCTION__,wIndex,wIndex);
+ break;
+ }
+ return wRet;
+}
+
+
+
+#ifdef _PVI_IOCTL_INTERFACE//[
+//////////////////////////////////////////////////////////////
+//
+// pvi ioctl interface ...
+//
+
+// pvi ioctl helper functions ...
+
+static WORD packed_bpp2_to_bbp3_dot3[] = {
+ (0x0 << 1),
+ (0x2 << 1),
+ (0x5 << 1),
+ (0x7 << 1)
+};
+
+static WORD packed_bpp2_to_bbp3_dot2[] = {
+ (0x0 << (1+4)),
+ (0x2 << (1+4)),
+ (0x5 << (1+4)),
+ (0x7 << (1+4))
+};
+
+static WORD packed_bpp2_to_bbp3_dot1[] = {
+ (0x0 << (1+4+4)),
+ (0x2 << (1+4+4)),
+ (0x5 << (1+4+4)),
+ (0x7 << (1+4+4))
+};
+
+static WORD packed_bpp2_to_bbp3_dot0[] = {
+ (0x0 << (1+4+4+4)),
+ (0x2 << (1+4+4+4)),
+ (0x5 << (1+4+4+4)),
+ (0x7 << (1+4+4+4))
+};
+
+static WORD packed_bpp1_to_bbp3_dot3[] = {
+ (0x0 << 1),
+ (0x7 << 1)
+};
+
+static WORD packed_bpp1_to_bbp3_dot2[] = {
+ (0x0 << (1+4)),
+ (0x7 << (1+4))
+};
+
+static WORD packed_bpp1_to_bbp3_dot1[] = {
+ (0x0 << (1+4+4)),
+ (0x7 << (1+4+4))
+};
+
+static WORD packed_bpp1_to_bbp3_dot0[] = {
+ (0x0 << (1+4+4+4)),
+ (0x7 << (1+4+4+4))
+};
+
+
+
+static BOOL SPI_FlashWaitReady(VOID)
+{
+ return TRUE;
+}
+
+static EN_EPSON_ERROR_CODE PutImage_PVI2EPSON(PBYTE pData, BOOL fPartial, UINT16 WaveForm)
+{
+int length, cc;
+s1d13521_ioctl_cmd_params CommandParam;
+PBYTE pSource;
+PUINT16 pDest;
+BYTE data;
+//EN_EPSON_ERROR_CODE ret;
+EN_EPSON_ERROR_CODE ret=_EPSON_ERROR_SUCCESS;
+// int save_index=-1; // KEG 20090914 commented out to stop compiler warning
+
+ //DBG_MSG("%s() skip \n",__FUNCTION__);
+ //return _EPSON_ERROR_SUCCESS;
+
+ #if 0//[ gallen remove .
+ if(BusWaitForHRDY() != _EPSON_ERROR_SUCCESS)
+ return _EPSON_ERROR_NOT_READY;
+ #endif //]
+
+// Joseph 100714, clear soft reset flag
+// ConfigPVI.Reg0x330=BusIssueReadReg(0x330);
+ ConfigPVI.Reg0x330=BusIssueReadReg(0x330)&0x00FF;
+
+ ret=BusIssueWriteReg(0x330, 0x84);// LUT auto,auto waveform disable ...
+ if(ret != _EPSON_ERROR_SUCCESS)
+ {
+ //DEBUGMSG(_MSG_ERROR, "%s] write 0x330=0x0x84 fail [0x330]=0x%04x !!!!!!!!!!!!\n", __FUNCTION__, BusIssueReadReg(0x330));
+ goto _PutImage_PVI2EPSON_Fail;
+ }
+ DBG_MSG("%s() : CurRotateMod=0x%x,PastRotateMod=0x%x\n",__FUNCTION__,ConfigPVI.CurRotateMode,ConfigPVI.PastRotateMode);
+ if(ConfigPVI.PastRotateMode != ConfigPVI.CurRotateMode)
+ {
+ epdfb_rotate_t tRotateInPVI_modA[4] = {epdfb_rotate_0,epdfb_rotate_270,epdfb_rotate_180,epdfb_rotate_90};
+ CommandParam.param[0]=ConfigPVI.CurRotateMode;
+
+ //ret=BusIssueCmd(INIT_ROTMODE, &CommandParam, 1);
+ DBG_MSG("%s(%d):rotate change %d=>%d\n",__FILE__,__LINE__,\
+ gtRotate,tRotateInPVI_modA[(epdfb_rotate_t)(ConfigPVI.CurRotateMode>>8)]);
+
+ gtRotate = tRotateInPVI_modA[(epdfb_rotate_t)(ConfigPVI.CurRotateMode>>8)];
+ //gtRotate = (epdfb_rotate_t)(ConfigPVI.CurRotateMode>>8);
+
+
+ ConfigPVI.PastRotateMode=ConfigPVI.CurRotateMode;
+ }
+
+
+ CommandParam.param[0] = (0x2 << 4);// image 4 bits/pixel .
+ if(fPartial == FALSE)
+ {
+ length=(gwScrH*gwScrW) >> 2;
+ }
+ else
+ {
+ // gallen remove : CommandParam.param[1]=ConfigPVI.StartX;
+ // gallen remove : CommandParam.param[2]=ConfigPVI.StartY;
+ // gallen remove : CommandParam.param[3]=ConfigPVI.Width;
+ // gallen remove : CommandParam.param[4]=ConfigPVI.Height;
+ // gallen remove : ret=BusIssueCmd(LD_IMG_AREA, &CommandParam, 5);
+ length=(ConfigPVI.Width*ConfigPVI.Height) >> 2;
+ }
+ if(ret != _EPSON_ERROR_SUCCESS)
+ {
+ //DEBUGMSG(_MSG_ERROR, "%s] LD_IMG/LD_IMG_AREA fail !!!!!!!!!!!!\n", __FUNCTION__);
+ goto _PutImage_PVI2EPSON_Fail;
+ }
+ pSource=(PBYTE) pData;
+
+
+ DBG_MSG("%s() : ConfigPVI.Deepth=0x%x,ReverseGrade=0x%x\n",\
+ __FUNCTION__,ConfigPVI.Deepth,ConfigPVI.ReverseGrade);
+
+ #if 0//[ gallen remove 20110315: no more hardware check ,this is fake hardware .
+ //pDest=(PUINT16) s1d13521fb_info.VirtualFramebufferAddr;
+ pDest=(PUINT16) gbTemp4BitsFBA;
+
+ if(ConfigPVI.Deepth == 2) {
+ for(cc=length; cc--; pDest++, pSource++)
+ {
+ data=*pSource ^ ConfigPVI.ReverseGrade;
+ *pDest=(packed_bpp2_to_bbp3_dot0[(data >> 0) & 3]
+ | packed_bpp2_to_bbp3_dot1[(data >> 2) & 3]
+ | packed_bpp2_to_bbp3_dot2[(data >> 4) & 3]
+ | packed_bpp2_to_bbp3_dot3[(data >> 6) & 3]);
+ }
+ }
+ else {
+ for(cc=length >> 1; cc--; pDest++, pSource++)
+ {
+ data=*pSource ^ ConfigPVI.ReverseGrade;
+ *pDest=(packed_bpp1_to_bbp3_dot0[(data >> 0) & 1]
+ | packed_bpp1_to_bbp3_dot1[(data >> 1) & 1]
+ | packed_bpp1_to_bbp3_dot2[(data >> 2) & 1]
+ | packed_bpp1_to_bbp3_dot3[(data >> 3) & 1]);
+ pDest++;
+ *pDest=(packed_bpp1_to_bbp3_dot0[(data >> 4) & 1]
+ | packed_bpp1_to_bbp3_dot1[(data >> 5) & 1]
+ | packed_bpp1_to_bbp3_dot2[(data >> 6) & 1]
+ | packed_bpp1_to_bbp3_dot3[(data >> 7) & 1]);
+ }
+ }
+
+ ret=BusIssueWriteRegBuf(0x154, (PUINT16) s1d13521fb_info.VirtualFramebufferAddr, length);
+ if(ret != _EPSON_ERROR_SUCCESS)
+ {
+ //DEBUGMSG(_MSG_ERROR, "%s] Download image fail !!!!!!!!!!!!\n", __FUNCTION__);
+ goto _PutImage_PVI2EPSON_Fail;
+ }
+ ret=BusIssueCmd(LD_IMG_END, &CommandParam, 0);
+ if(ret != _EPSON_ERROR_SUCCESS)
+ {
+ //DEBUGMSG(_MSG_ERROR, "%s] LD_IMG_END fail !!!!!!!!!!!!\n", __FUNCTION__);
+ goto _PutImage_PVI2EPSON_Fail;
+ }
+ // FRANKLIN ret=WaitDPEIdle();
+ ret=WaitDPEIdle();
+ EpsonReady(); // Joseph 100222
+ if(fPartial == false) {
+ //mdelay (1000);
+ _msleep(1000);
+ }
+ else {
+ //mdelay (100);
+ _msleep(100);
+ }
+
+ CommandParam.param[0] = (WaveForm << 8);
+ if(fPartial == FALSE)
+ {
+#if (defined(__5_INCH__) || defined(__6_INCH__))
+printk ("[%s-%d] ............\n",__func__,__LINE__);
+ ret=BusIssueCmd(UPD_FULL, &CommandParam, 1);
+ if (g_is_s1d13522) {
+ BusIssueCmd(WAIT_DSPE_TRG,&CommandParam,0);
+ BusIssueCmd(WAIT_DSPE_FREND,&CommandParam,0);
+ }
+#elif defined(__8_INCH__)
+#elif defined(__9D7_INCH__)
+ CommandParam.param[1]=(1200-800)/2;
+ CommandParam.param[2]=(826-600)/2;
+ CommandParam.param[3]=800;
+ CommandParam.param[4]=600;
+ ret=BusIssueCmd(UPD_PART_AREA, &CommandParam, 5);
+ if (g_is_s1d13522)
+ BusIssueCmd(WAIT_DSPE_TRG,&CommandParam,0);
+#endif
+ }
+ else
+ {
+ CommandParam.param[1]=ConfigPVI.StartX;
+ CommandParam.param[2]=ConfigPVI.StartY;
+ CommandParam.param[3]=ConfigPVI.Width;
+ CommandParam.param[4]=ConfigPVI.Height;
+ ret=BusIssueCmd(UPD_PART_AREA, &CommandParam, 5);
+ if (g_is_s1d13522)
+ BusIssueCmd(WAIT_DSPE_TRG,&CommandParam,0);
+ }
+ if(ret != _EPSON_ERROR_SUCCESS)
+ {
+ //DEBUGMSG(_MSG_ERROR, "%s] UPD_FULL fail !!!!!!!!!!!!!!\n", __FUNCTION__);
+ goto _PutImage_PVI2EPSON_Fail;
+ }
+ BusUpdateThermal(); // adon, 2009-04-09
+ ret=BusIssueWriteReg(0x330, ConfigPVI.Reg0x330);
+ if(ret != _EPSON_ERROR_SUCCESS)
+ {
+ //DEBUGMSG(_MSG_ERROR, "%s] update 0x330 fail !!!!!!!!!!!!!!\n", __FUNCTION__);
+ goto _PutImage_PVI2EPSON_Fail;
+ }
+ #else //][ gallen
+ //gptEPD_dc_current->pfnIsUpdating
+ printk("%s() : w.f=%d,partial=%d\n",__FUNCTION__,WaveForm,fPartial);
+ fake_s1d13522_setwfmode(gptEPD_dc_current,(int)WaveForm);
+ if(gptEPD_dc_current->pfnSetPartialUpdate) {
+ //DBG_MSG("[%s] %s update mode\n",__FUNCTION__,fPartial?"partial":"full");
+ gptEPD_dc_current->pfnSetPartialUpdate(fPartial);
+ }
+
+ ret=BusIssueWriteReg(0x330, ConfigPVI.Reg0x330);
+
+ #if 0
+
+ fake_s1d13522_display_img(ConfigPVI.StartX,ConfigPVI.StartY,
+ ConfigPVI.Width,ConfigPVI.Height,gbTemp4BitsFBA,
+ gptEPD_dc_current,4,gtRotate);
+ #else
+ fake_s1d13522_display_img(ConfigPVI.StartX,ConfigPVI.StartY,
+ ConfigPVI.Width,ConfigPVI.Height,pSource,
+ gptEPD_dc_current,ConfigPVI.Deepth,gtRotate);
+ #endif
+
+ #endif //] gallen remove 20110315.
+
+
+_PutImage_PVI2EPSON_Fail:
+ ret=BusIssueWriteReg(0x310,0xa600);
+ ret=BusIssueWriteReg(0x312, 0x0e);
+ return ret;
+}
+
+static BOOL SPI_FlashWriteData(BYTE Data)
+{
+ BusIssueWriteReg(_EPSON_REG_SPI_WRITE_DATA, (Data | _EPSON_REG_SPI_WRITE_DATA_ENABLE));
+ return SPI_FlashWaitReady();
+}
+static BOOL SPI_FlashWriteDummy(VOID)
+{
+ BusIssueWriteReg(_EPSON_REG_SPI_WRITE_DATA, 0x00);
+ return SPI_FlashWaitReady();
+}
+
+/////
+#if 1
+static void pvi_DoTimerForcedRefresh(ULONG dummy)
+{
+ if(ConfigPVI.fNormalMode == FALSE)
+ return;
+ pvi_ioctl_ForcedRefresh(NULL);
+ //pvi_refersh_timer.expires = jiffies+HZ*ConfigPVI.AutoRefreshTimer;
+ //add_timer();
+ mod_timer(&pvi_refersh_timer,jiffies+HZ*ConfigPVI.AutoRefreshTimer);
+}
+
+int pvi_Init(VOID)
+{
+ int temp,ret;
+
+ if(gpbLOGO_vaddr&&gdwLOGO_size>=_PVI_IMAGE_SIZE) {
+ ImagePVI = gpbLOGO_vaddr;
+ }
+ else {
+ ImagePVI = kmalloc(_PVI_IMAGE_SIZE,GFP_KERNEL);
+ ASSERT(ImagePVI);
+ }
+
+ ConfigPVI.fAutoRefreshMode=FALSE;
+ init_timer(&pvi_refersh_timer);
+ pvi_refersh_timer.function=pvi_DoTimerForcedRefresh;
+ pvi_ioctl_Reset(NULL);
+ ConfigPVI.fNormalMode=true;
+
+/* Joseph 100222
+ //Battery Low is GPG1
+ temp=__raw_readl(S3C2410_GPGCON);
+ temp&=~(0x03<<2);
+ temp|=0x01<<2;
+ //20090803 david use to check GPG1
+ printk("GPGCON1=%x",temp);
+ __raw_writel(temp, S3C2410_GPGCON);
+*/
+ return 0;
+}
+
+void pvi_Deinit(VOID)
+{
+ if(ConfigPVI.fAutoRefreshMode != FALSE) {
+ del_timer_sync(&pvi_refersh_timer);
+ }
+
+ if(ImagePVI&&ImagePVI!=gpbLOGO_vaddr) {
+ kfree(ImagePVI);ImagePVI=0;
+ }
+}
+#endif // FRANKLIN
+
+int pvi_ioctl_DisplayImage(PTDisplayCommand puDisplayCommand)
+{
+ //char buf[128];
+
+ if(ConfigPVI.fNormalMode == FALSE) {
+ ERR_MSG("[WARNING] %s() : not in normal mode .\n",__FUNCTION__);
+ return -EFAULT;
+ }
+
+ ASSERT(gptEPD_dc_current);
+ ASSERT(gptEPD_dc_current->pfnGetWaveformBpp);
+
+ // gallen add 20110323 [
+ ConfigPVI.StartX = 0;
+ ConfigPVI.StartY = 0;
+ if( ((ConfigPVI.CurRotateMode)==0x00) ||
+ ((ConfigPVI.CurRotateMode)==0x02<<8) )
+ {
+ ConfigPVI.Width = gptEPD_dc_current->dwWidth;
+ ConfigPVI.Height = gptEPD_dc_current->dwHeight;
+ }
+ else {
+ ConfigPVI.Width = gptEPD_dc_current->dwHeight;
+ ConfigPVI.Height = gptEPD_dc_current->dwWidth;
+ }
+
+ DBG_MSG("%s(): rotate=0x%x,w=%d,h=%d\n",__FUNCTION__,\
+ ConfigPVI.CurRotateMode,ConfigPVI.Width,ConfigPVI.Height);
+ if (ConfigPVI.Width & 1) {
+ ConfigPVI.Width += 1;
+ }
+ //]
+
+ if(4==gptEPD_dc_current->pfnGetWaveformBpp()){//this is 4bit
+ printk("4bit waveform mark!\n");
+ PutImage_PVI2EPSON(ImagePVI, FALSE, WF_MODE_GU);
+ }else{
+ printk("3bit waveform mark!\n");
+ PutImage_PVI2EPSON(ImagePVI, FALSE, WF_MODE_GC);
+ }
+
+ return 0;
+}
+
+int pvi_ioctl_NewImage(PTDisplayCommand puDisplayCommand)
+{
+ if(ConfigPVI.fNormalMode == FALSE) {
+ ERR_MSG("[WARNING] %s() : not in normal mode .\n",__FUNCTION__);
+ return -EFAULT;
+ }
+ //DEBUGMSG(_MSG_SEND_COMMAND, "dc_NewImage");
+ dbgENTER();
+ if(!gptHWCFG||0==gptHWCFG->m_val.bDisplayResolution) {
+ memcpy(ImagePVI, puDisplayCommand->Data, _PVI_IMAGE_SIZE_800x600);
+ }
+ else {
+ memcpy(ImagePVI, puDisplayCommand->Data, _PVI_IMAGE_SIZE);
+ }
+ dbgLEAVE();
+ return 0;
+}
+
+int pvi_ioctl_StopNewImage(PTDisplayCommand puDisplayCommand)
+{
+ if(ConfigPVI.fNormalMode == FALSE) {
+ ERR_MSG("[WARNING] %s() : not in normal mode .\n",__FUNCTION__);
+ return -EFAULT;
+ }
+ return 0;
+}
+
+
+int pvi_ioctl_PartialImage(PTDisplayCommand puDisplayCommand)
+{
+ if(ConfigPVI.fNormalMode == FALSE) {
+ ERR_MSG("[WARNING] %s() : not in normal mode .\n",__FUNCTION__);
+ return -EFAULT;
+ }
+ ConfigPVI.StartX=(puDisplayCommand->Data [0] << 8)+puDisplayCommand->Data[1];
+ ConfigPVI.StartY=(puDisplayCommand->Data [2] << 8)+puDisplayCommand->Data[3];
+ ConfigPVI.Width =((puDisplayCommand->Data [4] << 8)+puDisplayCommand->Data[5])+1-ConfigPVI.StartX;
+ ConfigPVI.Height=((puDisplayCommand->Data [6] << 8)+puDisplayCommand->Data[7])+1-ConfigPVI.StartY;
+ //DEBUGMSG(_MSG_SEND_COMMAND, "X=%u ", ConfigPVI.StartX);
+ //DEBUGMSG(_MSG_SEND_COMMAND, "Y=%u ", ConfigPVI.StartY);
+ //DEBUGMSG(_MSG_SEND_COMMAND, "W=%u ", ConfigPVI.Width);
+ //DEBUGMSG(_MSG_SEND_COMMAND, "H=%u", ConfigPVI.Height);
+ /////////////////////////////////////////////////////////
+ // align pexil to modula 4
+ //DEBUGMSG(_MSG_SEND_COMMAND, " [dc_PartialImage end 1] %u", ConfigPVI.Height*((ConfigPVI.Width+3) & ~3)/4);
+ memcpy(ImagePVI, &puDisplayCommand->Data[8], ConfigPVI.Height*((ConfigPVI.Width+3) & ~3)/4);
+ //DEBUGMSG(_MSG_SEND_COMMAND, " [dc_PartialImage end 2] ");
+ return 0;
+}
+
+int pvi_ioctl_DisplayPartial(PTDisplayCommand puDisplayCommand)
+{
+// printk("pvi_ioctl_DisplayPartial()\n");
+ if(ConfigPVI.fNormalMode == FALSE) {
+ ERR_MSG("[WARNING] %s() : not in normal mode .\n",__FUNCTION__);
+ return -EFAULT;
+ }
+ //DEBUGMSG(1, "zzzzz\n");
+#if 0
+ BusIssueWriteReg(0x360, 0x1001);
+ BusIssueWriteReg(0x364, 0x1001);
+ PutImage_PVI2EPSON(ImagePVI, TRUE, WF_MODE_MU);
+ //DEBUGMSG(1, " [0x360]=0x%04x\n", BusIssueReadReg(0x360));
+ //DEBUGMSG(1, " [0x364]=0x%04x\n", BusIssueReadReg(0x364));
+ //DEBUGMSG(1, " [0x368]=0x%04x\n", BusIssueReadReg(0x368));
+ //DEBUGMSG(1, " [0x36c]=0x%04x\n", BusIssueReadReg(0x36c));
+#else
+ PutImage_PVI2EPSON(ImagePVI, TRUE, WF_MODE_MU);
+#endif
+ return 0;
+}
+
+int pvi_ioctl_DisplayPartialGU(PTDisplayCommand puDisplayCommand)
+{
+// printk("pvi_ioctl_DisplayPartial()\n");
+ if(ConfigPVI.fNormalMode == FALSE)
+ return -EFAULT;
+ //DEBUGMSG(1, "yyyyy\n");
+ //if (!g_is_s1d13522)
+ {
+ BusIssueWriteReg(0x360, 0x1001);
+ BusIssueWriteReg(0x364, 0x1001);
+ }
+ PutImage_PVI2EPSON(ImagePVI, TRUE, WF_MODE_GU);
+#ifndef _S1D13522_ //Joseph 100629
+ //DEBUGMSG(1, " [0x360]=0x%04x\n", BusIssueReadReg(0x360));
+ //DEBUGMSG(1, " [0x364]=0x%04x\n", BusIssueReadReg(0x364));
+ //DEBUGMSG(1, " [0x368]=0x%04x\n", BusIssueReadReg(0x368));
+ //DEBUGMSG(1, " [0x36c]=0x%04x\n", BusIssueReadReg(0x36c));
+#endif
+ return 0;
+}
+
+int pvi_ioctl_Reset(PTDisplayCommand puDisplayCommand)
+{
+ ConfigPVI.PastRotateMode = 0xffff;
+ ConfigPVI.CurRotateMode=(_INIT_ROTMODE << 8);
+ ConfigPVI.ReverseGrade=0;
+ ConfigPVI.Deepth=2;
+ ConfigPVI.fNormalMode=FALSE;
+ ConfigPVI.AutoRefreshTimer=100;
+ ConfigPVI.StepSPI=_STEP_NOR_BYPASS;
+ if(ConfigPVI.fAutoRefreshMode != FALSE) {
+ del_timer_sync(&pvi_refersh_timer);
+ }
+ //printk("\n==== [%s] 0x0a=0x%04x====\n", __FUNCTION__, BusIssueReadReg(0x0a));
+ return 0;
+}
+
+int pvi_ioctl_SetDepth(PTDisplayCommand puDisplayCommand)
+{
+int RetCode=0;
+ printk("%s()\n",__FUNCTION__);
+
+ if(ConfigPVI.fNormalMode == FALSE) {
+ ERR_MSG("[WARNING] %s() : not in normal mode .\n",__FUNCTION__);
+ return -EFAULT;
+ }
+
+ if((puDisplayCommand->Data[0] == 0) || (puDisplayCommand->Data[0] == 2)) {
+ ConfigPVI.Deepth=puDisplayCommand->Data[0];
+ }
+ else {
+ ERR_MSG("%s() : parameter value error (0x%x)!\n",__FUNCTION__,puDisplayCommand->Data[0]);
+ RetCode=-EFAULT;
+ }
+ return RetCode;
+}
+
+int pvi_ioctl_EraseDisplay(PTDisplayCommand pDisplayCommand)
+{
+BYTE Color=0xff;
+
+ if(ConfigPVI.fNormalMode == FALSE) {
+ ERR_MSG("[WARNING] %s() : not in normal mode .\n",__FUNCTION__);
+ return -EFAULT;
+ }
+
+ switch(pDisplayCommand->Data[0]) {
+ case 0:
+ Color=0x00;
+ break;
+ case 1:
+ Color=0xff;
+ break;
+ case 2:
+ Color=0x55;
+ break;
+ case 3:
+ Color=0xaa;
+ break;
+ };
+ memset(ImagePVI, Color, _PVI_IMAGE_SIZE);
+ PutImage_PVI2EPSON(ImagePVI, FALSE, WF_MODE_GC);
+// PutImage_PVI2EPSON(ImagePVI, FALSE, WF_MODE_MU);
+ return 0;
+}
+
+int pvi_ioctl_Rotate(PTDisplayCommand puDisplayCommand)
+{
+ if(ConfigPVI.fNormalMode == FALSE) {
+ ERR_MSG("[WARNING] %s() : not in normal mode .\n",__FUNCTION__);
+ return -EFAULT;
+ }
+ DBG_MSG("%s():puDisplayCommand->Data[0]=0x%02x \n",__FUNCTION__,puDisplayCommand->Data[0]);
+
+ switch(puDisplayCommand->Data[0]) {
+ case 1:
+ ConfigPVI.CurRotateMode=3<<8;
+ break;
+ case 2:
+ ConfigPVI.CurRotateMode=2<<8;
+ break;
+ case 3:
+ ConfigPVI.CurRotateMode=1<<8;
+ break;
+ case 0:
+ ConfigPVI.CurRotateMode=0<<8;
+ break;
+ };
+ return 0;
+}
+
+int pvi_ioctl_Positive(PTDisplayCommand pDisplayCommand)
+{
+ if(ConfigPVI.fNormalMode == FALSE) {
+ ERR_MSG("[WARNING] %s() : not in normal mode .\n",__FUNCTION__);
+ return -EFAULT;
+ }
+
+ ConfigPVI.ReverseGrade=0x0000;
+ return 0;
+}
+
+int pvi_ioctl_Negative(PTDisplayCommand pDisplayCommand)
+{
+ if(ConfigPVI.fNormalMode == FALSE) {
+ ERR_MSG("[WARNING] %s() : not in normal mode .\n",__FUNCTION__);
+ return -EFAULT;
+ }
+
+ ConfigPVI.ReverseGrade=0xffff;
+ return 0;
+}
+
+int pvi_ioctl_GoToNormal(PTDisplayCommand pDisplayCommand)
+{
+ DBG_MSG("%s()\n",__FUNCTION__);
+
+ //BusIssueWriteRegX(0x0a, BusIssueReadReg(0x0a) | 0x1000);
+ // printk("pvi_ioctl_GoToNormal\n");
+ //BusIssueWriteReg(0x16, 0);
+ // printk("pvi_ioctl_GoToNormal\n");
+ pvi_ioctl_AutoRefreshOn(NULL);
+ ConfigPVI.fNormalMode=TRUE;
+ //BusIssueCmd(RUN_SYS, NULL, 0);
+ return 0;
+}
+int pvi_ioctl_GoToSleep(PTDisplayCommand pDisplayCommand)
+{
+ DBG_MSG("\n===== %s() =====\n",__FUNCTION__);
+
+ ConfigPVI.fNormalMode=FALSE;
+ if(ConfigPVI.fAutoRefreshMode != FALSE) {
+ del_timer_sync(&pvi_refersh_timer);
+ }
+//printk("+pvi_ioctl_GoToSleep\n");
+ //BusIssueCmd(SLP, NULL, 0);
+//printk("-pvi_ioctl_GoToSleep\n");
+ return 0;
+}
+
+int pvi_ioctl_GoToStandBy(PTDisplayCommand pDisplayCommand)
+{
+ DBG_MSG("\n====== %s() =======\n",__FUNCTION__);
+ ConfigPVI.fNormalMode=FALSE;
+ //BusIssueCmd(STBY, NULL, 0);
+ return 0;
+}
+
+int pvi_ioctl_WriteToFlash(PTDisplayCommand pDisplayCommand)
+{
+ if(ConfigPVI.fNormalMode == FALSE) {
+ ERR_MSG("[WARNING] %s() : not in normal mode .\n",__FUNCTION__);
+ return -EFAULT;
+ }
+
+ switch(ConfigPVI.StepSPI) {
+ case _STEP_NOR_BYPASS:
+ //DEBUGMSG(_MSG_SPI_WRITE, "_STEP_NOR_BYPASS ");
+ if((pDisplayCommand->Data[0] == 0x00) && (pDisplayCommand->Data[1] == 0x0a) && (pDisplayCommand->Data[2] == 0xaa) && (pDisplayCommand->Data[3] == 0xaa))
+ ConfigPVI.StepSPI=_STEP_NOR_WRITE_AA_INTO_555;
+ break;
+
+ case _STEP_NOR_WRITE_AA_INTO_555:
+ //DEBUGMSG(_MSG_SPI_WRITE, "_STEP_NOR_WRITE_AA_INTO_555 ");
+ if((pDisplayCommand->Data[0] == 0x00) && (pDisplayCommand->Data[1] == 0x05) && (pDisplayCommand->Data[2] == 0x55) && (pDisplayCommand->Data[3] == 0x55))
+ ConfigPVI.StepSPI=_STEP_NOR_WRITE_55_INTO_2AA;
+ else
+ ConfigPVI.StepSPI=_STEP_NOR_BYPASS;
+ break;
+
+ case _STEP_NOR_WRITE_55_INTO_2AA:
+ //DEBUGMSG(_MSG_SPI_WRITE, "_STEP_NOR_WRITE_55_INTO_2AA ");
+ if((pDisplayCommand->Data[0] == 0x00) && (pDisplayCommand->Data[1] == 0x0a) && (pDisplayCommand->Data[2] == 0xaa))
+ {
+ if(pDisplayCommand->Data[3] == 0x90)
+ ConfigPVI.StepSPI=_STEP_NOR_CMD_STATUS;
+ else if(pDisplayCommand->Data[3] == 0xa0)
+ ConfigPVI.StepSPI=_STEP_NOR_CMD_PROGRAM;
+ else if(pDisplayCommand->Data[3] == 0x80)
+ ConfigPVI.StepSPI=_STEP_NOR_CMD_ERASE;
+ else
+ ConfigPVI.StepSPI=_STEP_NOR_BYPASS;
+ }
+ else
+ ConfigPVI.StepSPI=_STEP_NOR_BYPASS;
+ break;
+
+ case _STEP_NOR_CMD_PROGRAM:
+ //DEBUGMSG(_MSG_SPI_WRITE, "_STEP_NOR_CMD_PROGRAM ");
+ //spi_FlashWriteByte(pDisplayCommand);
+ ConfigPVI.StepSPI=_STEP_NOR_BYPASS;
+ break;
+
+ case _STEP_NOR_CMD_ERASE:
+ //DEBUGMSG(_MSG_SPI_WRITE, "_STEP_NOR_CMD_ERASE ");
+ if((pDisplayCommand->Data[0] == 0x00) && (pDisplayCommand->Data[1] == 0x0a) && (pDisplayCommand->Data[2] == 0xaa) && (pDisplayCommand->Data[3] == 0xaa))
+ ConfigPVI.StepSPI=_STEP_NOR_CMD_ERASE_WRITE_AA_INTO_555;
+ else
+ ConfigPVI.StepSPI=_STEP_NOR_BYPASS;
+ break;
+
+ case _STEP_NOR_CMD_ERASE_WRITE_AA_INTO_555:
+ //DEBUGMSG(_MSG_SPI_WRITE, "_STEP_NOR_CMD_ERASE_WRITE_AA_INTO_555 ");
+ if((pDisplayCommand->Data[0] == 0x00) && (pDisplayCommand->Data[1] == 0x05) && (pDisplayCommand->Data[2] == 0x55) && (pDisplayCommand->Data[3] == 0x55))
+ ConfigPVI.StepSPI=_STEP_NOR_CMD_ERASE_WRITE_55_INTO_2AA;
+ else
+ ConfigPVI.StepSPI=_STEP_NOR_BYPASS;
+ break;
+
+ case _STEP_NOR_CMD_ERASE_WRITE_55_INTO_2AA:
+ //DEBUGMSG(_MSG_SPI_WRITE, "_STEP_NOR_CMD_ERASE_WRITE_55_INTO_2AA ");
+ if((pDisplayCommand->Data[0] == 0x00) && (pDisplayCommand->Data[1] == 0x0a) && (pDisplayCommand->Data[2] == 0xaa) && (pDisplayCommand->Data[3] == 0x10)) {
+ //spi_FlashEraseChip();
+ }
+ else if(pDisplayCommand->Data[3] == 0x30) {
+ //spi_FlashEraseSector(pDisplayCommand);
+ }
+ ConfigPVI.StepSPI=_STEP_NOR_BYPASS;
+ break;
+
+ case _STEP_NOR_CMD_STATUS:
+ //DEBUGMSG(_MSG_SPI_WRITE, "_STEP_NOR_CMD_STATUS ");
+ if(pDisplayCommand->Data[3] == 0xf0)
+ ConfigPVI.StepSPI=_STEP_NOR_BYPASS;
+ break;
+
+ default:
+ //DEBUGMSG(_MSG_SPI_WRITE, "_STEP_UNDEFINE ");
+ ConfigPVI.StepSPI=_STEP_NOR_BYPASS;
+ };
+ return 0;
+}
+
+int pvi_ioctl_ReadFromFlash(PTDisplayCommand pDisplayCommand)
+{
+int RetCode=-EFAULT;
+
+ if(ConfigPVI.fNormalMode == FALSE) {
+ ERR_MSG("[WARNING] %s() : not in normal mode .\n",__FUNCTION__);
+ return -EFAULT;
+ }
+
+ if(ConfigPVI.StepSPI == _STEP_NOR_CMD_STATUS)
+ {
+ RetCode=0;
+ if((pDisplayCommand->Data[2] & 3) == 0)
+ pDisplayCommand->Data[0]=0x3e;
+ else if((pDisplayCommand->Data[2] & 3) == 1)
+ pDisplayCommand->Data[0]=0xc2;
+ else
+ pDisplayCommand->Data[0]=0;
+ return RetCode;
+ }
+ #if 0// gallen disable .
+ SPI_FlashSwitchToHost();
+ if(SPI_FlashWriteData(0x03) == FALSE)
+ goto _pvi_ioctl_ReadFromFlashFail;
+ if(SPI_FlashWriteData(pDisplayCommand->Data[0]) == FALSE)
+ goto _pvi_ioctl_ReadFromFlashFail;
+ if(SPI_FlashWriteData(pDisplayCommand->Data[1]) == FALSE)
+ goto _pvi_ioctl_ReadFromFlashFail;
+ if(SPI_FlashWriteData(pDisplayCommand->Data[2]) == FALSE)
+ goto _pvi_ioctl_ReadFromFlashFail;
+ if(SPI_FlashWriteDummy() == FALSE)
+ goto _pvi_ioctl_ReadFromFlashFail;
+ pDisplayCommand->Data[0]=(BYTE) BusIssueReadReg(_EPSON_REG_SPI_READ_DATA);
+ //DEBUGMSG(_MSG_SPI_READ, "ReadData=0x%02x", pDisplayCommand->Data[0]);
+ #endif
+ RetCode=0;
+_pvi_ioctl_ReadFromFlashFail:
+ //SPI_FlashSwitchToDPE();
+ return RetCode;
+}
+
+int pvi_ioctl_Init(PTDisplayCommand pDisplayCommand)
+{
+ if(ConfigPVI.fNormalMode == FALSE) {
+ ERR_MSG("[WARNING] %s() : not in normal mode .\n",__FUNCTION__);
+ return -EFAULT;
+ }
+
+// BusIssueInitDisplay();
+ return 0;
+}
+
+int pvi_ioctl_AutoRefreshOn(PTDisplayCommand pDisplayCommand)
+{
+ if(ConfigPVI.fNormalMode == FALSE) {
+ ERR_MSG("[WARNING] %s() : not in normal mode .\n",__FUNCTION__);
+ return -EFAULT;
+ }
+
+ if((ConfigPVI.AutoRefreshTimer != 0) && (ConfigPVI.fAutoRefreshMode == FALSE))
+ {
+ //pvi_refersh_timer.expires = jiffies+HZ*ConfigPVI.AutoRefreshTimer;
+ //add_timer(&pvi_refersh_timer);
+ mod_timer(&pvi_refersh_timer,jiffies+HZ*ConfigPVI.AutoRefreshTimer);
+ ConfigPVI.fAutoRefreshMode=TRUE;
+ }
+ return 0;
+}
+
+int pvi_ioctl_AutoRefreshOff(PTDisplayCommand pDisplayCommand)
+{
+ del_timer_sync(&pvi_refersh_timer);
+ ConfigPVI.fAutoRefreshMode=FALSE;
+ if(ConfigPVI.fNormalMode == FALSE) {
+ ERR_MSG("[WARNING] %s() : not in normal mode .\n",__FUNCTION__);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+int pvi_ioctl_SetRefresh(PTDisplayCommand pDisplayCommand)
+{
+ if(ConfigPVI.fNormalMode == FALSE) {
+ ERR_MSG("[WARNING] %s() : not in normal mode .\n",__FUNCTION__);
+ return -EFAULT;
+ }
+ ConfigPVI.AutoRefreshTimer=pDisplayCommand->Data[0];
+ return 0;
+}
+
+int pvi_ioctl_ForcedRefresh(PTDisplayCommand pDisplayCommand)
+{
+#ifdef __TURN_ON_FORCE_DISPLAY__
+s1d13521_ioctl_cmd_params CommandParam;
+
+ if(ConfigPVI.fNormalMode == FALSE) {
+ ERR_MSG("[WARNING] %s() : not in normal mode .\n",__FUNCTION__);
+ return -EFAULT;
+ }
+
+ CommandParam.param[0]=((WF_MODE_GC << 8) | 0x4000);
+ BusIssueCmd(UPD_FULL, &CommandParam, 1);
+// pvi_ioctl_AutoRefreshOn (NULL);
+#endif
+ return 0;
+}
+
+int pvi_ioctl_GetRefresh(PTDisplayCommand pDisplayCommand)
+{
+ if(ConfigPVI.fNormalMode == FALSE) {
+ ERR_MSG("[WARNING] %s() : not in normal mode .\n",__FUNCTION__);
+ return -EFAULT;
+ }
+
+ pDisplayCommand->Data[0]=ConfigPVI.AutoRefreshTimer;
+ return 0;
+}
+
+int pvi_ioctl_RestoreImage(PTDisplayCommand pDisplayCommand)
+{
+ if(ConfigPVI.fNormalMode == FALSE) {
+ ERR_MSG("[WARNING] %s() : not in normal mode .\n",__FUNCTION__);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+int pvi_ioctl_ControllerVersion(PTDisplayCommand puDisplayCommand)
+{
+ if(ConfigPVI.fNormalMode == FALSE) {
+ ERR_MSG("[WARNING] %s() : not in normal mode .\n",__FUNCTION__);
+ return -EFAULT;
+ }
+ puDisplayCommand->Data[0]=0x06;
+ return 0;
+}
+
+int pvi_ioctl_SoftwareVersion(PTDisplayCommand puDisplayCommand)
+{
+ if(ConfigPVI.fNormalMode == FALSE) {
+ ERR_MSG("[WARNING] %s() : not in normal mode .\n",__FUNCTION__);
+ return -EFAULT;
+ }
+
+ puDisplayCommand->Data[0]=0x06;
+ return 0;
+}
+
+int pvi_ioctl_DisplaySize(PTDisplayCommand puDisplayCommand)
+{
+ puDisplayCommand->Data[0]=0x22;
+ return 0;
+}
+
+int pvi_ioctl_GetStatus(PTDisplayCommand puDisplayCommand)
+{
+ puDisplayCommand->Data[0]=0x06;
+ if(ConfigPVI.fNormalMode == FALSE)
+ puDisplayCommand->Data[0]|=_BIT0;
+ if(ConfigPVI.Deepth != 0)
+ puDisplayCommand->Data[0]|=_BIT4;
+ return 0;
+}
+
+int pvi_ioctl_Temperature(PTDisplayCommand pDisplayCommand)
+{
+ if(ConfigPVI.fNormalMode == FALSE) {
+ ERR_MSG("[WARNING] %s() : not in normal mode .\n",__FUNCTION__);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+int pvi_ioctl_WriteRegister(PTDisplayCommand pDisplayCommand)
+{
+ if(ConfigPVI.fNormalMode == FALSE) {
+ ERR_MSG("[WARNING] %s() : not in normal mode .\n",__FUNCTION__);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+int pvi_ioctl_ReadRegister(PTDisplayCommand pDisplayCommand)
+{
+ if(ConfigPVI.fNormalMode == FALSE) {
+ ERR_MSG("[WARNING] %s() : not in normal mode .\n",__FUNCTION__);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+
+
+int pvi_SwitchCommand(PTDisplayCommand pDisplayCommand)
+ //printk("ubStatus =%d\n",PHMSCD_USB_STATUS_FAIL);
+{
+ static int partial=0;
+
+int ReturnCode=0;
+
+ GALLEN_DBGLOCAL_MUTEBEGIN_EX(48);
+ DBG0_MSG("%s,cmd=0x%02x,wr=%u,rd=%u\n",__FUNCTION__,pDisplayCommand->Command,
+ pDisplayCommand->BytesToWrite,pDisplayCommand->BytesToRead);
+
+
+ //lock_kernel();
+ switch(pDisplayCommand->Command) {
+ case dc_NewImage:GALLEN_DBGLOCAL_RUNLOG(0);
+ ReturnCode=pvi_ioctl_NewImage(pDisplayCommand);
+//=============================================================================
+// Arron
+ partial = 0;
+//------------------------------------------------------------------------------
+ break;
+
+ case dc_StopNewImage:GALLEN_DBGLOCAL_RUNLOG(1);
+ ReturnCode=pvi_ioctl_StopNewImage(pDisplayCommand);
+ break;
+
+ case dc_DisplayImage:GALLEN_DBGLOCAL_RUNLOG(2);
+ //printk("David: dc_DisplayImage\n");
+//==============================================================================
+ if(partial==0)
+ {
+ GALLEN_DBGLOCAL_RUNLOG(3);
+ ReturnCode=pvi_ioctl_DisplayImage(pDisplayCommand);
+ }else{
+ GALLEN_DBGLOCAL_RUNLOG(4);
+ ReturnCode=pvi_ioctl_DisplayPartial(pDisplayCommand);
+ }
+ //kay add battery detect
+ // FRANKLIN s3c2410_adc_read_once();
+ //check HARDWARE BATTERY LOW PIN ( GPF4 )
+//#if defined (__COOKIE__)
+ //printk("Check Cookie Battery State.\n");
+
+ //pvi_CheckHwBatteryLow();
+//#endif
+//------------------------------------------------------------------------------
+ break;
+
+ case dc_PartialImage:GALLEN_DBGLOCAL_RUNLOG(5);
+ // FRANKLIN s3c2410_adc_read_once();
+ ReturnCode=pvi_ioctl_PartialImage(pDisplayCommand);
+//=============================================================================
+// Arron
+ partial = 1;
+//------------------------------------------------------------------------------
+ break;
+
+ case dc_DisplayPartial:GALLEN_DBGLOCAL_RUNLOG(6);
+ ReturnCode=pvi_ioctl_DisplayPartial(pDisplayCommand);
+ break;
+
+ case dc_DisplayPartialGU:GALLEN_DBGLOCAL_RUNLOG(7);
+ ReturnCode=pvi_ioctl_DisplayPartialGU(pDisplayCommand);
+ break;
+
+ case dc_Reset:GALLEN_DBGLOCAL_RUNLOG(8);
+ ReturnCode=pvi_ioctl_Reset(pDisplayCommand);
+ break;
+
+ case dc_SetDepth:GALLEN_DBGLOCAL_RUNLOG(9);
+ ReturnCode=pvi_ioctl_SetDepth(pDisplayCommand);
+ break;
+
+ case dc_EraseDisplay:GALLEN_DBGLOCAL_RUNLOG(10);
+ ReturnCode=pvi_ioctl_EraseDisplay(pDisplayCommand);
+ break;
+
+ case dc_Rotate:GALLEN_DBGLOCAL_RUNLOG(11);
+ ReturnCode=pvi_ioctl_Rotate(pDisplayCommand);
+ break;
+
+ case dc_Positive:GALLEN_DBGLOCAL_RUNLOG(12);
+ ReturnCode=pvi_ioctl_Positive(pDisplayCommand);
+ break;
+
+ case dc_Negative:GALLEN_DBGLOCAL_RUNLOG(13);
+ ReturnCode=pvi_ioctl_Negative(pDisplayCommand);
+ break;
+
+ case dc_GoToNormal:GALLEN_DBGLOCAL_RUNLOG(14);
+ ReturnCode=pvi_ioctl_GoToNormal(pDisplayCommand);
+ break;
+
+ case dc_GoToSleep:GALLEN_DBGLOCAL_RUNLOG(15);
+ ReturnCode=pvi_ioctl_GoToSleep(pDisplayCommand);
+ break;
+
+ case dc_GoToStandBy:GALLEN_DBGLOCAL_RUNLOG(16);
+ ReturnCode=pvi_ioctl_GoToStandBy(pDisplayCommand);
+ break;
+
+ case dc_WriteToFlash:GALLEN_DBGLOCAL_RUNLOG(17);
+ ReturnCode=pvi_ioctl_WriteToFlash(pDisplayCommand);
+ break;
+
+ case dc_ReadFromFlash:GALLEN_DBGLOCAL_RUNLOG(18);
+ ReturnCode=pvi_ioctl_ReadFromFlash(pDisplayCommand);
+ break;
+
+ case dc_Init:GALLEN_DBGLOCAL_RUNLOG(19);
+ ReturnCode=pvi_ioctl_Init(pDisplayCommand);
+ break;
+
+ case dc_AutoRefreshOn:GALLEN_DBGLOCAL_RUNLOG(20);
+ ReturnCode=pvi_ioctl_AutoRefreshOn(pDisplayCommand);
+ break;
+
+ case dc_AutoRefreshOff:GALLEN_DBGLOCAL_RUNLOG(21);
+ ReturnCode=pvi_ioctl_AutoRefreshOff(pDisplayCommand);
+ break;
+
+ case dc_SetRefresh:GALLEN_DBGLOCAL_RUNLOG(22);
+ ReturnCode=pvi_ioctl_SetRefresh(pDisplayCommand);
+ break;
+
+ case dc_ForcedRefresh:GALLEN_DBGLOCAL_RUNLOG(23);
+ ReturnCode=pvi_ioctl_ForcedRefresh(pDisplayCommand);
+ break;
+
+ case dc_GetRefresh:GALLEN_DBGLOCAL_RUNLOG(24);
+ ReturnCode=pvi_ioctl_GetRefresh(pDisplayCommand);
+ break;
+
+ case dc_RestoreImage:GALLEN_DBGLOCAL_RUNLOG(25);
+ ReturnCode=pvi_ioctl_RestoreImage(pDisplayCommand);
+ break;
+
+ case dc_ControllerVersion:GALLEN_DBGLOCAL_RUNLOG(26);
+ ReturnCode=pvi_ioctl_ControllerVersion(pDisplayCommand);
+ break;
+
+ case dc_SoftwareVersion:GALLEN_DBGLOCAL_RUNLOG(27);
+ ReturnCode=pvi_ioctl_SoftwareVersion(pDisplayCommand);
+ break;
+
+ case dc_DisplaySize:GALLEN_DBGLOCAL_RUNLOG(28);
+ ReturnCode=pvi_ioctl_DisplaySize(pDisplayCommand);
+ break;
+
+ case dc_GetStatus:GALLEN_DBGLOCAL_RUNLOG(29);
+ ReturnCode=pvi_ioctl_GetStatus(pDisplayCommand);
+ break;
+
+ case dc_Temperature:GALLEN_DBGLOCAL_RUNLOG(30);
+ ReturnCode=pvi_ioctl_Temperature(pDisplayCommand);
+ break;
+
+ case dc_WriteRegister:GALLEN_DBGLOCAL_RUNLOG(31);
+ ReturnCode=pvi_ioctl_WriteRegister(pDisplayCommand);
+ break;
+
+ case dc_ReadRegister:GALLEN_DBGLOCAL_RUNLOG(32);
+ ReturnCode=pvi_ioctl_ReadRegister(pDisplayCommand);
+ break;
+
+ default:GALLEN_DBGLOCAL_RUNLOG(33);
+ ReturnCode=-EFAULT;
+ }
+ //unlock_kernel();
+ GALLEN_DBGLOCAL_END();
+ return ReturnCode;
+}
+
+#endif //] _PVI_IOCTL_INTERFACE
+
+
+
+#ifdef _QT_SUPPORT //[
+
+
+static void s1d13521fb_resolve_conflict(unsigned long dummy)
+{
+#ifndef _ES_LUTNOWAIT_
+ struct s1d13521_ioctl_hwc ioctl_hwc;
+ s1d13521_ioctl_cmd_params cmd_params;
+ extern unsigned long gLastUpdParm0;
+
+ if (BusIssueReadReg(0x0a) & 0x20)
+ {
+// printk("[%s-%d] \n",__func__,__LINE__);
+ if (resolve_conflict_step)
+ mod_timer(&conflict_resolution_timer, jiffies+(HZ/10));
+ else
+ mod_timer(&conflict_resolution_timer, jiffies+(HZ/4));
+ return;
+ }
+// printk("[%s-%d] \n",__func__,__LINE__);
+ switch (resolve_conflict_step) {
+ case 0:
+ // check the register to see if we have a conflict
+ ioctl_hwc.addr = 0x33A;
+ ioctl_hwc.value = BusIssueReadReg(ioctl_hwc.addr);
+
+ if (ioctl_hwc.value & 0x0080) // if we have a conflict
+ {
+ resolve_conflict_step++;
+ printk("evidentpoint: In %s, conflict found\n", __FUNCTION__);
+ // wait for frame end
+ BusIssueCmd(WAIT_DSPE_FREND, &cmd_params, 0);
+
+
+ mod_timer(&conflict_resolution_timer, jiffies+(HZ/10));
+ }
+ break;
+ case 1:
+ cmd_params.param[0] = gLastUpdParm0;
+ // printk("[%s-%d] update parm 0 0x%04X\n", __FUNCTION__, __LINE__,cmd_params.param[0]);
+ BusIssueCmd(UPD_PART,&cmd_params,1);
+ mod_timer(&conflict_resolution_timer, jiffies+(HZ/10));
+ resolve_conflict_step++;
+ break;
+ case 2:
+ BusIssueWriteReg(0x33A, 0x80);
+ printk("[%s-%d] done with 0x%04X\n", __FUNCTION__, __LINE__,gLastUpdParm0);
+ default:
+ resolve_conflict_step = 0;
+ bConflictTimerRunning = FALSE;
+ break;
+ }
+#else
+ bConflictTimerRunning = FALSE;
+#endif
+}
+
+static int ImagePGM(PST_IMAGE_PGM pPGM,EPDFB_DC *pDC)
+{
+ int ret=-1;
+
+ DBG_MSG("[%s](%d) : w=%ld,h=%ld,r=%d,data=0x%p\n",__FUNCTION__,__LINE__,\
+ pPGM->Width,pPGM->Height,gtRotate,pPGM->Data);
+ giPixelBits = 8;
+ ret = fake_s1d13522_display_img(0,0,pPGM->Width,pPGM->Height,pPGM->Data,
+ pDC,8,gtRotate) ;
+
+ return ret;
+}
+
+
+
+// ES modify for 4 bbp support.
+// Qookie usage
+static int Epson_LoadImageArea(PTloadImageArea area,EPDFB_DC *pDC)
+{
+ unsigned char *pbArea;
+ unsigned long dwAreaWidthBytes;
+ unsigned long dwFBSize;
+
+ unsigned long dwStoreDCFlags = 0;
+ int iIsSetDCFlags = 0;
+ int iShifBits;
+
+ ASSERT(pDC);
+ ASSERT(area);
+
+ GALLEN_DBGLOCAL_MUTEBEGIN();
+ GALLEN_DBGLOCAL_PRINTMSG("%s():x=%d,y=%d,w=%d,h=%d,mode=%d,cmd=%x\n",__FUNCTION__,
+ area->XStart,area->YStart,area->Width,area->Height,area->mode,area->cmd);
+
+ if(pDC->pfnSetWaveformMode) {
+ pDC->pfnSetWaveformMode(area->mode);
+ }
+
+ if(UPD_FULL==area->cmd) {
+ pDC->pfnSetPartialUpdate(0);
+ }
+ else {
+ pDC->pfnSetPartialUpdate(1);
+ }
+
+
+ if(pDC->pfnGetRealFrameBufEx) {
+ GALLEN_DBGLOCAL_RUNLOG(0);
+
+ if(!(pDC->dwFlags&EPDFB_DC_FLAG_REVERSEINPDATA)) {
+ dwStoreDCFlags = pDC->dwFlags;
+ iIsSetDCFlags = 1;
+ pDC->dwFlags |= EPDFB_DC_FLAG_REVERSEINPDATA;
+ }
+
+ pbArea = pDC->pfnGetRealFrameBufEx(&dwFBSize);
+ GALLEN_DBGLOCAL_PRINTMSG("fbsize=%u,depth=%d bit\n",dwFBSize,giPixelBits);
+
+ switch(giPixelBits) {
+ default :GALLEN_DBGLOCAL_RUNLOG(1);
+ ERR_MSG("%s:depth %d not supported !\n",__FUNCTION__,giPixelBits);
+ GALLEN_DBGLOCAL_ESC();
+ return -1;
+ case 8:GALLEN_DBGLOCAL_RUNLOG(2);
+ iShifBits = 0;
+ break;
+ case 4:GALLEN_DBGLOCAL_RUNLOG(3);
+ iShifBits = 1;
+ break;
+ case 2:GALLEN_DBGLOCAL_RUNLOG(4);
+ iShifBits = 2;
+ break;
+ case 1:GALLEN_DBGLOCAL_RUNLOG(5);
+ iShifBits = 3;
+ break;
+ }
+
+ pbArea += dwFBSize ;
+
+ #if 1
+
+ #if 1 //[
+ {
+ EPDFB_DC *ptEPD_dcimg ;
+
+ ptEPD_dcimg = epdfbdc_create_ex(pDC->dwHeight,pDC->dwWidth,\
+ giPixelBits,pbArea,pDC->dwFlags);
+
+ epdfbdc_put_dcimg(pDC,ptEPD_dcimg,gtRotate,area->XStart,area->YStart,
+ area->Width,area->Height,area->XStart,area->YStart);
+
+ epdfbdc_delete(ptEPD_dcimg);
+ }
+ #else //][!
+ {
+
+ int i;
+ unsigned char *pbRow;
+ EPDFB_IMG tImg;
+
+ tImg.dwX = area->XStart;
+ tImg.dwW = area->Width;
+ tImg.dwH = 1;
+ tImg.bPixelBits = (unsigned char)giPixelBits;
+ for(i=0;i<area->Height;i++)
+ {
+ pbRow = pbArea + (area->XStart>>iShifBits)+((area->YStart+i)*(pDC->dwHeight>>iShifBits));
+
+ tImg.dwY = area->YStart+i;
+ tImg.pbImgBuf = pbRow;
+
+ epdfbdc_put_fbimg(pDC,&tImg,gtRotate);
+ //fake_s1d13522_display_img(area->XStart,area->YStart+i,area->Width,1,\
+ // pbRow,pDC,giPixelBits,gtRotate);
+ }
+
+
+ }
+ #endif//]
+
+ if(pDC->pfnDispStart) {
+ if(pDC->pfnVcomEnable) {
+ pDC->pfnVcomEnable(1);
+ }
+
+ if(pDC->pfnSetUpdateRect) {
+ unsigned long dwX,dwY,dwW,dwH;
+ dwX = area->XStart;
+ dwY = area->YStart;
+ dwW = area->Width;
+ dwH = area->Height;
+ epdfbdc_get_rotate_active(pDC,&dwX,&dwY,&dwW,&dwH,gtRotate);
+
+ pDC->pfnSetUpdateRect(dwX,dwY,dwW,dwH);
+ }
+
+ pDC->pfnDispStart(1);
+
+ if(pDC->pfnVcomEnable) {
+ pDC->pfnVcomEnable(0);
+ }
+ }
+
+ #else
+ switch(gtRotate) {
+ case epdfb_rotate_0:
+ case epdfb_rotate_180:
+ case epdfb_rotate_90:
+ case epdfb_rotate_270:
+ fake_s1d13522_display_img(0,0,pDC->dwHeight,pDC->dwWidth,\
+ pbArea,pDC,giPixelBits,gtRotate);
+ break;
+ }
+ #endif
+
+ if(iIsSetDCFlags) {
+ pDC->dwFlags = dwStoreDCFlags;
+ }
+ }
+
+ GALLEN_DBGLOCAL_END();
+
+ return 0;
+}
+#endif //] _QT_SUPPORT
+
+
+int fake_s1d13522_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ GALLEN_DBGLOCAL_BEGIN();
+ if(gptHWCFG) {
+ if(1==gptHWCFG->m_val.bDisplayResolution) {
+ switch(gtRotate)
+ {
+ case epdfb_rotate_0:
+ case epdfb_rotate_180:
+ var->xres = 1024;
+ var->yres = 758;
+ break;
+ case epdfb_rotate_90:
+ case epdfb_rotate_270:
+ var->xres = 758;
+ var->yres = 1024;
+ break;
+ }
+ }
+ else if(2==gptHWCFG->m_val.bDisplayResolution) {
+ switch(gtRotate)
+ {
+ case epdfb_rotate_0:
+ case epdfb_rotate_180:
+ var->xres = 1024;
+ var->yres = 768;
+ break;
+ case epdfb_rotate_90:
+ case epdfb_rotate_270:
+ var->xres = 768;
+ var->yres = 1024;
+ break;
+ }
+ }
+ else if(3==gptHWCFG->m_val.bDisplayResolution) {
+ switch(gtRotate)
+ {
+ case epdfb_rotate_0:
+ case epdfb_rotate_180:
+ var->xres = 1440;
+ var->yres = 1080;
+ break;
+ case epdfb_rotate_90:
+ case epdfb_rotate_270:
+ var->xres = 1080;
+ var->yres = 1440;
+ break;
+ }
+ }
+ else if(5==gptHWCFG->m_val.bDisplayResolution) {
+ switch(gtRotate)
+ {
+ case epdfb_rotate_0:
+ case epdfb_rotate_180:
+ var->xres = 1448;
+ var->yres = 1072;
+ break;
+ case epdfb_rotate_90:
+ case epdfb_rotate_270:
+ var->xres = 1072;
+ var->yres = 1448;
+ break;
+ }
+ }
+ else if(6==gptHWCFG->m_val.bDisplayResolution) {
+ switch(gtRotate)
+ {
+ case epdfb_rotate_0:
+ case epdfb_rotate_180:
+ var->xres = 1600;
+ var->yres = 1200;
+ break;
+ case epdfb_rotate_90:
+ case epdfb_rotate_270:
+ var->xres = 1200;
+ var->yres = 1600;
+ break;
+ }
+ }
+ else {
+ switch(gtRotate)
+ {
+ case epdfb_rotate_0:
+ case epdfb_rotate_180:
+ var->xres = 800;
+ var->yres = 600;
+ break;
+ case epdfb_rotate_90:
+ case epdfb_rotate_270:
+ var->xres = 600;
+ var->yres = 800;
+ break;
+ }
+ }
+ }
+ else {
+ switch(gtRotate)
+ {
+ case epdfb_rotate_0:
+ case epdfb_rotate_180:
+ var->xres = 800;
+ var->yres = 600;
+ break;
+ case epdfb_rotate_90:
+ case epdfb_rotate_270:
+ var->xres = 600;
+ var->yres = 800;
+ break;
+ }
+ }
+
+ var->xres_virtual = var->xres;
+ var->yres_virtual = var->yres;
+ var->xoffset = var->yoffset = 0;
+ var->bits_per_pixel = S1D_DISPLAY_BPP;
+ var->grayscale = 1;
+ var->nonstd = 0; /* != 0 Non standard pixel format */
+ var->activate = FB_ACTIVATE_NOW; /* see FB_ACTIVATE_* */
+ var->height = -1; /* height of picture in mm */
+ var->width = -1; /* width of picture in mm */
+ var->accel_flags = 0; /* acceleration flags (hints */
+ var->pixclock = S1D_DISPLAY_PCLK;
+ var->right_margin = 0;
+ var->lower_margin = 0;
+ var->hsync_len = 0;
+ var->vsync_len = 0;
+ var->left_margin = 0;
+ var->upper_margin = 0;
+ var->sync = 0;
+ var->vmode = FB_VMODE_NONINTERLACED;
+ var->red.msb_right = var->green.msb_right = var->blue.msb_right = 0;
+ var->transp.offset = var->transp.length = var->transp.msb_right = 0;
+
+ //s1d13521fb_fix.visual = FB_VISUAL_TRUECOLOR;
+
+ switch (info->var.bits_per_pixel)
+ {
+ case 1:GALLEN_DBGLOCAL_RUNLOG(0);
+ case 2:GALLEN_DBGLOCAL_RUNLOG(1);
+ case 4:GALLEN_DBGLOCAL_RUNLOG(2);
+ case 5:GALLEN_DBGLOCAL_RUNLOG(3);
+ case 8:GALLEN_DBGLOCAL_RUNLOG(4);
+ var->red.offset = var->green.offset = var->blue.offset = 0;
+ var->red.length = var->green.length = var->blue.length = S1D_DISPLAY_BPP;
+ break;
+
+ default:
+ printk(KERN_WARNING "%dbpp is not supported.\n",
+ info->var.bits_per_pixel);
+ GALLEN_DBGLOCAL_ESC();
+ return -EINVAL;
+ }
+
+ GALLEN_DBGLOCAL_END();
+ return 0;
+}
+
+// gallen 2011/05/24 copy from s1d13521base.c .
+int fake_s1d13522_setcolreg(unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp, struct fb_info *info)
+{
+#if 1 //[
+
+ printk("%s(): r.no=%02Xh (r,g,b,t)=(%04X,%04X,%04X,%04X)h\n",
+ __FUNCTION__, regno, red, green, blue, transp);
+ return 0; // ensure that setcolreg does nothing for now
+#else //][
+ // Make the first 16 LUT entries available to the console
+
+ if (info->var.bits_per_pixel == 8 && s1d13521fb_fix.visual == FB_VISUAL_TRUECOLOR)
+ {
+ if (regno < 16)
+ {
+ // G= 30%R + 59%G + 11%B
+ unsigned gray = (red*30 + green*59 + blue*11)/100;
+ gray = (gray>>8) & 0xFF;
+
+#ifdef CONFIG_FB_EPSON_BLACK_AND_WHITE
+ if (gray != 0) {
+ gray = 0xFF;
+ }
+#endif
+
+ // invert: black on white
+ gray = 0xFF-gray;
+ ((u32*)info->pseudo_palette)[regno] = gray;
+
+ dbg_info("%s(): regno=%02Xh red=%04Xh green=%04Xh blue=%04Xh transp=%04Xh ->gray=%02Xh\n",
+ __FUNCTION__, regno, red, green, blue, transp,gray);
+ }
+ }
+ else {
+ return 1;
+ }
+
+ return 0;
+#endif //]
+}
+
+
+int32_t fake_s1d13522_ioctl(unsigned int cmd,unsigned long arg,EPDFB_DC *pDC)
+{
+
+ int32_t ret=0;
+ struct s1d13521_ioctl_hwc ioctl_hwc;
+
+
+ GALLEN_DBGLOCAL_MUTEBEGIN_EX(64);
+ GALLEN_DBGLOCAL_PRINTMSG("%s(%x,%x)\n",__FUNCTION__,cmd,arg);
+
+ #ifdef SHOW_PROGRESS_BAR//[
+ gIsProgessRunning = 0;
+ #endif //] SHOW_PROGRESS_BAR
+
+ switch(cmd)
+ {
+ case S1D13521_DISPLAY:GALLEN_DBGLOCAL_RUNLOG(0);
+ {
+
+ int iBpp = 4;
+ ST_IMAGE_PGM *pt = (ST_IMAGE_PGM *)arg;
+
+
+ pt = kmalloc(sizeof(*pt),GFP_KERNEL);
+ if(pt) {
+ unsigned int dwImgStructSize;
+ GALLEN_DBGLOCAL_RUNLOG(1);
+
+ if(gptHWCFG&&0!=gptHWCFG->m_val.bDisplayResolution) {
+ switch (gptHWCFG->m_val.bDisplayResolution) {
+ case 1:// 1024x758 .
+ dwImgStructSize=((sizeof(unsigned int)*6)+(1024*758));
+ break;
+ case 3:// 1440x1080
+ dwImgStructSize=((sizeof(unsigned int)*6)+(1440*1080));
+ break;
+ case 5:// 1448x1072
+ dwImgStructSize=((sizeof(unsigned int)*6)+(1448*1072));
+ break;
+ case 6:// 1600x1200
+ dwImgStructSize=((sizeof(unsigned int)*6)+(1600*1200));
+ break;
+ default :
+ dwImgStructSize=sizeof(ST_IMAGE_PGM);
+ break;
+ }
+ copy_from_user(pt,(ST_IMAGE_PGM *)arg,dwImgStructSize);
+ GALLEN_DBGLOCAL_PRINTMSG("DISPLAY INFO : X=%ld,Y=%ld,W=%ld,H=%ld\n\twaveform=%ld,LUT no.=%ld,datap=%p,size=%ld\n",
+ pt->StartX,pt->StartY,pt->Width,pt->Height,
+ pt->WaveForm,pt->LUT_NO,pt->Data,dwImgStructSize);
+ }
+ else {
+ copy_from_user(pt,(ST_IMAGE_PGM_800x600 *)arg,sizeof(ST_IMAGE_PGM_800x600));
+ GALLEN_DBGLOCAL_PRINTMSG("DISPLAY INFO : X=%ld,Y=%ld,W=%ld,H=%ld\n\twaveform=%ld,LUT no.=%ld,datap=%p,size=%ld\n",
+ pt->StartX,pt->StartY,pt->Width,pt->Height,
+ pt->WaveForm,pt->LUT_NO,pt->Data,sizeof(ST_IMAGE_PGM_800x600));
+ }
+
+
+
+ _Epson_displayCMD(pt,pDC);
+
+
+ #if 0//(GDEBUG>0)//[
+ {
+ int iShowCnt = 100,i;
+ DBG_MSG("data={{{\n\t");
+ for(i=0;i<iShowCnt;i++) {
+ DBG_MSG("0x02,",pt->Data[i]);
+ }
+ DBG_MSG("\n}}}\n");
+ }
+ #endif //]
+
+ /*
+ //fr.pointer = ;
+ if((ConfigPVI.Reg0x140&0x0030)==0x0030) {
+ GALLEN_DBGLOCAL_RUNLOG(0);
+ iBpp = 8;
+ fr.offset = 0 ;
+ fr.count = pt->Width * pt->Height * iBpp / 8;
+ par->board->write_gray8(fr.offset,pt->Data,fr.count);
+ }
+ else {
+ GALLEN_DBGLOCAL_RUNLOG(1);
+ iBpp = 4;
+ fr.offset = 0 ;
+ fr.count = pt->Width * pt->Height * iBpp / 8;
+ par->board->write(fr.offset,pt->Data,fr.count);
+ }
+ */
+
+ kfree(pt);
+ }
+ else {
+ ERR_MSG("%s(%d):memory not enough !!\n",__func__,__LINE__);
+ GALLEN_DBGLOCAL_RUNLOG(8);
+ ret = -ENOMEM;
+ }
+
+
+ }
+ break;
+
+ case S1D13521_REGWRITE:GALLEN_DBGLOCAL_RUNLOG(9);
+ {
+ struct s1d13521_ioctl_hwc *pIoctl_hwc = &ioctl_hwc;
+
+ if (copy_from_user(&ioctl_hwc, arg, sizeof(ioctl_hwc))) {
+ GALLEN_DBGLOCAL_ESC();
+ return -EFAULT;
+ }
+
+ GALLEN_DBGLOCAL_PRINTMSG("REG[%x] <== 0x%04x\n",pIoctl_hwc->addr,pIoctl_hwc->value);
+ S1D13522_reg_write(pIoctl_hwc->addr,pIoctl_hwc->value);
+ }
+ break;
+
+ case S1D13521_REGREAD:GALLEN_DBGLOCAL_RUNLOG(10);
+ {
+ struct s1d13521_ioctl_hwc *pIoctl_hwc = &ioctl_hwc;
+
+ //DBG0_MSG("--- REGREAD 1----\n");
+
+ if (copy_from_user(&ioctl_hwc, arg, sizeof(ioctl_hwc))) {
+ GALLEN_DBGLOCAL_ESC();
+ return -EFAULT;
+ }
+ //DBG0_MSG("--- REGREAD 2,addr=0x%x----\n",pIoctl_hwc->addr);
+ pIoctl_hwc->value = S1D13522_reg_read(pIoctl_hwc->addr);
+ if (copy_to_user(arg,&ioctl_hwc,sizeof(ioctl_hwc))) {
+ GALLEN_DBGLOCAL_ESC();
+ return -EFAULT;
+ }
+ GALLEN_DBGLOCAL_PRINTMSG("REG[%x] ==> 0x%04x\n",pIoctl_hwc->addr,pIoctl_hwc->value);
+ }
+ break;
+
+ case S1D13521_FLASH:GALLEN_DBGLOCAL_RUNLOG(11);
+ {
+ //S1D13532_FLASH_PACKAGE tFlashPack ;
+ PS1D13532_FLASH_PACKAGE pFlashPack=(PS1D13532_FLASH_PACKAGE)arg;
+
+ //if (copy_from_user(pFlashPack, arg, sizeof(tFlashPack))) {
+ // GALLEN_DBGLOCAL_ESC();
+ // return -EFAULT;
+ //}
+
+ GALLEN_DBGLOCAL_PRINTMSG("Cmd=0x%x,StartAddr=0x%08x,DataLength=%lu\n,Buf=0x%02x,0x%02x,0x%02x,0x%02x\n",
+ pFlashPack->Command,pFlashPack->StartAddr,pFlashPack->DataLength,
+ pFlashPack->Buf[0],pFlashPack->Buf[1],pFlashPack->Buf[2],pFlashPack->Buf[3]);
+ BusIssueFlashOperation(pFlashPack,pDC);
+ }
+ break;
+
+ case S1D13521_SETDEPTH:GALLEN_DBGLOCAL_RUNLOG(12);
+ {
+ s1d13521_ioctl_cmd_params cmd_params ,*pcmd_params = &cmd_params;
+
+ if (copy_from_user(pcmd_params, arg, sizeof(cmd_params))) {
+ GALLEN_DBGLOCAL_ESC();
+ return -EFAULT;
+ }
+
+ GALLEN_DBGLOCAL_PRINTMSG("S1D13521 SETDEPTH param[0]=%x\n",pcmd_params->param[0]);
+ }
+ break;
+
+ case S1D13521_LOAD_AREA:GALLEN_DBGLOCAL_RUNLOG(13);
+ {
+ int status = Epson_LoadImageArea((PTloadImageArea) arg,pDC);
+
+ #ifndef _ES_LUTNOWAIT_//[
+ // start a timer if there is not one already running.
+
+ while (resolve_conflict_step) {
+ msleep (100);
+ }
+ if (bConflictTimerRunning) {
+ GALLEN_DBGLOCAL_RUNLOG(14);
+ // printk("evidentpoint: Pushing the conflict resolution timer forward\n");
+ mod_timer(&conflict_resolution_timer, jiffies+(HZ/4));
+ } else {
+ GALLEN_DBGLOCAL_RUNLOG(15);
+ // printk("evidentpoint: Starting the conflict resolution timer\n");
+ bConflictTimerRunning = TRUE;
+ conflict_resolution_timer.expires = jiffies+(HZ/4);
+ add_timer(&conflict_resolution_timer);
+ }
+ #endif //] _ES_LUTNOWAIT_
+ GALLEN_DBGLOCAL_ESC();
+ return status;
+ // end KEG 20090814
+
+ }
+ break;
+ case S1D13521_PGM:GALLEN_DBGLOCAL_RUNLOG(16);
+ ImagePGM((PST_IMAGE_PGM) arg,pDC);
+ GALLEN_DBGLOCAL_ESC();
+ return 0;
+
+ case S1D13521_WAIT_DSPE_TRG:GALLEN_DBGLOCAL_RUNLOG(18);
+ GALLEN_DBGLOCAL_ESC();
+ return 0;
+ case S1D13521_WAIT_DSPE_FREND:GALLEN_DBGLOCAL_RUNLOG(19);
+ pDC->iIsForceWaitUpdateFinished=1;
+ GALLEN_DBGLOCAL_ESC();
+ return 0;
+
+ #ifdef _PVI_IOCTL_INTERFACE //[
+
+ case CMD_SendCommand:GALLEN_DBGLOCAL_RUNLOG(17);
+ {
+ ret = pvi_SwitchCommand((PTDisplayCommand) arg);
+ }
+ break;
+ #endif //] _PVI_IOCTL_INTERFACE
+
+ case EPDC_PWR_CTRL:GALLEN_DBGLOCAL_RUNLOG(20);
+ {
+ int iPwrCtrlCmd=*((int*)arg);
+ DBG_MSG("EPDC_PWR_CTRL %d\n",iPwrCtrlCmd);
+ switch(iPwrCtrlCmd) {
+ case EPDC_AUTOPWR_INTERVAL_MAX:// set auto off interval max .
+ case EPDC_AUTOPWR_INTERVAL_NORMAL:// set auto off interval mormal .
+ if(pDC->pfnPwrAutoOffIntervalMax) {
+ pDC->pfnPwrAutoOffIntervalMax(EPDC_AUTOPWR_INTERVAL_MAX==iPwrCtrlCmd?1:0);
+ ret = 0;
+ }
+ break;
+
+ case EPDC_PWR_ON:
+ case EPDC_PWR_OFF:
+ if(pDC->pfnPwrOnOff) {
+ pDC->pfnPwrOnOff(EPDC_PWR_ON==iPwrCtrlCmd?1:0);
+ ret = 0;
+ }
+ break;
+
+ case EPDC_VCOM_ON:
+ case EPDC_VCOM_OFF:
+ if(pDC->pfnVcomEnable) {
+ pDC->pfnVcomEnable(EPDC_VCOM_ON==iPwrCtrlCmd?1:0);
+ ret = 0;
+ }
+ break;
+
+ case EPDC_AUTOOFF_ENABLE:
+ case EPDC_AUTOOFF_DISABLE:
+ if(pDC->pfnAutoOffEnable) {
+ pDC->pfnAutoOffEnable(EPDC_AUTOOFF_ENABLE==iPwrCtrlCmd?1:0);
+ ret = 0;
+ }
+ break;
+ }
+ }
+ break;
+
+ case EPDC_VCOM_SET:GALLEN_DBGLOCAL_RUNLOG(21);
+ {
+ int iVCOM_set_val=(int)arg;
+ if(pDC->pfnSetVCOM) {
+ ret = pDC->pfnSetVCOM(iVCOM_set_val)>=0?0:-ENOTTY;
+ }
+ else {
+ ret = -ENOTTY;
+ }
+ }
+ break;
+
+ case EPDC_VCOM_SET_TO_FLASH:GALLEN_DBGLOCAL_RUNLOG(22);
+ {
+ int iVCOM_set_val=(int)arg;
+ if(pDC->pfnSetVCOMToFlash) {
+ ret = pDC->pfnSetVCOMToFlash(iVCOM_set_val)>=0?0:-ENOTTY;
+ }
+ else {
+ ret = -ENOTTY;
+ }
+ }
+ break;
+
+ case EPDC_VCOM_GET:GALLEN_DBGLOCAL_RUNLOG(23);
+ {
+ int iVCOM_get_val;
+ if(pDC->pfnGetVCOM) {
+ pDC->pfnGetVCOM(&iVCOM_get_val);
+ copy_to_user(arg,&iVCOM_get_val,sizeof(iVCOM_get_val));
+ }
+ else {
+ ret = -ENOTTY;
+ }
+ }
+ break;
+
+ default :
+ ERR_MSG("[fake_21d13522] %s() : unsupported cmd (0x%x)\n",__FUNCTION__,cmd);
+ ret = -ENOTTY;
+ break;
+ }
+
+
+ GALLEN_DBGLOCAL_END();
+ return ret;
+}
+
+int fb_capture_ex(EPDFB_DC *pDC,int iSrcImgX,int iSrcImgY,int iSrcImgW,int iSrcImgH,
+ int iBitsTo,EPDFB_ROTATE_T I_tRotateDegree,char *pszFileName,
+ unsigned long I_dwCapTotal)
+{
+ EPDFB_DC *ptDC_Capture;
+ char cfnbufA[256];
+// unsigned long _FBw,_FBh;
+ unsigned long _w,_h,_x,_y;
+ static unsigned long gdwCaptureCnt=0;
+#ifdef OUTPUT_SNAPSHOT_IMGFILE//[
+ const unsigned long dwCaptureTotals=OUTPUT_SNAPSHOT_IMGFILE;
+#else//][!OUTPUT_SNAPSHOT_IMGFILE
+ const unsigned long dwCaptureTotals=I_dwCapTotal;
+#endif //]OUTPUT_SNAPSHOT_IMGFILE
+ int iCaptureIDX;
+
+
+ ASSERT(pDC);
+
+ if(0==pszFileName) {
+ return -1;
+ }
+
+ if('\0'==pszFileName[0]) {
+ return -2;
+ }
+
+ switch(I_tRotateDegree)
+ {
+ default:
+ case EPDFB_R_0:
+ case EPDFB_R_180:
+ _w = (unsigned long)iSrcImgW;
+ _h = (unsigned long)iSrcImgH;
+ //_FBw = _w + pDC->dwFBWExtra;
+ //_FBh = _h + pDC->dwFBHExtra;
+ _x = (unsigned long)iSrcImgX;
+ _y = (unsigned long)iSrcImgY;
+ break;
+ case EPDFB_R_90:
+ case EPDFB_R_270:
+ _w = (unsigned long)iSrcImgW;
+ _h = (unsigned long)iSrcImgH;
+ //_FBh = _w + pDC->dwFBWExtra;
+ //_FBw = _h + pDC->dwFBHExtra;
+ _x = (unsigned long)iSrcImgX;
+ _y = (unsigned long)iSrcImgY;
+ break;
+ }
+
+ iCaptureIDX = gdwCaptureCnt++%dwCaptureTotals;
+
+ ptDC_Capture = epdfbdc_create_ex2 ( _w,_h,_w,_h,iBitsTo,0,pDC->dwFlags);
+ epdfbdc_put_dcimg(ptDC_Capture,pDC,I_tRotateDegree,_x,_y,_w,_h,0,0);
+
+ sprintf(cfnbufA,"%s_%ux%u-%d_%dx-%dy-%dx%d.raw%d",
+ pszFileName,pDC->dwWidth,pDC->dwHeight,iCaptureIDX,
+ _x,_y,_w,_h,iBitsTo);
+
+ printk("write raw img %d/%d -> \"%s\" ,%d bits,%u bytes,w=%u,h=%u\n",
+ iCaptureIDX,dwCaptureTotals,cfnbufA,iBitsTo,ptDC_Capture->dwDCSize,_w,_h);
+
+ write_file_ex(cfnbufA,ptDC_Capture->pbDCbuf, ptDC_Capture->dwDCSize) ;
+
+ if(8==iBitsTo) {
+ printk("printf \"P5\\n%u %u\\n255\\n\"> %s.pgm \n",_w,_h,cfnbufA);
+ printk("cat \"%s\">> %s.pgm \n",cfnbufA,cfnbufA);
+ }
+ else if(16 == iBitsTo) {
+ printk("rawtoppm %u %u %s > %s.ppm \n",_w,_h,cfnbufA,cfnbufA);
+ }
+ else {
+ }
+
+ epdfbdc_delete(ptDC_Capture);
+
+ if(gdwCaptureCnt>=dwCaptureTotals) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+}
+
+#ifdef SHOW_PROGRESS_BAR //[
+
+static VOID PROGRESS_BAR(EPDFB_DC *pDC)
+{
+ int i;
+
+ int startX = _PROGRESS_BAR_X_;
+ int startY = _PROGRESS_BAR_Y_;
+ int icons = _PROGRESS_BAR_ICONS_;
+ const int width = 16;
+ const int height = 16;
+ int space = 20;
+ unsigned long dwDrawDataSize ;
+ unsigned char *pbDrawData;
+ epdfb_rotate_t L_tRotate ;
+
+ if(!gptHWCFG) {
+ WARNING_MSG("%s(%d): %s() skip without ntx hwconfig !\n",
+ __FILE__,__LINE__,__FUNCTION__);
+ return ;
+ }
+
+ ASSERT(pDC);
+ //gIsProgessRunning = 1;
+ DBG_MSG("%s() ,progresscnt=%d\n",__FUNCTION__,gIsProgessRunning);
+
+ icons = gptHWCFG->m_val.bProgressCnts;
+ startX = (int)(gptHWCFG->m_val.bProgressXHiByte<<8|gptHWCFG->m_val.bProgressXLoByte);
+ startY = (int)(gptHWCFG->m_val.bProgressYHiByte<<8|gptHWCFG->m_val.bProgressYLoByte);
+
+ if (! icons) {
+ ERR_MSG("[%s-%d] No progess ...\n",__func__,__LINE__);
+ return;
+ }
+ else {
+ DBG_MSG("progress icons=%d,x=%d,y=%d\n",icons,startX,startY);
+ }
+
+
+ dwDrawDataSize = width*height;
+ pbDrawData = kmalloc(dwDrawDataSize,GFP_KERNEL);
+ ASSERT(pbDrawData);
+
+
+
+
+ if(pDC->pfnWaitUpdateComplete) {
+ pDC->pfnWaitUpdateComplete();
+ }
+ else {
+ while(pDC->pfnIsUpdating()) {
+ //DBG0_MSG("%s(%d):wait for update done .\n");
+ schedule();
+ }
+ }
+
+ msleep(500);
+
+ fake_s1d13522_setwfmode(pDC,1);
+ pDC->pfnSetPartialUpdate(1);
+
+ if(0==gptHWCFG->m_val.bDisplayPanel||3==gptHWCFG->m_val.bDisplayPanel||\
+ 6==gptHWCFG->m_val.bDisplayPanel||8==gptHWCFG->m_val.bDisplayPanel)
+ {
+ L_tRotate = epdfb_rotate_270;
+ }
+ else {
+ L_tRotate = epdfb_rotate_90;
+ }
+ //DBG0_MSG("pbDrawData=%p,size=%u,rotate=%d\n",pbDrawData,dwDrawDataSize,L_tRotate);
+
+
+
+ while (gIsProgessRunning) {
+ for(i=0;i<icons && gIsProgessRunning;){
+ //DBG0_MSG("%s[%d]: rotate=%d\n",__FUNCTION__,__LINE__,L_tRotate);
+ if(pDC->pfnIsUpdating()) {
+ DBG_MSG("%s[%d]: wait update done ...\n",__FUNCTION__,__LINE__);
+ schedule();
+ continue;
+ }
+ else {
+ //fake_s1d13522_setwfmode(pDC,1);
+ memset(pbDrawData , 0 , dwDrawDataSize);
+ fake_s1d13522_display_img(startX+(space*i),startY,width,height,\
+ pbDrawData,pDC,4,L_tRotate);
+ }
+ sleep_on_timeout(&progress_WaitQueue,HZ/2);
+ i++;
+ }
+ for(i=0;i<icons && gIsProgessRunning;){
+ //DBG0_MSG("%s[%d]: rotate=%d\n",__FUNCTION__,__LINE__,L_tRotate);
+ if(pDC->pfnIsUpdating()) {
+ DBG_MSG("%s[%d]: wait update done ...\n",__FUNCTION__,__LINE__);
+ schedule();
+ continue;
+ }
+ else {
+ //fake_s1d13522_setwfmode(pDC,1);
+ memset(pbDrawData , 0xFF , dwDrawDataSize);
+ fake_s1d13522_display_img(startX+(space*i)+1,startY+1,\
+ width-2,height-2,pbDrawData,pDC,4,L_tRotate);
+ }
+ sleep_on_timeout(&progress_WaitQueue,HZ/2);
+ i++;
+ }
+ }
+ kfree(pbDrawData);
+}
+void fb_capture(EPDFB_DC *pDC,int iBitsTo,EPDFB_ROTATE_T I_tRotateDegree,char *pszFileName)
+{
+ fb_capture_ex(pDC,0,0,pDC->dwWidth,pDC->dwHeight,
+ iBitsTo,I_tRotateDegree,pszFileName,1);
+}
+#endif //] SHOW_PROGRESS_BAR
+
diff --git a/drivers/video/mxc/fake_s1d13522.h b/drivers/video/mxc/fake_s1d13522.h
new file mode 100644
index 00000000..f9ec721e
--- /dev/null
+++ b/drivers/video/mxc/fake_s1d13522.h
@@ -0,0 +1,67 @@
+#ifndef fake_s1d13522_h //[
+#define fake_s1d13522_h
+
+#include <linux/kernel.h>
+#include "epdfb_dc.h"
+
+
+//#define OUTPUT_SNAPSHOT_IMGFILE 16
+
+
+typedef enum {
+ epdfb_rotate_0=0,
+ epdfb_rotate_90=1,
+ epdfb_rotate_180=2,
+ epdfb_rotate_270=3,
+} epdfb_rotate_t;
+
+
+
+extern volatile unsigned char *gpbLOGO_vaddr;
+extern volatile unsigned char *gpbLOGO_paddr;
+extern volatile unsigned long gdwLOGO_size;
+
+extern volatile unsigned char *gpbWF_vaddr;
+extern volatile unsigned char *gpbWF_paddr;
+extern volatile unsigned long gdwWF_size;
+
+extern volatile long glVCOM_uV;
+
+
+EPDFB_DC *fake_s1d13522_init(void);
+EPDFB_DC *fake_s1d13522_initEx(unsigned char bBitsPerPixel,unsigned char *pbDCBuf);
+int fake_s1d13522_release(EPDFB_DC *pDC);
+EPDFB_DC *fake_s1d13522_initEx2(unsigned char bBitsPerPixel,unsigned char *pbDCBuf,
+ unsigned short wScrW,unsigned short wScrH);
+EPDFB_DC *fake_s1d13522_initEx3(unsigned char bBitsPerPixel,unsigned char *pbDCBuf,
+ unsigned short wScrW,unsigned short wScrH,unsigned short wFBW,unsigned short wFBH);
+
+int32_t fake_s1d13522_ioctl(unsigned int cmd,unsigned long arg,EPDFB_DC *pDC);
+void fake_s1d13522_progress_start(EPDFB_DC *pDC);
+
+void fake_s1d13522_progress_stop(void);
+
+void fake_s1d13522_parse_epd_cmdline(void);
+
+int fake_s1d13522_display_img(u16 wX,u16 wY,u16 wW,u16 wH,u8 *pbImgBuf,
+ EPDFB_DC *pDC,int iPixelBits,epdfb_rotate_t I_tRotate);
+
+
+// fb_check_var() helper ...
+int fake_s1d13522_check_var(struct fb_var_screeninfo *var, struct fb_info *info);
+// gallen 2011/05/24 copy from s1d13521base.c .
+int fake_s1d13522_setcolreg(unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp, struct fb_info *info);
+
+
+int fake_s1d13522_write_file_ex2(char *filename, char *data,unsigned long dwDataSize,unsigned long dwSeekSize);
+
+
+int fb_capture_ex(EPDFB_DC *pDC,int iSrcImgX,int iSrcImgY,int iSrcImgW,int iSrcImgH,
+ int iBitsTo,EPDFB_ROTATE_T I_tRotateDegree,char *pszFileName,
+ unsigned long I_dwCapTotal);
+
+void fb_capture(EPDFB_DC *pDC,int iBitsTo,EPDFB_ROTATE_T I_tRotateDegree,char *pszFileName);
+
+int fake_s1d13522_is_logo_bypss(void);
+#endif //]fake_s1d13522_h
diff --git a/drivers/video/mxc/ldb.c b/drivers/video/mxc/ldb.c
new file mode 100644
index 00000000..fabc2529
--- /dev/null
+++ b/drivers/video/mxc/ldb.c
@@ -0,0 +1,974 @@
+/*
+ * Copyright (C) 2012-2013 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*!
+ * @file mxc_ldb.c
+ *
+ * @brief This file contains the LDB driver device interface and fops
+ * functions.
+ */
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/io.h>
+#include <linux/ipu.h>
+#include <linux/mxcfb.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spinlock.h>
+#include <linux/fsl_devices.h>
+#include <mach/hardware.h>
+#include <mach/clock.h>
+#include "mxc_dispdrv.h"
+
+#define DISPDRV_LDB "ldb"
+
+#define LDB_BGREF_RMODE_MASK 0x00008000
+#define LDB_BGREF_RMODE_INT 0x00008000
+#define LDB_BGREF_RMODE_EXT 0x0
+
+#define LDB_DI1_VS_POL_MASK 0x00000400
+#define LDB_DI1_VS_POL_ACT_LOW 0x00000400
+#define LDB_DI1_VS_POL_ACT_HIGH 0x0
+#define LDB_DI0_VS_POL_MASK 0x00000200
+#define LDB_DI0_VS_POL_ACT_LOW 0x00000200
+#define LDB_DI0_VS_POL_ACT_HIGH 0x0
+
+#define LDB_BIT_MAP_CH1_MASK 0x00000100
+#define LDB_BIT_MAP_CH1_JEIDA 0x00000100
+#define LDB_BIT_MAP_CH1_SPWG 0x0
+#define LDB_BIT_MAP_CH0_MASK 0x00000040
+#define LDB_BIT_MAP_CH0_JEIDA 0x00000040
+#define LDB_BIT_MAP_CH0_SPWG 0x0
+
+#define LDB_DATA_WIDTH_CH1_MASK 0x00000080
+#define LDB_DATA_WIDTH_CH1_24 0x00000080
+#define LDB_DATA_WIDTH_CH1_18 0x0
+#define LDB_DATA_WIDTH_CH0_MASK 0x00000020
+#define LDB_DATA_WIDTH_CH0_24 0x00000020
+#define LDB_DATA_WIDTH_CH0_18 0x0
+
+#define LDB_CH1_MODE_MASK 0x0000000C
+#define LDB_CH1_MODE_EN_TO_DI1 0x0000000C
+#define LDB_CH1_MODE_EN_TO_DI0 0x00000004
+#define LDB_CH1_MODE_DISABLE 0x0
+#define LDB_CH0_MODE_MASK 0x00000003
+#define LDB_CH0_MODE_EN_TO_DI1 0x00000003
+#define LDB_CH0_MODE_EN_TO_DI0 0x00000001
+#define LDB_CH0_MODE_DISABLE 0x0
+
+#define LDB_SPLIT_MODE_EN 0x00000010
+
+struct ldb_data {
+ struct platform_device *pdev;
+ struct mxc_dispdrv_handle *disp_ldb;
+ uint32_t *reg;
+ uint32_t *control_reg;
+ uint32_t *gpr3_reg;
+ uint32_t control_reg_data;
+ struct regulator *lvds_bg_reg;
+ int mode;
+ bool inited;
+ struct ldb_setting {
+ struct clk *di_clk;
+ struct clk *ldb_di_clk;
+ bool active;
+ bool clk_en;
+ int ipu;
+ int di;
+ uint32_t ch_mask;
+ uint32_t ch_val;
+ } setting[2];
+ struct notifier_block nb;
+};
+
+static int g_ldb_mode;
+static struct i2c_client *ldb_i2c_client[2];
+
+static struct fb_videomode ldb_modedb[] = {
+ {
+ "LDB-WXGA", 60, 1280, 800, 14065,
+ 40, 40,
+ 10, 3,
+ 80, 10,
+ 0,
+ FB_VMODE_NONINTERLACED,
+ FB_MODE_IS_DETAILED,},
+ {
+ "LDB-XGA", 60, 1024, 768, 15385,
+ 220, 40,
+ 21, 7,
+ 60, 10,
+ 0,
+ FB_VMODE_NONINTERLACED,
+ FB_MODE_IS_DETAILED,},
+ {
+ "LDB-1080P60", 60, 1920, 1080, 7692,
+ 100, 40,
+ 30, 3,
+ 10, 2,
+ 0,
+ FB_VMODE_NONINTERLACED,
+ FB_MODE_IS_DETAILED,},
+};
+static int ldb_modedb_sz = ARRAY_SIZE(ldb_modedb);
+
+static int bits_per_pixel(int pixel_fmt)
+{
+ switch (pixel_fmt) {
+ case IPU_PIX_FMT_BGR24:
+ case IPU_PIX_FMT_RGB24:
+ return 24;
+ break;
+ case IPU_PIX_FMT_BGR666:
+ case IPU_PIX_FMT_RGB666:
+ case IPU_PIX_FMT_LVDS666:
+ return 18;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int valid_mode(int pixel_fmt)
+{
+ return ((pixel_fmt == IPU_PIX_FMT_RGB24) ||
+ (pixel_fmt == IPU_PIX_FMT_BGR24) ||
+ (pixel_fmt == IPU_PIX_FMT_LVDS666) ||
+ (pixel_fmt == IPU_PIX_FMT_RGB666) ||
+ (pixel_fmt == IPU_PIX_FMT_BGR666));
+}
+
+/*
+ * "ldb=spl0/1" -- split mode on DI0/1
+ * "ldb=dul0/1" -- dual mode on DI0/1
+ * "ldb=sin0/1" -- single mode on LVDS0/1
+ * "ldb=sep0/1" -- separate mode begin from LVDS0/1
+ *
+ * there are two LVDS channels(LVDS0 and LVDS1) which can transfer video
+ * datas, there two channels can be used as split/dual/single/separate mode.
+ *
+ * split mode means display data from DI0 or DI1 will send to both channels
+ * LVDS0+LVDS1.
+ * dual mode means display data from DI0 or DI1 will be duplicated on LVDS0
+ * and LVDS1, it said, LVDS0 and LVDS1 has the same content.
+ * single mode means only work for DI0/DI1->LVDS0 or DI0/DI1->LVDS1.
+ * separate mode means you can make DI0/DI1->LVDS0 and DI0/DI1->LVDS1 work
+ * at the same time.
+ */
+static int __init ldb_setup(char *options)
+{
+ if (!strcmp(options, "spl0"))
+ g_ldb_mode = LDB_SPL_DI0;
+ else if (!strcmp(options, "spl1"))
+ g_ldb_mode = LDB_SPL_DI1;
+ else if (!strcmp(options, "dul0"))
+ g_ldb_mode = LDB_DUL_DI0;
+ else if (!strcmp(options, "dul1"))
+ g_ldb_mode = LDB_DUL_DI1;
+ else if (!strcmp(options, "sin0"))
+ g_ldb_mode = LDB_SIN0;
+ else if (!strcmp(options, "sin1"))
+ g_ldb_mode = LDB_SIN1;
+ else if (!strcmp(options, "sep0"))
+ g_ldb_mode = LDB_SEP0;
+ else if (!strcmp(options, "sep1"))
+ g_ldb_mode = LDB_SEP1;
+
+ return 1;
+}
+__setup("ldb=", ldb_setup);
+
+static int find_ldb_setting(struct ldb_data *ldb, struct fb_info *fbi)
+{
+ char *id_di[] = {
+ "DISP3 BG",
+ "DISP3 BG - DI1",
+ };
+ char id[16];
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ if (ldb->setting[i].active) {
+ memset(id, 0, 16);
+ memcpy(id, id_di[ldb->setting[i].di],
+ strlen(id_di[ldb->setting[i].di]));
+ id[4] += ldb->setting[i].ipu;
+ if (!strcmp(id, fbi->fix.id))
+ return i;
+ }
+ }
+ return -EINVAL;
+}
+
+static int ldb_disp_setup(struct mxc_dispdrv_handle *disp, struct fb_info *fbi)
+{
+ uint32_t reg, val;
+ uint32_t pixel_clk, rounded_pixel_clk;
+ struct clk *ldb_clk_parent;
+ struct ldb_data *ldb = mxc_dispdrv_getdata(disp);
+ int setting_idx, di;
+
+ setting_idx = find_ldb_setting(ldb, fbi);
+ if (setting_idx < 0)
+ return setting_idx;
+
+ di = ldb->setting[setting_idx].di;
+
+ /* restore channel mode setting */
+ val = readl(ldb->control_reg);
+ val |= ldb->setting[setting_idx].ch_val;
+ writel(val, ldb->control_reg);
+ dev_dbg(&ldb->pdev->dev, "LDB setup, control reg:0x%x\n",
+ readl(ldb->control_reg));
+
+ /* vsync setup */
+ reg = readl(ldb->control_reg);
+ if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT) {
+ if (di == 0)
+ reg = (reg & ~LDB_DI0_VS_POL_MASK)
+ | LDB_DI0_VS_POL_ACT_HIGH;
+ else
+ reg = (reg & ~LDB_DI1_VS_POL_MASK)
+ | LDB_DI1_VS_POL_ACT_HIGH;
+ } else {
+ if (di == 0)
+ reg = (reg & ~LDB_DI0_VS_POL_MASK)
+ | LDB_DI0_VS_POL_ACT_LOW;
+ else
+ reg = (reg & ~LDB_DI1_VS_POL_MASK)
+ | LDB_DI1_VS_POL_ACT_LOW;
+ }
+ writel(reg, ldb->control_reg);
+
+ /* clk setup */
+ if (ldb->setting[setting_idx].clk_en)
+ clk_disable(ldb->setting[setting_idx].ldb_di_clk);
+ pixel_clk = (PICOS2KHZ(fbi->var.pixclock)) * 1000UL;
+ ldb_clk_parent = clk_get_parent(ldb->setting[setting_idx].ldb_di_clk);
+ if ((ldb->mode == LDB_SPL_DI0) || (ldb->mode == LDB_SPL_DI1))
+ clk_set_rate(ldb_clk_parent, pixel_clk * 7 / 2);
+ else
+ clk_set_rate(ldb_clk_parent, pixel_clk * 7);
+ rounded_pixel_clk = clk_round_rate(ldb->setting[setting_idx].ldb_di_clk,
+ pixel_clk);
+ clk_set_rate(ldb->setting[setting_idx].ldb_di_clk, rounded_pixel_clk);
+ clk_enable(ldb->setting[setting_idx].ldb_di_clk);
+ if (!ldb->setting[setting_idx].clk_en)
+ ldb->setting[setting_idx].clk_en = true;
+
+ return 0;
+}
+
+int ldb_fb_event(struct notifier_block *nb, unsigned long val, void *v)
+{
+ struct ldb_data *ldb = container_of(nb, struct ldb_data, nb);
+ struct fb_event *event = v;
+ struct fb_info *fbi = event->info;
+ int index;
+ uint32_t data;
+
+ index = find_ldb_setting(ldb, fbi);
+ if (index < 0)
+ return 0;
+
+ fbi->mode = (struct fb_videomode *)fb_match_mode(&fbi->var,
+ &fbi->modelist);
+
+ if (!fbi->mode) {
+ dev_warn(&ldb->pdev->dev,
+ "LDB: can not find mode for xres=%d, yres=%d\n",
+ fbi->var.xres, fbi->var.yres);
+ if (ldb->setting[index].clk_en) {
+ clk_disable(ldb->setting[index].ldb_di_clk);
+ ldb->setting[index].clk_en = false;
+ data = readl(ldb->control_reg);
+ data &= ~ldb->setting[index].ch_mask;
+ writel(data, ldb->control_reg);
+ }
+ return 0;
+ }
+
+ switch (val) {
+ case FB_EVENT_BLANK:
+ {
+ if (*((int *)event->data) == FB_BLANK_UNBLANK) {
+ if (!ldb->setting[index].clk_en) {
+ clk_enable(ldb->setting[index].ldb_di_clk);
+ ldb->setting[index].clk_en = true;
+ }
+ } else {
+ if (ldb->setting[index].clk_en) {
+ clk_disable(ldb->setting[index].ldb_di_clk);
+ ldb->setting[index].clk_en = false;
+ data = readl(ldb->control_reg);
+ data &= ~ldb->setting[index].ch_mask;
+ writel(data, ldb->control_reg);
+ dev_dbg(&ldb->pdev->dev,
+ "LDB blank, control reg:0x%x\n",
+ readl(ldb->control_reg));
+ }
+ }
+ break;
+ }
+ case FB_EVENT_SUSPEND:
+ if (ldb->setting[index].clk_en) {
+ clk_disable(ldb->setting[index].ldb_di_clk);
+ ldb->setting[index].clk_en = false;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+#define LVDS_MUX_CTL_WIDTH 2
+#define LVDS_MUX_CTL_MASK 3
+#define LVDS0_MUX_CTL_OFFS 6
+#define LVDS1_MUX_CTL_OFFS 8
+#define LVDS0_MUX_CTL_MASK (LVDS_MUX_CTL_MASK << 6)
+#define LVDS1_MUX_CTL_MASK (LVDS_MUX_CTL_MASK << 8)
+#define ROUTE_IPU_DI(ipu, di) (((ipu << 1) | di) & LVDS_MUX_CTL_MASK)
+static int ldb_ipu_ldb_route(int ipu, int di, struct ldb_data *ldb)
+{
+ uint32_t reg;
+ int channel;
+ int shift;
+ int mode = ldb->mode;
+
+ reg = readl(ldb->gpr3_reg);
+ if (mode < LDB_SIN0) {
+ reg &= ~(LVDS0_MUX_CTL_MASK | LVDS1_MUX_CTL_MASK);
+ reg |= (ROUTE_IPU_DI(ipu, di) << LVDS0_MUX_CTL_OFFS) |
+ (ROUTE_IPU_DI(ipu, di) << LVDS1_MUX_CTL_OFFS);
+ dev_dbg(&ldb->pdev->dev,
+ "Dual/Split mode both channels route to IPU%d-DI%d\n",
+ ipu, di);
+ } else if ((mode == LDB_SIN0) || (mode == LDB_SIN1)) {
+ reg &= ~(LVDS0_MUX_CTL_MASK | LVDS1_MUX_CTL_MASK);
+ channel = mode - LDB_SIN0;
+ shift = LVDS0_MUX_CTL_OFFS + channel * LVDS_MUX_CTL_WIDTH;
+ reg |= ROUTE_IPU_DI(ipu, di) << shift;
+ dev_dbg(&ldb->pdev->dev,
+ "Single mode channel %d route to IPU%d-DI%d\n",
+ channel, ipu, di);
+ } else {
+ static bool first = true;
+
+ if (first) {
+ if (mode == LDB_SEP0) {
+ reg &= ~LVDS0_MUX_CTL_MASK;
+ channel = 0;
+ } else {
+ reg &= ~LVDS1_MUX_CTL_MASK;
+ channel = 1;
+ }
+ first = false;
+ } else {
+ if (mode == LDB_SEP0) {
+ reg &= ~LVDS1_MUX_CTL_MASK;
+ channel = 1;
+ } else {
+ reg &= ~LVDS0_MUX_CTL_MASK;
+ channel = 0;
+ }
+ }
+
+ shift = LVDS0_MUX_CTL_OFFS + channel * LVDS_MUX_CTL_WIDTH;
+ reg |= ROUTE_IPU_DI(ipu, di) << shift;
+
+ dev_dbg(&ldb->pdev->dev,
+ "Separate mode channel %d route to IPU%d-DI%d\n",
+ channel, ipu, di);
+ }
+ writel(reg, ldb->gpr3_reg);
+
+ return 0;
+}
+
+static int ldb_disp_init(struct mxc_dispdrv_handle *disp,
+ struct mxc_dispdrv_setting *setting)
+{
+ int ret = 0, i;
+ struct ldb_data *ldb = mxc_dispdrv_getdata(disp);
+ struct fsl_mxc_ldb_platform_data *plat_data = ldb->pdev->dev.platform_data;
+ struct i2c_client *i2c_dev;
+ struct resource *res;
+ uint32_t base_addr;
+ uint32_t reg, setting_idx;
+ uint32_t ch_mask = 0, ch_val = 0;
+ uint32_t ipu_id, disp_id;
+
+ /* if input format not valid, make RGB666 as default*/
+ if (!valid_mode(setting->if_fmt)) {
+ dev_warn(&ldb->pdev->dev, "Input pixel format not valid"
+ " use default RGB666\n");
+ setting->if_fmt = IPU_PIX_FMT_RGB666;
+ }
+
+ if (!ldb->inited) {
+ char di_clk[] = "ipu1_di0_clk";
+ char ldb_clk[] = "ldb_di0_clk";
+ int lvds_channel = 0;
+
+ setting_idx = 0;
+ res = platform_get_resource(ldb->pdev, IORESOURCE_MEM, 0);
+ if (IS_ERR(res))
+ return -ENOMEM;
+
+ base_addr = res->start;
+ ldb->reg = ioremap(base_addr, res->end - res->start + 1);
+ ldb->control_reg = ldb->reg + 2;
+ ldb->gpr3_reg = ldb->reg + 3;
+
+ ldb->lvds_bg_reg = regulator_get(&ldb->pdev->dev, plat_data->lvds_bg_reg);
+ if (!IS_ERR(ldb->lvds_bg_reg)) {
+ regulator_set_voltage(ldb->lvds_bg_reg, 2500000, 2500000);
+ regulator_enable(ldb->lvds_bg_reg);
+ }
+
+ /* ipu selected by platform data setting */
+ setting->dev_id = plat_data->ipu_id;
+
+ reg = readl(ldb->control_reg);
+
+ /* refrence resistor select */
+ reg &= ~LDB_BGREF_RMODE_MASK;
+ if (plat_data->ext_ref)
+ reg |= LDB_BGREF_RMODE_EXT;
+ else
+ reg |= LDB_BGREF_RMODE_INT;
+
+ /* TODO: now only use SPWG data mapping for both channel */
+ reg &= ~(LDB_BIT_MAP_CH0_MASK | LDB_BIT_MAP_CH1_MASK);
+ reg |= LDB_BIT_MAP_CH0_SPWG | LDB_BIT_MAP_CH1_SPWG;
+
+ /* channel mode setting */
+ reg &= ~(LDB_CH0_MODE_MASK | LDB_CH1_MODE_MASK);
+ reg &= ~(LDB_DATA_WIDTH_CH0_MASK | LDB_DATA_WIDTH_CH1_MASK);
+
+ if (bits_per_pixel(setting->if_fmt) == 24)
+ reg |= LDB_DATA_WIDTH_CH0_24 | LDB_DATA_WIDTH_CH1_24;
+ else
+ reg |= LDB_DATA_WIDTH_CH0_18 | LDB_DATA_WIDTH_CH1_18;
+
+ if (g_ldb_mode)
+ ldb->mode = g_ldb_mode;
+ else
+ ldb->mode = plat_data->mode;
+
+ if ((ldb->mode == LDB_SIN0) || (ldb->mode == LDB_SIN1)) {
+ ret = ldb->mode - LDB_SIN0;
+ if (plat_data->disp_id != ret) {
+ dev_warn(&ldb->pdev->dev,
+ "change IPU DI%d to IPU DI%d for LDB "
+ "channel%d.\n",
+ plat_data->disp_id, ret, ret);
+ plat_data->disp_id = ret;
+ }
+ } else if (((ldb->mode == LDB_SEP0) || (ldb->mode == LDB_SEP1))
+ && (cpu_is_mx6q() || cpu_is_mx6dl())) {
+ if (plat_data->disp_id == plat_data->sec_disp_id) {
+ dev_err(&ldb->pdev->dev,
+ "For LVDS separate mode,"
+ "two DIs should be different!\n");
+ return -EINVAL;
+ }
+
+ if (((!plat_data->disp_id) && (ldb->mode == LDB_SEP1))
+ || ((plat_data->disp_id) &&
+ (ldb->mode == LDB_SEP0))) {
+ dev_dbg(&ldb->pdev->dev,
+ "LVDS separate mode:"
+ "swap DI configuration!\n");
+ ipu_id = plat_data->ipu_id;
+ disp_id = plat_data->disp_id;
+ plat_data->ipu_id = plat_data->sec_ipu_id;
+ plat_data->disp_id = plat_data->sec_disp_id;
+ plat_data->sec_ipu_id = ipu_id;
+ plat_data->sec_disp_id = disp_id;
+ }
+ }
+
+ if (ldb->mode == LDB_SPL_DI0) {
+ reg |= LDB_SPLIT_MODE_EN | LDB_CH0_MODE_EN_TO_DI0
+ | LDB_CH1_MODE_EN_TO_DI0;
+ setting->disp_id = 0;
+ } else if (ldb->mode == LDB_SPL_DI1) {
+ reg |= LDB_SPLIT_MODE_EN | LDB_CH0_MODE_EN_TO_DI1
+ | LDB_CH1_MODE_EN_TO_DI1;
+ setting->disp_id = 1;
+ } else if (ldb->mode == LDB_DUL_DI0) {
+ reg &= ~LDB_SPLIT_MODE_EN;
+ reg |= LDB_CH0_MODE_EN_TO_DI0 | LDB_CH1_MODE_EN_TO_DI0;
+ setting->disp_id = 0;
+ } else if (ldb->mode == LDB_DUL_DI1) {
+ reg &= ~LDB_SPLIT_MODE_EN;
+ reg |= LDB_CH0_MODE_EN_TO_DI1 | LDB_CH1_MODE_EN_TO_DI1;
+ setting->disp_id = 1;
+ } else if (ldb->mode == LDB_SIN0) {
+ reg &= ~LDB_SPLIT_MODE_EN;
+ setting->disp_id = plat_data->disp_id;
+ if (setting->disp_id == 0)
+ reg |= LDB_CH0_MODE_EN_TO_DI0;
+ else
+ reg |= LDB_CH0_MODE_EN_TO_DI1;
+ ch_mask = LDB_CH0_MODE_MASK;
+ ch_val = reg & LDB_CH0_MODE_MASK;
+ } else if (ldb->mode == LDB_SIN1) {
+ reg &= ~LDB_SPLIT_MODE_EN;
+ setting->disp_id = plat_data->disp_id;
+ if (setting->disp_id == 0)
+ reg |= LDB_CH1_MODE_EN_TO_DI0;
+ else
+ reg |= LDB_CH1_MODE_EN_TO_DI1;
+ ch_mask = LDB_CH1_MODE_MASK;
+ ch_val = reg & LDB_CH1_MODE_MASK;
+ } else { /* separate mode*/
+ setting->disp_id = plat_data->disp_id;
+
+ /* first output is LVDS0 or LVDS1 */
+ if (ldb->mode == LDB_SEP0)
+ lvds_channel = 0;
+ else
+ lvds_channel = 1;
+
+ reg &= ~LDB_SPLIT_MODE_EN;
+
+ if ((lvds_channel == 0) && (setting->disp_id == 0))
+ reg |= LDB_CH0_MODE_EN_TO_DI0;
+ else if ((lvds_channel == 0) && (setting->disp_id == 1))
+ reg |= LDB_CH0_MODE_EN_TO_DI1;
+ else if ((lvds_channel == 1) && (setting->disp_id == 0))
+ reg |= LDB_CH1_MODE_EN_TO_DI0;
+ else
+ reg |= LDB_CH1_MODE_EN_TO_DI1;
+ ch_mask = lvds_channel ? LDB_CH1_MODE_MASK :
+ LDB_CH0_MODE_MASK;
+ ch_val = reg & ch_mask;
+
+ if (bits_per_pixel(setting->if_fmt) == 24) {
+ if (lvds_channel == 0)
+ reg &= ~LDB_DATA_WIDTH_CH1_24;
+ else
+ reg &= ~LDB_DATA_WIDTH_CH0_24;
+ } else {
+ if (lvds_channel == 0)
+ reg &= ~LDB_DATA_WIDTH_CH1_18;
+ else
+ reg &= ~LDB_DATA_WIDTH_CH0_18;
+ }
+ }
+
+ writel(reg, ldb->control_reg);
+ if (ldb->mode < LDB_SIN0) {
+ ch_mask = LDB_CH0_MODE_MASK | LDB_CH1_MODE_MASK;
+ ch_val = reg & (LDB_CH0_MODE_MASK | LDB_CH1_MODE_MASK);
+ }
+
+ /* clock setting */
+ if ((cpu_is_mx6q() || cpu_is_mx6dl()) &&
+ ((ldb->mode == LDB_SEP0) || (ldb->mode == LDB_SEP1)))
+ ldb_clk[6] += lvds_channel;
+ else
+ ldb_clk[6] += setting->disp_id;
+ ldb->setting[setting_idx].ldb_di_clk = clk_get(&ldb->pdev->dev,
+ ldb_clk);
+ if (IS_ERR(ldb->setting[setting_idx].ldb_di_clk)) {
+ dev_err(&ldb->pdev->dev, "get ldb clk0 failed\n");
+ iounmap(ldb->reg);
+ return PTR_ERR(ldb->setting[setting_idx].ldb_di_clk);
+ }
+ di_clk[3] += setting->dev_id;
+ di_clk[7] += setting->disp_id;
+ ldb->setting[setting_idx].di_clk = clk_get(&ldb->pdev->dev,
+ di_clk);
+ if (IS_ERR(ldb->setting[setting_idx].di_clk)) {
+ dev_err(&ldb->pdev->dev, "get di clk0 failed\n");
+ iounmap(ldb->reg);
+ return PTR_ERR(ldb->setting[setting_idx].di_clk);
+ }
+
+ dev_dbg(&ldb->pdev->dev, "ldb_clk to di clk: %s -> %s\n", ldb_clk, di_clk);
+
+ /* fb notifier for clk setting */
+ ldb->nb.notifier_call = ldb_fb_event,
+ ret = fb_register_client(&ldb->nb);
+ if (ret < 0) {
+ iounmap(ldb->reg);
+ return ret;
+ }
+
+ ldb->inited = true;
+
+ i2c_dev = ldb_i2c_client[0];
+
+ } else { /* second time for separate mode */
+ char di_clk[] = "ipu1_di0_clk";
+ char ldb_clk[] = "ldb_di0_clk";
+ int lvds_channel;
+
+ if ((ldb->mode == LDB_SPL_DI0) ||
+ (ldb->mode == LDB_SPL_DI1) ||
+ (ldb->mode == LDB_DUL_DI0) ||
+ (ldb->mode == LDB_DUL_DI1) ||
+ (ldb->mode == LDB_SIN0) ||
+ (ldb->mode == LDB_SIN1)) {
+ dev_err(&ldb->pdev->dev, "for second ldb disp"
+ "ldb mode should in separate mode\n");
+ return -EINVAL;
+ }
+
+ setting_idx = 1;
+ if (cpu_is_mx6q() || cpu_is_mx6dl()) {
+ setting->dev_id = plat_data->sec_ipu_id;
+ setting->disp_id = plat_data->sec_disp_id;
+ } else {
+ setting->dev_id = plat_data->ipu_id;
+ setting->disp_id = !plat_data->disp_id;
+ }
+ if (setting->disp_id == ldb->setting[0].di) {
+ dev_err(&ldb->pdev->dev, "Err: for second ldb disp in"
+ "separate mode, DI should be different!\n");
+ return -EINVAL;
+ }
+
+ /* second output is LVDS0 or LVDS1 */
+ if (ldb->mode == LDB_SEP0)
+ lvds_channel = 1;
+ else
+ lvds_channel = 0;
+
+ reg = readl(ldb->control_reg);
+ if ((lvds_channel == 0) && (setting->disp_id == 0))
+ reg |= LDB_CH0_MODE_EN_TO_DI0;
+ else if ((lvds_channel == 0) && (setting->disp_id == 1))
+ reg |= LDB_CH0_MODE_EN_TO_DI1;
+ else if ((lvds_channel == 1) && (setting->disp_id == 0))
+ reg |= LDB_CH1_MODE_EN_TO_DI0;
+ else
+ reg |= LDB_CH1_MODE_EN_TO_DI1;
+ ch_mask = lvds_channel ? LDB_CH1_MODE_MASK :
+ LDB_CH0_MODE_MASK;
+ ch_val = reg & ch_mask;
+
+ if (bits_per_pixel(setting->if_fmt) == 24) {
+ if (lvds_channel == 0)
+ reg |= LDB_DATA_WIDTH_CH0_24;
+ else
+ reg |= LDB_DATA_WIDTH_CH1_24;
+ } else {
+ if (lvds_channel == 0)
+ reg |= LDB_DATA_WIDTH_CH0_18;
+ else
+ reg |= LDB_DATA_WIDTH_CH1_18;
+ }
+ writel(reg, ldb->control_reg);
+
+ /* clock setting */
+ if (cpu_is_mx6q() || cpu_is_mx6dl())
+ ldb_clk[6] += lvds_channel;
+ else
+ ldb_clk[6] += setting->disp_id;
+ ldb->setting[setting_idx].ldb_di_clk = clk_get(&ldb->pdev->dev,
+ ldb_clk);
+ if (IS_ERR(ldb->setting[setting_idx].ldb_di_clk)) {
+ dev_err(&ldb->pdev->dev, "get ldb clk1 failed\n");
+ return PTR_ERR(ldb->setting[setting_idx].ldb_di_clk);
+ }
+ di_clk[3] += setting->dev_id;
+ di_clk[7] += setting->disp_id;
+ ldb->setting[setting_idx].di_clk = clk_get(&ldb->pdev->dev,
+ di_clk);
+ if (IS_ERR(ldb->setting[setting_idx].di_clk)) {
+ dev_err(&ldb->pdev->dev, "get di clk1 failed\n");
+ return PTR_ERR(ldb->setting[setting_idx].di_clk);
+ }
+
+ i2c_dev = ldb_i2c_client[1];
+
+ dev_dbg(&ldb->pdev->dev, "ldb_clk to di clk: %s -> %s\n", ldb_clk, di_clk);
+ }
+
+ if (i2c_dev)
+ mxc_dispdrv_setdev(ldb->disp_ldb, &i2c_dev->dev);
+ else
+ mxc_dispdrv_setdev(ldb->disp_ldb, NULL);
+
+ ldb->setting[setting_idx].ch_mask = ch_mask;
+ ldb->setting[setting_idx].ch_val = ch_val;
+
+ if (cpu_is_mx6q() || cpu_is_mx6dl())
+ ldb_ipu_ldb_route(setting->dev_id, setting->disp_id, ldb);
+
+ /*
+ * ldb_di0_clk -> ipux_di0_clk
+ * ldb_di1_clk -> ipux_di1_clk
+ */
+ clk_set_parent(ldb->setting[setting_idx].di_clk,
+ ldb->setting[setting_idx].ldb_di_clk);
+
+ /* must use spec video mode defined by driver */
+ ret = fb_find_mode(&setting->fbi->var, setting->fbi, setting->dft_mode_str,
+ ldb_modedb, ldb_modedb_sz, NULL, setting->default_bpp);
+ if (ret != 1)
+ fb_videomode_to_var(&setting->fbi->var, &ldb_modedb[0]);
+
+ INIT_LIST_HEAD(&setting->fbi->modelist);
+ for (i = 0; i < ldb_modedb_sz; i++) {
+ struct fb_videomode m;
+ fb_var_to_videomode(&m, &setting->fbi->var);
+ if (fb_mode_is_equal(&m, &ldb_modedb[i])) {
+ fb_add_videomode(&ldb_modedb[i],
+ &setting->fbi->modelist);
+ break;
+ }
+ }
+
+ /* save current ldb setting for fb notifier */
+ ldb->setting[setting_idx].active = true;
+ ldb->setting[setting_idx].ipu = setting->dev_id;
+ ldb->setting[setting_idx].di = setting->disp_id;
+
+ return ret;
+}
+
+static void ldb_disp_deinit(struct mxc_dispdrv_handle *disp)
+{
+ struct ldb_data *ldb = mxc_dispdrv_getdata(disp);
+ int i;
+
+ writel(0, ldb->control_reg);
+
+ for (i = 0; i < 2; i++) {
+ clk_disable(ldb->setting[i].ldb_di_clk);
+ clk_put(ldb->setting[i].ldb_di_clk);
+ }
+
+ fb_unregister_client(&ldb->nb);
+
+ iounmap(ldb->reg);
+}
+
+static struct mxc_dispdrv_driver ldb_drv = {
+ .name = DISPDRV_LDB,
+ .init = ldb_disp_init,
+ .deinit = ldb_disp_deinit,
+ .setup = ldb_disp_setup,
+};
+
+static int ldb_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct ldb_data *ldb = dev_get_drvdata(&pdev->dev);
+ uint32_t data;
+
+ if (!ldb->inited)
+ return 0;
+ data = readl(ldb->control_reg);
+ ldb->control_reg_data = data;
+ data &= ~(LDB_CH0_MODE_MASK | LDB_CH1_MODE_MASK);
+ writel(data, ldb->control_reg);
+
+ return 0;
+}
+
+static int ldb_resume(struct platform_device *pdev)
+{
+ struct ldb_data *ldb = dev_get_drvdata(&pdev->dev);
+
+ if (!ldb->inited)
+ return 0;
+ writel(ldb->control_reg_data, ldb->control_reg);
+
+ return 0;
+}
+
+static int mxc_ldb_edidread(struct i2c_adapter *adp)
+{
+ int ret = 0;
+ u8 edid[512];
+ unsigned char regaddr = 0;
+ struct i2c_msg msg[2] = {
+ {
+ .addr = 0x50,
+ .flags = 0,
+ .len = 1,
+ .buf = &regaddr,
+ }, {
+ .addr = 0x50,
+ .flags = I2C_M_RD,
+ .len = 512,
+ .buf = edid,
+ },
+ };
+
+ ret = i2c_transfer(adp, msg, ARRAY_SIZE(msg));
+ if (ret != ARRAY_SIZE(msg)) {
+ return -EIO;
+ }
+
+ return ret;
+}
+
+static ssize_t mxc_ldb_show_state(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret;
+ struct i2c_client *client = to_i2c_client(dev);
+
+ ret = mxc_ldb_edidread(client->adapter);
+ if (ret < 0)
+ strcpy(buf, "plugout\n");
+ else
+ strcpy(buf, "plugin\n");
+
+ return strlen(buf);
+}
+
+static DEVICE_ATTR(cable_state, S_IRUGO, mxc_ldb_show_state, NULL);
+
+static int __devinit mxc_ldb_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ int ldb_id = (int)client->dev.platform_data;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE | I2C_FUNC_I2C))
+ return -ENODEV;
+
+ ldb_i2c_client[ldb_id] = client;
+
+ ret = device_create_file(&client->dev, &dev_attr_cable_state);
+ if (ret < 0)
+ dev_warn(&client->dev,
+ "cound not create sys node for cable state\n");
+
+
+ return 0;
+}
+
+static int __devexit mxc_ldb_i2c_remove(struct i2c_client *client)
+{
+ int ldb_id = (int)client->dev.platform_data;
+ ldb_i2c_client[ldb_id] = NULL;
+ return 0;
+}
+
+static const struct i2c_device_id mxc_ldb_i2c_id[] = {
+ { "mxc_ldb_i2c", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, mxc_ldb_i2c_id);
+
+static struct i2c_driver mxc_ldb_i2c_driver = {
+ .driver = {
+ .name = "mxc_ldb_i2c",
+ },
+ .probe = mxc_ldb_i2c_probe,
+ .remove = mxc_ldb_i2c_remove,
+ .id_table = mxc_ldb_i2c_id,
+};
+
+static int __init mxc_ldb_i2c_init(void)
+{
+ return i2c_add_driver(&mxc_ldb_i2c_driver);
+}
+
+static void __exit mxc_ldb_i2c_exit(void)
+{
+ i2c_del_driver(&mxc_ldb_i2c_driver);
+}
+
+module_init(mxc_ldb_i2c_init);
+module_exit(mxc_ldb_i2c_exit);
+
+/*!
+ * This function is called by the driver framework to initialize the LDB
+ * device.
+ *
+ * @param dev The device structure for the LDB passed in by the
+ * driver framework.
+ *
+ * @return Returns 0 on success or negative error code on error
+ */
+static int ldb_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct ldb_data *ldb;
+
+ ldb = kzalloc(sizeof(struct ldb_data), GFP_KERNEL);
+ if (!ldb) {
+ ret = -ENOMEM;
+ goto alloc_failed;
+ }
+
+ ldb->pdev = pdev;
+ ldb->disp_ldb = mxc_dispdrv_register(&ldb_drv);
+ mxc_dispdrv_setdata(ldb->disp_ldb, ldb);
+
+ dev_set_drvdata(&pdev->dev, ldb);
+
+alloc_failed:
+ return ret;
+}
+
+static int ldb_remove(struct platform_device *pdev)
+{
+ struct ldb_data *ldb = dev_get_drvdata(&pdev->dev);
+
+ if (!ldb->inited)
+ return 0;
+ mxc_dispdrv_puthandle(ldb->disp_ldb);
+ mxc_dispdrv_unregister(ldb->disp_ldb);
+ kfree(ldb);
+ return 0;
+}
+
+static struct platform_driver mxcldb_driver = {
+ .driver = {
+ .name = "mxc_ldb",
+ },
+ .probe = ldb_probe,
+ .remove = ldb_remove,
+ .suspend = ldb_suspend,
+ .resume = ldb_resume,
+};
+
+static int __init ldb_init(void)
+{
+ return platform_driver_register(&mxcldb_driver);
+}
+
+static void __exit ldb_uninit(void)
+{
+ platform_driver_unregister(&mxcldb_driver);
+}
+
+module_init(ldb_init);
+module_exit(ldb_uninit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC LDB driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/mxc/lib.a_shipped b/drivers/video/mxc/lib.a_shipped
new file mode 120000
index 00000000..5c52a946
--- /dev/null
+++ b/drivers/video/mxc/lib.a_shipped
@@ -0,0 +1 @@
+lib.a_shipped-imx6sl-aa \ No newline at end of file
diff --git a/drivers/video/mxc/lib.a_shipped-imx6sl-aa b/drivers/video/mxc/lib.a_shipped-imx6sl-aa
new file mode 100755
index 00000000..ef1b1544
--- /dev/null
+++ b/drivers/video/mxc/lib.a_shipped-imx6sl-aa
Binary files differ
diff --git a/drivers/video/mxc/lib.a_shipped-imx6sl-noaa b/drivers/video/mxc/lib.a_shipped-imx6sl-noaa
new file mode 100755
index 00000000..674ba155
--- /dev/null
+++ b/drivers/video/mxc/lib.a_shipped-imx6sl-noaa
Binary files differ
diff --git a/drivers/video/mxc/lk_fp9928.c b/drivers/video/mxc/lk_fp9928.c
new file mode 100644
index 00000000..b259a128
--- /dev/null
+++ b/drivers/video/mxc/lk_fp9928.c
@@ -0,0 +1,1070 @@
+
+
+/*
+ *
+ * Purpose : FP9928 driver
+ * Author : Gallen Lin
+ * versions :
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/semaphore.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+
+
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+
+#include <mach/iomux-mx6sl.h>
+
+
+#include "ntx_hwconfig.h"
+#include "fake_s1d13522.h"
+
+#define GDEBUG 0
+#include <linux/gallen_dbg.h>
+
+#include "lk_fp9928.h"
+
+
+#define DRIVER_NAME "FP9928"
+
+#define INIT_POWER_STATE 0
+
+#define GPIO_FP9928_VIN_PADCFG MX6SL_PAD_EPDC_PWRWAKEUP__GPIO_2_14
+#define GPIO_FP9928_VIN IMX_GPIO_NR(2,14)
+
+#if 1
+//
+#define GPIO_FP9928_EN_PADCFG MX6SL_PAD_EPDC_PWRCTRL1__GPIO_2_8
+#define GPIO_FP9928_EN IMX_GPIO_NR(2,8)
+#define GPIO_FP9928_VCOM_PADCFG MX6SL_PAD_EPDC_VCOM0__GPIO_2_3
+#define GPIO_FP9928_VCOM IMX_GPIO_NR(2,3)
+#define GPIO_FP9928_EP_3V3_IN IMX_GPIO_NR(4,3) // EPDC_PWRWAKEUP
+#define GPIO_FP9928_EP_3V3_IN_PADCFG MX6SL_PAD_KEY_ROW5__GPIO_4_3
+#else
+// beta version .
+#define GPIO_FP9928_EN_PADCFG MX6SL_PAD_EPDC_VCOM0__GPIO_2_3
+#define GPIO_FP9928_EN IMX_GPIO_NR(2,3)
+#define GPIO_FP9928_VCOM_PADCFG MX6SL_PAD_EPDC_PWRCTRL1__GPIO_2_8
+#define GPIO_FP9928_VCOM IMX_GPIO_NR(2,8)
+#endif
+
+#define VIN_ON 1
+#define VIN_OFF 0
+#define EN_ON 1
+#define EN_OFF 0
+#define VCOM_ON 1
+#define VCOM_OFF 0
+
+
+#define FP9928_EP3V3OFF_TICKS_MAX 350
+#define FP9928_POWEROFF_TICKS_MAX 0 //
+#define FP9928_POWERON_WAIT_TICKS 2 //等待FP9928 POWERON->READY的時間.
+#define FP9928_PWROFFDELAYWORK_TICKS 50
+
+
+#if 0
+#define FP9928_VCOM_MV_MAX (-302)
+#define FP9928_VCOM_MV_MIN (-2501)
+//#define FP9928_VCOM_MV_STEP (11)
+#define FP9928_VCOM_UV_STEP (11000)
+#else
+#define FP9928_VCOM_MV_MAX (-302)
+#define FP9928_VCOM_MV_MIN (-6000)
+//#define FP9928_VCOM_MV_STEP (22)
+#define FP9928_VCOM_UV_STEP (21569)
+#endif
+
+#define FP9928_WAIT_TICKSTAMP(_TickEnd,_wait_item) \
+{\
+ unsigned long dwTickNow=jiffies,dwTicks;\
+ if ( time_before(dwTickNow,_TickEnd) ) {\
+ dwTicks = _TickEnd-dwTickNow;\
+ DBG0_MSG("%s() waiting to %ld ticks for %s ... ",__FUNCTION__,dwTicks,_wait_item);\
+ if(in_interrupt()) {\
+ mdelay(jiffies_to_msecs(dwTicks));\
+ DBG0_MSG("done(@INT)\n");\
+ }\
+ else {\
+ msleep(jiffies_to_msecs(dwTicks));\
+ DBG0_MSG("done\n");\
+ }\
+ }\
+}
+
+
+typedef struct tagFP9928_PWRDWN_WORK_PARAM {
+ struct delayed_work pwrdwn_work;
+ int iIsTurnOffChipPwr;
+} FP9928_PWRDWN_WORK_PARAM;
+
+
+
+typedef struct tagFP9228_data {
+ int iCurrent_temprature;
+ unsigned short wTempratureData,wReserved;
+ struct i2c_adapter *ptI2C_adapter;
+ struct i2c_client *ptI2C_client;
+ struct mutex tI2CLock;
+ struct mutex tPwrLock;
+ int iIsPoweredON;
+ int iIsOutputEnabled;
+ int iIsOutputPowerDownCounting;
+ int iIsVCOMNeedReInit;
+ int iCurrent_VCOM_mV;
+ unsigned long dwTickPowerOffEnd;
+ unsigned long dwTickPowerOnEnd;
+ unsigned long dwTickEP3V3OffEnd;
+ FP9928_PWRDWN_WORK_PARAM tPwrdwn_work_param;
+ int iIsEP3V3_SW_enabled;
+} FP9928_data;
+
+
+static FP9928_data *gptFP9928_data ;
+
+// externals ...
+extern volatile NTX_HWCONFIG *gptHWCFG;
+extern volatile int gSleep_Mode_Suspend;
+
+
+static struct i2c_board_info gtFP9928_BI = {
+ .type = "FP9928",
+ .addr = 0x48,
+ .platform_data = NULL,
+};
+
+static const unsigned short gwFP9928_AddrA[] = {
+ 0x48,
+ I2C_CLIENT_END
+};
+
+
+static volatile unsigned char gbFP9928_REG_TMST_addr=0x00;
+static volatile unsigned char gbFP9928_REG_TMST=0;
+
+#define FP9928_REG_FUNC_ADJUST_VCOM_ADJ 0x01
+#define FP9928_REG_FUNC_ADJUST_FIX_RD_PTR 0x02
+static volatile unsigned char gbFP9928_REG_FUNC_ADJUST_addr=0x01;
+static volatile unsigned char gbFP9928_REG_FUNC_ADJUST=0x01;
+
+#define FP9928_REG_VCOM_SETTING_ALL 0xff
+static volatile unsigned char gbFP9928_REG_VCOM_SETTING_addr=0x02;
+static volatile unsigned char gbFP9928_REG_VCOM_SETTING=0x74;
+
+
+static int _fp9928_set_reg(unsigned char bRegAddr,unsigned char bRegSetVal)
+{
+ int iRet=FP9928_RET_SUCCESS;
+ int iChk;
+ unsigned char bA[2] ;
+ int iIn_Interrupt = in_interrupt();
+ unsigned long dwTickNow = jiffies,dwTicks;
+
+ ASSERT(gptFP9928_data);
+
+ if(!iIn_Interrupt) {
+ mutex_lock(&gptFP9928_data->tI2CLock);
+ }
+
+ FP9928_WAIT_TICKSTAMP(gptFP9928_data->dwTickPowerOnEnd,"power on stable");
+
+ bA[0]=bRegAddr;
+ bA[1]=bRegSetVal;
+ iChk = i2c_master_send(gptFP9928_data->ptI2C_client, (const char *)bA, sizeof(bA));
+ if (iChk < 0) {
+ ERR_MSG("%s(%d):%d=%s(),regAddr=0x%x,regVal=0x%x fail !\n",__FILE__,__LINE__,\
+ iChk,"i2c_master_send",bRegAddr,bRegSetVal);
+ iRet=FP9928_RET_I2CTRANS_ERR;
+ }
+
+ if(!iIn_Interrupt) {
+ mutex_unlock(&gptFP9928_data->tI2CLock);
+ }
+
+ return iRet;
+}
+
+static int _fp9928_get_reg(unsigned char bRegAddr,unsigned char *O_pbRegVal)
+{
+ int iRet=FP9928_RET_SUCCESS;
+ int iChk;
+ unsigned char bA[1] ;
+ int iIn_Interrupt = in_interrupt();
+ unsigned long dwTickNow = jiffies,dwTicks;
+
+ ASSERT(gptFP9928_data);
+
+ if(!iIn_Interrupt) {
+ mutex_lock(&gptFP9928_data->tI2CLock);
+ }
+
+ FP9928_WAIT_TICKSTAMP(gptFP9928_data->dwTickPowerOnEnd,"power on stable");
+
+ bA[0]=bRegAddr;
+ iChk = i2c_master_send(gptFP9928_data->ptI2C_client, (const char *)bA, 1);
+ if (iChk < 0) {
+ ERR_MSG("%s(%d):%s i2c_master_send fail !\n",__FILE__,__LINE__,__FUNCTION__);
+ iRet = FP9928_RET_I2CTRANS_ERR;
+ }
+
+
+ iChk = i2c_master_recv(gptFP9928_data->ptI2C_client, bA, 1);
+ if (iChk < 0) {
+ ERR_MSG("%s(%d):%s i2c_master_recv fail !\n",__FILE__,__LINE__,__FUNCTION__);
+ iRet = FP9928_RET_I2CTRANS_ERR;
+ }
+
+
+ if(iRet>=0) {
+ *O_pbRegVal = bA[0];
+ }
+
+
+ //DBG_MSG("%s(0x%x,%p)==>0x%x\n",__FUNCTION__,bRegAddr,O_pbRegVal,bA[0]);
+
+ if(!iIn_Interrupt) {
+ mutex_unlock(&gptFP9928_data->tI2CLock);
+ }
+
+ return iRet;
+}
+
+
+#define FP9928_REG_SET(_regName,_bFieldName,_bSetVal) \
+({\
+ int _iRet=FP9928_RET_SUCCESS;\
+ int _iChk;\
+ unsigned char _bNewReg,_bFieldMask;\
+ \
+ _bFieldMask=(unsigned char)FP9928_REG_##_regName##_##_bFieldName;\
+ if(0xff==_bFieldMask) {\
+ _bNewReg = _bSetVal;\
+ }\
+ else {\
+ _bNewReg=gbFP9928_REG_##_regName;\
+ if(_bSetVal) {\
+ _bNewReg |= _bFieldMask ;\
+ }\
+ else {\
+ _bNewReg &= ~_bFieldMask;\
+ }\
+ }\
+ \
+ _iChk = _fp9928_set_reg(gbFP9928_REG_##_regName##_##addr,_bNewReg);\
+ if(_iChk<0) {\
+ _iRet = _iChk;\
+ }\
+ else {\
+ DBG_MSG("%s() : FP9928 write reg%s(%02Xh) 0x%02x->0x%02x\n",__FUNCTION__,\
+ #_regName,gbFP9928_REG_##_regName##_##addr,gbFP9928_REG_##_regName,_bNewReg);\
+ gbFP9928_REG_##_regName = _bNewReg;\
+ }\
+ _iRet;\
+})
+
+#define FP9928_REG_GET(_regName) \
+({\
+ int _iChk;\
+ unsigned char bReadReg=0;\
+ unsigned short _wRet=0;\
+ \
+ _iChk = _fp9928_get_reg(gbFP9928_REG_##_regName##_##addr,&bReadReg);\
+ if(_iChk<0) {\
+ _wRet = (unsigned short)(-1);\
+ }\
+ else {\
+ _wRet = bReadReg;\
+ gbFP9928_REG_##_regName = bReadReg;\
+ DBG_MSG("%s() : FP9928 read reg%s(%02Xh)=0x%02x\n",__FUNCTION__,\
+ #_regName,gbFP9928_REG_##_regName##_##addr,bReadReg);\
+ }\
+ _wRet;\
+})
+
+#define FP9928_REG(_regName) gbFP9928_REG_##_regName
+
+
+
+
+
+
+static int _fp9928_gpio_init(void)
+{
+ int iRet = FP9928_RET_SUCCESS;
+ int iVINState;
+
+ GALLEN_DBGLOCAL_BEGIN();
+
+ mxc_iomux_v3_setup_pad(GPIO_FP9928_VIN_PADCFG);
+ if(0!=gpio_request(GPIO_FP9928_VIN, "fp9928_VIN")) {
+ WARNING_MSG("%s(),request gpio fp9928_VIN fail !!\n",__FUNCTION__);
+ //gpio_direction_input(GPIO_FP9928_VIN);
+ }
+
+ iVINState = gpio_get_value(GPIO_FP9928_VIN);
+ //printk("%s():FP9928 VIN=%d\n",__FUNCTION__,iVINState);
+ gptFP9928_data->iIsPoweredON=(VIN_ON==iVINState)?1:0;
+
+ mxc_iomux_v3_setup_pad(GPIO_FP9928_EN_PADCFG);
+ if(0!=gpio_request(GPIO_FP9928_EN, "fp9928_EN")) {
+ WARNING_MSG("%s(),request gpio fp9928_EN fail !!\n",__FUNCTION__);
+ //gpio_direction_input(GPIO_FP9928_EN);
+ }
+ gpio_direction_output(GPIO_FP9928_EN,EN_OFF);
+ gptFP9928_data->iIsOutputEnabled = 0;
+
+
+ mxc_iomux_v3_setup_pad(GPIO_FP9928_VCOM_PADCFG);
+ gpio_request(GPIO_FP9928_VCOM, "fp9928_VCOM");
+ if(0!=gpio_request(GPIO_FP9928_VCOM, "fp9928_VCOM")) {
+ WARNING_MSG("%s(),request gpio fp9928_VCOM fail !!\n",__FUNCTION__);
+ //gpio_direction_input(GPIO_FP9928_VCOM);
+ }
+ gpio_direction_output(GPIO_FP9928_VCOM,0);
+
+#if defined(GPIO_FP9928_EP_3V3_IN) && (GPIO_FP9928_EP_3V3_IN!=GPIO_FP9928_VIN) //[
+ if(gptFP9928_data->iIsEP3V3_SW_enabled) {
+ // E60Q3X revA .
+ mxc_iomux_v3_setup_pad(GPIO_FP9928_EP_3V3_IN_PADCFG);
+ gpio_request(GPIO_FP9928_EP_3V3_IN, "fp9928_EP_3V3");
+ if(0!=gpio_request(GPIO_FP9928_EP_3V3_IN, "fp9928_EP_3V3")) {
+ WARNING_MSG("%s(),request gpio fp9928_EP_3V3_IN fail !!\n",__FUNCTION__);
+ }
+ gpio_direction_output(GPIO_FP9928_EP_3V3_IN,0);
+ }
+#endif //]GPIO_FP9928_EP_3V3_IN_PADCFG
+
+ GALLEN_DBGLOCAL_END();
+
+ return iRet;
+}
+
+static void _fp9928_gpio_release(void)
+{
+ GALLEN_DBGLOCAL_BEGIN();
+ if(!gptFP9928_data) {
+ WARNING_MSG("%s(%d) : %s cannot work before init !\n",__FILE__,__LINE__,__FUNCTION__);
+ GALLEN_DBGLOCAL_ESC();
+ return ;
+ }
+ gpio_free(GPIO_FP9928_VCOM);
+ gpio_free(GPIO_FP9928_EN);
+ gpio_free(GPIO_FP9928_VIN);
+
+#if defined(GPIO_FP9928_EP_3V3_IN) && (GPIO_FP9928_EP_3V3_IN!=GPIO_FP9928_VIN) //[
+ if(gptFP9928_data->iIsEP3V3_SW_enabled) {
+ gpio_free(GPIO_FP9928_EP_3V3_IN);
+ }
+#endif //]GPIO_FP9928_EP_3V3_IN_PADCFG
+ GALLEN_DBGLOCAL_END();
+}
+
+
+static int _fp9928_reg_init(void)
+{
+ //FP9928_REG_SET(FUNC_ADJUST,FIX_RD_PTR,1);
+ //FP9928_REG_GET(TMST);
+ //FP9928_REG_GET(TMST);
+ return 0;
+}
+
+static void _fp9928_reinit_vcom(void)
+{
+ ASSERT(gptFP9928_data);
+ if(gptFP9928_data->iIsVCOMNeedReInit) {
+ int iChk;
+ DBG_MSG("%s():re-write VCOM to 0x%02X\n",__FUNCTION__,FP9928_REG(VCOM_SETTING));
+ iChk = FP9928_REG_SET(VCOM_SETTING,ALL,FP9928_REG(VCOM_SETTING));
+ if(iChk>=0) {
+ gptFP9928_data->iIsVCOMNeedReInit = 0;
+ }
+ }
+}
+
+static int _fp9928_output_en(int iIsEnable)
+{
+ int iRet = FP9928_RET_SUCCESS;
+
+ DBG_MSG("%s(%d)\n",__FUNCTION__,iIsEnable);
+
+ if(!gptFP9928_data) {
+ WARNING_MSG("%s(%d) : %s cannot work before init !\n",__FILE__,__LINE__,__FUNCTION__);
+ return FP9928_RET_NOTINITEDSTATE;
+ }
+
+ if(gptFP9928_data->iIsOutputEnabled == iIsEnable) {
+ // nothing have to do when state not change .
+ if(iIsEnable) {
+ DBG_MSG("%s() : output power already enabled\n",__FUNCTION__);
+ gpio_direction_output(GPIO_FP9928_VCOM,VCOM_ON);
+ }
+ }
+ else {
+ //unsigned long dwTickNow = jiffies,dwTicks ;
+ if(iIsEnable) {
+ //FP9928_WAIT_TICKSTAMP(gptFP9928_data->dwTickPowerOffEnd,"pwroff stable");
+ gpio_direction_output(GPIO_FP9928_EN,EN_ON);
+ gptFP9928_data->iIsOutputEnabled = 1;
+
+ msleep(10);
+ _fp9928_reinit_vcom();
+ msleep(13);
+
+ gpio_direction_output(GPIO_FP9928_VCOM,VCOM_ON);
+ msleep(10);
+ }
+ else {
+
+ gpio_direction_output(GPIO_FP9928_EN,EN_OFF);
+ gptFP9928_data->iIsOutputEnabled = 0;
+ gptFP9928_data->dwTickPowerOffEnd = jiffies + FP9928_POWEROFF_TICKS_MAX;
+ }
+ }
+
+ return iRet ;
+}
+
+
+
+static int _fp9928_vin_onoff(int iIsON)
+{
+ int iRet = FP9928_RET_SUCCESS;
+
+ ASSERT(gptFP9928_data);
+
+ DBG_MSG("%s(%d)\n",__FUNCTION__,iIsON);
+
+ if(iIsON==gptFP9928_data->iIsPoweredON) {
+ }
+ else {
+ if(iIsON) {
+ gpio_direction_output(GPIO_FP9928_VIN,VIN_ON);
+ gptFP9928_data->iIsPoweredON = 1;
+ //msleep(10);
+ gptFP9928_data->dwTickPowerOnEnd = jiffies + FP9928_POWERON_WAIT_TICKS;
+ }
+ else {
+ _fp9928_output_en(0);
+ FP9928_WAIT_TICKSTAMP(gptFP9928_data->dwTickPowerOffEnd,"pwroff stable");
+ gpio_direction_output(GPIO_FP9928_VIN,VIN_OFF);
+ gptFP9928_data->iIsPoweredON = 0;
+ gptFP9928_data->iIsVCOMNeedReInit = 1;
+ if(3==gptHWCFG->m_val.bUIConfig) {
+ // MP/RD mode .
+ gptFP9928_data->dwTickEP3V3OffEnd = jiffies + 0;
+ }
+ else {
+ gptFP9928_data->dwTickEP3V3OffEnd = jiffies + FP9928_EP3V3OFF_TICKS_MAX;
+ }
+ }
+ }
+
+ return iRet;
+}
+
+
+static void _fp9928_pwrdwn_work_func(struct work_struct *work)
+{
+ GALLEN_DBGLOCAL_BEGIN();
+
+ mutex_lock(&gptFP9928_data->tPwrLock);
+
+ if(!gptFP9928_data->iIsOutputPowerDownCounting) {
+ WARNING_MSG("[WARNING]%s(%d): race condition occured !\n",__FILE__,__LINE__);
+ return ;
+ }
+
+ _fp9928_output_en(0);
+ gptFP9928_data->iIsOutputPowerDownCounting = 0;
+
+ if(gptFP9928_data->tPwrdwn_work_param.iIsTurnOffChipPwr) {
+ _fp9928_vin_onoff(0);
+ }
+
+ mutex_unlock(&gptFP9928_data->tPwrLock);
+
+ GALLEN_DBGLOCAL_END();
+}
+
+
+/**********************************************************************
+ *
+ * public functions .
+ *
+***********************************************************************/
+
+int fp9928_power_onoff(int iIsPowerOn,int iIsOutputPwr)
+{
+ int iRet = FP9928_RET_SUCCESS;
+
+ GALLEN_DBGLOCAL_BEGIN();
+ if(!gptFP9928_data) {
+ WARNING_MSG("%s(%d) : %s cannot work before init !\n",__FILE__,__LINE__,__FUNCTION__);
+ GALLEN_DBGLOCAL_ESC();
+ return FP9928_RET_NOTINITEDSTATE;
+ }
+
+
+
+ mutex_lock(&gptFP9928_data->tPwrLock);
+ if (iIsPowerOn) {
+ _fp9928_vin_onoff(1);
+
+ if(iIsOutputPwr==1) {
+ gptFP9928_data->iIsOutputPowerDownCounting = 0;
+ cancel_delayed_work_sync(&gptFP9928_data->tPwrdwn_work_param.pwrdwn_work);
+ _fp9928_output_en(1);
+ }
+ }
+ else {
+ gptFP9928_data->tPwrdwn_work_param.iIsTurnOffChipPwr = 1;
+ if(!gptFP9928_data->iIsOutputPowerDownCounting)
+ {
+ gptFP9928_data->iIsOutputPowerDownCounting = 1;
+ cancel_delayed_work_sync(&gptFP9928_data->tPwrdwn_work_param.pwrdwn_work);
+ schedule_delayed_work(&gptFP9928_data->tPwrdwn_work_param.pwrdwn_work, \
+ FP9928_PWROFFDELAYWORK_TICKS);
+ }
+ }
+ mutex_unlock(&gptFP9928_data->tPwrLock);
+
+ GALLEN_DBGLOCAL_BEGIN();
+ return iRet;
+}
+
+int fp9928_output_power(int iIsOutputPwr,int iIsChipPowerDown)
+{
+ int iRet=FP9928_RET_SUCCESS;
+
+ GALLEN_DBGLOCAL_BEGIN();
+
+ if(!gptFP9928_data) {
+ WARNING_MSG("%s(%d) : %s cannot work before init !\n",__FILE__,__LINE__,__FUNCTION__);
+ GALLEN_DBGLOCAL_ESC();
+ return FP9928_RET_NOTINITEDSTATE;
+ }
+
+
+ mutex_lock(&gptFP9928_data->tPwrLock);
+ if(iIsOutputPwr) {
+ GALLEN_DBGLOCAL_RUNLOG(0);
+ gptFP9928_data->iIsOutputPowerDownCounting = 0;
+ cancel_delayed_work_sync(&gptFP9928_data->tPwrdwn_work_param.pwrdwn_work);
+
+ if(!gptFP9928_data->iIsPoweredON) {
+ GALLEN_DBGLOCAL_RUNLOG(1);
+ // auto power on chip .
+ _fp9928_vin_onoff(1);
+ }
+
+ iRet = _fp9928_output_en(1);
+ }
+ else {
+ GALLEN_DBGLOCAL_RUNLOG(2);
+ if(!gptFP9928_data->iIsOutputPowerDownCounting) {
+ GALLEN_DBGLOCAL_RUNLOG(3);
+
+ udelay(100);gpio_direction_output(GPIO_FP9928_VCOM,VCOM_OFF);
+
+ gptFP9928_data->tPwrdwn_work_param.iIsTurnOffChipPwr = iIsChipPowerDown;
+ gptFP9928_data->iIsOutputPowerDownCounting = 1;
+ cancel_delayed_work_sync(&gptFP9928_data->tPwrdwn_work_param.pwrdwn_work);
+ schedule_delayed_work(&gptFP9928_data->tPwrdwn_work_param.pwrdwn_work, \
+ FP9928_PWROFFDELAYWORK_TICKS);
+ }
+ else {
+ GALLEN_DBGLOCAL_RUNLOG(4);
+ DBG_MSG("%s(%d),power down work already exist \n",__FUNCTION__,__LINE__);
+ }
+ }
+ mutex_unlock(&gptFP9928_data->tPwrLock);
+
+ GALLEN_DBGLOCAL_END();
+ return iRet;
+}
+
+#define FP9928_SUSPEND_ENABLED 1
+
+int fp9928_suspend(void)
+{
+#ifdef FP9928_SUSPEND_ENABLED //[
+ int iRet = FP9928_RET_SUCCESS;
+ //int iChk;
+
+ if(!gptFP9928_data) {
+ WARNING_MSG("%s(%d) : %s cannot work before init !\n",
+ __FILE__,__LINE__,__FUNCTION__);
+ return FP9928_RET_NOTINITEDSTATE;
+ }
+
+ if(gptFP9928_data->iIsOutputEnabled) {
+ WARNING_MSG("%s() : skip suspend when PMIC output enabled !! (%d)\n",__FUNCTION__,
+ delayed_work_pending(&gptFP9928_data->tPwrdwn_work_param.pwrdwn_work));
+ return FP9928_RET_PWRDWNWORKPENDING;
+ }
+
+ if(gSleep_Mode_Suspend) {
+ mutex_lock(&gptFP9928_data->tPwrLock);
+ _fp9928_vin_onoff(0);
+ mutex_unlock(&gptFP9928_data->tPwrLock);
+
+#if 0
+ //FP9928_WAIT_TICKSTAMP(gptFP9928_data->dwTickEP3V3OffEnd,"pwroff->EP3V3 off stable");
+#else
+ if(time_before(jiffies,gptFP9928_data->dwTickEP3V3OffEnd)) {
+ return FP9928_RET_PWRDWNWORKPENDING;
+ }
+ else {
+ #if defined(GPIO_FP9928_EP_3V3_IN) && (GPIO_FP9928_EP_3V3_IN!=GPIO_FP9928_VIN) //[
+ gpio_direction_output(GPIO_FP9928_EP_3V3_IN,0);
+ #endif //] defined(GPIO_FP9928_EP_3V3_IN) && (GPIO_FP9928_EP_3V3_IN!=GPIO_FP9928_VIN)
+ }
+#endif
+ }
+
+ return iRet;
+#else //][! FP9928_SUSPEND_ENABLED
+ printk("%s() skipped !\n",__FUNCTION__);
+ return 0;
+#endif //] FP9928_SUSPEND_ENABLED
+}
+
+#define AVOID_ANIMATION_LOOP
+
+void fp9928_shutdown(void)
+{
+ if(!gptFP9928_data) {
+ WARNING_MSG("%s(%d) : %s cannot work before init !\n",
+ __FILE__,__LINE__,__FUNCTION__);
+ return ;
+ }
+ if(delayed_work_pending(&gptFP9928_data->tPwrdwn_work_param.pwrdwn_work)) {
+#ifdef AVOID_ANIMATION_LOOP //[
+ cancel_delayed_work_sync(&gptFP9928_data->tPwrdwn_work_param.pwrdwn_work);
+#else //][!AVOID_ANIMATION_LOOP
+ fp9928_power_onoff(0,0);
+#endif //] AVOID_ANIMATION_LOOP
+ }
+#ifdef AVOID_ANIMATION_LOOP //[
+ //DBG0_ENTRY_TAG();
+ mutex_lock(&gptFP9928_data->tPwrLock);
+
+ msleep(jiffies_to_msecs(FP9928_PWROFFDELAYWORK_TICKS));
+ //DBG0_ENTRY_TAG();
+ _fp9928_output_en(0);
+ //DBG0_ENTRY_TAG();
+ _fp9928_vin_onoff(0);
+ //DBG0_ENTRY_TAG();
+
+#else //][!AVOID_ANIMATION_LOOP
+
+ while (1) {
+ if(gptFP9928_data->iIsOutputEnabled) {
+ DBG0_MSG("%s() : waiting for PMIC output disabled !! (%d)\n",__FUNCTION__,
+ delayed_work_pending(&gptFP9928_data->tPwrdwn_work_param.pwrdwn_work));
+ msleep(100);
+ }
+ else {
+ break;
+ }
+ }// while end .
+
+#endif //] AVOID_ANIMATION_LOOP
+
+ while (1) {
+ if(time_before(jiffies,gptFP9928_data->dwTickEP3V3OffEnd)) {
+ DBG0_MSG("%s() : waiting for VEE stable to power off the EP3V3 ...\n",__FUNCTION__);
+ msleep(100);
+ }
+ else {
+ #if defined(GPIO_FP9928_EP_3V3_IN) && (GPIO_FP9928_EP_3V3_IN!=GPIO_FP9928_VIN) //[
+ gpio_direction_output(GPIO_FP9928_EP_3V3_IN,0);
+ #endif //] defined(GPIO_FP9928_EP_3V3_IN) && (GPIO_FP9928_EP_3V3_IN!=GPIO_FP9928_VIN)
+ break;
+ }
+ }// while end .
+
+#ifdef AVOID_ANIMATION_LOOP//[
+ mutex_unlock(&gptFP9928_data->tPwrLock);
+#endif //]AVOID_ANIMATION_LOOP
+
+}
+
+void fp9928_resume(void)
+{
+#ifdef FP9928_SUSPEND_ENABLED//[
+ if(!gptFP9928_data) {
+ WARNING_MSG("%s(%d) : %s cannot work before init !\n",__FILE__,__LINE__,__FUNCTION__);
+ return ;
+ }
+
+ if(gSleep_Mode_Suspend) {
+ mutex_lock(&gptFP9928_data->tPwrLock);
+ _fp9928_vin_onoff(1);
+ mutex_unlock(&gptFP9928_data->tPwrLock);
+
+ }
+#else
+ printk("%s() skipped !\n",__FUNCTION__);
+#endif //] FP9928_SUSPEND_ENABLED
+}
+
+int fp9928_ONOFF(int iIsON)
+{
+ int iRet=FP9928_RET_SUCCESS;
+
+ if(!(8==gptHWCFG->m_val.bDisplayCtrl)) {
+ WARNING_MSG("%s() display controller (%d) not match !\n",
+ __FUNCTION__,(int)gptHWCFG->m_val.bDisplayCtrl);
+ return 1;
+ }
+
+
+
+ mutex_lock(&gptFP9928_data->tPwrLock);
+ if(iIsON) {
+#if defined(GPIO_FP9928_EP_3V3_IN) && (GPIO_FP9928_EP_3V3_IN!=GPIO_FP9928_VIN) //[
+ if(gptFP9928_data->iIsEP3V3_SW_enabled) {
+ DBG_MSG("%s() : Trun ON EP_3V3\n",__FUNCTION__);
+ gpio_direction_output(GPIO_FP9928_EP_3V3_IN,1);
+ }
+#endif //]
+
+ _fp9928_vin_onoff(1);
+ }
+ else {
+ _fp9928_vin_onoff(0);
+
+#if defined(GPIO_FP9928_EP_3V3_IN) && (GPIO_FP9928_EP_3V3_IN!=GPIO_FP9928_VIN) //[
+ /*
+ if(gptFP9928_data->iIsEP3V3_SW_enabled)
+ {
+ DBG_MSG("%s() : Trun OFF EP_3V3\n",__FUNCTION__);
+ FP9928_WAIT_TICKSTAMP(gptFP9928_data->dwTickEP3V3OffEnd,
+ "pwroff->EP3V3 off stable");
+ gpio_direction_output(GPIO_FP9928_EP_3V3_IN,0);
+ }
+ */
+#endif //]
+ }
+ mutex_unlock(&gptFP9928_data->tPwrLock);
+
+ return iRet;
+}
+
+
+int fp9928_get_temperature(int *O_piTemperature)
+{
+ unsigned short wReg;
+ int iRet=FP9928_RET_SUCCESS;
+
+ int iTemp;
+ unsigned char bReg,bTemp;
+ int iOldPowerState;
+
+ //return FP9928_RET_SUCCESS;
+
+ if(!gptFP9928_data) {
+ WARNING_MSG("%s() cannot work if driver is not initialed \n",__FUNCTION__);
+ return FP9928_RET_NOTINITEDSTATE;
+ }
+
+ iOldPowerState = gptFP9928_data->iIsPoweredON;
+ fp9928_output_power(1,0);
+
+ wReg = FP9928_REG_GET(TMST);
+
+ fp9928_ONOFF(iOldPowerState);
+
+ if(((unsigned short)(-1))==wReg) {
+ ERR_MSG("%s(%d):%s regTMST read fail !\n",__FILE__,__LINE__,__FUNCTION__);
+ return FP9928_RET_I2CTRANS_ERR;
+ }
+
+ bReg = (unsigned char)wReg;
+ gptFP9928_data->wTempratureData = wReg;
+ if(bReg&0x80) {
+ // negative .
+ bTemp=(~bReg)+1;
+ iTemp = bTemp;
+ iTemp = (~iTemp)+1;
+ }
+ else {
+ // positive .
+ iTemp = (int)(bReg);
+ }
+ gptFP9928_data->iCurrent_temprature = iTemp;
+ printk("%s temprature data = 0x%x,%d\n",DRIVER_NAME,wReg,gptFP9928_data->iCurrent_temprature);
+
+ if(O_piTemperature) {
+ *O_piTemperature = gptFP9928_data->iCurrent_temprature;
+ }
+
+ return iRet;
+}
+
+
+
+
+
+int fp9928_vcom_set(int iVCOM_mV,int iIsWriteToFlash)
+{
+
+ const int iVCOM_mV_max=FP9928_VCOM_MV_MAX,iVCOM_mV_min=FP9928_VCOM_MV_MIN;
+ int iVCOM_mV_ABS ;
+ int iRet=FP9928_RET_SUCCESS;
+
+ if(!gptFP9928_data) {
+ WARNING_MSG("%s() cannot work if driver is not initialed \n",__FUNCTION__);
+ return FP9928_RET_NOTINITEDSTATE;
+ }
+
+ if(iVCOM_mV<iVCOM_mV_min) {
+ ERR_MSG("%s(%d),VCOM %d cannot < %d mV\n",
+ __FUNCTION__,__LINE__,iVCOM_mV,iVCOM_mV_min);
+ }
+ else if(iVCOM_mV>iVCOM_mV_max) {
+ ERR_MSG("%s(%d),VCOM %d cannot > %d\n",
+ __FUNCTION__,__LINE__,iVCOM_mV,iVCOM_mV_max);
+ }
+ else {
+ unsigned char bReg;
+ int i10uV_Steps;
+ int i10uV_Steps_mod10;
+
+ int iOldPowerState ;
+
+
+ if( iVCOM_mV<(gptFP9928_data->iCurrent_VCOM_mV+FP9928_VCOM_UV_STEP/1000) && \
+ iVCOM_mV>(gptFP9928_data->iCurrent_VCOM_mV-FP9928_VCOM_UV_STEP/1000) )
+ {
+ // the VCOM range you want to set is close to current VCOM.
+ return FP9928_RET_SUCCESS;
+ }
+
+ if(iVCOM_mV<0) {
+ iVCOM_mV_ABS = -iVCOM_mV;
+ }
+ else {
+ iVCOM_mV_ABS = iVCOM_mV;
+ }
+
+ iOldPowerState = gptFP9928_data->iIsPoweredON;
+ fp9928_ONOFF(1);
+ i10uV_Steps = (int)(iVCOM_mV_ABS*10000/FP9928_VCOM_UV_STEP);
+ i10uV_Steps_mod10 = (int)(i10uV_Steps%10);
+ if(i10uV_Steps_mod10>=5) {
+ bReg = (unsigned char) ((i10uV_Steps/10)+1);
+ }
+ else {
+ bReg = (unsigned char) (i10uV_Steps/10);
+ }
+ printk("%s():want set VCOM %dmV,reg=0x%02X,output %dmV,to flash=%d\n",\
+ __FUNCTION__,iVCOM_mV,bReg,bReg*FP9928_VCOM_UV_STEP/1000,iIsWriteToFlash);
+ iRet = FP9928_REG_SET(VCOM_SETTING,ALL,bReg);
+ if(iRet>=0) {
+ gptFP9928_data->iCurrent_VCOM_mV=-((int)FP9928_REG(VCOM_SETTING)*FP9928_VCOM_UV_STEP/1000);
+ }
+ fp9928_ONOFF(iOldPowerState);
+
+ }
+
+ return iRet;
+}
+
+
+int fp9928_vcom_get(int *O_piVCOM_mV)
+{
+ int iVCOM_mV;
+ unsigned short wReg;
+ int iOldPowerState;
+ int iRet = FP9928_RET_SUCCESS;
+
+ if(!gptFP9928_data) {
+ WARNING_MSG("%s() cannot work if driver is not initialed \n",__FUNCTION__);
+ return FP9928_RET_NOTINITEDSTATE;
+ }
+
+ iOldPowerState = gptFP9928_data->iIsPoweredON;
+ fp9928_ONOFF(1);
+
+ _fp9928_reinit_vcom();
+
+ wReg = FP9928_REG_GET(VCOM_SETTING);
+ iVCOM_mV = (int)(wReg);
+ iVCOM_mV = -(iVCOM_mV*FP9928_VCOM_UV_STEP/1000);
+ //DBG_MSG("%s(%d):iVCOM_mV=%d,wReg=0x%x\n",__FUNCTION__,__LINE__,iVCOM_mV,wReg);
+
+ if(iVCOM_mV!=gptFP9928_data->iCurrent_VCOM_mV) {
+ WARNING_MSG("%s(%d) VCOM read from register is 0x%x not equal with stored \n",__FUNCTION__,__LINE__,wReg);
+ }
+
+ if(O_piVCOM_mV) {
+ *O_piVCOM_mV = iVCOM_mV;
+ gptFP9928_data->iCurrent_VCOM_mV=iVCOM_mV;
+ }
+
+ fp9928_ONOFF(iOldPowerState);
+
+ return iRet;
+}
+
+int fp9928_vcom_get_cached(int *O_piVCOM_mV)
+{
+ if(!gptFP9928_data) {
+ WARNING_MSG("%s() cannot work if driver is not initialed \n",__FUNCTION__);
+ return FP9928_RET_NOTINITEDSTATE;
+ }
+
+ if(O_piVCOM_mV) {
+ *O_piVCOM_mV = gptFP9928_data->iCurrent_VCOM_mV;
+ }
+
+ return FP9928_RET_SUCCESS;
+}
+
+
+void fp9928_release(void)
+{
+ if(!gptFP9928_data) {
+ WARNING_MSG("%s() cannot work if driver is not initialed \n",__FUNCTION__);
+ return ;
+ }
+
+
+ mutex_lock(&gptFP9928_data->tPwrLock);
+ _fp9928_vin_onoff(0);
+ mutex_unlock(&gptFP9928_data->tPwrLock);
+
+ gptFP9928_data->ptI2C_adapter = 0;
+ i2c_unregister_device(gptFP9928_data->ptI2C_client);
+ gptFP9928_data->ptI2C_client = 0;
+
+ _fp9928_gpio_release();
+
+ kfree(gptFP9928_data);gptFP9928_data = 0;
+
+}
+
+int fp9928_init(int iPort)
+{
+
+ int iRet = FP9928_RET_SUCCESS;
+ int iChk;
+
+ unsigned long dwSize;
+
+ GALLEN_DBGLOCAL_BEGIN();
+
+ if(gptFP9928_data) {
+ WARNING_MSG("skipped %s() calling over twice !!\n",__FUNCTION__);
+ return 0;
+ }
+
+ dwSize=sizeof(FP9928_data);
+
+ gptFP9928_data = kmalloc(dwSize,GFP_KERNEL);
+ if(!gptFP9928_data) {
+ iRet = FP9928_RET_MEMNOTENOUGH;
+ ERR_MSG("%s(%d) : memory not enough !!\n",__FILE__,__LINE__);
+ GALLEN_DBGLOCAL_RUNLOG(0);
+ goto MEM_MALLOC_FAIL;
+ }
+
+ memset(gptFP9928_data,0,sizeof(FP9928_data));
+
+ if(36==gptHWCFG->m_val.bPCB || 40==gptHWCFG->m_val.bPCB) {
+ // E60Q3X/E60Q5X
+ if((0==gptHWCFG->m_val.bPCB_LVL&&gptHWCFG->m_val.bPCB_REV>=0x10) || 40==gptHWCFG->m_val.bPCB) {
+ // >= E60Q30A10 ,E60Q5X
+ printk("%s(): EP3V3 switch enabled",__FUNCTION__);
+ gptFP9928_data->iIsEP3V3_SW_enabled = 1;
+ }
+ else {
+ gptFP9928_data->iIsEP3V3_SW_enabled = 0;
+ }
+ }
+ else {
+ gptFP9928_data->iIsEP3V3_SW_enabled = 0;
+ }
+
+ gptFP9928_data->dwTickPowerOffEnd = jiffies;
+ gptFP9928_data->dwTickPowerOnEnd = jiffies;
+ gptFP9928_data->dwTickEP3V3OffEnd = jiffies;
+
+ iChk = _fp9928_gpio_init();
+ if(iChk<0) {
+ iRet = FP9928_RET_GPIOINITFAIL;
+ ERR_MSG("%s(%d) : gpio init fail !!\n",__FILE__,__LINE__);
+ GALLEN_DBGLOCAL_RUNLOG(1);
+ goto GPIO_INIT_FAIL;
+ }
+
+ {
+ unsigned long dwTicks,dwTickNow;
+
+ _fp9928_vin_onoff(1);
+
+ dwTickNow = jiffies;
+ FP9928_WAIT_TICKSTAMP(gptFP9928_data->dwTickPowerOnEnd,"power on stable");
+ }
+
+ gptFP9928_data->ptI2C_adapter = i2c_get_adapter(iPort-1);//
+ if( NULL == gptFP9928_data->ptI2C_adapter) {
+ ERR_MSG ("[Error] %s : FP9928_RET_I2CCHN_NOTFOUND,chn=%d\n",__FUNCTION__,iPort);
+ GALLEN_DBGLOCAL_RUNLOG(2);
+ iRet=FP9928_RET_I2CCHN_NOTFOUND;
+ goto I2CCHN_GET_FAIL;
+ }
+
+ gptFP9928_data->ptI2C_client = i2c_new_probed_device(gptFP9928_data->ptI2C_adapter, &gtFP9928_BI,gwFP9928_AddrA,0);
+ if( NULL == gptFP9928_data->ptI2C_client ) {
+ GALLEN_DBGLOCAL_RUNLOG(3);
+ ERR_MSG("[Error] %s : FP9928 probe fail \n",__FUNCTION__);
+ goto I2CPROBE_DEVICE_FAIL;
+ }
+
+ gptFP9928_data->iCurrent_VCOM_mV=-((int)FP9928_REG(VCOM_SETTING)*FP9928_VCOM_UV_STEP/1000);// default VCOM voltage .
+ //fp9928_vcom_get(&gptFP9928_data->iCurrent_VCOM_mV);
+
+
+ // kernel objects initialize ...
+ mutex_init(&gptFP9928_data->tPwrLock);
+ mutex_init(&gptFP9928_data->tI2CLock);
+
+ INIT_DELAYED_WORK(&gptFP9928_data->tPwrdwn_work_param.pwrdwn_work,_fp9928_pwrdwn_work_func);
+
+
+ _fp9928_reg_init();
+ fp9928_ONOFF(INIT_POWER_STATE);
+
+ GALLEN_DBGLOCAL_ESC();
+ return FP9928_RET_SUCCESS;
+
+
+ i2c_unregister_device(gptFP9928_data->ptI2C_client);
+ gptFP9928_data->ptI2C_client = 0;
+I2CPROBE_DEVICE_FAIL:
+ gptFP9928_data->ptI2C_adapter = 0;
+I2CCHN_GET_FAIL:
+ _fp9928_gpio_release();
+GPIO_INIT_FAIL:
+ kfree(gptFP9928_data);gptFP9928_data = 0;
+MEM_MALLOC_FAIL:
+
+ GALLEN_DBGLOCAL_END();
+ return iRet;
+}
+
+
diff --git a/drivers/video/mxc/lk_fp9928.h b/drivers/video/mxc/lk_fp9928.h
new file mode 100644
index 00000000..4c409682
--- /dev/null
+++ b/drivers/video/mxc/lk_fp9928.h
@@ -0,0 +1,34 @@
+#ifndef __LK_FP9928_H //[
+#define __LK_FP9928_H
+
+
+#define FP9928_RET_SUCCESS 0
+#define FP9928_RET_GPIOINITFAIL (-1)
+#define FP9928_RET_MEMNOTENOUGH (-2)
+#define FP9928_RET_NOTINITEDSTATE (-3)
+#define FP9928_RET_PWRDWNWORKPENDING (-4)
+#define FP9928_RET_I2CCHN_NOTFOUND (-5)
+#define FP9928_RET_I2CTRANS_ERR (-6)
+
+
+int fp9928_init(int iPort);
+void fp9928_release(void);
+
+
+int fp9928_suspend(void);
+void fp9928_resume(void);
+void fp9928_shutdown(void);
+
+
+int fp9928_output_power(int iIsOutputPwr,int iIsChipPowerDown);
+int fp9928_power_onoff(int iIsPowerOn,int iIsOutputPwr);
+
+int fp9928_get_temperature(int *O_piTemperature);
+int fp9928_vcom_set(int iVCOM_mV,int iIsWriteToFlash);
+int fp9928_vcom_get(int *O_piVCOM_mV);
+int fp9928_vcom_get_cached(int *O_piVCOM_mV);
+
+int fp9928_ONOFF(int iIsON);
+
+#endif //] __LK_FP9928_H
+
diff --git a/drivers/video/mxc/lk_tps65185.c b/drivers/video/mxc/lk_tps65185.c
new file mode 100644
index 00000000..c3937c90
--- /dev/null
+++ b/drivers/video/mxc/lk_tps65185.c
@@ -0,0 +1,2311 @@
+
+
+/*
+ * purpose : TPS65185 driver
+ *
+ * author : Gallen Lin
+ * versions :
+ *
+ */
+
+
+#include <linux/kernel.h>
+//#include <linux/config.h>
+
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/workqueue.h>
+#include <linux/completion.h>
+#include <linux/wait.h>
+#include <linux/semaphore.h>
+
+
+#ifdef CONFIG_MACH_MX6SL_NTX//[
+ #define TPS65185_PLATFORM_MX6 1
+#endif//]CONFIG_MACH_MX6SL_NTX
+
+
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+
+#ifdef TPS65185_PLATFORM_MX6//[
+ #include <mach/iomux-mx6sl.h>
+#else //][!TPS65185_PLATFORM_MX6
+ #include <mach/iomux-mx50.h>
+#endif //] TPS65185_PLATFORM_MX6
+
+#include "ntx_hwconfig.h"
+
+#define GDEBUG 0
+#include <linux/gallen_dbg.h>
+
+#include "lk_tps65185.h"
+
+#define TPS65185_SUSPEND 1
+
+// command byte definitions ...
+
+#define DRIVERNAME "TPS65185"
+#define REG_UNKOWN_VAL 0xcc
+
+#define TOTAL_CHIPS 1
+
+//MODULE_LICENSE("GPL");
+
+//#define TPS65185_EP3V3_PWROFF 1 // turn off the EP_3V3 when EPD ON/OFF .
+#define TPS65185_PWR_ONOFF_INT 1
+//#define TPS65185_PWR_ONOFF_WAITBYCOMPLETE 1
+
+#define TPS65185_PWROFFDELAYWORK_TICKS 50
+#define TPS65185_RESUME_EP3V3_ON 1
+
+#define VCOM_ENABLE 1
+#define VCOM_DISABLE 0
+
+#ifdef TPS65185_PLATFORM_MX6//[
+
+ #define GPIO_TPS65185_PWRUP IMX_GPIO_NR(2,8) // EPDC_PWRCTRL1
+ #define GPIO_TPS65185_PWRUP_PADCFG MX6SL_PAD_EPDC_PWRCTRL1__GPIO_2_8
+
+ #define GPIO_TPS65185_WAKEUP IMX_GPIO_NR(2,7) // EPDC_PWRCTRL0
+ #define GPIO_TPS65185_WAKEUP_PADCFG MX6SL_PAD_EPDC_PWRCTRL0__GPIO_2_7
+
+ #define GPIO_TPS65185_VCOMCTRL IMX_GPIO_NR(2,3) // EPDC_VCOM0
+ #define GPIO_TPS65185_VCOMCTRL_PADCFG MX6SL_PAD_EPDC_VCOM0__GPIO_2_3
+
+ #define GPIO_TPS65185_VIN IMX_GPIO_NR(2,14) // EPDC_PWRWAKEUP
+ #define GPIO_TPS65185_VIN_PADCFG MX6SL_PAD_EPDC_PWRWAKEUP__GPIO_2_14
+
+ #define GPIO_TPS65185_EP_3V3_IN IMX_GPIO_NR(4,3) // EPDC_VDD 1.8V/3.3V .
+ #define GPIO_TPS65185_EP_3V3_IN_PADCFG MX6SL_PAD_KEY_ROW5__GPIO_4_3
+
+ // GPIO input
+ #define GPIO_TPS65185_PWRGOOD IMX_GPIO_NR(2,13) // EPDC_PWRSTAT
+ #define GPIO_TPS65185_PWRGOOD_INT_PADCFG MX6SL_PAD_EPDC_PWRSTAT__GPIO_2_13_PUINT
+ #define GPIO_TPS65185_PWRGOOD_GPIO_PADCFG MX6SL_PAD_EPDC_PWRSTAT__GPIO_2_13
+
+ #define GPIO_TPS65185_INT IMX_GPIO_NR(2,9) // EPDC_PWRCTRL2
+ #define GPIO_TPS65185_INT_PADCFG MX6SL_PAD_EPDC_PWRCTRL2__GPIO_2_9_PUINT
+ #define GPIO_TPS65185_INT_GPIO_PADCFG MX6SL_PAD_EPDC_PWRCTRL2__GPIO_2_9
+
+ //#define GPIO_TPS65185_SDA
+ //#define GPIO_TPS65185_SDL
+ #define set_irq_type(_irq,_irqtype) irq_set_irq_type((_irq),(_irqtype))
+
+#else //][!TPS65185_PLATFORM_MX6
+
+//////////////////////////////////////////////////////////////
+// definitions gpio ...
+// GPIO output
+ #define GPIO_TPS65185_PWRUP (2*32 + 30) /* GPIO_3_30 */
+ #define GPIO_TPS65185_PWRUP_PADCFG MX50_PAD_EPDC_PWRCTRL1__GPIO_3_30
+
+ #define GPIO_TPS65185_WAKEUP (2*32 + 29) /* GPIO_3_29 */
+ #define GPIO_TPS65185_WAKEUP_PADCFG MX50_PAD_EPDC_PWRCTRL0__GPIO_3_29
+
+ #define GPIO_TPS65185_VCOMCTRL (3*32 + 21) /* GPIO_4_21 */
+ #define GPIO_TPS65185_VCOMCTRL_PADCFG MX50_PAD_EPDC_VCOM0__GPIO_4_21
+
+ #define GPIO_TPS65185_VIN (0*32 + 27) /* GPIO_1_27 */
+ #define GPIO_TPS65185_VIN_PADCFG MX50_PAD_EIM_CRE__GPIO_1_27
+
+ // GPIO input
+ #define GPIO_TPS65185_PWRGOOD (2*32 + 28) /* GPIO_3_28 */
+ #define GPIO_TPS65185_PWRGOOD_INT_PADCFG MX50_PAD_EPDC_PWRSTAT__GPIO_3_28_INT
+ #define GPIO_TPS65185_PWRGOOD_GPIO_PADCFG MX50_PAD_EPDC_PWRSTAT__GPIO_3_28
+
+ #define GPIO_TPS65185_INT (3*32 + 15) /* GPIO_4_15 */
+ #define GPIO_TPS65185_INT_PADCFG MX50_PAD_ECSPI1_SS0__GPIO_4_15_PUINT
+ #define GPIO_TPS65185_INT_GPIO_PADCFG MX50_PAD_ECSPI1_SS0__GPIO_4_15
+
+ //#define GPIO_TPS65185_SDA (5*32+21) /* GPIO_6_21 */
+ //#define GPIO_TPS65185_SDL (5*32+20) /* GPIO_6_20 */
+ //
+#endif //] TPS65185_PLATFORM_MX6
+///////////////////////////////////////////////////////
+// definitions for config_epd_timing() ...
+
+typedef struct tagTPS65185_VERSIONS{
+ unsigned char bMajor;
+ unsigned char bMinor;
+ unsigned char bVersion;
+ unsigned char bRevID;
+} TPS65185_VERSIONS;
+
+typedef struct tagTPS65185_data {
+ int iCurrent_temprature;
+ unsigned short wTempratureData;
+ unsigned long dwCurrent_mode;// active , sleep , standby .
+ TPS65185_VERSIONS t65185_versions;
+ int iCurrentPwrupState;
+ int iCurrentWakeupState;
+ int iIsInitPwrON;// is first power on or not , if yes ,you must to delay 1.8ms for i2c protocol initial .
+ //unsigned char bRegENABLE;
+ //int iLast_temprature;
+ struct semaphore i2clock;
+ struct semaphore chmod_lock;
+ int iCurrentVCOM;
+ int iRestoreVCOM;
+ unsigned long dwEP3V3_Off_Ticks;
+} TPS65185_data;
+
+
+typedef struct tagTPS65185_PWRDWN_WORK_PARAM {
+ struct delayed_work pwrdwn_work;
+ unsigned long dwNewMode;
+ int iIsWaitPwrOff;
+} TPS65185_PWRDWN_WORK_PARAM;
+
+#define LKDRIVER_DATA_INIT(_iChipIdx) \
+{\
+ gtTPS65185_DataA[_iChipIdx].iCurrent_temprature=-1;\
+ gtTPS65185_DataA[_iChipIdx].wTempratureData=0;\
+ gtTPS65185_DataA[_iChipIdx].dwCurrent_mode=TPS65185_MODE_UNKOWN;\
+ gtTPS65185_DataA[_iChipIdx].iCurrentPwrupState=-1;\
+ gtTPS65185_DataA[_iChipIdx].iCurrentWakeupState=-1;\
+ gtTPS65185_DataA[_iChipIdx].iIsInitPwrON=1;\
+ sema_init(&gtTPS65185_DataA[_iChipIdx].i2clock,1);\
+ sema_init(&gtTPS65185_DataA[_iChipIdx].chmod_lock,1);\
+}
+
+// externals ...
+extern volatile NTX_HWCONFIG *gptHWCFG;
+extern volatile int gSleep_Mode_Suspend;
+
+
+//
+static TPS65185_PWRDWN_WORK_PARAM gtPwrdwn_work_param;
+
+static struct i2c_adapter *gpI2C_adapter = 0;
+static struct i2c_client *gpI2C_clientA[TOTAL_CHIPS] = {0,};
+volatile static int giIsTPS65185_inited=0;
+volatile static int giIsTPS65185_gpio_inited=0;
+volatile static int giIsTPS65185_turnoff_EP3V3=0;
+volatile static unsigned long gdwSafeTick_To_TurnON_RailPower;
+volatile static unsigned long gdwSafeTick_To_TurnOFF_EP3V3;
+
+static struct i2c_board_info gtTPS65185_BIA[TOTAL_CHIPS] = {
+ {
+ .type = "tps65185-1",
+ .addr = 0x68,
+ .platform_data = NULL,
+ },
+};
+
+
+static const unsigned short gwTPS65185_AddrA[] = {
+ 0x68,
+ I2C_CLIENT_END
+};
+
+
+static TPS65185_data gtTPS65185_DataA[TOTAL_CHIPS] = {
+ {-1,0x0000,TPS65185_MODE_UNKOWN,},
+};
+
+
+
+//////////////////////////////////////////////////////////////////////////
+//
+// internal hardware helper ...
+//
+
+
+
+// registers (write)....
+#define TPS65185_REG_ENABLE_ACTIVE 0x80
+#define TPS65185_REG_ENABLE_STANDBY 0x40
+#define TPS65185_REG_ENABLE_V3P3_EN 0x20
+#define TPS65185_REG_ENABLE_VCOM_EN 0x10
+#define TPS65185_REG_ENABLE_VDDH_EN 0x08
+#define TPS65185_REG_ENABLE_VPOS_EN 0x04
+#define TPS65185_REG_ENABLE_VEE_EN 0x02
+#define TPS65185_REG_ENABLE_VNEG_EN 0x01
+#define TPS65185_REG_ENABLE_ALL 0xff
+static volatile unsigned char gbTPS65185_REG_ENABLE=0; // default reset value is zero .
+static const unsigned char gbTPS65185_REG_ENABLE_addr=0x01;
+
+static volatile unsigned char gbTPS65185_REG_VADJ=0x23; // 15V .
+static const unsigned char gbTPS65185_REG_VADJ_addr=0x02;
+
+
+#define TPS65185_REG_VCOM1_ALL 0xff
+static volatile unsigned char gbTPS65185_REG_VCOM1=0x7d; // .
+static const unsigned char gbTPS65185_REG_VCOM1_addr=0x03;
+
+
+#define TPS65185_REG_VCOM2_ACQ 0x80
+#define TPS65185_REG_VCOM2_PROG 0x40
+#define TPS65185_REG_VCOM2_HiZ 0x20
+//#define TPS65185_REG_VCOM2_AVG 0x18
+#define TPS65185_REG_VCOM2_VCOM8 0x01
+#define TPS65185_REG_VCOM2_ALL 0xff
+static volatile unsigned char gbTPS65185_REG_VCOM2=0x04; // .
+static const unsigned char gbTPS65185_REG_VCOM2_addr=0x04;
+
+//#define TPS65185_REG_INT_EN1_DTX_EN 0x80
+#define TPS65185_REG_INT_EN1_TSD_EN 0x40
+#define TPS65185_REG_INT_EN1_HOT_EN 0x20
+#define TPS65185_REG_INT_EN1_TMST_HOT_EN 0x10
+#define TPS65185_REG_INT_EN1_TMST_COLD_EN 0x08
+#define TPS65185_REG_INT_EN1_UVLO_EN 0x04
+#define TPS65185_REG_INT_EN1_ACQC_EN 0x02
+#define TPS65185_REG_INT_EN1_PRGC_EN 0x01
+#define TPS65185_REG_INT_EN1_ALL 0xff
+static volatile unsigned char gbTPS65185_REG_INT_EN1=0x7f; // .
+static const unsigned char gbTPS65185_REG_INT_EN1_addr=0x05;
+
+#define TPS65185_REG_INT_EN2_VBUVEN 0x80
+#define TPS65185_REG_INT_EN2_VDDHUVEN 0x40
+#define TPS65185_REG_INT_EN2_VNUV_EN 0x20
+#define TPS65185_REG_INT_EN2_VPOSUVEN 0x10
+#define TPS65185_REG_INT_EN2_VEEUVEN 0x08
+#define TPS65185_REG_INT_EN2_VCOMFEN 0x04
+#define TPS65185_REG_INT_EN2_VNEGUVEN 0x02
+#define TPS65185_REG_INT_EN2_EOCEN 0x01
+#define TPS65185_REG_INT_EN2_ALL 0xff
+static volatile unsigned char gbTPS65185_REG_INT_EN2=0xff; // .
+static const unsigned char gbTPS65185_REG_INT_EN2_addr=0x06;
+
+#define TPS65185_REG_INT1_ACQC 0x02
+#define TPS65185_REG_INT1_PRGC 0x01
+#define TPS65185_REG_INT1_UVLO 0x04
+#define TPS65185_REG_INT1_HOT 0x20
+#define TPS65185_REG_INT1_TSD 0x40
+static volatile unsigned char gbTPS65185_REG_INT1=0x0; // .
+static const unsigned char gbTPS65185_REG_INT1_addr=0x07;
+
+#define TPS65185_REG_INT2_VB_UV 0x80
+#define TPS65185_REG_INT2_VDDH_UV 0x40
+#define TPS65185_REG_INT2_VN_UV 0x20
+#define TPS65185_REG_INT2_VPOS_UV 0x10
+#define TPS65185_REG_INT2_VEE_UV 0x08
+#define TPS65185_REG_INT2_VCOMF 0x04
+#define TPS65185_REG_INT2_VNEG_UV 0x02
+#define TPS65185_REG_INT2_EOC 0x01
+static volatile unsigned char gbTPS65185_REG_INT2=0x0; // .
+static const unsigned char gbTPS65185_REG_INT2_addr=0x08;
+
+
+
+static volatile unsigned char gbTPS65185_REG_UPSEQ0=0xe4; // .
+static const unsigned char gbTPS65185_REG_UPSEQ0_addr=0x09;
+
+static volatile unsigned char gbTPS65185_REG_UPSEQ1=0x55; // .
+static const unsigned char gbTPS65185_REG_UPSEQ1_addr=0x0a;
+
+
+#define TPS65185_REG_DWNSEQ0_ALL 0xff
+static volatile unsigned char gbTPS65185_REG_DWNSEQ0=0x1e; // .
+static const unsigned char gbTPS65185_REG_DWNSEQ0_default=0x1e; // .
+//static volatile unsigned char gbTPS65185_REG_DWNSEQ0=0x1b; // .
+static const unsigned char gbTPS65185_REG_DWNSEQ0_addr=0x0b;
+
+static volatile unsigned char gbTPS65185_REG_DWNSEQ1=0xe0; // .
+static const unsigned char gbTPS65185_REG_DWNSEQ1_addr=0x0c;
+
+#define TPS65185_REG_TMST1_READ_THERM 0x80
+#define TPS65185_REG_TMST1_CONV_END 0x20
+static volatile unsigned char gbTPS65185_REG_TMST1=0x20; // .
+static const unsigned char gbTPS65185_REG_TMST1_addr=0x0d;
+
+//static unsigned char gbTPS65185_REG_TMST2=0x78; // .
+//static const unsigned char gbTPS65185_REG_TMST2_addr=0x0e;
+
+// registers (read)....
+static volatile unsigned char gbTPS65185_REG_TMST_VALUE=0; //
+static const unsigned char gbTPS65185_REG_TMST_VALUE_addr=0x00;
+
+static volatile unsigned char gbTPS65185_REG_PG=0;
+static const unsigned char gbTPS65185_REG_PG_addr=0x0f;
+
+static volatile unsigned char gbTPS65185_REG_REVID=0x45; // default is TPS65185 1p0 .
+static const unsigned char gbTPS65185_REG_REVID_addr=0x10;
+
+
+
+static int tps65185_set_reg(unsigned char bRegAddr,unsigned char bRegSetVal)
+{
+ int iRet=TPS65185_RET_SUCCESS;
+ int iChk;
+ unsigned char bA[2] ;
+ //int irq_INT,irq_PG;
+
+#if 0
+ ASSERT(gpI2C_adapter);
+ ASSERT(gpI2C_clientA[0]);
+#else
+ if(!gpI2C_adapter) {
+ WARNING_MSG("%s gpI2C_adapter null \n",__FUNCTION__);
+ return (TPS65185_RET_PARAMERR);
+ }
+ if(!gpI2C_clientA[0]) {
+ WARNING_MSG("%s gpI2C_clientA[0] null \n",__FUNCTION__);
+ return (TPS65185_RET_PARAMERR);
+ }
+#endif
+ //irq_INT = gpio_to_irq(GPIO_TPS65185_INT);
+ //irq_PG = gpio_to_irq(GPIO_TPS65185_PWRGOOD);
+
+ //disable_irq(irq_INT);
+ //disable_irq(irq_PG);
+
+ down(&gtTPS65185_DataA[0].i2clock);
+ bA[0]=bRegAddr;
+ bA[1]=bRegSetVal;
+ iChk = i2c_master_send(gpI2C_clientA[0], (const char *)bA, sizeof(bA));
+ if (iChk < 0) {
+ ERR_MSG("%s(%d):%d=%s(),regAddr=0x%x,regVal=0x%x fail !\n",__FILE__,__LINE__,\
+ iChk,"i2c_master_send",bRegAddr,bRegSetVal);
+ iRet=TPS65185_RET_I2CTRANS_ERR;
+ }
+ up(&gtTPS65185_DataA[0].i2clock);
+ //enable_irq(irq_PG);
+ //enable_irq(irq_INT);
+ return iRet;
+}
+
+static int tps65185_get_reg(unsigned char bRegAddr,unsigned char *O_pbRegVal)
+{
+ int iRet=TPS65185_RET_SUCCESS;
+ int iChk;
+ unsigned char bA[1] ;
+ //int irq_INT , irq_PG;
+
+#if 0
+ ASSERT(gpI2C_adapter);
+ ASSERT(gpI2C_clientA[0]);
+#else
+ if(!gpI2C_adapter) {
+ WARNING_MSG("%s gpI2C_adapter null \n",__FUNCTION__);
+ return (TPS65185_RET_PARAMERR);
+ }
+ if(!gpI2C_clientA[0]) {
+ WARNING_MSG("%s gpI2C_clientA[0] null \n",__FUNCTION__);
+ return (TPS65185_RET_PARAMERR);
+ }
+#endif
+
+ ASSERT(O_pbRegVal);
+
+ bA[0]=bRegAddr;
+
+
+ if(!in_interrupt()) {
+
+ //irq_INT = gpio_to_irq(GPIO_TPS65185_INT);
+ //irq_PG = gpio_to_irq(GPIO_TPS65185_PWRGOOD);
+
+ //disable_irq(irq_INT);
+ //disable_irq(irq_PG);
+ down(&gtTPS65185_DataA[0].i2clock);
+ }
+
+ iChk = i2c_master_send(gpI2C_clientA[0], (const char *)bA, 1);
+ if (iChk < 0) {
+ ERR_MSG("%s(%d):%s i2c_master_send fail !\n",__FILE__,__LINE__,__FUNCTION__);
+ iRet = TPS65185_RET_I2CTRANS_ERR;
+ }
+
+
+ iChk = i2c_master_recv(gpI2C_clientA[0], bA, 1);
+ if (iChk < 0) {
+ ERR_MSG("%s(%d):%s i2c_master_recv fail !\n",__FILE__,__LINE__,__FUNCTION__);
+ iRet = TPS65185_RET_I2CTRANS_ERR;
+ }
+
+ if(!in_interrupt()) {
+ up(&gtTPS65185_DataA[0].i2clock);
+ //enable_irq(irq_PG);
+ //enable_irq(irq_INT);
+ }
+
+ if(iRet>=0) {
+ *O_pbRegVal = bA[0];
+ }
+
+ return iRet;
+}
+
+
+#define TPS65185_REG_SET(_regName,_bFieldName,_bSetVal) \
+({\
+ int _iRet=TPS65185_RET_SUCCESS;\
+ int _iChk;\
+ unsigned char _bNewReg,_bFieldMask;\
+ \
+ _bFieldMask=(unsigned char)TPS65185_REG_##_regName##_##_bFieldName;\
+ if(0xff==_bFieldMask) {\
+ _bNewReg = _bSetVal;\
+ }\
+ else {\
+ _bNewReg=gbTPS65185_REG_##_regName;\
+ if(_bSetVal) {\
+ _bNewReg |= _bFieldMask ;\
+ }\
+ else {\
+ _bNewReg &= ~_bFieldMask;\
+ }\
+ }\
+ \
+ _iChk = tps65185_set_reg(gbTPS65185_REG_##_regName##_##addr,_bNewReg);\
+ if(_iChk<0) {\
+ _iRet = _iChk;\
+ }\
+ else {\
+ DBG_MSG("%s() : tps65185 write reg%s(%02Xh) 0x%02x->0x%02x\n",__FUNCTION__,\
+ #_regName,gbTPS65185_REG_##_regName##_##addr,gbTPS65185_REG_##_regName,_bNewReg);\
+ gbTPS65185_REG_##_regName = _bNewReg;\
+ }\
+ _iRet;\
+})
+
+#define TPS65185_REG_GET(_regName) \
+({\
+ int _iChk;\
+ unsigned char bReadReg=0;\
+ unsigned short _wRet=0;\
+ \
+ _iChk = tps65185_get_reg(gbTPS65185_REG_##_regName##_##addr,&bReadReg);\
+ if(_iChk<0) {\
+ _wRet = (unsigned short)(-1);\
+ }\
+ else {\
+ _wRet = bReadReg;\
+ gbTPS65185_REG_##_regName = bReadReg;\
+ DBG_MSG("%s() : tps65185 read reg%s(%02Xh)=0x%02x\n",__FUNCTION__,\
+ #_regName,gbTPS65185_REG_##_regName##_##addr,bReadReg);\
+ }\
+ _wRet;\
+})
+
+#define TPS65185_REG(_regName) gbTPS65185_REG_##_regName
+
+#if 0 //[
+#define TPS65185_VCOM_OUT(_out_val) \
+ if(_out_val) { \
+ gpio_direction_output(GPIO_TPS65185_VCOMCTRL, VCOM_ENABLE); \
+ } \
+ else { \
+ gpio_direction_output(GPIO_TPS65185_VCOMCTRL, VCOM_DISABLE); \
+ }
+#else //][!
+#define TPS65185_VCOM_OUT(_out_val) \
+ if(_out_val) { \
+ unsigned char bReg;\
+ gpio_direction_output(GPIO_TPS65185_VCOMCTRL, VCOM_ENABLE); \
+ bReg = (unsigned char)TPS65185_REG_GET(ENABLE);\
+ if(!(bReg&TPS65185_REG_ENABLE_VCOM_EN)) {\
+ WARNING_MSG("[WARNING] %s():ENABLE=0x%x, VOM_EN=0 but gpio=1\n",__FUNCTION__,bReg);\
+ TPS65185_REG_SET(ENABLE,VCOM_EN,1);\
+ }\
+ } \
+ else { \
+ gpio_direction_output(GPIO_TPS65185_VCOMCTRL, VCOM_DISABLE); \
+ }
+#endif //]
+
+DECLARE_WAIT_QUEUE_HEAD(tps65185_ACQC_WQ);
+DECLARE_WAIT_QUEUE_HEAD(tps65185_PRGC_WQ);
+
+
+static struct work_struct tps65185_int_work;
+static struct workqueue_struct *tps65185_int_workqueue;
+
+static void tps65185_int_func(struct work_struct *work)
+{
+ unsigned char bRegINT1,bRegINT2;
+ unsigned short wReg;
+
+
+ wReg = TPS65185_REG_GET(INT1);
+ if(((unsigned short)(-1))==wReg) {
+ ERR_MSG("%s(%d):%s regINT1 read fail !\n",__FILE__,__LINE__,__FUNCTION__);
+ }
+ bRegINT1=(unsigned char)wReg;
+
+ wReg = TPS65185_REG_GET(INT2);
+ if(((unsigned short)(-1))==wReg) {
+ ERR_MSG("%s(%d):%s regINT2 read fail !\n",__FILE__,__LINE__,__FUNCTION__);
+ }
+ bRegINT2=(unsigned char)wReg;
+
+ if(bRegINT1&TPS65185_REG_INT1_ACQC) {
+ wake_up_all(&tps65185_ACQC_WQ);
+ }
+
+ if(bRegINT1&TPS65185_REG_INT1_PRGC) {
+ wake_up_all(&tps65185_PRGC_WQ);
+ }
+
+ if(bRegINT1&TPS65185_REG_INT1_UVLO) {
+ ERR_MSG("%s(%d):%s input voltage is below UVLO threshold !\n",__FILE__,__LINE__,__FUNCTION__);
+ }
+ if(bRegINT1&TPS65185_REG_INT1_TSD) {
+ ERR_MSG("%s(%d):%s chip is over-temperature shutdown !\n",__FILE__,__LINE__,__FUNCTION__);
+ }
+ if(bRegINT1&TPS65185_REG_INT1_HOT) {
+ ERR_MSG("%s(%d):%s chip is approaching over-temperature shutdown !\n",__FILE__,__LINE__,__FUNCTION__);
+ }
+
+ if(bRegINT2&TPS65185_REG_INT2_VB_UV) {
+ ERR_MSG("%s(%d):%s under-voltage on DCDC1 detected !\n",__FILE__,__LINE__,__FUNCTION__);
+ }
+ if(bRegINT2&TPS65185_REG_INT2_VDDH_UV) {
+ ERR_MSG("%s(%d):%s under-voltage on VDDH charge pump detected !\n",__FILE__,__LINE__,__FUNCTION__);
+ }
+ if(bRegINT2&TPS65185_REG_INT2_VN_UV) {
+ ERR_MSG("%s(%d):%s under-voltage on DCDC2 detected !\n",__FILE__,__LINE__,__FUNCTION__);
+ }
+ if(bRegINT2&TPS65185_REG_INT2_VPOS_UV) {
+ ERR_MSG("%s(%d):%s under-voltage on LDO1(VPOS) detected !\n",__FILE__,__LINE__,__FUNCTION__);
+ }
+ if(bRegINT2&TPS65185_REG_INT2_VEE_UV) {
+ ERR_MSG("%s(%d):%s under-voltage on VEE charge pump detected !\n",__FILE__,__LINE__,__FUNCTION__);
+ }
+ if(bRegINT2&TPS65185_REG_INT2_VCOMF) {
+ ERR_MSG("%s(%d):%s fault on VCOM detected !\n",__FILE__,__LINE__,__FUNCTION__);
+ }
+ if(bRegINT2&TPS65185_REG_INT2_VNEG_UV) {
+ ERR_MSG("%s(%d):%s under-voltage on LDO2(VNEG) detected !\n",__FILE__,__LINE__,__FUNCTION__);
+ }
+
+#if 0
+ if(bRegINT2&TPS65185_REG_INT2_EOC) {
+ ERR_MSG("%s(%d):%s ADC conversion is complete (temperature acquisition is complete !\n",__FILE__,__LINE__,__FUNCTION__);
+ }
+#endif
+
+ //DBG0_MSG("%s() : INT1=0x%x,INT2=0x%x\n",__FUNCTION__,bRegINT1,bRegINT2);
+}
+
+
+static irqreturn_t tps65185_int(int irq, void *dev_id)
+{
+ DBG_MSG("[%s-%d] tps65185 interrupt triggered !!!\n",__func__,__LINE__);
+ queue_work(tps65185_int_workqueue,&tps65185_int_work);
+
+ return 0;
+}
+
+
+#ifdef TPS65185_PWR_ONOFF_WAITBYCOMPLETE//[
+
+DECLARE_COMPLETION(tps65185_pwrgood_on_completion);
+DECLARE_COMPLETION(tps65185_pwrgood_off_completion);
+#else //][!TPS65185_PWR_ONOFF_WAITBYCOMPLETE
+DECLARE_WAIT_QUEUE_HEAD(tps65185_pwron_wq);
+DECLARE_WAIT_QUEUE_HEAD(tps65185_pwroff_wq);
+#endif //] TPS65185_PWR_ONOFF_WAITBYCOMPLETE
+
+static struct work_struct tps65185_pwrgood_work;
+static struct workqueue_struct *tps65185_pwrgood_workqueue;
+volatile int giIsTPS65185_PwrOn=0;
+static void tps65185_pwrgood_func(struct work_struct *work)
+{
+ int iIsPwrOn;
+
+ #if 0
+ {
+ unsigned char bRegPG;
+ unsigned short wReg;
+
+ wReg = TPS65185_REG_GET(PG);
+ if(((unsigned short)(-1))==wReg) {
+ ERR_MSG("%s(%d):%s regPG read fail !\n",__FILE__,__LINE__,__FUNCTION__);
+ }
+ else {
+ bRegPG=(unsigned char)wReg;
+ DBG_MSG("%s() : PG=0x%x \n",__FUNCTION__,bRegPG);
+ }
+ }
+ #endif
+
+ iIsPwrOn=giIsTPS65185_PwrOn=gpio_get_value(GPIO_TPS65185_PWRGOOD);
+ DBG_MSG("%s() : powergood signal=%d \n",__FUNCTION__,iIsPwrOn);
+
+ if(iIsPwrOn) {
+#ifdef TPS65185_PWR_ONOFF_WAITBYCOMPLETE//[
+
+ complete_all(&tps65185_pwrgood_on_completion);
+#else //][!TPS65185_PWR_ONOFF_WAITBYCOMPLETE
+ wake_up_interruptible_all(&tps65185_pwron_wq);
+#endif //] TPS65185_PWR_ONOFF_WAITBYCOMPLETE
+ }
+ else{
+#ifdef TPS65185_PWR_ONOFF_WAITBYCOMPLETE//[
+
+ complete_all(&tps65185_pwrgood_off_completion);
+#else//][!TPS65185_PWR_ONOFF_WAITBYCOMPLETE
+ wake_up_interruptible_all(&tps65185_pwroff_wq);
+#endif//] TPS65185_PWR_ONOFF_WAITBYCOMPLETE
+ }
+}
+
+
+static irqreturn_t tps65185_pwrgood_inthandler(int irq, void *dev_id)
+{
+ DBG_MSG("[%s-%d] tps65185 pwrgood interrupt triggered !!!\n",__func__,__LINE__);
+ //queue_work(tps65185_pwrgood_workqueue,&tps65185_pwrgood_work);
+ //
+ tps65185_pwrgood_func(&tps65185_pwrgood_work);
+ return 0;
+}
+
+static void _tps65185_pwrdwn(void)
+{
+ unsigned long dwCurrentMode,dwNewMode;
+ int iIsWaitPwrOff;
+
+ // parameters setup ...
+ dwCurrentMode = gtTPS65185_DataA[0].dwCurrent_mode;
+ dwNewMode = gtPwrdwn_work_param.dwNewMode ;
+ iIsWaitPwrOff = gtPwrdwn_work_param.iIsWaitPwrOff;
+
+ DBG_MSG("%s : mode=%ld begin\n",__FUNCTION__,dwNewMode);
+
+ ASSERT(giIsTPS65185_inited);
+
+ if(dwCurrentMode==dwNewMode) {
+ DBG_MSG("%s : skip same mode\n",__FUNCTION__);
+ goto exit ;
+ }
+
+ gpio_direction_output(GPIO_TPS65185_PWRUP, 0);
+ if( TPS65185_MODE_ACTIVE==dwCurrentMode) {
+ if(3==gptHWCFG->m_val.bUIConfig) {
+ // MP/RD mode .
+ gdwSafeTick_To_TurnOFF_EP3V3 = jiffies+0;
+ }
+ else {
+ gdwSafeTick_To_TurnOFF_EP3V3 = jiffies+ \
+ gtTPS65185_DataA[0].dwEP3V3_Off_Ticks;
+ }
+ if(iIsWaitPwrOff) {
+ tps65185_wait_panel_poweroff();
+ }
+ }
+
+
+ if( dwNewMode == TPS65185_MODE_SLEEP) {
+ if( giIsTPS65185_turnoff_EP3V3 )
+ {
+ // Turn off EV_3V3 ...
+ //TPS65185_REG_SET(ENABLE,V3P3_EN,0);
+#ifdef GPIO_TPS65185_EP_3V3_IN //[
+ //udelay(10);gpio_direction_output(GPIO_TPS65185_EP_3V3_IN, 0);
+#endif //]GPIO_TPS65185_EP_3V3_IN
+ }
+ gpio_direction_output(GPIO_TPS65185_WAKEUP, 0);
+ }
+
+ gtTPS65185_DataA[0].dwCurrent_mode = dwNewMode;
+
+exit:
+ DBG_MSG("%s : mode=%ld end .\n",__FUNCTION__,dwNewMode);
+}
+
+static void tps65185_pwrdwn_work_func(struct work_struct *work)
+{
+ down(&gtTPS65185_DataA[0].chmod_lock);
+ _tps65185_pwrdwn();
+ up(&gtTPS65185_DataA[0].chmod_lock);
+}
+
+
+
+//
+static int tps65185_gpio_init(void)
+{
+ int iRet=TPS65185_RET_SUCCESS;
+ int iChk;
+ int irq;
+
+ if(giIsTPS65185_gpio_inited) {
+ return TPS65185_RET_SUCCESS;
+ }
+
+ // inputs
+ mxc_iomux_v3_setup_pad(GPIO_TPS65185_PWRGOOD_INT_PADCFG);
+ gpio_request(GPIO_TPS65185_PWRGOOD, "tps65185_PWRGOOD");
+ gpio_direction_input(GPIO_TPS65185_PWRGOOD);
+ //giIsTPS65185_PwrOn=gpio_get_value(GPIO_TPS65185_PWRGOOD);
+
+#ifdef TPS65185_PWR_ONOFF_INT//[
+
+ tps65185_pwrgood_workqueue=create_singlethread_workqueue("tps65185_PWRGOOD");
+ INIT_WORK(&tps65185_pwrgood_work, tps65185_pwrgood_func);
+
+ irq = gpio_to_irq(GPIO_TPS65185_PWRGOOD);
+ set_irq_type(irq, IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING);//
+ //disable_irq_wake(irq);
+#endif //] TPS65185_PWR_ONOFF_INT
+
+
+
+ mxc_iomux_v3_setup_pad(GPIO_TPS65185_INT_PADCFG);
+ gpio_request(GPIO_TPS65185_INT, "tps65185_INT");
+ gpio_direction_input (GPIO_TPS65185_INT);
+
+ tps65185_int_workqueue=create_singlethread_workqueue("tps65185_INT");
+ INIT_WORK(&tps65185_int_work, tps65185_int_func);
+
+ irq = gpio_to_irq(GPIO_TPS65185_INT);
+ set_irq_type(irq, IRQF_TRIGGER_FALLING);//IRQF_TRIGGER_RISING|
+ //disable_irq_wake(irq);
+
+
+ // outputs
+#ifdef GPIO_TPS65185_EP_3V3_IN //[
+ mxc_iomux_v3_setup_pad(GPIO_TPS65185_EP_3V3_IN_PADCFG);
+ gpio_request(GPIO_TPS65185_EP_3V3_IN, "tps65185_EP_3V3_IN");
+ gpio_direction_output(GPIO_TPS65185_EP_3V3_IN, 1);
+#endif //]GPIO_TPS65185_EP_3V3_IN
+ gdwSafeTick_To_TurnOFF_EP3V3 = jiffies;
+
+ mxc_iomux_v3_setup_pad(GPIO_TPS65185_VIN_PADCFG);
+ gpio_request(GPIO_TPS65185_VIN, "tps65185_VIN");
+ gpio_direction_output(GPIO_TPS65185_VIN, 1);
+ mdelay(5);
+ gdwSafeTick_To_TurnON_RailPower = jiffies+4;
+
+
+ mxc_iomux_v3_setup_pad(GPIO_TPS65185_PWRUP_PADCFG);
+ gpio_request(GPIO_TPS65185_PWRUP, "tps65185_PWRUP");
+ gpio_direction_output(GPIO_TPS65185_PWRUP, 0);
+
+ mxc_iomux_v3_setup_pad(GPIO_TPS65185_WAKEUP_PADCFG);
+ gpio_request(GPIO_TPS65185_WAKEUP, "tps65185_WAKEUP");
+ gpio_direction_output(GPIO_TPS65185_WAKEUP, 1);
+ mdelay(2);
+ gtTPS65185_DataA[0].dwCurrent_mode=TPS65185_MODE_STANDBY;
+
+ mxc_iomux_v3_setup_pad(GPIO_TPS65185_VCOMCTRL_PADCFG);
+ gpio_request(GPIO_TPS65185_VCOMCTRL, "tps65185_VCOMCTRL");
+ gpio_direction_output(GPIO_TPS65185_VCOMCTRL, VCOM_DISABLE);
+
+ giIsTPS65185_gpio_inited=1;
+
+ return iRet;
+}
+
+
+static int tps65185_gpio_release(void)
+{
+ int iRet=TPS65185_RET_SUCCESS;
+ int irq;
+
+
+ // release gpios ...
+
+
+#if 0
+#ifdef TPS65185_PWR_ONOFF_INT//[
+
+ irq = gpio_to_irq(GPIO_TPS65185_PWRGOOD);
+ free_irq(irq,0);
+ flush_workqueue(tps65185_pwrgood_workqueue);
+ destroy_workqueue(tps65185_pwrgood_workqueue);
+
+#endif //] TPS65185_PWR_ONOFF_INT
+
+ irq = gpio_to_irq(GPIO_TPS65185_INT);
+ free_irq(irq,0);
+ flush_workqueue(tps65185_int_workqueue);
+ destroy_workqueue(tps65185_int_workqueue);
+
+ if(giIsTPS65185_gpio_inited) {
+ gpio_free(GPIO_TPS65185_PWRGOOD);
+ gpio_free(GPIO_TPS65185_INT);
+ gpio_free(GPIO_TPS65185_VIN);
+ gpio_free(GPIO_TPS65185_PWRUP);
+ gpio_free(GPIO_TPS65185_WAKEUP);
+ gpio_free(GPIO_TPS65185_VCOMCTRL);
+ }
+#endif
+
+ return iRet;
+}
+
+
+
+
+
+
+
+//////////////////////////////////////////////////////////////////////////
+//
+// internal helper ...
+//
+
+
+
+static int tps65185_chk_PG(unsigned char I_bChkMask)
+{
+ int iRet;
+ unsigned short wReg;
+ unsigned char bReg;
+
+
+ wReg = TPS65185_REG_GET(PG);
+
+ if(((unsigned short)(-1))==wReg) {
+ return TPS65185_RET_REGREADFAIL;
+ }
+
+ bReg=(unsigned char)wReg;
+ //bChkMask=0xfa;
+
+ bReg &= ~I_bChkMask;
+ if(I_bChkMask==bReg) {
+ iRet = TPS65185_RET_ALLPOWERGOOD;
+ }
+ else {
+ //printk(KERN_ERR "PG reg=0x%02X,Chk=0x02%X\n",bReg,I_bChkMask);
+ iRet = TPS65185_RET_POWERNOTGOOD;
+ }
+
+ return iRet;
+}
+
+
+
+static int tps65185_get_versions(TPS65185_VERSIONS *O_pt65185ver)
+{
+ int iRet=TPS65185_RET_SUCCESS;
+ int iChk;
+ unsigned short wReg;
+ unsigned char bReg;
+
+ ASSERT(gpI2C_adapter);
+ ASSERT(O_pt65185ver);
+
+ wReg = TPS65185_REG_GET(REVID);
+
+ if(((unsigned short)(-1))==wReg) {
+ return TPS65185_RET_REGREADFAIL;
+ }
+
+ bReg=(unsigned char)wReg;
+ O_pt65185ver->bMajor = (bReg>>6)&0x3;
+ O_pt65185ver->bMinor = (bReg>>4)&0x3;
+ O_pt65185ver->bVersion = (bReg)&0xf;
+ O_pt65185ver->bRevID = bReg;
+
+ return iRet;
+}
+
+
+
+static int tps65185_config_epd_timing(int iEPDTimingType)
+{
+ int iRet=TPS65185_RET_SUCCESS;
+
+
+ return iRet;
+}
+
+static int tps65185_reg_init(int I_iIsEP_3V3_ON)
+{
+ int iRet=TPS65185_RET_SUCCESS;
+ unsigned char bRegVal;
+
+ GALLEN_DBGLOCAL_BEGIN();
+
+ bRegVal = TPS65185_REG_ENABLE_VCOM_EN|TPS65185_REG_ENABLE_VDDH_EN|\
+ TPS65185_REG_ENABLE_VPOS_EN|TPS65185_REG_ENABLE_VEE_EN|TPS65185_REG_ENABLE_VNEG_EN;
+ if(1==I_iIsEP_3V3_ON) {
+ GALLEN_DBGLOCAL_RUNLOG(0);
+ bRegVal |= TPS65185_REG_ENABLE_V3P3_EN;
+ }
+ else if(0==I_iIsEP_3V3_ON) {
+ GALLEN_DBGLOCAL_RUNLOG(5);
+ bRegVal &= ~TPS65185_REG_ENABLE_V3P3_EN;
+ }
+ else if(-1==I_iIsEP_3V3_ON) {
+ GALLEN_DBGLOCAL_RUNLOG(6);
+ if( TPS65185_REG(ENABLE) & TPS65185_REG_ENABLE_V3P3_EN) {
+ bRegVal |= TPS65185_REG_ENABLE_V3P3_EN;
+ }
+ else {
+ bRegVal &= ~TPS65185_REG_ENABLE_V3P3_EN;
+ }
+ }
+
+ DBG_MSG("%s() EP3V3ON=%d,ENABLE REG=>%x\n",__FUNCTION__,I_iIsEP_3V3_ON,bRegVal);
+
+ iRet = TPS65185_REG_SET(ENABLE,ALL,bRegVal);
+ if(iRet<0) {
+ GALLEN_DBGLOCAL_RUNLOG(1);
+ goto error;
+ }
+
+
+ //bRegVal = 0;
+ bRegVal = 0x7f;
+ iRet = TPS65185_REG_SET(INT_EN1,ALL,bRegVal);
+ if(iRet<0) {
+ GALLEN_DBGLOCAL_RUNLOG(2);
+ goto error;
+ }
+
+ bRegVal = 0xff;
+ iRet = TPS65185_REG_SET(INT_EN2,ALL,bRegVal);
+ if(iRet<0) {
+ GALLEN_DBGLOCAL_RUNLOG(3);
+ goto error;
+ }
+
+ bRegVal = gbTPS65185_REG_DWNSEQ0_default;
+ iRet = TPS65185_REG_SET(DWNSEQ0,ALL,bRegVal);
+ if(iRet<0) {
+ GALLEN_DBGLOCAL_RUNLOG(4);
+ goto error;
+ }
+
+error:
+ GALLEN_DBGLOCAL_END();
+ return iRet;
+}
+
+static void tps65185_restore_vcom(void)
+{
+ if(gtTPS65185_DataA[0].iRestoreVCOM) {
+ int iRestoreVCOM = gtTPS65185_DataA[0].iRestoreVCOM;
+ int iCurrentVCOM ;
+
+ gtTPS65185_DataA[0].iRestoreVCOM=0;
+ tps65185_vcom_get(&iCurrentVCOM);
+ printk("restore vcom from %dmV to %dmV\n",iCurrentVCOM,iRestoreVCOM);
+ tps65185_vcom_set(iRestoreVCOM,0);
+ }
+}
+
+
+// auto detect tps65185 .
+// parameters :
+// iPort : i2c channel in system (from 1~3) .
+// iEPDTimingType :
+int tps65185_init(int iPort,int iEPDTimingType)
+{
+ int iRet = TPS65185_RET_SUCCESS;
+ int iChipIdx;
+ unsigned long dw65185mode;
+ int iChk;
+ int irq;
+
+ GALLEN_DBGLOCAL_BEGIN();
+
+ //printk ("%s(%d) \n",__func__,iPort);
+
+ if(0!=giIsTPS65185_inited) {
+ WARNING_MSG("%s(%d):skip ps65185 init twice !\n",__FILE__,__LINE__);
+ return TPS65185_RET_SUCCESS;
+ }
+
+ iChk = tps65185_gpio_init();
+ if(iChk<0) {
+ ERR_MSG("[Error] %s : gpio init fail !\n",__FUNCTION__);
+ GALLEN_DBGLOCAL_ESC();
+ return iChk;
+ }
+
+ gpI2C_adapter = i2c_get_adapter(iPort-1);//
+ if( NULL == gpI2C_adapter )
+ {
+ ERR_MSG ("[Error] %s : TPS65185_RET_I2CCHN_NOTFOUND\n",__FUNCTION__);
+ GALLEN_DBGLOCAL_ESC();
+ return TPS65185_RET_I2CCHN_NOTFOUND;
+ }
+
+
+ for(iChipIdx=0;iChipIdx<TOTAL_CHIPS;iChipIdx++) {
+ LKDRIVER_DATA_INIT(iChipIdx);
+
+#if 0 //[
+ gpI2C_clientA[iChipIdx] = i2c_new_device(gpI2C_adapter, &gtTPS65185_BIA[iChipIdx]);
+#else //][!
+
+#ifdef TPS65185_PLATFORM_MX6//[
+ gpI2C_clientA[iChipIdx] = \
+ i2c_new_probed_device(gpI2C_adapter, &gtTPS65185_BIA[iChipIdx],gwTPS65185_AddrA,0);
+#else //][!TPS65185_PLATFORM_MX6
+ gpI2C_clientA[iChipIdx] = \
+ i2c_new_probed_device(gpI2C_adapter, &gtTPS65185_BIA[iChipIdx],gwTPS65185_AddrA);
+#endif //]TPS65185_PLATFORM_MX6
+
+#endif//]
+
+ if(NULL == gpI2C_clientA[iChipIdx]) {
+ tps65185_release();
+ GALLEN_DBGLOCAL_ESC();
+ ERR_MSG("[Error] %s : TPS65185_RET_NEWDEVFAIL\n",__FUNCTION__);
+ return TPS65185_RET_NEWDEVFAIL;
+ }
+ printk("client%d@i2c%d ,addr=0x%x,name=%s\n",iChipIdx,iPort,
+ gpI2C_clientA[iChipIdx]->addr,gpI2C_clientA[iChipIdx]->name);
+
+ }
+
+
+ INIT_DELAYED_WORK(&gtPwrdwn_work_param.pwrdwn_work,tps65185_pwrdwn_work_func);
+
+
+ if( gptHWCFG->m_val.bPCB>=34)
+ {
+ // new HW design after E606FXA...
+ giIsTPS65185_turnoff_EP3V3=1;
+ if(5==gptHWCFG->m_val.bDisplayResolution) {
+ // resolution is 1448x1072 ...
+#if 1
+ // measured on Q92 .
+ gtTPS65185_DataA[0].dwEP3V3_Off_Ticks = 800;// times wait VEE to 0V .
+#else
+ gtTPS65185_DataA[0].dwEP3V3_Off_Ticks = 200;// times wait VEE >= -7.4V .
+#endif
+ }
+ else if(1==gptHWCFG->m_val.bDisplayResolution) {
+ // resolution is 1024x758 ...
+ gtTPS65185_DataA[0].dwEP3V3_Off_Ticks = 400;//measured on Q32 .
+ }
+ else if(0==gptHWCFG->m_val.bDisplayResolution) {
+ // resolution is 800x600 ...
+ gtTPS65185_DataA[0].dwEP3V3_Off_Ticks = 650;//measured on Q92 .
+ }
+ else {
+ gtTPS65185_DataA[0].dwEP3V3_Off_Ticks = 0;
+ }
+ }
+ printk("%s(%d): EP_3V3 ON/OFF control enabled !EP3V3 OFF ticks=%d\n",__FILE__,__LINE__,gtTPS65185_DataA[0].dwEP3V3_Off_Ticks);
+ giIsTPS65185_inited=1;
+
+ // change TPS65185 to standby mode .
+ dw65185mode = TPS65185_MODE_STANDBY;
+ iChk = tps65185_chg_mode(&dw65185mode,1);
+ if(iChk<0) {
+ ERR_MSG("[Error] %s : change to standby mode fail !\n",__FUNCTION__);
+ tps65185_release();
+ GALLEN_DBGLOCAL_ESC();
+ return iChk;
+ }
+
+#if 0
+ iChk = tps65185_reg_init(0);
+ if(iChk<0) {
+ ERR_MSG("[Error] %s : tps65185 regs init fail !\n",__FUNCTION__);
+ tps65185_release();
+ GALLEN_DBGLOCAL_ESC();
+ return iChk;
+ }
+#endif
+
+
+ iChk = tps65185_get_versions(&gtTPS65185_DataA[0].t65185_versions);
+ printk("TPS65185 versions : major=0x%x,minor=0x%x,version=0x%x,RevID=0x%x\n",
+ gtTPS65185_DataA[0].t65185_versions.bMajor,
+ gtTPS65185_DataA[0].t65185_versions.bMinor,
+ gtTPS65185_DataA[0].t65185_versions.bVersion,
+ gtTPS65185_DataA[0].t65185_versions.bRevID);
+
+ if(iChk<0) {
+ ERR_MSG("[Error] %s : get TPS65185 version fail !\n",__FUNCTION__);
+ tps65185_release();
+ GALLEN_DBGLOCAL_ESC();
+ return iChk;
+ }
+
+
+ //tps65185_get_temperature(0,0);// test .
+
+
+ //
+ // config registers of TPS65185 ...
+ //
+ iChk = tps65185_config_epd_timing(iEPDTimingType);
+ if(iChk<0) {
+ ERR_MSG("[Error] %s : config EPD timing fail !\n",__FUNCTION__);
+ tps65185_release();
+ GALLEN_DBGLOCAL_ESC();
+ return iChk;
+ }
+ printk ("[%s-%d] gpio_to_irq ()\n",__func__,__LINE__);
+
+#ifdef TPS65185_PWR_ONOFF_INT//[
+
+ irq = gpio_to_irq(GPIO_TPS65185_PWRGOOD);
+ iChk = request_irq(irq, tps65185_pwrgood_inthandler, 0, "tps65185_PWRGOOD", 0);
+ if (iChk) {
+ pr_info("register TPS65185 pwrgood interrupt failed\n");
+ }
+ else {
+ //enable_irq_wake(irq);
+ //disable_irq_wake(irq);
+ //disable_irq(irq);
+ }
+#endif //]TPS65185_PWR_ONOFF_INT
+
+#if 1
+ irq = gpio_to_irq(GPIO_TPS65185_INT);
+ iChk = request_irq(irq, tps65185_int, 0, "tps65185_INT", 0);
+ if (iChk) {
+ pr_info("register TPS65185 interrupt failed\n");
+ }
+ else {
+ //enable_irq_wake(irq);
+ //disable_irq_wake(irq);
+ //disable_irq(irq);
+ }
+#endif
+
+
+ /*
+ dw65185mode = TPS65185_MODE_ACTIVE;
+ iChk = tps65185_chg_mode(&dw65185mode,1);
+ if(iChk<0) {
+ ERR_MSG("[Error] %s : change to active mode fail !\n",__FUNCTION__);
+ tps65185_release();
+ GALLEN_DBGLOCAL_ESC();
+ return iChk;
+ }
+ */
+ GALLEN_DBGLOCAL_END();
+ return iRet;
+}
+
+
+
+
+
+int tps65185_release(void)
+{
+ int iRet = TPS65185_RET_SUCCESS;
+ int iChipIdx ;
+ int iChk;
+ unsigned long dw65185mode ;
+
+ GALLEN_DBGLOCAL_BEGIN();
+ //printk("%s(%d):%s()\n",__FILE__,__LINE__,__FUNCTION__);
+ for(iChipIdx=0;iChipIdx<TOTAL_CHIPS;iChipIdx++) {
+ if(gpI2C_clientA[iChipIdx]) {
+ i2c_unregister_device(gpI2C_clientA[iChipIdx]);
+ gpI2C_clientA[iChipIdx] = NULL;
+ }
+ gtTPS65185_DataA[iChipIdx].iCurrent_temprature = -1;
+ //gtTPS65185_DataA[iChipIdx].iLast_temprature = -1;
+ }
+
+ if(giIsTPS65185_inited) {
+ dw65185mode = TPS65185_MODE_SLEEP;
+ iChk = tps65185_chg_mode(&dw65185mode,1);
+ if(iChk<0) {
+ ERR_MSG("[Error] %s : change to power down mode fail !\n",__FUNCTION__);
+ tps65185_release();
+ GALLEN_DBGLOCAL_ESC();
+ return iChk;
+ }
+ // release interrupt ...
+
+ iChk = tps65185_gpio_release();
+ if(iChk<0) {
+ WARNING_MSG("[Warnig] %s : gpio release fail !\n",__FUNCTION__);
+ }
+ }
+
+
+ gpI2C_adapter = NULL;
+
+ GALLEN_DBGLOCAL_END();
+ return iRet;
+}
+
+
+
+
+
+int tps65185_get_temperature(int *O_piTemperature)
+{
+ int iRet = TPS65185_RET_SUCCESS;
+ int iChk;
+
+ unsigned short wTemp,wReg;
+
+ unsigned char bTemp,bReg;
+ int iTemp;
+
+ int iChipIdx = 0;
+
+ unsigned long ulTimeoutTick;
+ unsigned long ulCurTick;
+ unsigned long dw65185mode;
+
+ GALLEN_DBGLOCAL_BEGIN();
+
+ if(TPS65185_MODE_STANDBY!=gtTPS65185_DataA[0].dwCurrent_mode&&TPS65185_MODE_ACTIVE!=gtTPS65185_DataA[0].dwCurrent_mode)
+ {
+ dw65185mode = TPS65185_MODE_STANDBY;
+ iChk = tps65185_chg_mode(&dw65185mode,1);
+ if(iChk<0) {
+ ERR_MSG("[Error] %s : change to standby mode fail !\n",__FUNCTION__);
+ GALLEN_DBGLOCAL_ESC();
+ return iChk;
+ }
+ }
+
+ //printk("%s()\n",__FUNCTION__);
+ iChk = TPS65185_REG_SET(TMST1,READ_THERM,1);
+
+ //udelay(1);
+ ulTimeoutTick = jiffies + 50;
+ do {
+
+ wReg = TPS65185_REG_GET(TMST1);
+ if(((unsigned short)(-1))==wReg) {
+ GALLEN_DBGLOCAL_ESC();
+ return TPS65185_RET_REGREADFAIL;
+ }
+ if(wReg&TPS65185_REG_TMST1_CONV_END) {
+ break;
+ }
+
+ schedule_timeout(1);
+
+ ulCurTick = jiffies;
+ if(ulCurTick>ulTimeoutTick) {
+ ERR_MSG("%s(%d):wait TMST1 ADC timeout \n",__FILE__,__LINE__);
+ GALLEN_DBGLOCAL_ESC();
+ return TPS65185_RET_TIMEOUT;
+ }
+ } while(1);
+
+ wReg = TPS65185_REG_GET(TMST_VALUE);
+
+ if(((unsigned short)(-1))==wReg) {
+ GALLEN_DBGLOCAL_ESC();
+ return TPS65185_RET_REGREADFAIL;
+ }
+
+ bReg = (unsigned char)wReg;
+ gtTPS65185_DataA[iChipIdx].wTempratureData = wReg;
+ if(bReg&0x80) {
+ GALLEN_DBGLOCAL_RUNLOG(0);
+ // negative .
+ bTemp=(~bReg)+1;
+ iTemp = bTemp;
+ iTemp = (~iTemp)+1;
+ }
+ else {
+ GALLEN_DBGLOCAL_RUNLOG(1);
+ // positive .
+ iTemp = (int)(bReg);
+ }
+ gtTPS65185_DataA[iChipIdx].iCurrent_temprature = iTemp;
+ printk("%s temprature data = 0x%x,%d\n",DRIVERNAME,wReg,gtTPS65185_DataA[iChipIdx].iCurrent_temprature);
+
+ //gtTPS65185_DataA[iChipIdx].iCurrent_temprature = bA[0];
+ if(O_piTemperature) {
+ GALLEN_DBGLOCAL_RUNLOG(2);
+ *O_piTemperature = gtTPS65185_DataA[iChipIdx].iCurrent_temprature;
+ }
+
+ GALLEN_DBGLOCAL_END();
+ return iRet;
+}
+
+int tps65185_set_ep3v3_pwrdn_delay_ticks(unsigned long dwTicks)
+{
+ printk("%s(%d)\n",__FUNCTION__,dwTicks);
+ gtTPS65185_DataA[0].dwEP3V3_Off_Ticks=dwTicks;
+ return 0;
+}
+
+int tps65185_is_panel_poweron(void)
+{
+ int iRet;
+ #if 0
+ int iChk;
+
+ iChk = tps65185_chk_PG(0xfa);
+ if(TPS65185_RET_ALLPOWERGOOD==iChk) {
+ iRet = 1;
+ }
+ else {
+ iRet = 0;
+ }
+ #else
+ iRet = gpio_get_value(GPIO_TPS65185_PWRGOOD)?1:0;
+ #endif
+
+ return iRet;
+}
+
+int tps65185_wait_panel_poweron(void)
+{
+#if 0
+ //printk("%s(%d);\n",__FILE__,__LINE__);
+ //msleep(30);
+ return TPS65185_RET_SUCCESS;
+#else
+ int iRet = TPS65185_RET_SUCCESS;
+ int iChk;
+ int ilpcnt=0;
+
+
+ #ifdef TPS65185_PWR_ONOFF_INT//[
+
+ if(in_interrupt()) {
+ printk("%s(%d):[warning] call %s in interrupt !!\n",
+ __FILE__,__LINE__,__FUNCTION__);
+ }
+ else
+ {
+ do {
+ #ifdef TPS65185_PWR_ONOFF_WAITBYCOMPLETE//[
+
+ if(!tps65185_is_panel_poweron()) {
+ ERR_MSG("%s(),before wait PG,jiffies=%ld\n",__FUNCTION__,jiffies);
+ wait_for_completion_timeout(&tps65185_pwrgood_on_completion,500);
+ ERR_MSG("%s(),after wait PG,jiffies=%ld\n",__FUNCTION__,jiffies);
+ }
+
+ #else //][!TPS65185_PWR_ONOFF_WAITBYCOMPLETE
+ iChk = wait_event_interruptible_timeout(tps65185_pwron_wq,tps65185_is_panel_poweron(),50);
+ //iChk = wait_event_timeout(tps65185_pwron_wq,tps65185_is_panel_poweron(),50);
+ #endif//]TPS65185_PWR_ONOFF_WAITBYCOMPLETE
+ if(!tps65185_is_panel_poweron()) {
+ iRet = TPS65185_RET_TIMEOUT;
+ ERR_MSG("%s(%d):wait power on timeout lpcnt=%d!\n",__FILE__,__LINE__,ilpcnt);
+ }
+ else {
+ iRet = TPS65185_RET_SUCCESS;
+ break;
+ }
+
+ } while(++ilpcnt<10);
+ }
+
+ #else //][!TPS65185_PWR_ONOFF_INT
+
+ volatile unsigned long dwCnt=0;
+
+ do {
+ ++dwCnt;
+ if(dwCnt>=100) {
+ ERR_MSG("%s timeout >=%ld !!\n",__FUNCTION__,dwCnt);
+ break;
+ }
+
+ //if(tps65185_is_panel_poweron())
+ if(giIsTPS65185_PwrOn)
+ {
+ DBG_MSG("%s poweron@cnt=%ld\n",__FUNCTION__,dwCnt);
+ break;
+ }
+
+ schedule_timeout(1);
+
+ }while(1) ;
+
+
+ #endif //]TPS65185_PWR_ONOFF_INT
+
+ DBG_MSG("%s:PMIC poweron=%d,%d\n",__FUNCTION__,tps65185_is_panel_poweron(),giIsTPS65185_PwrOn);
+ return iRet;
+#endif
+}
+
+int tps65185_wait_panel_poweroff(void)
+{
+#if 0
+ //schedule_timeout(1);
+ //printk("%s(%d);\n",__FILE__,__LINE__);
+ //msleep(30);
+ return TPS65185_RET_SUCCESS;
+#else
+
+ int iRet = TPS65185_RET_SUCCESS;
+ int iChk;
+ int ilpcnt=0;
+
+
+ #ifdef TPS65185_PWR_ONOFF_INT//[
+
+ if(in_interrupt()) {
+ printk("%s(%d):[warning] call %s in interrupt !!\n",
+ __FILE__,__LINE__,__FUNCTION__);
+ }
+ else
+ {
+ do {
+ #ifdef TPS65185_PWR_ONOFF_WAITBYCOMPLETE//[
+
+ if(tps65185_is_panel_poweron()) {
+ ERR_MSG("%s(),before wait PGoff,jiffies=%ld\n",__FUNCTION__,jiffies);
+ wait_for_completion_timeout(&tps65185_pwrgood_off_completion,500);
+ ERR_MSG("%s(),after wait PGoff,jiffies=%ld\n",__FUNCTION__,jiffies);
+ }
+ #else //][!TPS65185_PWR_ONOFF_WAITBYCOMPLETE
+ iChk = wait_event_interruptible_timeout(tps65185_pwroff_wq,!tps65185_is_panel_poweron(),50);
+ //iChk = wait_event_timeout(tps65185_pwroff_wq,!tps65185_is_panel_poweron(),50);
+ #endif //] TPS65185_PWR_ONOFF_WAITBYCOMPLETE
+ if(tps65185_is_panel_poweron()) {
+ iRet = TPS65185_RET_TIMEOUT;
+ ERR_MSG("%s(%d):wait power off timeout lpcnt=%d!\n",__FILE__,__LINE__,ilpcnt);
+ }
+ else {
+ iRet = TPS65185_RET_SUCCESS;
+ break;
+ }
+
+ }while(++ilpcnt<10);
+
+ }
+
+ #else //][!TPS65185_PWR_ONOFF_INT
+
+ volatile unsigned long dwCnt=0;
+
+ do {
+ ++dwCnt;
+ if(dwCnt>=100) {
+ ERR_MSG("%s timeout >=%ld !!\n",__FUNCTION__,dwCnt);
+ break;
+ }
+ //if(!tps65185_is_panel_poweron())
+ if(!giIsTPS65185_PwrOn)
+ {
+ DBG_MSG("%s poweroff@cnt=%ld\n",__FUNCTION__,dwCnt);
+ break;
+ }
+
+ schedule_timeout(1);
+ }while(1) ;
+
+ #endif //] TPS65185_PWR_ONOFF_INT
+
+ DBG_MSG("%s:PMIC poweron=%d,%d\n",__FUNCTION__,tps65185_is_panel_poweron(),giIsTPS65185_PwrOn);
+ return iRet;
+#endif
+}
+
+
+int tps65185_chg_mode(unsigned long *IO_pdwMode,int iIsWaitPwrOff)
+{
+ int iRet=TPS65185_RET_SUCCESS;
+ int iChk;
+ unsigned long dwCurrent_mode;
+ unsigned long dwNewMode;
+ int iRetryCnt;
+ unsigned long dwCurTick,dwTicks;
+ int iPoweringOn;
+ //int irq_INT,irq_PG;
+
+ GALLEN_DBGLOCAL_BEGIN();
+
+ //int iNewWakeupState,iNewPwrupState;
+
+ //irq_INT = gpio_to_irq(GPIO_TPS65185_INT);
+ //irq_PG = gpio_to_irq(GPIO_TPS65185_PWRGOOD);
+
+ //disable_irq(irq_INT);
+ //disable_irq(irq_PG);
+ if(*IO_pdwMode==TPS65185_MODE_ACTIVE) {
+ tps65185_restore_vcom();
+ }
+
+ down(&gtTPS65185_DataA[0].chmod_lock);
+
+ dwCurrent_mode = gtTPS65185_DataA[0].dwCurrent_mode;
+ dwNewMode = *IO_pdwMode;
+
+ if(0==giIsTPS65185_inited) {
+ ERR_MSG("[Error] %s : tps65185 must be initialized first !\n",__FUNCTION__);
+ GALLEN_DBGLOCAL_ESC();
+ iRet = TPS65185_RET_INITNOTYET;
+ goto exit;
+ }
+
+ if(!IO_pdwMode) {
+ ERR_MSG("[Warning] %s : parameter error !\n",__FUNCTION__);
+ GALLEN_DBGLOCAL_ESC();
+ iRet = TPS65185_RET_PARAMERR;
+ goto exit;
+ }
+
+ if(dwNewMode!=TPS65185_MODE_ACTIVE && dwCurrent_mode == dwNewMode) {
+ DBG_MSG("%s : skip same mode \n",__FUNCTION__);
+ GALLEN_DBGLOCAL_ESC();
+ iRet = TPS65185_RET_SUCCESS;
+ goto exit;
+ }
+
+
+ // change tps65185 work mode ...
+ //gpio_direction_input(GPIO_TPS65185_WAKEUP);
+ //iCurrentWakeupState = gpio_get_value(GPIO_TPS65185_WAKEUP);
+ //gpio_direction_input(GPIO_TPS65185_PWRUP);
+ //iCurrentPwrupState = gpio_get_value(GPIO_TPS65185_PWRUP);
+
+ DBG_MSG("%s begin %ld->%ld\n",__FUNCTION__,dwCurrent_mode,dwNewMode);
+ DBG_MSG("%s():TPS65185 wakeup=%d,pwrup=%d\n",__FUNCTION__,\
+ gpio_get_value(GPIO_TPS65185_WAKEUP),\
+ gpio_get_value(GPIO_TPS65185_PWRUP));
+
+ switch(dwNewMode) {
+ case TPS65185_MODE_ACTIVE:GALLEN_DBGLOCAL_RUNLOG(0);
+ #if 0
+ if( TPS65185_MODE_SLEEP==dwCurrent_mode ||
+ TPS65185_MODE_STANDBY==dwCurrent_mode||
+ TPS65185_MODE_UNKOWN==dwCurrent_mode)
+ {
+ tps65185_wait_panel_poweroff();
+ }
+ #endif
+
+ up(&gtTPS65185_DataA[0].chmod_lock);
+ iChk = cancel_delayed_work_sync(&gtPwrdwn_work_param.pwrdwn_work);
+ down(&gtTPS65185_DataA[0].chmod_lock);
+
+ //iNewWakeupState = 1;
+ //iNewPwrupState = 1;
+
+ if(TPS65185_MODE_SLEEP==dwCurrent_mode || TPS65185_MODE_UNKOWN==dwCurrent_mode) {
+ GALLEN_DBGLOCAL_RUNLOG(3);
+ gpio_direction_output(GPIO_TPS65185_WAKEUP, 1);
+ if(gtTPS65185_DataA[0].iIsInitPwrON) {
+ GALLEN_DBGLOCAL_RUNLOG(2);
+ gtTPS65185_DataA[0].iIsInitPwrON = 0;
+ }
+
+ iRetryCnt = 0;
+ do {
+ mdelay(2);
+ //if(giIsTPS65185_turnoff_EP3V3) {
+ //iRet = tps65185_reg_init(0);
+ //iRet = tps65185_reg_init(-1);
+ iRet = tps65185_reg_init(1);
+ //}
+ //else {
+ // iRet = tps65185_reg_init(1);
+ //}
+
+ if(iRet>=0) {
+ dwCurTick=jiffies;
+ if(time_before(dwCurTick,gdwSafeTick_To_TurnON_RailPower)) {
+ dwTicks = gdwSafeTick_To_TurnON_RailPower-dwCurTick;
+ msleep(jiffies_to_msecs(dwTicks));
+ DBG_MSG("msleep %ld ticks for resume times\n",dwTicks);
+ }
+
+#ifdef GPIO_TPS65185_EP_3V3_IN //[
+ //gpio_direction_output(GPIO_TPS65185_EP_3V3_IN, 1);udelay(30);
+#endif //]GPIO_TPS65185_EP_3V3_IN
+
+ //TPS65185_REG_SET(ENABLE,V3P3_EN,1);udelay(100);
+ gpio_direction_output(GPIO_TPS65185_PWRUP, 1);
+ break;
+ }
+ WARNING_MSG("PMIC sleep->active, retry %d\n",iRetryCnt);
+ } while(++iRetryCnt<10);
+
+ iPoweringOn = 1;
+ }
+ else if(TPS65185_MODE_STANDBY==dwCurrent_mode) {
+ //gpio_direction_output(GPIO_TPS65185_WAKEUP, 1);
+ //gpio_direction_output(GPIO_TPS65185_PWRUP, 0);
+ //ERR_MSG(".");
+ //udelay(1900);
+ if(giIsTPS65185_turnoff_EP3V3) {
+ if(!(TPS65185_REG_GET(ENABLE)&TPS65185_REG_ENABLE_V3P3_EN)) {
+ dwCurTick=jiffies;
+ if(time_before(dwCurTick,gdwSafeTick_To_TurnON_RailPower)) {
+ dwTicks = gdwSafeTick_To_TurnON_RailPower-dwCurTick;
+ msleep(jiffies_to_msecs(dwTicks));
+ DBG_MSG("msleep %ld ticks for resume times\n",dwTicks);
+ }
+
+#ifdef GPIO_TPS65185_EP_3V3_IN //[
+ //gpio_direction_output(GPIO_TPS65185_EP_3V3_IN, 1);udelay(10);
+#endif //]GPIO_TPS65185_EP_3V3_IN
+
+ //TPS65185_REG_SET(ENABLE,V3P3_EN,1);udelay(100);
+ }
+ }
+
+ gpio_direction_output(GPIO_TPS65185_PWRUP, 1);
+ //ERR_MSG(".");
+ iPoweringOn = 1;
+ }
+ else if(TPS65185_MODE_ACTIVE==dwCurrent_mode) {
+ iPoweringOn = 0;
+ }
+ else
+ {
+ GALLEN_DBGLOCAL_RUNLOG(4);
+ ERR_MSG("wrong mode !!? (mode=%ld)\n",dwCurrent_mode);
+ iRet = TPS65185_REG_SET(ENABLE,ACTIVE,1);
+ iPoweringOn = 0;
+ }
+
+ if(iPoweringOn)
+ {
+ //ERR_MSG(".");
+ tps65185_wait_panel_poweron();
+ }
+
+ msleep(5);
+ TPS65185_VCOM_OUT(1);
+ msleep(5);
+ //ERR_MSG(".\n");
+ gtTPS65185_DataA[0].dwCurrent_mode = dwNewMode;
+
+ break;
+
+ case TPS65185_MODE_SLEEP:GALLEN_DBGLOCAL_RUNLOG(5);
+ //iNewWakeupState = 0;
+ //iNewPwrupState = 0;
+
+ gtPwrdwn_work_param.dwNewMode = dwNewMode;
+ gtPwrdwn_work_param.iIsWaitPwrOff = iIsWaitPwrOff;
+
+ if(TPS65185_MODE_ACTIVE==dwCurrent_mode)
+ {
+ msleep(2);TPS65185_VCOM_OUT(0);
+
+#ifdef TPS65185_PWROFFDELAYWORK_TICKS//[
+
+ if(!delayed_work_pending(&gtPwrdwn_work_param.pwrdwn_work)) {
+ GALLEN_DBGLOCAL_RUNLOG(13);
+ up(&gtTPS65185_DataA[0].chmod_lock);
+ iChk = cancel_delayed_work_sync(&gtPwrdwn_work_param.pwrdwn_work);
+ down(&gtTPS65185_DataA[0].chmod_lock);
+ schedule_delayed_work(&gtPwrdwn_work_param.pwrdwn_work, TPS65185_PWROFFDELAYWORK_TICKS);
+ }
+#else//][! TPS65185_PWROFFDELAYWORK_TICKS
+ GALLEN_DBGLOCAL_RUNLOG(14);
+ _tps65185_pwrdwn();
+#endif //] TPS65185_PWROFFDELAYWORK_TICKS
+
+ }
+ else
+ {
+ GALLEN_DBGLOCAL_RUNLOG(15);
+ _tps65185_pwrdwn();
+ }
+
+ break;
+
+ case TPS65185_MODE_STANDBY:GALLEN_DBGLOCAL_RUNLOG(6);
+ //iNewWakeupState = 1;
+ //iNewPwrupState = 0;
+
+ if(TPS65185_MODE_SLEEP==dwCurrent_mode || TPS65185_MODE_UNKOWN==dwCurrent_mode)
+ {
+ //GALLEN_DBGLOCAL_RUNLOG(9);
+ GALLEN_DBGLOCAL_RUNLOG(7);
+
+
+ gpio_direction_output(GPIO_TPS65185_WAKEUP, 1);
+ if(gtTPS65185_DataA[0].iIsInitPwrON) {
+ GALLEN_DBGLOCAL_RUNLOG(8);
+ gtTPS65185_DataA[0].iIsInitPwrON = 0;
+ }
+
+
+ //gpio_direction_output(GPIO_TPS65185_PWRUP, 0);
+ iRetryCnt = 0;
+ do {
+ mdelay(2);
+ //if(giIsTPS65185_turnoff_EP3V3) {
+ //iRet = tps65185_reg_init(-1);
+ iRet = tps65185_reg_init(1);
+ //}
+ //else {
+ //iRet = tps65185_reg_init(1);
+ //}
+ if(iRet>=0) {
+ gtTPS65185_DataA[0].dwCurrent_mode = dwNewMode;
+
+#ifdef TPS65185_RESUME_EP3V3_ON //[
+
+#ifdef GPIO_TPS65185_EP_3V3_IN //[
+ //gpio_direction_output(GPIO_TPS65185_EP_3V3_IN, 1);udelay(10);
+#endif //]GPIO_TPS65185_EP_3V3_IN
+
+ //TPS65185_REG_SET(ENABLE,V3P3_EN,1);udelay(100);
+#endif //]TPS65185_RESUME_EP3V3_ON
+ break;
+ }
+ WARNING_MSG("PMIC sleep->standby, retry %d\n",iRetryCnt);
+ } while(++iRetryCnt<10);
+
+
+ }
+ else if(TPS65185_MODE_ACTIVE==dwCurrent_mode) {
+ msleep(2);TPS65185_VCOM_OUT(0);
+
+ gtPwrdwn_work_param.dwNewMode = dwNewMode;
+ gtPwrdwn_work_param.iIsWaitPwrOff = iIsWaitPwrOff;
+#ifdef TPS65185_PWROFFDELAYWORK_TICKS//[
+
+ if(!delayed_work_pending(&gtPwrdwn_work_param.pwrdwn_work)) {
+ GALLEN_DBGLOCAL_RUNLOG(16);
+ up(&gtTPS65185_DataA[0].chmod_lock);
+ iChk = cancel_delayed_work_sync(&gtPwrdwn_work_param.pwrdwn_work);
+ down(&gtTPS65185_DataA[0].chmod_lock);
+ schedule_delayed_work(&gtPwrdwn_work_param.pwrdwn_work, TPS65185_PWROFFDELAYWORK_TICKS);
+ }
+#else//][! TPS65185_PWROFFDELAYWORK_TICKS
+ GALLEN_DBGLOCAL_RUNLOG(17);
+ _tps65185_pwrdwn();
+#endif //] TPS65185_PWROFFDELAYWORK_TICKS
+
+ }
+ else {
+ GALLEN_DBGLOCAL_RUNLOG(10);
+ ERR_MSG("wrong mode !!? (mode=%ld)\n",dwCurrent_mode);
+ iRet = TPS65185_REG_SET(ENABLE,STANDBY,1);
+ gtTPS65185_DataA[0].dwCurrent_mode = dwNewMode;
+ }
+
+
+ break;
+
+ default:
+ GALLEN_DBGLOCAL_RUNLOG(11);
+ WARNING_MSG("%s : mode unsupported (0x%x) !!\n",__FUNCTION__,
+ (unsigned int)dwNewMode);
+ GALLEN_DBGLOCAL_ESC();
+ iRet = TPS65185_RET_PARAMERR;
+ goto exit;
+ }
+
+ DBG_MSG("%s :[mode] 0x%x->0x%x \n",__FUNCTION__,\
+ (unsigned int)dwCurrent_mode,(unsigned int)dwNewMode);
+
+ //gtTPS65185_DataA[0].iCurrentPwrupState = iCurrentPwrupState;
+ //gtTPS65185_DataA[0].iCurrentWakeupState = iCurrentWakeupState;
+
+ *IO_pdwMode = dwCurrent_mode;
+
+exit:
+
+ DBG_MSG("%s end,mode %lu->%lu \n",__FUNCTION__,dwCurrent_mode,dwNewMode);
+
+ GALLEN_DBGLOCAL_END();
+
+ up(&gtTPS65185_DataA[0].chmod_lock);
+ //enable_irq(irq_PG);
+ //enable_irq(irq_INT);
+
+ return iRet;
+}
+
+
+
+int tps65185_vcom_set(int I_iVCOM_mv,int iIsWriteToFlash)
+{
+ int iRet = TPS65185_RET_SUCCESS;
+ int iChk;
+ unsigned short wVCOM_val;
+ unsigned long dwTPS65185_mode;
+ int iChkVCOM;
+ //
+
+ GALLEN_DBGLOCAL_BEGIN();
+
+ // set VCOM @ VCOM1/VCOM2 register ...
+ if(I_iVCOM_mv>0) {
+ GALLEN_DBGLOCAL_RUNLOG(0);
+ I_iVCOM_mv = 0;
+ wVCOM_val = 0;
+ }
+ else {
+ GALLEN_DBGLOCAL_RUNLOG(1);
+ wVCOM_val = (unsigned short)((-I_iVCOM_mv)/10);
+ }
+
+ // TPS65185 should be in Standby or Active mode .
+ dwTPS65185_mode = TPS65185_MODE_STANDBY;
+ iChk = tps65185_chg_mode(&dwTPS65185_mode,1);
+
+ //printk(KERN_ERR"====>[wVCOM_val]:%x\n",wVCOM_val);
+ if(wVCOM_val&0x100) {
+ GALLEN_DBGLOCAL_RUNLOG(2);
+ TPS65185_REG_SET(VCOM2,VCOM8,1);
+ }
+ else {
+ TPS65185_REG_SET(VCOM2,VCOM8,0);
+ }
+ TPS65185_REG_SET(VCOM1,ALL,(unsigned char)wVCOM_val);
+
+ if(iIsWriteToFlash) {
+#if 1//[
+
+ GALLEN_DBGLOCAL_RUNLOG(3);
+ TPS65185_REG_SET(INT_EN1,PRGC_EN,1);
+ TPS65185_REG_SET(VCOM2,PROG,1);
+ iChk = wait_event_timeout(tps65185_PRGC_WQ,
+ TPS65185_REG(INT1)&TPS65185_REG_INT1_PRGC,100);
+ if(!(TPS65185_REG(INT1)&TPS65185_REG_INT1_PRGC)) {
+ iRet = TPS65185_RET_TIMEOUT;
+ ERR_MSG("%s(%d):wait TPS65185 PRGC timeout !\n",__FILE__,__LINE__);
+ }
+#else//!][
+ {
+ unsigned long ulTimeoutTick,ulCurTick;
+ unsigned short wReg;
+
+ GALLEN_DBGLOCAL_RUNLOG(4);
+ //printk("%s()\n",__FUNCTION__);
+ iChk = TPS65185_REG_SET(INT_EN1,PRGC_EN,1);
+ iChk = TPS65185_REG_SET(VCOM2,PROG,1);
+
+ //udelay(1);
+ ulTimeoutTick = jiffies + 100;
+ do {
+
+ wReg = TPS65185_REG(INT1);
+ if(((unsigned short)(-1))==wReg) {
+ GALLEN_DBGLOCAL_ESC();
+ return TPS65185_RET_REGREADFAIL;
+ }
+ if(wReg&TPS65185_REG_INT1_PRGC) {
+ GALLEN_DBGLOCAL_RUNLOG(5);
+ break;
+ }
+
+
+ GALLEN_DBGLOCAL_RUNLOG(6);
+ schedule_timeout(1);
+
+ ulCurTick = jiffies;
+ if(ulCurTick>ulTimeoutTick) {
+
+ ERR_MSG("%s(%d):wait TMST1 ADC timeout ,wReg=0x%x\n",
+ __FILE__,__LINE__,wReg);
+
+ GALLEN_DBGLOCAL_ESC();
+ return TPS65185_RET_TIMEOUT;
+ }
+
+ } while(1);
+ }
+#endif//]
+
+#if 1
+ dwTPS65185_mode = TPS65185_MODE_SLEEP;
+ iChk = tps65185_chg_mode(&dwTPS65185_mode,1);
+
+ dwTPS65185_mode = TPS65185_MODE_STANDBY;
+ iChk = tps65185_chg_mode(&dwTPS65185_mode,1);
+
+ iChk = tps65185_vcom_get(&iChkVCOM);
+ //printk("%s:iChkVCOM = %d mV\n",__FUNCTION__,iChkVCOM);
+ if(iChkVCOM!=(I_iVCOM_mv/10*10)) {
+
+ ERR_MSG("%s(%d):VCOM check fail (%d!=%d)!\n",
+ __FILE__,__LINE__,iChk,I_iVCOM_mv);
+
+ GALLEN_DBGLOCAL_ESC();
+ return TPS65185_RET_VCOMWRFAIL;
+ }
+#endif
+
+ }
+ else {
+ iChk = tps65185_vcom_get(&iChkVCOM);
+ }
+ GALLEN_DBGLOCAL_END();
+
+ return iRet;
+}
+int tps65185_vcom_get(int *O_piVCOM_mv)
+{
+ int iRet = TPS65185_RET_SUCCESS;
+
+ unsigned char bRegVCOM1,bRegVCOM2;
+ unsigned short wTemp;
+ int iTemp;
+
+
+ bRegVCOM2 = TPS65185_REG_GET(VCOM2);
+ bRegVCOM1 = TPS65185_REG_GET(VCOM1);
+ wTemp = (bRegVCOM1|bRegVCOM2<<8)&0x1ff;
+ iTemp = -(wTemp*10);
+ gtTPS65185_DataA[0].iCurrentVCOM = iTemp;
+ //printk("%s():VCOM=%d\n",__FUNCTION__,iTemp);
+ if(O_piVCOM_mv) {
+ *O_piVCOM_mv = iTemp;
+ }
+ else {
+ iRet = TPS65185_RET_PARAMERR;
+ }
+
+ return iRet;
+}
+
+int tps65185_vcom_get_cached(int *O_piVCOM_mv)
+{
+ int iRet = TPS65185_RET_SUCCESS;
+ if(O_piVCOM_mv) {
+ if(0!=gtTPS65185_DataA[0].iCurrentVCOM) {
+ *O_piVCOM_mv = gtTPS65185_DataA[0].iCurrentVCOM;
+ }
+ else {
+ return tps65185_vcom_get(O_piVCOM_mv);
+ }
+ }
+ return iRet;
+}
+
+int tps65185_vcom_kickback_measurement(int *O_piVCOM_mv)
+{
+ int iRet = TPS65185_RET_SUCCESS;
+ int iChk;
+
+ GALLEN_DBGLOCAL_BEGIN();
+
+ if(O_piVCOM_mv) {
+ unsigned long dwTPS65185_mode;
+
+
+ // Pull the WAKEUP pin and the PWRUP pin to enable all output rails .
+ dwTPS65185_mode = TPS65185_MODE_ACTIVE;
+ iChk = tps65185_chg_mode(&dwTPS65185_mode,1);
+ // Set the HiZ bit in the VCOM2 register. This puts the VCOM pin in high-impedance state .
+ TPS65185_REG_SET(VCOM2,HiZ,1);
+
+ // Drive the panel with the Null waveform.
+
+
+ // Set ACQ bt in the VCOM2 register to 1. This starts the mesurement routine .
+ TPS65185_REG_SET(VCOM2,ACQ,1);
+
+ // When the measurement is complete, the ACQC (Acquisition Complete)
+ // bit in the INT1 register is set and the nINT pin is pulled low .
+
+ iChk = wait_event_timeout(tps65185_ACQC_WQ,
+ TPS65185_REG(INT1)&TPS65185_REG_INT1_ACQC,50);
+ if(!(TPS65185_REG(INT1)&TPS65185_REG_INT1_ACQC)) {
+ iRet = TPS65185_RET_TIMEOUT;
+ ERR_MSG("%s(%d):wait TPS65185 ACQC timeout !\n",__FILE__,__LINE__);
+ }
+
+ // The measurement result is stored in the VCOM[8:0] bits of the VCOM1 and VCOM2 register .
+ iRet = tps65185_vcom_get(O_piVCOM_mv);
+
+ }
+ else {
+ iRet = TPS65185_RET_PARAMERR;
+ }
+
+ GALLEN_DBGLOCAL_END();
+ return iRet;
+}
+
+
+int tps65185_ONOFF(int iIsON)
+{
+#ifdef TPS65185_SUSPEND //[
+ int iRet=0;
+
+ if(!giIsTPS65185_turnoff_EP3V3) {
+ return 1;
+ }
+
+ if(!(6==gptHWCFG->m_val.bDisplayCtrl||7==gptHWCFG->m_val.bDisplayCtrl)) {
+ WARNING_MSG("%s() display controller (%d) not match !\n",
+ __FUNCTION__,(int)gptHWCFG->m_val.bDisplayCtrl);
+ return 1;
+ }
+
+ //printk("%s(%d)\n",__FUNCTION__,iIsON);
+
+ if(iIsON) {
+ // Turn on the TPS65185 power .
+
+ //gpio_free(GPIO_TPS65185_SDL);
+ //gpio_free(GPIO_TPS65185_SDA);
+
+ //mxc_iomux_v3_setup_pad(MX50_PAD_I2C2_SCL__I2C2_SCL);
+ //mxc_iomux_v3_setup_pad(MX50_PAD_I2C2_SDA__I2C2_SDA);
+
+ //TPS65185_VCOM_OUT(1);
+
+#ifdef GPIO_TPS65185_EP_3V3_IN //[
+ gpio_direction_output(GPIO_TPS65185_EP_3V3_IN, 1);
+#endif //]GPIO_TPS65185_EP_3V3_IN
+
+ gpio_direction_output(GPIO_TPS65185_VIN, 1);
+ gdwSafeTick_To_TurnON_RailPower = jiffies+4;
+
+ mxc_iomux_v3_setup_pad(GPIO_TPS65185_PWRGOOD_INT_PADCFG); // POWERGOOD .
+ mxc_iomux_v3_setup_pad(GPIO_TPS65185_INT_PADCFG); // EP_INT
+ gpio_direction_input(GPIO_TPS65185_PWRGOOD);
+ gpio_direction_input(GPIO_TPS65185_INT);
+
+
+ mdelay(5);
+
+ }
+ else {
+
+
+ // Cut off the TPS65185 power .
+ //
+ mxc_iomux_v3_setup_pad(GPIO_TPS65185_PWRGOOD_GPIO_PADCFG); // POWERGOOD
+ mxc_iomux_v3_setup_pad(GPIO_TPS65185_INT_GPIO_PADCFG); // EP_INT
+ //udelay(50);
+ //mxc_iomux_v3_setup_pad(MX50_PAD_I2C2_SCL__GPIO_6_20);
+ //gpio_request(GPIO_TPS65185_SDL, "tps65185_i2c_sdl");
+ //gpio_direction_input (GPIO_TPS65185_SDL);
+ //gpio_direction_output(GPIO_TPS65185_SDL, 1);
+
+ //mxc_iomux_v3_setup_pad(MX50_PAD_I2C2_SDA__GPIO_6_21);
+ //gpio_request(GPIO_TPS65185_SDA, "tps65185_i2c_sda");
+ //gpio_direction_input (GPIO_TPS65185_SDL);
+ //gpio_direction_output(GPIO_TPS65185_SDA, 1);
+
+
+ gpio_direction_output(GPIO_TPS65185_PWRUP, 0);
+ gpio_direction_output(GPIO_TPS65185_WAKEUP, 0);
+
+ gpio_direction_output(GPIO_TPS65185_PWRGOOD, 0);
+ gpio_direction_output(GPIO_TPS65185_INT, 0);
+
+ TPS65185_VCOM_OUT(0);
+
+ gpio_direction_output(GPIO_TPS65185_VIN, 0);
+ gtTPS65185_DataA[0].iRestoreVCOM=gtTPS65185_DataA[0].iCurrentVCOM;
+ gtTPS65185_DataA[0].iCurrentVCOM=0;
+
+ down(&gtTPS65185_DataA[0].chmod_lock);
+ gtTPS65185_DataA[0].dwCurrent_mode=TPS65185_MODE_SLEEP;
+ up(&gtTPS65185_DataA[0].chmod_lock);
+#ifdef GPIO_TPS65185_EP_3V3_IN //[
+ gpio_direction_output(GPIO_TPS65185_EP_3V3_IN, 0);
+#endif //]GPIO_TPS65185_EP_3V3_IN
+ }
+
+ return iRet;
+#else //][!TPS65185_SUSPEND
+ return 0;
+#endif //] TPS65185_SUSPEND
+}
+
+
+
+void tps65185_irqs_enable(int iIsEnable)
+{
+ int irq,iChk;
+
+ if(iIsEnable) {
+#ifdef TPS65185_PWR_ONOFF_INT//[
+ irq = gpio_to_irq(GPIO_TPS65185_INT);
+ #ifdef TPS65185_PLATFORM_MX6//[
+ iChk = request_irq(irq, tps65185_int, 0, "tps65185_INT", 0);
+ if (iChk) {
+ pr_info("register TPS65185 interrupt failed\n");
+ }
+ #else //][!TPS65185_PLATFORM_MX6
+ enable_irq(irq);
+ #endif //] TPS65185_PLATFORM_MX6
+
+
+ irq = gpio_to_irq(GPIO_TPS65185_PWRGOOD);
+ #ifdef TPS65185_PLATFORM_MX6//[
+ iChk = request_irq(irq, tps65185_pwrgood_inthandler, 0, "tps65185_PWRGOOD", 0);
+ if (iChk) {
+ pr_info("register TPS65185 pwrgood interrupt failed\n");
+ }
+ #else //][!TPS65185_PLATFORM_MX6
+ enable_irq(irq);
+ #endif //] TPS65185_PLATFORM_MX6
+#endif //]TPS65185_PWR_ONOFF_INT
+ }
+ else {
+#ifdef TPS65185_PWR_ONOFF_INT//[
+ irq = gpio_to_irq(GPIO_TPS65185_INT);
+ #ifdef TPS65185_PLATFORM_MX6//[
+ free_irq(irq,0);
+ #else //][!TPS65185_PLATFORM_MX6
+ disable_irq(irq);
+ #endif //] TPS65185_PLATFORM_MX6
+
+
+ irq = gpio_to_irq(GPIO_TPS65185_PWRGOOD);
+ #ifdef TPS65185_PLATFORM_MX6//[
+ free_irq(irq,0);
+ #else //][!TPS65185_PLATFORM_MX6
+ disable_irq(irq);
+ #endif //] TPS65185_PLATFORM_MX6
+#endif //] TPS65185_PWR_ONOFF_INT
+ }
+}
+
+int tps65185_suspend(void)
+{
+#ifdef TPS65185_SUSPEND //[
+ unsigned long dwTPS65185_mode;
+ unsigned char bVal;
+ int irq;
+ int iRet = 0;
+ unsigned long dwCurTick;
+ int iIsIRQ_Disabled = 0;
+ int iIsChkVEE_Stable=0;
+
+ dbgENTER();
+
+
+ //ERR_MSG(KERN_ERR "%s()\n",__FUNCTION__);
+ if(gSleep_Mode_Suspend) {
+ DBG_MSG("%s:Go to sleep mode\n",__FUNCTION__);
+ //dwTPS65185_mode = TPS65185_MODE_SLEEP; // sleep mode will turn off EP_3V3 .
+ dwTPS65185_mode = TPS65185_MODE_STANDBY;
+ }
+ else {
+ dwTPS65185_mode = TPS65185_MODE_STANDBY;
+ }
+
+ if( TPS65185_MODE_SLEEP == dwTPS65185_mode ) {
+ tps65185_irqs_enable(0);iIsIRQ_Disabled=1;
+ }
+
+ tps65185_chg_mode(&dwTPS65185_mode,1);
+
+ //flush_workqueue(tps65185_pwrgood_workqueue);
+ //flush_workqueue(tps65185_int_workqueue);
+
+ //tps65185_wait_panel_poweroff();
+
+ //bVal=0;
+ //TPS65185_REG_SET(INT_EN1,ALL,bVal);
+ //TPS65185_REG_SET(INT_EN2,ALL,bVal);
+ //TPS65185_REG_SET(ENABLE,ALL,bVal);
+
+ if(delayed_work_pending(&gtPwrdwn_work_param.pwrdwn_work)) {
+ WARNING_MSG("pmic pwrdwn delay work pending !!\n");
+ //flush_delayed_work(&gtPwrdwn_work_param.pwrdwn_work);
+ iRet = -1;goto error_out;
+ }
+
+#if 0
+ if( (1==gptHWCFG->m_val.bPMIC) && (/*PMIC is Ricoh */
+ NTXHWCFG_TST_FLAG(gptHWCFG->m_val.bPCB_Flags,4)/*Panel is designed for low voltage */ ||
+ NTXHWCFG_TST_FLAG(gptHWCFG->m_val.bPCB_Flags,5)/*EPD VDD source is standalone */
+ ) )
+ {
+ if(gSleep_Mode_Suspend) {
+ iIsChkVEE_Stable=1;
+ }
+ else {
+ iIsChkVEE_Stable=0;
+ }
+ }
+ else {
+ iIsChkVEE_Stable=1;
+ }
+#else
+ if(gSleep_Mode_Suspend) {
+ iIsChkVEE_Stable=1;
+ }
+ else {
+ iIsChkVEE_Stable=0;
+ }
+#endif
+ dwCurTick=jiffies;
+ if(iIsChkVEE_Stable && time_before(dwCurTick,gdwSafeTick_To_TurnOFF_EP3V3))
+ {
+ WARNING_MSG("waiting for VEE stable ,please retry suspend later !!!\n");
+ iRet = -2;goto error_out;
+ }
+ else {
+ if(!iIsIRQ_Disabled) {
+ tps65185_irqs_enable(0);iIsIRQ_Disabled=1;
+ }
+ dwTPS65185_mode = TPS65185_MODE_SLEEP;
+ tps65185_chg_mode(&dwTPS65185_mode,1);
+ }
+
+
+ dbgLEAVE();
+ return iRet;
+error_out:
+ if(iIsIRQ_Disabled) {
+ tps65185_irqs_enable(1);
+ }
+ return iRet;
+#else
+ printk("%s() skipped !!\n",__FUNCTION__);
+ return 0;
+#endif //]TPS65185_SUSPEND
+}
+
+void tps65185_shutdown(void)
+{
+ unsigned long dwTPS65185_mode;
+ unsigned long dwCurTick;
+
+#if 0
+ if(!delayed_work_pending(&gtPwrdwn_work_param.pwrdwn_work)) {
+ dwTPS65185_mode = TPS65185_MODE_STANDBY;
+ tps65185_chg_mode(&dwTPS65185_mode,1);
+ }
+#endif
+
+ while (1) {
+ if(delayed_work_pending(&gtPwrdwn_work_param.pwrdwn_work)) {
+ DBG0_MSG("%s() : waiting for TPS65185 pwrdwn !!\n",__FUNCTION__);
+ msleep(100);
+ }
+ else {
+ break;
+ }
+ }
+
+ while (1) {
+ dwCurTick=jiffies;
+ if(time_before(dwCurTick,gdwSafeTick_To_TurnOFF_EP3V3)) {
+ DBG0_MSG("%s() : waiting for VEE stable to turn off EP3V3 .\n",__FUNCTION__);
+ msleep(100);
+ }
+ else {
+ dwTPS65185_mode = TPS65185_MODE_SLEEP;
+ tps65185_chg_mode(&dwTPS65185_mode,1);
+ break;
+ }
+ }
+
+}
+
+void tps65185_resume(void)
+{
+#ifdef TPS65185_SUSPEND //[
+ unsigned long dwTPS65185_mode,dwTPS65185_current_mode;
+
+ dbgENTER();
+ dwTPS65185_current_mode=gtTPS65185_DataA[0].dwCurrent_mode;
+
+ dwTPS65185_mode = TPS65185_MODE_STANDBY;
+ tps65185_chg_mode(&dwTPS65185_mode,1);
+
+ if( TPS65185_MODE_SLEEP == dwTPS65185_current_mode ) {
+ tps65185_irqs_enable(1);
+ }
+
+ //tps65185_reg_init(1);
+
+ dbgLEAVE();
+#else
+ printk("%s() skipped !!\n",__FUNCTION__);
+#endif //]TPS65185_SUSPEND
+}
+
+//EXPORT_SYMBOL(tps65185_init);
+//EXPORT_SYMBOL(tps65185_release);
+//EXPORT_SYMBOL(tps65185_get_temperature);
+
diff --git a/drivers/video/mxc/lk_tps65185.h b/drivers/video/mxc/lk_tps65185.h
new file mode 100644
index 00000000..77d20ec9
--- /dev/null
+++ b/drivers/video/mxc/lk_tps65185.h
@@ -0,0 +1,53 @@
+#ifndef LK_TPS65185_H//[
+#define LK_TPS65185_H
+
+
+#define TPS65185_RET_ALLPOWEROFF (2) // all power is turned off .
+#define TPS65185_RET_ALLPOWERGOOD (1) // all power is good .
+#define TPS65185_RET_SUCCESS (0) // all right .
+#define TPS65185_RET_PARAMERR (-1) // parameter error !
+#define TPS65185_RET_I2CCHN_NOTFOUND (-2) // i2c adapter not found !
+#define TPS65185_RET_NEWDEVFAIL (-3) // register i2c client fail !!
+#define TPS65185_RET_CHIP_NOTFOUND (-4) // hardware chip not found !
+#define TPS65185_RET_I2CTRANS_ERR (-5) // i2c trans error !
+#define TPS65185_RET_INITNOTYET (-6) // should init first !
+#define TPS65185_RET_IOCONFIG_ERR (-7) // gpio configuration error !
+#define TPS65185_RET_REGREADFAIL (-8) // register read fail !
+#define TPS65185_RET_POWERNOTGOOD (-9) // .
+#define TPS65185_RET_TIMEOUT (-10) // .
+#define TPS65185_RET_VCOMWRFAIL (-11) // VCOM write fail .
+
+
+int tps65185_release(void);
+
+#define EPDTIMING_V110 1
+#define EPDTIMING_V220 2
+int tps65185_init(int iPort,int iEPDTimingType);
+
+int tps65185_get_temperature(int *O_piTemperature);
+
+int tps65185_is_panel_poweron(void);
+
+int tps65185_wait_panel_poweron(void);
+int tps65185_wait_panel_poweroff(void);
+
+////////////////////////////////////////////////////////
+// definitions for chg_mode() ...
+#define TPS65185_MODE_UNKOWN 0x00000000
+#define TPS65185_MODE_ACTIVE 0x00000001
+#define TPS65185_MODE_SLEEP 0x00000002
+#define TPS65185_MODE_STANDBY 0x00000004
+int tps65185_chg_mode(unsigned long *IO_pdwMode,int iIsWaitPwrOff);
+
+int tps65185_vcom_set(int I_iVCOM_mv,int iIsWriteToFlash);
+int tps65185_vcom_get(int *O_piVCOM_mv);
+int tps65185_vcom_get_cached(int *O_piVCOM_mv);
+int tps65185_vcom_kickback_measurement(int *O_piVCOM_mv);
+int tps65185_suspend(void);
+void tps65185_resume(void);
+void tps65185_shutdown(void);
+
+int tps65185_ONOFF(int iIsON);
+int tps65185_set_ep3v3_pwrdn_delay_ticks(unsigned long dwTicks);
+
+#endif //]LK_TPS65185_H
diff --git a/drivers/video/mxc/mipi_dsi.c b/drivers/video/mxc/mipi_dsi.c
new file mode 100644
index 00000000..73c2dda0
--- /dev/null
+++ b/drivers/video/mxc/mipi_dsi.c
@@ -0,0 +1,1010 @@
+/*
+ * Copyright (C) 2011-2012 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/io.h>
+#include <linux/bitops.h>
+#include <linux/ipu.h>
+#include <linux/mxcfb.h>
+#include <linux/regulator/consumer.h>
+#include <linux/backlight.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/fsl_devices.h>
+#include <video/mipi_display.h>
+
+#include <mach/hardware.h>
+#include <mach/clock.h>
+#include <mach/mipi_dsi.h>
+
+#include "mxc_dispdrv.h"
+#include "mipi_dsi.h"
+
+#define DISPDRV_MIPI "mipi_dsi"
+#define ROUND_UP(x) ((x)+1)
+#define NS2PS_RATIO (1000)
+#define NUMBER_OF_CHUNKS (0x8)
+#define NULL_PKT_SIZE (0x8)
+#define PHY_BTA_MAXTIME (0xd00)
+#define PHY_LP2HS_MAXTIME (0x40)
+#define PHY_HS2LP_MAXTIME (0x40)
+#define PHY_STOP_WAIT_TIME (0x20)
+#define DSI_CLKMGR_CFG_CLK_DIV (0x107)
+#define DSI_GEN_PLD_DATA_BUF_ENTRY (0x10)
+#define MIPI_MUX_CTRL(v) (((v) & 0x3) << 4)
+#define IOMUXC_GPR3_OFFSET (0xc)
+#define MIPI_LCD_SLEEP_MODE_DELAY (120)
+#define MIPI_DSI_REG_RW_TIMEOUT (20)
+#define MIPI_DSI_PHY_TIMEOUT (10)
+
+static struct mipi_dsi_match_lcd mipi_dsi_lcd_db[] = {
+#ifdef CONFIG_FB_MXC_TRULY_WVGA_SYNC_PANEL
+ {
+ "TRULY-WVGA",
+ {mipid_hx8369_get_lcd_videomode, mipid_hx8369_lcd_setup}
+ },
+#endif
+ {
+ "", {NULL, NULL}
+ }
+};
+
+struct _mipi_dsi_phy_pll_clk {
+ u32 max_phy_clk;
+ u32 config;
+};
+
+/* configure data for DPHY PLL 27M reference clk out */
+static const struct _mipi_dsi_phy_pll_clk mipi_dsi_phy_pll_clk_table[] = {
+ {1000, 0x74}, /* 950-1000MHz */
+ {950, 0x54}, /* 900-950Mhz */
+ {900, 0x34}, /* 850-900Mhz */
+ {850, 0x14}, /* 800-850MHz */
+ {800, 0x32}, /* 750-800MHz */
+ {750, 0x12}, /* 700-750Mhz */
+ {700, 0x30}, /* 650-700Mhz */
+ {650, 0x10}, /* 600-650MHz */
+ {600, 0x2e}, /* 550-600MHz */
+ {550, 0x0e}, /* 500-550Mhz */
+ {500, 0x2c}, /* 450-500Mhz */
+ {450, 0x0c}, /* 400-450MHz */
+ {400, 0x4a}, /* 360-400MHz */
+ {360, 0x2a}, /* 330-360Mhz */
+ {330, 0x48}, /* 300-330Mhz */
+ {300, 0x28}, /* 270-300MHz */
+ {270, 0x08}, /* 250-270MHz */
+ {250, 0x46}, /* 240-250Mhz */
+ {240, 0x26}, /* 210-240Mhz */
+ {210, 0x06}, /* 200-210MHz */
+ {200, 0x44}, /* 180-200MHz */
+ {180, 0x24}, /* 160-180MHz */
+ {160, 0x04}, /* 150-160MHz */
+};
+
+static int valid_mode(int pixel_fmt)
+{
+ return ((pixel_fmt == IPU_PIX_FMT_RGB24) ||
+ (pixel_fmt == IPU_PIX_FMT_BGR24) ||
+ (pixel_fmt == IPU_PIX_FMT_RGB666) ||
+ (pixel_fmt == IPU_PIX_FMT_RGB565) ||
+ (pixel_fmt == IPU_PIX_FMT_BGR666) ||
+ (pixel_fmt == IPU_PIX_FMT_RGB332));
+}
+
+static inline void mipi_dsi_read_register(struct mipi_dsi_info *mipi_dsi,
+ u32 reg, u32 *val)
+{
+ *val = ioread32(mipi_dsi->mmio_base + reg);
+ dev_dbg(&mipi_dsi->pdev->dev, "read_reg:0x%02x, val:0x%08x.\n",
+ reg, *val);
+}
+
+static inline void mipi_dsi_write_register(struct mipi_dsi_info *mipi_dsi,
+ u32 reg, u32 val)
+{
+ iowrite32(val, mipi_dsi->mmio_base + reg);
+ dev_dbg(&mipi_dsi->pdev->dev, "\t\twrite_reg:0x%02x, val:0x%08x.\n",
+ reg, val);
+}
+
+int mipi_dsi_pkt_write(struct mipi_dsi_info *mipi_dsi,
+ u8 data_type, const u32 *buf, int len)
+{
+ u32 val;
+ u32 status = 0;
+ int write_len = len;
+ uint32_t timeout = 0;
+
+ if (len) {
+ /* generic long write command */
+ while (len / DSI_GEN_PLD_DATA_BUF_SIZE) {
+ mipi_dsi_write_register(mipi_dsi,
+ MIPI_DSI_GEN_PLD_DATA, *buf);
+ buf++;
+ len -= DSI_GEN_PLD_DATA_BUF_SIZE;
+ mipi_dsi_read_register(mipi_dsi,
+ MIPI_DSI_CMD_PKT_STATUS, &status);
+ while ((status & DSI_CMD_PKT_STATUS_GEN_PLD_W_FULL) ==
+ DSI_CMD_PKT_STATUS_GEN_PLD_W_FULL) {
+ msleep(1);
+ timeout++;
+ if (timeout == MIPI_DSI_REG_RW_TIMEOUT)
+ return -EIO;
+ mipi_dsi_read_register(mipi_dsi,
+ MIPI_DSI_CMD_PKT_STATUS, &status);
+ }
+ }
+ /* write the remainder bytes */
+ if (len > 0) {
+ while ((status & DSI_CMD_PKT_STATUS_GEN_PLD_W_FULL) ==
+ DSI_CMD_PKT_STATUS_GEN_PLD_W_FULL) {
+ msleep(1);
+ timeout++;
+ if (timeout == MIPI_DSI_REG_RW_TIMEOUT)
+ return -EIO;
+ mipi_dsi_read_register(mipi_dsi,
+ MIPI_DSI_CMD_PKT_STATUS, &status);
+ }
+ mipi_dsi_write_register(mipi_dsi,
+ MIPI_DSI_GEN_PLD_DATA, *buf);
+ }
+
+ val = data_type | ((write_len & DSI_GEN_HDR_DATA_MASK)
+ << DSI_GEN_HDR_DATA_SHIFT);
+ } else {
+ /* generic short write command */
+ val = data_type | ((*buf & DSI_GEN_HDR_DATA_MASK)
+ << DSI_GEN_HDR_DATA_SHIFT);
+ }
+
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_CMD_PKT_STATUS, &status);
+ while ((status & DSI_CMD_PKT_STATUS_GEN_CMD_FULL) ==
+ DSI_CMD_PKT_STATUS_GEN_CMD_FULL) {
+ msleep(1);
+ timeout++;
+ if (timeout == MIPI_DSI_REG_RW_TIMEOUT)
+ return -EIO;
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_CMD_PKT_STATUS,
+ &status);
+ }
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_GEN_HDR, val);
+
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_CMD_PKT_STATUS, &status);
+ while (!((status & DSI_CMD_PKT_STATUS_GEN_CMD_EMPTY) ==
+ DSI_CMD_PKT_STATUS_GEN_CMD_EMPTY) ||
+ !((status & DSI_CMD_PKT_STATUS_GEN_PLD_W_EMPTY) ==
+ DSI_CMD_PKT_STATUS_GEN_PLD_W_EMPTY)) {
+ msleep(1);
+ timeout++;
+ if (timeout == MIPI_DSI_REG_RW_TIMEOUT)
+ return -EIO;
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_CMD_PKT_STATUS,
+ &status);
+ }
+
+ return 0;
+}
+
+int mipi_dsi_pkt_read(struct mipi_dsi_info *mipi_dsi,
+ u8 data_type, u32 *buf, int len)
+{
+ u32 val;
+ int read_len = 0;
+ uint32_t timeout = 0;
+
+ if (!len) {
+ mipi_dbg("%s, len = 0 invalid error!\n", __func__);
+ return -EINVAL;
+ }
+
+ val = data_type | ((*buf & DSI_GEN_HDR_DATA_MASK)
+ << DSI_GEN_HDR_DATA_SHIFT);
+ memset(buf, 0, len);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_GEN_HDR, val);
+
+ /* wait for cmd to sent out */
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_CMD_PKT_STATUS, &val);
+ while ((val & DSI_CMD_PKT_STATUS_GEN_RD_CMD_BUSY) !=
+ DSI_CMD_PKT_STATUS_GEN_RD_CMD_BUSY) {
+ msleep(1);
+ timeout++;
+ if (timeout == MIPI_DSI_REG_RW_TIMEOUT)
+ return -EIO;
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_CMD_PKT_STATUS,
+ &val);
+ }
+ /* wait for entire response stroed in FIFO */
+ while ((val & DSI_CMD_PKT_STATUS_GEN_RD_CMD_BUSY) ==
+ DSI_CMD_PKT_STATUS_GEN_RD_CMD_BUSY) {
+ msleep(1);
+ timeout++;
+ if (timeout == MIPI_DSI_REG_RW_TIMEOUT)
+ return -EIO;
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_CMD_PKT_STATUS,
+ &val);
+ }
+
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_CMD_PKT_STATUS, &val);
+ while (!(val & DSI_CMD_PKT_STATUS_GEN_PLD_R_EMPTY)) {
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_GEN_PLD_DATA, buf);
+ read_len += DSI_GEN_PLD_DATA_BUF_SIZE;
+ buf++;
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_CMD_PKT_STATUS,
+ &val);
+ if (read_len == (DSI_GEN_PLD_DATA_BUF_ENTRY *
+ DSI_GEN_PLD_DATA_BUF_SIZE))
+ break;
+ }
+
+ if ((len <= read_len) &&
+ ((len + DSI_GEN_PLD_DATA_BUF_SIZE) >= read_len))
+ return 0;
+ else {
+ dev_err(&mipi_dsi->pdev->dev,
+ "actually read_len:%d != len:%d.\n", read_len, len);
+ return -ERANGE;
+ }
+}
+
+int mipi_dsi_dcs_cmd(struct mipi_dsi_info *mipi_dsi,
+ u8 cmd, const u32 *param, int num)
+{
+ int err = 0;
+ u32 buf[DSI_CMD_BUF_MAXSIZE];
+
+ switch (cmd) {
+ case MIPI_DCS_EXIT_SLEEP_MODE:
+ case MIPI_DCS_ENTER_SLEEP_MODE:
+ case MIPI_DCS_SET_DISPLAY_ON:
+ case MIPI_DCS_SET_DISPLAY_OFF:
+ buf[0] = cmd;
+ err = mipi_dsi_pkt_write(mipi_dsi,
+ MIPI_DSI_DCS_SHORT_WRITE, buf, 0);
+ break;
+
+ default:
+ dev_err(&mipi_dsi->pdev->dev,
+ "MIPI DSI DCS Command:0x%x Not supported!\n", cmd);
+ break;
+ }
+
+ return err;
+}
+
+static void mipi_dsi_dphy_init(struct mipi_dsi_info *mipi_dsi,
+ u32 cmd, u32 data)
+{
+ u32 val;
+ u32 timeout = 0;
+
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_IF_CTRL,
+ DSI_PHY_IF_CTRL_RESET);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PWR_UP, DSI_PWRUP_POWERUP);
+
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_TST_CTRL0, 0);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_TST_CTRL1,
+ (0x10000 | cmd));
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_TST_CTRL0, 2);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_TST_CTRL0, 0);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_TST_CTRL1, (0 | data));
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_TST_CTRL0, 2);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_TST_CTRL0, 0);
+ val = DSI_PHY_RSTZ_EN_CLK | DSI_PHY_RSTZ_DISABLE_RST |
+ DSI_PHY_RSTZ_DISABLE_SHUTDOWN;
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_RSTZ, val);
+
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_PHY_STATUS, &val);
+ while ((val & DSI_PHY_STATUS_LOCK) != DSI_PHY_STATUS_LOCK) {
+ msleep(1);
+ timeout++;
+ if (timeout == MIPI_DSI_PHY_TIMEOUT) {
+ dev_err(&mipi_dsi->pdev->dev,
+ "Error: phy lock timeout!\n");
+ break;
+ }
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_PHY_STATUS, &val);
+ }
+ timeout = 0;
+ while ((val & DSI_PHY_STATUS_STOPSTATE_CLK_LANE) !=
+ DSI_PHY_STATUS_STOPSTATE_CLK_LANE) {
+ msleep(1);
+ timeout++;
+ if (timeout == MIPI_DSI_PHY_TIMEOUT) {
+ dev_err(&mipi_dsi->pdev->dev,
+ "Error: phy lock lane timeout!\n");
+ break;
+ }
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_PHY_STATUS, &val);
+ }
+}
+
+static void mipi_dsi_enable_controller(struct mipi_dsi_info *mipi_dsi,
+ bool init)
+{
+ u32 val;
+ u32 lane_byte_clk_period;
+ struct fb_videomode *mode = mipi_dsi->mode;
+ struct mipi_lcd_config *lcd_config = mipi_dsi->lcd_config;
+
+ if (init) {
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PWR_UP,
+ DSI_PWRUP_RESET);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_RSTZ,
+ DSI_PHY_RSTZ_RST);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_CLKMGR_CFG,
+ DSI_CLKMGR_CFG_CLK_DIV);
+
+ if (!(mode->sync & FB_SYNC_VERT_HIGH_ACT))
+ val = DSI_DPI_CFG_VSYNC_ACT_LOW;
+ if (!(mode->sync & FB_SYNC_HOR_HIGH_ACT))
+ val |= DSI_DPI_CFG_HSYNC_ACT_LOW;
+ if ((mode->sync & FB_SYNC_OE_LOW_ACT))
+ val |= DSI_DPI_CFG_DATAEN_ACT_LOW;
+ if (MIPI_RGB666_LOOSELY == lcd_config->dpi_fmt)
+ val |= DSI_DPI_CFG_EN18LOOSELY;
+ val |= (lcd_config->dpi_fmt & DSI_DPI_CFG_COLORCODE_MASK)
+ << DSI_DPI_CFG_COLORCODE_SHIFT;
+ val |= (lcd_config->virtual_ch & DSI_DPI_CFG_VID_MASK)
+ << DSI_DPI_CFG_VID_SHIFT;
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_DPI_CFG, val);
+
+ val = DSI_PCKHDL_CFG_EN_BTA |
+ DSI_PCKHDL_CFG_EN_ECC_RX |
+ DSI_PCKHDL_CFG_EN_CRC_RX;
+
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PCKHDL_CFG, val);
+
+ val = (mode->xres & DSI_VID_PKT_CFG_VID_PKT_SZ_MASK)
+ << DSI_VID_PKT_CFG_VID_PKT_SZ_SHIFT;
+ val |= (NUMBER_OF_CHUNKS & DSI_VID_PKT_CFG_NUM_CHUNKS_MASK)
+ << DSI_VID_PKT_CFG_NUM_CHUNKS_SHIFT;
+ val |= (NULL_PKT_SIZE & DSI_VID_PKT_CFG_NULL_PKT_SZ_MASK)
+ << DSI_VID_PKT_CFG_NULL_PKT_SZ_SHIFT;
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_VID_PKT_CFG, val);
+
+ /* enable LP mode when TX DCS cmd and enable DSI command mode */
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_CMD_MODE_CFG,
+ MIPI_DSI_CMD_MODE_CFG_EN_LOWPOWER);
+
+ /* mipi lane byte clk period in ns unit */
+ lane_byte_clk_period = NS2PS_RATIO /
+ (lcd_config->max_phy_clk / BITS_PER_BYTE);
+ val = ROUND_UP(mode->hsync_len * mode->pixclock /
+ NS2PS_RATIO / lane_byte_clk_period)
+ << DSI_TME_LINE_CFG_HSA_TIME_SHIFT;
+ val |= ROUND_UP(mode->left_margin * mode->pixclock /
+ NS2PS_RATIO / lane_byte_clk_period)
+ << DSI_TME_LINE_CFG_HBP_TIME_SHIFT;
+ val |= ROUND_UP((mode->left_margin + mode->right_margin +
+ mode->hsync_len + mode->xres) * mode->pixclock
+ / NS2PS_RATIO / lane_byte_clk_period)
+ << DSI_TME_LINE_CFG_HLINE_TIME_SHIFT;
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_TMR_LINE_CFG, val);
+
+ val = ((mode->vsync_len & DSI_VTIMING_CFG_VSA_LINES_MASK)
+ << DSI_VTIMING_CFG_VSA_LINES_SHIFT);
+ val |= ((mode->upper_margin & DSI_VTIMING_CFG_VBP_LINES_MASK)
+ << DSI_VTIMING_CFG_VBP_LINES_SHIFT);
+ val |= ((mode->lower_margin & DSI_VTIMING_CFG_VFP_LINES_MASK)
+ << DSI_VTIMING_CFG_VFP_LINES_SHIFT);
+ val |= ((mode->yres & DSI_VTIMING_CFG_V_ACT_LINES_MASK)
+ << DSI_VTIMING_CFG_V_ACT_LINES_SHIFT);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_VTIMING_CFG, val);
+
+ val = ((PHY_BTA_MAXTIME & DSI_PHY_TMR_CFG_BTA_TIME_MASK)
+ << DSI_PHY_TMR_CFG_BTA_TIME_SHIFT);
+ val |= ((PHY_LP2HS_MAXTIME & DSI_PHY_TMR_CFG_LP2HS_TIME_MASK)
+ << DSI_PHY_TMR_CFG_LP2HS_TIME_SHIFT);
+ val |= ((PHY_HS2LP_MAXTIME & DSI_PHY_TMR_CFG_HS2LP_TIME_MASK)
+ << DSI_PHY_TMR_CFG_HS2LP_TIME_SHIFT);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_TMR_CFG, val);
+
+ val = (((lcd_config->data_lane_num - 1) &
+ DSI_PHY_IF_CFG_N_LANES_MASK)
+ << DSI_PHY_IF_CFG_N_LANES_SHIFT);
+ val |= ((PHY_STOP_WAIT_TIME & DSI_PHY_IF_CFG_WAIT_TIME_MASK)
+ << DSI_PHY_IF_CFG_WAIT_TIME_SHIFT);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_IF_CFG, val);
+
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_ERROR_ST0, &val);
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_ERROR_ST1, &val);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_ERROR_MSK0, 0);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_ERROR_MSK1, 0);
+
+ mipi_dsi_dphy_init(mipi_dsi, DSI_PHY_CLK_INIT_COMMAND,
+ mipi_dsi->dphy_pll_config);
+ } else {
+ mipi_dsi_dphy_init(mipi_dsi, DSI_PHY_CLK_INIT_COMMAND,
+ mipi_dsi->dphy_pll_config);
+ }
+}
+
+static void mipi_dsi_disable_controller(struct mipi_dsi_info *mipi_dsi)
+{
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_IF_CTRL,
+ DSI_PHY_IF_CTRL_RESET);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PWR_UP, DSI_PWRUP_RESET);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_RSTZ, DSI_PHY_RSTZ_RST);
+}
+
+static irqreturn_t mipi_dsi_irq_handler(int irq, void *data)
+{
+ u32 mask0;
+ u32 mask1;
+ u32 status0;
+ u32 status1;
+ struct mipi_dsi_info *mipi_dsi;
+
+ mipi_dsi = (struct mipi_dsi_info *)data;
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_ERROR_ST0, &status0);
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_ERROR_ST1, &status1);
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_ERROR_MSK0, &mask0);
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_ERROR_MSK1, &mask1);
+
+ if ((status0 & (~mask0)) || (status1 & (~mask1))) {
+ dev_err(&mipi_dsi->pdev->dev,
+ "mipi_dsi IRQ status0:0x%x, status1:0x%x!\n",
+ status0, status1);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static inline void mipi_dsi_set_mode(struct mipi_dsi_info *mipi_dsi,
+ bool cmd_mode)
+{
+ u32 val;
+
+ if (cmd_mode) {
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PWR_UP,
+ DSI_PWRUP_RESET);
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_CMD_MODE_CFG, &val);
+ val |= MIPI_DSI_CMD_MODE_CFG_EN_CMD_MODE;
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_CMD_MODE_CFG, val);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_VID_MODE_CFG, 0);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PWR_UP,
+ DSI_PWRUP_POWERUP);
+ } else {
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PWR_UP,
+ DSI_PWRUP_RESET);
+ /* Disable Command mode when tranfering video data */
+ mipi_dsi_read_register(mipi_dsi, MIPI_DSI_CMD_MODE_CFG, &val);
+ val &= ~MIPI_DSI_CMD_MODE_CFG_EN_CMD_MODE;
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_CMD_MODE_CFG, val);
+ val = DSI_VID_MODE_CFG_EN | DSI_VID_MODE_CFG_EN_BURSTMODE |
+ DSI_VID_MODE_CFG_EN_LP_MODE;
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_VID_MODE_CFG, val);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PWR_UP,
+ DSI_PWRUP_POWERUP);
+ mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_IF_CTRL,
+ DSI_PHY_IF_CTRL_TX_REQ_CLK_HS);
+ }
+}
+
+static int mipi_dsi_power_on(struct mxc_dispdrv_handle *disp)
+{
+ int err;
+ struct mipi_dsi_info *mipi_dsi = mxc_dispdrv_getdata(disp);
+
+ if (!mipi_dsi->dsi_power_on) {
+ clk_enable(mipi_dsi->dphy_clk);
+ mipi_dsi_enable_controller(mipi_dsi, false);
+ mipi_dsi_set_mode(mipi_dsi, false);
+ /* host send pclk/hsync/vsync for two frames before sleep-out */
+ msleep((1000/mipi_dsi->mode->refresh + 1) << 1);
+ mipi_dsi_set_mode(mipi_dsi, true);
+ err = mipi_dsi_dcs_cmd(mipi_dsi, MIPI_DCS_EXIT_SLEEP_MODE,
+ NULL, 0);
+ if (err) {
+ dev_err(&mipi_dsi->pdev->dev,
+ "MIPI DSI DCS Command sleep-in error!\n");
+ }
+ msleep(MIPI_LCD_SLEEP_MODE_DELAY);
+ mipi_dsi_set_mode(mipi_dsi, false);
+ mipi_dsi->dsi_power_on = 1;
+ }
+
+ return 0;
+}
+
+void mipi_dsi_power_off(struct mxc_dispdrv_handle *disp)
+{
+ int err;
+ struct mipi_dsi_info *mipi_dsi = mxc_dispdrv_getdata(disp);
+
+ if (mipi_dsi->dsi_power_on) {
+ mipi_dsi_set_mode(mipi_dsi, true);
+ err = mipi_dsi_dcs_cmd(mipi_dsi, MIPI_DCS_ENTER_SLEEP_MODE,
+ NULL, 0);
+ if (err) {
+ dev_err(&mipi_dsi->pdev->dev,
+ "MIPI DSI DCS Command display on error!\n");
+ }
+ /* To allow time for the supply voltages
+ * and clock circuits to stabilize.
+ */
+ msleep(5);
+ /* video stream timing on */
+ mipi_dsi_set_mode(mipi_dsi, false);
+ msleep(MIPI_LCD_SLEEP_MODE_DELAY);
+
+ mipi_dsi_set_mode(mipi_dsi, true);
+ mipi_dsi_disable_controller(mipi_dsi);
+ mipi_dsi->dsi_power_on = 0;
+ clk_disable(mipi_dsi->dphy_clk);
+ }
+}
+
+static int mipi_dsi_lcd_init(struct mipi_dsi_info *mipi_dsi,
+ struct mxc_dispdrv_setting *setting)
+{
+ int err;
+ int size;
+ int i;
+ struct fb_videomode *mipi_lcd_modedb;
+ struct fb_videomode mode;
+ struct device *dev = &mipi_dsi->pdev->dev;
+
+ for (i = 0; i < ARRAY_SIZE(mipi_dsi_lcd_db); i++) {
+ if (!strcmp(mipi_dsi->lcd_panel,
+ mipi_dsi_lcd_db[i].lcd_panel)) {
+ mipi_dsi->lcd_callback =
+ &mipi_dsi_lcd_db[i].lcd_callback;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(mipi_dsi_lcd_db)) {
+ dev_err(dev, "failed to find supported lcd panel.\n");
+ return -EINVAL;
+ }
+ /* get the videomode in the order: cmdline->platform data->driver */
+ mipi_dsi->lcd_callback->get_mipi_lcd_videomode(&mipi_lcd_modedb, &size,
+ &mipi_dsi->lcd_config);
+ err = fb_find_mode(&setting->fbi->var, setting->fbi,
+ setting->dft_mode_str,
+ mipi_lcd_modedb, size, NULL,
+ setting->default_bpp);
+ if (err != 1)
+ fb_videomode_to_var(&setting->fbi->var, mipi_lcd_modedb);
+
+ INIT_LIST_HEAD(&setting->fbi->modelist);
+ for (i = 0; i < size; i++) {
+ fb_var_to_videomode(&mode, &setting->fbi->var);
+ if (fb_mode_is_equal(&mode, mipi_lcd_modedb + i)) {
+ err = fb_add_videomode(mipi_lcd_modedb + i,
+ &setting->fbi->modelist);
+ /* Note: only support fb mode from driver */
+ mipi_dsi->mode = mipi_lcd_modedb + i;
+ break;
+ }
+ }
+ if ((err < 0) || (size == i)) {
+ dev_err(dev, "failed to add videomode.\n");
+ return err;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(mipi_dsi_phy_pll_clk_table); i++) {
+ if (mipi_dsi_phy_pll_clk_table[i].max_phy_clk <
+ mipi_dsi->lcd_config->max_phy_clk)
+ break;
+ }
+ if ((i == ARRAY_SIZE(mipi_dsi_phy_pll_clk_table)) ||
+ (mipi_dsi->lcd_config->max_phy_clk >
+ mipi_dsi_phy_pll_clk_table[0].max_phy_clk)) {
+ dev_err(dev, "failed to find data in"
+ "mipi_dsi_phy_pll_clk_table.\n");
+ return -EINVAL;
+ }
+ mipi_dsi->dphy_pll_config = mipi_dsi_phy_pll_clk_table[--i].config;
+ dev_dbg(dev, "dphy_pll_config:0x%x.\n", mipi_dsi->dphy_pll_config);
+
+ return 0;
+}
+
+int mipi_dsi_enable(struct mxc_dispdrv_handle *disp)
+{
+ int err;
+ struct mipi_dsi_info *mipi_dsi = mxc_dispdrv_getdata(disp);
+
+ if (!mipi_dsi->lcd_inited) {
+ err = clk_enable(mipi_dsi->dphy_clk);
+ if (err)
+ dev_err(&mipi_dsi->pdev->dev,
+ "clk_enable error:%d!\n", err);
+ mipi_dsi_enable_controller(mipi_dsi, true);
+ err = mipi_dsi->lcd_callback->mipi_lcd_setup(
+ mipi_dsi);
+ if (err < 0) {
+ dev_err(&mipi_dsi->pdev->dev,
+ "failed to init mipi lcd.");
+ clk_disable(mipi_dsi->dphy_clk);
+ return err;
+ }
+ mipi_dsi_set_mode(mipi_dsi, false);
+ mipi_dsi->dsi_power_on = 1;
+ mipi_dsi->lcd_inited = 1;
+ }
+ mipi_dsi_power_on(mipi_dsi->disp_mipi);
+
+ return 0;
+}
+
+static int mipi_dsi_disp_init(struct mxc_dispdrv_handle *disp,
+ struct mxc_dispdrv_setting *setting)
+{
+ int err;
+ void __iomem *reg_base;
+ u32 val;
+ char dphy_clk[] = "mipi_pllref_clk";
+ struct resource *res;
+ struct resource *res_irq;
+ struct device *dev;
+ struct mipi_dsi_info *mipi_dsi;
+ struct mipi_dsi_platform_data *pdata;
+
+ mipi_dsi = mxc_dispdrv_getdata(disp);
+ if (IS_ERR(mipi_dsi)) {
+ pr_err("failed to get dispdrv data\n");
+ return -EINVAL;
+ }
+ dev = &mipi_dsi->pdev->dev;
+ pdata = dev->platform_data;
+ if (!pdata) {
+ dev_err(dev, "No platform_data available\n");
+ return -EINVAL;
+ }
+
+ mipi_dsi->lcd_panel = kstrdup(pdata->lcd_panel, GFP_KERNEL);
+ if (!mipi_dsi->lcd_panel)
+ return -ENOMEM;
+
+ if (!valid_mode(setting->if_fmt)) {
+ dev_warn(dev, "Input pixel format not valid"
+ "use default RGB24\n");
+ setting->if_fmt = IPU_PIX_FMT_RGB24;
+ }
+
+ res = platform_get_resource(mipi_dsi->pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "get platform resource 0 error\n");
+ return -ENODEV;
+ }
+
+ res = request_mem_region(res->start, resource_size(res),
+ mipi_dsi->pdev->name);
+ if (!res) {
+ dev_err(dev, "request mem region error\n");
+ return -EBUSY;
+ }
+ mipi_dsi->mmio_base = ioremap(res->start, resource_size(res));
+ if (!mipi_dsi->mmio_base) {
+ dev_err(dev, "Cannot map mipi dsi registers\n");
+ err = -EIO;
+ goto err_ioremap;
+ }
+
+ res = platform_get_resource(mipi_dsi->pdev, IORESOURCE_MEM, 1);
+ if (!res) {
+ dev_err(dev, "get platform resource 1 error\n");
+ err = -ENODEV;
+ goto err_get_res1;
+ }
+
+ reg_base = ioremap(res->start, resource_size(res));
+ if (!reg_base) {
+ dev_err(dev, "Cannot map iomuxc registers\n");
+ err = -EIO;
+ goto err_ioremap_iomuxc;
+ }
+ val = ioread32(reg_base + IOMUXC_GPR3_OFFSET);
+ val |= MIPI_MUX_CTRL((pdata->ipu_id << 1) | (pdata->disp_id));
+ iowrite32(val, reg_base + IOMUXC_GPR3_OFFSET);
+ iounmap(reg_base);
+
+ res_irq = platform_get_resource(mipi_dsi->pdev, IORESOURCE_IRQ, 0);
+ if (!res_irq) {
+ dev_err(dev, "failed to acquire irq resource\n");
+ err = -ENODEV;
+ goto err_get_irq;
+ }
+ mipi_dsi->irq = res_irq->start;
+
+ mipi_dsi->dphy_clk = clk_get(dev, dphy_clk);
+ if (IS_ERR(mipi_dsi->dphy_clk)) {
+ dev_err(dev, "failed to get dphy pll_ref_clk\n");
+ err = PTR_ERR(mipi_dsi->dphy_clk);
+ goto err_clk;
+ }
+
+ dev_dbg(dev, "got resources: regs %p, irq:%d\n",
+ mipi_dsi->mmio_base, mipi_dsi->irq);
+
+ if (pdata->io_regulator) {
+ mipi_dsi->io_regulator = regulator_get(dev,
+ pdata->io_regulator);
+ if (IS_ERR(mipi_dsi->io_regulator)) {
+ dev_err(dev, "failed to get io_regulator\n");
+ err = PTR_ERR(mipi_dsi->io_regulator);
+ goto err_ioreg;
+ }
+ err = regulator_set_voltage(mipi_dsi->io_regulator,
+ pdata->io_volt,
+ pdata->io_volt);
+ if (err < 0) {
+ dev_err(dev, "failed to set io_regulator voltage\n");
+ goto err_corereg;
+ }
+ err = regulator_enable(mipi_dsi->io_regulator);
+ if (err < 0) {
+ dev_err(dev, "failed to enable io_regulator voltage\n");
+ goto err_corereg;
+ }
+ }
+ if (pdata->core_regulator) {
+ mipi_dsi->core_regulator = regulator_get(dev,
+ pdata->core_regulator);
+ if (IS_ERR(mipi_dsi->core_regulator)) {
+ dev_err(dev, "failed to get core_regulator\n");
+ err = PTR_ERR(mipi_dsi->core_regulator);
+ goto err_corereg;
+ }
+ err = regulator_set_voltage(mipi_dsi->core_regulator,
+ pdata->core_volt,
+ pdata->core_volt);
+ if (err < 0) {
+ dev_err(dev, "failed to set core_regulator voltage\n");
+ goto err_analogreg;
+ }
+ err = regulator_enable(mipi_dsi->core_regulator);
+ if (err < 0) {
+ dev_err(dev,
+ "failed to enable core_regulator voltage\n");
+ goto err_analogreg;
+ }
+ }
+ if (pdata->analog_regulator) {
+ mipi_dsi->analog_regulator =
+ regulator_get(dev, pdata->analog_regulator);
+ if (IS_ERR(mipi_dsi->analog_regulator)) {
+ dev_err(dev, "failed to get analog_regulator\n");
+ err = PTR_ERR(mipi_dsi->analog_regulator);
+ goto err_analogreg;
+ }
+ err = regulator_set_voltage(mipi_dsi->analog_regulator,
+ pdata->analog_volt,
+ pdata->analog_volt);
+ if (err < 0) {
+ dev_err(dev,
+ "failed to set analog_regulator voltage\n");
+ goto err_pdata_init;
+ }
+ err = regulator_enable(mipi_dsi->analog_regulator);
+ if (err < 0) {
+ dev_err(dev,
+ "failed to enable analog_regulator voltage\n");
+ goto err_pdata_init;
+ }
+ }
+ if (pdata->lcd_power)
+ pdata->lcd_power(true);
+ if (pdata->backlight_power)
+ pdata->backlight_power(true);
+
+ if (pdata->init) {
+ err = pdata->init(mipi_dsi->pdev);
+ if (err < 0) {
+ dev_err(dev, "failed to do pdata->init()\n");
+ goto err_pdata_init;
+ }
+ }
+ if (pdata->reset)
+ pdata->reset();
+
+ mipi_dsi->ipu_id = pdata->ipu_id;
+ mipi_dsi->disp_id = pdata->disp_id;
+ mipi_dsi->reset = pdata->reset;
+ mipi_dsi->lcd_power = pdata->lcd_power;
+ mipi_dsi->backlight_power = pdata->backlight_power;
+
+ /* ipu selected by platform data setting */
+ setting->dev_id = pdata->ipu_id;
+ setting->disp_id = pdata->disp_id;
+
+ err = request_irq(mipi_dsi->irq, mipi_dsi_irq_handler,
+ 0, "mipi_dsi", mipi_dsi);
+ if (err) {
+ dev_err(dev, "failed to request irq\n");
+ err = -EBUSY;
+ goto err_req_irq;
+ }
+
+ err = mipi_dsi_lcd_init(mipi_dsi, setting);
+ if (err < 0) {
+ dev_err(dev, "failed to init mipi dsi lcd\n");
+ goto err_dsi_lcd;
+ }
+
+ dev_dbg(dev, "MIPI DSI dispdrv inited!\n");
+ return 0;
+
+err_dsi_lcd:
+ free_irq(mipi_dsi->irq, mipi_dsi);
+err_req_irq:
+ if (pdata->exit)
+ pdata->exit(mipi_dsi->pdev);
+err_pdata_init:
+ /* cannot disable analog/io/core regulator, maybe others use it,
+ * according to board design
+ */
+ if (mipi_dsi->analog_regulator)
+ regulator_put(mipi_dsi->analog_regulator);
+err_analogreg:
+ if (mipi_dsi->core_regulator)
+ regulator_put(mipi_dsi->core_regulator);
+err_corereg:
+ if (mipi_dsi->io_regulator)
+ regulator_put(mipi_dsi->io_regulator);
+err_ioreg:
+ clk_put(mipi_dsi->dphy_clk);
+err_clk:
+err_get_irq:
+err_get_res1:
+err_ioremap_iomuxc:
+ iounmap(mipi_dsi->mmio_base);
+err_ioremap:
+ release_mem_region(res->start, resource_size(res));
+
+ return err;
+}
+
+static void mipi_dsi_disp_deinit(struct mxc_dispdrv_handle *disp)
+{
+ struct mipi_dsi_info *mipi_dsi;
+ struct resource *res;
+
+ mipi_dsi = mxc_dispdrv_getdata(disp);
+ res = platform_get_resource(mipi_dsi->pdev, IORESOURCE_MEM, 0);
+
+ disable_irq(mipi_dsi->irq);
+ free_irq(mipi_dsi->irq, mipi_dsi);
+ mipi_dsi_power_off(mipi_dsi->disp_mipi);
+ if (mipi_dsi->bl)
+ backlight_device_unregister(mipi_dsi->bl);
+ if (mipi_dsi->analog_regulator)
+ regulator_put(mipi_dsi->analog_regulator);
+ if (mipi_dsi->core_regulator)
+ regulator_put(mipi_dsi->core_regulator);
+ if (mipi_dsi->io_regulator)
+ regulator_put(mipi_dsi->io_regulator);
+ clk_put(mipi_dsi->dphy_clk);
+ iounmap(mipi_dsi->mmio_base);
+ release_mem_region(res->start, resource_size(res));
+}
+
+static struct mxc_dispdrv_driver mipi_dsi_drv = {
+ .name = DISPDRV_MIPI,
+ .init = mipi_dsi_disp_init,
+ .deinit = mipi_dsi_disp_deinit,
+ .enable = mipi_dsi_enable,
+ .disable = mipi_dsi_power_off,
+};
+
+/**
+ * This function is called by the driver framework to initialize the MIPI DSI
+ * device.
+ *
+ * @param pdev The device structure for the MIPI DSI passed in by the
+ * driver framework.
+ *
+ * @return Returns 0 on success or negative error code on error
+ */
+static int mipi_dsi_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct mipi_dsi_info *mipi_dsi;
+
+ mipi_dsi = kzalloc(sizeof(struct mipi_dsi_info), GFP_KERNEL);
+ if (!mipi_dsi) {
+ ret = -ENOMEM;
+ goto alloc_failed;
+ }
+
+ mipi_dsi->pdev = pdev;
+ mipi_dsi->disp_mipi = mxc_dispdrv_register(&mipi_dsi_drv);
+ if (IS_ERR(mipi_dsi->disp_mipi)) {
+ dev_err(&pdev->dev, "mxc_dispdrv_register error:%ld!\n",
+ PTR_ERR(mipi_dsi->disp_mipi));
+ ret = -ENOMEM;
+ goto register_failed;
+ }
+
+ mxc_dispdrv_setdata(mipi_dsi->disp_mipi, mipi_dsi);
+ dev_set_drvdata(&pdev->dev, mipi_dsi);
+
+ mxc_dispdrv_setdev(mipi_dsi->disp_mipi, &pdev->dev);
+
+ dev_info(&pdev->dev, "i.MX MIPI DSI driver probed\n");
+ return 0;
+
+register_failed:
+ kfree(mipi_dsi);
+alloc_failed:
+ return ret;
+}
+
+static void mipi_dsi_shutdown(struct platform_device *pdev)
+{
+ struct mipi_dsi_info *mipi_dsi = dev_get_drvdata(&pdev->dev);
+
+ mipi_dsi_power_off(mipi_dsi->disp_mipi);
+ if (mipi_dsi->lcd_power)
+ mipi_dsi->lcd_power(false);
+ if (mipi_dsi->backlight_power)
+ mipi_dsi->backlight_power(false);
+}
+
+static int __devexit mipi_dsi_remove(struct platform_device *pdev)
+{
+ struct mipi_dsi_info *mipi_dsi = dev_get_drvdata(&pdev->dev);
+
+ mxc_dispdrv_puthandle(mipi_dsi->disp_mipi);
+ mxc_dispdrv_unregister(mipi_dsi->disp_mipi);
+ kfree(mipi_dsi);
+ dev_set_drvdata(&pdev->dev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver mipi_dsi_driver = {
+ .driver = {
+ .name = "mxc_mipi_dsi",
+ },
+ .probe = mipi_dsi_probe,
+ .remove = __devexit_p(mipi_dsi_remove),
+ .shutdown = mipi_dsi_shutdown,
+};
+
+static int __init mipi_dsi_init(void)
+{
+ int err;
+
+ err = platform_driver_register(&mipi_dsi_driver);
+ if (err) {
+ pr_err("mipi_dsi_driver register failed\n");
+ return -ENODEV;
+ }
+ pr_info("MIPI DSI driver module loaded\n");
+ return 0;
+}
+
+static void __exit mipi_dsi_cleanup(void)
+{
+ platform_driver_unregister(&mipi_dsi_driver);
+}
+
+module_init(mipi_dsi_init);
+module_exit(mipi_dsi_cleanup);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("i.MX MIPI DSI driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/mxc/mipi_dsi.h b/drivers/video/mxc/mipi_dsi.h
new file mode 100644
index 00000000..75a57f41
--- /dev/null
+++ b/drivers/video/mxc/mipi_dsi.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2011-2012 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __MIPI_DSI_H__
+#define __MIPI_DSI_H__
+
+#ifdef DEBUG
+#define mipi_dbg(fmt, ...) printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
+#else
+#define mipi_dbg(fmt, ...)
+#endif
+
+#define DSI_CMD_BUF_MAXSIZE (32)
+
+/* DPI interface pixel color coding map */
+enum mipi_dsi_dpi_fmt {
+ MIPI_RGB565_PACKED = 0,
+ MIPI_RGB565_LOOSELY,
+ MIPI_RGB565_CONFIG3,
+ MIPI_RGB666_PACKED,
+ MIPI_RGB666_LOOSELY,
+ MIPI_RGB888,
+};
+
+struct mipi_lcd_config {
+ u32 virtual_ch;
+ u32 data_lane_num;
+ /* device max DPHY clock in MHz unit */
+ u32 max_phy_clk;
+ enum mipi_dsi_dpi_fmt dpi_fmt;
+};
+
+struct mipi_dsi_info;
+struct mipi_dsi_lcd_callback {
+ /* callback for lcd panel operation */
+ void (*get_mipi_lcd_videomode)(struct fb_videomode **, int *,
+ struct mipi_lcd_config **);
+ int (*mipi_lcd_setup)(struct mipi_dsi_info *);
+
+};
+
+struct mipi_dsi_match_lcd {
+ char *lcd_panel;
+ struct mipi_dsi_lcd_callback lcd_callback;
+};
+
+/* driver private data */
+struct mipi_dsi_info {
+ struct platform_device *pdev;
+ void __iomem *mmio_base;
+ int dsi_power_on;
+ int lcd_inited;
+ u32 dphy_pll_config;
+ int ipu_id;
+ int disp_id;
+ char *lcd_panel;
+ int irq;
+ struct clk *dphy_clk;
+ struct mxc_dispdrv_handle *disp_mipi;
+ struct fb_videomode *mode;
+ struct mipi_lcd_config *lcd_config;
+ /* board related power control */
+ struct backlight_device *bl;
+ struct regulator *io_regulator;
+ struct regulator *core_regulator;
+ struct regulator *analog_regulator;
+ int io_volt;
+ int core_volt;
+ int analog_volt;
+ void (*reset) (void);
+ void (*lcd_power)(int);
+ void (*backlight_power)(int);
+ /* callback for lcd panel operation */
+ struct mipi_dsi_lcd_callback *lcd_callback;
+};
+
+int mipi_dsi_pkt_write(struct mipi_dsi_info *mipi,
+ u8 data_type, const u32 *buf, int len);
+int mipi_dsi_pkt_read(struct mipi_dsi_info *mipi,
+ u8 data_type, u32 *buf, int len);
+int mipi_dsi_dcs_cmd(struct mipi_dsi_info *mipi,
+ u8 cmd, const u32 *param, int num);
+
+#ifdef CONFIG_FB_MXC_TRULY_WVGA_SYNC_PANEL
+void mipid_hx8369_get_lcd_videomode(struct fb_videomode **mode, int *size,
+ struct mipi_lcd_config **data);
+int mipid_hx8369_lcd_setup(struct mipi_dsi_info *);
+#endif
+
+#ifndef CONFIG_FB_MXC_TRULY_WVGA_SYNC_PANEL
+#error "Please configure MIPI LCD panel, we cannot find one!"
+#endif
+
+#endif
diff --git a/drivers/video/mxc/mx2fb.c b/drivers/video/mxc/mx2fb.c
new file mode 100644
index 00000000..0b967712
--- /dev/null
+++ b/drivers/video/mxc/mx2fb.c
@@ -0,0 +1,1349 @@
+/*
+ * Copyright (C) 2004-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @defgroup Framebuffer_MX27 Framebuffer Driver for MX27.
+ */
+
+/*!
+ * @file mx2fb.c
+ *
+ * @brief Frame buffer driver for MX27 ADS.
+ *
+ * @ingroup Framebuffer_MX27
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/mxcfb.h>
+#include <linux/uaccess.h>
+#include <mach/hardware.h>
+
+#include "mx2fb.h"
+
+#define MX2FB_TYPE_BG 0
+#define MX2FB_TYPE_GW 1
+
+extern void gpio_lcdc_active(void);
+extern void gpio_lcdc_inactive(void);
+extern void board_power_lcd(int on);
+
+static char *fb_mode;
+static int fb_enabled;
+static unsigned long default_bpp = 16;
+static ATOMIC_NOTIFIER_HEAD(mx2fb_notifier_list);
+static struct clk *lcdc_clk;
+/*!
+ * @brief Structure containing the MX2 specific framebuffer information.
+ */
+struct mx2fb_info {
+ int type;
+ char *id;
+ int registered;
+ int blank;
+ unsigned long pseudo_palette[16];
+};
+
+/* Framebuffer APIs */
+static int mx2fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info);
+static int mx2fb_set_par(struct fb_info *info);
+static int mx2fb_setcolreg(unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp,
+ struct fb_info *info);
+static int mx2fb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info);
+static int mx2fb_blank(int blank_mode, struct fb_info *info);
+static int mx2fb_ioctl(struct fb_info *info, unsigned int cmd,
+ unsigned long arg);
+
+/* Driver entries */
+int __init mx2fb_init(void);
+void __exit mx2fb_exit(void);
+#ifndef MODULE
+static int __init mx2fb_setup(char *);
+#endif
+
+/* Internal functions */
+static int __init _init_fbinfo(struct fb_info *info,
+ struct platform_device *pdev);
+static int __init _install_fb(struct fb_info *info,
+ struct platform_device *pdev);
+static void __exit _uninstall_fb(struct fb_info *info);
+static int _map_video_memory(struct fb_info *info);
+static void _unmap_video_memory(struct fb_info *info);
+static void _set_fix(struct fb_info *info);
+static void _enable_lcdc(struct fb_info *info);
+static void _disable_lcdc(struct fb_info *info);
+static void _enable_graphic_window(struct fb_info *info);
+static void _disable_graphic_window(struct fb_info *info);
+static void _update_lcdc(struct fb_info *info);
+static void _request_irq(void);
+static void _free_irq(void);
+
+#ifdef CONFIG_PM
+static int mx2fb_suspend(struct platform_device *pdev, pm_message_t state);
+static int mx2fb_resume(struct platform_device *pdev);
+#else
+#define mx2fb_suspend 0
+#define mx2fb_resume 0
+#endif
+
+static int mx2fb_probe(struct platform_device *pdev);
+
+#ifdef CONFIG_FB_MXC_TVOUT
+#include <linux/video_encoder.h>
+/*
+ * FIXME: VGA mode is not defined by video_encoder.h
+ * while FS453 supports VGA output.
+ */
+#ifndef VIDEO_ENCODER_VGA
+#define VIDEO_ENCODER_VGA 32
+#endif
+
+#define MODE_PAL "TV-PAL"
+#define MODE_NTSC "TV-NTSC"
+#define MODE_VGA "TV-VGA"
+
+extern int fs453_ioctl(unsigned int cmd, void *arg);
+#endif
+
+struct mx2fb_info mx2fbi_bg = {
+ .type = MX2FB_TYPE_BG,
+ .id = "DISP0 BG",
+ .registered = 0,
+};
+
+static struct mx2fb_info mx2fbi_gw = {
+ .type = MX2FB_TYPE_GW,
+ .id = "DISP0 FG",
+ .registered = 0,
+};
+
+/*! Current graphic window information */
+static struct fb_gwinfo g_gwinfo = {
+ .enabled = 0,
+ .alpha_value = 255,
+ .ck_enabled = 0,
+ .ck_red = 0,
+ .ck_green = 0,
+ .ck_blue = 0,
+ .xpos = 0,
+ .ypos = 0,
+};
+
+/*!
+ * @brief Framebuffer information structures.
+ * There are up to 3 framebuffers: background, TVout, and graphic window.
+ * If graphic window is configured, it must be the last framebuffer.
+ */
+static struct fb_info mx2fb_info[] = {
+ {.par = &mx2fbi_bg},
+ {.par = &mx2fbi_gw},
+};
+
+/*!
+ * @brief This structure contains pointers to the power management
+ * callback functions.
+ */
+static struct platform_driver mx2fb_driver = {
+ .driver = {
+ .name = "mxc_sdc_fb",
+ .owner = THIS_MODULE,
+ .bus = &platform_bus_type,
+ },
+ .probe = mx2fb_probe,
+ .suspend = mx2fb_suspend,
+ .resume = mx2fb_resume,
+};
+
+/*!
+ * @brief Framebuffer file operations
+ */
+static struct fb_ops mx2fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = mx2fb_check_var,
+ .fb_set_par = mx2fb_set_par,
+ .fb_setcolreg = mx2fb_setcolreg,
+ .fb_blank = mx2fb_blank,
+ .fb_pan_display = mx2fb_pan_display,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_ioctl = mx2fb_ioctl,
+};
+
+/*!
+ * @brief Validates a var passed in.
+ *
+ * @param var Frame buffer variable screen structure
+ * @param info Frame buffer structure that represents a single frame buffer
+ *
+ * @return Negative errno on error, or zero on success.
+ *
+ * Checks to see if the hardware supports the state requested by var passed
+ * in. This function does not alter the hardware state! If the var passed in
+ * is slightly off by what the hardware can support then we alter the var
+ * PASSED in to what we can do. If the hardware doesn't support mode change
+ * a -EINVAL will be returned by the upper layers.
+ *
+ */
+static int mx2fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ unsigned long htotal, vtotal;
+
+ if (var->xres_virtual < var->xres)
+ var->xres_virtual = var->xres;
+ if (var->yres_virtual < var->yres)
+ var->yres_virtual = var->yres;
+
+ if (var->xoffset < 0)
+ var->xoffset = 0;
+
+ if (var->yoffset < 0)
+ var->yoffset = 0;
+
+ if (var->xoffset + info->var.xres > info->var.xres_virtual)
+ var->xoffset = info->var.xres_virtual - info->var.xres;
+
+ if (var->yoffset + info->var.yres > info->var.yres_virtual)
+ var->yoffset = info->var.yres_virtual - info->var.yres;
+
+ if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) &&
+ (var->bits_per_pixel != 16)) {
+ var->bits_per_pixel = default_bpp;
+ }
+
+ switch (var->bits_per_pixel) {
+ case 16:
+ var->red.length = 5;
+ var->red.offset = 11;
+ var->red.msb_right = 0;
+
+ var->green.length = 6;
+ var->green.offset = 5;
+ var->green.msb_right = 0;
+
+ var->blue.length = 5;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ var->transp.msb_right = 0;
+ break;
+ case 24:
+ var->red.length = 8;
+ var->red.offset = 16;
+ var->red.msb_right = 0;
+
+ var->green.length = 8;
+ var->green.offset = 8;
+ var->green.msb_right = 0;
+
+ var->blue.length = 8;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ var->transp.msb_right = 0;
+ break;
+ case 32:
+ var->red.length = 8;
+ var->red.offset = 16;
+ var->red.msb_right = 0;
+
+ var->green.length = 8;
+ var->green.offset = 8;
+ var->green.msb_right = 0;
+
+ var->blue.length = 8;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 8;
+ var->transp.offset = 24;
+ var->transp.msb_right = 0;
+ break;
+ }
+
+ if (var->pixclock < 1000) {
+ htotal = var->xres + var->right_margin + var->hsync_len +
+ var->left_margin;
+ vtotal = var->yres + var->lower_margin + var->vsync_len +
+ var->upper_margin;
+ var->pixclock = (vtotal * htotal * 6UL) / 100UL;
+ var->pixclock = KHZ2PICOS(var->pixclock);
+ dev_dbg(info->device,
+ "pixclock set for 60Hz refresh = %u ps\n",
+ var->pixclock);
+ }
+
+ var->height = -1;
+ var->width = -1;
+ var->grayscale = 0;
+
+ /* Copy nonstd field to/from sync for fbset usage */
+ var->sync |= var->nonstd;
+ var->nonstd |= var->sync;
+
+ return 0;
+}
+
+/*!
+ * @brief Alters the hardware state.
+ *
+ * @param info Frame buffer structure that represents a single frame buffer
+ *
+ * @return Zero on success others on failure
+ *
+ * Using the fb_var_screeninfo in fb_info we set the resolution of this
+ * particular framebuffer. This function alters the fb_fix_screeninfo stored
+ * in fb_info. It doesn't not alter var in fb_info since we are using that
+ * data. This means we depend on the data in var inside fb_info to be
+ * supported by the hardware. mx2fb_check_var is always called before
+ * mx2fb_set_par to ensure this.
+ */
+static int mx2fb_set_par(struct fb_info *info)
+{
+ unsigned long len;
+ struct mx2fb_info *mx2fbi = (struct mx2fb_info *)info->par;
+
+ _set_fix(info);
+
+ len = info->var.yres_virtual * info->fix.line_length;
+ if (len > info->fix.smem_len) {
+ if (info->fix.smem_start)
+ _unmap_video_memory(info);
+
+ /* Memory allocation for framebuffer */
+ if (_map_video_memory(info)) {
+ dev_err(info->device, "Unable to allocate fb memory\n");
+ return -ENOMEM;
+ }
+ }
+
+ _update_lcdc(info);
+ if (info->fbops->fb_blank)
+ info->fbops->fb_blank(mx2fbi->blank, info);
+
+ return 0;
+}
+
+/*!
+ * @brief Sets a color register.
+ *
+ * @param regno Which register in the CLUT we are programming
+ * @param red The red value which can be up to 16 bits wide
+ * @param green The green value which can be up to 16 bits wide
+ * @param blue The blue value which can be up to 16 bits wide.
+ * @param transp If supported the alpha value which can be up to
+ * 16 bits wide.
+ * @param info Frame buffer info structure
+ *
+ * @return Negative errno on error, or zero on success.
+ *
+ * Set a single color register. The values supplied have a 16 bit magnitude
+ * which needs to be scaled in this function for the hardware. Things to take
+ * into consideration are how many color registers, if any, are supported with
+ * the current color visual. With truecolor mode no color palettes are
+ * supported. Here a psuedo palette is created which we store the value in
+ * pseudo_palette in struct fb_info. For pseudocolor mode we have a limited
+ * color palette.
+ */
+static int mx2fb_setcolreg(unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp, struct fb_info *info)
+{
+ int ret = 1;
+
+ /*
+ * If greyscale is true, then we convert the RGB value
+ * to greyscale no matter what visual we are using.
+ */
+ if (info->var.grayscale)
+ red = green = blue = (19595 * red + 38470 * green +
+ 7471 * blue) >> 16;
+ switch (info->fix.visual) {
+ case FB_VISUAL_TRUECOLOR:
+ /*
+ * 16-bit True Colour. We encode the RGB value
+ * according to the RGB bitfield information.
+ */
+ if (regno < 16) {
+ u32 *pal = info->pseudo_palette;
+ u32 v;
+
+#define CNVT_TOHW(val, width) ((((val)<<(width))+0x7FFF-(val))>>16)
+ red = CNVT_TOHW(red, info->var.red.length);
+ green = CNVT_TOHW(green, info->var.green.length);
+ blue = CNVT_TOHW(blue, info->var.blue.length);
+ transp = CNVT_TOHW(transp, info->var.transp.length);
+#undef CNVT_TOHW
+
+ v = (red << info->var.red.offset) |
+ (green << info->var.green.offset) |
+ (blue << info->var.blue.offset) |
+ (transp << info->var.transp.offset);
+
+ pal[regno] = v;
+ ret = 0;
+ }
+ break;
+ case FB_VISUAL_STATIC_PSEUDOCOLOR:
+ case FB_VISUAL_PSEUDOCOLOR:
+ break;
+ }
+
+ return ret;
+}
+
+/*!
+ * @brief Pans the display.
+ *
+ * @param var Frame buffer variable screen structure
+ * @param info Frame buffer structure that represents a single frame buffer
+ *
+ * @return Negative errno on error, or zero on success.
+ *
+ * Pan (or wrap, depending on the `vmode' field) the display using the
+ * 'xoffset' and 'yoffset' fields of the 'var' structure. If the values
+ * don't fit, return -EINVAL.
+ */
+static int mx2fb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ if ((info->var.xoffset == var->xoffset) &&
+ (info->var.yoffset == var->yoffset)) {
+ return 0; /* No change, do nothing */
+ }
+
+ if (var->xoffset < 0 || var->yoffset < 0
+ || var->xoffset + info->var.xres > info->var.xres_virtual
+ || var->yoffset + info->var.yres > info->var.yres_virtual)
+ return -EINVAL;
+
+ info->var.xoffset = var->xoffset;
+ info->var.yoffset = var->yoffset;
+
+ _update_lcdc(info);
+
+ if (var->vmode & FB_VMODE_YWRAP)
+ info->var.vmode |= FB_VMODE_YWRAP;
+ else
+ info->var.vmode &= ~FB_VMODE_YWRAP;
+
+ return 0;
+}
+
+/*!
+ * @brief Blanks the display.
+ *
+ * @param blank_mode The blank mode we want.
+ * @param info Frame buffer structure that represents a single frame buffer
+ *
+ * @return Negative errno on error, or zero on success.
+ *
+ * Blank the screen if blank_mode != 0, else unblank. Return 0 if blanking
+ * succeeded, != 0 if un-/blanking failed.
+ * blank_mode == 2: suspend vsync
+ * blank_mode == 3: suspend hsync
+ * blank_mode == 4: powerdown
+ */
+static int mx2fb_blank(int blank_mode, struct fb_info *info)
+{
+ struct mx2fb_info *mx2fbi = (struct mx2fb_info *)info->par;
+
+ dev_dbg(info->device, "blank mode = %d\n", blank_mode);
+
+ mx2fbi->blank = blank_mode;
+
+ switch (blank_mode) {
+ case FB_BLANK_POWERDOWN:
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_HSYNC_SUSPEND:
+ case FB_BLANK_NORMAL:
+ _disable_lcdc(info);
+ break;
+ case FB_BLANK_UNBLANK:
+ _enable_lcdc(info);
+ break;
+ }
+
+ return 0;
+}
+
+/*!
+ * @brief Ioctl function to support customized ioctl operations.
+ *
+ * @param info Framebuffer structure that represents a single frame buffer
+ * @param cmd The command number
+ * @param arg Argument which depends on cmd
+ *
+ * @return Negative errno on error, or zero on success.
+ */
+static int mx2fb_ioctl(struct fb_info *info, unsigned int cmd,
+ unsigned long arg)
+{
+ struct mx2fb_info *mx2fbi = (struct mx2fb_info *)info->par;
+ struct mx2fb_gbl_alpha ga;
+ struct mx2fb_color_key ck;
+
+ switch (cmd) {
+ case MX2FB_SET_GBL_ALPHA:
+ if (mx2fbi->type != MX2FB_TYPE_GW)
+ return -ENODEV;
+
+ if (!arg)
+ return -EINVAL;
+
+ /* set graphic window information */
+ if (copy_from_user((void *)&ga, (void *)arg, sizeof(ga)))
+ return -EFAULT;
+
+ g_gwinfo.alpha_value = ga.alpha;
+
+ if (g_gwinfo.enabled)
+ _enable_graphic_window(info);
+ else
+ _disable_graphic_window(info);
+ break;
+ case MX2FB_SET_CLR_KEY:
+ if (mx2fbi->type != MX2FB_TYPE_GW)
+ return -ENODEV;
+
+ if (!arg)
+ return -EINVAL;
+
+ /* set graphic window information */
+ if (copy_from_user((void *)&ck, (void *)arg, sizeof(ck)))
+ return -EFAULT;
+
+ g_gwinfo.ck_enabled = ck.enable;
+ g_gwinfo.ck_red = (ck.color_key & 0x003F0000) >> 16;
+ g_gwinfo.ck_green = (ck.color_key & 0x00003F00) >> 8;
+ g_gwinfo.ck_blue = ck.color_key & 0x0000003F;
+
+ if (g_gwinfo.enabled)
+ _enable_graphic_window(info);
+ else
+ _disable_graphic_window(info);
+ break;
+ case FBIOGET_GWINFO:
+ if (mx2fbi->type != MX2FB_TYPE_GW)
+ return -ENODEV;
+
+ if (!arg)
+ return -EINVAL;
+
+ /* get graphic window information */
+ if (copy_to_user((void *)arg, (void *)&g_gwinfo,
+ sizeof(g_gwinfo)))
+ return -EFAULT;
+ break;
+ case FBIOPUT_GWINFO:
+ if (mx2fbi->type != MX2FB_TYPE_GW)
+ return -ENODEV;
+
+ if (!arg)
+ return -EINVAL;
+
+ /* set graphic window information */
+ if (copy_from_user((void *)&g_gwinfo, (void *)arg,
+ sizeof(g_gwinfo)))
+ return -EFAULT;
+
+ if (g_gwinfo.enabled)
+ _enable_graphic_window(info);
+ else
+ _disable_graphic_window(info);
+ break;
+#ifdef CONFIG_FB_MXC_TVOUT
+ case ENCODER_GET_CAPABILITIES:{
+ int ret;
+ struct video_encoder_capability cap;
+
+ if (mx2fbi->type != MX2FB_TYPE_BG)
+ return -ENODEV;
+
+ ret = fs453_ioctl(cmd, &cap);
+ if (ret)
+ return ret;
+
+ if (copy_to_user((void *)arg, &cap, sizeof(cap)))
+ return -EFAULT;
+ break;
+ }
+ case ENCODER_SET_NORM:{
+ int ret;
+ unsigned long mode;
+ char *smode;
+ struct fb_var_screeninfo var;
+
+ if (mx2fbi->type != MX2FB_TYPE_BG)
+ return -ENODEV;
+
+ if (copy_from_user(&mode, (void *)arg, sizeof(mode)))
+ return -EFAULT;
+ ret = fs453_ioctl(cmd, &mode);
+ if (ret)
+ return ret;
+
+ if (mode == VIDEO_ENCODER_PAL)
+ smode = MODE_PAL;
+ else if (mode == VIDEO_ENCODER_NTSC)
+ smode = MODE_NTSC;
+ else
+ smode = MODE_VGA;
+
+ var = info->var;
+ var.nonstd = 0;
+ ret = fb_find_mode(&var, info, smode, mxcfb_modedb,
+ mxcfb_modedb_sz, NULL, default_bpp);
+ /* check for specified mode not found */
+ if ((ret != 1) && (ret != 2))
+ return -ENODEV;
+
+ info->var = var;
+ fb_mode = smode;
+ return mx2fb_set_par(info);
+ }
+ case ENCODER_SET_INPUT:
+ case ENCODER_SET_OUTPUT:
+ case ENCODER_ENABLE_OUTPUT:{
+ unsigned long varg;
+
+ if (mx2fbi->type != MX2FB_TYPE_BG)
+ return -ENODEV;
+
+ if (copy_from_user(&varg, (void *)arg, sizeof(varg)))
+ return -EFAULT;
+ return fs453_ioctl(cmd, &varg);
+ }
+#endif
+ default:
+ dev_dbg(info->device, "Unknown ioctl command (0x%08X)\n", cmd);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*!
+ * @brief Set fixed framebuffer parameters based on variable settings.
+ *
+ * @param info framebuffer information pointer
+ * @return Negative errno on error, or zero on success.
+ */
+static void _set_fix(struct fb_info *info)
+{
+ struct fb_fix_screeninfo *fix = &info->fix;
+ struct fb_var_screeninfo *var = &info->var;
+ struct mx2fb_info *mx2fbi = (struct mx2fb_info *)info->par;
+
+ strncpy(fix->id, mx2fbi->id, strlen(mx2fbi->id));
+ fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->accel = FB_ACCEL_NONE;
+ fix->visual = FB_VISUAL_TRUECOLOR;
+ fix->xpanstep = 1;
+ fix->ypanstep = 1;
+}
+
+/*!
+ * @brief Initialize framebuffer information structure.
+ *
+ * @param info framebuffer information pointer
+ * @param pdev pointer to struct device
+ * @return Negative errno on error, or zero on success.
+ */
+static int __init _init_fbinfo(struct fb_info *info,
+ struct platform_device *pdev)
+{
+ struct mx2fb_info *mx2fbi = (struct mx2fb_info *)info->par;
+
+ info->device = &pdev->dev;
+ info->var.activate = FB_ACTIVATE_NOW;
+ info->fbops = &mx2fb_ops;
+ info->flags = FBINFO_FLAG_DEFAULT;
+ info->pseudo_palette = &mx2fbi->pseudo_palette;
+
+ /* Allocate colormap */
+ fb_alloc_cmap(&info->cmap, 16, 0);
+
+ return 0;
+}
+
+/*!
+ * @brief Install framebuffer into the system.
+ *
+ * @param info framebuffer information pointer
+ * @param pdev pointer to struct device
+ * @return Negative errno on error, or zero on success.
+ */
+static int __init _install_fb(struct fb_info *info,
+ struct platform_device *pdev)
+{
+ struct mx2fb_info *mx2fbi = (struct mx2fb_info *)info->par;
+
+ if (_init_fbinfo(info, pdev))
+ return -EINVAL;
+
+ if (fb_mode == 0)
+ fb_mode = pdev->dev.platform_data;
+
+ if (!fb_find_mode(&info->var, info, fb_mode, mxcfb_modedb,
+ mxcfb_modedb_sz, NULL, default_bpp)) {
+ fb_dealloc_cmap(&info->cmap);
+ return -EBUSY;
+ }
+
+ /* Default Y virtual size is 2x panel size */
+ /* info->var.yres_virtual = info->var.yres << 1; */
+
+ if (mx2fbi->type == MX2FB_TYPE_GW)
+ mx2fbi->blank = FB_BLANK_NORMAL;
+ else
+ mx2fbi->blank = FB_BLANK_UNBLANK;
+
+ if (mx2fb_set_par(info)) {
+ fb_dealloc_cmap(&info->cmap);
+ return -EINVAL;
+ }
+
+ if (register_framebuffer(info) < 0) {
+ _unmap_video_memory(info);
+ fb_dealloc_cmap(&info->cmap);
+ return -EINVAL;
+ }
+
+ mx2fbi->registered = 1;
+ dev_info(info->device, "fb%d: %s fb device registered successfully.\n",
+ info->node, info->fix.id);
+
+ return 0;
+}
+
+/*!
+ * @brief Uninstall framebuffer from the system.
+ *
+ * @param info framebuffer information pointer
+ */
+static void __exit _uninstall_fb(struct fb_info *info)
+{
+ struct mx2fb_info *mx2fbi = (struct mx2fb_info *)info->par;
+
+ if (!mx2fbi->registered)
+ return;
+
+ unregister_framebuffer(info);
+ _unmap_video_memory(info);
+ if (&info->cmap)
+ fb_dealloc_cmap(&info->cmap);
+
+ mx2fbi->registered = 0;
+}
+
+/*!
+ * @brief Allocate memory for framebuffer.
+ *
+ * @param info framebuffer information pointer
+ * @return Negative errno on error, or zero on success.
+ */
+static int _map_video_memory(struct fb_info *info)
+{
+ info->fix.smem_len = info->fix.line_length * info->var.yres_virtual;
+ info->screen_base = dma_alloc_coherent(0,
+ info->fix.smem_len,
+ (dma_addr_t *) &info->fix.smem_start,
+ GFP_DMA | GFP_KERNEL);
+
+ if (info->screen_base == 0) {
+ dev_err(info->device, "Unable to allocate fb memory\n");
+ return -EBUSY;
+ }
+ dev_dbg(info->device, "Allocated fb @ paddr=0x%08lX, size=%d.\n",
+ info->fix.smem_start, info->fix.smem_len);
+
+ info->screen_size = info->fix.smem_len;
+
+ /* Clear the screen */
+ memset((char *)info->screen_base, 0, info->fix.smem_len);
+
+ return 0;
+}
+
+/*!
+ * @brief Release memory for framebuffer.
+ * @param info framebuffer information pointer
+ */
+static void _unmap_video_memory(struct fb_info *info)
+{
+ dma_free_coherent(0, info->fix.smem_len, info->screen_base,
+ (dma_addr_t) info->fix.smem_start);
+
+ info->screen_base = 0;
+ info->fix.smem_start = 0;
+ info->fix.smem_len = 0;
+}
+
+/*!
+ * @brief Enable LCD controller.
+ * @param info framebuffer information pointer
+ */
+static void _enable_lcdc(struct fb_info *info)
+{
+ static int first_enable = 1;
+ struct mx2fb_info *mx2fbi = (struct mx2fb_info *)info->par;
+
+ /*
+ * Graphic window can only be enabled while the HCLK to the LCDC
+ * is disabled. Once enabled it can subsequently be disabled and
+ * enabled without turning off the HCLK.
+ * The graphic window is enabled and then disabled here. So next
+ * time to enable graphic window the HCLK to LCDC does not need
+ * to be disabled, and the flicker (due to disabling of HCLK to
+ * LCDC) is avoided.
+ */
+ if (first_enable) {
+ _enable_graphic_window(info);
+ _disable_graphic_window(info);
+ first_enable = 0;
+ }
+
+ if (mx2fbi->type == MX2FB_TYPE_GW)
+ _enable_graphic_window(info);
+ else if (!fb_enabled) {
+ clk_enable(lcdc_clk);
+ gpio_lcdc_active();
+ board_power_lcd(1);
+ fb_enabled++;
+#ifdef CONFIG_FB_MXC_TVOUT
+ if (fb_mode) {
+ unsigned long mode = 0;
+
+ if (strcmp(fb_mode, MODE_VGA) == 0)
+ mode = VIDEO_ENCODER_VGA;
+ else if (strcmp(fb_mode, MODE_NTSC) == 0)
+ mode = VIDEO_ENCODER_NTSC;
+ else if (strcmp(fb_mode, MODE_PAL) == 0)
+ mode = VIDEO_ENCODER_PAL;
+ if (mode)
+ fs453_ioctl(ENCODER_SET_NORM, &mode);
+ }
+#endif
+ }
+}
+
+/*!
+ * @brief Disable LCD controller.
+ * @param info framebuffer information pointer
+ */
+static void _disable_lcdc(struct fb_info *info)
+{
+ struct mx2fb_info *mx2fbi = (struct mx2fb_info *)info->par;
+
+ if (mx2fbi->type == MX2FB_TYPE_GW)
+ _disable_graphic_window(info);
+ else {
+ if (fb_enabled) {
+ gpio_lcdc_inactive();
+ board_power_lcd(0);
+ clk_disable(lcdc_clk);
+ fb_enabled = 0;
+ }
+#ifdef CONFIG_FB_MXC_TVOUT
+ if (fb_mode) {
+ int enable = 0;
+
+ if ((strcmp(fb_mode, MODE_VGA) == 0)
+ || (strcmp(fb_mode, MODE_NTSC) == 0)
+ || (strcmp(fb_mode, MODE_PAL) == 0))
+ fs453_ioctl(ENCODER_ENABLE_OUTPUT, &enable);
+ }
+#endif
+ }
+}
+
+/*!
+ * @brief Enable graphic window.
+ * @param info framebuffer information pointer
+ */
+static void _enable_graphic_window(struct fb_info *info)
+{
+ struct fb_var_screeninfo *var = &info->var;
+
+ g_gwinfo.enabled = 1;
+
+ g_gwinfo.base = (var->yoffset * var->xres_virtual + var->xoffset);
+ g_gwinfo.base *= (var->bits_per_pixel) / 8;
+ g_gwinfo.base += info->fix.smem_start;
+
+ g_gwinfo.xres = var->xres;
+ g_gwinfo.yres = var->yres;
+ g_gwinfo.xres_virtual = var->xres_virtual;
+
+ mx2_gw_set(&g_gwinfo);
+}
+
+/*!
+ * @brief Disable graphic window.
+ * @param info framebuffer information pointer
+ */
+static void _disable_graphic_window(struct fb_info *info)
+{
+ unsigned long i = 0;
+
+ g_gwinfo.enabled = 0;
+
+ /*
+ * Set alpha value to zero and reduce gw size, otherwise the graphic
+ * window will not be able to be enabled again.
+ */
+ __raw_writel(__raw_readl(LCDC_REG(LCDC_LGWCR)) & 0x00FFFFFF,
+ LCDC_REG(LCDC_LGWCR));
+ __raw_writel(((16 >> 4) << 20) + 16, LCDC_REG(LCDC_LGWSR));
+ while (i < 1000)
+ i++;
+
+ /* Now disable graphic window */
+ __raw_writel(__raw_readl(LCDC_REG(LCDC_LGWCR)) & ~0x00400000,
+ LCDC_REG(LCDC_LGWCR));
+
+ dev_dbg(info->device, "Graphic window disabled.\n");
+}
+
+/*!
+ * @brief Setup graphic window properties.
+ * @param gwinfo graphic window information pointer
+ */
+void mx2_gw_set(struct fb_gwinfo *gwinfo)
+{
+ int width, height, xpos, ypos;
+ int width_bg, height_bg;
+ /* Graphic window control register */
+ unsigned long lgwcr = 0x00400000;
+
+ if (!gwinfo->enabled) {
+ _disable_graphic_window(0);
+ return;
+ }
+
+ /* Graphic window start address register */
+ __raw_writel(gwinfo->base, LCDC_REG(LCDC_LGWSAR));
+
+ /*
+ * The graphic window width, height, x position and y position
+ * must be synced up width the background window, otherwise there
+ * may be flickering.
+ */
+ width_bg = (__raw_readl(LCDC_REG(LCDC_LSR)) & 0x03F00000) >> 16;
+ height_bg = __raw_readl(LCDC_REG(LCDC_LSR)) & 0x000003FF;
+
+ width = (gwinfo->xres > width_bg) ? width_bg : gwinfo->xres;
+ height = (gwinfo->yres > height_bg) ? height_bg : gwinfo->yres;
+
+ xpos = gwinfo->xpos;
+ ypos = gwinfo->ypos;
+
+ if (xpos + width > width_bg)
+ xpos = width_bg - width;
+ if (ypos + height > height_bg)
+ ypos = height_bg - height;
+
+ /* Graphic window size register */
+ __raw_writel(((width >> 4) << 20) + height, LCDC_REG(LCDC_LGWSR));
+
+ /* Graphic window virtual page width register */
+ __raw_writel(gwinfo->xres_virtual >> 1, LCDC_REG(LCDC_LGWVPWR));
+
+ /* Graphic window position register */
+ __raw_writel(((xpos & 0x000003FF) << 16) | (ypos & 0x000003FF),
+ LCDC_REG(LCDC_LGWPR));
+
+ /* Graphic window panning offset register */
+ __raw_writel(0, LCDC_REG(LCDC_LGWPOR));
+
+ /* Graphic window DMA control register */
+ if (cpu_is_mx27_rev(CHIP_REV_2_0) > 0)
+ __raw_writel(0x00040060, LCDC_REG(LCDC_LGWDCR));
+ else
+ __raw_writel(0x00020010, LCDC_REG(LCDC_LGWDCR));
+
+ /* Graphic window control register */
+ lgwcr |= (gwinfo->alpha_value & 0x000000FF) << 24;
+ lgwcr |= gwinfo->ck_enabled ? 0x00800000 : 0;
+ lgwcr |= gwinfo->vs_reversed ? 0x00200000 : 0;
+
+ /*
+ * Color keying value
+ * Todo: assume always use RGB565
+ */
+ lgwcr |= (gwinfo->ck_red & 0x0000003F) << 12;
+ lgwcr |= (gwinfo->ck_green & 0x0000003F) << 6;
+ lgwcr |= gwinfo->ck_blue & 0x0000003F;
+
+ __raw_writel(lgwcr, LCDC_REG(LCDC_LGWCR));
+
+ pr_debug("Graphic window enabled.\n");
+}
+EXPORT_SYMBOL(mx2_gw_set);
+
+/*!
+ * @brief Update LCDC registers
+ * @param info framebuffer information pointer
+ */
+static void _update_lcdc(struct fb_info *info)
+{
+ unsigned long base;
+ unsigned long perclk, pcd, pcr;
+ struct fb_var_screeninfo *var = &info->var;
+ struct mx2fb_info *mx2fbi = (struct mx2fb_info *)info->par;
+
+ if (mx2fbi->type == MX2FB_TYPE_GW) {
+ _enable_graphic_window(info);
+ return;
+ }
+
+ base = (var->yoffset * var->xres_virtual + var->xoffset);
+ base *= (var->bits_per_pixel) / 8;
+ base += info->fix.smem_start;
+
+ /* Screen start address register */
+ __raw_writel(base, LCDC_REG(LCDC_LSSAR));
+
+ /* Size register */
+ dev_dbg(info->device, "xres = %d, yres = %d\n",
+ info->var.xres, info->var.yres);
+ __raw_writel(((info->var.xres >> 4) << 20) + info->var.yres,
+ LCDC_REG(LCDC_LSR));
+
+ /* Virtual page width register */
+ __raw_writel(info->var.xres_virtual >> 1, LCDC_REG(LCDC_LVPWR));
+
+ /* To setup LCDC pixel clock */
+ perclk = clk_round_rate(lcdc_clk, 134000000);
+ if (clk_set_rate(lcdc_clk, perclk)) {
+ printk(KERN_INFO "mx2fb: Unable to set clock to %lu\n", perclk);
+ perclk = clk_get_rate(lcdc_clk);
+ }
+
+ /* Calculate pixel clock divider, and round to the nearest integer */
+ pcd = (perclk * 8 / (PICOS2KHZ(var->pixclock) * 1000UL) + 4) / 8;
+ if (--pcd > 0x3F)
+ pcd = 0x3F;
+
+ /* Panel configuration register */
+ pcr = 0xFA008B80 | pcd;
+ pcr |= (var->sync & FB_SYNC_CLK_LAT_FALL) ? 0x00200000 : 0;
+ pcr |= (var->sync & FB_SYNC_DATA_INVERT) ? 0x01000000 : 0;
+ pcr |= (var->sync & FB_SYNC_SHARP_MODE) ? 0x00000040 : 0;
+ pcr |= (var->sync & FB_SYNC_OE_LOW_ACT) ? 0x00100000 : 0;
+ __raw_writel(pcr, LCDC_REG(LCDC_LPCR));
+
+ /* Horizontal and vertical configuration register */
+ __raw_writel(((var->hsync_len - 1) << 26)
+ + ((var->right_margin - 1) << 8)
+ + (var->left_margin - 3), LCDC_REG(LCDC_LHCR));
+ __raw_writel((var->vsync_len << 26)
+ + (var->lower_margin << 8)
+ + var->upper_margin, LCDC_REG(LCDC_LVCR));
+
+ /* Sharp configuration register */
+ __raw_writel(0x00120300, LCDC_REG(LCDC_LSCR));
+
+ /* Refresh mode control reigster */
+ __raw_writel(0x00000000, LCDC_REG(LCDC_LRMCR));
+
+ /* DMA control register */
+ if (cpu_is_mx27_rev(CHIP_REV_2_0) > 0)
+ __raw_writel(0x00040060, LCDC_REG(LCDC_LDCR));
+ else
+ __raw_writel(0x00020010, LCDC_REG(LCDC_LDCR));
+}
+
+/*!
+ * @brief Set LCD brightness
+ * @param level brightness level
+ */
+void mx2fb_set_brightness(uint8_t level)
+{
+ /* Set LCDC PWM contract control register */
+ __raw_writel(0x00A90300 | level, LCDC_REG(LCDC_LPCCR));
+}
+EXPORT_SYMBOL(mx2fb_set_brightness);
+
+/*
+ * @brief LCDC interrupt handler
+ */
+static irqreturn_t mx2fb_isr(int irq, void *dev_id)
+{
+ struct fb_event event;
+ unsigned long status = __raw_readl(LCDC_REG(LCDC_LISR));
+
+ if (status & MX2FB_INT_EOF) {
+ event.info = &mx2fb_info[0];
+ atomic_notifier_call_chain(&mx2fb_notifier_list,
+ FB_EVENT_MXC_EOF, &event);
+ }
+
+ if (status & MX2FB_INT_GW_EOF) {
+ event.info = &mx2fb_info[1];
+ atomic_notifier_call_chain(&mx2fb_notifier_list,
+ FB_EVENT_MXC_EOF, &event);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * @brief Config and request LCDC interrupt
+ */
+static void _request_irq(void)
+{
+ unsigned long status;
+ unsigned long flags;
+
+ /* Read to clear the status */
+ status = __raw_readl(LCDC_REG(LCDC_LISR));
+
+ if (request_irq(MXC_INT_LCDC, mx2fb_isr, 0, "LCDC", 0))
+ pr_info("Request LCDC IRQ failed.\n");
+ else {
+ spin_lock_irqsave(&mx2fb_notifier_list.lock, flags);
+
+ /* Enable interrupt in case client has registered */
+ if (mx2fb_notifier_list.head != NULL) {
+ unsigned long status;
+ unsigned long ints = MX2FB_INT_EOF;
+
+ ints |= MX2FB_INT_GW_EOF;
+
+ /* Read to clear the status */
+ status = __raw_readl(LCDC_REG(LCDC_LISR));
+
+ /* Configure interrupt condition for EOF */
+ __raw_writel(0x0, LCDC_REG(LCDC_LICR));
+
+ /* Enable EOF and graphic window EOF interrupt */
+ __raw_writel(ints, LCDC_REG(LCDC_LIER));
+ }
+
+ spin_unlock_irqrestore(&mx2fb_notifier_list.lock, flags);
+ }
+}
+
+/*!
+ * @brief Free LCDC interrupt handler
+ */
+static void _free_irq(void)
+{
+ /* Disable all LCDC interrupt */
+ __raw_writel(0x0, LCDC_REG(LCDC_LIER));
+
+ free_irq(MXC_INT_LCDC, 0);
+}
+
+/*!
+ * @brief Register a client notifier
+ * @param nb notifier block to callback on events
+ */
+int mx2fb_register_client(struct notifier_block *nb)
+{
+ unsigned long flags;
+ int ret;
+
+ ret = atomic_notifier_chain_register(&mx2fb_notifier_list, nb);
+
+ spin_lock_irqsave(&mx2fb_notifier_list.lock, flags);
+
+ /* Enable interrupt in case client has registered */
+ if (mx2fb_notifier_list.head != NULL) {
+ unsigned long status;
+ unsigned long ints = MX2FB_INT_EOF;
+
+ ints |= MX2FB_INT_GW_EOF;
+
+ /* Read to clear the status */
+ status = __raw_readl(LCDC_REG(LCDC_LISR));
+
+ /* Configure interrupt condition for EOF */
+ __raw_writel(0x0, LCDC_REG(LCDC_LICR));
+
+ /* Enable EOF and graphic window EOF interrupt */
+ __raw_writel(ints, LCDC_REG(LCDC_LIER));
+ }
+
+ spin_unlock_irqrestore(&mx2fb_notifier_list.lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL(mx2fb_register_client);
+
+/*!
+ * @brief Unregister a client notifier
+ * @param nb notifier block to callback on events
+ */
+int mx2fb_unregister_client(struct notifier_block *nb)
+{
+ unsigned long flags;
+ int ret;
+
+ ret = atomic_notifier_chain_unregister(&mx2fb_notifier_list, nb);
+
+ spin_lock_irqsave(&mx2fb_notifier_list.lock, flags);
+
+ /* Mask interrupt in case no client registered */
+ if (mx2fb_notifier_list.head == NULL)
+ __raw_writel(0x0, LCDC_REG(LCDC_LIER));
+
+ spin_unlock_irqrestore(&mx2fb_notifier_list.lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL(mx2fb_unregister_client);
+
+#ifdef CONFIG_PM
+/*
+ * Power management hooks. Note that we won't be called from IRQ context,
+ * unlike the blank functions above, so we may sleep.
+ */
+
+/*!
+ * @brief Suspends the framebuffer and blanks the screen.
+ * Power management support
+ */
+static int mx2fb_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ _disable_lcdc(&mx2fb_info[0]);
+
+ return 0;
+}
+
+/*!
+ * @brief Resumes the framebuffer and unblanks the screen.
+ * Power management support
+ */
+static int mx2fb_resume(struct platform_device *pdev)
+{
+ _enable_lcdc(&mx2fb_info[0]);
+
+ return 0;
+}
+
+#endif /* CONFIG_PM */
+
+/*!
+ * @brief Probe routine for the framebuffer driver. It is called during the
+ * driver binding process.
+ *
+ * @return Appropriate error code to the kernel common code
+ */
+static int mx2fb_probe(struct platform_device *pdev)
+{
+ int ret, i;
+
+ lcdc_clk = clk_get(&pdev->dev, "lcdc_clk");
+
+ for (i = 0; i < sizeof(mx2fb_info) / sizeof(struct fb_info); i++) {
+ ret = _install_fb(&mx2fb_info[i], pdev);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to register framebuffer %d\n", i);
+ return ret;
+ }
+ }
+ _request_irq();
+
+ return 0;
+}
+
+/*!
+ * @brief Initialization
+ */
+int __init mx2fb_init(void)
+{
+ /*
+ * For kernel boot options (in 'video=xxxfb:<options>' format)
+ */
+#ifndef MODULE
+ {
+ char *option;
+
+ if (fb_get_options("mxcfb", &option))
+ return -ENODEV;
+ mx2fb_setup(option);
+ }
+#endif
+ return platform_driver_register(&mx2fb_driver);
+}
+
+/*!
+ * @brief Cleanup
+ */
+void __exit mx2fb_exit(void)
+{
+ int i;
+
+ _free_irq();
+ for (i = sizeof(mx2fb_info) / sizeof(struct fb_info); i > 0; i--)
+ _uninstall_fb(&mx2fb_info[i - 1]);
+
+ platform_driver_unregister(&mx2fb_driver);
+}
+
+#ifndef MODULE
+/*!
+ * @brief Setup
+ * Parse user specified options
+ * Example: video=mxcfb:240x320,bpp=16,Sharp-QVGA
+ */
+static int __init mx2fb_setup(char *options)
+{
+ char *opt;
+
+ if (!options || !*options)
+ return 0;
+
+ fb_mode = 0;
+ fb_enabled = 0;
+
+ while ((opt = strsep(&options, ",")) != NULL) {
+ if (!*opt)
+ continue;
+
+ if (!strncmp(opt, "bpp=", 4))
+ default_bpp = simple_strtoul(opt + 4, NULL, 0);
+ else
+ fb_mode = opt;
+ }
+
+ return 0;
+}
+#endif
+
+/* Modularization */
+module_init(mx2fb_init);
+module_exit(mx2fb_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MX2 framebuffer driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/mxc/mx2fb.h b/drivers/video/mxc/mx2fb.h
new file mode 100644
index 00000000..86c7abfd
--- /dev/null
+++ b/drivers/video/mxc/mx2fb.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2004-2007, 2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file mx2fb.h
+ *
+ * @brief Header file for the MX27 Frame buffer
+ *
+ * @ingroup Framebuffer
+ */
+#ifndef __MX2FB_H__
+#define __MX2FB_H__
+
+/*! @brief MX27 LCDC graphic window information */
+struct fb_gwinfo {
+ /*! Non-zero if graphic window is enabled */
+ __u32 enabled;
+
+ /* The fields below are valid only when graphic window is enabled */
+
+ /*! Graphic window alpha value from 0 to 255 */
+ __u32 alpha_value;
+
+ /*! Non-zero if graphic window color keying is enabled. */
+ __u32 ck_enabled;
+
+ /*
+ * The fields ck_red, ck_green and ck_blue are valid only when
+ * graphic window and the color keying are enabled. They are the
+ * color component of graphic window color keying.
+ */
+
+ /*! Color keying red component */
+ __u32 ck_red;
+
+ /*! Color keying green component */
+ __u32 ck_green;
+
+ /*! Color keying blue component */
+ __u32 ck_blue;
+
+ /*! Graphic window x position */
+ __u32 xpos;
+
+ /*! Graphic window y position */
+ __u32 ypos;
+
+ /*! Non-zero if graphic window vertical scan in reverse direction. */
+ __u32 vs_reversed;
+
+ /*
+ * The following fields are valid for FBIOGET_GWINFO and
+ * mx2_gw_set(). FBIOPUT_GWINFO ignores these fields.
+ */
+ __u32 base; /* Graphic window start address */
+ __u32 xres; /* Visible x resolution */
+ __u32 yres; /* Visible y resolution */
+ __u32 xres_virtual; /* Virtual x resolution */
+};
+
+/* 0x46E0-0x46FF are reserved for MX27 */
+#define FBIOGET_GWINFO 0x46E0 /*!< Get graphic window information */
+#define FBIOPUT_GWINFO 0x46E1 /*!< Set graphic window information */
+
+struct mx2fb_gbl_alpha {
+ int enable;
+ int alpha;
+};
+
+struct mx2fb_color_key {
+ int enable;
+ __u32 color_key;
+};
+
+#define MX2FB_SET_GBL_ALPHA _IOW('M', 0, struct mx2fb_gbl_alpha)
+#define MX2FB_SET_CLR_KEY _IOW('M', 1, struct mx2fb_color_key)
+#define MX2FB_WAIT_FOR_VSYNC _IOW('F', 0x20, u_int32_t)
+
+#ifdef __KERNEL__
+
+/*
+ * LCDC register definitions
+ */
+#define LCDC_LSSAR 0x00
+#define LCDC_LSR 0x04
+#define LCDC_LVPWR 0x08
+#define LCDC_LCPR 0x0C
+#define LCDC_LCWHBR 0x10
+#define LCDC_LCCMR 0x14
+#define LCDC_LPCR 0x18
+#define LCDC_LHCR 0x1C
+#define LCDC_LVCR 0x20
+#define LCDC_LPOR 0x24
+#define LCDC_LSCR 0x28
+#define LCDC_LPCCR 0x2C
+#define LCDC_LDCR 0x30
+#define LCDC_LRMCR 0x34
+#define LCDC_LICR 0x38
+#define LCDC_LIER 0x3C
+#define LCDC_LISR 0x40
+#define LCDC_LGWSAR 0x50
+#define LCDC_LGWSR 0x54
+#define LCDC_LGWVPWR 0x58
+#define LCDC_LGWPOR 0x5C
+#define LCDC_LGWPR 0x60
+#define LCDC_LGWCR 0x64
+#define LCDC_LGWDCR 0x68
+#define LCDC_LAUSCR 0x80
+#define LCDC_LAUSCCR 0x84
+
+#define LCDC_REG(reg) (IO_ADDRESS(LCDC_BASE_ADDR) + reg)
+
+#define MX2FB_INT_BOF 0x0001 /* Beginning of Frame */
+#define MX2FB_INT_EOF 0x0002 /* End of Frame */
+#define MX2FB_INT_ERR_RES 0x0004 /* Error Response */
+#define MX2FB_INT_UDR_ERR 0x0008 /* Under Run Error */
+#define MX2FB_INT_GW_BOF 0x0010 /* Graphic Window BOF */
+#define MX2FB_INT_GW_EOF 0x0020 /* Graphic Window EOF */
+#define MX2FB_INT_GW_ERR_RES 0x0040 /* Graphic Window ERR_RES */
+#define MX2FB_INT_GW_UDR_ERR 0x0080 /* Graphic Window UDR_ERR */
+
+#define FB_EVENT_MXC_EOF 0x8001 /* End of Frame event */
+
+int mx2fb_register_client(struct notifier_block *nb);
+int mx2fb_unregister_client(struct notifier_block *nb);
+
+void mx2_gw_set(struct fb_gwinfo *gwinfo);
+
+#endif /* __KERNEL__ */
+
+#endif /* __MX2FB_H__ */
diff --git a/drivers/video/mxc/mxc_dispdrv.c b/drivers/video/mxc/mxc_dispdrv.c
new file mode 100644
index 00000000..3d1cb43e
--- /dev/null
+++ b/drivers/video/mxc/mxc_dispdrv.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2011-2012 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file mxc_dispdrv.c
+ * @brief mxc display driver framework.
+ *
+ * A display device driver could call mxc_dispdrv_register(drv) in its dev_probe() function.
+ * Move all dev_probe() things into mxc_dispdrv_driver->init(), init() function should init
+ * and feedback setting;
+ * Move all dev_remove() things into mxc_dispdrv_driver->deinit();
+ * Move all dev_suspend() things into fb_notifier for SUSPEND, if there is;
+ * Move all dev_resume() things into fb_notifier for RESUME, if there is;
+ *
+ * ipuv3 fb driver could call mxc_dispdrv_gethandle(name, setting) before a fb
+ * need be added, with fbi param passing by setting, after
+ * mxc_dispdrv_gethandle() return, FB driver should get the basic setting
+ * about fbi info and ipuv3-hw (ipu_id and disp_id).
+ *
+ * @ingroup Framebuffer
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include "mxc_dispdrv.h"
+
+static LIST_HEAD(dispdrv_list);
+static DEFINE_MUTEX(dispdrv_lock);
+
+struct mxc_dispdrv_entry {
+ /* Note: drv always the first element */
+ struct mxc_dispdrv_driver *drv;
+ bool active;
+ void *priv;
+ struct list_head list;
+ struct device *dev;
+};
+
+void mxc_dispdrv_setdev(struct mxc_dispdrv_handle *drv_handle, struct device *dev)
+{
+ struct mxc_dispdrv_entry *dentry;
+ dentry = (struct mxc_dispdrv_entry *)drv_handle;
+ dentry->dev = dev;
+}
+
+struct device *mxc_dispdrv_getdev(struct mxc_dispdrv_handle *drv_handle)
+{
+ struct mxc_dispdrv_entry *dentry;
+ dentry = (struct mxc_dispdrv_entry *)drv_handle;
+ return dentry->dev;
+}
+
+struct mxc_dispdrv_handle *mxc_dispdrv_register(struct mxc_dispdrv_driver *drv)
+{
+ struct mxc_dispdrv_entry *new;
+
+ mutex_lock(&dispdrv_lock);
+
+ new = kzalloc(sizeof(struct mxc_dispdrv_entry), GFP_KERNEL);
+ if (!new) {
+ mutex_unlock(&dispdrv_lock);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ new->drv = drv;
+ list_add_tail(&new->list, &dispdrv_list);
+
+ mutex_unlock(&dispdrv_lock);
+
+ return (struct mxc_dispdrv_handle *)new;
+}
+EXPORT_SYMBOL_GPL(mxc_dispdrv_register);
+
+int mxc_dispdrv_unregister(struct mxc_dispdrv_handle *handle)
+{
+ struct mxc_dispdrv_entry *entry = (struct mxc_dispdrv_entry *)handle;
+
+ if (entry) {
+ mutex_lock(&dispdrv_lock);
+ list_del(&entry->list);
+ mutex_unlock(&dispdrv_lock);
+ kfree(entry);
+ return 0;
+ } else
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(mxc_dispdrv_unregister);
+
+struct mxc_dispdrv_handle *mxc_dispdrv_gethandle(char *name,
+ struct mxc_dispdrv_setting *setting)
+{
+ int ret, found = 0;
+ struct mxc_dispdrv_entry *entry;
+
+ mutex_lock(&dispdrv_lock);
+ list_for_each_entry(entry, &dispdrv_list, list) {
+ if (!strcmp(entry->drv->name, name) && (entry->drv->init)) {
+ ret = entry->drv->init((struct mxc_dispdrv_handle *)
+ entry, setting);
+ if (ret >= 0) {
+ entry->active = true;
+ found = 1;
+ break;
+ }
+ }
+ }
+ mutex_unlock(&dispdrv_lock);
+
+ return found ? (struct mxc_dispdrv_handle *)entry:ERR_PTR(-ENODEV);
+}
+EXPORT_SYMBOL_GPL(mxc_dispdrv_gethandle);
+
+void mxc_dispdrv_puthandle(struct mxc_dispdrv_handle *handle)
+{
+ struct mxc_dispdrv_entry *entry = (struct mxc_dispdrv_entry *)handle;
+
+ mutex_lock(&dispdrv_lock);
+ if (entry && entry->active && entry->drv->deinit) {
+ entry->drv->deinit(handle);
+ entry->active = false;
+ }
+ mutex_unlock(&dispdrv_lock);
+
+}
+EXPORT_SYMBOL_GPL(mxc_dispdrv_puthandle);
+
+int mxc_dispdrv_setdata(struct mxc_dispdrv_handle *handle, void *data)
+{
+ struct mxc_dispdrv_entry *entry = (struct mxc_dispdrv_entry *)handle;
+
+ if (entry) {
+ entry->priv = data;
+ return 0;
+ } else
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(mxc_dispdrv_setdata);
+
+void *mxc_dispdrv_getdata(struct mxc_dispdrv_handle *handle)
+{
+ struct mxc_dispdrv_entry *entry = (struct mxc_dispdrv_entry *)handle;
+
+ if (entry) {
+ return entry->priv;
+ } else
+ return ERR_PTR(-EINVAL);
+}
+EXPORT_SYMBOL_GPL(mxc_dispdrv_getdata);
diff --git a/drivers/video/mxc/mxc_dispdrv.h b/drivers/video/mxc/mxc_dispdrv.h
new file mode 100644
index 00000000..9a722176
--- /dev/null
+++ b/drivers/video/mxc/mxc_dispdrv.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2011-2012 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#ifndef __MXC_DISPDRV_H__
+#define __MXC_DISPDRV_H__
+#include <linux/fb.h>
+
+struct mxc_dispdrv_handle {
+ struct mxc_dispdrv_driver *drv;
+};
+
+struct mxc_dispdrv_setting {
+ /*input-feedback parameter*/
+ struct fb_info *fbi;
+ int if_fmt;
+ int default_bpp;
+ char *dft_mode_str;
+
+ /*feedback parameter*/
+ int dev_id;
+ int disp_id;
+};
+
+struct mxc_dispdrv_driver {
+ const char *name;
+ int (*init) (struct mxc_dispdrv_handle *, struct mxc_dispdrv_setting *);
+ void (*deinit) (struct mxc_dispdrv_handle *);
+ /* display driver enable function for extension */
+ int (*enable) (struct mxc_dispdrv_handle *);
+ /* display driver disable function, called at early part of fb_blank */
+ void (*disable) (struct mxc_dispdrv_handle *);
+ /* display driver setup function, called at early part of fb_set_par */
+ int (*setup) (struct mxc_dispdrv_handle *, struct fb_info *fbi);
+};
+
+struct mxc_dispdrv_handle *mxc_dispdrv_register(struct mxc_dispdrv_driver *drv);
+int mxc_dispdrv_unregister(struct mxc_dispdrv_handle *handle);
+struct mxc_dispdrv_handle *mxc_dispdrv_gethandle(char *name,
+ struct mxc_dispdrv_setting *setting);
+void mxc_dispdrv_puthandle(struct mxc_dispdrv_handle *handle);
+int mxc_dispdrv_setdata(struct mxc_dispdrv_handle *handle, void *data);
+void *mxc_dispdrv_getdata(struct mxc_dispdrv_handle *handle);
+void mxc_dispdrv_setdev(struct mxc_dispdrv_handle *drv_handle, struct device *dev);
+struct device *mxc_dispdrv_getdev(struct mxc_dispdrv_handle *drv_handle);
+#endif
diff --git a/drivers/video/mxc/mxc_dvi.c b/drivers/video/mxc/mxc_dvi.c
new file mode 100644
index 00000000..923db438
--- /dev/null
+++ b/drivers/video/mxc/mxc_dvi.c
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*!
+ * @defgroup Framebuffer Framebuffer Driver for SDC and ADC.
+ */
+
+/*!
+ * @file mxc_dvi.c
+ *
+ * @brief MXC DVI driver
+ *
+ * @ingroup Framebuffer
+ */
+
+/*!
+ * Include files
+ */
+#include <linux/i2c.h>
+#include <linux/fb.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/ipu.h>
+#include <linux/mxcfb.h>
+#include <linux/fsl_devices.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/regulator/consumer.h>
+#include <mach/mxc_edid.h>
+#include "mxc_dispdrv.h"
+#include "../edid.h"
+
+#define MXC_EDID_LENGTH (EDID_LENGTH*4)
+
+#define DISPDRV_DVI "dvi"
+
+struct mxc_dvi_data {
+ struct i2c_client *client;
+ struct platform_device *pdev;
+ struct mxc_dispdrv_handle *disp_dvi;
+ struct delayed_work det_work;
+ struct fb_info *fbi;
+ struct mxc_edid_cfg edid_cfg;
+ u8 cable_plugin;
+ u8 edid[MXC_EDID_LENGTH];
+
+ u32 ipu;
+ u32 di;
+ void (*init)(void);
+ int (*update)(void);
+ struct regulator *analog_reg;
+};
+
+static ssize_t mxc_dvi_show_state(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mxc_dvi_data *dvi = dev_get_drvdata(dev);
+
+ if (dvi->cable_plugin == 0)
+ strcpy(buf, "plugout\n");
+ else
+ strcpy(buf, "plugin\n");
+
+ return strlen(buf);
+}
+
+static DEVICE_ATTR(cable_state, S_IRUGO, mxc_dvi_show_state, NULL);
+
+static ssize_t mxc_dvi_show_name(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mxc_dvi_data *dvi = dev_get_drvdata(dev);
+
+ strcpy(buf, dvi->fbi->fix.id);
+ sprintf(buf+strlen(buf), "\n");
+
+ return strlen(buf);
+}
+
+static DEVICE_ATTR(fb_name, S_IRUGO, mxc_dvi_show_name, NULL);
+
+static ssize_t mxc_dvi_show_edid(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mxc_dvi_data *dvi = dev_get_drvdata(dev);
+ int i, j, len = 0;
+
+ for (j = 0; j < MXC_EDID_LENGTH/16; j++) {
+ for (i = 0; i < 16; i++)
+ len += sprintf(buf+len, "0x%02X ",
+ dvi->edid[j*16 + i]);
+ len += sprintf(buf+len, "\n");
+ }
+
+ return len;
+}
+
+static DEVICE_ATTR(edid, S_IRUGO, mxc_dvi_show_edid, NULL);
+
+static void det_worker(struct work_struct *work)
+{
+ struct delayed_work *delay_work = to_delayed_work(work);
+ struct mxc_dvi_data *dvi =
+ container_of(delay_work, struct mxc_dvi_data, det_work);
+ char event_string[16];
+ char *envp[] = { event_string, NULL };
+
+ /* cable connection changes */
+ if (dvi->update()) {
+ u8 edid_old[MXC_EDID_LENGTH];
+ dvi->cable_plugin = 1;
+ sprintf(event_string, "EVENT=plugin");
+
+ memcpy(edid_old, dvi->edid, MXC_EDID_LENGTH);
+
+ if (mxc_edid_read(dvi->client->adapter, dvi->client->addr,
+ dvi->edid, &dvi->edid_cfg, dvi->fbi) < 0)
+ dev_err(&dvi->client->dev,
+ "MXC dvi: read edid fail\n");
+ else {
+ if (!memcmp(edid_old, dvi->edid, MXC_EDID_LENGTH))
+ dev_info(&dvi->client->dev,
+ "Sii902x: same edid\n");
+ else if (dvi->fbi->monspecs.modedb_len > 0) {
+ int i;
+ const struct fb_videomode *mode;
+ struct fb_videomode m;
+
+ fb_destroy_modelist(&dvi->fbi->modelist);
+
+ for (i = 0; i < dvi->fbi->monspecs.modedb_len; i++)
+ /*FIXME now we do not support interlaced mode */
+ if (!(dvi->fbi->monspecs.modedb[i].vmode & FB_VMODE_INTERLACED))
+ fb_add_videomode(&dvi->fbi->monspecs.modedb[i],
+ &dvi->fbi->modelist);
+
+ fb_var_to_videomode(&m, &dvi->fbi->var);
+ mode = fb_find_nearest_mode(&m,
+ &dvi->fbi->modelist);
+
+ fb_videomode_to_var(&dvi->fbi->var, mode);
+
+ dvi->fbi->var.activate |= FB_ACTIVATE_FORCE;
+ console_lock();
+ dvi->fbi->flags |= FBINFO_MISC_USEREVENT;
+ fb_set_var(dvi->fbi, &dvi->fbi->var);
+ dvi->fbi->flags &= ~FBINFO_MISC_USEREVENT;
+ console_unlock();
+ }
+ }
+ } else {
+ dvi->cable_plugin = 0;
+ sprintf(event_string, "EVENT=plugout");
+ }
+
+ kobject_uevent_env(&dvi->pdev->dev.kobj, KOBJ_CHANGE, envp);
+}
+
+static irqreturn_t mxc_dvi_detect_handler(int irq, void *data)
+{
+ struct mxc_dvi_data *dvi = data;
+ schedule_delayed_work(&(dvi->det_work), msecs_to_jiffies(300));
+ return IRQ_HANDLED;
+}
+
+static int dvi_init(struct mxc_dispdrv_handle *disp,
+ struct mxc_dispdrv_setting *setting)
+{
+ int ret = 0;
+ struct mxc_dvi_data *dvi = mxc_dispdrv_getdata(disp);
+ struct fsl_mxc_dvi_platform_data *plat = dvi->client->dev.platform_data;
+
+ setting->dev_id = dvi->ipu = plat->ipu_id;
+ setting->disp_id = dvi->di = plat->disp_id;
+ setting->if_fmt = IPU_PIX_FMT_RGB24;
+ dvi->fbi = setting->fbi;
+ dvi->init = plat->init;
+ dvi->update = plat->update;
+
+ dvi->analog_reg = regulator_get(&dvi->pdev->dev, plat->analog_regulator);
+ if (!IS_ERR(dvi->analog_reg)) {
+ regulator_set_voltage(dvi->analog_reg, 2775000, 2775000);
+ regulator_enable(dvi->analog_reg);
+ }
+
+ if (dvi->init)
+ dvi->init();
+
+ /* get video mode from edid */
+ if (!dvi->update)
+ return -EINVAL;
+ else {
+ bool found = false;
+
+ INIT_LIST_HEAD(&dvi->fbi->modelist);
+ if (dvi->update()) {
+ dvi->cable_plugin = 1;
+ /* try to read edid */
+ if (mxc_edid_read(dvi->client->adapter, dvi->client->addr,
+ dvi->edid, &dvi->edid_cfg, dvi->fbi) < 0)
+ dev_warn(&dvi->client->dev, "Can not read edid\n");
+ else if (dvi->fbi->monspecs.modedb_len > 0) {
+ int i;
+ const struct fb_videomode *mode;
+ struct fb_videomode m;
+
+ for (i = 0; i < dvi->fbi->monspecs.modedb_len; i++) {
+ /*FIXME now we do not support interlaced mode */
+ if (!(dvi->fbi->monspecs.modedb[i].vmode
+ & FB_VMODE_INTERLACED))
+ fb_add_videomode(
+ &dvi->fbi->monspecs.modedb[i],
+ &dvi->fbi->modelist);
+ }
+
+ fb_find_mode(&dvi->fbi->var, dvi->fbi, setting->dft_mode_str,
+ NULL, 0, NULL, setting->default_bpp);
+
+ fb_var_to_videomode(&m, &dvi->fbi->var);
+ mode = fb_find_nearest_mode(&m,
+ &dvi->fbi->modelist);
+ fb_videomode_to_var(&dvi->fbi->var, mode);
+ found = 1;
+ }
+ } else
+ dvi->cable_plugin = 0;
+
+ if (!found) {
+ ret = fb_find_mode(&dvi->fbi->var, dvi->fbi, setting->dft_mode_str,
+ NULL, 0, NULL, setting->default_bpp);
+ if (!ret)
+ return -EINVAL;
+ }
+ }
+
+ /* cable detection */
+ if (dvi->client->irq) {
+ ret = request_irq(dvi->client->irq, mxc_dvi_detect_handler,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ "dvi_det", dvi);
+ if (ret < 0) {
+ dev_warn(&dvi->client->dev,
+ "MXC dvi: cound not request det irq %d\n",
+ dvi->client->irq);
+ goto err;
+ } else {
+ INIT_DELAYED_WORK(&(dvi->det_work), det_worker);
+ ret = device_create_file(&dvi->pdev->dev, &dev_attr_fb_name);
+ if (ret < 0)
+ dev_warn(&dvi->client->dev,
+ "MXC dvi: cound not create sys node for fb name\n");
+ ret = device_create_file(&dvi->pdev->dev, &dev_attr_cable_state);
+ if (ret < 0)
+ dev_warn(&dvi->client->dev,
+ "MXC dvi: cound not create sys node for cable state\n");
+ ret = device_create_file(&dvi->pdev->dev, &dev_attr_edid);
+ if (ret < 0)
+ dev_warn(&dvi->client->dev,
+ "MXC dvi: cound not create sys node for edid\n");
+
+ dev_set_drvdata(&dvi->pdev->dev, dvi);
+ }
+ }
+
+err:
+ return ret;
+}
+
+static void dvi_deinit(struct mxc_dispdrv_handle *disp)
+{
+ struct mxc_dvi_data *dvi = mxc_dispdrv_getdata(disp);
+
+ if (!IS_ERR(dvi->analog_reg))
+ regulator_disable(dvi->analog_reg);
+
+ free_irq(dvi->client->irq, dvi);
+}
+
+static struct mxc_dispdrv_driver dvi_drv = {
+ .name = DISPDRV_DVI,
+ .init = dvi_init,
+ .deinit = dvi_deinit,
+};
+
+static int __devinit mxc_dvi_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct mxc_dvi_data *dvi;
+ int ret = 0;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE | I2C_FUNC_I2C))
+ return -ENODEV;
+
+ dvi = kzalloc(sizeof(struct mxc_dvi_data), GFP_KERNEL);
+ if (!dvi) {
+ ret = -ENOMEM;
+ goto alloc_failed;
+ }
+
+ dvi->pdev = platform_device_register_simple("mxc_dvi", 0, NULL, 0);
+ if (IS_ERR(dvi->pdev)) {
+ printk(KERN_ERR
+ "Unable to register MXC DVI as a platform device\n");
+ ret = PTR_ERR(dvi->pdev);
+ goto pdev_reg_failed;
+ }
+
+ dvi->client = client;
+ dvi->disp_dvi = mxc_dispdrv_register(&dvi_drv);
+ mxc_dispdrv_setdata(dvi->disp_dvi, dvi);
+
+ i2c_set_clientdata(client, dvi);
+
+ return ret;
+
+pdev_reg_failed:
+ kfree(dvi);
+alloc_failed:
+ return ret;
+}
+
+static int __devexit mxc_dvi_remove(struct i2c_client *client)
+{
+ struct mxc_dvi_data *dvi = i2c_get_clientdata(client);
+
+ mxc_dispdrv_puthandle(dvi->disp_dvi);
+ mxc_dispdrv_unregister(dvi->disp_dvi);
+ platform_device_unregister(dvi->pdev);
+ kfree(dvi);
+ return 0;
+}
+
+static const struct i2c_device_id mxc_dvi_id[] = {
+ { "mxc_dvi", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, mxc_dvi_id);
+
+static struct i2c_driver mxc_dvi_i2c_driver = {
+ .driver = {
+ .name = "mxc_dvi",
+ },
+ .probe = mxc_dvi_probe,
+ .remove = mxc_dvi_remove,
+ .id_table = mxc_dvi_id,
+};
+
+static int __init mxc_dvi_init(void)
+{
+ return i2c_add_driver(&mxc_dvi_i2c_driver);
+}
+
+static void __exit mxc_dvi_exit(void)
+{
+ i2c_del_driver(&mxc_dvi_i2c_driver);
+}
+
+module_init(mxc_dvi_init);
+module_exit(mxc_dvi_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC DVI driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/mxc/mxc_edid.c b/drivers/video/mxc/mxc_edid.c
new file mode 100644
index 00000000..721ff886
--- /dev/null
+++ b/drivers/video/mxc/mxc_edid.c
@@ -0,0 +1,763 @@
+/*
+ * Copyright 2009-2013 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @defgroup Framebuffer Framebuffer Driver for SDC and ADC.
+ */
+
+/*!
+ * @file mxc_edid.c
+ *
+ * @brief MXC EDID driver
+ *
+ * @ingroup Framebuffer
+ */
+
+/*!
+ * Include files
+ */
+#include <linux/i2c.h>
+#include <linux/fb.h>
+#include <mach/mxc_edid.h>
+#include "../edid.h"
+
+#undef DEBUG /* define this for verbose EDID parsing output */
+#ifdef DEBUG
+#define DPRINTK(fmt, args...) printk(fmt, ## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+const struct fb_videomode mxc_cea_mode[64] = {
+ /* #1: 640x480p@59.94/60Hz 4:3 */
+ [1] = {
+ NULL, 60, 640, 480, 39722, 48, 16, 33, 10, 96, 2, 0,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0,
+ },
+ /* #2: 720x480p@59.94/60Hz 4:3 */
+ [2] = {
+ NULL, 60, 720, 480, 37037, 60, 16, 30, 9, 62, 6, 0,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0,
+ },
+ /* #3: 720x480p@59.94/60Hz 16:9 */
+ [3] = {
+ NULL, 60, 720, 480, 37037, 60, 16, 30, 9, 62, 6, 0,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
+ },
+ /* #4: 1280x720p@59.94/60Hz 16:9 */
+ [4] = {
+ NULL, 60, 1280, 720, 13468, 220, 110, 20, 5, 40, 5,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0
+ },
+ /* #5: 1920x1080i@59.94/60Hz 16:9 */
+ [5] = {
+ NULL, 60, 1920, 1080, 13763, 148, 88, 15, 2, 44, 5,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_INTERLACED | FB_VMODE_ASPECT_16_9, 0,
+ },
+ /* #6: 720(1440)x480iH@59.94/60Hz 4:3 */
+ [6] = {
+ NULL, 60, 1440, 480, 18554/*37108*/, 114, 38, 15, 4, 124, 3, 0,
+ FB_VMODE_INTERLACED | FB_VMODE_ASPECT_4_3, 0,
+ },
+ /* #7: 720(1440)x480iH@59.94/60Hz 16:9 */
+ [7] = {
+ NULL, 60, 1440, 480, 18554/*37108*/, 114, 38, 15, 4, 124, 3, 0,
+ FB_VMODE_INTERLACED | FB_VMODE_ASPECT_16_9, 0,
+ },
+ /* #8: 720(1440)x240pH@59.94/60Hz 4:3 */
+ [8] = {
+ NULL, 60, 1440, 240, 37108, 114, 38, 15, 4, 124, 3, 0,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0,
+ },
+ /* #9: 720(1440)x240pH@59.94/60Hz 16:9 */
+ [9] = {
+ NULL, 60, 1440, 240, 37108, 114, 38, 15, 4, 124, 3, 0,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
+ },
+ /* #14: 1440x480p@59.94/60Hz 4:3 */
+ [14] = {
+ NULL, 60, 1440, 480, 18500, 120, 32, 30, 9, 124, 6, 0,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0,
+ },
+ /* #15: 1440x480p@59.94/60Hz 16:9 */
+ [15] = {
+ NULL, 60, 1440, 480, 18500, 120, 32, 30, 9, 124, 6, 0,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
+ },
+ /* #16: 1920x1080p@60Hz 16:9 */
+ [16] = {
+ NULL, 60, 1920, 1080, 6734, 148, 88, 36, 4, 44, 5,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
+ },
+ /* #17: 720x576pH@50Hz 4:3 */
+ [17] = {
+ NULL, 50, 720, 576, 37037, 68, 12, 39, 5, 64, 5, 0,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0,
+ },
+ /* #18: 720x576pH@50Hz 16:9 */
+ [18] = {
+ NULL, 50, 720, 576, 37037, 68, 12, 39, 5, 64, 5, 0,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
+ },
+ /* #19: 1280x720p@50Hz */
+ [19] = {
+ NULL, 50, 1280, 720, 13468, 220, 440, 20, 5, 40, 5,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
+ },
+ /* #20: 1920x1080i@50Hz */
+ [20] = {
+ NULL, 50, 1920, 1080, 13480, 148, 528, 15, 5, 528, 5,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_INTERLACED | FB_VMODE_ASPECT_16_9, 0,
+ },
+ /* #23: 720(1440)x288pH@50Hz 4:3 */
+ [23] = {
+ NULL, 50, 1440, 288, 37037, 138, 24, 19, 2, 126, 3, 0,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0,
+ },
+ /* #24: 720(1440)x288pH@50Hz 16:9 */
+ [24] = {
+ NULL, 50, 1440, 288, 37037, 138, 24, 19, 2, 126, 3, 0,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
+ },
+ /* #29: 720(1440)x576pH@50Hz 4:3 */
+ [29] = {
+ NULL, 50, 1440, 576, 18518, 136, 24, 39, 5, 128, 5, 0,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0,
+ },
+ /* #30: 720(1440)x576pH@50Hz 16:9 */
+ [30] = {
+ NULL, 50, 1440, 576, 18518, 136, 24, 39, 5, 128, 5, 0,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
+ },
+ /* #31: 1920x1080p@50Hz */
+ [31] = {
+ NULL, 50, 1920, 1080, 6734, 148, 528, 36, 4, 44, 5,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
+ },
+ /* #32: 1920x1080p@23.98/24Hz */
+ [32] = {
+ NULL, 24, 1920, 1080, 13468, 148, 638, 36, 4, 44, 5,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
+ },
+ /* #33: 1920x1080p@25Hz */
+ [33] = {
+ NULL, 25, 1920, 1080, 13468, 148, 528, 36, 4, 44, 5,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
+ },
+ /* #34: 1920x1080p@30Hz */
+ [34] = {
+ NULL, 30, 1920, 1080, 13468, 148, 88, 36, 4, 44, 5,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
+ },
+ /* #41: 1280x720p@100Hz 16:9 */
+ [41] = {
+ NULL, 100, 1280, 720, 6734, 220, 440, 20, 5, 40, 5,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0
+ },
+ /* #47: 1280x720p@119.88/120Hz 16:9 */
+ [47] = {
+ NULL, 120, 1280, 720, 6734, 220, 110, 20, 5, 40, 5,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0
+ },
+};
+
+/*
+ * We have a special version of fb_mode_is_equal that ignores
+ * pixclock, since for many CEA modes, 2 frequencies are supported
+ * e.g. 640x480 @ 60Hz or 59.94Hz
+ */
+int mxc_edid_fb_mode_is_equal(bool use_aspect,
+ const struct fb_videomode *mode1,
+ const struct fb_videomode *mode2)
+{
+ u32 mask;
+
+ if (use_aspect)
+ mask = ~0;
+ else
+ mask = ~FB_VMODE_ASPECT_MASK;
+
+ return (mode1->xres == mode2->xres &&
+ mode1->yres == mode2->yres &&
+ mode1->hsync_len == mode2->hsync_len &&
+ mode1->vsync_len == mode2->vsync_len &&
+ mode1->left_margin == mode2->left_margin &&
+ mode1->right_margin == mode2->right_margin &&
+ mode1->upper_margin == mode2->upper_margin &&
+ mode1->lower_margin == mode2->lower_margin &&
+ mode1->sync == mode2->sync &&
+ /* refresh check, 59.94Hz and 60Hz have the same parameter
+ * in struct of mxc_cea_mode */
+ abs(mode1->refresh - mode2->refresh) <= 1 &&
+ (mode1->vmode & mask) == (mode2->vmode & mask));
+}
+
+static void get_detailed_timing(unsigned char *block,
+ struct fb_videomode *mode)
+{
+ mode->xres = H_ACTIVE;
+ mode->yres = V_ACTIVE;
+ mode->pixclock = PIXEL_CLOCK;
+ mode->pixclock /= 1000;
+ mode->pixclock = KHZ2PICOS(mode->pixclock);
+ mode->right_margin = H_SYNC_OFFSET;
+ mode->left_margin = (H_ACTIVE + H_BLANKING) -
+ (H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH);
+ mode->upper_margin = V_BLANKING - V_SYNC_OFFSET -
+ V_SYNC_WIDTH;
+ mode->lower_margin = V_SYNC_OFFSET;
+ mode->hsync_len = H_SYNC_WIDTH;
+ mode->vsync_len = V_SYNC_WIDTH;
+ if (HSYNC_POSITIVE)
+ mode->sync |= FB_SYNC_HOR_HIGH_ACT;
+ if (VSYNC_POSITIVE)
+ mode->sync |= FB_SYNC_VERT_HIGH_ACT;
+ mode->refresh = PIXEL_CLOCK/((H_ACTIVE + H_BLANKING) *
+ (V_ACTIVE + V_BLANKING));
+ if (INTERLACED) {
+ mode->yres *= 2;
+ mode->upper_margin *= 2;
+ mode->lower_margin *= 2;
+ mode->vsync_len *= 2;
+ mode->vmode |= FB_VMODE_INTERLACED;
+ }
+ mode->flag = FB_MODE_IS_DETAILED;
+
+ if ((H_SIZE / 16) == (V_SIZE / 9))
+ mode->vmode |= FB_VMODE_ASPECT_16_9;
+ else if ((H_SIZE / 4) == (V_SIZE / 3))
+ mode->vmode |= FB_VMODE_ASPECT_4_3;
+ else if ((mode->xres / 16) == (mode->yres / 9))
+ mode->vmode |= FB_VMODE_ASPECT_16_9;
+ else if ((mode->xres / 4) == (mode->yres / 3))
+ mode->vmode |= FB_VMODE_ASPECT_4_3;
+
+ if (mode->vmode & FB_VMODE_ASPECT_16_9)
+ DPRINTK("Aspect ratio: 16:9\n");
+ if (mode->vmode & FB_VMODE_ASPECT_4_3)
+ DPRINTK("Aspect ratio: 4:3\n");
+ DPRINTK(" %d MHz ", PIXEL_CLOCK/1000000);
+ DPRINTK("%d %d %d %d ", H_ACTIVE, H_ACTIVE + H_SYNC_OFFSET,
+ H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH, H_ACTIVE + H_BLANKING);
+ DPRINTK("%d %d %d %d ", V_ACTIVE, V_ACTIVE + V_SYNC_OFFSET,
+ V_ACTIVE + V_SYNC_OFFSET + V_SYNC_WIDTH, V_ACTIVE + V_BLANKING);
+ DPRINTK("%sHSync %sVSync\n\n", (HSYNC_POSITIVE) ? "+" : "-",
+ (VSYNC_POSITIVE) ? "+" : "-");
+}
+
+int mxc_edid_parse_ext_blk(unsigned char *edid,
+ struct mxc_edid_cfg *cfg,
+ struct fb_monspecs *specs)
+{
+ char detail_timing_desc_offset;
+ struct fb_videomode *mode, *m;
+ unsigned char index = 0x0;
+ unsigned char *block;
+ int i, num = 0, revision;
+
+ if (edid[index++] != 0x2) /* only support cea ext block now */
+ return -1;
+ revision = edid[index++];
+ DPRINTK("cea extent revision %d\n", revision);
+ mode = kzalloc(50 * sizeof(struct fb_videomode), GFP_KERNEL);
+ if (mode == NULL)
+ return -1;
+
+ detail_timing_desc_offset = edid[index++];
+
+ if (revision >= 2) {
+ cfg->cea_underscan = (edid[index] >> 7) & 0x1;
+ cfg->cea_basicaudio = (edid[index] >> 6) & 0x1;
+ cfg->cea_ycbcr444 = (edid[index] >> 5) & 0x1;
+ cfg->cea_ycbcr422 = (edid[index] >> 4) & 0x1;
+
+ DPRINTK("CEA underscan %d\n", cfg->cea_underscan);
+ DPRINTK("CEA basicaudio %d\n", cfg->cea_basicaudio);
+ DPRINTK("CEA ycbcr444 %d\n", cfg->cea_ycbcr444);
+ DPRINTK("CEA ycbcr422 %d\n", cfg->cea_ycbcr422);
+ }
+
+ if (revision >= 3) {
+ /* short desc */
+ DPRINTK("CEA Short desc timmings\n");
+ index++;
+ while (index < detail_timing_desc_offset) {
+ unsigned char tagcode, blklen;
+
+ tagcode = (edid[index] >> 5) & 0x7;
+ blklen = (edid[index]) & 0x1f;
+
+ DPRINTK("Tagcode %x Len %d\n", tagcode, blklen);
+
+ switch (tagcode) {
+ case 0x2: /*Video data block*/
+ {
+ int cea_idx;
+ i = 0;
+ while (i < blklen) {
+ index++;
+ cea_idx = edid[index] & 0x7f;
+ if (cea_idx < ARRAY_SIZE(mxc_cea_mode) &&
+ (mxc_cea_mode[cea_idx].xres)) {
+ DPRINTK("Support CEA Format #%d\n", cea_idx);
+ mode[num] = mxc_cea_mode[cea_idx];
+ mode[num].flag |= FB_MODE_IS_STANDARD;
+ num++;
+ }
+ i++;
+ }
+ break;
+ }
+ case 0x3: /*Vendor specific data*/
+ {
+ unsigned char IEEE_reg_iden[3];
+ unsigned char deep_color;
+ unsigned char latency_present;
+ unsigned char I_latency_present;
+ unsigned char hdmi_video_present;
+ unsigned char hdmi_3d_present;
+ unsigned char hdmi_3d_multi_present;
+ unsigned char hdmi_vic_len;
+ unsigned char hdmi_3d_len;
+ unsigned char index_inc = 0;
+ unsigned char vsd_end;
+
+ vsd_end = index + blklen;
+
+ IEEE_reg_iden[0] = edid[index+1];
+ IEEE_reg_iden[1] = edid[index+2];
+ IEEE_reg_iden[2] = edid[index+3];
+ cfg->physical_address[0] = (edid[index+4] & 0xf0) >> 4;
+ cfg->physical_address[1] = (edid[index+4] & 0x0f);
+ cfg->physical_address[2] = (edid[index+5] & 0xf0) >> 4;
+ cfg->physical_address[3] = (edid[index+5] & 0x0f);
+
+ if ((IEEE_reg_iden[0] == 0x03) &&
+ (IEEE_reg_iden[1] == 0x0c) &&
+ (IEEE_reg_iden[2] == 0x00))
+ cfg->hdmi_cap = 1;
+
+ if (blklen > 5) {
+ deep_color = edid[index+6];
+ if (deep_color & 0x80)
+ cfg->vsd_support_ai = true;
+ if (deep_color & 0x40)
+ cfg->vsd_dc_48bit = true;
+ if (deep_color & 0x20)
+ cfg->vsd_dc_36bit = true;
+ if (deep_color & 0x10)
+ cfg->vsd_dc_30bit = true;
+ if (deep_color & 0x08)
+ cfg->vsd_dc_y444 = true;
+ if (deep_color & 0x01)
+ cfg->vsd_dvi_dual = true;
+ }
+
+ DPRINTK("VSD hdmi capability %d\n", cfg->hdmi_cap);
+ DPRINTK("VSD support ai %d\n", cfg->vsd_support_ai);
+ DPRINTK("VSD support deep color 48bit %d\n", cfg->vsd_dc_48bit);
+ DPRINTK("VSD support deep color 36bit %d\n", cfg->vsd_dc_36bit);
+ DPRINTK("VSD support deep color 30bit %d\n", cfg->vsd_dc_30bit);
+ DPRINTK("VSD support deep color y444 %d\n", cfg->vsd_dc_y444);
+ DPRINTK("VSD support dvi dual %d\n", cfg->vsd_dvi_dual);
+
+ if (blklen > 6)
+ cfg->vsd_max_tmdsclk_rate = edid[index+7] * 5;
+ DPRINTK("VSD MAX TMDS CLOCK RATE %d\n", cfg->vsd_max_tmdsclk_rate);
+
+ if (blklen > 7) {
+ latency_present = edid[index+8] >> 7;
+ I_latency_present = (edid[index+8] & 0x40) >> 6;
+ hdmi_video_present = (edid[index+8] & 0x20) >> 5;
+ cfg->vsd_cnc3 = (edid[index+8] & 0x8) >> 3;
+ cfg->vsd_cnc2 = (edid[index+8] & 0x4) >> 2;
+ cfg->vsd_cnc1 = (edid[index+8] & 0x2) >> 1;
+ cfg->vsd_cnc0 = edid[index+8] & 0x1;
+
+ DPRINTK("VSD cnc0 %d\n", cfg->vsd_cnc0);
+ DPRINTK("VSD cnc1 %d\n", cfg->vsd_cnc1);
+ DPRINTK("VSD cnc2 %d\n", cfg->vsd_cnc2);
+ DPRINTK("VSD cnc3 %d\n", cfg->vsd_cnc3);
+ DPRINTK("latency_present %d\n", latency_present);
+ DPRINTK("I_latency_present %d\n", I_latency_present);
+ DPRINTK("hdmi_video_present %d\n", hdmi_video_present);
+
+ } else {
+ index += blklen;
+ break;
+ }
+
+ index += 9;
+
+ /*latency present */
+ if (latency_present) {
+ cfg->vsd_video_latency = edid[index++];
+ cfg->vsd_audio_latency = edid[index++];
+
+ if (I_latency_present) {
+ cfg->vsd_I_video_latency = edid[index++];
+ cfg->vsd_I_audio_latency = edid[index++];
+ } else {
+ cfg->vsd_I_video_latency = cfg->vsd_video_latency;
+ cfg->vsd_I_audio_latency = cfg->vsd_audio_latency;
+ }
+
+ DPRINTK("VSD latency video_latency %d\n", cfg->vsd_video_latency);
+ DPRINTK("VSD latency audio_latency %d\n", cfg->vsd_audio_latency);
+ DPRINTK("VSD latency I_video_latency %d\n", cfg->vsd_I_video_latency);
+ DPRINTK("VSD latency I_audio_latency %d\n", cfg->vsd_I_audio_latency);
+ }
+
+ if (hdmi_video_present) {
+ hdmi_3d_present = edid[index] >> 7;
+ hdmi_3d_multi_present = (edid[index] & 0x60) >> 5;
+ index++;
+ hdmi_vic_len = (edid[index] & 0xe0) >> 5;
+ hdmi_3d_len = edid[index] & 0x1f;
+ index++;
+
+ DPRINTK("hdmi_3d_present %d\n", hdmi_3d_present);
+ DPRINTK("hdmi_3d_multi_present %d\n", hdmi_3d_multi_present);
+ DPRINTK("hdmi_vic_len %d\n", hdmi_vic_len);
+ DPRINTK("hdmi_3d_len %d\n", hdmi_3d_len);
+
+ if (hdmi_vic_len > 0) {
+ for (i = 0; i < hdmi_vic_len; i++) {
+ cfg->hdmi_vic[i] = edid[index++];
+ DPRINTK("HDMI_vic=%d\n", cfg->hdmi_vic[i]);
+ }
+ }
+
+ if (hdmi_3d_len > 0) {
+ if (hdmi_3d_present) {
+ if (hdmi_3d_multi_present == 0x1) {
+ cfg->hdmi_3d_struct_all = (edid[index] << 8) | edid[index+1];
+ index_inc = 2;
+ } else if (hdmi_3d_multi_present == 0x2) {
+ cfg->hdmi_3d_struct_all = (edid[index] << 8) | edid[index+1];
+ cfg->hdmi_3d_mask_all = (edid[index+2] << 8) | edid[index+3];
+ index_inc = 4;
+ } else
+ index_inc = 0;
+ }
+
+ DPRINTK("HDMI 3d struct all =0x%x\n", cfg->hdmi_3d_struct_all);
+ DPRINTK("HDMI 3d mask all =0x%x\n", cfg->hdmi_3d_mask_all);
+
+ /* Read 2D vic 3D_struct */
+ if ((hdmi_3d_len - index_inc) > 0) {
+ DPRINTK("Support 3D video format\n");
+ i = 0;
+ while ((hdmi_3d_len - index_inc) > 0) {
+
+ cfg->hdmi_3d_format[i].vic_order_2d = edid[index+index_inc] >> 4;
+ cfg->hdmi_3d_format[i].struct_3d = edid[index+index_inc] & 0x0f;
+ index_inc++;
+
+ if (cfg->hdmi_3d_format[i].struct_3d == 8) {
+ cfg->hdmi_3d_format[i].detail_3d = edid[index+index_inc] >> 4;
+ index_inc++;
+ } else if (cfg->hdmi_3d_format[i].struct_3d > 8) {
+ cfg->hdmi_3d_format[i].detail_3d = 0;
+ index_inc++;
+ }
+
+ DPRINTK("vic_order_2d=%d, 3d_struct=%d, 3d_detail=0x%x\n",
+ cfg->hdmi_3d_format[i].vic_order_2d,
+ cfg->hdmi_3d_format[i].struct_3d,
+ cfg->hdmi_3d_format[i].detail_3d);
+ i++;
+ }
+ }
+ index += index_inc;
+ }
+ }
+
+ index = vsd_end;
+
+ break;
+ }
+ case 0x1: /*Audio data block*/
+ {
+ u8 audio_format, max_ch, byte1, byte2, byte3;
+
+ i = 0;
+ cfg->max_channels = 0;
+ cfg->sample_rates = 0;
+ cfg->sample_sizes = 0;
+
+ while (i < blklen) {
+ byte1 = edid[index + 1];
+ byte2 = edid[index + 2];
+ byte3 = edid[index + 3];
+ index += 3;
+ i += 3;
+
+ audio_format = byte1 >> 3;
+ max_ch = (byte1 & 0x07) + 1;
+
+ DPRINTK("Audio Format Descriptor : %2d\n", audio_format);
+ DPRINTK("Max Number of Channels : %2d\n", max_ch);
+ DPRINTK("Sample Rates : %02x\n", byte2);
+
+ /* ALSA can't specify specific compressed
+ * formats, so only care about PCM for now. */
+ if (audio_format == AUDIO_CODING_TYPE_LPCM) {
+ if (max_ch > cfg->max_channels)
+ cfg->max_channels = max_ch;
+
+ cfg->sample_rates |= byte2;
+ cfg->sample_sizes |= byte3 & 0x7;
+ DPRINTK("Sample Sizes : %02x\n",
+ byte3 & 0x7);
+ }
+ }
+ break;
+ }
+ case 0x4: /*Speaker allocation block*/
+ {
+ i = 0;
+ while (i < blklen) {
+ cfg->speaker_alloc = edid[index + 1];
+ index += 3;
+ i += 3;
+ DPRINTK("Speaker Alloc : %02x\n", cfg->speaker_alloc);
+ }
+ break;
+ }
+ case 0x7: /*User extended block*/
+ default:
+ /* skip */
+ DPRINTK("Not handle block, tagcode = 0x%x\n", tagcode);
+ index += blklen;
+ break;
+ }
+
+ index++;
+ }
+ }
+
+ /* long desc */
+ DPRINTK("CEA long desc timmings\n");
+ index = detail_timing_desc_offset;
+ block = edid + index;
+ while (index < (EDID_LENGTH - DETAILED_TIMING_DESCRIPTION_SIZE)) {
+ if (!(block[0] == 0x00 && block[1] == 0x00)) {
+ get_detailed_timing(block, &mode[num]);
+ num++;
+ }
+ block += DETAILED_TIMING_DESCRIPTION_SIZE;
+ index += DETAILED_TIMING_DESCRIPTION_SIZE;
+ }
+
+ if (!num) {
+ kfree(mode);
+ return 0;
+ }
+
+ m = kmalloc((num + specs->modedb_len) *
+ sizeof(struct fb_videomode), GFP_KERNEL);
+ if (!m)
+ return 0;
+
+ if (specs->modedb_len) {
+ memmove(m, specs->modedb,
+ specs->modedb_len * sizeof(struct fb_videomode));
+ kfree(specs->modedb);
+ }
+ memmove(m+specs->modedb_len, mode,
+ num * sizeof(struct fb_videomode));
+ kfree(mode);
+
+ specs->modedb_len += num;
+ specs->modedb = m;
+
+ return 0;
+}
+EXPORT_SYMBOL(mxc_edid_parse_ext_blk);
+
+static int mxc_edid_readblk(struct i2c_adapter *adp,
+ unsigned short addr, unsigned char *edid)
+{
+ int ret = 0, extblknum = 0;
+ unsigned char regaddr = 0x0;
+ struct i2c_msg msg[2] = {
+ {
+ .addr = addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &regaddr,
+ }, {
+ .addr = addr,
+ .flags = I2C_M_RD,
+ .len = EDID_LENGTH,
+ .buf = edid,
+ },
+ };
+
+ ret = i2c_transfer(adp, msg, ARRAY_SIZE(msg));
+ if (ret != ARRAY_SIZE(msg)) {
+ DPRINTK("unable to read EDID block\n");
+ return -EIO;
+ }
+
+ if (edid[1] == 0x00)
+ return -ENOENT;
+
+ extblknum = edid[0x7E];
+
+ if (extblknum) {
+ regaddr = 128;
+ msg[1].buf = edid + EDID_LENGTH;
+
+ ret = i2c_transfer(adp, msg, ARRAY_SIZE(msg));
+ if (ret != ARRAY_SIZE(msg)) {
+ DPRINTK("unable to read EDID ext block\n");
+ return -EIO;
+ }
+ }
+
+ return extblknum;
+}
+
+static int mxc_edid_readsegblk(struct i2c_adapter *adp, unsigned short addr,
+ unsigned char *edid, int seg_num)
+{
+ int ret = 0;
+ unsigned char segment = 0x1, regaddr = 0;
+ struct i2c_msg msg[3] = {
+ {
+ .addr = 0x30,
+ .flags = 0,
+ .len = 1,
+ .buf = &segment,
+ }, {
+ .addr = addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &regaddr,
+ }, {
+ .addr = addr,
+ .flags = I2C_M_RD,
+ .len = EDID_LENGTH,
+ .buf = edid,
+ },
+ };
+
+ ret = i2c_transfer(adp, msg, ARRAY_SIZE(msg));
+ if (ret != ARRAY_SIZE(msg)) {
+ DPRINTK("unable to read EDID block\n");
+ return -EIO;
+ }
+
+ if (seg_num == 2) {
+ regaddr = 128;
+ msg[2].buf = edid + EDID_LENGTH;
+
+ ret = i2c_transfer(adp, msg, ARRAY_SIZE(msg));
+ if (ret != ARRAY_SIZE(msg)) {
+ DPRINTK("unable to read EDID block\n");
+ return -EIO;
+ }
+ }
+
+ return ret;
+}
+
+int mxc_edid_var_to_vic(struct fb_var_screeninfo *var)
+{
+ int i;
+ struct fb_videomode m;
+
+ for (i = 0; i < ARRAY_SIZE(mxc_cea_mode); i++) {
+ fb_var_to_videomode(&m, var);
+ if (mxc_edid_fb_mode_is_equal(false, &m, &mxc_cea_mode[i]))
+ break;
+ }
+
+ if (i == ARRAY_SIZE(mxc_cea_mode))
+ return 0;
+
+ return i;
+}
+
+EXPORT_SYMBOL(mxc_edid_var_to_vic);
+
+int mxc_edid_mode_to_vic(const struct fb_videomode *mode)
+{
+ int i;
+ bool use_aspect = (mode->vmode & FB_VMODE_ASPECT_MASK);
+
+ for (i = 0; i < ARRAY_SIZE(mxc_cea_mode); i++) {
+ if (mxc_edid_fb_mode_is_equal(use_aspect, mode, &mxc_cea_mode[i]))
+ break;
+ }
+
+ if (i == ARRAY_SIZE(mxc_cea_mode))
+ return 0;
+
+ return i;
+}
+EXPORT_SYMBOL(mxc_edid_mode_to_vic);
+
+/* make sure edid has 512 bytes*/
+int mxc_edid_read(struct i2c_adapter *adp, unsigned short addr,
+ unsigned char *edid, struct mxc_edid_cfg *cfg, struct fb_info *fbi)
+{
+ int ret = 0, extblknum;
+ if (!adp || !edid || !cfg || !fbi)
+ return -EINVAL;
+
+ memset(edid, 0, EDID_LENGTH*4);
+ memset(cfg, 0, sizeof(struct mxc_edid_cfg));
+
+ extblknum = mxc_edid_readblk(adp, addr, edid);
+ if (extblknum < 0)
+ return extblknum;
+
+ /* edid first block parsing */
+ memset(&fbi->monspecs, 0, sizeof(fbi->monspecs));
+ fb_edid_to_monspecs(edid, &fbi->monspecs);
+
+ if (extblknum) {
+ int i;
+
+ /* need read segment block? */
+ if (extblknum > 1) {
+ ret = mxc_edid_readsegblk(adp, addr,
+ edid + EDID_LENGTH*2, extblknum - 1);
+ if (ret < 0)
+ return ret;
+ }
+
+ for (i = 1; i <= extblknum; i++)
+ /* edid ext block parsing */
+ mxc_edid_parse_ext_blk(edid + i*EDID_LENGTH,
+ cfg, &fbi->monspecs);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(mxc_edid_read);
+
diff --git a/drivers/video/mxc/mxc_elcdif_fb.c b/drivers/video/mxc/mxc_elcdif_fb.c
new file mode 100644
index 00000000..08ca1e07
--- /dev/null
+++ b/drivers/video/mxc/mxc_elcdif_fb.c
@@ -0,0 +1,1723 @@
+/*
+ * Copyright (C) 2010-2013 Freescale Semiconductor, Inc.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+/*
+ * Based on drivers/video/mxc/mxc_ipuv3_fb.c, drivers/video/mxs/lcdif.c
+ * and arch/arm/mach-mx28/include/mach/lcdif.h.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/fb.h>
+#include <linux/fsl_devices.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/mxcfb.h>
+#include <linux/uaccess.h>
+
+#include <mach/hardware.h>
+
+#include "elcdif_regs.h"
+
+#define GDEBUG 10
+#include <linux/gallen_dbg.h>
+
+/* ELCDIF Pixel format definitions */
+/* Four-character-code (FOURCC) */
+#define fourcc(a, b, c, d) \
+ (((__u32)(a)<<0)|((__u32)(b)<<8)|((__u32)(c)<<16)|((__u32)(d)<<24))
+
+/*
+ * ELCDIF RGB Formats
+ */
+#define ELCDIF_PIX_FMT_RGB332 fourcc('R', 'G', 'B', '1')
+#define ELCDIF_PIX_FMT_RGB555 fourcc('R', 'G', 'B', 'O')
+#define ELCDIF_PIX_FMT_RGB565 fourcc('R', 'G', 'B', 'P')
+#define ELCDIF_PIX_FMT_RGB666 fourcc('R', 'G', 'B', '6')
+#define ELCDIF_PIX_FMT_BGR666 fourcc('B', 'G', 'R', '6')
+#define ELCDIF_PIX_FMT_BGR24 fourcc('B', 'G', 'R', '3')
+#define ELCDIF_PIX_FMT_RGB24 fourcc('R', 'G', 'B', '3')
+#define ELCDIF_PIX_FMT_BGR32 fourcc('B', 'G', 'R', '4')
+#define ELCDIF_PIX_FMT_BGRA32 fourcc('B', 'G', 'R', 'A')
+#define ELCDIF_PIX_FMT_RGB32 fourcc('R', 'G', 'B', '4')
+#define ELCDIF_PIX_FMT_RGBA32 fourcc('R', 'G', 'B', 'A')
+#define ELCDIF_PIX_FMT_ABGR32 fourcc('A', 'B', 'G', 'R')
+
+struct mxc_elcdif_fb_data {
+ int cur_blank;
+ int next_blank;
+ int output_pix_fmt;
+ int dma_irq;
+ bool wait4vsync;
+ bool wait4framedone;
+ bool panning;
+ bool running;
+ struct completion vsync_complete;
+ struct completion frame_done_complete;
+ struct semaphore flip_sem;
+ struct fb_var_screeninfo var;
+ u32 pseudo_palette[16];
+};
+
+struct elcdif_signal_cfg {
+ unsigned clk_pol:1; /* true = falling edge */
+ unsigned enable_pol:1; /* true = active high */
+ unsigned Hsync_pol:1; /* true = active high */
+ unsigned Vsync_pol:1; /* true = active high */
+};
+
+struct mxcfb_mode {
+ int dev_mode;
+ int num_modes;
+ struct fb_videomode *mode;
+};
+
+static int mxc_elcdif_fb_blank(int blank, struct fb_info *info);
+static int mxc_elcdif_fb_map_video_memory(struct fb_info *info);
+static int mxc_elcdif_fb_unmap_video_memory(struct fb_info *info);
+static char *fb_mode;
+static unsigned long default_bpp = 16;
+static void __iomem *elcdif_base;
+static struct device *g_elcdif_dev;
+static bool g_elcdif_axi_clk_enable;
+static bool g_elcdif_pix_clk_enable;
+static struct clk *g_elcdif_axi_clk;
+static struct clk *g_elcdif_pix_clk;
+static struct mxcfb_mode mxc_disp_mode;
+
+static void dump_fb_videomode(struct fb_videomode *m)
+{
+ pr_debug("fb_videomode = %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
+ m->refresh, m->xres, m->yres, m->pixclock, m->left_margin,
+ m->right_margin, m->upper_margin, m->lower_margin,
+ m->hsync_len, m->vsync_len, m->sync, m->vmode, m->flag);
+}
+
+static inline void setup_dotclk_panel(u32 pixel_clk,
+ u16 v_pulse_width,
+ u16 v_period,
+ u16 v_wait_cnt,
+ u16 v_active,
+ u16 h_pulse_width,
+ u16 h_period,
+ u16 h_wait_cnt,
+ u16 h_active,
+ int in_pixel_format,
+ int out_pixel_format,
+ struct elcdif_signal_cfg sig_cfg,
+ int enable_present)
+{
+ u32 val, rounded_pixel_clk;
+ u32 rounded_parent_clk;
+ struct clk *clk_parent;
+
+ if (!g_elcdif_axi_clk_enable) {
+ clk_enable(g_elcdif_axi_clk);
+ g_elcdif_axi_clk_enable = true;
+ }
+
+ /* Init clocking */
+ dev_dbg(g_elcdif_dev, "pixel clk = %d\n", pixel_clk);
+
+ clk_parent = clk_get_parent(g_elcdif_pix_clk);
+ if (clk_parent == NULL)
+ dev_err(g_elcdif_dev, "%s Failed get clk parent\n", __func__);
+
+ rounded_pixel_clk = pixel_clk * 2;
+
+ rounded_parent_clk = clk_round_rate(clk_parent,
+ rounded_pixel_clk);
+
+ while (rounded_pixel_clk < rounded_parent_clk) {
+ /* the max divider from parent to di is 8 */
+ if (rounded_parent_clk / pixel_clk < 8)
+ rounded_pixel_clk += pixel_clk * 2;
+ }
+
+ clk_set_rate(clk_parent, rounded_pixel_clk);
+ rounded_pixel_clk =
+ clk_round_rate(g_elcdif_pix_clk, pixel_clk);
+ clk_set_rate(g_elcdif_pix_clk, rounded_pixel_clk);
+
+ msleep(5);
+
+ __raw_writel(BM_ELCDIF_CTRL_DATA_SHIFT_DIR,
+ elcdif_base + HW_ELCDIF_CTRL_CLR);
+
+ __raw_writel(BM_ELCDIF_CTRL_SHIFT_NUM_BITS,
+ elcdif_base + HW_ELCDIF_CTRL_CLR);
+
+ __raw_writel(BM_ELCDIF_CTRL2_OUTSTANDING_REQS,
+ elcdif_base + HW_ELCDIF_CTRL2_CLR);
+ __raw_writel(BF_ELCDIF_CTRL2_OUTSTANDING_REQS
+ (BV_ELCDIF_CTRL2_OUTSTANDING_REQS__REQ_16),
+ elcdif_base + HW_ELCDIF_CTRL2_SET);
+
+ /* Recover on underflow */
+ __raw_writel(BM_ELCDIF_CTRL1_RECOVER_ON_UNDERFLOW,
+ elcdif_base + HW_ELCDIF_CTRL1_SET);
+
+ /* Configure the input pixel format */
+ __raw_writel(BM_ELCDIF_CTRL_WORD_LENGTH |
+ BM_ELCDIF_CTRL_INPUT_DATA_SWIZZLE |
+ BM_ELCDIF_CTRL_DATA_FORMAT_16_BIT |
+ BM_ELCDIF_CTRL_DATA_FORMAT_18_BIT |
+ BM_ELCDIF_CTRL_DATA_FORMAT_24_BIT,
+ elcdif_base + HW_ELCDIF_CTRL_CLR);
+ __raw_writel(BM_ELCDIF_CTRL1_BYTE_PACKING_FORMAT,
+ elcdif_base + HW_ELCDIF_CTRL1_CLR);
+ switch (in_pixel_format) {
+ case ELCDIF_PIX_FMT_RGB565:
+ __raw_writel(BF_ELCDIF_CTRL1_BYTE_PACKING_FORMAT(0xF),
+ elcdif_base + HW_ELCDIF_CTRL1_SET);
+ __raw_writel(BF_ELCDIF_CTRL_WORD_LENGTH(0) |
+ BF_ELCDIF_CTRL_INPUT_DATA_SWIZZLE(0),
+ elcdif_base + HW_ELCDIF_CTRL_SET);
+ break;
+ case ELCDIF_PIX_FMT_RGB24:
+ __raw_writel(BF_ELCDIF_CTRL1_BYTE_PACKING_FORMAT(0xF),
+ elcdif_base + HW_ELCDIF_CTRL1_SET);
+ __raw_writel(BF_ELCDIF_CTRL_WORD_LENGTH(3) |
+ BF_ELCDIF_CTRL_INPUT_DATA_SWIZZLE(0),
+ elcdif_base + HW_ELCDIF_CTRL_SET);
+ break;
+ case ELCDIF_PIX_FMT_RGB32:
+ __raw_writel(BF_ELCDIF_CTRL1_BYTE_PACKING_FORMAT(0x7),
+ elcdif_base + HW_ELCDIF_CTRL1_SET);
+ __raw_writel(BF_ELCDIF_CTRL_WORD_LENGTH(3) |
+ BF_ELCDIF_CTRL_INPUT_DATA_SWIZZLE(0),
+ elcdif_base + HW_ELCDIF_CTRL_SET);
+ break;
+ default:
+ dev_err(g_elcdif_dev, "ELCDIF unsupported input pixel format "
+ "%d\n", in_pixel_format);
+ break;
+ }
+
+ /* Configure the output pixel format */
+ __raw_writel(BM_ELCDIF_CTRL_LCD_DATABUS_WIDTH,
+ elcdif_base + HW_ELCDIF_CTRL_CLR);
+ switch (out_pixel_format) {
+ case ELCDIF_PIX_FMT_RGB565:
+ __raw_writel(BF_ELCDIF_CTRL_LCD_DATABUS_WIDTH(0),
+ elcdif_base + HW_ELCDIF_CTRL_SET);
+ break;
+ case ELCDIF_PIX_FMT_RGB666:
+ __raw_writel(BF_ELCDIF_CTRL_LCD_DATABUS_WIDTH(2),
+ elcdif_base + HW_ELCDIF_CTRL_SET);
+ break;
+ case ELCDIF_PIX_FMT_RGB24:
+ __raw_writel(BF_ELCDIF_CTRL_LCD_DATABUS_WIDTH(3),
+ elcdif_base + HW_ELCDIF_CTRL_SET);
+ break;
+ default:
+ dev_err(g_elcdif_dev, "ELCDIF unsupported output pixel format "
+ "%d\n", out_pixel_format);
+ break;
+ }
+
+ val = __raw_readl(elcdif_base + HW_ELCDIF_TRANSFER_COUNT);
+ val &= ~(BM_ELCDIF_TRANSFER_COUNT_V_COUNT |
+ BM_ELCDIF_TRANSFER_COUNT_H_COUNT);
+ val |= BF_ELCDIF_TRANSFER_COUNT_H_COUNT(h_active) |
+ BF_ELCDIF_TRANSFER_COUNT_V_COUNT(v_active);
+ __raw_writel(val, elcdif_base + HW_ELCDIF_TRANSFER_COUNT);
+
+ __raw_writel(BM_ELCDIF_CTRL_VSYNC_MODE,
+ elcdif_base + HW_ELCDIF_CTRL_CLR);
+ __raw_writel(BM_ELCDIF_CTRL_WAIT_FOR_VSYNC_EDGE,
+ elcdif_base + HW_ELCDIF_CTRL_CLR);
+ __raw_writel(BM_ELCDIF_CTRL_DVI_MODE,
+ elcdif_base + HW_ELCDIF_CTRL_CLR);
+ __raw_writel(BM_ELCDIF_CTRL_DOTCLK_MODE,
+ elcdif_base + HW_ELCDIF_CTRL_SET);
+ __raw_writel(BM_ELCDIF_CTRL_BYPASS_COUNT,
+ elcdif_base + HW_ELCDIF_CTRL_SET);
+
+ val = __raw_readl(elcdif_base + HW_ELCDIF_VDCTRL0);
+ val &= ~(BM_ELCDIF_VDCTRL0_VSYNC_POL |
+ BM_ELCDIF_VDCTRL0_HSYNC_POL |
+ BM_ELCDIF_VDCTRL0_ENABLE_POL |
+ BM_ELCDIF_VDCTRL0_DOTCLK_POL);
+ if (sig_cfg.Vsync_pol)
+ val |= BM_ELCDIF_VDCTRL0_VSYNC_POL;
+ if (sig_cfg.Hsync_pol)
+ val |= BM_ELCDIF_VDCTRL0_HSYNC_POL;
+ if (sig_cfg.clk_pol)
+ val |= BM_ELCDIF_VDCTRL0_DOTCLK_POL;
+ if (sig_cfg.enable_pol)
+ val |= BM_ELCDIF_VDCTRL0_ENABLE_POL;
+ __raw_writel(val, elcdif_base + HW_ELCDIF_VDCTRL0);
+
+ /* vsync is output */
+ val = __raw_readl(elcdif_base + HW_ELCDIF_VDCTRL0);
+ val &= ~(BM_ELCDIF_VDCTRL0_VSYNC_OEB);
+ __raw_writel(val, elcdif_base + HW_ELCDIF_VDCTRL0);
+
+ /*
+ * need enable sig for true RGB i/f. Or, if not true RGB, leave it
+ * zero.
+ */
+ if (enable_present) {
+ val = __raw_readl(elcdif_base + HW_ELCDIF_VDCTRL0);
+ val |= BM_ELCDIF_VDCTRL0_ENABLE_PRESENT;
+ __raw_writel(val, elcdif_base + HW_ELCDIF_VDCTRL0);
+ }
+
+ /*
+ * For DOTCLK mode, count VSYNC_PERIOD in terms of complete hz lines
+ */
+ val = __raw_readl(elcdif_base + HW_ELCDIF_VDCTRL0);
+ val &= ~(BM_ELCDIF_VDCTRL0_VSYNC_PERIOD_UNIT |
+ BM_ELCDIF_VDCTRL0_VSYNC_PULSE_WIDTH_UNIT);
+ val |= BM_ELCDIF_VDCTRL0_VSYNC_PERIOD_UNIT |
+ BM_ELCDIF_VDCTRL0_VSYNC_PULSE_WIDTH_UNIT;
+ __raw_writel(val, elcdif_base + HW_ELCDIF_VDCTRL0);
+
+ __raw_writel(BM_ELCDIF_VDCTRL0_VSYNC_PULSE_WIDTH,
+ elcdif_base + HW_ELCDIF_VDCTRL0_CLR);
+ __raw_writel(v_pulse_width, elcdif_base + HW_ELCDIF_VDCTRL0_SET);
+
+ __raw_writel(BF_ELCDIF_VDCTRL1_VSYNC_PERIOD(v_period),
+ elcdif_base + HW_ELCDIF_VDCTRL1);
+
+ __raw_writel(BF_ELCDIF_VDCTRL2_HSYNC_PULSE_WIDTH(h_pulse_width) |
+ BF_ELCDIF_VDCTRL2_HSYNC_PERIOD(h_period),
+ elcdif_base + HW_ELCDIF_VDCTRL2);
+
+ val = __raw_readl(elcdif_base + HW_ELCDIF_VDCTRL4);
+ val &= ~BM_ELCDIF_VDCTRL4_DOTCLK_H_VALID_DATA_CNT;
+ val |= BF_ELCDIF_VDCTRL4_DOTCLK_H_VALID_DATA_CNT(h_active);
+ __raw_writel(val, elcdif_base + HW_ELCDIF_VDCTRL4);
+
+ val = __raw_readl(elcdif_base + HW_ELCDIF_VDCTRL3);
+ val &= ~(BM_ELCDIF_VDCTRL3_HORIZONTAL_WAIT_CNT |
+ BM_ELCDIF_VDCTRL3_VERTICAL_WAIT_CNT);
+ val |= BF_ELCDIF_VDCTRL3_HORIZONTAL_WAIT_CNT(h_wait_cnt) |
+ BF_ELCDIF_VDCTRL3_VERTICAL_WAIT_CNT(v_wait_cnt);
+ __raw_writel(val, elcdif_base + HW_ELCDIF_VDCTRL3);
+
+ val = __raw_readl(elcdif_base + HW_ELCDIF_VDCTRL4);
+ val |= BM_ELCDIF_VDCTRL4_SYNC_SIGNALS_ON;
+ __raw_writel(val, elcdif_base + HW_ELCDIF_VDCTRL4);
+
+ return;
+}
+
+static inline void release_dotclk_panel(void)
+{
+ if (!g_elcdif_axi_clk_enable) {
+ clk_enable(g_elcdif_axi_clk);
+ g_elcdif_axi_clk_enable = true;
+ }
+
+ __raw_writel(BM_ELCDIF_CTRL_DOTCLK_MODE,
+ elcdif_base + HW_ELCDIF_CTRL_CLR);
+ __raw_writel(0, elcdif_base + HW_ELCDIF_VDCTRL0);
+ __raw_writel(0, elcdif_base + HW_ELCDIF_VDCTRL1);
+ __raw_writel(0, elcdif_base + HW_ELCDIF_VDCTRL2);
+ __raw_writel(0, elcdif_base + HW_ELCDIF_VDCTRL3);
+
+ return;
+}
+
+static inline void setup_dvi_panel(u16 h_active, u16 v_active,
+ u16 h_blanking, u16 v_lines,
+ u16 v1_blank_start, u16 v1_blank_end,
+ u16 v2_blank_start, u16 v2_blank_end,
+ u16 f1_start, u16 f1_end,
+ u16 f2_start, u16 f2_end)
+{
+ u32 val;
+
+ if (!g_elcdif_axi_clk_enable) {
+ clk_enable(g_elcdif_axi_clk);
+ g_elcdif_axi_clk_enable = true;
+ }
+
+ /* 32bit packed format (RGB) */
+ __raw_writel(BM_ELCDIF_CTRL1_BYTE_PACKING_FORMAT,
+ elcdif_base + HW_ELCDIF_CTRL1_CLR);
+ __raw_writel(BF_ELCDIF_CTRL1_BYTE_PACKING_FORMAT(0x7) |
+ BM_ELCDIF_CTRL1_RECOVER_ON_UNDERFLOW,
+ elcdif_base + HW_ELCDIF_CTRL1_SET);
+
+ val = __raw_readl(elcdif_base + HW_ELCDIF_TRANSFER_COUNT);
+ val &= ~(BM_ELCDIF_TRANSFER_COUNT_V_COUNT |
+ BM_ELCDIF_TRANSFER_COUNT_H_COUNT);
+ val |= BF_ELCDIF_TRANSFER_COUNT_H_COUNT(h_active) |
+ BF_ELCDIF_TRANSFER_COUNT_V_COUNT(v_active);
+ __raw_writel(val, elcdif_base + HW_ELCDIF_TRANSFER_COUNT);
+
+ /* set elcdif to DVI mode */
+ __raw_writel(BM_ELCDIF_CTRL_DVI_MODE,
+ elcdif_base + HW_ELCDIF_CTRL_SET);
+ __raw_writel(BM_ELCDIF_CTRL_VSYNC_MODE,
+ elcdif_base + HW_ELCDIF_CTRL_CLR);
+ __raw_writel(BM_ELCDIF_CTRL_DOTCLK_MODE,
+ elcdif_base + HW_ELCDIF_CTRL_CLR);
+
+ __raw_writel(BM_ELCDIF_CTRL_BYPASS_COUNT,
+ elcdif_base + HW_ELCDIF_CTRL_SET);
+ /* convert input RGB -> YCbCr */
+ __raw_writel(BM_ELCDIF_CTRL_RGB_TO_YCBCR422_CSC,
+ elcdif_base + HW_ELCDIF_CTRL_SET);
+ /* interlace odd and even fields */
+ __raw_writel(BM_ELCDIF_CTRL1_INTERLACE_FIELDS,
+ elcdif_base + HW_ELCDIF_CTRL1_SET);
+
+ __raw_writel(BM_ELCDIF_CTRL_WORD_LENGTH |
+ BM_ELCDIF_CTRL_INPUT_DATA_SWIZZLE |
+ BM_ELCDIF_CTRL_LCD_DATABUS_WIDTH,
+ elcdif_base + HW_ELCDIF_CTRL_CLR);
+ __raw_writel(BF_ELCDIF_CTRL_WORD_LENGTH(3) | /* 24 bit */
+ BM_ELCDIF_CTRL_DATA_SELECT | /* data mode */
+ BF_ELCDIF_CTRL_INPUT_DATA_SWIZZLE(0) | /* no swap */
+ BF_ELCDIF_CTRL_LCD_DATABUS_WIDTH(1), /* 8 bit */
+ elcdif_base + HW_ELCDIF_CTRL_SET);
+
+ /* ELCDIF_DVI */
+ /* set frame size */
+ val = __raw_readl(elcdif_base + HW_ELCDIF_DVICTRL0);
+ __raw_writel(val, elcdif_base + HW_ELCDIF_DVICTRL0);
+
+ /* set start/end of field-1 and start of field-2 */
+ val = __raw_readl(elcdif_base + HW_ELCDIF_DVICTRL1);
+ val &= ~(BM_ELCDIF_DVICTRL1_F1_START_LINE |
+ BM_ELCDIF_DVICTRL1_F1_END_LINE |
+ BM_ELCDIF_DVICTRL1_F2_START_LINE);
+ val |= BF_ELCDIF_DVICTRL1_F1_START_LINE(f1_start) |
+ BF_ELCDIF_DVICTRL1_F1_END_LINE(f1_end) |
+ BF_ELCDIF_DVICTRL1_F2_START_LINE(f2_start);
+ __raw_writel(val, elcdif_base + HW_ELCDIF_DVICTRL1);
+
+ /* set first vertical blanking interval and end of filed-2 */
+ val = __raw_readl(elcdif_base + HW_ELCDIF_DVICTRL2);
+ val &= ~(BM_ELCDIF_DVICTRL2_F2_END_LINE |
+ BM_ELCDIF_DVICTRL2_V1_BLANK_START_LINE |
+ BM_ELCDIF_DVICTRL2_V1_BLANK_END_LINE);
+ val |= BF_ELCDIF_DVICTRL2_F2_END_LINE(f2_end) |
+ BF_ELCDIF_DVICTRL2_V1_BLANK_START_LINE(v1_blank_start) |
+ BF_ELCDIF_DVICTRL2_V1_BLANK_END_LINE(v1_blank_end);
+ __raw_writel(val, elcdif_base + HW_ELCDIF_DVICTRL2);
+
+ /* set second vertical blanking interval */
+ val = __raw_readl(elcdif_base + HW_ELCDIF_DVICTRL3);
+ val &= ~(BM_ELCDIF_DVICTRL3_V2_BLANK_START_LINE |
+ BM_ELCDIF_DVICTRL3_V2_BLANK_END_LINE);
+ val |= BF_ELCDIF_DVICTRL3_V2_BLANK_START_LINE(v2_blank_start) |
+ BF_ELCDIF_DVICTRL3_V2_BLANK_END_LINE(v2_blank_end);
+ __raw_writel(val, elcdif_base + HW_ELCDIF_DVICTRL3);
+
+ /* fill the rest area black color if the input frame
+ * is not 720 pixels/line
+ */
+ if (h_active != 720) {
+ /* the input frame can't be less then (720-256) pixels/line */
+ if (720 - h_active > 0xff)
+ h_active = 720 - 0xff;
+
+ val = __raw_readl(elcdif_base + HW_ELCDIF_DVICTRL4);
+ val &= ~(BM_ELCDIF_DVICTRL4_H_FILL_CNT |
+ BM_ELCDIF_DVICTRL4_Y_FILL_VALUE |
+ BM_ELCDIF_DVICTRL4_CB_FILL_VALUE |
+ BM_ELCDIF_DVICTRL4_CR_FILL_VALUE);
+ val |= BF_ELCDIF_DVICTRL4_H_FILL_CNT(720 - h_active) |
+ BF_ELCDIF_DVICTRL4_Y_FILL_VALUE(16) |
+ BF_ELCDIF_DVICTRL4_CB_FILL_VALUE(128) |
+ BF_ELCDIF_DVICTRL4_CR_FILL_VALUE(128);
+ __raw_writel(val, elcdif_base + HW_ELCDIF_DVICTRL4);
+ }
+
+ /* Color Space Conversion RGB->YCbCr */
+ val = __raw_readl(elcdif_base + HW_ELCDIF_CSC_COEFF0);
+ val &= ~(BM_ELCDIF_CSC_COEFF0_C0 |
+ BM_ELCDIF_CSC_COEFF0_CSC_SUBSAMPLE_FILTER);
+ val |= BF_ELCDIF_CSC_COEFF0_C0(0x41) |
+ BF_ELCDIF_CSC_COEFF0_CSC_SUBSAMPLE_FILTER(3);
+ __raw_writel(val, elcdif_base + HW_ELCDIF_CSC_COEFF0);
+
+ val = __raw_readl(elcdif_base + HW_ELCDIF_CSC_COEFF1);
+ val &= ~(BM_ELCDIF_CSC_COEFF1_C1 | BM_ELCDIF_CSC_COEFF1_C2);
+ val |= BF_ELCDIF_CSC_COEFF1_C1(0x81) |
+ BF_ELCDIF_CSC_COEFF1_C2(0x19);
+ __raw_writel(val, elcdif_base + HW_ELCDIF_CSC_COEFF1);
+
+ val = __raw_readl(elcdif_base + HW_ELCDIF_CSC_COEFF2);
+ val &= ~(BM_ELCDIF_CSC_COEFF2_C3 | BM_ELCDIF_CSC_COEFF2_C4);
+ val |= BF_ELCDIF_CSC_COEFF2_C3(0x3DB) |
+ BF_ELCDIF_CSC_COEFF2_C4(0x3B6);
+ __raw_writel(val, elcdif_base + HW_ELCDIF_CSC_COEFF2);
+
+ val = __raw_readl(elcdif_base + HW_ELCDIF_CSC_COEFF3);
+ val &= ~(BM_ELCDIF_CSC_COEFF3_C5 | BM_ELCDIF_CSC_COEFF3_C6);
+ val |= BF_ELCDIF_CSC_COEFF3_C5(0x70) |
+ BF_ELCDIF_CSC_COEFF3_C6(0x70);
+ __raw_writel(val, elcdif_base + HW_ELCDIF_CSC_COEFF3);
+
+ val = __raw_readl(elcdif_base + HW_ELCDIF_CSC_COEFF4);
+ val &= ~(BM_ELCDIF_CSC_COEFF4_C7 | BM_ELCDIF_CSC_COEFF4_C8);
+ val |= BF_ELCDIF_CSC_COEFF4_C7(0x3A2) |
+ BF_ELCDIF_CSC_COEFF4_C8(0x3EE);
+ __raw_writel(val, elcdif_base + HW_ELCDIF_CSC_COEFF4);
+
+ val = __raw_readl(elcdif_base + HW_ELCDIF_CSC_OFFSET);
+ val &= ~(BM_ELCDIF_CSC_OFFSET_CBCR_OFFSET |
+ BM_ELCDIF_CSC_OFFSET_Y_OFFSET);
+ val |= BF_ELCDIF_CSC_OFFSET_CBCR_OFFSET(0x80) |
+ BF_ELCDIF_CSC_OFFSET_Y_OFFSET(0x10);
+ __raw_writel(val, elcdif_base + HW_ELCDIF_CSC_OFFSET);
+
+ val = __raw_readl(elcdif_base + HW_ELCDIF_CSC_LIMIT);
+ val &= ~(BM_ELCDIF_CSC_LIMIT_CBCR_MIN |
+ BM_ELCDIF_CSC_LIMIT_CBCR_MAX |
+ BM_ELCDIF_CSC_LIMIT_Y_MIN |
+ BM_ELCDIF_CSC_LIMIT_Y_MAX);
+ val |= BF_ELCDIF_CSC_LIMIT_CBCR_MIN(16) |
+ BF_ELCDIF_CSC_LIMIT_CBCR_MAX(240) |
+ BF_ELCDIF_CSC_LIMIT_Y_MIN(16) |
+ BF_ELCDIF_CSC_LIMIT_Y_MAX(235);
+ __raw_writel(val, elcdif_base + HW_ELCDIF_CSC_LIMIT);
+
+ return;
+}
+
+static inline void release_dvi_panel(void)
+{
+ if (!g_elcdif_axi_clk_enable) {
+ clk_enable(g_elcdif_axi_clk);
+ g_elcdif_axi_clk_enable = true;
+ }
+
+ __raw_writel(BM_ELCDIF_CTRL_DVI_MODE,
+ elcdif_base + HW_ELCDIF_CTRL_CLR);
+ return;
+}
+
+static inline void mxc_init_elcdif(void)
+{
+ if (!g_elcdif_axi_clk_enable) {
+ clk_enable(g_elcdif_axi_clk);
+ g_elcdif_axi_clk_enable = true;
+ }
+
+ __raw_writel(BM_ELCDIF_CTRL_CLKGATE,
+ elcdif_base + HW_ELCDIF_CTRL_CLR);
+ /* Reset controller */
+ __raw_writel(BM_ELCDIF_CTRL_SFTRST,
+ elcdif_base + HW_ELCDIF_CTRL_SET);
+ udelay(10);
+
+ /* Take controller out of reset */
+ __raw_writel(BM_ELCDIF_CTRL_SFTRST | BM_ELCDIF_CTRL_CLKGATE,
+ elcdif_base + HW_ELCDIF_CTRL_CLR);
+
+ /* Setup the bus protocol */
+ __raw_writel(BM_ELCDIF_CTRL1_MODE86,
+ elcdif_base + HW_ELCDIF_CTRL1_CLR);
+ __raw_writel(BM_ELCDIF_CTRL1_BUSY_ENABLE,
+ elcdif_base + HW_ELCDIF_CTRL1_CLR);
+
+ /* Take display out of reset */
+ __raw_writel(BM_ELCDIF_CTRL1_RESET,
+ elcdif_base + HW_ELCDIF_CTRL1_SET);
+
+ /* VSYNC is an input by default */
+ __raw_writel(BM_ELCDIF_VDCTRL0_VSYNC_OEB,
+ elcdif_base + HW_ELCDIF_VDCTRL0_SET);
+
+ /* Reset display */
+ __raw_writel(BM_ELCDIF_CTRL1_RESET,
+ elcdif_base + HW_ELCDIF_CTRL1_CLR);
+ udelay(10);
+ __raw_writel(BM_ELCDIF_CTRL1_RESET,
+ elcdif_base + HW_ELCDIF_CTRL1_SET);
+ udelay(10);
+
+ return;
+}
+
+void mxcfb_elcdif_register_mode(const struct fb_videomode *modedb,
+ int num_modes, int dev_mode)
+{
+ struct fb_videomode *mode;
+
+ mode = kzalloc(num_modes * sizeof(struct fb_videomode), GFP_KERNEL);
+
+ if (!mode) {
+ dev_err(g_elcdif_dev, "%s Failed to allocate mode data\n", __func__);
+ return;
+ }
+
+ if (mxc_disp_mode.num_modes)
+ memcpy(mode, mxc_disp_mode.mode,
+ mxc_disp_mode.num_modes * sizeof(struct fb_videomode));
+ if (modedb)
+ memcpy(mode + mxc_disp_mode.num_modes, modedb,
+ num_modes * sizeof(struct fb_videomode));
+
+ if (mxc_disp_mode.num_modes)
+ kfree(mxc_disp_mode.mode);
+
+ mxc_disp_mode.mode = mode;
+ mxc_disp_mode.num_modes += num_modes;
+ mxc_disp_mode.dev_mode = dev_mode;
+
+ return;
+}
+EXPORT_SYMBOL(mxcfb_elcdif_register_mode);
+
+int mxc_elcdif_frame_addr_setup(dma_addr_t phys)
+{
+ int ret = 0;
+
+ if (!g_elcdif_axi_clk_enable) {
+ clk_enable(g_elcdif_axi_clk);
+ g_elcdif_axi_clk_enable = true;
+ }
+
+ __raw_writel(BM_ELCDIF_CTRL_ELCDIF_MASTER,
+ elcdif_base + HW_ELCDIF_CTRL_SET);
+
+ __raw_writel(phys, elcdif_base + HW_ELCDIF_CUR_BUF);
+ __raw_writel(phys, elcdif_base + HW_ELCDIF_NEXT_BUF);
+ return ret;
+}
+
+static inline void mxc_elcdif_dma_release(void)
+{
+ if (!g_elcdif_axi_clk_enable) {
+ clk_enable(g_elcdif_axi_clk);
+ g_elcdif_axi_clk_enable = true;
+ }
+
+ __raw_writel(BM_ELCDIF_CTRL_ELCDIF_MASTER,
+ elcdif_base + HW_ELCDIF_CTRL_CLR);
+ return;
+}
+
+static inline void mxc_elcdif_run(struct mxc_elcdif_fb_data *data)
+{
+ if (!g_elcdif_axi_clk_enable) {
+ clk_enable(g_elcdif_axi_clk);
+ g_elcdif_axi_clk_enable = true;
+ }
+
+ __raw_writel(BM_ELCDIF_CTRL_ELCDIF_MASTER,
+ elcdif_base + HW_ELCDIF_CTRL_SET);
+ __raw_writel(BM_ELCDIF_CTRL_RUN,
+ elcdif_base + HW_ELCDIF_CTRL_SET);
+
+ data->running = true;
+
+ return;
+}
+
+static inline void mxc_elcdif_stop(void)
+{
+ if (!g_elcdif_axi_clk_enable) {
+ clk_enable(g_elcdif_axi_clk);
+ g_elcdif_axi_clk_enable = true;
+ }
+
+ __raw_writel(BM_ELCDIF_CTRL_RUN,
+ elcdif_base + HW_ELCDIF_CTRL_CLR);
+ __raw_writel(BM_ELCDIF_CTRL_ELCDIF_MASTER,
+ elcdif_base + HW_ELCDIF_CTRL_CLR);
+ msleep(1);
+ __raw_writel(BM_ELCDIF_CTRL_CLKGATE, elcdif_base + HW_ELCDIF_CTRL_SET);
+ return;
+}
+
+static int mxc_elcdif_blank_panel(int blank)
+{
+ int ret = 0, count;
+
+ if (!g_elcdif_axi_clk_enable) {
+ clk_enable(g_elcdif_axi_clk);
+ g_elcdif_axi_clk_enable = true;
+ }
+
+ switch (blank) {
+ case FB_BLANK_NORMAL:
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_HSYNC_SUSPEND:
+ case FB_BLANK_POWERDOWN:
+ __raw_writel(BM_ELCDIF_CTRL_BYPASS_COUNT,
+ elcdif_base + HW_ELCDIF_CTRL_CLR);
+ for (count = 10000; count; count--) {
+ if (__raw_readl(elcdif_base + HW_ELCDIF_STAT) &
+ BM_ELCDIF_STAT_TXFIFO_EMPTY)
+ break;
+ msleep(1);
+ }
+ break;
+
+ case FB_BLANK_UNBLANK:
+ __raw_writel(BM_ELCDIF_CTRL_BYPASS_COUNT,
+ elcdif_base + HW_ELCDIF_CTRL_SET);
+ break;
+
+ default:
+ dev_err(g_elcdif_dev, "unknown blank parameter\n");
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int mxc_elcdif_init_panel(void)
+{
+ if (!g_elcdif_axi_clk_enable) {
+ clk_enable(g_elcdif_axi_clk);
+ g_elcdif_axi_clk_enable = true;
+ }
+
+ /*
+ * Make sure we do a high-to-low transition to reset the panel.
+ * First make it low for 100 msec, hi for 10 msec, low for 10 msec,
+ * then hi.
+ */
+ __raw_writel(BM_ELCDIF_CTRL1_RESET,
+ elcdif_base + HW_ELCDIF_CTRL1_CLR); /* low */
+ msleep(100);
+ __raw_writel(BM_ELCDIF_CTRL1_RESET,
+ elcdif_base + HW_ELCDIF_CTRL1_SET); /* high */
+ msleep(10);
+ __raw_writel(BM_ELCDIF_CTRL1_RESET,
+ elcdif_base + HW_ELCDIF_CTRL1_CLR); /* low */
+
+ /* For the Samsung, Reset must be held low at least 30 uSec
+ * Therefore, we'll hold it low for about 10 mSec just to be sure.
+ * Then we'll wait 1 mSec afterwards.
+ */
+ msleep(10);
+ __raw_writel(BM_ELCDIF_CTRL1_RESET,
+ elcdif_base + HW_ELCDIF_CTRL1_SET); /* high */
+ msleep(1);
+
+ return 0;
+}
+
+static uint32_t bpp_to_pixfmt(struct fb_info *fbi)
+{
+ uint32_t pixfmt = 0;
+
+ if (fbi->var.nonstd)
+ return fbi->var.nonstd;
+
+ switch (fbi->var.bits_per_pixel) {
+ case 32:
+ pixfmt = ELCDIF_PIX_FMT_RGB32;
+ break;
+ case 24:
+ pixfmt = ELCDIF_PIX_FMT_RGB24;
+ break;
+ case 18:
+ pixfmt = ELCDIF_PIX_FMT_RGB666;
+ break;
+ case 16:
+ pixfmt = ELCDIF_PIX_FMT_RGB565;
+ break;
+ case 8:
+ pixfmt = ELCDIF_PIX_FMT_RGB332;
+ break;
+ }
+ return pixfmt;
+}
+
+static int mxc_elcdif_fb_set_fix(struct fb_info *info)
+{
+ struct fb_fix_screeninfo *fix = &info->fix;
+ struct fb_var_screeninfo *var = &info->var;
+
+ fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
+
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->accel = FB_ACCEL_NONE;
+ fix->visual = FB_VISUAL_TRUECOLOR;
+ fix->xpanstep = 1;
+ fix->ypanstep = 1;
+
+ return 0;
+}
+
+static irqreturn_t lcd_irq_handler(int irq, void *dev_id)
+{
+ struct mxc_elcdif_fb_data *data = dev_id;
+ u32 status_lcd = __raw_readl(elcdif_base + HW_ELCDIF_CTRL1);
+ dev_dbg(g_elcdif_dev, "%s: irq %d\n", __func__, irq);
+
+ if ((status_lcd & BM_ELCDIF_CTRL1_VSYNC_EDGE_IRQ) &&
+ data->wait4vsync) {
+ dev_dbg(g_elcdif_dev, "%s: VSYNC irq\n", __func__);
+ __raw_writel(BM_ELCDIF_CTRL1_VSYNC_EDGE_IRQ_EN,
+ elcdif_base + HW_ELCDIF_CTRL1_CLR);
+ data->wait4vsync = 0;
+ complete(&data->vsync_complete);
+ }
+ if ((status_lcd & BM_ELCDIF_CTRL1_CUR_FRAME_DONE_IRQ) &&
+ data->wait4framedone) {
+ dev_dbg(g_elcdif_dev, "%s: frame done irq\n", __func__);
+ __raw_writel(BM_ELCDIF_CTRL1_CUR_FRAME_DONE_IRQ_EN,
+ elcdif_base + HW_ELCDIF_CTRL1_CLR);
+ if (data->panning) {
+ up(&data->flip_sem);
+ data->panning = 0;
+ }
+ data->wait4framedone = 0;
+ complete(&data->frame_done_complete);
+ }
+ if (status_lcd & BM_ELCDIF_CTRL1_UNDERFLOW_IRQ) {
+ dev_dbg(g_elcdif_dev, "%s: underflow irq\n", __func__);
+ __raw_writel(BM_ELCDIF_CTRL1_UNDERFLOW_IRQ,
+ elcdif_base + HW_ELCDIF_CTRL1_CLR);
+ }
+ if (status_lcd & BM_ELCDIF_CTRL1_OVERFLOW_IRQ) {
+ dev_dbg(g_elcdif_dev, "%s: overflow irq\n", __func__);
+ __raw_writel(BM_ELCDIF_CTRL1_OVERFLOW_IRQ,
+ elcdif_base + HW_ELCDIF_CTRL1_CLR);
+ }
+ return IRQ_HANDLED;
+}
+
+static inline u_int _chan_to_field(u_int chan, struct fb_bitfield *bf)
+{
+ chan &= 0xffff;
+ chan >>= 16 - bf->length;
+ return chan << bf->offset;
+}
+
+static int mxc_elcdif_fb_setcolreg(u_int regno, u_int red, u_int green,
+ u_int blue, u_int transp,
+ struct fb_info *fbi)
+{
+ unsigned int val;
+ int ret = 1;
+
+ /*
+ * If greyscale is true, then we convert the RGB value
+ * to greyscale no matter what visual we are using.
+ */
+ if (fbi->var.grayscale)
+ red = green = blue = (19595 * red + 38470 * green +
+ 7471 * blue) >> 16;
+ switch (fbi->fix.visual) {
+ case FB_VISUAL_TRUECOLOR:
+ /*
+ * 16-bit True Colour. We encode the RGB value
+ * according to the RGB bitfield information.
+ */
+ if (regno < 16) {
+ u32 *pal = fbi->pseudo_palette;
+
+ val = _chan_to_field(red, &fbi->var.red);
+ val |= _chan_to_field(green, &fbi->var.green);
+ val |= _chan_to_field(blue, &fbi->var.blue);
+
+ pal[regno] = val;
+ ret = 0;
+ }
+ break;
+
+ case FB_VISUAL_STATIC_PSEUDOCOLOR:
+ case FB_VISUAL_PSEUDOCOLOR:
+ break;
+ }
+ return ret;
+}
+
+/**
+ This function compare the fb parameter see whether it was different
+ parameter for hardware, if it was different parameter, the hardware
+ will reinitialize. All will compared except x/y offset.
+ */
+static bool mxc_elcdif_fb_par_equal(struct fb_info *fbi, struct mxc_elcdif_fb_data *data)
+{
+ /* Here we set the xoffset, yoffset to zero, and compare two
+ * var see have different or not. */
+ struct fb_var_screeninfo oldvar = data->var;
+ struct fb_var_screeninfo newvar = fbi->var;
+
+ if ((fbi->var.activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW &&
+ fbi->var.activate & FB_ACTIVATE_FORCE)
+ return false;
+
+ oldvar.xoffset = newvar.xoffset = 0;
+ oldvar.yoffset = newvar.yoffset = 0;
+
+ return memcmp(&oldvar, &newvar, sizeof(struct fb_var_screeninfo)) == 0;
+}
+
+/*
+ * This routine actually sets the video mode. It's in here where we
+ * the hardware state info->par and fix which can be affected by the
+ * change in par. For this driver it doesn't do much.
+ *
+ */
+static int mxc_elcdif_fb_set_par(struct fb_info *fbi)
+{
+ struct mxc_elcdif_fb_data *data = (struct mxc_elcdif_fb_data *)fbi->par;
+ struct elcdif_signal_cfg sig_cfg;
+ int mem_len;
+
+ GALLEN_DBGLOCAL_BEGIN();
+
+ dev_dbg(fbi->device, "Reconfiguring framebuffer\n");
+
+ /* If parameter no change, don't reconfigure. */
+ if (mxc_elcdif_fb_par_equal(fbi, data) && (data->running == true)) {
+ GALLEN_DBGLOCAL_ESC();
+ return 0;
+ }
+
+ sema_init(&data->flip_sem, 1);
+
+ /* release prev panel */
+ if (!g_elcdif_pix_clk_enable) {
+ GALLEN_DBGLOCAL_RUNLOG(0);
+ clk_enable(g_elcdif_pix_clk);
+ printk("[GALLEN DBG][SKIP %s-%d]\n",__FUNCTION__,__LINE__);GALLEN_DBGLOCAL_ESC();return -EIO;
+ g_elcdif_pix_clk_enable = true;
+ }
+ printk("[GALLEN DBG][SKIP %s-%d]\n",__FUNCTION__,__LINE__);GALLEN_DBGLOCAL_ESC();return -EIO;
+ mxc_elcdif_blank_panel(FB_BLANK_POWERDOWN);
+ printk("[GALLEN DBG][SKIP %s-%d]\n",__FUNCTION__,__LINE__);GALLEN_DBGLOCAL_ESC();return -EIO;
+ mxc_elcdif_stop();
+ release_dotclk_panel();
+ mxc_elcdif_dma_release();
+ mxc_elcdif_fb_set_fix(fbi);
+ if (g_elcdif_pix_clk_enable) {
+ GALLEN_DBGLOCAL_RUNLOG(1);
+ clk_disable(g_elcdif_pix_clk);
+ g_elcdif_pix_clk_enable = false;
+ }
+
+ mem_len = fbi->var.yres_virtual * fbi->fix.line_length;
+ if (!fbi->fix.smem_start || (mem_len > fbi->fix.smem_len)) {
+ GALLEN_DBGLOCAL_RUNLOG(2);
+ if (fbi->fix.smem_start) {
+ GALLEN_DBGLOCAL_RUNLOG(3);
+ mxc_elcdif_fb_unmap_video_memory(fbi);
+ }
+
+ if (mxc_elcdif_fb_map_video_memory(fbi) < 0) {
+ GALLEN_DBGLOCAL_ESC();
+ return -ENOMEM;
+ }
+ }
+
+ if (data->next_blank != FB_BLANK_UNBLANK) {
+ GALLEN_DBGLOCAL_ESC();
+ return 0;
+ }
+
+ printk("[GALLEN DBG][SKIP %s-%d]\n",__FUNCTION__,__LINE__);return -EIO; // pass
+ /* init next panel */
+ if (!g_elcdif_pix_clk_enable) {
+ GALLEN_DBGLOCAL_RUNLOG(4);
+ clk_enable(g_elcdif_pix_clk);
+ g_elcdif_pix_clk_enable = true;
+ }
+ mxc_init_elcdif();
+ mxc_elcdif_init_panel();
+
+ dev_dbg(fbi->device, "pixclock = %u Hz\n",
+ (u32) (PICOS2KHZ(fbi->var.pixclock) * 1000UL));
+
+ memset(&sig_cfg, 0, sizeof(sig_cfg));
+ if (fbi->var.sync & FB_SYNC_HOR_HIGH_ACT) {
+ GALLEN_DBGLOCAL_RUNLOG(5);
+ sig_cfg.Hsync_pol = true;
+ }
+
+ if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT) {
+ GALLEN_DBGLOCAL_RUNLOG(6);
+ sig_cfg.Vsync_pol = true;
+ }
+
+ if (fbi->var.sync & FB_SYNC_CLK_LAT_FALL) {
+ GALLEN_DBGLOCAL_RUNLOG(6);
+ sig_cfg.clk_pol = true;
+ }
+
+ if (!(fbi->var.sync & FB_SYNC_OE_LOW_ACT)) {
+ GALLEN_DBGLOCAL_RUNLOG(7);
+ sig_cfg.enable_pol = true;
+ }
+
+ printk("[GALLEN DBG][SKIP %s-%d]\n",__FUNCTION__,__LINE__);return -EIO;
+
+ setup_dotclk_panel((PICOS2KHZ(fbi->var.pixclock)) * 1000UL,
+ fbi->var.vsync_len,
+ fbi->var.upper_margin + fbi->var.yres +
+ fbi->var.lower_margin + fbi->var.vsync_len,
+ fbi->var.upper_margin + fbi->var.vsync_len,
+ fbi->var.yres,
+ fbi->var.hsync_len,
+ fbi->var.left_margin + fbi->var.xres +
+ fbi->var.right_margin + fbi->var.hsync_len,
+ fbi->var.left_margin + fbi->var.hsync_len,
+ fbi->var.xres,
+ bpp_to_pixfmt(fbi),
+ data->output_pix_fmt,
+ sig_cfg,
+ 1);
+ mxc_elcdif_frame_addr_setup(fbi->fix.smem_start);
+ mxc_elcdif_run(data);
+ mxc_elcdif_blank_panel(FB_BLANK_UNBLANK);
+
+ fbi->mode = (struct fb_videomode *)fb_match_mode(&fbi->var,
+ &fbi->modelist);
+ data->var = fbi->var;
+
+ GALLEN_DBGLOCAL_END();
+ return 0;
+}
+
+static int mxc_elcdif_fb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ //GALLEN_DBGLOCAL_BEGIN();
+ if (var->xres_virtual < var->xres)
+ var->xres_virtual = var->xres;
+ if (var->yres_virtual < var->yres)
+ var->yres_virtual = var->yres;
+
+ if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) &&
+ (var->bits_per_pixel != 16) && (var->bits_per_pixel != 8))
+ var->bits_per_pixel = default_bpp;
+
+ switch (var->bits_per_pixel) {
+ case 8:
+ var->red.length = 3;
+ var->red.offset = 5;
+ var->red.msb_right = 0;
+
+ var->green.length = 3;
+ var->green.offset = 2;
+ var->green.msb_right = 0;
+
+ var->blue.length = 2;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ var->transp.msb_right = 0;
+ break;
+ case 16:
+ var->red.length = 5;
+ var->red.offset = 11;
+ var->red.msb_right = 0;
+
+ var->green.length = 6;
+ var->green.offset = 5;
+ var->green.msb_right = 0;
+
+ var->blue.length = 5;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ var->transp.msb_right = 0;
+ break;
+ case 24:
+ var->red.length = 8;
+ var->red.offset = 16;
+ var->red.msb_right = 0;
+
+ var->green.length = 8;
+ var->green.offset = 8;
+ var->green.msb_right = 0;
+
+ var->blue.length = 8;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ var->transp.msb_right = 0;
+ break;
+ case 32:
+ var->red.length = 8;
+ var->red.offset = 16;
+ var->red.msb_right = 0;
+
+ var->green.length = 8;
+ var->green.offset = 8;
+ var->green.msb_right = 0;
+
+ var->blue.length = 8;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 8;
+ var->transp.offset = 24;
+ var->transp.msb_right = 0;
+ break;
+ }
+
+ var->height = -1;
+ var->width = -1;
+ var->grayscale = 0;
+ //GALLEN_DBGLOCAL_END();
+ return 0;
+}
+
+static int mxc_elcdif_fb_wait_for_vsync(struct fb_info *info)
+{
+ struct mxc_elcdif_fb_data *data =
+ (struct mxc_elcdif_fb_data *)info->par;
+ int ret = 0;
+
+ if (data->cur_blank != FB_BLANK_UNBLANK) {
+ dev_err(info->device, "can't wait for VSYNC when fb "
+ "is blank\n");
+ return -EINVAL;
+ }
+
+ init_completion(&data->vsync_complete);
+
+ __raw_writel(BM_ELCDIF_CTRL1_VSYNC_EDGE_IRQ,
+ elcdif_base + HW_ELCDIF_CTRL1_CLR);
+ data->wait4vsync = 1;
+ __raw_writel(BM_ELCDIF_CTRL1_VSYNC_EDGE_IRQ_EN,
+ elcdif_base + HW_ELCDIF_CTRL1_SET);
+ ret = wait_for_completion_interruptible_timeout(
+ &data->vsync_complete, 1 * HZ);
+ if (ret == 0) {
+ dev_err(info->device,
+ "MXC ELCDIF wait for vsync timeout\n");
+ data->wait4vsync = 0;
+ ret = -ETIME;
+ } else if (ret > 0) {
+ ret = 0;
+ }
+ return ret;
+}
+
+static int mxc_elcdif_fb_wait_for_frame_done(struct fb_info *info)
+{
+ struct mxc_elcdif_fb_data *data =
+ (struct mxc_elcdif_fb_data *)info->par;
+ int ret = 0;
+
+ if (data->cur_blank != FB_BLANK_UNBLANK) {
+ dev_err(info->device, "can't wait for frame done when fb "
+ "is blank\n");
+ return -EINVAL;
+ }
+
+ init_completion(&data->frame_done_complete);
+
+ __raw_writel(BM_ELCDIF_CTRL1_CUR_FRAME_DONE_IRQ,
+ elcdif_base + HW_ELCDIF_CTRL1_CLR);
+ data->wait4framedone = 1;
+ __raw_writel(BM_ELCDIF_CTRL1_CUR_FRAME_DONE_IRQ_EN,
+ elcdif_base + HW_ELCDIF_CTRL1_SET);
+ ret = wait_for_completion_interruptible_timeout(
+ &data->frame_done_complete, 1 * HZ);
+ if (ret == 0) {
+ dev_err(info->device,
+ "MXC ELCDIF wait for frame done timeout\n");
+ data->wait4framedone = 0;
+ ret = -ETIME;
+ } else if (ret > 0) {
+ ret = 0;
+ }
+ return ret;
+}
+
+static int mxc_elcdif_fb_ioctl(struct fb_info *info, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret = -EINVAL;
+
+ switch (cmd) {
+ case MXCFB_WAIT_FOR_VSYNC:
+ ret = mxc_elcdif_fb_wait_for_vsync(info);
+ break;
+ case MXCFB_GET_FB_BLANK:
+ {
+ struct mxc_elcdif_fb_data *data =
+ (struct mxc_elcdif_fb_data *)info->par;
+
+ if (put_user(data->cur_blank, (__u32 __user *)arg))
+ return -EFAULT;
+ break;
+ }
+ default:
+ break;
+ }
+ return ret;
+}
+
+static int mxc_elcdif_fb_blank(int blank, struct fb_info *info)
+{
+ struct mxc_elcdif_fb_data *data =
+ (struct mxc_elcdif_fb_data *)info->par;
+ int ret = 0;
+
+ if (data->cur_blank == blank)
+ return ret;
+
+ data->next_blank = blank;
+
+ if (!g_elcdif_pix_clk_enable) {
+ clk_enable(g_elcdif_pix_clk);
+ g_elcdif_pix_clk_enable = true;
+ }
+ ret = mxc_elcdif_blank_panel(blank);
+ if (ret == 0)
+ data->cur_blank = blank;
+ else
+ return ret;
+
+ if (blank == FB_BLANK_UNBLANK) {
+ info->var.activate = (info->var.activate & ~FB_ACTIVATE_MASK) |
+ FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;
+ ret = mxc_elcdif_fb_set_par(info);
+ if (ret)
+ return ret;
+ }
+
+ if (data->cur_blank != FB_BLANK_UNBLANK) {
+ if (g_elcdif_axi_clk_enable) {
+ clk_disable(g_elcdif_axi_clk);
+ g_elcdif_axi_clk_enable = false;
+ }
+ if (g_elcdif_pix_clk_enable) {
+ clk_disable(g_elcdif_pix_clk);
+ g_elcdif_pix_clk_enable = false;
+ }
+ } else {
+ if (!g_elcdif_axi_clk_enable) {
+ clk_enable(g_elcdif_axi_clk);
+ g_elcdif_axi_clk_enable = true;
+ }
+ if (!g_elcdif_pix_clk_enable) {
+ clk_enable(g_elcdif_pix_clk);
+ g_elcdif_pix_clk_enable = true;
+ }
+ }
+
+ return ret;
+}
+
+static int mxc_elcdif_fb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct mxc_elcdif_fb_data *data =
+ (struct mxc_elcdif_fb_data *)info->par;
+ unsigned long base = 0;
+
+ if (data->cur_blank != FB_BLANK_UNBLANK) {
+ dev_err(info->device, "can't do pan display when fb "
+ "is blank\n");
+ return -EINVAL;
+ }
+
+ if (var->xoffset > 0) {
+ dev_dbg(info->device, "x panning not supported\n");
+ return -EINVAL;
+ }
+
+ if ((var->yoffset + var->yres > var->yres_virtual)) {
+ dev_err(info->device, "y panning exceeds\n");
+ return -EINVAL;
+ }
+
+ /* update framebuffer visual */
+ base = (var->yoffset * var->xres_virtual + var->xoffset);
+ base = (var->bits_per_pixel) * base / 8;
+ base += info->fix.smem_start;
+
+ if (down_timeout(&data->flip_sem, HZ / 2)) {
+ dev_err(info->device, "timeout when waiting for flip irq\n");
+ return -ETIMEDOUT;
+ }
+
+ __raw_writel(base, elcdif_base + HW_ELCDIF_NEXT_BUF);
+
+ data->panning = 1;
+ return mxc_elcdif_fb_wait_for_frame_done(info);
+}
+
+static struct fb_ops mxc_elcdif_fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = mxc_elcdif_fb_check_var,
+ .fb_set_par = mxc_elcdif_fb_set_par,
+ .fb_setcolreg = mxc_elcdif_fb_setcolreg,
+ .fb_ioctl = mxc_elcdif_fb_ioctl,
+ .fb_blank = mxc_elcdif_fb_blank,
+ .fb_pan_display = mxc_elcdif_fb_pan_display,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+};
+
+/*!
+ * Allocates the DRAM memory for the frame buffer. This buffer is remapped
+ * into a non-cached, non-buffered, memory region to allow palette and pixel
+ * writes to occur without flushing the cache. Once this area is remapped,
+ * all virtual memory access to the video memory should occur at the new region.
+ *
+ * @param fbi framebuffer information pointer
+ *
+ * @return Error code indicating success or failure
+ */
+static int mxc_elcdif_fb_map_video_memory(struct fb_info *fbi)
+{
+ if (fbi->fix.smem_len < fbi->var.yres_virtual * fbi->fix.line_length)
+ fbi->fix.smem_len = fbi->var.yres_virtual *
+ fbi->fix.line_length;
+
+ fbi->screen_base = dma_alloc_writecombine(fbi->device,
+ fbi->fix.smem_len,
+ (dma_addr_t *)&fbi->fix.smem_start,
+ GFP_DMA);
+ if (fbi->screen_base == 0) {
+ dev_err(fbi->device, "Unable to allocate framebuffer memory\n");
+ fbi->fix.smem_len = 0;
+ fbi->fix.smem_start = 0;
+ return -EBUSY;
+ }
+
+ dev_dbg(fbi->device, "allocated fb @ paddr=0x%08X, size=%d.\n",
+ (uint32_t) fbi->fix.smem_start, fbi->fix.smem_len);
+
+ fbi->screen_size = fbi->fix.smem_len;
+
+ /* Clear the screen */
+ memset((char *)fbi->screen_base, 0, fbi->fix.smem_len);
+
+ return 0;
+}
+
+/*!
+ * De-allocates the DRAM memory for the frame buffer.
+ *
+ * @param fbi framebuffer information pointer
+ *
+ * @return Error code indicating success or failure
+ */
+static int mxc_elcdif_fb_unmap_video_memory(struct fb_info *fbi)
+{
+ dma_free_writecombine(fbi->device, fbi->fix.smem_len,
+ fbi->screen_base, fbi->fix.smem_start);
+ fbi->screen_base = 0;
+ fbi->fix.smem_start = 0;
+ fbi->fix.smem_len = 0;
+ return 0;
+}
+
+static ssize_t show_disp_dev(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (dev) {
+ char *panel_type = dev->platform_data;
+
+ if (panel_type)
+ return sprintf(buf, "%s", panel_type);
+ else
+ return sprintf(buf, "elcd default");
+ } else {
+ dev_err(dev, "%s[%s], none dev\n", __FILE__, __func__);
+ return 0;
+ }
+}
+static DEVICE_ATTR(fsl_disp_dev_property, S_IRUGO, show_disp_dev, NULL);
+
+static int mxc_elcdif_fb_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct mxc_elcdif_fb_data *data;
+ struct resource *res;
+ struct fb_info *fbi;
+ struct mxc_fb_platform_data *pdata = pdev->dev.platform_data;
+ const struct fb_videomode *mode;
+ struct fb_videomode m;
+ int num;
+
+ GALLEN_DBGLOCAL_BEGIN();
+
+ fbi = framebuffer_alloc(sizeof(struct mxc_elcdif_fb_data), &pdev->dev);
+ if (fbi == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ data = (struct mxc_elcdif_fb_data *)fbi->par;
+ data->cur_blank = data->next_blank = FB_BLANK_UNBLANK;
+
+ fbi->var.activate = FB_ACTIVATE_NOW;
+ fbi->fbops = &mxc_elcdif_fb_ops;
+ fbi->flags = FBINFO_FLAG_DEFAULT;
+ fbi->pseudo_palette = data->pseudo_palette;
+
+ ret = fb_alloc_cmap(&fbi->cmap, 16, 0);
+ if (ret)
+ goto out;
+
+ g_elcdif_dev = &pdev->dev;
+
+ {
+ g_elcdif_pix_clk = clk_get(g_elcdif_dev, "elcdif_pix");
+ clk_set_rate(g_elcdif_pix_clk, 25000000);
+ clk_enable(g_elcdif_pix_clk);
+ printk("[GALLEN DBG][SKIP %s-%d]\n",__FUNCTION__,__LINE__);
+ goto err0;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "cannot get IRQ resource\n");
+ ret = -ENODEV;
+ goto err0;
+ }
+ data->dma_irq = res->start;
+
+ ret = request_irq(data->dma_irq, lcd_irq_handler, 0,
+ "mxc_elcdif_fb", data);
+ if (ret) {
+ dev_err(&pdev->dev, "request_irq (%d) failed with error %d\n",
+ data->dma_irq, ret);
+ goto err0;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ ret = -ENODEV;
+ goto err1;
+ }
+ elcdif_base = ioremap(res->start, SZ_4K);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (res) {
+ fbi->fix.smem_len = res->end - res->start + 1;
+ fbi->fix.smem_start = res->start;
+ fbi->screen_base = ioremap(fbi->fix.smem_start,
+ fbi->fix.smem_len);
+ }
+
+ strcpy(fbi->fix.id, "mxc_elcdif_fb");
+
+ fbi->var.xres = 800;
+ fbi->var.yres = 480;
+
+ if (pdata && !data->output_pix_fmt)
+ data->output_pix_fmt = pdata->interface_pix_fmt;
+
+ INIT_LIST_HEAD(&fbi->modelist);
+
+ if (pdata && pdata->mode && pdata->num_modes)
+ fb_videomode_to_modelist(pdata->mode, pdata->num_modes,
+ &fbi->modelist);
+
+ if (mxc_disp_mode.num_modes) {
+ int i;
+ mode = mxc_disp_mode.mode;
+ num = mxc_disp_mode.num_modes;
+
+ for (i = 0; i < num; i++) {
+ /*
+ * FIXME now we do not support interlaced
+ * mode for ddc mode
+ */
+ if ((mxc_disp_mode.dev_mode
+ & MXC_DISP_DDC_DEV) &&
+ (mode[i].vmode & FB_VMODE_INTERLACED))
+ continue;
+ else {
+ dev_dbg(&pdev->dev, "Added mode %d:", i);
+ dev_dbg(&pdev->dev,
+ "xres = %d, yres = %d, freq = %d, vmode = %d, flag = %d\n",
+ mode[i].xres, mode[i].yres, mode[i].refresh, mode[i].vmode,
+ mode[i].flag);
+ fb_add_videomode(&mode[i], &fbi->modelist);
+ }
+ }
+ }
+
+ if (!fb_mode && pdata && pdata->mode_str)
+ fb_mode = pdata->mode_str;
+
+ if (fb_mode) {
+ dev_dbg(&pdev->dev, "default video mode %s\n", fb_mode);
+ ret = fb_find_mode(&fbi->var, fbi, fb_mode, NULL, 0, NULL,
+ default_bpp);
+ if ((ret == 1) || (ret == 2)) {
+ fb_var_to_videomode(&m, &fbi->var);
+ dump_fb_videomode(&m);
+ mode = fb_find_nearest_mode(&m,
+ &fbi->modelist);
+ fb_videomode_to_var(&fbi->var, mode);
+ } else if (pdata && pdata->mode && pdata->num_modes) {
+ ret = fb_find_mode(&fbi->var, fbi, fb_mode, pdata->mode,
+ pdata->num_modes, NULL, default_bpp);
+ if (!ret) {
+ dev_err(fbi->device,
+ "No valid video mode found");
+ goto err2;
+ }
+ } else {
+ dev_err(fbi->device,
+ "No valid video mode found");
+ goto err2;
+ }
+ }
+
+ mxc_elcdif_fb_check_var(&fbi->var, fbi);
+
+ fbi->var.xres_virtual = fbi->var.xres;
+ fbi->var.yres_virtual = fbi->var.yres * 3;
+
+ mxc_elcdif_fb_set_fix(fbi);
+
+ if (!res || !res->end)
+ if (mxc_elcdif_fb_map_video_memory(fbi) < 0) {
+ ret = -ENOMEM;
+ goto err2;
+ }
+
+ g_elcdif_axi_clk = clk_get(g_elcdif_dev, "elcdif_axi");
+ if (g_elcdif_axi_clk == NULL) {
+ dev_err(&pdev->dev, "can't get ELCDIF axi clk\n");
+ ret = -ENODEV;
+ goto err3;
+ }
+ g_elcdif_pix_clk = clk_get(g_elcdif_dev, "elcdif_pix");
+ if (g_elcdif_pix_clk == NULL) {
+ dev_err(&pdev->dev, "can't get ELCDIF pix clk\n");
+ ret = -ENODEV;
+ goto err3;
+ }
+ /*
+ * Set an appropriate pixel clk rate first, so that we can
+ * access ELCDIF registers.
+ */
+ clk_set_rate(g_elcdif_pix_clk, 25000000);
+ {
+ clk_enable(g_elcdif_pix_clk);
+ printk("skip elcdif ...\n");
+ goto err3;
+ }
+
+
+ fbi->var.activate |= FB_ACTIVATE_FORCE;
+ console_lock();
+ fbi->flags |= FBINFO_MISC_USEREVENT;
+ ret = fb_set_var(fbi, &fbi->var);
+ fbi->flags &= ~FBINFO_MISC_USEREVENT;
+ console_unlock();
+
+
+ if (data->cur_blank == FB_BLANK_UNBLANK) {
+ console_lock();
+ fb_blank(fbi, FB_BLANK_UNBLANK);
+ console_unlock();
+ }
+
+ ret = register_framebuffer(fbi);
+ if (ret)
+ goto err3;
+
+
+ fbi->dev->platform_data = pdata->panel_type;
+
+ ret = device_create_file(fbi->dev, &dev_attr_fsl_disp_dev_property);
+ if (ret)
+ dev_err(&pdev->dev, "Error %d on creating file for disp "
+ " device property\n", ret);
+
+ GALLEN_DBGLOCAL_ESC();
+
+ return 0;
+err3:
+ mxc_elcdif_fb_unmap_video_memory(fbi);
+err2:
+ iounmap(elcdif_base);
+err1:
+ free_irq(data->dma_irq, data);
+err0:
+ fb_dealloc_cmap(&fbi->cmap);
+ framebuffer_release(fbi);
+out:
+
+ GALLEN_DBGLOCAL_END();
+ return ret;
+}
+
+static int mxc_elcdif_fb_remove(struct platform_device *pdev)
+{
+ struct fb_info *fbi = platform_get_drvdata(pdev);
+ struct mxc_elcdif_fb_data *data = (struct mxc_elcdif_fb_data *)fbi->par;
+
+ mxc_elcdif_fb_blank(FB_BLANK_POWERDOWN, fbi);
+ mxc_elcdif_stop();
+ release_dotclk_panel();
+ mxc_elcdif_dma_release();
+
+ if (g_elcdif_axi_clk_enable) {
+ clk_disable(g_elcdif_axi_clk);
+ g_elcdif_axi_clk_enable = false;
+ }
+ if (g_elcdif_pix_clk_enable) {
+ clk_disable(g_elcdif_pix_clk);
+ g_elcdif_pix_clk_enable = false;
+ }
+ clk_put(g_elcdif_axi_clk);
+ clk_put(g_elcdif_pix_clk);
+
+ free_irq(data->dma_irq, data);
+ mxc_elcdif_fb_unmap_video_memory(fbi);
+
+ if (&fbi->cmap)
+ fb_dealloc_cmap(&fbi->cmap);
+
+ unregister_framebuffer(fbi);
+ framebuffer_release(fbi);
+
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+#if 0
+//#ifdef CONFIG_PM
+static int mxc_elcdif_fb_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct fb_info *fbi = platform_get_drvdata(pdev);
+ struct mxc_elcdif_fb_data *data = (struct mxc_elcdif_fb_data *)fbi->par;
+ int saved_blank;
+
+ GALLEN_DBGLOCAL_BEGIN();
+
+ console_lock();
+ fb_set_suspend(fbi, 1);
+ saved_blank = data->cur_blank;
+ mxc_elcdif_fb_blank(FB_BLANK_POWERDOWN, fbi);
+ data->next_blank = saved_blank;
+ if (!g_elcdif_pix_clk_enable) {
+ clk_enable(g_elcdif_pix_clk);
+ g_elcdif_pix_clk_enable = true;
+ }
+ mxc_elcdif_stop();
+ mxc_elcdif_dma_release();
+ if (g_elcdif_pix_clk_enable) {
+ clk_disable(g_elcdif_pix_clk);
+ g_elcdif_pix_clk_enable = false;
+ }
+ if (g_elcdif_axi_clk_enable) {
+ clk_disable(g_elcdif_axi_clk);
+ g_elcdif_axi_clk_enable = false;
+ }
+ data->running = false;
+ console_unlock();
+
+ GALLEN_DBGLOCAL_END();
+
+ return 0;
+}
+
+static int mxc_elcdif_fb_resume(struct platform_device *pdev)
+{
+ struct fb_info *fbi = platform_get_drvdata(pdev);
+ struct mxc_elcdif_fb_data *data = (struct mxc_elcdif_fb_data *)fbi->par;
+
+ GALLEN_DBGLOCAL_BEGIN();
+
+ console_lock();
+ mxc_elcdif_fb_blank(data->next_blank, fbi);
+ fb_set_suspend(fbi, 0);
+ console_unlock();
+
+ GALLEN_DBGLOCAL_END();
+ return 0;
+}
+#else
+#define mxc_elcdif_fb_suspend NULL
+#define mxc_elcdif_fb_resume NULL
+#endif
+
+static struct platform_driver mxc_elcdif_fb_driver = {
+ .probe = mxc_elcdif_fb_probe,
+ .remove = mxc_elcdif_fb_remove,
+ .suspend = mxc_elcdif_fb_suspend,
+ .resume = mxc_elcdif_fb_resume,
+ .driver = {
+ .name = "mxc_elcdif_fb",
+ .owner = THIS_MODULE,
+ },
+};
+
+/*
+ * Parse user specified options (`video=trident:')
+ * example:
+ * video=trident:800x600,bpp=16,noaccel
+ */
+int mxc_elcdif_fb_setup(char *options)
+{
+ char *opt;
+ if (!options || !*options)
+ return 0;
+ while ((opt = strsep(&options, ",")) != NULL) {
+ if (!*opt)
+ continue;
+
+ if (!strncmp(opt, "bpp=", 4))
+ default_bpp = simple_strtoul(opt + 4, NULL, 0);
+ else
+ fb_mode = opt;
+ }
+ return 0;
+}
+
+static int __init mxc_elcdif_fb_init(void)
+{
+ char *option = NULL;
+
+ if (fb_get_options("mxc_elcdif_fb", &option))
+ return -ENODEV;
+ mxc_elcdif_fb_setup(option);
+
+ return platform_driver_register(&mxc_elcdif_fb_driver);
+}
+
+static void __exit mxc_elcdif_fb_exit(void)
+{
+ platform_driver_unregister(&mxc_elcdif_fb_driver);
+}
+
+module_init(mxc_elcdif_fb_init);
+module_exit(mxc_elcdif_fb_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC ELCDIF Framebuffer Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/mxc/mxc_epdc_eink_module.c b/drivers/video/mxc/mxc_epdc_eink_module.c
new file mode 100644
index 00000000..1366a600
--- /dev/null
+++ b/drivers/video/mxc/mxc_epdc_eink_module.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2012 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL FREESCALE SEMICONDUCTOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include "eink_processing2.h"
+
+/*#if (LINUX_VERSION_CODE == KERNEL_VERSION(2,6,31))*/
+
+EXPORT_SYMBOL(do_aa_processing_v2_2_1);
+EXPORT_SYMBOL(do_aa_processing_v2_2_0);
+EXPORT_SYMBOL(do_aad_processing_v2_1_1);
+EXPORT_SYMBOL(do_aad_processing_v2_1_0);
+EXPORT_SYMBOL(set_aad_update_counter);
+EXPORT_SYMBOL(mxc_epdc_fb_prep_algorithm_data);
+EXPORT_SYMBOL(mxc_epdc_fb_fetch_vc_data);
+EXPORT_SYMBOL(mxc_epdc_fb_fetch_wxi_data);
+EXPORT_SYMBOL(mxc_epdc_fb_aa_info);
+/*#endif*/
+
+MODULE_LICENSE("Proprietary");
diff --git a/drivers/video/mxc/mxc_epdc_fake_s1d13522.c b/drivers/video/mxc/mxc_epdc_fake_s1d13522.c
new file mode 100644
index 00000000..1b0fac3e
--- /dev/null
+++ b/drivers/video/mxc/mxc_epdc_fake_s1d13522.c
@@ -0,0 +1,1123 @@
+
+// this file should be included by epdc driver from manufacturer .
+
+
+#include "fake_s1d13522.h"
+#include "lk_tps65185.h"
+#include "lk_fp9928.h"
+#include <linux/completion.h>
+
+
+#ifdef CONFIG_MACH_MX6SL_NTX//[
+ #define FAKE_S1D13522_PLATFORM_MX6 1
+#endif //]CONFIG_MACH_MX6SL_NTX
+//#define LM75_ENABLED 1
+
+
+#ifdef LM75_ENABLED//[
+#include "lk_lm75.h"
+#endif //]LM75_ENABLED
+
+
+//#define SYSFS_DBG 1
+//#define DITHER_ENABLE 1
+#define FW_IN_RAM 1
+
+#define WF_INIT 0
+#define WF_DU 1
+#define WF_GC16 2
+#define WF_GC4 3
+//
+static EPDFB_DC *gptDC;
+
+// global mxc update data ....
+static struct mxcfb_update_data g_mxc_upd_data;
+#ifdef FAKE_S1D13522_PLATFORM_MX6//[
+static struct mxcfb_update_marker_data g_mxc_upd_marker_data;
+#endif //]FAKE_S1D13522_PLATFORM_MX6
+
+extern int check_hardware_name(void);
+
+
+
+#include "ntx_hwconfig.h"
+extern volatile NTX_HWCONFIG *gptHWCFG;
+
+static int giLastTemprature = DEFAULT_TEMP;
+static volatile unsigned long gdwLastUpdateJiffies = 0;
+static int giIsInited = 0;
+DECLARE_COMPLETION(mxc_epdc_fake13522_inited);
+
+
+static char gcFB_snapshot_pathA[512+2];
+static int giFB_snapshot_enable;
+#ifdef OUTPUT_SNAPSHOT_IMGFILE//[
+static int giFB_snapshot_total=OUTPUT_SNAPSHOT_IMGFILE;
+#else //]OUTPUT_SNAPSHOT_IMGFILE
+static int giFB_snapshot_total=3;
+#endif//]OUTPUT_SNAPSHOT_IMGFILE
+static int giFB_snapshot_repeat;
+#ifdef DITHER_ENABLE//[
+static int giDither_enable;
+#endif //]DITHER_ENABLE
+
+
+
+static void epdc_powerup(struct mxc_epdc_fb_data *fb_data);
+
+
+//
+// private help functions prototype ...
+//
+
+
+static ssize_t waveform_mode_ver_read(struct device *dev, struct device_attribute *attr,char *buf);
+static ssize_t waveform_mode_ver_write(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t waveform_rev_read(struct device *dev, struct device_attribute *attr,char *buf);
+static ssize_t waveform_rev_write(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count);
+
+
+static ssize_t temperature_read(struct device *dev, struct device_attribute *attr,char *buf);
+static ssize_t temperature_write(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t waveform_mode_read(struct device *dev, struct device_attribute *attr,char *buf);
+static ssize_t waveform_mode_write(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count);
+
+#ifdef SYSFS_DBG//[
+static ssize_t dbg_read(struct device *dev, struct device_attribute *attr,char *buf);
+static ssize_t dbg_write(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count);
+#endif //]SYSFS_DBG
+static ssize_t fbsnapshot_path_read(struct device *dev, struct device_attribute *attr,char *buf);
+static ssize_t fbsnapshot_path_write(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count);
+static ssize_t fbsnapshot_enable_read(struct device *dev, struct device_attribute *attr,char *buf);
+static ssize_t fbsnapshot_enable_write(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t vcom_read(struct device *dev, struct device_attribute *attr,char *buf);
+static ssize_t vcom_write(struct device *dev, struct device_attribute *attr,const char *buf, size_t count);
+
+static ssize_t xwi_read(struct device *dev, struct device_attribute *attr,char *buf);
+static ssize_t xwi_write(struct device *dev, struct device_attribute *attr,const char *buf, size_t count);
+#ifdef DITHER_ENABLE//[
+static ssize_t dither_enable_read(struct device *dev, struct device_attribute *attr,char *buf);
+static ssize_t dither_enable_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+#endif //]DITHER_ENABLE
+
+static DEVICE_ATTR(waveform_mode_ver, 0666, waveform_mode_ver_read, waveform_mode_ver_write);
+static DEVICE_ATTR(waveform_rev, 0666, waveform_rev_read, waveform_rev_write);
+static DEVICE_ATTR(temperature, 0666, temperature_read, temperature_write);
+static DEVICE_ATTR(waveform_mode_gc16, 0666, waveform_mode_read, waveform_mode_write);
+static DEVICE_ATTR(waveform_mode_du, 0666, waveform_mode_read, waveform_mode_write);
+static DEVICE_ATTR(waveform_mode_gl16, 0666, waveform_mode_read, waveform_mode_write);
+static DEVICE_ATTR(waveform_mode_reagl, 0666, waveform_mode_read, waveform_mode_write);
+static DEVICE_ATTR(waveform_mode_reagld, 0666, waveform_mode_read, waveform_mode_write);
+static DEVICE_ATTR(waveform_mode_a2, 0666, waveform_mode_read, waveform_mode_write);
+static DEVICE_ATTR(fbsnapshot_path, 0666, fbsnapshot_path_read, fbsnapshot_path_write);
+static DEVICE_ATTR(fbsnapshot_enable, 0666, fbsnapshot_enable_read, fbsnapshot_enable_write);
+static DEVICE_ATTR(vcom, 0666, vcom_read, vcom_write);
+static DEVICE_ATTR(xwi, 0666, xwi_read, xwi_write);
+#ifdef SYSFS_DBG//[
+static DEVICE_ATTR(dbg, 0666, dbg_read, dbg_write);
+#endif //]SYSFS_DBG
+#ifdef DITHER_ENABLE//[
+static DEVICE_ATTR(dither_enable, 0666, dither_enable_read, dither_enable_write);
+#endif //]DITHER_ENABLE
+
+
+static ssize_t xwi_read(struct device *dev, struct device_attribute *attr,char *buf)
+{
+ if(g_fb_data&&g_fb_data->waveform_xwi_string)
+ {
+ sprintf(buf,"%s\n",g_fb_data->waveform_xwi_string);
+ return strlen(buf);
+ }
+ else {
+ return 0;
+ }
+}
+static ssize_t xwi_write(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return 0;
+}
+static ssize_t vcom_read(struct device *dev, struct device_attribute *attr,char *buf)
+{
+ int iVCOM_mV;
+ int iChk;
+
+ if(8==gptHWCFG->m_val.bDisplayCtrl) {
+ iChk = fp9928_vcom_get_cached(&iVCOM_mV);
+ if(iChk>=0) {
+ sprintf (buf,"%dmV\n",iVCOM_mV);
+ }
+ }
+ else if(6==gptHWCFG->m_val.bDisplayCtrl||7==gptHWCFG->m_val.bDisplayCtrl) {
+ iChk = tps65185_vcom_get_cached(&iVCOM_mV);
+ if(iChk>=0) {
+ sprintf (buf,"%dmV\n",iVCOM_mV);
+ }
+ }
+ else {
+ }
+ return strlen(buf);
+}
+
+static ssize_t vcom_write(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int iVCOM_mV;
+ int iChk;
+
+ sscanf(buf,"%dmV",&iVCOM_mV);
+ //printk("%s():user input %dmV\n",__FUNCTION__,iVCOM_mV);
+
+ if(iVCOM_mV<0) {
+ if(8==gptHWCFG->m_val.bDisplayCtrl) {
+ iChk = fp9928_vcom_set(iVCOM_mV,0);
+ if(iChk<0) {
+ printk(KERN_ERR"FP9928 VCOM %dmV write failed !\n",iVCOM_mV);
+ }
+ }
+ else if(6==gptHWCFG->m_val.bDisplayCtrl||7==gptHWCFG->m_val.bDisplayCtrl) {
+ iChk = tps65185_vcom_set(iVCOM_mV,0);
+ if(iChk<0) {
+ printk(KERN_ERR"TPS65185 VCOM %dmV write failed !\n",iVCOM_mV);
+ }
+ }
+ else {
+ }
+ }
+ return strlen(buf);
+}
+
+#ifdef SYSFS_DBG//[
+static char gcDbgStringA[128];
+static ssize_t dbg_read(struct device *dev, struct device_attribute *attr,char *buf)
+{
+ sprintf (buf,"%s\n",gcDbgStringA);
+ //printk(KERN_ALERT"%s():%s\n",__FUNCTION__,gcDbgStringA);
+ return strlen(buf);
+}
+static ssize_t dbg_write(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ sscanf(buf,"%s\n",gcDbgStringA);
+ //strcpy(gcFB_snapshot_pathA,buf);
+ printk(KERN_ALERT"%s():%s\n",__FUNCTION__,gcDbgStringA);
+ return strlen(buf);
+}
+#endif //]SYSFS_DBG
+static ssize_t fbsnapshot_path_read(struct device *dev, struct device_attribute *attr,char *buf)
+{
+
+ sprintf (buf,"%s\n",gcFB_snapshot_pathA);
+ return strlen(buf);
+}
+
+static ssize_t fbsnapshot_path_write(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ sscanf(buf,"%s\n",gcFB_snapshot_pathA);
+ //strcpy(gcFB_snapshot_pathA,buf);
+ return strlen(buf);
+}
+static ssize_t fbsnapshot_enable_read(struct device *dev, struct device_attribute *attr,char *buf)
+{
+ sprintf (buf,"%d %d %d\n",giFB_snapshot_enable,giFB_snapshot_total,giFB_snapshot_repeat);
+ return strlen(buf);
+}
+static ssize_t fbsnapshot_enable_write(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int iEnable=-1,iCapTotal=-1,iRepeat=-1;
+ sscanf(buf,"%d %d %d\n",&iEnable,&iCapTotal,&iRepeat);
+ switch(iEnable) {
+ case 0:
+ case 1:
+ case 2:
+ giFB_snapshot_enable=iEnable;
+ if(-1!=iRepeat) {
+ giFB_snapshot_repeat=iRepeat;
+ }
+ if(iCapTotal>0) {
+ giFB_snapshot_total=iCapTotal;
+ }
+ break;
+ default:
+ break;
+ }
+ return strlen(buf);
+}
+
+#ifdef DITHER_ENABLE//[
+static ssize_t dither_enable_read(struct device *dev, struct device_attribute *attr,char *buf)
+{
+ sprintf (buf,"%d\n",giDither_enable);
+ return strlen(buf);
+}
+
+static ssize_t dither_enable_write(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int iEnable;
+ sscanf(buf,"%d\n",&iEnable);
+ switch(iEnable) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ giDither_enable=iEnable;
+ break;
+ default:
+ break;
+ }
+ return strlen(buf);
+}
+
+static int dither_enable_state(void)
+{
+ return giDither_enable;
+}
+#else //][!DITHER_ENABLE
+static int dither_enable_state(void)
+{
+ return 0;
+}
+#endif //]DITHER_ENABLE
+
+static ssize_t waveform_mode_ver_read(struct device *dev, struct device_attribute *attr,char *buf)
+{
+ sprintf (buf,"0x%x\n",gbModeVersion);
+ return strlen(buf);
+}
+
+static ssize_t waveform_mode_ver_write(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ DBG_MSG("%s()\n",__FUNCTION__);
+ return count;
+}
+
+static ssize_t waveform_rev_read(struct device *dev, struct device_attribute *attr,char *buf)
+{
+ sprintf (buf,"0x%x\n",gbWFM_REV);
+ return strlen(buf);
+}
+
+static ssize_t waveform_rev_write(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ DBG_MSG("%s()\n",__FUNCTION__);
+ return count;
+}
+
+static ssize_t temperature_read(struct device *dev, struct device_attribute *attr,char *buf)
+{
+ sprintf (buf,"%d\n",giLastTemprature);
+ return strlen(buf);
+}
+
+static ssize_t temperature_write(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int iChk,iLastTemprature;
+
+ iLastTemprature = simple_strtol(buf,NULL,0);
+ iChk = mxc_epdc_fb_set_temperature(iLastTemprature,&g_fb_data->info);
+ if(0==iChk) {
+ DBG_MSG("%s(),temp <== %d\n",__FUNCTION__,iLastTemprature);
+ giLastTemprature = iLastTemprature;
+ gdwLastUpdateJiffies = jiffies+(60*HZ);
+ }
+ else {
+ ERR_MSG("%s(),temp <== %d fail!\n",__FUNCTION__,iLastTemprature);
+ }
+
+ return count;
+}
+
+static ssize_t waveform_mode_read(struct device *dev, struct device_attribute *attr,char *buf)
+{
+ int iWaveform_mode=-1,iWaveform_mode_eink=-1;
+
+ if(attr==&dev_attr_waveform_mode_du.attr) {
+ iWaveform_mode_eink = giNTX_waveform_modeA[NTX_WFM_MODE_DU];
+ iWaveform_mode = NTX_WFM_MODE_DU;
+ }
+ else
+ if(attr==&dev_attr_waveform_mode_a2.attr) {
+ iWaveform_mode_eink = giNTX_waveform_modeA[NTX_WFM_MODE_A2];
+ iWaveform_mode = NTX_WFM_MODE_A2;
+ }
+ else
+ if(attr==&dev_attr_waveform_mode_gc16.attr) {
+ iWaveform_mode_eink = giNTX_waveform_modeA[NTX_WFM_MODE_GC16];
+ iWaveform_mode = NTX_WFM_MODE_GC16;
+ }
+ else
+ if(attr==&dev_attr_waveform_mode_gl16.attr) {
+ iWaveform_mode_eink = giNTX_waveform_modeA[NTX_WFM_MODE_GL16];
+ iWaveform_mode = NTX_WFM_MODE_GL16;
+ }
+ else
+ if(attr==&dev_attr_waveform_mode_reagl.attr) {
+ iWaveform_mode_eink = giNTX_waveform_modeA[NTX_WFM_MODE_GLR16];
+ iWaveform_mode = NTX_WFM_MODE_GLR16;
+ }
+ else
+ if(attr==&dev_attr_waveform_mode_reagld.attr) {
+ iWaveform_mode_eink = giNTX_waveform_modeA[NTX_WFM_MODE_GLD16];
+ iWaveform_mode = NTX_WFM_MODE_GLD16;
+ }
+
+ sprintf (buf,"%d:%d\n",iWaveform_mode,iWaveform_mode_eink);
+ return strlen(buf);
+}
+
+static ssize_t waveform_mode_write(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ DBG_MSG("%s() not supported!\n",__FUNCTION__);
+ return count;
+}
+
+static const struct attribute *sysfs_epdc_attrs[] = {
+ &dev_attr_waveform_mode_ver.attr,
+ &dev_attr_waveform_rev.attr,
+ &dev_attr_temperature.attr,
+ &dev_attr_waveform_mode_du.attr,
+ &dev_attr_waveform_mode_a2.attr,
+ &dev_attr_waveform_mode_gc16.attr,
+ &dev_attr_waveform_mode_gl16.attr,
+ &dev_attr_waveform_mode_reagl.attr,
+ &dev_attr_waveform_mode_reagld.attr,
+#ifdef OUTPUT_SNAPSHOT_IMGFILE//[
+ &dev_attr_fbsnapshot_path.attr,
+ &dev_attr_fbsnapshot_enable.attr,
+#endif //]OUTPUT_SNAPSHOT_IMGFILE
+ &dev_attr_vcom.attr,
+ &dev_attr_xwi.attr,
+#ifdef SYSFS_DBG//[
+ &dev_attr_dbg.attr,
+#endif //]SYSFS_DBG
+#ifdef DITHER_ENABLE//[
+ &dev_attr_dither_enable.attr,
+#endif //]DITHER_ENABLE
+ NULL,
+};
+
+
+//////////////////////////////////////////////////////
+//
+// driver extention helper functions ...
+//
+int mxc_epdc_fb_check_update_complete(u32 update_marker, struct fb_info *info)
+{
+ struct mxc_epdc_fb_data *fb_data = info ?
+ (struct mxc_epdc_fb_data *)info:g_fb_data;
+ struct update_marker_data *next_marker;
+ struct update_marker_data *temp;
+ unsigned long flags;
+ bool marker_found = false;
+ int ret = 0;
+
+ //GALLEN_DBGLOCAL_BEGIN();
+
+ /* 0 is an invalid update_marker value */
+ if (update_marker == 0) {
+ //GALLEN_DBGLOCAL_ESC();
+ return -EINVAL;
+ }
+
+ /*
+ * Find completion associated with update_marker requested.
+ * Note: If update completed already, marker will have been
+ * cleared, it won't be found, and function will just return.
+ */
+
+ /* Grab queue lock to protect access to marker list */
+#ifdef FAKE_S1D13522_PLATFORM_MX6 //[
+ mutex_lock(&fb_data->queue_mutex);
+#else //][!FAKE_S1D13522_PLATFORM_MX6
+ spin_lock_irqsave(&fb_data->queue_lock, flags);
+#endif //] FAKE_S1D13522_PLATFORM_MX6
+
+ list_for_each_entry_safe(next_marker, temp,
+ &fb_data->full_marker_list, full_list) {
+ //GALLEN_DBGLOCAL_RUNLOG(0);
+ if (next_marker->update_marker == update_marker) {
+ //GALLEN_DBGLOCAL_RUNLOG(1);
+ dev_dbg(fb_data->dev, "Waiting for marker %d\n",
+ update_marker);
+ next_marker->waiting = true;
+ marker_found = true;
+ break;
+ }
+ }
+
+#ifdef FAKE_S1D13522_PLATFORM_MX6 //[
+ mutex_unlock(&fb_data->queue_mutex);
+#else //][!FAKE_S1D13522_PLATFORM_MX6
+ spin_unlock_irqrestore(&fb_data->queue_lock, flags);
+#endif //] FAKE_S1D13522_PLATFORM_MX6
+
+ /*
+ * If marker not found, it has either been signalled already
+ * or the update request failed. In either case, just return.
+ */
+ if (!marker_found) {
+ //GALLEN_DBGLOCAL_ESC();
+ return ret;
+ }
+
+ ret = completion_done(&next_marker->update_completion)?1:0;
+
+
+ //GALLEN_DBGLOCAL_END();
+ return ret;
+}
+//EXPORT_SYMBOL(mxc_epdc_fb_check_update_complete);
+
+
+
+//////////////////////////////////////////////////////
+//
+// fake_s1d13522 HAL interface .
+//
+
+
+static void k_fake_s1d13522_progress_start(void)
+{
+ if(0==giIsInited) {
+ printk("[%s]:skip before init .",__FUNCTION__);
+ return ;
+ }
+
+ //fake_s1d13522_progress_start(gptDC);
+}
+
+
+static int k_fake_s1d13522_wait_inited(void)
+{
+ int iRet=0;
+
+ if(giIsInited<=1)
+ {
+ if(in_interrupt()) {
+ printk("[%s]:skip before init (interrupt).",__FUNCTION__);
+ }
+ else {
+ printk("[%s]:wait init .",__FUNCTION__);
+ wait_for_completion(&mxc_epdc_fake13522_inited);
+ printk("[%s]:wait init ok.",__FUNCTION__);
+ }
+ }
+ return iRet;
+}
+
+static int32_t k_fake_s1d13522_ioctl(unsigned int cmd,unsigned long arg)
+{
+ //k_fake_s1d13522_wait_inited();
+ return fake_s1d13522_ioctl(cmd,arg,gptDC);
+}
+
+
+static void k_vcom_enable(int iIsEnable)
+{
+ if(iIsEnable) {
+ //vcom enable .
+ }
+ else {
+ //vcom disable .
+ }
+}
+
+static int k_get_wfbpp(void)
+{
+ int i_wf_bpp=4;
+
+ if(*(gpbWF_vaddr+0x10) == 0x2) {
+ i_wf_bpp=3;
+ }
+
+ return i_wf_bpp;
+}
+
+static int k_set_partial(int iIsSetPartial)
+{
+ u32 temp;
+ if(0==giIsInited) {
+ printk("[%s]:skip before init .",__FUNCTION__);
+ return -1;
+ }
+
+ if(iIsSetPartial) {
+ g_mxc_upd_data.update_mode = UPDATE_MODE_PARTIAL;
+ }
+ else {
+ g_mxc_upd_data.update_mode = UPDATE_MODE_FULL;
+ }
+ return 0;
+}
+
+static unsigned char *k_get_realfbEx(unsigned long *O_pdwFBSize)
+{
+ unsigned char *pbRet ;
+ if(0==giIsInited) {
+ printk("[%s]:skip before init .",__FUNCTION__);
+ return 0;
+ }
+
+ //pbRet = (unsigned char *)g_fb_data->working_buffer_virt;
+ pbRet = (unsigned char *)g_fb_data->info.screen_base;
+ if(O_pdwFBSize) {
+ *O_pdwFBSize = g_fb_data->info.screen_size;
+ }
+
+ return pbRet;
+}
+
+static void k_display_start(int iIsStart)
+{
+ int iChk;
+ if(0==giIsInited) {
+ printk("[%s]:skip before init .",__FUNCTION__);
+ return ;
+ }
+
+
+ if(iIsStart) {
+ #if 0
+ printk("%s(%d):==============================>\n",__FUNCTION__,__LINE__) ;
+ printk("\t%d bits/pixel\n",g_fb_data->info.var.bits_per_pixel) ;
+ printk("\t grayscale=%d \n",g_fb_data->info.var.grayscale) ;
+ printk("\t yoffset=%d \n",g_fb_data->info.var.yoffset) ;
+ printk("\t rotate=%d \n",g_fb_data->info.var.rotate) ;
+ printk("\t activate=%d \n",g_fb_data->info.var.activate) ;
+ printk("<======================================\n") ;
+ #endif
+
+ g_mxc_upd_data.update_marker=g_mxc_upd_marker_data.update_marker;
+ DBG_MSG("%s() (x,y)=(%u,%u),(w,h)(%u,%u),marker=%d\n",__FUNCTION__,
+ g_mxc_upd_data.update_region.top,g_mxc_upd_data.update_region.left,
+ g_mxc_upd_data.update_region.width,g_mxc_upd_data.update_region.height,
+ g_mxc_upd_data.update_marker);
+
+
+ iChk = mxc_epdc_fb_send_update(&g_mxc_upd_data,&g_fb_data->info);
+ if(iChk<0) {
+ printk(KERN_WARNING"%s(%d):mxc_epdc_fb_send_update fail !\n",
+ __FUNCTION__,__LINE__);
+ }
+
+ }
+ else {
+ }
+}
+
+
+static int k_get_wfmode(void)
+{
+ int i_wf_mode;
+ if(0==giIsInited) {
+ printk("[%s]:skip before init .",__FUNCTION__);
+ return 0;
+ }
+
+ i_wf_mode = g_mxc_upd_data.waveform_mode;
+ return i_wf_mode;
+}
+
+static void k_set_wfmode(int iWaveform)
+{
+ if(0==giIsInited) {
+ printk("[%s]:skip before init .",__FUNCTION__);
+ return ;
+ }
+
+ g_mxc_upd_data.waveform_mode = iWaveform;
+}
+
+
+static int k_is_updating(void)
+{
+ int iRet;
+ int iChk = 0;
+
+ if(0==giIsInited) {
+ printk("[%s]:skip before init .",__FUNCTION__);
+ return 0;
+ }
+
+ //if(epdc_is_working_buffer_busy())
+ iChk = mxc_epdc_fb_check_update_complete(g_mxc_upd_data.update_marker,&g_fb_data->info);
+
+ if(1==iChk)
+ {
+ // updating ...
+ iRet = 1;
+ }
+ else {
+ // update done
+ iRet = 0;
+ }
+ return iRet;
+}
+
+static int k_wait_update_complete(void)
+{
+ int iRet;
+ if(0==giIsInited) {
+ printk("[%s]:skip before init .",__FUNCTION__);
+ return 0;
+ }
+
+
+ //if(1==g_mxc_upd_data.waveform_mode||4==g_mxc_upd_data.waveform_mode) {
+ // skip wait update complete at DOC mode and A2 mode .
+ //iRet = 0;
+ //}
+ //else if(k_is_updating())
+ {
+ unsigned long dwJiffiesStart,dwJiffiesEnd;
+ dwJiffiesStart = jiffies ;
+#ifdef FAKE_S1D13522_PLATFORM_MX6//[
+ iRet = mxc_epdc_fb_wait_update_complete(&g_mxc_upd_marker_data,&g_fb_data->info);
+ g_mxc_upd_marker_data.update_marker++;
+#else //][! FAKE_S1D13522_PLATFORM_MX6
+ iRet = mxc_epdc_fb_wait_update_complete(g_mxc_upd_data.update_marker++,&g_fb_data->info);
+#endif //] FAKE_S1D13522_PLATFORM_MX6
+ dwJiffiesEnd = jiffies;
+ printk("[%s]waitupdate ret=%d,%u->%u\n",__FUNCTION__,iRet,\
+ (unsigned int)dwJiffiesStart,(unsigned int)dwJiffiesEnd);
+ }
+ return iRet;
+}
+
+
+
+/////////////////////////////////////////////////////////////
+// calling by real epdc driver .
+static int k_set_temperature(struct fb_info *info)
+{
+ int iRet;
+ int iChk;
+
+
+ //printk("%s(),timeout_tick=%u,current_tick=%u\n",__FUNCTION__,
+ // gdwLastUpdateJiffies,jiffies);
+
+ if(0==gdwLastUpdateJiffies||time_after(jiffies,gdwLastUpdateJiffies)) {
+
+
+ gdwLastUpdateJiffies = jiffies+(60*HZ);
+
+ ASSERT(gptHWCFG);
+
+ if(8==gptHWCFG->m_val.bDisplayCtrl) {
+ iChk = fp9928_get_temperature(&giLastTemprature);
+ }
+ else
+ if(6==gptHWCFG->m_val.bDisplayCtrl||7==gptHWCFG->m_val.bDisplayCtrl)
+ {
+ // imx508 + tps16585 .
+ iChk = tps65185_get_temperature(&giLastTemprature);
+ }
+
+#ifdef LM75_ENABLED//[
+ else {
+ iChk = lm75_get_temperature(0,&giLastTemprature);
+ }
+#else
+ else {
+ iChk = -100;
+ }
+#endif //]LM75_ENABLED
+
+ if(iChk>=0) {
+ //printk("%s():mxc_epdc_fb_set_temperature...\n",__FUNCTION__);// DBG
+ iChk = mxc_epdc_fb_set_temperature(giLastTemprature,info);
+ }
+ else {
+ gdwLastUpdateJiffies = jiffies;
+ }
+ }
+ return giLastTemprature;
+}
+
+////////////////////////////////////////////////////////////
+
+static int k_set_update_rect(unsigned short wX,unsigned short wY,
+ unsigned short wW,unsigned short wH)
+{
+ int iRet = 0;
+ if(0==giIsInited) {
+ printk("[%s]:skip before init .",__FUNCTION__);
+ return 0;
+ }
+
+
+ DBG_MSG("%s() x=%u,y=%u,w=%u,h=%u\n",__FUNCTION__,wX,wY,wW,wH);
+ g_mxc_upd_data.update_region.top = wY;
+ g_mxc_upd_data.update_region.left = wX;
+ g_mxc_upd_data.update_region.height = wH;
+ g_mxc_upd_data.update_region.width = wW;
+
+
+ return iRet;
+}
+
+static int k_set_vcom(int iVCOM_set_mV)
+{
+ int iRet=0;
+ ASSERT(gptHWCFG);
+ //printk("%s(%d):%s\n",__FILE__,__LINE__,__FUNCTION__);
+ if(8==gptHWCFG->m_val.bDisplayCtrl) {
+ iRet = fp9928_vcom_set(iVCOM_set_mV,0);
+ if(iRet>=0) {
+ int iVCOM_get_mV;
+ if(fp9928_vcom_get(&iVCOM_get_mV)>=0) {
+ vcom_nominal=iVCOM_get_mV*1000;/* save the vcom_nominal value in uV */
+ }
+ }
+ }
+ else
+ if(6==gptHWCFG->m_val.bDisplayCtrl||7==gptHWCFG->m_val.bDisplayCtrl) {
+ iRet = tps65185_vcom_set(iVCOM_set_mV,0);
+ if(iRet>=0) {
+ int iVCOM_get_mV;
+ if(tps65185_vcom_get(&iVCOM_get_mV)>=0) {
+ vcom_nominal=iVCOM_get_mV*1000;/* save the vcom_nominal value in uV */
+ }
+ }
+ }
+ else {
+ }
+
+ return iRet;
+}
+static int k_set_vcom_to_flash(int iVCOM_set_mV)
+{
+ int iRet=0;
+ //printk("%s(%d):%s\n",__FILE__,__LINE__,__FUNCTION__);
+ if(8==gptHWCFG->m_val.bDisplayCtrl) {
+ iRet = fp9928_vcom_set(iVCOM_set_mV,1);
+ if(iRet>=0) {
+ int iVCOM_get_mV;
+ if(fp9928_vcom_get(&iVCOM_get_mV)>=0) {
+ vcom_nominal=iVCOM_get_mV*1000;/* save the vcom_nominal value in uV */
+ }
+ }
+ }
+ else
+ if(gptHWCFG&&(6==gptHWCFG->m_val.bDisplayCtrl||7==gptHWCFG->m_val.bDisplayCtrl)) {
+ iRet = tps65185_vcom_set(iVCOM_set_mV,1);
+ if(iRet>=0) {
+ int iVCOM_get_mV;
+ if(tps65185_vcom_get(&iVCOM_get_mV)>=0) {
+ vcom_nominal=iVCOM_get_mV*1000;/* save the vcom_nominal value in uV */
+ }
+ }
+ }
+ else {
+ }
+ return iRet;
+}
+
+static int k_get_vcom(int *O_piVCOM_get_mV)
+{
+ int iRet=0;
+ //printk("%s(%d):%s\n",__FILE__,__LINE__,__FUNCTION__);
+ if(8==gptHWCFG->m_val.bDisplayCtrl) {
+ if(vcom_nominal) {
+ if(O_piVCOM_get_mV) {
+ *O_piVCOM_get_mV = vcom_nominal/1000;
+ }
+ }
+ else {
+ iRet = fp9928_vcom_get(O_piVCOM_get_mV);
+ }
+ }
+ else
+ if(gptHWCFG&&(6==gptHWCFG->m_val.bDisplayCtrl||7==gptHWCFG->m_val.bDisplayCtrl)) {
+ if(vcom_nominal) {
+ if(O_piVCOM_get_mV) {
+ *O_piVCOM_get_mV = vcom_nominal/1000;
+ }
+ }
+ else {
+ iRet = tps65185_vcom_get(O_piVCOM_get_mV);
+ }
+ }
+ else {
+ }
+
+ return iRet;
+}
+
+static void k_create_sys_attr(void)
+{
+ int err;
+ DBG_MSG("%s()\n",__FUNCTION__);
+ ASSERT(g_fb_data);
+ ASSERT(g_fb_data->dev);
+ //ASSERT(g_fb_data->dev->kobj);
+ err = sysfs_create_files(&g_fb_data->info.dev->kobj, sysfs_epdc_attrs);
+ if (err) {
+ pr_err("Can't create epdc attr sysfs !\n");
+ }
+}
+
+static int k_fake_s1d13522_init(unsigned char *pbInitDCbuf)
+{
+
+ int iChk;
+ int vcom_mV;
+
+
+ //printk("\n%s() DisplayCtrl=%d\n\n",__FUNCTION__,(int)gptHWCFG->m_val.bDisplayCtrl);
+
+ gptDC = fake_s1d13522_initEx3(default_bpp,g_fb_data->info.screen_base,\
+ g_fb_data->info.var.xres,g_fb_data->info.var.yres, \
+ ALIGN(g_fb_data->info.var.xres,32),ALIGN(g_fb_data->info.var.yres,128));
+
+ if(gptDC) {
+ gptDC->pfnGetWaveformBpp = k_get_wfbpp;
+ gptDC->pfnVcomEnable = k_vcom_enable;
+ gptDC->pfnSetPartialUpdate = k_set_partial;
+ //gptDC->pfnGetRealFrameBuf = k_get_realfb;
+ gptDC->pfnGetRealFrameBufEx = k_get_realfbEx;
+ gptDC->pfnDispStart = k_display_start;
+ gptDC->pfnGetWaveformMode = k_get_wfmode;
+ gptDC->pfnSetWaveformMode = k_set_wfmode;
+ gptDC->pfnIsUpdating = k_is_updating;
+ gptDC->pfnWaitUpdateComplete = k_wait_update_complete;
+ gptDC->pfnSetUpdateRect = k_set_update_rect;
+ gptDC->pfnSetVCOM = k_set_vcom;
+ gptDC->pfnGetVCOM = k_get_vcom;
+ gptDC->pfnSetVCOMToFlash = k_set_vcom_to_flash;
+
+ //gptDC->dwFlags |= EPDFB_DC_FLAG_OFB_RGB565;
+ gptDC->dwFlags |= EPDFB_DC_FLAG_FLASHDIRTY;
+
+ //
+ g_mxc_upd_data.update_region.top = 0;
+ g_mxc_upd_data.update_region.left = 0;
+ g_mxc_upd_data.update_region.height = g_fb_data->info.var.yres;
+ g_mxc_upd_data.update_region.width = g_fb_data->info.var.xres;
+
+ //g_mxc_upd_data.waveform_mode = g_fb_data->wv_modes.mode_gc16;
+ g_mxc_upd_data.waveform_mode = WF_GC16;
+
+ g_mxc_upd_data.update_mode = UPDATE_MODE_FULL;
+ g_mxc_upd_data.update_marker = 0;
+ g_mxc_upd_data.temp = TEMP_USE_AMBIENT;
+ g_mxc_upd_data.flags = 0;
+ //g_mxc_upd_data.alt_buffer_data = ;
+ //mxc_epdc_fb_set_upd_scheme(UPDATE_SCHEME_SNAPSHOT,&g_fb_data->info);
+
+ // printk("%s(%d):%s,Display=%s\n",__FILE__,__LINE__,__FUNCTION__,
+ // NtxHwCfg_GetCfgFldStrVal(gptHWCFG,HWCFG_FLDIDX_DisplayCtrl));
+#ifdef LM75_ENABLED//[
+ if(gptHWCFG&&\
+ (6==gptHWCFG->m_val.bDisplayCtrl||\
+ 7==gptHWCFG->m_val.bDisplayCtrl||\
+ 8==gptHWCFG->m_val.bDisplayCtrl))
+ {
+#endif //]LM75_ENABLED
+ int iPortA[2]={-1,-1} ;
+ int i;
+ // imx508 + tps16585 .
+
+#ifdef FAKE_S1D13522_PLATFORM_MX6 //[
+
+#else//][!FAKE_S1D13522_PLATFORM_MX6
+
+ if(28==gptHWCFG->m_val.bPCB) {
+ // E606C2B3 PMIC in Channel 3. before E606C2B3 in Channel 2.
+
+ #if 0 //[
+ // <E606C2B3
+ iPortA[0] = 2;
+ iPortA[1] = -1;
+ #else //][
+ // >=E606C2B3 .
+ iPortA[0] = 1;
+ iPortA[1] = 2;
+ #endif//]
+
+ }
+ else
+#endif //]FAKE_S1D13522_PLATFORM_MX6
+ {
+ iPortA[0] = 2;
+ }
+
+ for(i=0;i<2;i++) {
+ if(iPortA[i]>0) {
+ //printk("%s(),init TPS65185 @ i2c%d\n",__FUNCTION__,iPortA[i]);
+ if(8==gptHWCFG->m_val.bDisplayCtrl) {
+ iChk = fp9928_init(iPortA[i]);
+ if(iChk>=0){
+ if(0!=glVCOM_uV) {
+ if(fp9928_vcom_set((int)(glVCOM_uV/1000),0)<0) {
+ WARNING_MSG("%s(),FP9928 vcom set fail !\n",__FUNCTION__);
+ }
+ }
+ fp9928_vcom_get(&vcom_mV);
+ }
+ }
+ else {
+ iChk = tps65185_init(iPortA[i],EPDTIMING_V110);
+ if(iChk>=0) {
+ if(0!=glVCOM_uV) {
+ if(tps65185_vcom_set((int)(glVCOM_uV/1000),0)<0) {
+ WARNING_MSG("%s(),tps65185 vcom set fail !\n",__FUNCTION__);
+ }
+ }
+ tps65185_vcom_get(&vcom_mV);
+ }
+ }
+ if(iChk>=0) {
+ break;
+ }
+ else {
+ WARNING_MSG("%s(),init @ i2c%d fail !\n",__FUNCTION__,iPortA[i]);
+ }
+ }
+ }
+#ifdef LM75_ENABLED//[
+ }
+ else {
+ if ((4 == check_hardware_name()) || (3 == check_hardware_name())) {
+ lm75_init (3);
+ }
+ else {
+ lm75_init (2);
+ }
+ }
+#endif //]LM75_ENABLED//
+
+ vcom_nominal=vcom_mV*1000;/* save the vcom_nominal value in uV */
+
+ DBG_MSG("%s():PMIC VCOM=%d mV,vcom_nominal=%d\n",
+ __FUNCTION__,vcom_mV,vcom_nominal);
+
+ //if(!fake_s1d13522_is_logo_bypss())
+ {
+ g_fb_data->wfm = 0; /* initial waveform mode should be INIT */
+ //printk("draw mode0\n");
+ epdc_powerup(g_fb_data);
+ draw_mode0(g_fb_data);
+ //printk("draw mode0 end\n");
+
+ g_fb_data->powering_down = true;
+ schedule_delayed_work(&g_fb_data->epdc_done_work,
+ msecs_to_jiffies(g_fb_data->pwrdown_delay));
+ }
+ //epdc_powerdown(g_fb_data);
+
+ giIsInited=1;
+
+ //while(k_is_updating()) {
+ //DBG0_MSG("%s(%d):wait for update done .\n");
+ //schedule();
+ //}
+ if(fake_s1d13522_is_logo_bypss()) {
+ printk("%s() bypass logo\n",__FUNCTION__);
+ }
+ else {
+ if(pbInitDCbuf) {
+ int ilogo_width ,ilogo_height;
+
+ #if 1
+ if(k_get_wfbpp() == 4)
+ {
+ k_set_wfmode(WF_GC16); // fill LUT with default waveform, for 4bit, use mode 2
+ }
+ else {
+ k_set_wfmode(WF_GC4); // fill LUT with default waveform, for 3bit, use mode 3 (GC)
+ }
+ #endif
+
+ if(gptHWCFG) {
+ if(1==gptHWCFG->m_val.bDisplayResolution) {
+ ilogo_width = 1024 ;
+ ilogo_height = 758 ;
+ }
+ else if(2==gptHWCFG->m_val.bDisplayResolution) {
+ ilogo_width = 1024 ;
+ ilogo_height = 768 ;
+ }
+ else if(3==gptHWCFG->m_val.bDisplayResolution) {
+ ilogo_width = 1440 ;
+ ilogo_height = 1080 ;
+ }
+ else if(5==gptHWCFG->m_val.bDisplayResolution) {
+ ilogo_width = 1448 ;
+ ilogo_height = 1072 ;
+ }
+ else if(6==gptHWCFG->m_val.bDisplayResolution) {
+ ilogo_width = 1600 ;
+ ilogo_height = 1200 ;
+ }
+ else {
+ ilogo_width = 800 ;
+ ilogo_height = 600 ;
+ }
+ }
+ else {
+ ilogo_width = 800 ;
+ ilogo_height = 600 ;
+ }
+
+ if( gdwLOGO_size>=((ilogo_width*ilogo_height)>>1) ) {
+ u32 old_upd_scheme=g_fb_data->upd_scheme;
+ u32 old_auto_mode=g_fb_data->auto_mode;
+ u32 old_rotate=g_fb_data->info.var.rotate;
+
+
+ DBG0_MSG("drawing logo begin ...\n");
+ g_fb_data->epdc_fb_var.rotate = FB_ROTATE_UR;
+ mxc_epdc_fb_check_var(&g_fb_data->info.var,&g_fb_data->info);
+
+ mxc_epdc_fb_set_auto_update(AUTO_UPDATE_MODE_REGION_MODE,&g_fb_data->info);
+ mxc_epdc_fb_set_upd_scheme(UPDATE_SCHEME_SNAPSHOT,&g_fb_data->info);
+
+ fake_s1d13522_display_img(0,0,ilogo_width,ilogo_height,pbInitDCbuf,gptDC,4,0);
+ k_wait_update_complete();
+
+ mxc_epdc_fb_set_upd_scheme(old_upd_scheme,&g_fb_data->info);
+ mxc_epdc_fb_set_auto_update(old_auto_mode,&g_fb_data->info);
+
+ g_fb_data->info.var.rotate = old_rotate;
+ mxc_epdc_fb_check_var(&g_fb_data->info.var,&g_fb_data->info);
+
+ DBG_MSG("drawing logo end ...\n");
+ }
+ else {
+ printk("logo skip : logosize %u < %u !! \n ",(unsigned int)gdwLOGO_size,
+ ((ilogo_width*ilogo_height)>>1));
+ }
+ }
+ }
+ fake_s1d13522_progress_start(gptDC);
+ giIsInited=2;
+ k_create_sys_attr();
+ complete_all(&mxc_epdc_fake13522_inited);
+
+ return 0;
+ }
+ else {
+ printk("%s(%d): init fail !!\n",__FUNCTION__,__LINE__);
+ return -1;
+ }
+}
+
diff --git a/drivers/video/mxc/mxc_epdc_fb.c b/drivers/video/mxc/mxc_epdc_fb.c
new file mode 100644
index 00000000..39de51db
--- /dev/null
+++ b/drivers/video/mxc/mxc_epdc_fb.c
@@ -0,0 +1,8045 @@
+/*
+ * Copyright (C) 2010-2012 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+/*
+ * Based on STMP378X LCDIF
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/uaccess.h>
+#include <linux/cpufreq.h>
+#include <linux/firmware.h>
+#include <linux/kthread.h>
+#include <linux/dmaengine.h>
+#include <linux/pxp_dma.h>
+#include <linux/mxcfb.h>
+#include <linux/mxcfb_epdc_kernel.h>
+#include <linux/gpio.h>
+#include <linux/regulator/driver.h>
+#include <linux/fsl_devices.h>
+#include <linux/bitops.h>
+#include <mach/epdc.h>
+#include <mach/dma.h>
+#include <asm/cacheflush.h>
+
+
+#include "epdc_regs.h"
+
+#include "lk_tps65185.h"
+#include "lk_fp9928.h"
+
+//#define USE_BSP_PMIC 1
+
+#define GDEBUG 0
+#define giDbgLvl 1000
+#include <linux/gallen_dbg.h>
+
+#define FIX_WRONG_REGION_TEMP 1
+#define AVOID_REAGL_DIRTY_TEMP 1
+//#define DO_NOT_POWEROFF 1
+#define NTX_AUTOMODE_PATCH 1
+#define NTX_WFM_MODE_OPTIMIZED 1
+//#define NTX_WFM_MODE_OPTIMIZED_REAGL 1
+#ifdef CONFIG_FB_MXC_EINK_REGAL//[
+ #define NO_AUTO_REAGL_MODE 1
+ #define NO_CUS_REAGL_MODE 1
+#elif defined(CONFIG_ANDROID)//[
+ //#define NO_AUTO_REAGL_MODE 1
+ //#define NO_CUS_REAGL_MODE 1
+#else //][!CONFIG_ANDROID
+ #define NO_AUTO_REAGL_MODE 1
+ //#define NO_CUS_REAGL_MODE 1
+#endif//] CONFIG_ANDROID
+#include "eink_processing2.h"
+
+/*******************************************
+ * AA/AA-D testing
+ *******************************************/
+
+static uint32_t get_uSecs(void);
+static uint32_t getTimeDiff(uint32_t, uint32_t);
+
+static uint32_t get_uSecs(void) {
+ struct timeval tv;
+ do_gettimeofday(&tv);
+ return (uint32_t) (tv.tv_usec); // mircoseconds
+};
+
+static uint32_t getTimeDiff(uint32_t time1, uint32_t time2)
+{
+ return (time2 >= time1) ? (time2 - time1) : (1000000 + time2 -time1);
+}
+
+uint32_t aa_time_stamp[2];
+
+
+
+/*******************************************
+ * End of AA/AAD testing header part
+ *******************************************/
+
+/*
+ * define the default parameters for the histogram registers
+ */
+#define EPDC_HIST1_P4N_PARAM (0x00000000)
+#define EPDC_HIST2_P4N_PARAM (0x00000f00)
+#define EPDC_HIST4_P4N_PARAM (0x0f0a0500)
+#define EPDC_HIST8_P4N_PARAM0 (0x06040200)
+#define EPDC_HIST8_P4N_PARAM1 (0x0f0d0b09)
+#define EPDC_HIST16_P4N_PARAM0 (0x03020100)
+#define EPDC_HIST16_P4N_PARAM1 (0x07060504)
+#define EPDC_HIST16_P4N_PARAM2 (0x0b0a0908)
+#define EPDC_HIST16_P4N_PARAM3 (0x0f0e0d0c)
+
+#define EPDC_HIST1_P5N_PARAM (EPDC_HIST1_P4N_PARAM << 1)
+#define EPDC_HIST2_P5N_PARAM (EPDC_HIST2_P4N_PARAM << 1)
+#define EPDC_HIST4_P5N_PARAM (EPDC_HIST4_P4N_PARAM << 1)
+#define EPDC_HIST8_P5N_PARAM0 (EPDC_HIST8_P4N_PARAM0 << 1)
+#define EPDC_HIST8_P5N_PARAM1 (EPDC_HIST8_P4N_PARAM1 << 1)
+#define EPDC_HIST16_P5N_PARAM0 (EPDC_HIST16_P4N_PARAM0 << 1)
+#define EPDC_HIST16_P5N_PARAM1 (EPDC_HIST16_P4N_PARAM1 << 1)
+#define EPDC_HIST16_P5N_PARAM2 (EPDC_HIST16_P4N_PARAM2 << 1)
+#define EPDC_HIST16_P5N_PARAM3 (EPDC_HIST16_P4N_PARAM3 << 1)
+
+/*
+ * Enable this define to have a default panel
+ * loaded during driver initialization
+ */
+//#ifdef CONFIG_ANDROID//[
+ #define DEFAULT_PANEL_HW_INIT
+//#endif//]CONFIG_ANDROID
+
+#define NUM_SCREENS_MIN 2
+
+#define EPDC_V1_NUM_LUTS 16
+#define EPDC_V1_MAX_NUM_UPDATES 20
+#define EPDC_V2_NUM_LUTS 64
+#define EPDC_V2_MAX_NUM_UPDATES 64
+#define EPDC_MAX_NUM_BUFFERS 2
+#define INVALID_LUT (-1)
+#define DRY_RUN_NO_LUT 100
+
+/* Maximum update buffer image width due to v2.0 and v2.1 errata ERR005313. */
+#define EPDC_V2_MAX_UPDATE_WIDTH 2047
+#define EPDC_V2_ROTATION_ALIGNMENT 8
+
+#define DEFAULT_TEMP_INDEX 0
+#define DEFAULT_TEMP 20 /* room temp in deg Celsius */
+
+#define INIT_UPDATE_MARKER 0x12345678
+#define PAN_UPDATE_MARKER 0x12345679
+
+#define POWER_STATE_OFF 0
+#define POWER_STATE_ON 1
+
+#define MERGE_OK 0
+#define MERGE_FAIL 1
+#define MERGE_BLOCK 2
+
+
+#define NTX_WFM_MODE_INIT 0
+#define NTX_WFM_MODE_DU 1
+#define NTX_WFM_MODE_GC16 2
+#define NTX_WFM_MODE_GC4 3
+#define NTX_WFM_MODE_A2 4
+#define NTX_WFM_MODE_GL16 5
+#define NTX_WFM_MODE_GLR16 6
+#define NTX_WFM_MODE_GLD16 7
+#define NTX_WFM_MODE_TOTAL 8
+static int giNTX_waveform_modeA[NTX_WFM_MODE_TOTAL];
+
+
+unsigned char gbModeVersion=0 ;
+unsigned char gbWFM_REV ;
+unsigned char gbFPL_Platform ;
+
+static int vcom_nominal;
+
+
+static unsigned long default_bpp = 16;
+
+struct update_marker_data {
+ struct list_head full_list;
+ struct list_head upd_list;
+ u32 update_marker;
+ struct completion update_completion;
+ int lut_num;
+ bool collision_test;
+ bool waiting;
+};
+
+struct update_desc_list {
+ struct list_head list;
+ struct mxcfb_update_data upd_data;/* Update parameters */
+ u32 epdc_offs; /* Added to buffer ptr to resolve alignment */
+ u32 epdc_stride; /* Depends on rotation & whether we skip PxP */
+ struct list_head upd_marker_list; /* List of markers for this update */
+ u32 update_order; /* Numeric ordering value for update */
+ bool is_aa; /* Does the update require advance algorithms? */
+ bool wb_pause; /* Boolean to identify whether the EPDC update
+ requires pause for AA processing */
+ int iDither_state; /* Dither state */
+};
+
+/* This structure represents a list node containing both
+ * a memory region allocated as an output buffer for the PxP
+ * update processing task, and the update description (mode, region, etc.) */
+struct update_data_list {
+ struct list_head list;
+ dma_addr_t phys_addr; /* Pointer to phys address of processed Y buf */
+ void *virt_addr;
+ struct update_desc_list *update_desc;
+ int lut_num; /* Assigned before update is processed into working buffer */
+ u64 collision_mask; /* Set when update creates collision */
+ /* Mask of the LUTs the update collides with */
+};
+
+struct mxc_epdc_fb_data {
+ struct fb_info info;
+ struct fb_var_screeninfo epdc_fb_var; /* Internal copy of screeninfo
+ so we can sync changes to it */
+ u32 pseudo_palette[16];
+ char fw_str[64];
+ struct list_head list;
+ struct imx_epdc_fb_mode *cur_mode;
+ struct imx_epdc_fb_platform_data *pdata;
+ int blank;
+ u32 max_pix_size;
+ ssize_t map_size;
+ dma_addr_t phys_start;
+ u32 fb_offset;
+ int default_bpp;
+ int native_width;
+ int native_height;
+ int num_screens;
+ int epdc_irq;
+ struct device *dev;
+ int power_state;
+ int wait_for_powerdown;
+ struct completion powerdown_compl;
+ struct clk *epdc_clk_axi;
+ struct clk *epdc_clk_pix;
+ struct regulator *display_regulator;
+ struct regulator *vcom_regulator;
+ struct regulator *v3p3_regulator;
+ struct regulator *vdd_1v8_regulator;
+ bool fw_default_load;
+ int rev;
+
+ /* FB elements related to EPDC updates */
+ int num_luts;
+ int max_num_updates;
+ bool in_init;
+ bool hw_ready;
+ bool hw_initializing;
+ bool waiting_for_idle;
+ u32 auto_mode;
+ u32 upd_scheme;
+ struct list_head upd_pending_list;
+ struct list_head upd_buf_queue;
+ struct list_head upd_buf_free_list;
+ struct list_head upd_buf_collision_list;
+ struct update_data_list *cur_update;
+ struct mutex queue_mutex;
+ int trt_entries;
+ int temp_index;
+ u8 *temp_range_bounds;
+ int buf_pix_fmt;
+ int wfm;
+ struct mxcfb_waveform_modes wv_modes;
+ bool wv_modes_update;
+ u32 *waveform_buffer_virt;
+ u32 waveform_buffer_phys;
+ u32 waveform_buffer_size;
+ u32 *working_buffer_A_virt;
+ u32 working_buffer_A_phys;
+ u32 *working_buffer_B_virt;
+ u32 working_buffer_B_phys;
+ u32 working_buffer_size;
+ dma_addr_t *phys_addr_updbuf;
+ void **virt_addr_updbuf;
+ u32 upd_buffer_num;
+ u32 max_num_buffers;
+ dma_addr_t phys_addr_copybuf; /* Phys address of copied update data */
+ void *virt_addr_copybuf; /* Used for PxP SW workaround */
+ u32 order_cnt;
+ struct list_head full_marker_list;
+ u32 *lut_update_order; /* Array size = number of luts */
+ u64 epdc_colliding_luts;
+ u64 luts_complete_wb;
+ struct completion updates_done;
+ struct delayed_work epdc_done_work;
+ struct workqueue_struct *epdc_submit_workqueue;
+ struct work_struct epdc_submit_work;
+ struct workqueue_struct *epdc_intr_workqueue;
+ struct work_struct epdc_intr_work;
+//#ifdef FW_IN_RAM//[
+ struct workqueue_struct *epdc_firmware_workqueue;
+ //struct work_struct epdc_firmware_work;
+ struct delayed_work epdc_firmware_work;
+ struct semaphore firmware_work_lock;
+//#endif //]FW_IN_RAM
+ struct workqueue_struct *epdc_aa_workqueue;
+ struct work_struct epdc_aa_work;
+ bool waiting_for_wb;
+ bool waiting_for_lut;
+ bool waiting_for_lut15;
+ struct completion update_res_free;
+ struct completion lut15_free;
+ struct completion eof_event;
+ int eof_sync_period;
+ struct mutex power_mutex;
+ bool powering_down;
+ bool updates_active;
+ int pwrdown_delay;
+ unsigned long tce_prevent;
+ bool restrict_width; /* work around rev >=2.0 width and
+ stride restriction */
+
+ /* FB elements related to gen2 waveform data */
+ u8 *waveform_vcd_buffer;
+ u8 *waveform_acd_buffer;
+ u32 waveform_magic_number;
+ u8 *waveform_xwi_buffer;
+ char *waveform_xwi_string;
+ unsigned waveform_xwi_string_length;
+ u32 waveform_mc;
+ u32 waveform_trc;
+
+ /* FB elements related to PxP DMA */
+ struct completion pxp_tx_cmpl;
+ struct pxp_channel *pxp_chan;
+ struct pxp_config_data pxp_conf;
+ struct dma_async_tx_descriptor *txd;
+ dma_cookie_t cookie;
+ struct scatterlist sg[2];
+ struct mutex pxp_mutex; /* protects access to PxP */
+};
+
+struct waveform_data_header {
+ unsigned int wi0;
+ unsigned int wi1;
+ unsigned int wi2;
+ unsigned int wi3;
+ unsigned int wi4;
+ unsigned int wi5;
+ unsigned int wi6;
+ unsigned int xwia:24;
+ unsigned int cs1:8;
+ unsigned int wmta:24;
+ unsigned int fvsn:8;
+ unsigned int luts:8;
+ unsigned int mc:8;
+ unsigned int trc:8;
+ unsigned int awv:8;
+ unsigned int eb:8;
+ unsigned int sb:8;
+ unsigned int reserved0_1:8;
+ unsigned int reserved0_2:8;
+ unsigned int reserved0_3:8;
+ unsigned int reserved0_4:8;
+ unsigned int reserved0_5:8;
+ unsigned int cs2:8;
+};
+
+#include "ntx_hwconfig.h"
+extern volatile NTX_HWCONFIG *gptHWCFG ;
+
+struct mxcfb_waveform_data_file {
+ struct waveform_data_header wdh;
+ u32 *data; /* Temperature Range Table + Waveform Data */
+};
+
+void __iomem *epdc_base;
+
+volatile struct mxc_epdc_fb_data *g_fb_data;
+
+/* forward declaration */
+static int mxc_epdc_fb_get_temp_index(struct mxc_epdc_fb_data *fb_data,
+ int temp);
+static void mxc_epdc_fb_flush_updates(struct mxc_epdc_fb_data *fb_data);
+static int mxc_epdc_fb_blank(int blank, struct fb_info *info);
+static int mxc_epdc_fb_init_hw(struct fb_info *info);
+static int pxp_process_update(struct mxc_epdc_fb_data *fb_data,
+ u32 src_width, u32 src_height,
+ struct mxcfb_rect *update_region);
+static int pxp_complete_update(struct mxc_epdc_fb_data *fb_data, u32 *hist_stat);
+
+static void draw_mode0(struct mxc_epdc_fb_data *fb_data);
+static bool is_free_list_full(struct mxc_epdc_fb_data *fb_data);
+static void epdc_submit_paused_update(void);
+
+static void do_dithering_processing_Y1_v1_0(
+ unsigned char *update_region_ptr,
+ struct mxcfb_rect *update_region,
+ unsigned long update_region_stride,
+ int *err_dist);
+static void do_dithering_processing_Y4_v1_0(
+ unsigned char *update_region_ptr,
+ struct mxcfb_rect *update_region,
+ unsigned long update_region_stride,
+ int *err_dist);
+
+// FW_IN_RAM //[
+static void mxc_epdc_fb_fw_handler(const struct firmware *fw,void *context);
+// FW_IN_RAM //]
+
+static int mxc_epdc_fb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info);
+#ifdef DEBUG
+static void dump_pxp_config(struct mxc_epdc_fb_data *fb_data,
+ struct pxp_config_data *pxp_conf)
+{
+ dev_info(fb_data->dev, "S0 fmt 0x%x",
+ pxp_conf->s0_param.pixel_fmt);
+ dev_info(fb_data->dev, "S0 width 0x%x",
+ pxp_conf->s0_param.width);
+ dev_info(fb_data->dev, "S0 height 0x%x",
+ pxp_conf->s0_param.height);
+ dev_info(fb_data->dev, "S0 ckey 0x%x",
+ pxp_conf->s0_param.color_key);
+ dev_info(fb_data->dev, "S0 ckey en 0x%x",
+ pxp_conf->s0_param.color_key_enable);
+
+ dev_info(fb_data->dev, "OL0 combine en 0x%x",
+ pxp_conf->ol_param[0].combine_enable);
+ dev_info(fb_data->dev, "OL0 fmt 0x%x",
+ pxp_conf->ol_param[0].pixel_fmt);
+ dev_info(fb_data->dev, "OL0 width 0x%x",
+ pxp_conf->ol_param[0].width);
+ dev_info(fb_data->dev, "OL0 height 0x%x",
+ pxp_conf->ol_param[0].height);
+ dev_info(fb_data->dev, "OL0 ckey 0x%x",
+ pxp_conf->ol_param[0].color_key);
+ dev_info(fb_data->dev, "OL0 ckey en 0x%x",
+ pxp_conf->ol_param[0].color_key_enable);
+ dev_info(fb_data->dev, "OL0 alpha 0x%x",
+ pxp_conf->ol_param[0].global_alpha);
+ dev_info(fb_data->dev, "OL0 alpha en 0x%x",
+ pxp_conf->ol_param[0].global_alpha_enable);
+ dev_info(fb_data->dev, "OL0 local alpha en 0x%x",
+ pxp_conf->ol_param[0].local_alpha_enable);
+
+ dev_info(fb_data->dev, "Out fmt 0x%x",
+ pxp_conf->out_param.pixel_fmt);
+ dev_info(fb_data->dev, "Out width 0x%x",
+ pxp_conf->out_param.width);
+ dev_info(fb_data->dev, "Out height 0x%x",
+ pxp_conf->out_param.height);
+
+ dev_info(fb_data->dev,
+ "drect left 0x%x right 0x%x width 0x%x height 0x%x",
+ pxp_conf->proc_data.drect.left, pxp_conf->proc_data.drect.top,
+ pxp_conf->proc_data.drect.width,
+ pxp_conf->proc_data.drect.height);
+ dev_info(fb_data->dev,
+ "srect left 0x%x right 0x%x width 0x%x height 0x%x",
+ pxp_conf->proc_data.srect.left, pxp_conf->proc_data.srect.top,
+ pxp_conf->proc_data.srect.width,
+ pxp_conf->proc_data.srect.height);
+ dev_info(fb_data->dev, "Scaling en 0x%x", pxp_conf->proc_data.scaling);
+ dev_info(fb_data->dev, "HFlip en 0x%x", pxp_conf->proc_data.hflip);
+ dev_info(fb_data->dev, "VFlip en 0x%x", pxp_conf->proc_data.vflip);
+ dev_info(fb_data->dev, "Rotation 0x%x", pxp_conf->proc_data.rotate);
+ dev_info(fb_data->dev, "BG Color 0x%x", pxp_conf->proc_data.bgcolor);
+}
+
+static void dump_epdc_reg(void)
+{
+ printk(KERN_DEBUG "\n\n");
+ printk(KERN_DEBUG "EPDC_CTRL 0x%x\n", __raw_readl(EPDC_CTRL));
+ printk(KERN_DEBUG "EPDC_WVADDR 0x%x\n", __raw_readl(EPDC_WVADDR));
+ printk(KERN_DEBUG "EPDC_WB_ADDR 0x%x\n", __raw_readl(EPDC_WB_ADDR));
+ printk(KERN_DEBUG "EPDC_RES 0x%x\n", __raw_readl(EPDC_RES));
+ printk(KERN_DEBUG "EPDC_FORMAT 0x%x\n", __raw_readl(EPDC_FORMAT));
+ printk(KERN_DEBUG "EPDC_FIFOCTRL 0x%x\n", __raw_readl(EPDC_FIFOCTRL));
+ printk(KERN_DEBUG "EPDC_UPD_ADDR 0x%x\n", __raw_readl(EPDC_UPD_ADDR));
+ printk(KERN_DEBUG "EPDC_UPD_STRIDE 0x%x\n", __raw_readl(EPDC_UPD_STRIDE));
+ printk(KERN_DEBUG "EPDC_UPD_FIXED 0x%x\n", __raw_readl(EPDC_UPD_FIXED));
+ printk(KERN_DEBUG "EPDC_UPD_CORD 0x%x\n", __raw_readl(EPDC_UPD_CORD));
+ printk(KERN_DEBUG "EPDC_UPD_SIZE 0x%x\n", __raw_readl(EPDC_UPD_SIZE));
+ printk(KERN_DEBUG "EPDC_UPD_CTRL 0x%x\n", __raw_readl(EPDC_UPD_CTRL));
+ printk(KERN_DEBUG "EPDC_TEMP 0x%x\n", __raw_readl(EPDC_TEMP));
+ printk(KERN_DEBUG "EPDC_AUTOWV_LUT 0x%x\n", __raw_readl(EPDC_AUTOWV_LUT));
+ printk(KERN_DEBUG "EPDC_TCE_CTRL 0x%x\n", __raw_readl(EPDC_TCE_CTRL));
+ printk(KERN_DEBUG "EPDC_TCE_SDCFG 0x%x\n", __raw_readl(EPDC_TCE_SDCFG));
+ printk(KERN_DEBUG "EPDC_TCE_GDCFG 0x%x\n", __raw_readl(EPDC_TCE_GDCFG));
+ printk(KERN_DEBUG "EPDC_TCE_HSCAN1 0x%x\n", __raw_readl(EPDC_TCE_HSCAN1));
+ printk(KERN_DEBUG "EPDC_TCE_HSCAN2 0x%x\n", __raw_readl(EPDC_TCE_HSCAN2));
+ printk(KERN_DEBUG "EPDC_TCE_VSCAN 0x%x\n", __raw_readl(EPDC_TCE_VSCAN));
+ printk(KERN_DEBUG "EPDC_TCE_OE 0x%x\n", __raw_readl(EPDC_TCE_OE));
+ printk(KERN_DEBUG "EPDC_TCE_POLARITY 0x%x\n", __raw_readl(EPDC_TCE_POLARITY));
+ printk(KERN_DEBUG "EPDC_TCE_TIMING1 0x%x\n", __raw_readl(EPDC_TCE_TIMING1));
+ printk(KERN_DEBUG "EPDC_TCE_TIMING2 0x%x\n", __raw_readl(EPDC_TCE_TIMING2));
+ printk(KERN_DEBUG "EPDC_TCE_TIMING3 0x%x\n", __raw_readl(EPDC_TCE_TIMING3));
+ printk(KERN_DEBUG "EPDC_PIGEON_CTRL0 0x%x\n", __raw_readl(EPDC_PIGEON_CTRL0));
+ printk(KERN_DEBUG "EPDC_PIGEON_CTRL1 0x%x\n", __raw_readl(EPDC_PIGEON_CTRL1));
+ printk(KERN_DEBUG "EPDC_IRQ_MASK1 0x%x\n", __raw_readl(EPDC_IRQ_MASK1));
+ printk(KERN_DEBUG "EPDC_IRQ_MASK2 0x%x\n", __raw_readl(EPDC_IRQ_MASK2));
+ printk(KERN_DEBUG "EPDC_IRQ1 0x%x\n", __raw_readl(EPDC_IRQ1));
+ printk(KERN_DEBUG "EPDC_IRQ2 0x%x\n", __raw_readl(EPDC_IRQ2));
+ printk(KERN_DEBUG "EPDC_IRQ_MASK 0x%x\n", __raw_readl(EPDC_IRQ_MASK));
+ printk(KERN_DEBUG "EPDC_IRQ 0x%x\n", __raw_readl(EPDC_IRQ));
+ printk(KERN_DEBUG "EPDC_STATUS_LUTS 0x%x\n", __raw_readl(EPDC_STATUS_LUTS));
+ printk(KERN_DEBUG "EPDC_STATUS_LUTS2 0x%x\n", __raw_readl(EPDC_STATUS_LUTS2));
+ printk(KERN_DEBUG "EPDC_STATUS_NEXTLUT 0x%x\n", __raw_readl(EPDC_STATUS_NEXTLUT));
+ printk(KERN_DEBUG "EPDC_STATUS_COL1 0x%x\n", __raw_readl(EPDC_STATUS_COL));
+ printk(KERN_DEBUG "EPDC_STATUS_COL2 0x%x\n", __raw_readl(EPDC_STATUS_COL2));
+ printk(KERN_DEBUG "EPDC_STATUS 0x%x\n", __raw_readl(EPDC_STATUS));
+ printk(KERN_DEBUG "EPDC_UPD_COL_CORD 0x%x\n", __raw_readl(EPDC_UPD_COL_CORD));
+ printk(KERN_DEBUG "EPDC_UPD_COL_SIZE 0x%x\n", __raw_readl(EPDC_UPD_COL_SIZE));
+ printk(KERN_DEBUG "EPDC_DEBUG 0x%x\n", __raw_readl(EPDC_DEBUG));
+ printk(KERN_DEBUG "EPDC_DEBUG_LUT 0x%x\n", __raw_readl(EPDC_DEBUG_LUT));
+ printk(KERN_DEBUG "EPDC_HIST1_PARAM 0x%x\n", __raw_readl(EPDC_HIST1_PARAM));
+ printk(KERN_DEBUG "EPDC_HIST2_PARAM 0x%x\n", __raw_readl(EPDC_HIST2_PARAM));
+ printk(KERN_DEBUG "EPDC_HIST4_PARAM 0x%x\n", __raw_readl(EPDC_HIST4_PARAM));
+ printk(KERN_DEBUG "EPDC_HIST8_PARAM0 0x%x\n", __raw_readl(EPDC_HIST8_PARAM0));
+ printk(KERN_DEBUG "EPDC_HIST8_PARAM1 0x%x\n", __raw_readl(EPDC_HIST8_PARAM1));
+ printk(KERN_DEBUG "EPDC_HIST16_PARAM0 0x%x\n", __raw_readl(EPDC_HIST16_PARAM0));
+ printk(KERN_DEBUG "EPDC_HIST16_PARAM1 0x%x\n", __raw_readl(EPDC_HIST16_PARAM1));
+ printk(KERN_DEBUG "EPDC_HIST16_PARAM2 0x%x\n", __raw_readl(EPDC_HIST16_PARAM2));
+ printk(KERN_DEBUG "EPDC_HIST16_PARAM3 0x%x\n", __raw_readl(EPDC_HIST16_PARAM3));
+ printk(KERN_DEBUG "EPDC_GPIO 0x%x\n", __raw_readl(EPDC_GPIO));
+ printk(KERN_DEBUG "EPDC_VERSION 0x%x\n", __raw_readl(EPDC_VERSION));
+ printk(KERN_DEBUG "\n\n");
+}
+
+static void dump_update_data(struct device *dev,
+ struct update_data_list *upd_data_list)
+{
+ dev_info(dev,
+ "X = %d, Y = %d, Width = %d, Height = %d, WaveMode = %d, "
+ "LUT = %d, Coll Mask = 0x%llx, order = %d\n",
+ upd_data_list->update_desc->upd_data.update_region.left,
+ upd_data_list->update_desc->upd_data.update_region.top,
+ upd_data_list->update_desc->upd_data.update_region.width,
+ upd_data_list->update_desc->upd_data.update_region.height,
+ upd_data_list->update_desc->upd_data.waveform_mode,
+ upd_data_list->lut_num,
+ upd_data_list->collision_mask,
+ upd_data_list->update_desc->update_order);
+}
+
+static void dump_collision_list(struct mxc_epdc_fb_data *fb_data)
+{
+ struct update_data_list *plist;
+
+ dev_info(fb_data->dev, "Collision List:\n");
+ if (list_empty(&fb_data->upd_buf_collision_list))
+ dev_info(fb_data->dev, "Empty");
+ list_for_each_entry(plist, &fb_data->upd_buf_collision_list, list) {
+ dev_info(fb_data->dev, "Virt Addr = 0x%x, Phys Addr = 0x%x ",
+ (u32)plist->virt_addr, plist->phys_addr);
+ dump_update_data(fb_data->dev, plist);
+ }
+}
+
+static void dump_free_list(struct mxc_epdc_fb_data *fb_data)
+{
+ struct update_data_list *plist;
+
+ dev_info(fb_data->dev, "Free List:\n");
+ if (list_empty(&fb_data->upd_buf_free_list))
+ dev_info(fb_data->dev, "Empty");
+ list_for_each_entry(plist, &fb_data->upd_buf_free_list, list)
+ dev_info(fb_data->dev, "Virt Addr = 0x%x, Phys Addr = 0x%x ",
+ (u32)plist->virt_addr, plist->phys_addr);
+}
+
+static void dump_queue(struct mxc_epdc_fb_data *fb_data)
+{
+ struct update_data_list *plist;
+
+ dev_info(fb_data->dev, "Queue:\n");
+ if (list_empty(&fb_data->upd_buf_queue))
+ dev_info(fb_data->dev, "Empty");
+ list_for_each_entry(plist, &fb_data->upd_buf_queue, list) {
+ dev_info(fb_data->dev, "Virt Addr = 0x%x, Phys Addr = 0x%x ",
+ (u32)plist->virt_addr, plist->phys_addr);
+ dump_update_data(fb_data->dev, plist);
+ }
+}
+
+static void dump_desc_data(struct device *dev,
+ struct update_desc_list *upd_desc_list)
+{
+ dev_info(dev,
+ "X = %d, Y = %d, Width = %d, Height = %d, WaveMode = %d, "
+ "order = %d\n",
+ upd_desc_list->upd_data.update_region.left,
+ upd_desc_list->upd_data.update_region.top,
+ upd_desc_list->upd_data.update_region.width,
+ upd_desc_list->upd_data.update_region.height,
+ upd_desc_list->upd_data.waveform_mode,
+ upd_desc_list->update_order);
+}
+
+static void dump_pending_list(struct mxc_epdc_fb_data *fb_data)
+{
+ struct update_desc_list *plist;
+
+ dev_info(fb_data->dev, "Queue:\n");
+ if (list_empty(&fb_data->upd_pending_list))
+ dev_info(fb_data->dev, "Empty");
+ list_for_each_entry(plist, &fb_data->upd_pending_list, list)
+ dump_desc_data(fb_data->dev, plist);
+}
+
+static void dump_all_updates(struct mxc_epdc_fb_data *fb_data)
+{
+ dump_free_list(fb_data);
+ dump_queue(fb_data);
+ dump_collision_list(fb_data);
+ dev_info(fb_data->dev, "Current update being processed:\n");
+ if (fb_data->cur_update == NULL)
+ dev_info(fb_data->dev, "No current update\n");
+ else
+ dump_update_data(fb_data->dev, fb_data->cur_update);
+}
+#else
+static inline void dump_pxp_config(struct mxc_epdc_fb_data *fb_data,
+ struct pxp_config_data *pxp_conf) {}
+static inline void dump_epdc_reg(void) {}
+static inline void dump_update_data(struct device *dev,
+ struct update_data_list *upd_data_list) {}
+static inline void dump_collision_list(struct mxc_epdc_fb_data *fb_data) {}
+static inline void dump_free_list(struct mxc_epdc_fb_data *fb_data) {}
+static inline void dump_queue(struct mxc_epdc_fb_data *fb_data) {}
+static inline void dump_all_updates(struct mxc_epdc_fb_data *fb_data) {}
+
+#endif
+
+/*
+ * EPDC Voltage Control data handler
+ */
+struct epd_vc_data {
+ unsigned version:16;
+ unsigned v1:16;
+ unsigned v2:16;
+ unsigned v3:16;
+ unsigned v4:16;
+ unsigned v5:16;
+ unsigned v6:16;
+ unsigned v7:8;
+ u8 cs:8;
+ };
+void fetch_Epdc_Pmic_Voltages( struct epd_vc_data *vcd, struct mxc_epdc_fb_data *fb_data,
+ u32 waveform_mode,
+ u32 waveform_tempRange)
+{
+ /* fetch and display the voltage control data */
+ if (fb_data->waveform_vcd_buffer) {
+
+ /* fetch the voltage control data */
+ if (mxc_epdc_fb_fetch_vc_data( fb_data->waveform_vcd_buffer, waveform_mode, waveform_tempRange, fb_data->waveform_mc, fb_data->waveform_trc, (unsigned char *) vcd) < 0)
+ dev_err(fb_data->dev, " *** Extra Waveform Data checksum error ***\n");
+ else {
+#if 0
+ dev_info(fb_data->dev, " -- VC Data version 0x%04x : v1 = 0x%04x, v2 = 0x%04x, v3 = 0x%04x, v4 = 0x%04x, v5 = 0x%04x, v6 = 0x%04x, v7 = 0x%02x --\n",
+ vcd->version, vcd->v1, vcd->v2, vcd->v3, vcd->v4, vcd->v5, vcd->v6, vcd->v7 );
+#endif
+ }
+
+ }
+}
+
+/********************************************************
+ * Start Low-Level EPDC Functions
+ ********************************************************/
+
+static inline void epdc_lut_complete_intr(int rev, u32 lut_num, bool enable)
+{
+ if (rev < 20) {
+ if (enable)
+ __raw_writel(1 << lut_num, EPDC_IRQ_MASK_SET);
+ else
+ __raw_writel(1 << lut_num, EPDC_IRQ_MASK_CLEAR);
+ } else {
+ if (enable) {
+ if (lut_num < 32)
+ __raw_writel(1 << lut_num, EPDC_IRQ_MASK1_SET);
+ else
+ __raw_writel(1 << (lut_num - 32),
+ EPDC_IRQ_MASK2_SET);
+ } else {
+ if (lut_num < 32)
+ __raw_writel(1 << lut_num,
+ EPDC_IRQ_MASK1_CLEAR);
+ else
+ __raw_writel(1 << (lut_num - 32),
+ EPDC_IRQ_MASK2_CLEAR);
+ }
+ }
+}
+
+static inline void epdc_working_buf_intr(bool enable)
+{
+ if (enable)
+ __raw_writel(EPDC_IRQ_WB_CMPLT_IRQ, EPDC_IRQ_MASK_SET);
+ else
+ __raw_writel(EPDC_IRQ_WB_CMPLT_IRQ, EPDC_IRQ_MASK_CLEAR);
+}
+
+static inline void epdc_clear_working_buf_irq(void)
+{
+ __raw_writel(EPDC_IRQ_WB_CMPLT_IRQ | EPDC_IRQ_LUT_COL_IRQ,
+ EPDC_IRQ_CLEAR);
+}
+
+static inline void epdc_upd_done_intr(bool enable)
+{
+ if (enable)
+ __raw_writel(EPDC_IRQ_UPD_DONE_IRQ, EPDC_IRQ_MASK_SET);
+ else
+ __raw_writel(EPDC_IRQ_UPD_DONE_IRQ, EPDC_IRQ_MASK_CLEAR);
+}
+
+static inline void epdc_eof_intr(bool enable)
+{
+ if (enable)
+ __raw_writel(EPDC_IRQ_FRAME_END_IRQ, EPDC_IRQ_MASK_SET);
+ else
+ __raw_writel(EPDC_IRQ_FRAME_END_IRQ, EPDC_IRQ_MASK_CLEAR);
+}
+
+static inline void epdc_clear_eof_irq(void)
+{
+ __raw_writel(EPDC_IRQ_FRAME_END_IRQ, EPDC_IRQ_CLEAR);
+}
+
+static inline bool epdc_signal_eof(void)
+{
+ return (__raw_readl(EPDC_IRQ_MASK) & __raw_readl(EPDC_IRQ)
+ & EPDC_IRQ_FRAME_END_IRQ) ? true : false;
+}
+
+static inline void epdc_set_temp(u32 temp)
+{
+ __raw_writel(temp, EPDC_TEMP);
+}
+
+static inline void epdc_set_screen_res(u32 width, u32 height)
+{
+ u32 val = (height << EPDC_RES_VERTICAL_OFFSET) | width;
+ __raw_writel(val, EPDC_RES);
+}
+
+static inline void epdc_set_update_addr(u32 addr)
+{
+ __raw_writel(addr, EPDC_UPD_ADDR);
+}
+
+static inline void epdc_set_update_coord(u32 x, u32 y)
+{
+ DBG_MSG("%s(),x=%hd,y=%hd\n",__FUNCTION__,x,y);
+ u32 val = (y << EPDC_UPD_CORD_YCORD_OFFSET) | x;
+ __raw_writel(val, EPDC_UPD_CORD);
+}
+
+static inline void epdc_set_update_dimensions(u32 width, u32 height)
+{
+ DBG_MSG("%s(),w=%hd,h=%hd\n",__FUNCTION__,width,height);
+ u32 val = (height << EPDC_UPD_SIZE_HEIGHT_OFFSET) | width;
+ __raw_writel(val, EPDC_UPD_SIZE);
+}
+
+static void epdc_set_update_waveform(struct mxcfb_waveform_modes *wv_modes)
+{
+ u32 val;
+
+ /* Configure the auto-waveform look-up table based on waveform modes */
+
+ /* Entry 1 = DU, 2 = GC4, 3 = GC8, etc. */
+ val = (wv_modes->mode_du << EPDC_AUTOWV_LUT_DATA_OFFSET) |
+ (0 << EPDC_AUTOWV_LUT_ADDR_OFFSET);
+ __raw_writel(val, EPDC_AUTOWV_LUT);
+ val = (wv_modes->mode_du << EPDC_AUTOWV_LUT_DATA_OFFSET) |
+ (1 << EPDC_AUTOWV_LUT_ADDR_OFFSET);
+ __raw_writel(val, EPDC_AUTOWV_LUT);
+ val = (wv_modes->mode_gc4 << EPDC_AUTOWV_LUT_DATA_OFFSET) |
+ (2 << EPDC_AUTOWV_LUT_ADDR_OFFSET);
+ __raw_writel(val, EPDC_AUTOWV_LUT);
+ val = (wv_modes->mode_gc8 << EPDC_AUTOWV_LUT_DATA_OFFSET) |
+ (3 << EPDC_AUTOWV_LUT_ADDR_OFFSET);
+ __raw_writel(val, EPDC_AUTOWV_LUT);
+ val = (wv_modes->mode_gc16 << EPDC_AUTOWV_LUT_DATA_OFFSET) |
+ (4 << EPDC_AUTOWV_LUT_ADDR_OFFSET);
+ __raw_writel(val, EPDC_AUTOWV_LUT);
+ val = (wv_modes->mode_gc32 << EPDC_AUTOWV_LUT_DATA_OFFSET) |
+ (5 << EPDC_AUTOWV_LUT_ADDR_OFFSET);
+ __raw_writel(val, EPDC_AUTOWV_LUT);
+}
+
+static void epdc_set_update_stride(u32 stride)
+{
+ __raw_writel(stride, EPDC_UPD_STRIDE);
+}
+
+static void epdc_submit_update(u32 lut_num, u32 waveform_mode, u32 update_mode,
+ bool use_dry_run, bool update_pause,
+ bool use_test_mode, u32 np_val)
+{
+ volatile static int giLast_waveform_mode=-1;
+ u32 reg_val = 0;
+
+
+ if(giLast_waveform_mode!=waveform_mode) {
+ if( g_fb_data->wv_modes.mode_a2==waveform_mode &&
+ g_fb_data->wv_modes.mode_du!=giLast_waveform_mode)
+ {
+ waveform_mode=g_fb_data->wv_modes.mode_du;
+ DBG_MSG("%s():waveform mode has been force chage to DU before A2\n",__FUNCTION__);
+ }
+
+ DBG_MSG("%s(%d):%s(),lut=%d,wf_mode=%d,last_wf_mode=%d,upd_mode=%d,test=%d,np_val=%d\n",
+ __FILE__,__LINE__,__FUNCTION__,lut_num,waveform_mode,giLast_waveform_mode,update_mode,use_test_mode,np_val);
+ }
+ else
+ DBG_MSG("%s(%d):%s(),lut=%d,wf_mode=%d,last_wf_mode=%d,upd_mode=%d,test=%d,np_val=%d\n",
+ __FILE__,__LINE__,__FUNCTION__,lut_num,waveform_mode,giLast_waveform_mode,update_mode,use_test_mode,np_val);
+
+ if (use_test_mode) {
+ reg_val |=
+ ((np_val << EPDC_UPD_FIXED_FIXNP_OFFSET) &
+ EPDC_UPD_FIXED_FIXNP_MASK) | EPDC_UPD_FIXED_FIXNP_EN;
+
+ __raw_writel(reg_val, EPDC_UPD_FIXED);
+
+ reg_val = EPDC_UPD_CTRL_USE_FIXED;
+ } else {
+ __raw_writel(reg_val, EPDC_UPD_FIXED);
+ }
+
+ if (waveform_mode == WAVEFORM_MODE_AUTO) {
+ // disable autowv mode
+ reg_val |= ((g_fb_data->wv_modes.mode_gc16 <<
+ EPDC_UPD_CTRL_WAVEFORM_MODE_OFFSET) &
+ EPDC_UPD_CTRL_WAVEFORM_MODE_MASK);
+ //reg_val |= EPDC_UPD_CTRL_AUTOWV;
+ }
+ else {
+ reg_val |= ((waveform_mode <<
+ EPDC_UPD_CTRL_WAVEFORM_MODE_OFFSET) &
+ EPDC_UPD_CTRL_WAVEFORM_MODE_MASK);
+ }
+
+ if (update_pause)
+ reg_val |= EPDC_UPD_CTRL_AUTOWV_PAUSE;
+
+ reg_val |= (use_dry_run ? EPDC_UPD_CTRL_DRY_RUN : 0) |
+ ((lut_num << EPDC_UPD_CTRL_LUT_SEL_OFFSET) &
+ EPDC_UPD_CTRL_LUT_SEL_MASK) |
+ update_mode;
+
+ __raw_writel(reg_val, EPDC_UPD_CTRL);
+
+ giLast_waveform_mode = waveform_mode;
+
+}
+
+static void epdc_submit_paused_update(void)
+{
+ u32 reg_val = __raw_readl(EPDC_UPD_CTRL);
+ reg_val &= ~EPDC_UPD_CTRL_AUTOWV_PAUSE;
+ __raw_writel(reg_val, EPDC_UPD_CTRL);
+}
+
+static inline bool epdc_is_lut_complete(int rev, u32 lut_num)
+{
+ u32 val;
+ bool is_compl;
+ if (rev < 20) {
+ val = __raw_readl(EPDC_IRQ);
+ is_compl = val & (1 << lut_num) ? true : false;
+ } else if (lut_num < 32) {
+ val = __raw_readl(EPDC_IRQ1);
+ is_compl = val & (1 << lut_num) ? true : false;
+ } else {
+ val = __raw_readl(EPDC_IRQ2);
+ is_compl = val & (1 << (lut_num - 32)) ? true : false;
+ }
+
+ return is_compl;
+}
+
+static inline void epdc_clear_lut_complete_irq(int rev, u32 lut_num)
+{
+ if (rev < 20)
+ __raw_writel(1 << lut_num, EPDC_IRQ_CLEAR);
+ else if (lut_num < 32)
+ __raw_writel(1 << lut_num, EPDC_IRQ1_CLEAR);
+ else
+ __raw_writel(1 << (lut_num - 32), EPDC_IRQ2_CLEAR);
+}
+
+static inline bool epdc_is_lut_active(u32 lut_num)
+{
+ u32 val;
+ bool is_active;
+
+ if (lut_num < 32) {
+ val = __raw_readl(EPDC_STATUS_LUTS);
+ is_active = val & (1 << lut_num) ? true : false;
+ } else {
+ val = __raw_readl(EPDC_STATUS_LUTS2);
+ is_active = val & (1 << (lut_num - 32)) ? true : false;
+ }
+
+ return is_active;
+}
+
+static inline bool epdc_any_luts_active(int rev)
+{
+ bool any_active;
+
+ if (rev < 20)
+ any_active = __raw_readl(EPDC_STATUS_LUTS) ? true : false;
+ else
+ any_active = (__raw_readl(EPDC_STATUS_LUTS) |
+ __raw_readl(EPDC_STATUS_LUTS2)) ? true : false;
+
+ return any_active;
+}
+
+static inline bool epdc_any_luts_available(void)
+{
+ bool luts_available =
+ (__raw_readl(EPDC_STATUS_NEXTLUT) &
+ EPDC_STATUS_NEXTLUT_NEXT_LUT_VALID) ? true : false;
+ return luts_available;
+}
+
+static inline int epdc_get_next_lut(void)
+{
+ u32 val =
+ __raw_readl(EPDC_STATUS_NEXTLUT) &
+ EPDC_STATUS_NEXTLUT_NEXT_LUT_MASK;
+ return val;
+}
+
+static int epdc_choose_next_lut(int rev, int *next_lut)
+{
+ u64 luts_status, unprocessed_luts;
+ bool next_lut_found = false;
+ u32 format_p5n = ((__raw_readl(EPDC_FORMAT) & EPDC_FORMAT_BUF_PIXEL_FORMAT_MASK) ==
+ EPDC_FORMAT_BUF_PIXEL_FORMAT_P5N);
+
+ luts_status = __raw_readl(EPDC_STATUS_LUTS);
+ if (rev < 20 || format_p5n)
+ luts_status &= 0xFFFF;
+ else
+ luts_status |= ((u64)__raw_readl(EPDC_STATUS_LUTS2) << 32);
+
+ if (rev < 20) {
+ unprocessed_luts = __raw_readl(EPDC_IRQ) & 0xFFFF;
+ } else {
+ unprocessed_luts = __raw_readl(EPDC_IRQ1) |
+ ((u64)__raw_readl(EPDC_IRQ2) << 32);
+ if (format_p5n)
+ unprocessed_luts &= 0xFFFF;
+ }
+
+ while (!next_lut_found) {
+ /*
+ * Selecting a LUT to minimize incidence of TCE Underrun Error
+ * --------------------------------------------------------
+ * We want to find the lowest order LUT that is of greater
+ * order than all other active LUTs. If highest order LUT
+ * is active, then we want to choose the lowest order
+ * available LUT.
+ *
+ * NOTE: For EPDC version 2.0 and later, TCE Underrun error
+ * bug is fixed, so it doesn't matter which LUT is used.
+ */
+ *next_lut = fls64(luts_status);
+
+ if (rev < 20 || format_p5n) {
+ if (*next_lut > 15)
+ *next_lut = ffz(luts_status);
+ } else {
+ if (*next_lut > 63) {
+ *next_lut = ffz((u32)luts_status);
+ if (*next_lut == -1)
+ *next_lut =
+ ffz((u32)(luts_status >> 32)) + 32;
+ }
+ }
+
+ /*
+ * Note on unprocessed_luts: There is a race condition
+ * where a LUT completes, but has not been processed by
+ * IRQ handler workqueue, and then a new update request
+ * attempts to use that LUT. We prevent that here by
+ * ensuring that the LUT we choose doesn't have its IRQ
+ * bit set (indicating it has completed but not yet been
+ * processed).
+ */
+ if ((1 << *next_lut) & unprocessed_luts)
+ luts_status |= (1 << *next_lut);
+ else
+ next_lut_found = true;
+ }
+
+ if (luts_status & 0x8000)
+ return 1;
+ else
+ return 0;
+}
+
+static inline bool epdc_is_working_buffer_busy(void)
+{
+ u32 val = __raw_readl(EPDC_STATUS);
+ bool is_busy = (val & EPDC_STATUS_WB_BUSY) ? true : false;
+
+ return is_busy;
+}
+
+static inline bool epdc_is_working_buffer_complete(void)
+{
+ u32 val = __raw_readl(EPDC_IRQ);
+ bool is_compl = (val & EPDC_IRQ_WB_CMPLT_IRQ) ? true : false;
+
+ return is_compl;
+}
+
+static inline bool epdc_is_lut_cancelled(void)
+{
+ u32 val = __raw_readl(EPDC_STATUS);
+ bool is_void = (val & EPDC_STATUS_UPD_VOID) ? true : false;
+
+ return is_void;
+}
+
+static inline bool epdc_is_collision(void)
+{
+ u32 val = __raw_readl(EPDC_IRQ);
+ return (val & EPDC_IRQ_LUT_COL_IRQ) ? true : false;
+}
+
+static inline u64 epdc_get_colliding_luts(int rev)
+{
+ u64 val = (u64)(__raw_readl(EPDC_STATUS_COL));
+ if (rev >= 20)
+ val |= (u64)__raw_readl(EPDC_STATUS_COL2) << 32;
+ return val;
+}
+
+static void epdc_set_horizontal_timing(u32 horiz_start, u32 horiz_end,
+ u32 hsync_width, u32 hsync_line_length)
+{
+ u32 reg_val =
+ ((hsync_width << EPDC_TCE_HSCAN1_LINE_SYNC_WIDTH_OFFSET) &
+ EPDC_TCE_HSCAN1_LINE_SYNC_WIDTH_MASK)
+ | ((hsync_line_length << EPDC_TCE_HSCAN1_LINE_SYNC_OFFSET) &
+ EPDC_TCE_HSCAN1_LINE_SYNC_MASK);
+ __raw_writel(reg_val, EPDC_TCE_HSCAN1);
+
+ reg_val =
+ ((horiz_start << EPDC_TCE_HSCAN2_LINE_BEGIN_OFFSET) &
+ EPDC_TCE_HSCAN2_LINE_BEGIN_MASK)
+ | ((horiz_end << EPDC_TCE_HSCAN2_LINE_END_OFFSET) &
+ EPDC_TCE_HSCAN2_LINE_END_MASK);
+ __raw_writel(reg_val, EPDC_TCE_HSCAN2);
+}
+
+static void epdc_set_vertical_timing(u32 vert_start, u32 vert_end,
+ u32 vsync_width)
+{
+ u32 reg_val =
+ ((vert_start << EPDC_TCE_VSCAN_FRAME_BEGIN_OFFSET) &
+ EPDC_TCE_VSCAN_FRAME_BEGIN_MASK)
+ | ((vert_end << EPDC_TCE_VSCAN_FRAME_END_OFFSET) &
+ EPDC_TCE_VSCAN_FRAME_END_MASK)
+ | ((vsync_width << EPDC_TCE_VSCAN_FRAME_SYNC_OFFSET) &
+ EPDC_TCE_VSCAN_FRAME_SYNC_MASK);
+ __raw_writel(reg_val, EPDC_TCE_VSCAN);
+}
+
+static void epdc_init_settings(struct mxc_epdc_fb_data *fb_data)
+{
+ struct imx_epdc_fb_mode *epdc_mode = fb_data->cur_mode;
+ struct fb_var_screeninfo *screeninfo = &fb_data->epdc_fb_var;
+ u32 reg_val;
+ int num_ce;
+ int i;
+
+ /* Enable clocks to access EPDC regs */
+ clk_enable(fb_data->epdc_clk_axi);
+ clk_enable(fb_data->epdc_clk_pix);
+
+ /* Reset */
+ __raw_writel(EPDC_CTRL_SFTRST, EPDC_CTRL_SET);
+ while (!(__raw_readl(EPDC_CTRL) & EPDC_CTRL_CLKGATE))
+ ;
+ __raw_writel(EPDC_CTRL_SFTRST, EPDC_CTRL_CLEAR);
+
+ /* Enable clock gating (clear to enable) */
+ __raw_writel(EPDC_CTRL_CLKGATE, EPDC_CTRL_CLEAR);
+ while (__raw_readl(EPDC_CTRL) & (EPDC_CTRL_SFTRST | EPDC_CTRL_CLKGATE))
+ ;
+
+ /* EPDC_CTRL */
+ reg_val = __raw_readl(EPDC_CTRL);
+ reg_val &= ~EPDC_CTRL_UPD_DATA_SWIZZLE_MASK;
+ reg_val |= EPDC_CTRL_UPD_DATA_SWIZZLE_NO_SWAP;
+ reg_val &= ~EPDC_CTRL_LUT_DATA_SWIZZLE_MASK;
+ reg_val |= EPDC_CTRL_LUT_DATA_SWIZZLE_NO_SWAP;
+ __raw_writel(reg_val, EPDC_CTRL_SET);
+
+ /* EPDC_FORMAT - 2bit TFT and buf_pix_fmt Buf pixel format */
+ reg_val = EPDC_FORMAT_TFT_PIXEL_FORMAT_2BIT
+ | fb_data->buf_pix_fmt
+ | ((0x0 << EPDC_FORMAT_DEFAULT_TFT_PIXEL_OFFSET) &
+ EPDC_FORMAT_DEFAULT_TFT_PIXEL_MASK);
+ __raw_writel(reg_val, EPDC_FORMAT);
+
+ /* EPDC_FIFOCTRL (disabled) */
+ reg_val =
+ ((100 << EPDC_FIFOCTRL_FIFO_INIT_LEVEL_OFFSET) &
+ EPDC_FIFOCTRL_FIFO_INIT_LEVEL_MASK)
+ | ((200 << EPDC_FIFOCTRL_FIFO_H_LEVEL_OFFSET) &
+ EPDC_FIFOCTRL_FIFO_H_LEVEL_MASK)
+ | ((100 << EPDC_FIFOCTRL_FIFO_L_LEVEL_OFFSET) &
+ EPDC_FIFOCTRL_FIFO_L_LEVEL_MASK);
+ __raw_writel(reg_val, EPDC_FIFOCTRL);
+
+ /* EPDC_TEMP - Use default temp to get index */
+ epdc_set_temp(fb_data->temp_index = mxc_epdc_fb_get_temp_index(fb_data, DEFAULT_TEMP));
+ /* EPDC_RES */
+ epdc_set_screen_res(epdc_mode->vmode->xres, epdc_mode->vmode->yres);
+
+ /* EPDC_AUTOWV_LUT */
+ /* Initialize all auto-wavefrom look-up values to 2 - GC16 */
+ for (i = 0; i < 8; i++)
+ __raw_writel((2 << EPDC_AUTOWV_LUT_DATA_OFFSET) |
+ (i << EPDC_AUTOWV_LUT_ADDR_OFFSET), EPDC_AUTOWV_LUT);
+
+ DBG_MSG("hwcfg@(%p),display bus width=0x%02x\n",gptHWCFG,gptHWCFG->m_val.bDisplayBusWidth);
+
+ switch(gptHWCFG->m_val.bDisplayBusWidth)
+ {
+ case 1:
+ // 16 bits .
+ //
+
+ /*
+ * EPDC_TCE_CTRL
+ * VSCAN_HOLDOFF = 4
+ * VCOM_MODE = MANUAL
+ * VCOM_VAL = 0
+ * DDR_MODE = DISABLED
+ * LVDS_MODE_CE = DISABLED
+ * LVDS_MODE = DISABLED
+ * DUAL_SCAN = DISABLED
+ * SDDO_WIDTH = 16bit
+ * PIXELS_PER_SDCLK = 8
+ */
+
+ DBG_MSG("16bits display bus width \n");
+ reg_val =
+ ((epdc_mode->vscan_holdoff << EPDC_TCE_CTRL_VSCAN_HOLDOFF_OFFSET) &
+ EPDC_TCE_CTRL_VSCAN_HOLDOFF_MASK)
+ | EPDC_TCE_CTRL_PIXELS_PER_SDCLK_8
+ | EPDC_TCE_CTRL_SDDO_WIDTH_16BIT
+ ;
+ break;
+
+ case 2:
+ // 8 bits ,mirror .
+ //
+
+ DBG_MSG("8bits mirror display bus width \n");
+
+ reg_val =
+ ((epdc_mode->vscan_holdoff << EPDC_TCE_CTRL_VSCAN_HOLDOFF_OFFSET) &
+ EPDC_TCE_CTRL_VSCAN_HOLDOFF_MASK)
+ | EPDC_TCE_CTRL_SCAN_DIR_0_UP
+ | EPDC_TCE_CTRL_SCAN_DIR_1_UP
+ | EPDC_TCE_CTRL_PIXELS_PER_SDCLK_4
+ //| EPDC_TCE_CTRL_SDDO_WIDTH_8BIT
+ ;
+ break;
+
+ case 3:
+ // 16 bits ,mirror .
+ //
+
+ DBG_MSG("16bits mirror display bus width \n");
+
+ reg_val =
+ ((epdc_mode->vscan_holdoff << EPDC_TCE_CTRL_VSCAN_HOLDOFF_OFFSET) &
+ EPDC_TCE_CTRL_VSCAN_HOLDOFF_MASK)
+ | EPDC_TCE_CTRL_SCAN_DIR_0_UP
+ // | EPDC_TCE_CTRL_SCAN_DIR_1_UP //@Sam 20140114 Marked for E60QC2
+ | EPDC_TCE_CTRL_PIXELS_PER_SDCLK_8
+ | EPDC_TCE_CTRL_SDDO_WIDTH_16BIT
+ ;
+ break;
+
+ default:
+ // normal 8 bits .
+
+ DBG_MSG("8bits normal display bus width \n");
+ /*
+ * EPDC_TCE_CTRL
+ * VSCAN_HOLDOFF = 4
+ * VCOM_MODE = MANUAL
+ * VCOM_VAL = 0
+ * DDR_MODE = DISABLED
+ * LVDS_MODE_CE = DISABLED
+ * LVDS_MODE = DISABLED
+ * DUAL_SCAN = DISABLED
+ * SDDO_WIDTH = 8bit
+ * PIXELS_PER_SDCLK = 4
+ */
+ reg_val =
+ ((epdc_mode->vscan_holdoff << EPDC_TCE_CTRL_VSCAN_HOLDOFF_OFFSET) &
+ EPDC_TCE_CTRL_VSCAN_HOLDOFF_MASK)
+ | EPDC_TCE_CTRL_PIXELS_PER_SDCLK_4;
+ break;
+ }
+
+ __raw_writel(reg_val, EPDC_TCE_CTRL);
+
+
+#if 1
+ {
+ struct fb_videomode *vmode = fb_data->cur_mode->vmode;
+ //DBG_MSG("%s() : left_margin=%d,right_margin=%d,hsync_len=%d\n",
+ // __FUNCTION__,vmode->left_margin,
+ // vmode->right_margin,vmode->hsync_len);
+ /* EPDC_TCE_HSCAN */
+ epdc_set_horizontal_timing(vmode->left_margin,
+ vmode->right_margin,
+ vmode->hsync_len,
+ vmode->hsync_len);
+
+ //DBG_MSG("%s() : upper_margin=%d,lower_margin=%d,vsync_len=%d\n",
+ // __FUNCTION__,vmode->upper_margin,
+ // vmode->lower_margin,vmode->vsync_len);
+ /* EPDC_TCE_VSCAN */
+ epdc_set_vertical_timing(vmode->upper_margin,
+ vmode->lower_margin,
+ vmode->vsync_len);
+ }
+#else
+ DBG_MSG("%s() : left_margin=%d,right_margin=%d,hsync_len=%d\n",
+ __FUNCTION__,screeninfo->left_margin,
+ screeninfo->right_margin,screeninfo->hsync_len);
+
+ /* EPDC_TCE_HSCAN */
+ epdc_set_horizontal_timing(screeninfo->left_margin,
+ screeninfo->right_margin,
+ screeninfo->hsync_len,
+ screeninfo->hsync_len);
+
+ DBG_MSG("%s() : upper_margin=%d,lower_margin=%d,vsync_len=%d\n",
+ __FUNCTION__,screeninfo->upper_margin,
+ screeninfo->lower_margin,screeninfo->vsync_len);
+ /* EPDC_TCE_VSCAN */
+ epdc_set_vertical_timing(screeninfo->upper_margin,
+ screeninfo->lower_margin,
+ screeninfo->vsync_len);
+#endif
+
+ /* EPDC_TCE_OE */
+ reg_val =
+ ((epdc_mode->sdoed_width << EPDC_TCE_OE_SDOED_WIDTH_OFFSET) &
+ EPDC_TCE_OE_SDOED_WIDTH_MASK)
+ | ((epdc_mode->sdoed_delay << EPDC_TCE_OE_SDOED_DLY_OFFSET) &
+ EPDC_TCE_OE_SDOED_DLY_MASK)
+ | ((epdc_mode->sdoez_width << EPDC_TCE_OE_SDOEZ_WIDTH_OFFSET) &
+ EPDC_TCE_OE_SDOEZ_WIDTH_MASK)
+ | ((epdc_mode->sdoez_delay << EPDC_TCE_OE_SDOEZ_DLY_OFFSET) &
+ EPDC_TCE_OE_SDOEZ_DLY_MASK);
+ __raw_writel(reg_val, EPDC_TCE_OE);
+
+ /* EPDC_TCE_TIMING1 */
+ __raw_writel(0x0, EPDC_TCE_TIMING1);
+
+ /* EPDC_TCE_TIMING2 */
+ reg_val =
+ ((epdc_mode->gdclk_hp_offs << EPDC_TCE_TIMING2_GDCLK_HP_OFFSET) &
+ EPDC_TCE_TIMING2_GDCLK_HP_MASK)
+ | ((epdc_mode->gdsp_offs << EPDC_TCE_TIMING2_GDSP_OFFSET_OFFSET) &
+ EPDC_TCE_TIMING2_GDSP_OFFSET_MASK);
+ __raw_writel(reg_val, EPDC_TCE_TIMING2);
+
+ /* EPDC_TCE_TIMING3 */
+ reg_val =
+ ((epdc_mode->gdoe_offs << EPDC_TCE_TIMING3_GDOE_OFFSET_OFFSET) &
+ EPDC_TCE_TIMING3_GDOE_OFFSET_MASK)
+ | ((epdc_mode->gdclk_offs << EPDC_TCE_TIMING3_GDCLK_OFFSET_OFFSET) &
+ EPDC_TCE_TIMING3_GDCLK_OFFSET_MASK);
+ __raw_writel(reg_val, EPDC_TCE_TIMING3);
+
+ /*
+ * EPDC_TCE_SDCFG
+ * SDCLK_HOLD = 1
+ * SDSHR = 1
+ * NUM_CE = 1
+ * SDDO_REFORMAT = FLIP_PIXELS
+ * SDDO_INVERT = DISABLED
+ * PIXELS_PER_CE = display horizontal resolution
+ */
+ num_ce = epdc_mode->num_ce;
+ if (num_ce == 0)
+ num_ce = 1;
+ reg_val = EPDC_TCE_SDCFG_SDCLK_HOLD | EPDC_TCE_SDCFG_SDSHR
+ | ((num_ce << EPDC_TCE_SDCFG_NUM_CE_OFFSET) &
+ EPDC_TCE_SDCFG_NUM_CE_MASK)
+ | EPDC_TCE_SDCFG_SDDO_REFORMAT_FLIP_PIXELS
+ | ((epdc_mode->vmode->xres/num_ce << EPDC_TCE_SDCFG_PIXELS_PER_CE_OFFSET) &
+ EPDC_TCE_SDCFG_PIXELS_PER_CE_MASK);
+ __raw_writel(reg_val, EPDC_TCE_SDCFG);
+
+ /*
+ * EPDC_TCE_GDCFG
+ * GDRL = 1
+ * GDOE_MODE = 0;
+ * GDSP_MODE = 0;
+ */
+ reg_val = EPDC_TCE_SDCFG_GDRL;
+ __raw_writel(reg_val, EPDC_TCE_GDCFG);
+
+ /*
+ * EPDC_TCE_POLARITY
+ * SDCE_POL = ACTIVE LOW
+ * SDLE_POL = ACTIVE HIGH
+ * SDOE_POL = ACTIVE HIGH
+ * GDOE_POL = ACTIVE HIGH
+ * GDSP_POL = ACTIVE LOW
+ */
+ reg_val = EPDC_TCE_POLARITY_SDLE_POL_ACTIVE_HIGH
+ | EPDC_TCE_POLARITY_SDOE_POL_ACTIVE_HIGH
+ | EPDC_TCE_POLARITY_GDOE_POL_ACTIVE_HIGH;
+ __raw_writel(reg_val, EPDC_TCE_POLARITY);
+
+ /* EPDC_IRQ_MASK */
+ __raw_writel(EPDC_IRQ_TCE_UNDERRUN_IRQ, EPDC_IRQ_MASK);
+
+ /*
+ * EPDC_GPIO
+ * PWRCOM = ?
+ * PWRCTRL = ?
+ * BDR = ?
+ */
+ reg_val = ((0 << EPDC_GPIO_PWRCTRL_OFFSET) & EPDC_GPIO_PWRCTRL_MASK)
+ | ((0 << EPDC_GPIO_BDR_OFFSET) & EPDC_GPIO_BDR_MASK);
+ __raw_writel(reg_val, EPDC_GPIO);
+
+ __raw_writel(fb_data->waveform_buffer_phys, EPDC_WVADDR);
+ __raw_writel(fb_data->working_buffer_A_phys, EPDC_WB_ADDR);
+ __raw_writel(fb_data->working_buffer_A_phys, EPDC_WB_ADDR_TCE);
+
+ /*
+ * init histogram registers according to the buffer pixel format
+ */
+ if (fb_data->buf_pix_fmt == EPDC_FORMAT_BUF_PIXEL_FORMAT_P4N) {
+ __raw_writel(EPDC_HIST1_P4N_PARAM, EPDC_HIST1_PARAM);
+ __raw_writel(EPDC_HIST2_P4N_PARAM, EPDC_HIST2_PARAM);
+ __raw_writel(EPDC_HIST4_P4N_PARAM, EPDC_HIST4_PARAM);
+ __raw_writel(EPDC_HIST8_P4N_PARAM0, EPDC_HIST8_PARAM0);
+ __raw_writel(EPDC_HIST8_P4N_PARAM1, EPDC_HIST8_PARAM1);
+ __raw_writel(EPDC_HIST16_P4N_PARAM0, EPDC_HIST16_PARAM0);
+ __raw_writel(EPDC_HIST16_P4N_PARAM1, EPDC_HIST16_PARAM1);
+ __raw_writel(EPDC_HIST16_P4N_PARAM2, EPDC_HIST16_PARAM2);
+ __raw_writel(EPDC_HIST16_P4N_PARAM3, EPDC_HIST16_PARAM3);
+ }
+ else {
+ __raw_writel(EPDC_HIST1_P5N_PARAM, EPDC_HIST1_PARAM);
+ __raw_writel(EPDC_HIST2_P5N_PARAM, EPDC_HIST2_PARAM);
+ __raw_writel(EPDC_HIST4_P5N_PARAM, EPDC_HIST4_PARAM);
+ __raw_writel(EPDC_HIST8_P5N_PARAM0, EPDC_HIST8_PARAM0);
+ __raw_writel(EPDC_HIST8_P5N_PARAM1, EPDC_HIST8_PARAM1);
+ __raw_writel(EPDC_HIST16_P5N_PARAM0, EPDC_HIST16_PARAM0);
+ __raw_writel(EPDC_HIST16_P5N_PARAM1, EPDC_HIST16_PARAM1);
+ __raw_writel(EPDC_HIST16_P5N_PARAM2, EPDC_HIST16_PARAM2);
+ __raw_writel(EPDC_HIST16_P5N_PARAM3, EPDC_HIST16_PARAM3);
+ }
+
+ /* Disable clock */
+ clk_disable(fb_data->epdc_clk_axi);
+ clk_disable(fb_data->epdc_clk_pix);
+}
+
+#include "mxc_epdc_fake_s1d13522.c"
+
+static void epdc_powerup(struct mxc_epdc_fb_data *fb_data)
+{
+ struct epd_vc_data vcd;
+ //int ret = 0;
+
+ GALLEN_DBGLOCAL_BEGIN();
+
+ mutex_lock(&fb_data->power_mutex);
+
+ /*
+ * If power down request is pending, clear
+ * powering_down to cancel the request.
+ */
+ if (fb_data->powering_down) {
+ GALLEN_DBGLOCAL_RUNLOG(0);
+ fb_data->powering_down = false;
+ }
+
+ if (fb_data->power_state == POWER_STATE_ON) {
+ mutex_unlock(&fb_data->power_mutex);
+ GALLEN_DBGLOCAL_ESC();
+ return;
+ }
+
+ dev_dbg(fb_data->dev, "EPDC Powerup\n");
+ if (fb_data->wfm < 256) {
+ /* fetch and display the voltage control data for waveform mode 0, temp range 0 */
+ fetch_Epdc_Pmic_Voltages(&vcd, fb_data, fb_data->wfm, fb_data->temp_index);
+ }
+ else
+ vcd.v5 = 0;
+
+
+ fb_data->updates_active = true;
+#ifdef USE_BSP_PMIC //[
+
+ /* Enable the v3p3 regulator */
+ ret = regulator_enable(fb_data->v3p3_regulator);
+ if (IS_ERR((void *)ret)) {
+ dev_err(fb_data->dev, "Unable to enable V3P3 regulator."
+ "err = 0x%x\n", ret);
+ mutex_unlock(&fb_data->power_mutex);
+ GALLEN_DBGLOCAL_ESC();
+ return;
+ }
+
+ msleep(1);
+#else //][!USE_BSP_PMIC
+
+ if(fb_data->waveform_vcd_buffer) {
+ DBG_MSG(" *** enabling the VCOM (%d,%d) ***\n", fb_data->wfm, fb_data->temp_index);
+ if (fb_data->wfm < 256) {
+ int vcom_uV, new_vcom_uV;
+ int v5_sign = 1;
+ int v5_offset = vcd.v5 & 0x7fff;
+ int vcom_mV;
+ int vcom_mV_offset;
+ int vcd_offset_uV;
+
+
+ //if(0x09==gbFPL_Platform)
+ if(36==gptHWCFG->m_val.bPCB||40==gptHWCFG->m_val.bPCB)
+ {
+ // V320 waveform .
+ vcom_mV_offset=-250;
+ }
+ else
+ {
+ vcom_mV_offset=0;
+ }
+
+ /* get vcom offset value */
+ if (vcd.v5 & 0x8000) {
+ v5_sign = -1;
+ }
+
+ if(8==gptHWCFG->m_val.bDisplayCtrl) {
+ fp9928_vcom_get_cached(&vcom_mV);
+ }
+ else {
+ tps65185_vcom_get_cached(&vcom_mV);
+ }
+ vcom_uV=vcom_mV*1000;
+
+ DBG_MSG("**** PMIC VCOM mV=%d,vcom_nominal=%d,v5_offset=%d,v5_sign=%d,vcom_mv_offset=%d *****\n",
+ vcom_mV,vcom_nominal,v5_offset,v5_sign,vcom_mV_offset);
+
+ vcd_offset_uV = v5_offset * 3125 * v5_sign;
+ new_vcom_uV = vcom_nominal + vcd_offset_uV+(vcom_mV_offset*1000);
+ if(vcom_uV!=new_vcom_uV) {
+ //if ((new_vcom_uV >= -3050000) && ( new_vcom_uV <= -500000))
+ if ((new_vcom_uV <= 0) && ( new_vcom_uV >= -5000000))
+ {
+ printk("vcom %d000 ==> %d uV (vcd offset:0x%04x=%duV,force %d000 uV)\n",
+ vcom_mV, new_vcom_uV, vcd.v5,vcd_offset_uV,vcom_mV_offset);
+
+ vcom_mV=new_vcom_uV/1000;
+ if(8==gptHWCFG->m_val.bDisplayCtrl) {
+ fp9928_vcom_set(vcom_mV,0);
+ }
+ else {
+ tps65185_vcom_set(vcom_mV,0);
+ }
+ }
+ else {
+ printk(" adjusted VCOM is out of range, %d uV (offset:0x%04x)\n", new_vcom_uV, vcd.v5);
+ }
+ }
+
+ }
+ }
+
+
+ if(8==gptHWCFG->m_val.bDisplayCtrl) {
+ int iChk;
+ iChk = fp9928_output_power(1,0);
+ if(iChk<0) {
+ printk(KERN_ERR "%s(%d):[warning] output power from FP9928 fail ,errno=%d !\n",
+ __FILE__,__LINE__,iChk);
+ }
+ }
+ else
+ {
+ int iChk;
+ unsigned long dwTPS65185_mode = TPS65185_MODE_ACTIVE;
+
+ iChk = tps65185_chg_mode(&dwTPS65185_mode,1);
+ if(iChk<0) {
+ printk(KERN_ERR "%s(%d):[warning] change to power active fail,errno=%d !\n",
+ __FILE__,__LINE__,iChk);
+ }
+ }
+
+#endif //] USE_BSP_PMIC
+
+ k_set_temperature(&fb_data->info);
+
+ /* Enable pins used by EPDC */
+ if (fb_data->pdata->enable_pins) {
+ GALLEN_DBGLOCAL_RUNLOG(1);
+ fb_data->pdata->enable_pins();
+ }
+
+ /* Enable clocks to EPDC */
+ clk_enable(fb_data->epdc_clk_axi);
+ clk_enable(fb_data->epdc_clk_pix);
+
+ __raw_writel(EPDC_CTRL_CLKGATE, EPDC_CTRL_CLEAR);
+
+#ifdef USE_BSP_PMIC //[
+ printk(" *** enabling the EPDC PMIC (%d,%d) ***\n",fb_data->wfm, fb_data->temp_index);
+
+ /* Enable power to the EPD panel */
+ ret = regulator_enable(fb_data->display_regulator);
+ if (IS_ERR((void *)ret)) {
+ dev_err(fb_data->dev, "Unable to enable DISPLAY regulator."
+ "err = 0x%x\n", ret);
+ mutex_unlock(&fb_data->power_mutex);
+ GALLEN_DBGLOCAL_ESC();
+ return;
+ }
+
+ printk(" *** enabling the VCOM (%d,%d) ***\n", fb_data->wfm, fb_data->temp_index);
+ if (fb_data->wfm < 256) {
+ int vcom_uV, new_vcom_uV;
+ int v5_sign = 1;
+ int v5_offset = vcd.v5 & 0x7fff;
+
+ /* get vcom offset value */
+ if (vcd.v5 & 0x8000) {
+ v5_sign = -1;
+ }
+ vcom_uV = regulator_get_voltage(fb_data->vcom_regulator);
+ new_vcom_uV = vcom_nominal + v5_offset * 3125 * v5_sign;
+ if ((new_vcom_uV >= -3050000) && ( new_vcom_uV <= -500000)) {
+ printk(" current vcom is %d uV, adjusted VCOM will be %d uV (offset:0x%04x)\n", vcom_uV, new_vcom_uV, vcd.v5);
+ regulator_set_voltage(fb_data->vcom_regulator, new_vcom_uV, new_vcom_uV);
+ }
+ printk(" adjusted VCOM is out of range, %d uV (offset:0x%04x)\n", new_vcom_uV, vcd.v5);
+ }
+ ret = regulator_enable(fb_data->vcom_regulator);
+ if (IS_ERR((void *)ret)) {
+ dev_err(fb_data->dev, "Unable to enable VCOM regulator."
+ "err = 0x%x\n", ret);
+ mutex_unlock(&fb_data->power_mutex);
+ GALLEN_DBGLOCAL_ESC();
+ return;
+ }
+#endif //] USE_BSP_PMIC
+
+ fb_data->power_state = POWER_STATE_ON;
+
+ mutex_unlock(&fb_data->power_mutex);
+
+ GALLEN_DBGLOCAL_END();
+}
+
+static void epdc_powerdown(struct mxc_epdc_fb_data *fb_data)
+{
+ GALLEN_DBGLOCAL_BEGIN();
+ mutex_lock(&fb_data->power_mutex);
+
+ /* If powering_down has been cleared, a powerup
+ * request is pre-empting this powerdown request.
+ */
+ if (!fb_data->powering_down
+ || (fb_data->power_state == POWER_STATE_OFF)) {
+ mutex_unlock(&fb_data->power_mutex);
+ GALLEN_DBGLOCAL_ESC();
+ return;
+ }
+
+ dev_dbg(fb_data->dev, "EPDC Powerdown\n");
+
+#ifdef USE_BSP_PMIC //[
+
+ /* Disable power to the EPD panel */
+ regulator_disable(fb_data->vcom_regulator);
+ regulator_disable(fb_data->display_regulator);
+#else //][!USB_BSP_PMIC
+
+#endif //] USB_BSP_PMIC
+
+ /* Disable clocks to EPDC */
+ __raw_writel(EPDC_CTRL_CLKGATE, EPDC_CTRL_SET);
+ clk_disable(fb_data->epdc_clk_pix);
+ clk_disable(fb_data->epdc_clk_axi);
+
+ /* Disable pins used by EPDC (to prevent leakage current) */
+ if (fb_data->pdata->disable_pins) {
+ GALLEN_DBGLOCAL_RUNLOG(0);
+ fb_data->pdata->disable_pins();
+ }
+
+#ifdef DO_NOT_POWEROFF
+#else//][!DO_NOT_POWEROFF
+#ifdef USE_BSP_PMIC //[
+ /* turn off the V3p3 */
+ regulator_disable(fb_data->v3p3_regulator);
+#else //][!USE_BSP_PMIC
+ if(8==gptHWCFG->m_val.bDisplayCtrl) {
+ int iChk;
+ iChk = fp9928_output_power(0,1);
+ if(iChk<0) {
+ printk(KERN_ERR "%s(%d):[warning] disable power from FP9928 fail ,errno=%d !\n",
+ __FILE__,__LINE__,iChk);
+ }
+ }
+ else
+ {
+ int iChk;
+ unsigned long dwTPS65185_mode = TPS65185_MODE_STANDBY;
+
+ GALLEN_DBGLOCAL_RUNLOG(1);
+ iChk = tps65185_chg_mode(&dwTPS65185_mode,1);
+ if(iChk<0) {
+ printk(KERN_ERR "%s(%d):[warning] change to power sleep fail ,errno=%d !\n",
+ __FILE__,__LINE__,iChk);
+ }
+
+ }
+#endif //] USE_BSP_PMIC
+#endif //] DO_NOT_POWEROFF
+
+ fb_data->power_state = POWER_STATE_OFF;
+ fb_data->powering_down = false;
+
+ if (fb_data->wait_for_powerdown) {
+ GALLEN_DBGLOCAL_RUNLOG(2);
+ fb_data->wait_for_powerdown = false;
+ complete(&fb_data->powerdown_compl);
+ }
+
+ fb_data->updates_active = false;// gallen add 20130116.
+ mutex_unlock(&fb_data->power_mutex);
+ GALLEN_DBGLOCAL_END();
+}
+
+
+static void epdc_init_sequence(struct mxc_epdc_fb_data *fb_data)
+{
+ /* Initialize EPDC, passing pointer to EPDC registers */
+ epdc_init_settings(fb_data);
+ fb_data->in_init = true;
+
+#if 0
+ epdc_powerup(fb_data);
+ draw_mode0(fb_data);
+ /* Force power down event */
+ fb_data->powering_down = true;
+ epdc_powerdown(fb_data);
+#endif
+
+ fb_data->updates_active = false;
+}
+
+static int mxc_epdc_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
+{
+ u32 len;
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ u32 mem_start,mem_len;
+
+ GALLEN_DBGLOCAL_BEGIN();
+
+#ifdef CONFIG_ANDROID//[
+#else//][!CONFIG_ANDROID
+ fake_s1d13522_progress_stop();
+ k_fake_s1d13522_wait_inited();
+#endif //]CONFIG_ANDROID
+
+ if(0==gptHWCFG->m_val.bUIStyle) {
+ GALLEN_DBGLOCAL_RUNLOG(0);
+ //mem_start = (u32)gpbLOGO_paddr;
+ //mem_len = (u32)((2048*1024*1024)-1024);
+ mem_start = info->fix.smem_start+info->fix.smem_len;
+ mem_len = info->fix.smem_len;
+
+ }
+ else {
+ GALLEN_DBGLOCAL_RUNLOG(1);
+ mem_start = info->fix.smem_start;
+ mem_len = info->fix.smem_len;
+ }
+
+ GALLEN_DBGLOCAL_PRINTMSG("mem_start=0x%x,mem_len=0x%x\n",\
+ mem_start,mem_len);
+ GALLEN_DBGLOCAL_PRINTMSG("vma.vm_start=0x%x,vma.vm_end=0x%x\n",\
+ vma->vm_start,vma->vm_end);
+ GALLEN_DBGLOCAL_PRINTMSG("offset=0x%x\n",\
+ offset);
+
+
+ if (offset < mem_len) {
+ /* mapping framebuffer memory */
+ GALLEN_DBGLOCAL_RUNLOG(2);
+ len = mem_len - offset;
+ vma->vm_pgoff = (mem_start + offset) >> PAGE_SHIFT;
+ }
+ else
+ {
+ GALLEN_DBGLOCAL_ESC();
+ return -EINVAL;
+ }
+
+ len = PAGE_ALIGN(len);
+ if (vma->vm_end - vma->vm_start > len) {
+ GALLEN_DBGLOCAL_ESC();
+ return -EINVAL;
+ }
+
+ /* make buffers bufferable */
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+ vma->vm_flags |= VM_IO | VM_RESERVED;
+
+ if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
+ vma->vm_end - vma->vm_start, vma->vm_page_prot)) {
+ GALLEN_DBGLOCAL_ESC();
+ dev_dbg(info->device, "mmap remap_pfn_range failed\n");
+ return -ENOBUFS;
+ }
+
+ GALLEN_DBGLOCAL_END();
+ return 0;
+}
+
+static inline u_int _chan_to_field(u_int chan, struct fb_bitfield *bf)
+{
+ chan &= 0xffff;
+ chan >>= 16 - bf->length;
+ return chan << bf->offset;
+}
+
+static int mxc_epdc_fb_setcolreg(u_int regno, u_int red, u_int green,
+ u_int blue, u_int transp, struct fb_info *info)
+{
+ unsigned int val;
+ int ret = 1;
+
+ /*
+ * If greyscale is true, then we convert the RGB value
+ * to greyscale no matter what visual we are using.
+ */
+ if (info->var.grayscale)
+ red = green = blue = (19595 * red + 38470 * green +
+ 7471 * blue) >> 16;
+ switch (info->fix.visual) {
+ case FB_VISUAL_TRUECOLOR:
+ /*
+ * 16-bit True Colour. We encode the RGB value
+ * according to the RGB bitfield information.
+ */
+ if (regno < 16) {
+ u32 *pal = info->pseudo_palette;
+
+ val = _chan_to_field(red, &info->var.red);
+ val |= _chan_to_field(green, &info->var.green);
+ val |= _chan_to_field(blue, &info->var.blue);
+
+ pal[regno] = val;
+ ret = 0;
+ }
+ break;
+
+ case FB_VISUAL_STATIC_PSEUDOCOLOR:
+ case FB_VISUAL_PSEUDOCOLOR:
+ break;
+ }
+
+ return ret;
+}
+
+static int mxc_epdc_fb_setcmap(struct fb_cmap *cmap, struct fb_info *info)
+{
+ int count, index, r;
+ u16 *red, *green, *blue, *transp;
+ u16 trans = 0xffff;
+ struct mxc_epdc_fb_data *fb_data = (struct mxc_epdc_fb_data *)info;
+ int i;
+
+ GALLEN_DBGLOCAL_BEGIN();
+
+ dev_dbg(fb_data->dev, "setcmap\n");
+
+ if (info->fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR) {
+ /* Only support an 8-bit, 256 entry lookup */
+ if (cmap->len != 256) {
+ GALLEN_DBGLOCAL_ESC();
+ return 1;
+ }
+
+ mxc_epdc_fb_flush_updates(fb_data);
+
+ mutex_lock(&fb_data->pxp_mutex);
+ /*
+ * Store colormap in pxp_conf structure for later transmit
+ * to PxP during update process to convert gray pixels.
+ *
+ * Since red=blue=green for pseudocolor visuals, we can
+ * just use red values.
+ */
+ for (i = 0; i < 256; i++)
+ fb_data->pxp_conf.proc_data.lut_map[i] = cmap->red[i] & 0xFF;
+
+ fb_data->pxp_conf.proc_data.lut_map_updated = true;
+
+ mutex_unlock(&fb_data->pxp_mutex);
+ } else {
+ red = cmap->red;
+ green = cmap->green;
+ blue = cmap->blue;
+ transp = cmap->transp;
+ index = cmap->start;
+
+ for (count = 0; count < cmap->len; count++) {
+ if (transp)
+ trans = *transp++;
+ r = mxc_epdc_fb_setcolreg(index++, *red++, *green++, *blue++,
+ trans, info);
+ if (r != 0)
+ return r;
+ }
+ }
+
+ GALLEN_DBGLOCAL_END();
+ return 0;
+}
+
+static void adjust_coordinates(u32 xres, u32 yres, u32 rotation,
+ struct mxcfb_rect *update_region, struct mxcfb_rect *adj_update_region)
+{
+ u32 temp;
+
+ /* If adj_update_region == NULL, pass result back in update_region */
+ /* If adj_update_region == valid, use it to pass back result */
+ if (adj_update_region)
+ switch (rotation) {
+ case FB_ROTATE_UR:
+ adj_update_region->top = update_region->top;
+ adj_update_region->left = update_region->left;
+ adj_update_region->width = update_region->width;
+ adj_update_region->height = update_region->height;
+ break;
+ case FB_ROTATE_CW:
+ adj_update_region->top = update_region->left;
+ if (yres < (update_region->top + update_region->height))
+ adj_update_region->left = 0;
+ else
+ adj_update_region->left = yres -
+ (update_region->top + update_region->height);
+ adj_update_region->width = update_region->height;
+ adj_update_region->height = update_region->width;
+ break;
+ case FB_ROTATE_UD:
+ adj_update_region->width = update_region->width;
+ adj_update_region->height = update_region->height;
+ if (yres < (update_region->top + update_region->height))
+ adj_update_region->top = 0;
+ else
+ adj_update_region->top = yres -
+ (update_region->top + update_region->height);
+ if (xres < (update_region->left + update_region->width))
+ adj_update_region->left = 0;
+ else
+ adj_update_region->left = xres -
+ (update_region->left + update_region->width);
+ break;
+ case FB_ROTATE_CCW:
+ adj_update_region->left = update_region->top;
+ if (xres < (update_region->left + update_region->width))
+ adj_update_region->top = 0;
+ else
+ adj_update_region->top = xres -
+ (update_region->left + update_region->width);
+ adj_update_region->width = update_region->height;
+ adj_update_region->height = update_region->width;
+ break;
+ }
+ else
+ switch (rotation) {
+ case FB_ROTATE_UR:
+ /* No adjustment needed */
+ break;
+ case FB_ROTATE_CW:
+ temp = update_region->top;
+ update_region->top = update_region->left;
+ if (yres < (temp + update_region->height))
+ update_region->left = 0;
+ else
+ update_region->left = yres -
+ (temp + update_region->height);
+ temp = update_region->width;
+ update_region->width = update_region->height;
+ update_region->height = temp;
+ break;
+ case FB_ROTATE_UD:
+ if (yres < (update_region->top + update_region->height))
+ update_region->top = 0;
+ else
+ update_region->top = yres -
+ (update_region->top + update_region->height);
+ if (xres < (update_region->left + update_region->width))
+ update_region->left = 0;
+ else
+ update_region->left = xres -
+ (update_region->left + update_region->width);
+ break;
+ case FB_ROTATE_CCW:
+ temp = update_region->left;
+ update_region->left = update_region->top;
+ if (xres < (temp + update_region->width))
+ update_region->top = 0;
+ else
+ update_region->top = xres -
+ (temp + update_region->width);
+ temp = update_region->width;
+ update_region->width = update_region->height;
+ update_region->height = temp;
+ break;
+ }
+}
+
+/*
+ * Set fixed framebuffer parameters based on variable settings.
+ *
+ * @param info framebuffer information pointer
+ */
+static int mxc_epdc_fb_set_fix(struct fb_info *info)
+{
+ struct fb_fix_screeninfo *fix = &info->fix;
+ struct fb_var_screeninfo *var = &info->var;
+
+ fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
+
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->accel = FB_ACCEL_NONE;
+ if (var->grayscale)
+ fix->visual = FB_VISUAL_STATIC_PSEUDOCOLOR;
+ else
+ fix->visual = FB_VISUAL_TRUECOLOR;
+ fix->xpanstep = 1;
+ fix->ypanstep = 1;
+
+ return 0;
+}
+
+/*
+ * This routine actually sets the video mode. It's in here where we
+ * the hardware state info->par and fix which can be affected by the
+ * change in par. For this driver it doesn't do much.
+ *
+ */
+static int mxc_epdc_fb_set_par(struct fb_info *info)
+{
+ struct mxc_epdc_fb_data *fb_data = (struct mxc_epdc_fb_data *)info;
+ struct pxp_config_data *pxp_conf = &fb_data->pxp_conf;
+ struct pxp_proc_data *proc_data = &pxp_conf->proc_data;
+ struct fb_var_screeninfo *screeninfo = &fb_data->info.var;
+ struct imx_epdc_fb_mode *epdc_modes = fb_data->pdata->epdc_mode;
+ int i;
+ int ret;
+ __u32 xoffset_old, yoffset_old;
+
+ GALLEN_DBGLOCAL_BEGIN();
+
+ /*
+ * Can't change the FB parameters until current updates have completed.
+ * This function returns when all active updates are done.
+ */
+ mxc_epdc_fb_flush_updates(fb_data);
+
+ mutex_lock(&fb_data->queue_mutex);
+ /*
+ * Set all screeninfo except for xoffset/yoffset
+ * Subsequent call to pan_display will handle those.
+ */
+ xoffset_old = fb_data->epdc_fb_var.xoffset;
+ yoffset_old = fb_data->epdc_fb_var.yoffset;
+ fb_data->epdc_fb_var = *screeninfo;
+ fb_data->epdc_fb_var.xoffset = xoffset_old;
+ fb_data->epdc_fb_var.yoffset = yoffset_old;
+ mutex_unlock(&fb_data->queue_mutex);
+
+ mutex_lock(&fb_data->pxp_mutex);
+
+ /*
+ * Update PxP config data (used to process FB regions for updates)
+ * based on FB info and processing tasks required
+ */
+
+ /* Initialize non-channel-specific PxP parameters */
+ proc_data->drect.left = proc_data->srect.left = 0;
+ proc_data->drect.top = proc_data->srect.top = 0;
+ proc_data->drect.width = proc_data->srect.width = screeninfo->xres;
+ proc_data->drect.height = proc_data->srect.height = screeninfo->yres;
+ proc_data->scaling = 0;
+ proc_data->hflip = 0;
+ proc_data->vflip = 0;
+ proc_data->rotate = screeninfo->rotate;
+ proc_data->bgcolor = 0;
+ proc_data->overlay_state = 0;
+ proc_data->lut_transform = PXP_LUT_NONE;
+
+ /*
+ * configure S0 channel parameters
+ * Parameters should match FB format/width/height
+ */
+ if (screeninfo->grayscale) {
+ GALLEN_DBGLOCAL_RUNLOG(0);
+ pxp_conf->s0_param.pixel_fmt = PXP_PIX_FMT_GREY;
+ }
+ else {
+ GALLEN_DBGLOCAL_RUNLOG(1);
+ switch (screeninfo->bits_per_pixel) {
+ case 16:
+ pxp_conf->s0_param.pixel_fmt = PXP_PIX_FMT_RGB565;
+ break;
+ case 24:
+ pxp_conf->s0_param.pixel_fmt = PXP_PIX_FMT_RGB24;
+ break;
+ case 32:
+ pxp_conf->s0_param.pixel_fmt = PXP_PIX_FMT_RGB32;
+ break;
+ default:
+ pxp_conf->s0_param.pixel_fmt = PXP_PIX_FMT_RGB565;
+ break;
+ }
+ }
+ pxp_conf->s0_param.width = screeninfo->xres_virtual;
+ pxp_conf->s0_param.height = screeninfo->yres;
+ pxp_conf->s0_param.color_key = -1;
+ pxp_conf->s0_param.color_key_enable = false;
+
+ /*
+ * Initialize Output channel parameters
+ * Output is Y-only greyscale
+ * Output width/height will vary based on update region size
+ */
+ pxp_conf->out_param.width = screeninfo->xres;
+ pxp_conf->out_param.height = screeninfo->yres;
+ pxp_conf->out_param.pixel_fmt = PXP_PIX_FMT_GREY;
+
+ mutex_unlock(&fb_data->pxp_mutex);
+
+ /*
+ * If HW not yet initialized, check to see if we are being sent
+ * an initialization request.
+ */
+ if (!fb_data->hw_ready) {
+ struct fb_videomode mode;
+ struct fb_videomode cur_mode;
+ bool found_match = false;
+ u32 xres_temp;
+
+ fb_var_to_videomode(&mode, screeninfo);
+
+ /* When comparing requested fb mode,
+ we need to use unrotated dimensions */
+ if ((screeninfo->rotate == FB_ROTATE_CW) ||
+ (screeninfo->rotate == FB_ROTATE_CCW)) {
+ xres_temp = mode.xres;
+ mode.xres = mode.yres;
+ mode.yres = xres_temp;
+ }
+
+ if(fb_data->cur_mode->vmode) {
+ cur_mode = *(fb_data->cur_mode->vmode);
+ cur_mode.pixclock = 1000000000/(fb_data->cur_mode->vmode->pixclock/1000);
+ }
+
+ /* if cur_mode resolution is correct, do not try to match it */
+ //if (fb_mode_is_equal(fb_data->cur_mode->vmode, &mode))
+ if (fb_mode_is_equal(&cur_mode, &mode))
+ found_match = true;
+
+ /* Match videomode against epdc modes */
+ else
+ for (i = 0; i < fb_data->pdata->num_modes; i++) {
+ //if (!fb_mode_is_equal(epdc_modes[i].vmode, &mode))
+ cur_mode = *(epdc_modes[i].vmode);
+ cur_mode.pixclock = 1000000000/(fb_data->cur_mode->vmode->pixclock/1000);
+ if(!fb_mode_is_equal(&cur_mode, &mode))
+ continue;
+ fb_data->cur_mode = &epdc_modes[i];
+ found_match = true;
+ break;
+ }
+
+ if (!found_match) {
+ dev_err(fb_data->dev,
+ "Failed to match requested video mode\n");
+ GALLEN_DBGLOCAL_ESC();
+ return EINVAL;
+ }
+
+ /* Found a match - Grab timing params */
+ screeninfo->left_margin = mode.left_margin;
+ screeninfo->right_margin = mode.right_margin;
+ screeninfo->upper_margin = mode.upper_margin;
+ screeninfo->lower_margin = mode.lower_margin;
+ screeninfo->hsync_len = mode.hsync_len;
+ screeninfo->vsync_len = mode.vsync_len;
+
+ fb_data->hw_initializing = true;
+
+ /* Initialize EPDC settings and init panel */
+ ret =
+ mxc_epdc_fb_init_hw((struct fb_info *)fb_data);
+ if (ret) {
+ dev_err(fb_data->dev,
+ "Failed to load panel waveform data\n");
+ GALLEN_DBGLOCAL_ESC();
+ return ret;
+ }
+ }
+
+ /*
+ * EOF sync delay (in us) should be equal to the vscan holdoff time
+ * VSCAN_HOLDOFF time = (VSCAN_HOLDOFF value + 1) * Vertical lines
+ * Add 25us for additional margin
+ */
+ fb_data->eof_sync_period = (fb_data->cur_mode->vscan_holdoff + 1) *
+ 1000000/(fb_data->cur_mode->vmode->refresh *
+ (fb_data->cur_mode->vmode->upper_margin +
+ fb_data->cur_mode->vmode->yres +
+ fb_data->cur_mode->vmode->lower_margin +
+ fb_data->cur_mode->vmode->vsync_len)) + 25;
+
+ mxc_epdc_fb_set_fix(info);
+ GALLEN_DBGLOCAL_END();
+ return 0;
+}
+
+static int mxc_epdc_fb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct mxc_epdc_fb_data *fb_data = (struct mxc_epdc_fb_data *)info;
+
+ GALLEN_DBGLOCAL_BEGIN();
+
+ if (!var->xres) {
+ GALLEN_DBGLOCAL_RUNLOG(0);
+ var->xres = 1;
+ }
+ if (!var->yres) {
+ GALLEN_DBGLOCAL_RUNLOG(1);
+ var->yres = 1;
+ }
+
+ if (var->xres_virtual < var->xoffset + var->xres) {
+ GALLEN_DBGLOCAL_RUNLOG(2);
+ var->xres_virtual = var->xoffset + var->xres;
+ }
+ if (var->yres_virtual < var->yoffset + var->yres) {
+ GALLEN_DBGLOCAL_RUNLOG(3);
+ var->yres_virtual = var->yoffset + var->yres;
+ }
+
+ if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) &&
+ (var->bits_per_pixel != 16) && (var->bits_per_pixel != 8)) {
+ GALLEN_DBGLOCAL_RUNLOG(4);
+ var->bits_per_pixel = default_bpp;
+ }
+
+ switch (var->bits_per_pixel) {
+ case 8:
+ GALLEN_DBGLOCAL_RUNLOG(5);
+ if (var->grayscale != 0) {
+ GALLEN_DBGLOCAL_RUNLOG(6);
+ /*
+ * For 8-bit grayscale, R, G, and B offset are equal.
+ *
+ */
+ var->red.length = 8;
+ var->red.offset = 0;
+ var->red.msb_right = 0;
+
+ var->green.length = 8;
+ var->green.offset = 0;
+ var->green.msb_right = 0;
+
+ var->blue.length = 8;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ var->transp.msb_right = 0;
+ } else {
+ GALLEN_DBGLOCAL_RUNLOG(7);
+ var->red.length = 3;
+ var->red.offset = 5;
+ var->red.msb_right = 0;
+
+ var->green.length = 3;
+ var->green.offset = 2;
+ var->green.msb_right = 0;
+
+ var->blue.length = 2;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ var->transp.msb_right = 0;
+ }
+ break;
+ case 16:
+ GALLEN_DBGLOCAL_RUNLOG(8);
+ var->red.length = 5;
+ var->red.offset = 11;
+ var->red.msb_right = 0;
+
+ var->green.length = 6;
+ var->green.offset = 5;
+ var->green.msb_right = 0;
+
+ var->blue.length = 5;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ var->transp.msb_right = 0;
+ break;
+ case 24:
+ GALLEN_DBGLOCAL_RUNLOG(9);
+ var->red.length = 8;
+ var->red.offset = 16;
+ var->red.msb_right = 0;
+
+ var->green.length = 8;
+ var->green.offset = 8;
+ var->green.msb_right = 0;
+
+ var->blue.length = 8;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ var->transp.msb_right = 0;
+ break;
+ case 32:
+ GALLEN_DBGLOCAL_RUNLOG(10);
+ var->red.length = 8;
+ var->red.offset = 16;
+ var->red.msb_right = 0;
+
+ var->green.length = 8;
+ var->green.offset = 8;
+ var->green.msb_right = 0;
+
+ var->blue.length = 8;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 8;
+ var->transp.offset = 24;
+ var->transp.msb_right = 0;
+ break;
+ }
+
+ switch (var->rotate) {
+ case FB_ROTATE_UR:
+ case FB_ROTATE_UD:
+ GALLEN_DBGLOCAL_RUNLOG(11);
+ var->xres = fb_data->native_width;
+ var->yres = fb_data->native_height;
+ break;
+ case FB_ROTATE_CW:
+ case FB_ROTATE_CCW:
+ GALLEN_DBGLOCAL_RUNLOG(12);
+ var->xres = fb_data->native_height;
+ var->yres = fb_data->native_width;
+ break;
+ default:
+ GALLEN_DBGLOCAL_RUNLOG(13);
+ /* Invalid rotation value */
+ var->rotate = 0;
+ dev_dbg(fb_data->dev, "Invalid rotation request\n");
+ GALLEN_DBGLOCAL_ESC();
+ return -EINVAL;
+ }
+
+ var->xres_virtual = ALIGN(var->xres, 32);
+ var->yres_virtual = ALIGN(var->yres, 128) * fb_data->num_screens;
+
+ epdfbdc_set_width_height(gptDC,ALIGN(var->xres, 32),ALIGN(var->yres, 128),var->xres,var->yres);
+
+ var->height = -1;
+ var->width = -1;
+
+ GALLEN_DBGLOCAL_END();
+ return 0;
+}
+
+void mxc_epdc_fb_set_waveform_modes(struct mxcfb_waveform_modes *modes,
+ struct fb_info *info)
+{
+ struct mxc_epdc_fb_data *fb_data = info ?
+ (struct mxc_epdc_fb_data *)info:g_fb_data;
+
+
+ WARNING_MSG("%s(%d):%s() skip\n",__FILE__,__LINE__,__FUNCTION__);
+ return ;
+
+ mutex_lock(&fb_data->queue_mutex);
+
+ memcpy(&fb_data->wv_modes, modes, sizeof(struct mxcfb_waveform_modes));
+
+ /* Set flag to ensure that new waveform modes
+ * are programmed into EPDC before next update */
+ fb_data->wv_modes_update = true;
+
+ mutex_unlock(&fb_data->queue_mutex);
+}
+EXPORT_SYMBOL(mxc_epdc_fb_set_waveform_modes);
+
+static int mxc_epdc_fb_get_temp_index(struct mxc_epdc_fb_data *fb_data, int temp)
+{
+ int i;
+ int index = -1;
+ int iTotallyHit=0;
+
+ if (fb_data->trt_entries == 0) {
+ dev_err(fb_data->dev,
+ "No TRT index match (%d)...using default temp index\n",
+ temp);
+
+ return DEFAULT_TEMP_INDEX;
+ }
+
+#if 1
+ /* Search temperature ranges for a match */
+ for (i = 0; i < fb_data->trt_entries; i++) {
+ if (temp >= fb_data->temp_range_bounds[i])
+ {
+ index = i;
+ if(temp < fb_data->temp_range_bounds[i+1]) {
+ iTotallyHit = 1;
+ break;
+ }
+ }
+ }
+#else
+ /* Search temperature ranges for a match */
+ for (i = 0; i < fb_data->trt_entries; i++) {
+ if ((temp >= fb_data->temp_range_bounds[i])
+ && (temp < fb_data->temp_range_bounds[i+1])) {
+ index = i;
+ iTotallyHit=1;
+ break;
+ }
+ }
+#endif
+
+ if (index < 0) {
+ dev_err(fb_data->dev,
+ "No TRT index (temp=%d) match,trt entries=%d...using default temp index,temp=%d\n",
+ temp,fb_data->trt_entries,fb_data->temp_range_bounds[0]);
+ return DEFAULT_TEMP_INDEX;
+ }
+ else {
+ if(1!=iTotallyHit) {
+ dev_err(fb_data->dev,
+ "closer TRT index (temp=%d) match,trt entries=%d...using temp index=%d,temp=%d~%d\n",
+ temp,fb_data->trt_entries,index,fb_data->temp_range_bounds[index],fb_data->temp_range_bounds[index+1]);
+ }
+ }
+
+ dev_dbg(fb_data->dev, "Using temperature index %d\n", index);
+
+ return index;
+}
+
+int mxc_epdc_fb_set_temperature(int temperature, struct fb_info *info)
+{
+ struct mxc_epdc_fb_data *fb_data = info ?
+ (struct mxc_epdc_fb_data *)info:g_fb_data;
+
+ /* Store temp index. Used later when configuring updates. */
+ mutex_lock(&fb_data->queue_mutex);
+ fb_data->temp_index = mxc_epdc_fb_get_temp_index(fb_data, temperature);
+ mutex_unlock(&fb_data->queue_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(mxc_epdc_fb_set_temperature);
+
+int mxc_epdc_fb_set_auto_update(u32 auto_mode, struct fb_info *info)
+{
+ struct mxc_epdc_fb_data *fb_data = info ?
+ (struct mxc_epdc_fb_data *)info:g_fb_data;
+
+ GALLEN_DBGLOCAL_BEGIN();
+ dev_dbg(fb_data->dev, "Setting auto update mode to %d\n", auto_mode);
+
+ if ((auto_mode == AUTO_UPDATE_MODE_AUTOMATIC_MODE)
+ || (auto_mode == AUTO_UPDATE_MODE_REGION_MODE)) {
+ GALLEN_DBGLOCAL_PRINTMSG("%s,auto_mode=%hd\n",__FUNCTION__,auto_mode);
+ fb_data->auto_mode = auto_mode;
+ }
+ else {
+ dev_err(fb_data->dev, "Invalid auto update mode parameter.\n");
+ GALLEN_DBGLOCAL_ESC();
+ return -EINVAL;
+ }
+
+ GALLEN_DBGLOCAL_END();
+ return 0;
+}
+EXPORT_SYMBOL(mxc_epdc_fb_set_auto_update);
+
+int mxc_epdc_fb_set_upd_scheme(u32 upd_scheme, struct fb_info *info)
+{
+ struct mxc_epdc_fb_data *fb_data = info ?
+ (struct mxc_epdc_fb_data *)info:g_fb_data;
+
+ GALLEN_DBGLOCAL_BEGIN();
+
+ dev_dbg(fb_data->dev, "Setting optimization level to %d\n", upd_scheme);
+
+ /*
+ * Can't change the scheme until current updates have completed.
+ * This function returns when all active updates are done.
+ */
+ mxc_epdc_fb_flush_updates(fb_data);
+
+ if ((upd_scheme == UPDATE_SCHEME_SNAPSHOT)
+ || (upd_scheme == UPDATE_SCHEME_QUEUE)
+ || (upd_scheme == UPDATE_SCHEME_QUEUE_AND_MERGE)) {
+ GALLEN_DBGLOCAL_RUNLOG(0);
+ fb_data->upd_scheme = upd_scheme;
+ }
+ else {
+ dev_err(fb_data->dev, "Invalid update scheme specified.\n");
+ GALLEN_DBGLOCAL_ESC();
+ return -EINVAL;
+ }
+
+ GALLEN_DBGLOCAL_END();
+ return 0;
+}
+EXPORT_SYMBOL(mxc_epdc_fb_set_upd_scheme);
+
+static void copy_before_process(struct mxc_epdc_fb_data *fb_data,
+ struct update_data_list *upd_data_list)
+{
+ struct mxcfb_update_data *upd_data =
+ &upd_data_list->update_desc->upd_data;
+ int i;
+ unsigned char *temp_buf_ptr = fb_data->virt_addr_copybuf;
+ unsigned char *src_ptr;
+ struct mxcfb_rect *src_upd_region;
+ int temp_buf_stride;
+ int src_stride;
+ int bpp = fb_data->epdc_fb_var.bits_per_pixel;
+ int left_offs, right_offs;
+ int x_trailing_bytes, y_trailing_bytes;
+ int alt_buf_offset;
+
+ /* Set source buf pointer based on input source, panning, etc. */
+ if (upd_data->flags & EPDC_FLAG_USE_ALT_BUFFER) {
+ src_upd_region = &upd_data->alt_buffer_data.alt_update_region;
+ src_stride =
+ upd_data->alt_buffer_data.width * bpp/8;
+ alt_buf_offset = upd_data->alt_buffer_data.phys_addr -
+ fb_data->info.fix.smem_start;
+ src_ptr = fb_data->info.screen_base + alt_buf_offset
+ + src_upd_region->top * src_stride;
+ } else {
+ src_upd_region = &upd_data->update_region;
+ src_stride = fb_data->epdc_fb_var.xres_virtual * bpp/8;
+ src_ptr = fb_data->info.screen_base + fb_data->fb_offset
+ + src_upd_region->top * src_stride;
+ }
+
+ temp_buf_stride = ALIGN(src_upd_region->width, 8) * bpp/8;
+ left_offs = src_upd_region->left * bpp/8;
+ right_offs = src_upd_region->width * bpp/8;
+ x_trailing_bytes = (ALIGN(src_upd_region->width, 8)
+ - src_upd_region->width) * bpp/8;
+
+ for (i = 0; i < src_upd_region->height; i++) {
+ /* Copy the full line */
+ memcpy(temp_buf_ptr, src_ptr + left_offs,
+ src_upd_region->width * bpp/8);
+
+ /* Clear any unwanted pixels at the end of each line */
+ if (src_upd_region->width & 0x7) {
+ memset(temp_buf_ptr + right_offs, 0x0,
+ x_trailing_bytes);
+ }
+
+ temp_buf_ptr += temp_buf_stride;
+ src_ptr += src_stride;
+ }
+
+ /* Clear any unwanted pixels at the bottom of the end of each line */
+ if (src_upd_region->height & 0x7) {
+ y_trailing_bytes = (ALIGN(src_upd_region->height, 8)
+ - src_upd_region->height) *
+ ALIGN(src_upd_region->width, 8) * bpp/8;
+ memset(temp_buf_ptr, 0x0, y_trailing_bytes);
+ }
+}
+
+static int epdc_process_update(struct update_data_list *upd_data_list,
+ struct mxc_epdc_fb_data *fb_data)
+{
+ struct mxcfb_rect *src_upd_region; /* Region of src buffer for update */
+ struct mxcfb_rect pxp_upd_region;
+ u32 src_width, src_height;
+ u32 offset_from_4, bytes_per_pixel;
+ u32 post_rotation_xcoord, post_rotation_ycoord, width_pxp_blocks;
+ u32 pxp_input_offs, pxp_output_offs, pxp_output_shift;
+ u32 hist_stat = 0;
+ int width_unaligned, height_unaligned;
+ bool input_unaligned = false;
+ bool line_overflow = false;
+ int pix_per_line_added;
+ bool use_temp_buf = false;
+ struct mxcfb_rect temp_buf_upd_region;
+ struct update_desc_list *upd_desc_list = upd_data_list->update_desc;
+
+ int ret;
+
+ GALLEN_DBGLOCAL_BEGIN_EX2(32,giDbgLvl);
+ /*
+ * Gotta do a whole bunch of buffer ptr manipulation to
+ * work around HW restrictions for PxP & EPDC
+ * Note: Applies to pre-2.0 versions of EPDC/PxP
+ */
+
+ /*
+ * Are we using FB or an alternate (overlay)
+ * buffer for source of update?
+ */
+ if (upd_desc_list->upd_data.flags & EPDC_FLAG_USE_ALT_BUFFER) {
+ src_width = upd_desc_list->upd_data.alt_buffer_data.width;
+ src_height = upd_desc_list->upd_data.alt_buffer_data.height;
+ src_upd_region = &upd_desc_list->upd_data.alt_buffer_data.alt_update_region;
+ GALLEN_DBGLOCAL_RUNLOG(0);
+ } else {
+ src_width = fb_data->epdc_fb_var.xres_virtual;
+ src_height = fb_data->epdc_fb_var.yres;
+ src_upd_region = &upd_desc_list->upd_data.update_region;
+ GALLEN_DBGLOCAL_RUNLOG(1);
+ }
+
+ bytes_per_pixel = fb_data->epdc_fb_var.bits_per_pixel/8;
+
+ /*
+ * SW workaround for PxP limitation (for pre-v2.0 HW)
+ *
+ * There are 3 cases where we cannot process the update data
+ * directly from the input buffer:
+ *
+ * 1) PxP must process 8x8 pixel blocks, and all pixels in each block
+ * are considered for auto-waveform mode selection. If the
+ * update region is not 8x8 aligned, additional unwanted pixels
+ * will be considered in auto-waveform mode selection.
+ *
+ * 2) PxP input must be 32-bit aligned, so any update
+ * address not 32-bit aligned must be shifted to meet the
+ * 32-bit alignment. The PxP will thus end up processing pixels
+ * outside of the update region to satisfy this alignment restriction,
+ * which can affect auto-waveform mode selection.
+ *
+ * 3) If input fails 32-bit alignment, and the resulting expansion
+ * of the processed region would add at least 8 pixels more per
+ * line than the original update line width, the EPDC would
+ * cause screen artifacts by incorrectly handling the 8+ pixels
+ * at the end of each line.
+ *
+ * Workaround is to copy from source buffer into a temporary
+ * buffer, which we pad with zeros to match the 8x8 alignment
+ * requirement. This temp buffer becomes the input to the PxP.
+ */
+ width_unaligned = src_upd_region->width & 0x7;
+ height_unaligned = src_upd_region->height & 0x7;
+
+ offset_from_4 = src_upd_region->left & 0x3;
+ input_unaligned = ((offset_from_4 * bytes_per_pixel % 4) != 0) ?
+ true : false;
+
+ pix_per_line_added = (offset_from_4 * bytes_per_pixel % 4)
+ / bytes_per_pixel;
+ if ((((fb_data->epdc_fb_var.rotate == FB_ROTATE_UR) ||
+ fb_data->epdc_fb_var.rotate == FB_ROTATE_UD)) &&
+ (ALIGN(src_upd_region->width, 8) <
+ ALIGN(src_upd_region->width + pix_per_line_added, 8))) {
+ GALLEN_DBGLOCAL_RUNLOG(2);
+ line_overflow = true;
+ }
+
+ /* Grab pxp_mutex here so that we protect access
+ * to copybuf in addition to the PxP structures */
+ mutex_lock(&fb_data->pxp_mutex);
+
+ if (((((width_unaligned || height_unaligned || input_unaligned) &&
+ (upd_desc_list->upd_data.waveform_mode == WAVEFORM_MODE_AUTO))
+ || line_overflow) && (fb_data->rev < 20)) ||
+ fb_data->restrict_width) {
+
+ GALLEN_DBGLOCAL_RUNLOG(3);
+ dev_dbg(fb_data->dev, "Copying update before processing.\n");
+
+ /* Update to reflect what the new source buffer will be */
+ src_width = ALIGN(src_upd_region->width, 8);
+ src_height = ALIGN(src_upd_region->height, 8);
+
+ copy_before_process(fb_data, upd_data_list);
+
+ /*
+ * src_upd_region should now describe
+ * the new update buffer attributes.
+ */
+ temp_buf_upd_region.left = 0;
+ temp_buf_upd_region.top = 0;
+ temp_buf_upd_region.width = src_upd_region->width;
+ temp_buf_upd_region.height = src_upd_region->height;
+ src_upd_region = &temp_buf_upd_region;
+
+ use_temp_buf = true;
+ }
+
+ /*
+ * For pre-2.0 HW, input address must be 32-bit aligned
+ * Compute buffer offset to account for this PxP limitation
+ */
+ offset_from_4 = src_upd_region->left & 0x3;
+ input_unaligned = ((offset_from_4 * bytes_per_pixel % 4) != 0) ?
+ true : false;
+ if ((fb_data->rev < 20) && input_unaligned) {
+ GALLEN_DBGLOCAL_RUNLOG(4);
+ /* Leave a gap between PxP input addr and update region pixels */
+ pxp_input_offs =
+ (src_upd_region->top * src_width + src_upd_region->left)
+ * bytes_per_pixel & 0xFFFFFFFC;
+ /* Update region left changes to reflect relative position to input ptr */
+ pxp_upd_region.left = (offset_from_4 * bytes_per_pixel % 4)
+ / bytes_per_pixel;
+ } else {
+ GALLEN_DBGLOCAL_RUNLOG(5);
+
+ pxp_input_offs =
+ (src_upd_region->top * src_width + src_upd_region->left)
+ * bytes_per_pixel;
+ pxp_upd_region.left = 0;
+ }
+
+ pxp_upd_region.top = 0;
+
+ /*
+ * For version 2.0 and later of EPDC & PxP, if no rotation, we don't
+ * need to align width & height (rotation always requires 8-pixel
+ * width & height alignment, per PxP limitations)
+ */
+ if ((fb_data->epdc_fb_var.rotate == 0) && (fb_data->rev >= 20)) {
+ GALLEN_DBGLOCAL_RUNLOG(6);
+ pxp_upd_region.width = src_upd_region->width;
+ pxp_upd_region.height = src_upd_region->height;
+ } else {
+ GALLEN_DBGLOCAL_RUNLOG(7);
+ /* Update region dimensions to meet 8x8 pixel requirement */
+ pxp_upd_region.width = ALIGN(src_upd_region->width + pxp_upd_region.left, 8);
+ pxp_upd_region.height = ALIGN(src_upd_region->height, 8);
+ }
+
+ switch (fb_data->epdc_fb_var.rotate) {
+ case FB_ROTATE_UR:
+ default:GALLEN_DBGLOCAL_RUNLOG(8);
+ post_rotation_xcoord = pxp_upd_region.left;
+ post_rotation_ycoord = pxp_upd_region.top;
+ width_pxp_blocks = pxp_upd_region.width;
+ break;
+ case FB_ROTATE_CW:GALLEN_DBGLOCAL_RUNLOG(9);
+ width_pxp_blocks = pxp_upd_region.height;
+ post_rotation_xcoord = width_pxp_blocks - src_upd_region->height;
+ post_rotation_ycoord = pxp_upd_region.left;
+ break;
+ case FB_ROTATE_UD:GALLEN_DBGLOCAL_RUNLOG(10);
+ width_pxp_blocks = pxp_upd_region.width;
+ post_rotation_xcoord = width_pxp_blocks - src_upd_region->width - pxp_upd_region.left;
+ post_rotation_ycoord = pxp_upd_region.height - src_upd_region->height - pxp_upd_region.top;
+ break;
+ case FB_ROTATE_CCW:GALLEN_DBGLOCAL_RUNLOG(11);
+ width_pxp_blocks = pxp_upd_region.height;
+ post_rotation_xcoord = pxp_upd_region.top;
+ post_rotation_ycoord = pxp_upd_region.width - src_upd_region->width - pxp_upd_region.left;
+ break;
+ }
+
+ /* Update region start coord to force PxP to process full 8x8 regions */
+ pxp_upd_region.top &= ~0x7;
+ pxp_upd_region.left &= ~0x7;
+
+ if (fb_data->rev < 20) {
+ GALLEN_DBGLOCAL_RUNLOG(12);
+ pxp_output_shift = ALIGN(post_rotation_xcoord, 8)
+ - post_rotation_xcoord;
+
+ pxp_output_offs = post_rotation_ycoord * width_pxp_blocks
+ + pxp_output_shift;
+
+ upd_desc_list->epdc_offs = ALIGN(pxp_output_offs, 8);
+ } else {
+ GALLEN_DBGLOCAL_RUNLOG(13);
+ pxp_output_shift = 0;
+ pxp_output_offs = post_rotation_ycoord * width_pxp_blocks
+ + post_rotation_xcoord;
+
+ upd_desc_list->epdc_offs = pxp_output_offs;
+ }
+
+ upd_desc_list->epdc_stride = width_pxp_blocks;
+
+ /* Source address either comes from alternate buffer
+ provided in update data, or from the framebuffer. */
+ if (use_temp_buf) {
+
+ GALLEN_DBGLOCAL_RUNLOG(14);
+ sg_dma_address(&fb_data->sg[0]) =
+ fb_data->phys_addr_copybuf;
+ }
+ else if (upd_desc_list->upd_data.flags & EPDC_FLAG_USE_ALT_BUFFER) {
+
+ GALLEN_DBGLOCAL_RUNLOG(15);
+ sg_dma_address(&fb_data->sg[0]) =
+ upd_desc_list->upd_data.alt_buffer_data.phys_addr
+ + pxp_input_offs;
+ }
+ else {
+
+ GALLEN_DBGLOCAL_RUNLOG(16);
+ sg_dma_address(&fb_data->sg[0]) =
+ fb_data->info.fix.smem_start + fb_data->fb_offset
+ + pxp_input_offs;
+ sg_set_page(&fb_data->sg[0],
+ virt_to_page(fb_data->info.screen_base),
+ fb_data->info.fix.smem_len,
+ offset_in_page(fb_data->info.screen_base));
+ }
+
+ /* Update sg[1] to point to output of PxP proc task */
+ sg_dma_address(&fb_data->sg[1]) = upd_data_list->phys_addr
+ + pxp_output_shift;
+ sg_set_page(&fb_data->sg[1], virt_to_page(upd_data_list->virt_addr),
+ fb_data->max_pix_size,
+ offset_in_page(upd_data_list->virt_addr));
+
+ /*
+ * Set PxP LUT transform type based on update flags.
+ */
+ fb_data->pxp_conf.proc_data.lut_transform = 0;
+ if (upd_desc_list->upd_data.flags & EPDC_FLAG_ENABLE_INVERSION) {
+ GALLEN_DBGLOCAL_RUNLOG(17);
+ fb_data->pxp_conf.proc_data.lut_transform |= PXP_LUT_INVERT;
+ }
+#if 0
+ if (upd_desc_list->upd_data.flags & EPDC_FLAG_FORCE_MONOCHROME ||
+ upd_desc_list->iDither_state )
+#else
+ if (upd_desc_list->upd_data.flags & EPDC_FLAG_FORCE_MONOCHROME)
+#endif
+ {
+ GALLEN_DBGLOCAL_RUNLOG(18);
+ fb_data->pxp_conf.proc_data.lut_transform |=
+ PXP_LUT_BLACK_WHITE;
+ }
+ if (upd_desc_list->upd_data.flags & EPDC_FLAG_USE_CMAP) {
+ GALLEN_DBGLOCAL_RUNLOG(19);
+ fb_data->pxp_conf.proc_data.lut_transform |=
+ PXP_LUT_USE_CMAP;
+ }
+
+ /*
+ * Toggle inversion processing if 8-bit
+ * inverted is the current pixel format.
+ */
+ if (fb_data->epdc_fb_var.grayscale == GRAYSCALE_8BIT_INVERTED) {
+ GALLEN_DBGLOCAL_RUNLOG(20);
+ fb_data->pxp_conf.proc_data.lut_transform ^= PXP_LUT_INVERT;
+ }
+
+ /* Enable PxP LUT option to configure pixel data for AA processing */
+ if (fb_data->buf_pix_fmt == EPDC_FORMAT_BUF_PIXEL_FORMAT_P5N) {
+ /* turn off PXP_LUT_AA if dithering flag is set */
+ if ((upd_desc_list->upd_data.flags & EPDC_FLAG_USE_DITHERING_Y1) || (upd_desc_list->upd_data.flags & EPDC_FLAG_USE_DITHERING_Y4)) {
+ fb_data->pxp_conf.proc_data.lut_transform &= ~PXP_LUT_AA;
+ }
+ else {
+ fb_data->pxp_conf.proc_data.lut_transform |= PXP_LUT_AA;
+ }
+ }
+
+ DBG_MSG("iDiterStat=%d,pxp lut tranform=0x%x\n",
+ upd_desc_list->iDither_state,fb_data->pxp_conf.proc_data.lut_transform);
+ flush_cache_all();
+ outer_flush_all();
+ /* This is a blocking call, so upon return PxP tx should be done */
+ ret = pxp_process_update(fb_data, src_width, src_height,
+ &pxp_upd_region);
+ if (ret) {
+ dev_err(fb_data->dev, "Unable to submit PxP update task.\n");
+ mutex_unlock(&fb_data->pxp_mutex);
+ GALLEN_DBGLOCAL_ESC();
+ return ret;
+ }
+
+ /* If needed, enable EPDC HW while ePxP is processing */
+ if ((fb_data->power_state == POWER_STATE_OFF)
+ || fb_data->powering_down) {
+ GALLEN_DBGLOCAL_RUNLOG(21);
+ epdc_powerup(fb_data);
+ }
+
+ /* This is a blocking call, so upon return PxP tx should be done */
+ ret = pxp_complete_update(fb_data, &hist_stat);
+ if (ret) {
+ dev_err(fb_data->dev, "Unable to complete PxP update task.\n");
+ mutex_unlock(&fb_data->pxp_mutex);
+ GALLEN_DBGLOCAL_ESC();
+ return ret;
+ }
+
+ mutex_unlock(&fb_data->pxp_mutex);
+ flush_cache_all();
+ outer_flush_all();
+
+ /* Update waveform mode from PxP histogram results */
+#if 0
+ if ((fb_data->rev <= 20) &&
+ (upd_desc_list->upd_data.waveform_mode == WAVEFORM_MODE_AUTO))
+#else
+
+ if ((upd_desc_list->upd_data.waveform_mode == WAVEFORM_MODE_AUTO))
+#endif
+ {
+ GALLEN_DBGLOCAL_RUNLOG(22);
+ if (hist_stat & 0x1) {
+ GALLEN_DBGLOCAL_RUNLOG(23);
+ upd_desc_list->upd_data.waveform_mode =
+ fb_data->wv_modes.mode_du;
+ }
+ else if (hist_stat & 0x2) {
+ GALLEN_DBGLOCAL_RUNLOG(24);
+ upd_desc_list->upd_data.waveform_mode =
+ fb_data->wv_modes.mode_gc4;
+ }
+ else if (hist_stat & 0x4) {
+ GALLEN_DBGLOCAL_RUNLOG(25);
+ upd_desc_list->upd_data.waveform_mode =
+ fb_data->wv_modes.mode_gc8;
+ }
+ else if (hist_stat & 0x8) {
+ GALLEN_DBGLOCAL_RUNLOG(26);
+#if defined(NTX_AUTOMODE_PATCH) //[ waveform mode change automatically .
+ #if !defined(NO_AUTO_REAGL_MODE) //[
+ if (fb_data->buf_pix_fmt == EPDC_FORMAT_BUF_PIXEL_FORMAT_P5N &&
+ fb_data->upd_scheme != UPDATE_SCHEME_SNAPSHOT)
+ {
+ DBG_MSG("assigned aa mode,5bits wf,colors>8\n");
+
+ if(UPDATE_MODE_FULL==upd_desc_list->upd_data.update_mode) {
+ GALLEN_DBGLOCAL_RUNLOG(24);
+ upd_data_list->update_desc->is_aa = 1;
+ upd_data_list->update_desc->upd_data.flags |= EPDC_FLAG_USE_AAD;
+ upd_desc_list->upd_data.waveform_mode =
+ fb_data->wv_modes.mode_aad;
+ }
+ else {
+ #if 0
+ upd_data_list->update_desc->is_aa = 1;
+ upd_data_list->update_desc->upd_data.flags &= ~EPDC_FLAG_USE_AAD;
+ upd_desc_list->upd_data.waveform_mode =
+ fb_data->wv_modes.mode_aa;
+ #else
+ upd_desc_list->upd_data.waveform_mode =
+ fb_data->wv_modes.mode_gl16;
+ #endif
+ }
+ }
+ else
+ #endif //]
+ {
+ if(UPDATE_MODE_FULL==upd_desc_list->upd_data.update_mode) {
+ GALLEN_DBGLOCAL_RUNLOG(28);
+ upd_desc_list->upd_data.waveform_mode =
+ fb_data->wv_modes.mode_gc16;
+ }
+ else {
+ upd_desc_list->upd_data.waveform_mode =
+ fb_data->wv_modes.mode_gl16;
+ }
+ }
+#else //][!NTX_AUTOMODE_PATCH
+ upd_desc_list->upd_data.waveform_mode =
+ fb_data->wv_modes.mode_gc16;
+#endif//] NTX_AUTOMODE_PATCH
+
+ }
+ else {
+ GALLEN_DBGLOCAL_RUNLOG(27);
+ upd_desc_list->upd_data.waveform_mode =
+ fb_data->wv_modes.mode_gc32;
+ }
+
+ dev_dbg(fb_data->dev, "hist_stat = 0x%x, new waveform = 0x%x\n",
+ hist_stat, upd_desc_list->upd_data.waveform_mode);
+ }
+
+ GALLEN_DBGLOCAL_END();
+ return 0;
+}
+
+static int epdc_submit_merge(struct update_desc_list *upd_desc_list,
+ struct update_desc_list *update_to_merge,
+ struct mxc_epdc_fb_data *fb_data)
+{
+ struct mxcfb_update_data *a, *b;
+ struct mxcfb_rect *arect, *brect;
+ struct mxcfb_rect combine;
+ bool use_flags = false;
+
+ GALLEN_DBGLOCAL_BEGIN_EX2(32,giDbgLvl);
+
+ a = &upd_desc_list->upd_data;
+ b = &update_to_merge->upd_data;
+ arect = &upd_desc_list->upd_data.update_region;
+ brect = &update_to_merge->upd_data.update_region;
+
+ /* Do not merge a dry-run collision test update */
+ if ((a->flags & EPDC_FLAG_TEST_COLLISION) ||
+ (b->flags & EPDC_FLAG_TEST_COLLISION))
+ {
+ GALLEN_DBGLOCAL_ESC();
+ return MERGE_BLOCK;
+ }
+
+ /*
+ * Updates with different flags must be executed sequentially.
+ * Halt the merge process to ensure this.
+ */
+ if (a->flags != b->flags) {
+ GALLEN_DBGLOCAL_RUNLOG(0);
+ /*
+ * Special exception: if update regions are identical,
+ * we may be able to merge them.
+ */
+ if ((arect->left != brect->left) ||
+ (arect->top != brect->top) ||
+ (arect->width != brect->width) ||
+ (arect->height != brect->height))
+ {
+ GALLEN_DBGLOCAL_ESC();
+ return MERGE_BLOCK;
+ }
+
+ use_flags = true;
+ }
+
+ if (arect->left > (brect->left + brect->width) ||
+ brect->left > (arect->left + arect->width) ||
+ arect->top > (brect->top + brect->height) ||
+ brect->top > (arect->top + arect->height))
+ {
+ GALLEN_DBGLOCAL_ESC();
+ return MERGE_FAIL;
+ }
+
+ combine.left = arect->left < brect->left ? arect->left : brect->left;
+ combine.top = arect->top < brect->top ? arect->top : brect->top;
+ combine.width = (arect->left + arect->width) >
+ (brect->left + brect->width) ?
+ (arect->left + arect->width - combine.left) :
+ (brect->left + brect->width - combine.left);
+ combine.height = (arect->top + arect->height) >
+ (brect->top + brect->height) ?
+ (arect->top + arect->height - combine.top) :
+ (brect->top + brect->height - combine.top);
+
+ DBG_MSG("%s():upd rect merge,a@%d(%d,%d,%d,%d)+b@%d(%d,%d,%d,%d)=>a(%d,%d,%d,%d)\n",
+ __FUNCTION__,(int)upd_desc_list->update_order,\
+ arect->left,arect->top,arect->width,arect->height,\
+ (int)update_to_merge->update_order,
+ brect->left,brect->top,brect->width,brect->height,\
+ combine.left,combine.top,combine.width,combine.height);
+
+ /* Don't merge if combined width exceeds max width */
+ if (fb_data->restrict_width) {
+ u32 max_width = EPDC_V2_MAX_UPDATE_WIDTH;
+ u32 combined_width = combine.width;
+
+ GALLEN_DBGLOCAL_RUNLOG(4);
+
+ if (fb_data->epdc_fb_var.rotate != FB_ROTATE_UR)
+ {
+ GALLEN_DBGLOCAL_RUNLOG(5);
+ max_width -= EPDC_V2_ROTATION_ALIGNMENT;
+ }
+ if ((fb_data->epdc_fb_var.rotate == FB_ROTATE_CW) ||
+ (fb_data->epdc_fb_var.rotate == FB_ROTATE_CCW))
+ {
+ GALLEN_DBGLOCAL_RUNLOG(6);
+ combined_width = combine.height;
+ }
+ if (combined_width > max_width)
+ {
+ GALLEN_DBGLOCAL_ESC();
+ return MERGE_FAIL;
+ }
+ }
+
+ if (a->update_mode != b->update_mode)
+ {
+ GALLEN_DBGLOCAL_RUNLOG(1);
+ a->update_mode = UPDATE_MODE_FULL;
+ }
+
+ if (a->waveform_mode != b->waveform_mode)
+ {
+ GALLEN_DBGLOCAL_RUNLOG(2);
+#if 1 //[
+ DBG_MSG("%s():wf mode merge %d->%d\n",__FUNCTION__,a->waveform_mode,fb_data->wv_modes.mode_gc16);
+ a->waveform_mode = fb_data->wv_modes.mode_gc16;
+#else //][!
+ DBG_MSG("%s():wf mode merge %d->%d",__FUNCTION__,a->waveform_mode,WAVEFORM_MODE_AUTO);
+ a->waveform_mode = WAVEFORM_MODE_AUTO;
+#endif //]
+
+#if defined(NO_AUTO_REAGL_MODE) //[
+ upd_desc_list->is_aa=0;
+#else //][
+ if(fb_data->buf_pix_fmt == EPDC_FORMAT_BUF_PIXEL_FORMAT_P5N) {
+ GALLEN_DBGLOCAL_RUNLOG(8);
+ if( a->waveform_mode==fb_data->wv_modes.mode_aa||
+ a->waveform_mode==fb_data->wv_modes.mode_aad )
+ {
+ GALLEN_DBGLOCAL_RUNLOG(9);
+ upd_desc_list->is_aa=1;
+ }
+ else {
+ upd_desc_list->is_aa=0;
+ }
+ }
+ else {
+ GALLEN_DBGLOCAL_RUNLOG(10);
+ upd_desc_list->is_aa=0;
+ }
+ DBG_MSG("%s():is_aa=%d,wf_mode=%d\n",__FUNCTION__,
+ upd_desc_list->is_aa,a->waveform_mode);
+#endif//]
+ }
+
+ *arect = combine;
+
+ /* Use flags of the later update */
+ if (use_flags)
+ {
+ GALLEN_DBGLOCAL_RUNLOG(7);
+ DBG_MSG("%s():flag merge 0x%x->0x%x\n",__FUNCTION__,a->flags,b->flags);
+ a->flags = b->flags;
+ if( upd_desc_list->is_aa &&
+ (a->waveform_mode==fb_data->wv_modes.mode_aad) &&
+ (fb_data->wv_modes.mode_aad != fb_data->wv_modes.mode_aa))
+ {
+ a->flags |= EPDC_FLAG_USE_AAD;
+ }
+ }
+
+
+ /* Merge markers */
+ list_splice_tail(&update_to_merge->upd_marker_list,
+ &upd_desc_list->upd_marker_list);
+
+ /* Merged update should take on the earliest order */
+ upd_desc_list->update_order =
+ (upd_desc_list->update_order > update_to_merge->update_order) ?
+ upd_desc_list->update_order : update_to_merge->update_order;
+
+
+ GALLEN_DBGLOCAL_END();
+ return MERGE_OK;
+}
+
+#ifdef FW_IN_RAM //[
+
+static void epdc_firmware_work_func(struct work_struct *work)
+{
+ struct mxc_epdc_fb_data *fb_data =
+ container_of(work, struct mxc_epdc_fb_data, epdc_firmware_work);
+ struct firmware fw;
+
+
+ if(!delayed_work_pending(&fb_data->epdc_firmware_work)) {
+
+ down(&fb_data->firmware_work_lock);
+ if(0==gbModeVersion) {
+ fw.size = gdwWF_size;
+ fw.data = (u8*)gpbWF_vaddr;
+
+ printk("[%s]:fw p=%p,size=%u,fb_data@%p,pdata=%p\n",__FUNCTION__,
+ fw.data,fw.size,fb_data,fb_data->pdata);
+ mxc_epdc_fb_fw_handler(&fw,fb_data);
+ }
+ else {
+ printk(KERN_WARNING"%s skipped cause firmware bas been setup\n",__FUNCTION__);
+ }
+ up(&fb_data->firmware_work_lock);
+ }
+ else {
+ printk(KERN_WARNING"%s skipped cause firmware work is pending \n",__FUNCTION__);
+ }
+}
+
+#endif //] FW_IN_RAM
+
+static void epdc_submit_work_func(struct work_struct *work)
+{
+ struct update_data_list *next_update, *temp_update;
+ struct update_desc_list *next_desc, *temp_desc;
+ struct update_marker_data *next_marker, *temp_marker;
+ struct mxc_epdc_fb_data *fb_data =
+ container_of(work, struct mxc_epdc_fb_data, epdc_submit_work);
+ struct update_data_list *upd_data_list = NULL;
+ struct mxcfb_rect adj_update_region, *upd_region;
+ bool end_merge = false;
+ bool is_transform;
+ u32 update_addr;
+ int *err_dist;
+ int ret;
+ struct mxcfb_rect actual_update_region;
+ int direct_draw = 0;
+
+ GALLEN_DBGLOCAL_BEGIN_EX2(64,giDbgLvl);
+ /* Protect access to buffer queues and to update HW */
+ mutex_lock(&fb_data->queue_mutex);
+
+ /*
+ * Are any of our collision updates able to go now?
+ * Go through all updates in the collision list and check to see
+ * if the collision mask has been fully cleared
+ */
+ list_for_each_entry_safe(next_update, temp_update,
+ &fb_data->upd_buf_collision_list, list) {
+
+ if (next_update->collision_mask != 0) {
+ GALLEN_DBGLOCAL_RUNLOG(0);
+ continue;
+ }
+
+ dev_dbg(fb_data->dev, "A collision update is ready to go!\n");
+
+#ifdef AVOID_REAGL_DIRTY_TEMP//[
+ /* Force waveform mode to gc15 for resubmitted collisions
+ * to avoid dirty issue since we use auto mode to enable reagl .
+ */
+ next_update->update_desc->upd_data.waveform_mode =
+ g_fb_data->wv_modes.mode_gc16;
+#else //][!AVOID_REAGL_DIRTY_TEMP
+ /* Force waveform mode to auto for resubmitted collisions */
+ next_update->update_desc->upd_data.waveform_mode =
+ WAVEFORM_MODE_AUTO;
+#endif //]AVOID_REAGL_DIRTY_TEMP
+
+ /*
+ * We have a collision cleared, so select it for resubmission.
+ * If an update is already selected, attempt to merge.
+ */
+ if (!upd_data_list) {
+ GALLEN_DBGLOCAL_RUNLOG(1);
+ upd_data_list = next_update;
+ list_del_init(&next_update->list);
+ if (fb_data->upd_scheme == UPDATE_SCHEME_QUEUE) {
+ GALLEN_DBGLOCAL_RUNLOG(2);
+ /* If not merging, we have our update */
+ break;
+ }
+ } else {
+ GALLEN_DBGLOCAL_RUNLOG(3);
+ switch (epdc_submit_merge(upd_data_list->update_desc,
+ next_update->update_desc,
+ fb_data)) {
+ case MERGE_OK:GALLEN_DBGLOCAL_RUNLOG(4);
+ dev_dbg(fb_data->dev,
+ "Update merged [collision]\n");
+ list_del_init(&next_update->update_desc->list);
+ kfree(next_update->update_desc);
+ next_update->update_desc = NULL;
+ list_del_init(&next_update->list);
+ /* Add to free buffer list */
+ list_add_tail(&next_update->list,
+ &fb_data->upd_buf_free_list);
+ break;
+ case MERGE_FAIL:GALLEN_DBGLOCAL_RUNLOG(5);
+ dev_dbg(fb_data->dev,
+ "Update not merged [collision]\n");
+ break;
+ case MERGE_BLOCK:GALLEN_DBGLOCAL_RUNLOG(6);
+ dev_dbg(fb_data->dev,
+ "Merge blocked [collision]\n");
+ end_merge = true;
+ break;
+ }
+
+ if (end_merge) {
+ GALLEN_DBGLOCAL_RUNLOG(7);
+ end_merge = false;
+ break;
+ }
+ }
+ }
+
+ /*
+ * Skip pending update list only if we found a collision
+ * update and we are not merging
+ */
+ if (!((fb_data->upd_scheme == UPDATE_SCHEME_QUEUE) &&
+ upd_data_list)) {
+ GALLEN_DBGLOCAL_RUNLOG(8);
+ /*
+ * If we didn't find a collision update ready to go, we
+ * need to get a free buffer and match it to a pending update.
+ */
+
+ /*
+ * Can't proceed if there are no free buffers (and we don't
+ * already have a collision update selected)
+ */
+ if (!upd_data_list &&
+ list_empty(&fb_data->upd_buf_free_list)) {
+ mutex_unlock(&fb_data->queue_mutex);
+ GALLEN_DBGLOCAL_ESC();
+ return;
+ }
+
+ list_for_each_entry_safe(next_desc, temp_desc,
+ &fb_data->upd_pending_list, list) {
+ GALLEN_DBGLOCAL_RUNLOG(9);
+
+ dev_dbg(fb_data->dev, "Found a pending update!\n");
+
+ if (!upd_data_list) {
+ GALLEN_DBGLOCAL_RUNLOG(10);
+ if (list_empty(&fb_data->upd_buf_free_list)) {
+ GALLEN_DBGLOCAL_RUNLOG(11);
+ break;
+ }
+ upd_data_list =
+ list_entry(fb_data->upd_buf_free_list.next,
+ struct update_data_list, list);
+ list_del_init(&upd_data_list->list);
+ upd_data_list->update_desc = next_desc;
+ list_del_init(&next_desc->list);
+ if (fb_data->upd_scheme == UPDATE_SCHEME_QUEUE) {
+ GALLEN_DBGLOCAL_RUNLOG(12);
+ /* If not merging, we have an update */
+ break;
+ }
+ } else {
+ GALLEN_DBGLOCAL_RUNLOG(13);
+ switch (epdc_submit_merge(upd_data_list->update_desc,
+ next_desc, fb_data)) {
+ case MERGE_OK:GALLEN_DBGLOCAL_RUNLOG(14);
+ dev_dbg(fb_data->dev,
+ "Update merged [queue]\n");
+ list_del_init(&next_desc->list);
+ kfree(next_desc);
+ break;
+ case MERGE_FAIL:GALLEN_DBGLOCAL_RUNLOG(15);
+ dev_dbg(fb_data->dev,
+ "Update not merged [queue]\n");
+ break;
+ case MERGE_BLOCK:GALLEN_DBGLOCAL_RUNLOG(16);
+ dev_dbg(fb_data->dev,
+ "Merge blocked [collision]\n");
+ end_merge = true;
+ break;
+ }
+
+ if (end_merge) {
+ GALLEN_DBGLOCAL_RUNLOG(17);
+ break;
+ }
+ }
+ }
+ }
+ /* Is update list empty? */
+ if (!upd_data_list) {
+ mutex_unlock(&fb_data->queue_mutex);
+ GALLEN_DBGLOCAL_ESC();
+ return;
+ }
+
+ /* Backup the actual update region */
+ upd_region = &upd_data_list->update_desc->upd_data.update_region;
+ actual_update_region.left = upd_region->left;
+ actual_update_region.top = upd_region->top;
+ actual_update_region.width = upd_region->width;
+ actual_update_region.height = upd_region->height;
+
+ // align update region to 8x8 boundary for PxP
+ {
+ int start, dim;
+
+ start = (upd_region->left/8)*8;
+ dim = ((upd_region->width + 7)/8)*8;
+ if (start + dim <
+ upd_region->left + upd_region->width) {
+ dim += 8;
+ }
+ upd_region->left = start;
+ upd_region->width = dim;
+
+ start = (upd_region->top/8)*8;
+ dim = ((upd_region->height + 7)/8)*8;
+ if (start + dim <
+ upd_region->top + upd_region->height) {
+ dim += 8;
+ }
+ upd_region->top = start;
+ upd_region->height = dim;
+ }
+
+ /*
+ * If no processing required, skip update processing
+ * No processing means:
+ * - FB unrotated
+ * - FB pixel format = 8-bit grayscale
+ * - No look-up transformations (inversion, posterization, etc.)
+ * - No advance algorithms, which requires a look-up transformation
+ * to convert from 4-bit to 5-bit lookup mode
+ *
+ * Note: A bug with EPDC stride prevents us from skipping
+ * PxP in versions 2.0 and earlier of EPDC.
+ */
+ is_transform = (upd_data_list->update_desc->upd_data.flags &
+ (EPDC_FLAG_ENABLE_INVERSION |
+ EPDC_FLAG_USE_DITHERING_Y1 | EPDC_FLAG_USE_DITHERING_Y4 |
+ EPDC_FLAG_FORCE_MONOCHROME | EPDC_FLAG_USE_CMAP)) ||
+ (fb_data->buf_pix_fmt == EPDC_FORMAT_BUF_PIXEL_FORMAT_P5N) ?
+ true : false;
+ DBG_MSG("%s(),is_transform=%d\n",__FUNCTION__,is_transform);
+ if ((fb_data->epdc_fb_var.rotate == FB_ROTATE_UR) &&
+ (fb_data->epdc_fb_var.grayscale == GRAYSCALE_8BIT) &&
+ !is_transform && (fb_data->rev > 20) &&
+ !fb_data->restrict_width) {
+ GALLEN_DBGLOCAL_RUNLOG(18);
+
+ /* If needed, enable EPDC HW while ePxP is processing */
+ if ((fb_data->power_state == POWER_STATE_OFF)
+ || fb_data->powering_down) {
+ GALLEN_DBGLOCAL_RUNLOG(19);
+ epdc_powerup(fb_data);
+ }
+
+ /*
+ * Set update buffer pointer to the start of
+ * the update region in the frame buffer.
+ */
+ upd_data_list->update_desc->epdc_stride =
+ fb_data->info.var.xres_virtual *
+ fb_data->info.var.bits_per_pixel/8;
+ direct_draw = 1;
+ flush_cache_all();
+ outer_flush_all();
+ } else {
+ GALLEN_DBGLOCAL_RUNLOG(20);
+ /* Select from PxP output buffers */
+ upd_data_list->phys_addr =
+ fb_data->phys_addr_updbuf[fb_data->upd_buffer_num];
+ upd_data_list->virt_addr =
+ fb_data->virt_addr_updbuf[fb_data->upd_buffer_num];
+ fb_data->upd_buffer_num++;
+ if (fb_data->upd_buffer_num > fb_data->max_num_buffers-1) {
+ GALLEN_DBGLOCAL_RUNLOG(21);
+ fb_data->upd_buffer_num = 0;
+ }
+
+ /* Release buffer queues */
+ mutex_unlock(&fb_data->queue_mutex);
+
+ /* Perform PXP processing - EPDC power will also be enabled */
+ if (epdc_process_update(upd_data_list, fb_data)) {
+ dev_dbg(fb_data->dev, "PXP processing error.\n");
+ /* Protect access to buffer queues and to update HW */
+ mutex_lock(&fb_data->queue_mutex);
+ list_del_init(&upd_data_list->update_desc->list);
+ kfree(upd_data_list->update_desc);
+ upd_data_list->update_desc = NULL;
+ /* Add to free buffer list */
+ list_add_tail(&upd_data_list->list,
+ &fb_data->upd_buf_free_list);
+ /* Release buffer queues */
+ mutex_unlock(&fb_data->queue_mutex);
+ GALLEN_DBGLOCAL_ESC();
+ return;
+ }
+
+ /* Protect access to buffer queues and to update HW */
+ mutex_lock(&fb_data->queue_mutex);
+
+ }
+
+ /* Get rotation-adjusted coordinates */
+ adjust_coordinates(fb_data->epdc_fb_var.xres,
+ fb_data->epdc_fb_var.yres, fb_data->epdc_fb_var.rotate,
+ &upd_data_list->update_desc->upd_data.update_region,
+ &adj_update_region);
+
+ /*
+ * Is the working buffer idle?
+ * If the working buffer is busy, we must wait for the resource
+ * to become free. The IST will signal this event.
+ */
+ if (fb_data->cur_update != NULL) {
+ GALLEN_DBGLOCAL_RUNLOG(22);
+ dev_dbg(fb_data->dev, "working buf busy!\n");
+
+ /* Initialize event signalling an update resource is free */
+ init_completion(&fb_data->update_res_free);
+
+ fb_data->waiting_for_wb = true;
+
+ /* Leave spinlock while waiting for WB to complete */
+ mutex_unlock(&fb_data->queue_mutex);
+ wait_for_completion(&fb_data->update_res_free);
+ mutex_lock(&fb_data->queue_mutex);
+ }
+
+ /*
+ * Dithering Processing (Version 1.0 - for i.MX508 and i.MX6SL)
+ */
+ if (upd_data_list->update_desc->upd_data.flags &
+ EPDC_FLAG_USE_DITHERING_Y1) {
+
+ GALLEN_DBGLOCAL_RUNLOG(23);
+
+ err_dist = kzalloc((fb_data->info.var.xres_virtual + 3) * 3
+ * sizeof(int), GFP_KERNEL);
+
+ mutex_unlock(&fb_data->queue_mutex);
+ mutex_lock(&fb_data->pxp_mutex);
+
+ /* Dithering Y8 -> Y1 */
+ do_dithering_processing_Y1_v1_0(
+ (uint8_t *)(upd_data_list->virt_addr +
+ upd_data_list->update_desc->epdc_offs),
+ &adj_update_region,
+ (fb_data->rev < 20) ?
+ ALIGN(adj_update_region.width, 8) :
+ adj_update_region.width,
+ err_dist);
+
+ mutex_unlock(&fb_data->pxp_mutex);
+ mutex_lock(&fb_data->queue_mutex);
+
+ kfree(err_dist);
+ } else if (upd_data_list->update_desc->upd_data.flags &
+ EPDC_FLAG_USE_DITHERING_Y4) {
+
+ GALLEN_DBGLOCAL_RUNLOG(24);
+
+ err_dist = kzalloc((fb_data->info.var.xres_virtual + 3) * 3
+ * sizeof(int), GFP_KERNEL);
+
+ mutex_unlock(&fb_data->queue_mutex);
+ mutex_lock(&fb_data->pxp_mutex);
+
+ /* Dithering Y8 -> Y4 */
+ do_dithering_processing_Y4_v1_0(
+ (uint8_t *)(upd_data_list->virt_addr +
+ upd_data_list->update_desc->epdc_offs),
+ &adj_update_region,
+ (fb_data->rev < 20) ?
+ ALIGN(adj_update_region.width, 8) :
+ adj_update_region.width,
+ err_dist);
+
+ mutex_unlock(&fb_data->pxp_mutex);
+ mutex_lock(&fb_data->queue_mutex);
+
+ kfree(err_dist);
+ }
+
+ /*
+ * advance algorithm flow
+ * Working buffer is now ensured to not be busy,
+ * so we can begin special waveform processing.
+ */
+ DBG_MSG("%s(%d):upd_order=%d,is_aa=%d,wb_pause=%d,flags=0x%x\n",
+ __FUNCTION__,__LINE__,
+ upd_data_list->update_desc->update_order,
+ upd_data_list->update_desc->is_aa,
+ upd_data_list->update_desc->wb_pause,
+ upd_data_list->update_desc->upd_data.flags);
+
+ if (upd_data_list->update_desc->is_aa &&
+ !upd_data_list->update_desc->wb_pause &&
+ !(upd_data_list->update_desc->upd_data.flags
+ & EPDC_FLAG_USE_AAD)) {
+ /* aa algorithm */
+ /* collect the system time data */
+ flush_cache_all();
+ outer_flush_all();
+ aa_time_stamp[0] = get_uSecs();
+
+
+ mutex_unlock(&fb_data->queue_mutex);
+ mutex_lock(&fb_data->pxp_mutex);
+
+ if (do_aa_processing_v2_2_0((uint8_t *)(upd_data_list->virt_addr
+ + upd_data_list->update_desc->epdc_offs),
+ &adj_update_region,
+ ALIGN(adj_update_region.width, 8),
+ (uint16_t *)fb_data->working_buffer_A_virt,
+ fb_data->native_width,
+ fb_data->native_height))
+ dev_err(fb_data->dev," AAD algorithm is not available in this EPDC driver!\n");
+
+ mutex_unlock(&fb_data->pxp_mutex);
+ mutex_lock(&fb_data->queue_mutex);
+
+ /* collect the system time data */
+ aa_time_stamp[1] = get_uSecs();
+
+ DBG_MSG (" --- AAl(v2.2.0) Algo Exec Time = %d\n", getTimeDiff(aa_time_stamp[0], aa_time_stamp[1]));
+ flush_cache_all();
+ outer_flush_all();
+ }
+ else if (upd_data_list->update_desc->is_aa &&
+ !upd_data_list->update_desc->wb_pause &&
+ (upd_data_list->update_desc->upd_data.flags
+ & EPDC_FLAG_USE_AAD)) {
+
+ flush_cache_all();
+ outer_flush_all();
+ /* AAD algorithm */
+ /* collect the system time data */
+ aa_time_stamp[0] = get_uSecs();
+
+ mutex_unlock(&fb_data->queue_mutex);
+ mutex_lock(&fb_data->pxp_mutex);
+
+ if (do_aad_processing_v2_1_0((uint8_t *)(upd_data_list->virt_addr
+ + upd_data_list->update_desc->epdc_offs),
+ &adj_update_region,
+ ALIGN(adj_update_region.width, 8),
+ (uint16_t *)fb_data->working_buffer_A_virt,
+ fb_data->native_width,
+ fb_data->native_height))
+ dev_err(fb_data->dev," AA algorithm is not available in this EPDC driver!\n");
+
+ mutex_unlock(&fb_data->pxp_mutex);
+ mutex_lock(&fb_data->queue_mutex);
+
+ /* collect the system time data */
+ aa_time_stamp[1] = get_uSecs();
+
+ DBG_MSG (" --- AA-D(v2.1.0) Algo Exec Time = %d\n", getTimeDiff(aa_time_stamp[0], aa_time_stamp[1]));
+ flush_cache_all();
+ outer_flush_all();
+ }
+
+ /* Finished all PxP processing, restore the original update region */
+ upd_region = &upd_data_list->update_desc->upd_data.update_region;
+
+ if(direct_draw) {
+ upd_region->left = actual_update_region.left;
+ upd_region->top = actual_update_region.top;
+ upd_region->width = actual_update_region.width;
+ upd_region->height = actual_update_region.height;
+
+ upd_data_list->update_desc->epdc_offs = ((upd_region->top * fb_data->info.var.xres_virtual) +
+ upd_region->left) * fb_data->info.var.bits_per_pixel/8;
+ update_addr = fb_data->info.fix.smem_start + upd_data_list->update_desc->epdc_offs;
+ }
+ else {
+ u32 post_rotation_xcoord, post_rotation_ycoord, width_pxp_blocks;
+
+ width_pxp_blocks = upd_data_list->update_desc->epdc_stride;
+
+ switch (fb_data->epdc_fb_var.rotate) {
+ default:
+ case FB_ROTATE_UR:
+ post_rotation_xcoord = actual_update_region.left - upd_region->left;
+ post_rotation_ycoord = actual_update_region.top - upd_region->top;
+ break;
+ case FB_ROTATE_CW:
+ post_rotation_xcoord = upd_region->height - actual_update_region.height -
+ (actual_update_region.top - upd_region->top);
+ post_rotation_ycoord = (actual_update_region.left - upd_region->left);
+ break;
+ case FB_ROTATE_UD:
+ post_rotation_xcoord = upd_region->width - actual_update_region.width -
+ (actual_update_region.left - upd_region->left);
+ post_rotation_ycoord = upd_region->height - actual_update_region.height -
+ (actual_update_region.top - upd_region->top);
+ break;
+ case FB_ROTATE_CCW:
+ post_rotation_xcoord = (actual_update_region.top - upd_region->top);
+ post_rotation_ycoord = upd_region->width - actual_update_region.width -
+ (actual_update_region.left - upd_region->left);
+ break;
+ }
+
+ if (fb_data->rev < 20) {
+ u32 pxp_input_offs, pxp_output_offs, pxp_output_shift;
+
+ pxp_output_shift = ALIGN(post_rotation_xcoord, 8) - post_rotation_xcoord;
+ pxp_output_offs = (post_rotation_ycoord * upd_data_list->update_desc->epdc_stride) + pxp_output_shift;
+ upd_data_list->update_desc->epdc_offs = ALIGN(pxp_output_offs, 8);
+ } else {
+ upd_data_list->update_desc->epdc_offs =
+ (post_rotation_ycoord * upd_data_list->update_desc->epdc_stride) + post_rotation_xcoord;
+ }
+
+ update_addr = upd_data_list->phys_addr + upd_data_list->update_desc->epdc_offs;
+
+ upd_region->left = actual_update_region.left;
+ upd_region->top = actual_update_region.top;
+ upd_region->width = actual_update_region.width;
+ upd_region->height = actual_update_region.height;
+ }
+
+ adjust_coordinates(fb_data->epdc_fb_var.xres,
+ fb_data->epdc_fb_var.yres, fb_data->epdc_fb_var.rotate,
+ upd_region,
+ &adj_update_region);
+
+ /*
+ * If there are no LUTs available,
+ * then we must wait for the resource to become free.
+ * The IST will signal this event.
+ */
+ if (!epdc_any_luts_available()) {
+ GALLEN_DBGLOCAL_RUNLOG(25);
+
+ dev_dbg(fb_data->dev, "no luts available!\n");
+
+ /* Initialize event signalling an update resource is free */
+ init_completion(&fb_data->update_res_free);
+
+ fb_data->waiting_for_lut = true;
+
+ /* Leave spinlock while waiting for LUT to free up */
+ mutex_unlock(&fb_data->queue_mutex);
+ wait_for_completion(&fb_data->update_res_free);
+ mutex_lock(&fb_data->queue_mutex);
+ }
+
+ ret = epdc_choose_next_lut(fb_data->rev, &upd_data_list->lut_num);
+ /*
+ * If LUT15 is in use (for pre-EPDC v2.0 hardware):
+ * - Wait for LUT15 to complete is if TCE underrun prevent is enabled
+ * - If we go ahead with update, sync update submission with EOF
+ */
+ if (ret && fb_data->tce_prevent && (fb_data->rev < 20)) {
+ GALLEN_DBGLOCAL_RUNLOG(26);
+
+ dev_dbg(fb_data->dev, "Waiting for LUT15\n");
+
+ /* Initialize event signalling that lut15 is free */
+ init_completion(&fb_data->lut15_free);
+
+ fb_data->waiting_for_lut15 = true;
+
+ /* Leave spinlock while waiting for LUT to free up */
+ mutex_unlock(&fb_data->queue_mutex);
+ wait_for_completion(&fb_data->lut15_free);
+ mutex_lock(&fb_data->queue_mutex);
+
+ epdc_choose_next_lut(fb_data->rev, &upd_data_list->lut_num);
+ } else if (ret && (fb_data->rev < 20)) {
+ GALLEN_DBGLOCAL_RUNLOG(27);
+
+ /* Synchronize update submission time to reduce
+ chances of TCE underrun */
+ init_completion(&fb_data->eof_event);
+
+ epdc_eof_intr(true);
+
+ /* Leave spinlock while waiting for EOF event */
+ mutex_unlock(&fb_data->queue_mutex);
+ ret = wait_for_completion_timeout(&fb_data->eof_event,
+ msecs_to_jiffies(1000));
+ if (!ret) {
+ GALLEN_DBGLOCAL_RUNLOG(28);
+ dev_err(fb_data->dev, "Missed EOF event!\n");
+ epdc_eof_intr(false);
+ }
+ udelay(fb_data->eof_sync_period);
+ mutex_lock(&fb_data->queue_mutex);
+
+ }
+
+ /* LUTs are available, so we get one here */
+ fb_data->cur_update = upd_data_list;
+
+ /* Reset mask for LUTS that have completed during WB processing */
+ fb_data->luts_complete_wb = 0;
+
+ /* If we are just testing for collision, we don't assign a LUT,
+ * so we don't need to update LUT-related resources. */
+ if (!(upd_data_list->update_desc->upd_data.flags
+ & EPDC_FLAG_TEST_COLLISION)) {
+ GALLEN_DBGLOCAL_RUNLOG(29);
+ /* Associate LUT with update marker */
+ list_for_each_entry_safe(next_marker, temp_marker,
+ &upd_data_list->update_desc->upd_marker_list, upd_list)
+ next_marker->lut_num = fb_data->cur_update->lut_num;
+
+ /* Mark LUT with order */
+ fb_data->lut_update_order[upd_data_list->lut_num] =
+ upd_data_list->update_desc->update_order;
+
+ epdc_lut_complete_intr(fb_data->rev, upd_data_list->lut_num,
+ true);
+ }
+
+ /*
+ * If this update requires a pause after WB processing,
+ * we will get UPD_DONE interrupt but not WB_CMPLT.
+ * For all other updates, we need an interrupt for WB_CMPLT.
+ */
+ if (upd_data_list->update_desc->wb_pause) {
+ __raw_writel(EPDC_IRQ_UPD_DONE_IRQ, EPDC_IRQ_CLEAR);
+ epdc_upd_done_intr(true);
+ } else
+ epdc_working_buf_intr(true);
+
+ /* Program EPDC update to process buffer */
+ if (upd_data_list->update_desc->upd_data.temp != TEMP_USE_AMBIENT) {
+ GALLEN_DBGLOCAL_RUNLOG(30);
+
+ fb_data->temp_index = mxc_epdc_fb_get_temp_index(fb_data,
+ upd_data_list->update_desc->upd_data.temp);
+ }
+ epdc_set_temp(fb_data->temp_index);
+ epdc_set_update_addr(update_addr);
+ epdc_set_update_coord(adj_update_region.left, adj_update_region.top);
+ epdc_set_update_dimensions(adj_update_region.width,
+ adj_update_region.height);
+ if (fb_data->rev > 20) {
+ GALLEN_DBGLOCAL_RUNLOG(32);
+
+ epdc_set_update_stride(upd_data_list->update_desc->epdc_stride);
+ }
+ if (fb_data->wv_modes_update &&
+ (upd_data_list->update_desc->upd_data.waveform_mode
+ == WAVEFORM_MODE_AUTO)) {
+ GALLEN_DBGLOCAL_RUNLOG(33);
+ epdc_set_update_waveform(&fb_data->wv_modes);
+ fb_data->wv_modes_update = false;
+ }
+
+ if (upd_data_list->update_desc->wb_pause)
+ __raw_writel(fb_data->working_buffer_B_phys, EPDC_WB_ADDR_TCE);
+ else
+ __raw_writel(fb_data->working_buffer_A_phys, EPDC_WB_ADDR_TCE);
+
+ epdc_submit_update(upd_data_list->lut_num,
+ upd_data_list->update_desc->upd_data.waveform_mode,
+ upd_data_list->update_desc->upd_data.update_mode,
+ (upd_data_list->update_desc->upd_data.flags
+ & EPDC_FLAG_TEST_COLLISION) ? true : false,
+ upd_data_list->update_desc->wb_pause, false, 0);
+
+ /* Release buffer queues */
+ mutex_unlock(&fb_data->queue_mutex);
+
+ GALLEN_DBGLOCAL_END();
+}
+
+static int mxc_epdc_fb_send_single_update(struct mxcfb_update_data *upd_data,
+ struct fb_info *info)
+{
+ struct mxc_epdc_fb_data *fb_data = info ?
+ (struct mxc_epdc_fb_data *)info:g_fb_data;
+ struct update_data_list *upd_data_list = NULL;
+ struct mxcfb_rect *screen_upd_region; /* Region on screen to update */
+ int ret;
+ struct update_desc_list *upd_desc;
+ struct update_marker_data *marker_data, *next_marker, *temp_marker;
+ int algorithmVersion = 0; /* assume no advance algorithm */
+ int iDither_state;
+
+ GALLEN_DBGLOCAL_MUTEBEGIN();
+ GALLEN_DBGLOCAL_DBGLVL_SET(giDbgLvl);
+
+ DBG_MSG("%s():upd update mode=%s,wf mode=%d,left=%d,top=%d,w=%d,h=%d,flag=0x%x,marker=%d\n",
+ __FUNCTION__,
+ upd_data->update_mode==UPDATE_MODE_PARTIAL?"partial":"full",
+ (int)upd_data->waveform_mode,(int)upd_data->update_region.left,\
+ (int)upd_data->update_region.top,(int)upd_data->update_region.width,\
+ (int)upd_data->update_region.height,upd_data->flags,upd_data->update_marker);
+
+ iDither_state = dither_enable_state();
+ if( upd_data->flags&EPDC_FLAG_USE_DITHERING_NTX_D8||\
+ 1==iDither_state||2==iDither_state||3==iDither_state)
+ {
+ unsigned long dwFlags,dwOldFlags;
+
+ epdfbdc_get_flags(gptDC,&dwOldFlags);
+ dwFlags = dwOldFlags | EPDFB_DC_FLAG_DITHER8;
+ epdfbdc_set_flags(gptDC,&dwFlags);
+
+ if (EPDFB_DC_SUCCESS!=epdfbdc_put_dcimg(gptDC,gptDC,EPDFB_R_0,
+ (unsigned long)upd_data->update_region.left,
+ (unsigned long)upd_data->update_region.top,
+ (unsigned long)upd_data->update_region.width,
+ (unsigned long)upd_data->update_region.height,
+ (unsigned long)upd_data->update_region.left,
+ (unsigned long)upd_data->update_region.top))
+ {
+ printk(KERN_ERR"%s() : dithering process failed !!\n",__FUNCTION__);
+ }
+ epdfbdc_set_flags(gptDC,&dwOldFlags);
+ //upd_data->update_mode = UPDATE_MODE_PARTIAL;
+ }
+ else if(4==iDither_state||5==iDither_state) {
+ upd_data->flags |= EPDC_FLAG_USE_DITHERING_Y1;
+ //upd_data->update_mode = UPDATE_MODE_PARTIAL;
+ }
+ else if(6==iDither_state||7==iDither_state) {
+ upd_data->flags |= EPDC_FLAG_USE_DITHERING_Y4;
+ //upd_data->update_mode = UPDATE_MODE_PARTIAL;
+ }
+
+#ifdef OUTPUT_SNAPSHOT_IMGFILE //[
+ if(2==giFB_snapshot_enable) {
+ int iChk;
+ // capture partial update retangle of framebuffer .
+ //
+
+ iChk = fb_capture_ex(gptDC,\
+ (int)upd_data->update_region.left,\
+ (int)upd_data->update_region.top,\
+ (int)upd_data->update_region.width,\
+ (int)upd_data->update_region.height,\
+ 8,EPDFB_R_0,gcFB_snapshot_pathA,\
+ (unsigned long) giFB_snapshot_total);
+
+ if( 1==giFB_snapshot_repeat && 1==iChk) {
+ giFB_snapshot_enable = 0;
+ }
+ }
+ else if(1==giFB_snapshot_enable){
+ int iChk;
+
+ iChk = fb_capture_ex(gptDC,0,0,gptDC->dwWidth,gptDC->dwHeight,
+ 8,EPDFB_R_0,gcFB_snapshot_pathA,\
+ (unsigned long) giFB_snapshot_total);
+
+ if( 1==giFB_snapshot_repeat && 1==iChk) {
+ giFB_snapshot_enable = 0;
+ }
+ }
+#endif //]OUTPUT_SNAPSHOT_IMGFILE
+ /* Has EPDC HW been initialized? */
+ if (!fb_data->hw_ready) {
+ /* Throw message if we are not mid-initialization */
+ if (!fb_data->hw_initializing)
+ dev_err(fb_data->dev, "Display HW not properly"
+ "initialized. Aborting update.\n");
+
+ GALLEN_DBGLOCAL_ESC();
+ return -EPERM;
+ }
+
+ /* Check validity of update params */
+ if ((upd_data->update_mode != UPDATE_MODE_PARTIAL) &&
+ (upd_data->update_mode != UPDATE_MODE_FULL)) {
+ dev_err(fb_data->dev,
+ "Update mode 0x%x is invalid. Aborting update.\n",
+ upd_data->update_mode);
+
+ GALLEN_DBGLOCAL_ESC();
+ return -EINVAL;
+ }
+
+#if (NTX_WFM_MODE_TOTAL>0) //[
+ if ( (upd_data->waveform_mode < 0) ||
+ ((upd_data->waveform_mode >= NTX_WFM_MODE_TOTAL) &&
+ (upd_data->waveform_mode != WAVEFORM_MODE_AUTO)) )
+#else
+ if ((upd_data->waveform_mode > 255) &&
+ (upd_data->waveform_mode != WAVEFORM_MODE_AUTO))
+#endif
+ {
+ dev_err(fb_data->dev,
+ "Update waveform mode 0x%x is invalid."
+ " Aborting update.\n",
+ upd_data->waveform_mode);
+
+ GALLEN_DBGLOCAL_ESC();
+ return -EINVAL;
+ }
+ if ((upd_data->update_region.left + upd_data->update_region.width > fb_data->epdc_fb_var.xres) ||
+ (upd_data->update_region.top + upd_data->update_region.height > fb_data->epdc_fb_var.yres)) {
+ dev_err(fb_data->dev,
+ "Update region is outside bounds of framebuffer."
+ "Aborting update.\n");
+
+ GALLEN_DBGLOCAL_ESC();
+ return -EINVAL;
+ }
+ if (upd_data->flags & EPDC_FLAG_USE_ALT_BUFFER) {
+ if ((upd_data->update_region.width !=
+ upd_data->alt_buffer_data.alt_update_region.width) ||
+ (upd_data->update_region.height !=
+ upd_data->alt_buffer_data.alt_update_region.height)) {
+ dev_err(fb_data->dev,
+ "Alternate update region dimensions must "
+ "match screen update region dimensions.\n");
+
+ GALLEN_DBGLOCAL_ESC();
+ return -EINVAL;
+ }
+ /* Validate physical address parameter */
+ if ((upd_data->alt_buffer_data.phys_addr <
+ fb_data->info.fix.smem_start) ||
+ (upd_data->alt_buffer_data.phys_addr >
+ fb_data->info.fix.smem_start + fb_data->map_size)) {
+ dev_err(fb_data->dev,
+ "Invalid physical address for alternate "
+ "buffer. Aborting update...\n");
+
+ GALLEN_DBGLOCAL_ESC();
+ return -EINVAL;
+ }
+ }
+
+ //k_set_temperature(info);
+ mutex_lock(&fb_data->queue_mutex);
+
+ /*
+ * If we are waiting to go into suspend, or the FB is blanked,
+ * we do not accept new updates
+ */
+ if ((fb_data->waiting_for_idle) ||
+ (fb_data->blank != FB_BLANK_UNBLANK)) {
+ dev_dbg(fb_data->dev, "EPDC not active."
+ "Update request abort.\n");
+ mutex_unlock(&fb_data->queue_mutex);
+
+ GALLEN_DBGLOCAL_ESC();
+ return -EPERM;
+ }
+
+ if (fb_data->upd_scheme == UPDATE_SCHEME_SNAPSHOT) {
+ int count = 0;
+ struct update_data_list *plist;
+
+ GALLEN_DBGLOCAL_RUNLOG(0);
+ /*
+ * If next update is a FULL mode update, then we must
+ * ensure that all pending & active updates are complete
+ * before submitting the update. Otherwise, the FULL
+ * mode update may cause an endless collision loop with
+ * other updates. Block here until updates are flushed.
+ */
+ if (upd_data->update_mode == UPDATE_MODE_FULL) {
+ GALLEN_DBGLOCAL_RUNLOG(1);
+ mutex_unlock(&fb_data->queue_mutex);
+ mxc_epdc_fb_flush_updates(fb_data);
+ mutex_lock(&fb_data->queue_mutex);
+ }
+
+
+ /* Count buffers in free buffer list */
+ list_for_each_entry(plist, &fb_data->upd_buf_free_list, list)
+ count++;
+
+ /* Use count to determine if we have enough
+ * free buffers to handle this update request */
+ if (count + fb_data->max_num_buffers
+ <= fb_data->max_num_updates) {
+ dev_err(fb_data->dev,
+ "No free intermediate buffers available.\n");
+ mutex_unlock(&fb_data->queue_mutex);
+
+ GALLEN_DBGLOCAL_ESC();
+ return -ENOMEM;
+ }
+
+ /* Grab first available buffer and delete from the free list */
+ upd_data_list =
+ list_entry(fb_data->upd_buf_free_list.next,
+ struct update_data_list, list);
+
+ list_del_init(&upd_data_list->list);
+ }
+
+ /*
+ * Create new update data structure, fill it with new update
+ * data and add it to the list of pending updates
+ */
+ upd_desc = kzalloc(sizeof(struct update_desc_list), GFP_KERNEL);
+ if (!upd_desc) {
+ dev_err(fb_data->dev,
+ "Insufficient system memory for update! Aborting.\n");
+ if (fb_data->upd_scheme == UPDATE_SCHEME_SNAPSHOT) {
+
+ GALLEN_DBGLOCAL_RUNLOG(2);
+ list_add(&upd_data_list->list,
+ &fb_data->upd_buf_free_list);
+ }
+ mutex_unlock(&fb_data->queue_mutex);
+
+ GALLEN_DBGLOCAL_ESC();
+ return -EPERM;
+ }
+
+
+ /* Initialize per-update marker list */
+ INIT_LIST_HEAD(&upd_desc->upd_marker_list);
+ upd_desc->upd_data = *upd_data;
+ upd_desc->update_order = fb_data->order_cnt++;
+
+
+ upd_desc->iDither_state = iDither_state;
+#if 1 //[ auto select mode
+ {
+
+
+ if( 2==iDither_state || 4==iDither_state || 6==iDither_state ) {
+ // force dither and A2 mode .
+ upd_desc->upd_data.waveform_mode=NTX_WFM_MODE_A2;
+ DBG_MSG("%s():force ntx wf mode %d for dither state %d\n",__FUNCTION__,upd_desc->upd_data.waveform_mode,iDither_state);
+ }
+ else
+ if( 3==iDither_state || 5==iDither_state || 7==iDither_state) {
+ // force dither and A2 mode .
+ upd_desc->upd_data.waveform_mode=NTX_WFM_MODE_DU;
+ DBG_MSG("%s():force ntx wf mode %d for dither state %d\n",__FUNCTION__,upd_desc->upd_data.waveform_mode,iDither_state);
+ }
+
+ if(WAVEFORM_MODE_AUTO!=upd_desc->upd_data.waveform_mode) {
+ GALLEN_DBGLOCAL_RUNLOG(12);
+
+#ifdef NTX_WFM_MODE_OPTIMIZED //[
+ //if(0==gptHWCFG->m_val.bUIStyle)
+ {
+
+ if(NTX_WFM_MODE_GC16==upd_desc->upd_data.waveform_mode) {
+ GALLEN_DBGLOCAL_RUNLOG(13);
+ if(upd_desc->upd_data.update_mode == UPDATE_MODE_FULL) {
+ GALLEN_DBGLOCAL_RUNLOG(14);
+ #ifdef NTX_WFM_MODE_OPTIMIZED_REAGL//[
+ if(giNTX_waveform_modeA[NTX_WFM_MODE_GLD16]!=giNTX_waveform_modeA[NTX_WFM_MODE_GC16]) {
+ GALLEN_DBGLOCAL_RUNLOG(15);
+ // has GLD16 mode .
+ DBG_MSG("WF Mode version=0x%02x,chg W.F Mode GC16(%d)->GLD16(%d) @ full\n",
+ gbModeVersion,NTX_WFM_MODE_GC16,NTX_WFM_MODE_GLD16);
+ upd_desc->upd_data.waveform_mode = NTX_WFM_MODE_GLD16;
+ }
+ #endif //]NTX_WFM_MODE_OPTIMIZED_REAGL
+ }
+ else if(upd_desc->upd_data.update_mode == UPDATE_MODE_PARTIAL){
+ GALLEN_DBGLOCAL_RUNLOG(16);
+ #ifdef NTX_WFM_MODE_OPTIMIZED_REAGL//[
+ if(giNTX_waveform_modeA[NTX_WFM_MODE_GLR16]!=giNTX_waveform_modeA[NTX_WFM_MODE_GC16]) {
+ GALLEN_DBGLOCAL_RUNLOG(17);
+ DBG_MSG("WF Mode version=0x%02x,chg W.F Mode GC16(%d)->GLR16(%d) @ partial\n",
+ gbModeVersion,NTX_WFM_MODE_GC16,NTX_WFM_MODE_GLR16);
+ upd_desc->upd_data.waveform_mode = NTX_WFM_MODE_GLR16;
+ }
+ else
+ #endif //]NTX_WFM_MODE_OPTIMIZED_REAGL
+ if (giNTX_waveform_modeA[NTX_WFM_MODE_GL16]!=giNTX_waveform_modeA[NTX_WFM_MODE_GC16]) {
+ GALLEN_DBGLOCAL_RUNLOG(18);
+ DBG_MSG("chg W.F Mode GC16(%d)->GL16(%d) @ partial\n",
+ NTX_WFM_MODE_GC16,NTX_WFM_MODE_GL16);
+ upd_desc->upd_data.waveform_mode = NTX_WFM_MODE_GL16;
+ }
+ }
+ }
+
+ }
+#endif //] NTX_WFM_MODE_OPTIMIZED
+
+ if( NTX_WFM_MODE_GLD16==upd_desc->upd_data.waveform_mode &&
+ giNTX_waveform_modeA[NTX_WFM_MODE_GLD16]!=giNTX_waveform_modeA[NTX_WFM_MODE_GC16])
+ {
+ GALLEN_DBGLOCAL_RUNLOG(19);
+ upd_desc->upd_data.flags |= EPDC_FLAG_USE_AAD;
+ }
+ /*
+ else {
+ upd_desc->upd_data.flags &= ~EPDC_FLAG_USE_AAD;
+ }
+ */
+
+ DBG_MSG("ntx wfm mode %d->eink wfm mode %d x\n",
+ upd_desc->upd_data.waveform_mode,giNTX_waveform_modeA[upd_desc->upd_data.waveform_mode]);
+ upd_desc->upd_data.waveform_mode = giNTX_waveform_modeA[upd_desc->upd_data.waveform_mode];
+
+ }
+ else
+ {
+ /*
+ if(2==gptHWCFG->m_val.bUIStyle) {
+ // android .
+ if(giNTX_waveform_modeA[NTX_WFM_MODE_A2]!=giNTX_waveform_modeA[NTX_WFM_MODE_DU]) {
+ extern int g_touch_pressed_cnt;
+ // waveform has A2 !!
+ if( g_touch_pressed_cnt>5 &&
+ upd_desc->upd_data.update_mode == UPDATE_MODE_PARTIAL)
+ {
+ DBG_MSG("WF Mode version=0x%02x,chg W.F Mode %d->A2(%d) @ partial\n",
+ bModeVersion,upd_desc->upd_data.waveform_mode,NTX_WFM_MODE_A2);
+ upd_desc->upd_data.waveform_mode = NTX_WFM_MODE_A2;
+ }
+
+ }
+
+ }
+ */
+ }
+ }
+#endif //]
+
+
+
+#if 1//[
+ /* Conditions for using advance algorithms :
+ * 1) AA waveform mode is specified and supported (note: advance algorithms may not available for all waveform modes and/or temperature ranges)
+ * 2) Waveform format is 5-bit
+ * 3) Scheme is Queue or Queue & Merge
+ */
+ if ((fb_data->waveform_acd_buffer) && (upd_desc->upd_data.waveform_mode < fb_data->waveform_mc) && (upd_desc->upd_data.waveform_mode > 0)) {
+ GALLEN_DBGLOCAL_RUNLOG(20);
+
+ /* prepare the advance algorithm data for this waveform mode and temperature range */
+ algorithmVersion = mxc_epdc_fb_prep_algorithm_data( fb_data->waveform_acd_buffer,
+ upd_desc->upd_data.waveform_mode /*upd_data_list->update_desc->upd_data.waveform_mode*/,
+ fb_data->temp_index,
+ fb_data->waveform_mc,
+ fb_data->waveform_trc,
+ fb_data->waveform_magic_number,
+ 0x3);
+ if (algorithmVersion < 0)
+ dev_info (fb_data->dev, " --- Advance Algorithm Data error: %d ---\n",algorithmVersion);
+ }
+#endif //]
+
+ /* algorithmVersion = 0 means no advance algorithm for this waveform and/or temperature range
+ * algorithmVersion = 1 means there is aa algorithm associated with this waveform and temperture range
+ * algorithmVersion = 2 means there area aa and aad algorithms associated with this waveform and temperature range
+ */
+ if (algorithmVersion > 0) {
+ GALLEN_DBGLOCAL_RUNLOG(21);
+ upd_desc->is_aa =
+ ((algorithmVersion > 0) && (algorithmVersion <= 2) &&
+ (fb_data->buf_pix_fmt == EPDC_FORMAT_BUF_PIXEL_FORMAT_P5N) &&
+ (fb_data->upd_scheme != UPDATE_SCHEME_SNAPSHOT)) ? 1 : 0;
+ }
+ else {
+ GALLEN_DBGLOCAL_RUNLOG(22);
+#if 1 // 20131022 gallen disabled aa selected by user .
+ upd_desc->is_aa = 0;
+#else
+ upd_desc->is_aa =
+ ((upd_desc->upd_data.waveform_mode == fb_data->wv_modes.mode_aa ||
+ upd_desc->upd_data.waveform_mode == fb_data->wv_modes.mode_aad) &&
+ (fb_data->buf_pix_fmt == EPDC_FORMAT_BUF_PIXEL_FORMAT_P5N) &&
+ (fb_data->upd_scheme != UPDATE_SCHEME_SNAPSHOT)) ? 1 : 0;
+ /* Warn against AA updates w/o AA waveform */
+ if ((upd_desc->upd_data.waveform_mode == fb_data->wv_modes.mode_aa ||
+ upd_desc->upd_data.waveform_mode == fb_data->wv_modes.mode_aad) &&
+ (fb_data->buf_pix_fmt != EPDC_FORMAT_BUF_PIXEL_FORMAT_P5N))
+ dev_err(fb_data->dev, "AA waveform update specified"
+ "without valid AA waveform file.\n");
+#endif
+ }
+
+ DBG_MSG("%s():upd_order=%d,AlgorithmVerion=%d,is_aa=%d\n",
+ __FUNCTION__,(int)upd_desc->update_order,
+ algorithmVersion,upd_desc->is_aa);
+
+#if 1
+ upd_desc->wb_pause = 0;
+#else
+ upd_desc->wb_pause = upd_desc->is_aa && (fb_data->rev > 20) ? 1 : 0;
+#endif
+
+ list_add_tail(&upd_desc->list, &fb_data->upd_pending_list);
+
+
+
+
+ /* If marker specified, associate it with a completion */
+ if (upd_data->update_marker != 0) {
+ GALLEN_DBGLOCAL_RUNLOG(3);
+ /* Allocate new update marker and set it up */
+ marker_data = kzalloc(sizeof(struct update_marker_data),
+ GFP_KERNEL);
+ if (!marker_data) {
+ dev_err(fb_data->dev, "No memory for marker!\n");
+ mutex_unlock(&fb_data->queue_mutex);
+
+ GALLEN_DBGLOCAL_ESC();
+ return -ENOMEM;
+ }
+ list_add_tail(&marker_data->upd_list,
+ &upd_desc->upd_marker_list);
+ marker_data->update_marker = upd_data->update_marker;
+ if (upd_desc->upd_data.flags & EPDC_FLAG_TEST_COLLISION) {
+ GALLEN_DBGLOCAL_RUNLOG(4);
+ marker_data->lut_num = DRY_RUN_NO_LUT;
+ }
+ else {
+ GALLEN_DBGLOCAL_RUNLOG(5);
+ marker_data->lut_num = INVALID_LUT;
+ }
+
+ init_completion(&marker_data->update_completion);
+ /* Add marker to master marker list */
+ list_add_tail(&marker_data->full_list,
+ &fb_data->full_marker_list);
+ }
+
+ if (fb_data->upd_scheme != UPDATE_SCHEME_SNAPSHOT) {
+ /* Queued update scheme processing */
+
+ mutex_unlock(&fb_data->queue_mutex);
+
+ /* Signal workqueue to handle new update */
+ queue_work(fb_data->epdc_submit_workqueue,
+ &fb_data->epdc_submit_work);
+
+ GALLEN_DBGLOCAL_ESC();
+ return 0;
+ }
+
+ /* Snapshot update scheme processing */
+
+ /* Set descriptor for current update, delete from pending list */
+ upd_data_list->update_desc = upd_desc;
+ list_del_init(&upd_desc->list);
+
+ mutex_unlock(&fb_data->queue_mutex);
+
+ /*
+ * Hold on to original screen update region, which we
+ * will ultimately use when telling EPDC where to update on panel
+ */
+ screen_upd_region = &upd_desc->upd_data.update_region;
+
+ /* Select from PxP output buffers */
+ upd_data_list->phys_addr =
+ fb_data->phys_addr_updbuf[fb_data->upd_buffer_num];
+ upd_data_list->virt_addr =
+ fb_data->virt_addr_updbuf[fb_data->upd_buffer_num];
+ fb_data->upd_buffer_num++;
+ if (fb_data->upd_buffer_num > fb_data->max_num_buffers-1) {
+ GALLEN_DBGLOCAL_RUNLOG(6);
+ fb_data->upd_buffer_num = 0;
+ }
+
+ ret = epdc_process_update(upd_data_list, fb_data);
+ if (ret) {
+ mutex_unlock(&fb_data->pxp_mutex);
+
+ GALLEN_DBGLOCAL_ESC();
+ return ret;
+ }
+
+ /* Pass selected waveform mode back to user */
+ upd_data->waveform_mode = upd_desc->upd_data.waveform_mode;
+
+ /* Get rotation-adjusted coordinates */
+ adjust_coordinates(fb_data->epdc_fb_var.xres,
+ fb_data->epdc_fb_var.yres, fb_data->epdc_fb_var.rotate,
+ &upd_desc->upd_data.update_region, NULL);
+
+ /* Grab lock for queue manipulation and update submission */
+ mutex_lock(&fb_data->queue_mutex);
+
+ /*
+ * Is the working buffer idle?
+ * If either the working buffer is busy, or there are no LUTs available,
+ * then we return and let the ISR handle the update later
+ */
+ if ((fb_data->cur_update != NULL) || !epdc_any_luts_available()) {
+ /* Add processed Y buffer to update list */
+ list_add_tail(&upd_data_list->list, &fb_data->upd_buf_queue);
+
+ /* Return and allow the update to be submitted by the ISR. */
+ mutex_unlock(&fb_data->queue_mutex);
+
+ GALLEN_DBGLOCAL_ESC();
+ return 0;
+ }
+
+ /* LUTs are available, so we get one here */
+ ret = epdc_choose_next_lut(fb_data->rev, &upd_data_list->lut_num);
+ if (ret && fb_data->tce_prevent && (fb_data->rev < 20)) {
+ dev_dbg(fb_data->dev, "Must wait for LUT15\n");
+ /* Add processed Y buffer to update list */
+ list_add_tail(&upd_data_list->list, &fb_data->upd_buf_queue);
+
+ /* Return and allow the update to be submitted by the ISR. */
+ mutex_unlock(&fb_data->queue_mutex);
+
+ GALLEN_DBGLOCAL_ESC();
+ return 0;
+ }
+
+ if (!(upd_data_list->update_desc->upd_data.flags
+ & EPDC_FLAG_TEST_COLLISION)) {
+
+ GALLEN_DBGLOCAL_RUNLOG(7);
+
+ /* Save current update */
+ fb_data->cur_update = upd_data_list;
+
+ /* Reset mask for LUTS that have completed during WB processing */
+ fb_data->luts_complete_wb = 0;
+
+ /* Associate LUT with update marker */
+ list_for_each_entry_safe(next_marker, temp_marker,
+ &upd_data_list->update_desc->upd_marker_list, upd_list)
+ next_marker->lut_num = upd_data_list->lut_num;
+
+ /* Mark LUT as containing new update */
+ fb_data->lut_update_order[upd_data_list->lut_num] =
+ upd_desc->update_order;
+
+ epdc_lut_complete_intr(fb_data->rev, upd_data_list->lut_num,
+ true);
+ }
+
+ /* Clear status and Enable LUT complete and WB complete IRQs */
+ epdc_working_buf_intr(true);
+
+ /* Program EPDC update to process buffer */
+ epdc_set_update_addr(upd_data_list->phys_addr + upd_desc->epdc_offs);
+ epdc_set_update_coord(screen_upd_region->left, screen_upd_region->top);
+ epdc_set_update_dimensions(screen_upd_region->width,
+ screen_upd_region->height);
+ if (fb_data->rev > 20) {
+ GALLEN_DBGLOCAL_RUNLOG(8);
+ epdc_set_update_stride(upd_desc->epdc_stride);
+ }
+ if (upd_desc->upd_data.temp != TEMP_USE_AMBIENT) {
+ GALLEN_DBGLOCAL_RUNLOG(9);
+ fb_data->temp_index = mxc_epdc_fb_get_temp_index(fb_data,
+ upd_desc->upd_data.temp);
+ }
+ epdc_set_temp(fb_data->temp_index);
+ if (fb_data->wv_modes_update &&
+ (upd_desc->upd_data.waveform_mode == WAVEFORM_MODE_AUTO)) {
+ GALLEN_DBGLOCAL_RUNLOG(11);
+ epdc_set_update_waveform(&fb_data->wv_modes);
+ fb_data->wv_modes_update = false;
+ }
+
+ epdc_submit_update(upd_data_list->lut_num,
+ upd_desc->upd_data.waveform_mode,
+ upd_desc->upd_data.update_mode,
+ (upd_desc->upd_data.flags
+ & EPDC_FLAG_TEST_COLLISION) ? true : false,
+ upd_data_list->update_desc->wb_pause, false, 0);
+
+ mutex_unlock(&fb_data->queue_mutex);
+ GALLEN_DBGLOCAL_END();
+ return 0;
+}
+
+int mxc_epdc_fb_send_update(struct mxcfb_update_data *upd_data,
+ struct fb_info *info)
+{
+ struct mxc_epdc_fb_data *fb_data = info ?
+ (struct mxc_epdc_fb_data *)info:g_fb_data;
+
+ GALLEN_DBGLOCAL_BEGIN();
+
+ //Yian: patch from freescale HK. Peter, fixed the line noise on EPD
+ flush_cache_all();
+ outer_flush_all();
+
+ if (!fb_data->restrict_width) {
+ GALLEN_DBGLOCAL_ESC();
+ /* No width restriction, send entire update region */
+ return mxc_epdc_fb_send_single_update(upd_data, info);
+ } else {
+ int ret;
+ __u32 width, left;
+ __u32 marker;
+ __u32 *region_width, *region_left;
+ u32 max_upd_width = EPDC_V2_MAX_UPDATE_WIDTH;
+
+ GALLEN_DBGLOCAL_RUNLOG(0);
+
+ /* Further restrict max width due to pxp rotation
+ * alignment requirement
+ */
+ if (fb_data->epdc_fb_var.rotate != FB_ROTATE_UR) {
+ GALLEN_DBGLOCAL_RUNLOG(1);
+ max_upd_width -= EPDC_V2_ROTATION_ALIGNMENT;
+ }
+
+ /* Select split of width or height based on rotation */
+ if ((fb_data->epdc_fb_var.rotate == FB_ROTATE_UR) ||
+ (fb_data->epdc_fb_var.rotate == FB_ROTATE_UD)) {
+ GALLEN_DBGLOCAL_RUNLOG(2);
+ region_width = &upd_data->update_region.width;
+ region_left = &upd_data->update_region.left;
+ } else {
+ GALLEN_DBGLOCAL_RUNLOG(3);
+ region_width = &upd_data->update_region.height;
+ region_left = &upd_data->update_region.top;
+ }
+
+ if (*region_width <= max_upd_width) {
+ GALLEN_DBGLOCAL_ESC();
+ return mxc_epdc_fb_send_single_update(upd_data, info);
+ }
+
+ width = *region_width;
+ left = *region_left;
+ marker = upd_data->update_marker;
+ upd_data->update_marker = 0;
+
+ do {
+ GALLEN_DBGLOCAL_RUNLOG(4);
+ *region_width = max_upd_width;
+ *region_left = left;
+ ret = mxc_epdc_fb_send_single_update(upd_data, info);
+ if (ret) {
+ GALLEN_DBGLOCAL_ESC();
+ return ret;
+ }
+ width -= max_upd_width;
+ left += max_upd_width;
+ } while (width > max_upd_width);
+
+ *region_width = width;
+ *region_left = left;
+ upd_data->update_marker = marker;
+ GALLEN_DBGLOCAL_ESC();
+ return mxc_epdc_fb_send_single_update(upd_data, info);
+ }
+
+ GALLEN_DBGLOCAL_END();
+}
+EXPORT_SYMBOL(mxc_epdc_fb_send_update);
+
+int mxc_epdc_fb_wait_update_complete(struct mxcfb_update_marker_data *marker_data,
+ struct fb_info *info)
+{
+ struct mxc_epdc_fb_data *fb_data = info ?
+ (struct mxc_epdc_fb_data *)info:g_fb_data;
+ struct update_marker_data *next_marker;
+ struct update_marker_data *temp;
+ bool marker_found = false;
+ int ret = 0;
+
+
+ GALLEN_DBGLOCAL_MUTEBEGIN();
+ GALLEN_DBGLOCAL_DBGLVL_SET(giDbgLvl);
+
+ DBG_MSG("%s() : fb_info@%p,marker_data@%p,update_marker=%d\n",
+ __FUNCTION__,info,marker_data,marker_data->update_marker);
+
+ /* 0 is an invalid update_marker value */
+ if (marker_data->update_marker == 0) {
+ GALLEN_DBGLOCAL_ESC();
+ return -EINVAL;
+ }
+
+ /*
+ * Find completion associated with update_marker requested.
+ * Note: If update completed already, marker will have been
+ * cleared, it won't be found, and function will just return.
+ */
+
+ /* Grab queue lock to protect access to marker list */
+ mutex_lock(&fb_data->queue_mutex);
+
+ list_for_each_entry_safe(next_marker, temp,
+ &fb_data->full_marker_list, full_list) {
+ GALLEN_DBGLOCAL_RUNLOG(0);
+ if (next_marker->update_marker == marker_data->update_marker) {
+ GALLEN_DBGLOCAL_RUNLOG(1);
+ dev_dbg(fb_data->dev, "Waiting for marker %d\n",
+ marker_data->update_marker);
+ next_marker->waiting = true;
+ marker_found = true;
+ break;
+ }
+ }
+
+ mutex_unlock(&fb_data->queue_mutex);
+
+ /*
+ * If marker not found, it has either been signalled already
+ * or the update request failed. In either case, just return.
+ */
+ if (!marker_found) {
+ GALLEN_DBGLOCAL_ESC();
+ return ret;
+ }
+
+ ret = wait_for_completion_timeout(&next_marker->update_completion,
+ msecs_to_jiffies(5000));
+ if (!ret) {
+ dev_err(fb_data->dev,
+ "Timed out waiting for update marker%d completion\n",marker_data->update_marker);
+ GALLEN_DBGLOCAL_ESC();
+ return -ETIMEDOUT;
+ }
+
+ marker_data->collision_test = next_marker->collision_test;
+
+ /* Free update marker object */
+ kfree(next_marker);
+
+ flush_cache_all();
+ outer_flush_all();
+ GALLEN_DBGLOCAL_END();
+ return ret;
+}
+EXPORT_SYMBOL(mxc_epdc_fb_wait_update_complete);
+
+int mxc_epdc_fb_set_pwrdown_delay(u32 pwrdown_delay,
+ struct fb_info *info)
+{
+ struct mxc_epdc_fb_data *fb_data = info ?
+ (struct mxc_epdc_fb_data *)info:g_fb_data;
+
+ fb_data->pwrdown_delay = pwrdown_delay;
+ return 0;
+}
+EXPORT_SYMBOL(mxc_epdc_fb_set_pwrdown_delay);
+
+int mxc_epdc_get_pwrdown_delay(struct fb_info *info)
+{
+ struct mxc_epdc_fb_data *fb_data = info ?
+ (struct mxc_epdc_fb_data *)info:g_fb_data;
+
+ return fb_data->pwrdown_delay;
+}
+EXPORT_SYMBOL(mxc_epdc_get_pwrdown_delay);
+
+
+static int mxc_epdc_fb_open (struct fb_info *info, int user)
+{
+ GALLEN_DBGLOCAL_BEGIN();
+ fake_s1d13522_progress_stop();
+ k_fake_s1d13522_wait_inited();
+ GALLEN_DBGLOCAL_END();
+ return 0;
+}
+
+static int mxc_epdc_fb_ioctl(struct fb_info *info, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int ret = -EINVAL;
+
+ GALLEN_DBGLOCAL_BEGIN();
+
+#ifdef CONFIG_ANDROID//[
+#else//][!CONFIG_ANDROID
+ fake_s1d13522_progress_stop();
+ k_fake_s1d13522_wait_inited();
+#endif //]CONFIG_ANDROID
+
+#if 0 //[
+ // the command code will be 0x400446XX .
+ DBG0_MSG("MXCFB_WAIT_FOR_UPDATE_COMPLETE=0x%08x\n",MXCFB_WAIT_FOR_UPDATE_COMPLETE);
+ //DBG0_MSG("MXCFB_WAIT_FOR_UPDATE_COMPLETE2=0x%08x\n",MXCFB_WAIT_FOR_UPDATE_COMPLETE2);
+ DBG0_MSG("MXCFB_SEND_UPDATE=0x%08x\n",MXCFB_SEND_UPDATE);
+ DBG0_MSG("cmd=0x%08x\n",cmd);
+#endif //]
+
+
+ //printk("skip %s()\n",__FUNCTION__);return 0;
+
+
+ switch (cmd) {
+ case MXCFB_SET_WAVEFORM_MODES:GALLEN_DBGLOCAL_RUNLOG(0);
+ //printk("\n==>skip MXCFB_SET_WAVEFORM_MODES\n");ret=0;break;
+ {
+ struct mxcfb_waveform_modes modes;
+ if (!copy_from_user(&modes, argp, sizeof(modes))) {
+ mxc_epdc_fb_set_waveform_modes(&modes, info);
+ ret = 0;
+ }
+ break;
+ }
+ case MXCFB_SET_TEMPERATURE:GALLEN_DBGLOCAL_RUNLOG(1);
+ //printk("\n==>skip MXCFB_SET_TEMPERATURE\n");ret=0;break;
+ {
+ int temperature;
+ if (!get_user(temperature, (int32_t __user *) arg))
+ ret = mxc_epdc_fb_set_temperature(temperature,
+ info);
+ break;
+ }
+ case MXCFB_SET_AUTO_UPDATE_MODE:GALLEN_DBGLOCAL_RUNLOG(2);
+ //printk("\n==>skip MXCFB_SET_AUTO_UPDATE_MODE\n");ret=0;break;
+ {
+ u32 auto_mode = 0;
+ if (!get_user(auto_mode, (__u32 __user *) arg))
+ ret = mxc_epdc_fb_set_auto_update(auto_mode,
+ info);
+ break;
+ }
+ case MXCFB_SET_UPDATE_SCHEME:GALLEN_DBGLOCAL_RUNLOG(3);
+ //printk("\n==>skip MXCFB_SET_UPDATE_SCHEME\n");ret=0;break;
+ {
+ u32 upd_scheme = 0;
+ if (!get_user(upd_scheme, (__u32 __user *) arg))
+ ret = mxc_epdc_fb_set_upd_scheme(upd_scheme,
+ info);
+ break;
+ }
+ case MXCFB_SEND_UPDATE:GALLEN_DBGLOCAL_RUNLOG(4);
+ //printk("\n==> MXCFB_SEND_UPDATE\n");
+ {
+ struct mxcfb_update_data upd_data;
+ if (!copy_from_user(&upd_data, argp,
+ sizeof(upd_data))) {
+ ret = mxc_epdc_fb_send_update(&upd_data, info);
+ if (ret == 0 && copy_to_user(argp, &upd_data,
+ sizeof(upd_data)))
+ ret = -EFAULT;
+ } else {
+ ret = -EFAULT;
+ }
+
+ break;
+ }
+
+#ifdef MX50_IOCTL_IF//[
+ case MXCFB_WAIT_FOR_UPDATE_COMPLETE:GALLEN_DBGLOCAL_RUNLOG(10);
+ //printk("\n==> MXCFB_WAIT_FOR_UPDATE_COMPLETE mx50\n");
+ {
+ u32 update_marker;
+ struct mxcfb_update_marker_data *p_mxc_upd_marker_data;
+
+
+ if (!get_user(update_marker, (__u32 __user *) arg)) {
+ GALLEN_DBGLOCAL_RUNLOG(13);
+ p_mxc_upd_marker_data = kmalloc(sizeof(struct mxcfb_update_marker_data), GFP_KERNEL);
+ if(p_mxc_upd_marker_data) {
+ p_mxc_upd_marker_data->collision_test = 0;
+ p_mxc_upd_marker_data->update_marker=update_marker;
+
+ GALLEN_DBGLOCAL_PRINTMSG("MXCFB_WAIT_FOR_UPDATE_COMPLETE marker=%d\n",update_marker);
+ ret = mxc_epdc_fb_wait_update_complete(p_mxc_upd_marker_data,info);
+ kfree(p_mxc_upd_marker_data);
+ }
+ else {
+ printk(KERN_ERR"%s(%d):memory not enough !\n",__FILE__,__LINE__);
+ }
+ }
+ break;
+ }
+
+ case MXCFB_WAIT_FOR_UPDATE_COMPLETE2:GALLEN_DBGLOCAL_RUNLOG(5);
+ printk("\n==> MXCFB_WAIT_FOR_UPDATE_COMPLETE2 mx50\n");
+#else//][!MX50_IOCTL_IF
+ case MXCFB_WAIT_FOR_UPDATE_COMPLETE:GALLEN_DBGLOCAL_RUNLOG(11);
+ //printk("\n==> MXCFB_WAIT_FOR_UPDATE_COMPLETE mx6sl\n");
+#endif//] MX50_IOCTL_IF
+ {
+ struct mxcfb_update_marker_data upd_marker_data;
+ if (!copy_from_user(&upd_marker_data, argp,
+ sizeof(upd_marker_data))) {
+ ret = mxc_epdc_fb_wait_update_complete(
+ &upd_marker_data, info);
+ if (copy_to_user(argp, &upd_marker_data,
+ sizeof(upd_marker_data)))
+ ret = -EFAULT;
+ } else {
+ ret = -EFAULT;
+ }
+
+ break;
+ }
+
+ case MXCFB_SET_PWRDOWN_DELAY:GALLEN_DBGLOCAL_RUNLOG(6);
+ //printk("\n==>skip MXCFB_SET_PWRDOWN_DELAY\n");ret=0;break;
+ {
+ int delay = 0;
+ if (!get_user(delay, (__u32 __user *) arg))
+ ret =
+ mxc_epdc_fb_set_pwrdown_delay(delay, info);
+ break;
+ }
+
+ case MXCFB_GET_PWRDOWN_DELAY:GALLEN_DBGLOCAL_RUNLOG(7);
+ //printk("\n==>skip MXCFB_GET_PWRDOWN_DELAY\n");ret=0;break;
+ {
+ int pwrdown_delay = mxc_epdc_get_pwrdown_delay(info);
+ if (put_user(pwrdown_delay,
+ (int __user *)argp))
+ ret = -EFAULT;
+ ret = 0;
+ break;
+ }
+
+#if 0
+ case MXCFB_GET_WORK_BUFFER:GALLEN_DBGLOCAL_RUNLOG(8);
+ //printk("\n==>skip MXCFB_GET_WORK_BUFFER\n");ret=0;break;
+ {
+ /* copy the epdc working buffer to the user space */
+ struct mxc_epdc_fb_data *fb_data = info ?
+ (struct mxc_epdc_fb_data *)info:g_fb_data;
+ flush_cache_all();
+ outer_flush_all();
+ if (copy_to_user((void __user *)arg,
+ (const void *) fb_data->working_buffer_A_virt,
+ fb_data->working_buffer_size))
+ ret = -EFAULT;
+ else
+ ret = 0;
+ flush_cache_all();
+ outer_flush_all();
+ break;
+ }
+
+#endif
+ default:GALLEN_DBGLOCAL_RUNLOG(9);
+ //printk("\n==>skip unkown fsl epd command %d\n",cmd);ret=0;break;
+ ret = k_fake_s1d13522_ioctl(cmd,arg);
+ break;
+ }
+
+ GALLEN_DBGLOCAL_END();
+ return ret;
+}
+
+static void mxc_epdc_fb_update_pages(struct mxc_epdc_fb_data *fb_data,
+ u16 y1, u16 y2)
+{
+ struct mxcfb_update_data update;
+
+ /* Do partial screen update, Update full horizontal lines */
+ update.update_region.left = 0;
+ update.update_region.width = fb_data->epdc_fb_var.xres;
+ update.update_region.top = y1;
+ update.update_region.height = y2 - y1;
+ update.waveform_mode = WAVEFORM_MODE_AUTO;
+ update.update_mode = UPDATE_MODE_FULL;
+ update.update_marker = 0;
+ update.temp = TEMP_USE_AMBIENT;
+ update.flags = 0;
+
+ mxc_epdc_fb_send_update(&update, &fb_data->info);
+}
+
+/* this is called back from the deferred io workqueue */
+static void mxc_epdc_fb_deferred_io(struct fb_info *info,
+ struct list_head *pagelist)
+{
+ struct mxc_epdc_fb_data *fb_data = (struct mxc_epdc_fb_data *)info;
+ struct page *page;
+ unsigned long beg, end;
+ int y1, y2, miny, maxy;
+
+ GALLEN_DBGLOCAL_BEGIN();
+
+ if (fb_data->auto_mode != AUTO_UPDATE_MODE_AUTOMATIC_MODE) {
+ return;
+ }
+
+ miny = INT_MAX;
+ maxy = 0;
+ list_for_each_entry(page, pagelist, lru) {
+ beg = page->index << PAGE_SHIFT;
+ end = beg + PAGE_SIZE - 1;
+ y1 = beg / info->fix.line_length;
+ y2 = end / info->fix.line_length;
+ if (y2 >= fb_data->epdc_fb_var.yres)
+ y2 = fb_data->epdc_fb_var.yres - 1;
+ if (miny > y1)
+ miny = y1;
+ if (maxy < y2)
+ maxy = y2;
+ }
+
+ mxc_epdc_fb_update_pages(fb_data, miny, maxy);
+
+ GALLEN_DBGLOCAL_END();
+}
+
+void mxc_epdc_fb_flush_updates(struct mxc_epdc_fb_data *fb_data)
+{
+ int ret;
+
+ GALLEN_DBGLOCAL_BEGIN();
+
+ if (fb_data->in_init) {
+ GALLEN_DBGLOCAL_ESC();
+ return;
+ }
+
+ /* Grab queue lock to prevent any new updates from being submitted */
+ mutex_lock(&fb_data->queue_mutex);
+
+ /*
+ * 3 places to check for updates that are active or pending:
+ * 1) Updates in the pending list
+ * 2) Update buffers in use (e.g., PxP processing)
+ * 3) Active updates to panel - We can key off of EPDC
+ * power state to know if we have active updates.
+ */
+#if 1
+ //printk("%s(%d) : \n",__FUNCTION__,__LINE__);
+ //printk("\tpower_state=%d\n",fb_data->power_state);
+ //printk("\tpending list is empty=%d\n",list_empty(&fb_data->upd_pending_list));
+ //printk("\tfree list is full=%d\n",is_free_list_full(fb_data));
+ if ( (fb_data->power_state == POWER_STATE_ON) &&
+ (!list_empty(&fb_data->upd_pending_list) ||
+ !is_free_list_full(fb_data)) )
+#else
+ if (!list_empty(&fb_data->upd_pending_list) ||
+ !is_free_list_full(fb_data) ||
+ (fb_data->updates_active == true))
+#endif
+ {
+
+ GALLEN_DBGLOCAL_RUNLOG(0);
+
+ /* Initialize event signalling updates are done */
+ init_completion(&fb_data->updates_done);
+ fb_data->waiting_for_idle = true;
+
+ mutex_unlock(&fb_data->queue_mutex);
+ /* Wait for any currently active updates to complete */
+ ret = wait_for_completion_timeout(&fb_data->updates_done,
+ msecs_to_jiffies(8000));
+ if (!ret)
+ dev_err(fb_data->dev,
+ "Flush updates timeout! ret = 0x%x\n", ret);
+
+ mutex_lock(&fb_data->queue_mutex);
+ fb_data->waiting_for_idle = false;
+ }
+
+ mutex_unlock(&fb_data->queue_mutex);
+
+ GALLEN_DBGLOCAL_END();
+}
+
+static int mxc_epdc_fb_blank(int blank, struct fb_info *info)
+{
+ struct mxc_epdc_fb_data *fb_data = (struct mxc_epdc_fb_data *)info;
+ int ret;
+
+ GALLEN_DBGLOCAL_BEGIN();
+
+ dev_dbg(fb_data->dev, "blank = %d\n", blank);
+
+ if (fb_data->blank == blank)
+ {
+ GALLEN_DBGLOCAL_ESC();
+ return 0;
+ }
+
+ //fb_data->blank = blank;
+
+ switch (blank) {
+ case FB_BLANK_POWERDOWN:GALLEN_DBGLOCAL_RUNLOG(0);
+ mxc_epdc_fb_flush_updates(fb_data);
+ /* Wait for powerdown */
+ mutex_lock(&fb_data->power_mutex);
+ if ((fb_data->power_state == POWER_STATE_ON) &&
+ (fb_data->pwrdown_delay == FB_POWERDOWN_DISABLE)) {
+
+ /* Powerdown disabled, so we disable EPDC manually */
+ int count = 0;
+ int sleep_ms = 10;
+
+ GALLEN_DBGLOCAL_RUNLOG(1);
+
+ mutex_unlock(&fb_data->power_mutex);
+
+ /* If any active updates, wait for them to complete */
+ while (fb_data->updates_active) {
+ GALLEN_DBGLOCAL_RUNLOG(2);
+ /* Timeout after 1 sec */
+ if ((count * sleep_ms) > 1000) {
+ GALLEN_DBGLOCAL_RUNLOG(3);
+ break;
+ }
+ msleep(sleep_ms);
+ count++;
+ }
+
+ fb_data->powering_down = true;
+ epdc_powerdown(fb_data);
+ } else if (fb_data->power_state != POWER_STATE_OFF) {
+ GALLEN_DBGLOCAL_RUNLOG(4);
+
+ fb_data->wait_for_powerdown = true;
+ init_completion(&fb_data->powerdown_compl);
+ mutex_unlock(&fb_data->power_mutex);
+ ret = wait_for_completion_timeout(&fb_data->powerdown_compl,
+ msecs_to_jiffies(5000));
+ if (!ret) {
+
+ dev_err(fb_data->dev,
+ "No powerdown received!\n");
+
+ GALLEN_DBGLOCAL_ESC();
+ return -ETIMEDOUT;
+ }
+ } else {
+ GALLEN_DBGLOCAL_RUNLOG(6);
+ mutex_unlock(&fb_data->power_mutex);
+ }
+ break;
+ case FB_BLANK_VSYNC_SUSPEND:GALLEN_DBGLOCAL_RUNLOG(7);
+ case FB_BLANK_HSYNC_SUSPEND:GALLEN_DBGLOCAL_RUNLOG(8);
+ case FB_BLANK_NORMAL:GALLEN_DBGLOCAL_RUNLOG(9);
+ mxc_epdc_fb_flush_updates(fb_data);
+ break;
+ }
+
+ GALLEN_DBGLOCAL_END();
+ return 0;
+}
+
+static int mxc_epdc_fb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct mxc_epdc_fb_data *fb_data = (struct mxc_epdc_fb_data *)info;
+ u_int y_bottom;
+
+ dev_dbg(info->device, "%s: var->yoffset %d, info->var.yoffset %d\n",
+ __func__, var->yoffset, info->var.yoffset);
+ /* check if var is valid; also, xpan is not supported */
+ if (!var || (var->xoffset != info->var.xoffset) ||
+ (var->yoffset + var->yres > var->yres_virtual)) {
+ dev_dbg(info->device, "x panning not supported\n");
+ return -EINVAL;
+ }
+
+ if ((fb_data->epdc_fb_var.xoffset == var->xoffset) &&
+ (fb_data->epdc_fb_var.yoffset == var->yoffset))
+ return 0; /* No change, do nothing */
+
+ y_bottom = var->yoffset;
+
+ if (!(var->vmode & FB_VMODE_YWRAP))
+ y_bottom += var->yres;
+
+ if (y_bottom > info->var.yres_virtual)
+ return -EINVAL;
+
+ //mutex_lock(&fb_data->queue_mutex);
+ mutex_lock(&fb_data->pxp_mutex);
+
+ fb_data->fb_offset = (var->yoffset * var->xres_virtual + var->xoffset)
+ * (var->bits_per_pixel) / 8;
+
+ fb_data->epdc_fb_var.xoffset = var->xoffset;
+ fb_data->epdc_fb_var.yoffset = var->yoffset;
+
+ if (var->vmode & FB_VMODE_YWRAP)
+ info->var.vmode |= FB_VMODE_YWRAP;
+ else
+ info->var.vmode &= ~FB_VMODE_YWRAP;
+
+ mutex_unlock(&fb_data->pxp_mutex);
+ //mutex_unlock(&fb_data->queue_mutex);
+
+ return 0;
+}
+
+static struct fb_ops mxc_epdc_fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = mxc_epdc_fb_check_var,
+ .fb_set_par = mxc_epdc_fb_set_par,
+ .fb_setcmap = mxc_epdc_fb_setcmap,
+ .fb_setcolreg = mxc_epdc_fb_setcolreg,
+ .fb_pan_display = mxc_epdc_fb_pan_display,
+#ifdef CONFIG_ANDROID //[
+ .fb_open = mxc_epdc_fb_open,
+#endif //] CONFIG_ANDROID
+ .fb_ioctl = mxc_epdc_fb_ioctl,
+ .fb_mmap = mxc_epdc_fb_mmap,
+ .fb_blank = mxc_epdc_fb_blank,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+};
+
+static struct fb_deferred_io mxc_epdc_fb_defio = {
+ .delay = HZ,
+ .deferred_io = mxc_epdc_fb_deferred_io,
+};
+
+static void epdc_done_work_func(struct work_struct *work)
+{
+ struct mxc_epdc_fb_data *fb_data =
+ container_of(work, struct mxc_epdc_fb_data,
+ epdc_done_work.work);
+ epdc_powerdown(fb_data);
+}
+
+static void epdc_aa_work_func(struct work_struct *work)
+{
+ struct mxc_epdc_fb_data *fb_data =
+ container_of(work, struct mxc_epdc_fb_data, epdc_aa_work);
+ struct update_data_list *upd_data_list = fb_data->cur_update;
+ struct mxcfb_rect adj_update_region;
+
+ /* Get rotation-adjusted coordinates */
+ adjust_coordinates(fb_data->epdc_fb_var.xres,
+ fb_data->epdc_fb_var.yres, fb_data->epdc_fb_var.rotate,
+ &upd_data_list->update_desc->upd_data.update_region,
+ &adj_update_region);
+
+ /*
+ * We are paused between WB Processing and TCE scan. Perform AA
+ * or AA-D processing and then allow the update to complete.
+ */
+ if (!(upd_data_list->update_desc->upd_data.flags
+ & EPDC_FLAG_USE_AAD)) {
+ /*
+ * AA processing API with Dual WB feature (EPDC v2.0+)
+ * do_aa_processing(
+ * unsigned short *working_buffer_ptr_in,
+ * unsigned short *working_buffer_ptr_out,
+ * struct mxcfb_rect *update_region,
+ * long working_buffer_width,
+ * long working_buffer_height);
+ */
+
+ /* collect the system time data */
+ aa_time_stamp[0] = get_uSecs();
+
+ flush_cache_all();
+ outer_flush_all();
+ if (do_aa_processing_v2_2_1(
+ (uint16_t *)fb_data->working_buffer_A_virt,
+ (uint16_t *)fb_data->working_buffer_B_virt,
+ &adj_update_region,
+ fb_data->native_width,
+ fb_data->native_height))
+ dev_err(fb_data->dev," AAD algorithm is not available in this EPDC driver!\n");
+
+
+ flush_cache_all();
+ outer_flush_all();
+ /* collect the system time data */
+ aa_time_stamp[1] = get_uSecs();
+
+ dev_info (fb_data->dev, "AA(v2.2.1) Algo Exec Time = %d\n", getTimeDiff(aa_time_stamp[0], aa_time_stamp[1]));
+ }
+ else if (upd_data_list->update_desc->upd_data.flags
+ & EPDC_FLAG_USE_AAD) {
+ /* AAD algorithm */
+
+ /* collect the system time data */
+ aa_time_stamp[0] = get_uSecs();
+
+ flush_cache_all();
+ outer_flush_all();
+
+ if (do_aad_processing_v2_1_1(
+ (uint16_t *)fb_data->working_buffer_A_virt,
+ (uint16_t *)fb_data->working_buffer_B_virt,
+ &adj_update_region,
+ fb_data->native_width,
+ fb_data->native_height))
+ dev_err(fb_data->dev," AAD algorithm is not available in this EPDC driver!\n");
+
+ flush_cache_all();
+ outer_flush_all();
+
+ /* collect the system time data */
+ aa_time_stamp[1] = get_uSecs();
+
+ dev_info (fb_data->dev, "AA-D(v2.1.1) Algo Exec Time = %d\n", getTimeDiff(aa_time_stamp[0], aa_time_stamp[1]));
+ }
+
+ /* Protect access to buffer queues and to update HW */
+ mutex_lock(&fb_data->queue_mutex);
+
+ fb_data->cur_update->update_desc->wb_pause = false;
+
+ epdc_submit_paused_update();
+
+ mutex_unlock(&fb_data->queue_mutex);
+}
+
+static bool is_free_list_full(struct mxc_epdc_fb_data *fb_data)
+{
+ int count = 0;
+ struct update_data_list *plist;
+
+ /* Count buffers in free buffer list */
+ list_for_each_entry(plist, &fb_data->upd_buf_free_list, list)
+ count++;
+
+ /* Check to see if all buffers are in this list */
+ if (count == fb_data->max_num_updates)
+ return true;
+ else
+ return false;
+}
+
+static irqreturn_t mxc_epdc_irq_handler(int irq, void *dev_id)
+{
+ struct mxc_epdc_fb_data *fb_data = dev_id;
+ u32 ints_fired, luts1_ints_fired, luts2_ints_fired;
+
+ GALLEN_DBGLOCAL_BEGIN();
+
+ /*
+ * If we just completed one-time panel init, bypass
+ * queue handling, clear interrupt and return
+ */
+ if (fb_data->in_init) {
+ GALLEN_DBGLOCAL_RUNLOG(0);
+
+ if (epdc_is_working_buffer_complete()) {
+ GALLEN_DBGLOCAL_RUNLOG(1);
+ epdc_working_buf_intr(false);
+ epdc_clear_working_buf_irq();
+ dev_dbg(fb_data->dev, "Cleared WB for init update\n");
+ }
+
+ if (epdc_is_lut_complete(fb_data->rev, 0)) {
+ GALLEN_DBGLOCAL_RUNLOG(2);
+ epdc_lut_complete_intr(fb_data->rev, 0, false);
+ epdc_clear_lut_complete_irq(fb_data->rev, 0);
+ fb_data->in_init = false;
+ dev_dbg(fb_data->dev, "Cleared LUT complete for init update\n");
+ }
+
+ GALLEN_DBGLOCAL_ESC();
+ return IRQ_HANDLED;
+ }
+
+ ints_fired = __raw_readl(EPDC_IRQ_MASK) & __raw_readl(EPDC_IRQ);
+ if (fb_data->rev < 20) {
+ GALLEN_DBGLOCAL_RUNLOG(3);
+ luts1_ints_fired = 0;
+ luts2_ints_fired = 0;
+ } else {
+ GALLEN_DBGLOCAL_RUNLOG(4);
+ luts1_ints_fired = __raw_readl(EPDC_IRQ_MASK1) & __raw_readl(EPDC_IRQ1);
+ luts2_ints_fired = __raw_readl(EPDC_IRQ_MASK2) & __raw_readl(EPDC_IRQ2);
+ }
+
+ if (!(ints_fired || luts1_ints_fired || luts2_ints_fired)) {
+ GALLEN_DBGLOCAL_ESC();
+ return IRQ_HANDLED;
+ }
+
+ if (__raw_readl(EPDC_IRQ) & EPDC_IRQ_TCE_UNDERRUN_IRQ) {
+ GALLEN_DBGLOCAL_RUNLOG(6);
+
+ dev_err(fb_data->dev,
+ "TCE underrun! Will continue to update panel\n");
+ /* Clear TCE underrun IRQ */
+ __raw_writel(EPDC_IRQ_TCE_UNDERRUN_IRQ, EPDC_IRQ_CLEAR);
+ }
+
+ /* Check if we are waiting on EOF to sync a new update submission */
+ if (epdc_signal_eof()) {
+ GALLEN_DBGLOCAL_RUNLOG(7);
+
+ epdc_eof_intr(false);
+ epdc_clear_eof_irq();
+ complete(&fb_data->eof_event);
+ }
+
+ /*
+ * Workaround for EPDC v2.0/v2.1 errata: Must read collision status
+ * before clearing IRQ, or else collision status for bits 16:63
+ * will be automatically cleared. So we read it here, and there is
+ * no conflict with using it in epdc_intr_work_func since the
+ * working buffer processing flow is strictly sequential (i.e.,
+ * only one WB processing done at a time, so the data grabbed
+ * here should be up-to-date and accurate when the WB processing
+ * completes. Also, note that there is no impact to other versions
+ * of EPDC by reading LUT status here.
+ */
+ if (fb_data->cur_update != NULL) {
+ GALLEN_DBGLOCAL_RUNLOG(8);
+ fb_data->epdc_colliding_luts = epdc_get_colliding_luts(fb_data->rev);
+ }
+
+ /* Clear the interrupt mask for any interrupts signalled */
+ __raw_writel(ints_fired, EPDC_IRQ_MASK_CLEAR);
+ __raw_writel(luts1_ints_fired, EPDC_IRQ_MASK1_CLEAR);
+ __raw_writel(luts2_ints_fired, EPDC_IRQ_MASK2_CLEAR);
+
+ dev_dbg(fb_data->dev, "EPDC interrupts fired = 0x%x, "
+ "LUTS1 fired = 0x%x, LUTS2 fired = 0x%x\n",
+ ints_fired, luts1_ints_fired, luts2_ints_fired);
+
+ queue_work(fb_data->epdc_intr_workqueue,
+ &fb_data->epdc_intr_work);
+
+ GALLEN_DBGLOCAL_END();
+ return IRQ_HANDLED;
+}
+
+static void epdc_intr_work_func(struct work_struct *work)
+{
+ struct mxc_epdc_fb_data *fb_data =
+ container_of(work, struct mxc_epdc_fb_data, epdc_intr_work);
+ struct update_data_list *collision_update;
+ struct mxcfb_rect *next_upd_region;
+ struct update_marker_data *next_marker;
+ struct update_marker_data *temp;
+ u64 temp_mask;
+ u32 lut;
+ bool ignore_collision = false;
+ int i;
+ bool wb_lut_done = false;
+ bool free_update = true;
+ int next_lut, epdc_next_lut_15;
+ u32 epdc_luts_active, epdc_wb_busy, epdc_upd_done, epdc_luts_avail;
+ u32 epdc_lut_cancelled;
+ u32 epdc_collision;
+ u64 epdc_irq_stat;
+ bool epdc_waiting_on_wb;
+ u32 coll_coord, coll_size;
+ struct mxcfb_rect coll_region;
+ bool update_paused = false;
+ bool wb_pause;
+ bool update_is_valid = true;
+
+ GALLEN_DBGLOCAL_BEGIN_EX(64);
+
+ /* Protect access to buffer queues and to update HW */
+ mutex_lock(&fb_data->queue_mutex);
+
+ /* Capture EPDC status one time to limit exposure to race conditions */
+ epdc_luts_active = epdc_any_luts_active(fb_data->rev);
+ epdc_wb_busy = epdc_is_working_buffer_busy();
+ epdc_upd_done = __raw_readl(EPDC_IRQ) & EPDC_IRQ_UPD_DONE_IRQ;
+ epdc_lut_cancelled = epdc_is_lut_cancelled();
+ epdc_luts_avail = epdc_any_luts_available();
+ epdc_collision = epdc_is_collision();
+ if (fb_data->rev < 20) {
+ GALLEN_DBGLOCAL_RUNLOG(0);
+ epdc_irq_stat = __raw_readl(EPDC_IRQ);
+ }
+ else {
+ GALLEN_DBGLOCAL_RUNLOG(1);
+ epdc_irq_stat = (u64)__raw_readl(EPDC_IRQ1) |
+ ((u64)__raw_readl(EPDC_IRQ2) << 32);
+ }
+ epdc_waiting_on_wb = (fb_data->cur_update != NULL) ? true : false;
+ if (fb_data->cur_update && fb_data->cur_update->update_desc->wb_pause)
+ wb_pause = true;
+ else
+ wb_pause = false;
+
+ /* Free any LUTs that have completed */
+ for (i = 0; i < fb_data->num_luts; i++) {
+
+ GALLEN_DBGLOCAL_RUNLOG(2);
+ if ((epdc_irq_stat & (1ULL << i)) == 0) {
+
+ GALLEN_DBGLOCAL_RUNLOG(3);
+ continue;
+ }
+
+ dev_dbg(fb_data->dev, "LUT %d completed\n", i);
+
+ /* Disable IRQ for completed LUT */
+ epdc_lut_complete_intr(fb_data->rev, i, false);
+
+ /*
+ * Go through all updates in the collision list and
+ * unmask any updates that were colliding with
+ * the completed LUT.
+ */
+ list_for_each_entry(collision_update,
+ &fb_data->upd_buf_collision_list, list) {
+
+ GALLEN_DBGLOCAL_RUNLOG(4);
+ collision_update->collision_mask =
+ collision_update->collision_mask & ~(1ULL << i);
+ }
+
+ epdc_clear_lut_complete_irq(fb_data->rev, i);
+
+ fb_data->luts_complete_wb |= 1ULL << i;
+
+ fb_data->lut_update_order[i] = 0;
+
+ /* Signal completion if submit workqueue needs a LUT */
+ if (fb_data->waiting_for_lut) {
+ GALLEN_DBGLOCAL_RUNLOG(5);
+ complete(&fb_data->update_res_free);
+ fb_data->waiting_for_lut = false;
+ }
+
+ /* Signal completion if LUT15 free and is needed */
+ if (fb_data->waiting_for_lut15 && (i == 15)) {
+ GALLEN_DBGLOCAL_RUNLOG(6);
+ complete(&fb_data->lut15_free);
+ fb_data->waiting_for_lut15 = false;
+ }
+
+ /* Detect race condition where WB and its LUT complete
+ (i.e. full update completes) in one swoop */
+ if (epdc_waiting_on_wb &&
+ (i == fb_data->cur_update->lut_num)) {
+ GALLEN_DBGLOCAL_RUNLOG(7);
+ wb_lut_done = true;
+ }
+
+ /* Signal completion if anyone waiting on this LUT */
+ if (!wb_lut_done) {
+
+ GALLEN_DBGLOCAL_RUNLOG(8);
+ list_for_each_entry_safe(next_marker, temp,
+ &fb_data->full_marker_list,
+ full_list) {
+
+ GALLEN_DBGLOCAL_RUNLOG(9);
+ if (next_marker->lut_num != i) {
+ GALLEN_DBGLOCAL_RUNLOG(10);
+ continue;
+ }
+
+ /* Found marker to signal - remove from list */
+ list_del_init(&next_marker->full_list);
+
+ /* Signal completion of update */
+ dev_dbg(fb_data->dev, "Signaling marker %d\n",
+ next_marker->update_marker);
+ if (next_marker->waiting) {
+ GALLEN_DBGLOCAL_RUNLOG(11);
+ DBG_MSG("%s()[%d]:update_complete @ marker %d\n",\
+ __FUNCTION__,__LINE__,next_marker->update_marker);
+ complete(&next_marker->update_completion);
+ }
+ else {
+ GALLEN_DBGLOCAL_RUNLOG(12);
+ kfree(next_marker);
+ }
+ }
+ }
+ }
+
+ /* Check to see if all updates have completed */
+ if (list_empty(&fb_data->upd_pending_list) &&
+ is_free_list_full(fb_data) &&
+ !epdc_waiting_on_wb &&
+ !epdc_luts_active) {
+
+ GALLEN_DBGLOCAL_RUNLOG(13);
+
+ fb_data->updates_active = false;
+
+ if (fb_data->pwrdown_delay != FB_POWERDOWN_DISABLE) {
+
+ GALLEN_DBGLOCAL_RUNLOG(14);
+ /*
+ * Set variable to prevent overlapping
+ * enable/disable requests
+ */
+ fb_data->powering_down = true;
+
+ /* Schedule task to disable EPDC HW until next update */
+ schedule_delayed_work(&fb_data->epdc_done_work,
+ msecs_to_jiffies(fb_data->pwrdown_delay));
+
+ /* Reset counter to reduce chance of overflow */
+ fb_data->order_cnt = 0;
+ }
+
+ if (fb_data->waiting_for_idle) {
+ GALLEN_DBGLOCAL_RUNLOG(15);
+ complete(&fb_data->updates_done);
+ }
+ }
+
+ /*
+ * Is Working Buffer busy (for normal update case)?
+ * Is UPD_DONE complete (for paused update case)?
+ */
+ if ((!wb_pause && epdc_wb_busy) || (wb_pause && !epdc_upd_done)) {
+ /* Can't submit another update until WB is done */
+ mutex_unlock(&fb_data->queue_mutex);
+ GALLEN_DBGLOCAL_ESC();
+ return;
+ }
+
+ /*
+ * Were we waiting on working buffer?
+ * If so, update queues and check for collisions
+ */
+ if (epdc_waiting_on_wb) {
+ GALLEN_DBGLOCAL_RUNLOG(16);
+ dev_dbg(fb_data->dev, "\nWorking buffer completed\n");
+
+ /* Signal completion if submit workqueue was waiting on WB */
+ if (fb_data->waiting_for_wb && !wb_pause) {
+ GALLEN_DBGLOCAL_RUNLOG(17);
+ complete(&fb_data->update_res_free);
+ fb_data->waiting_for_wb = false;
+ }
+
+ /*
+ * Handle 3 possible events exclusive of each other, in
+ * the following priority order:
+ * 1) This is a collision test update
+ * 2) The LUT was cancelled (no pixels changing)
+ * 3) The update is being paused after WB processing
+ */
+ if (fb_data->cur_update->update_desc->upd_data.flags
+ & EPDC_FLAG_TEST_COLLISION) {
+
+ GALLEN_DBGLOCAL_RUNLOG(18);
+ /* This was a dry run to test for collision */
+
+ /* Signal marker */
+ list_for_each_entry_safe(next_marker, temp,
+ &fb_data->full_marker_list,
+ full_list) {
+
+ GALLEN_DBGLOCAL_RUNLOG(19);
+ if (next_marker->lut_num != DRY_RUN_NO_LUT) {
+ GALLEN_DBGLOCAL_RUNLOG(20);
+ continue;
+ }
+
+ if (epdc_collision) {
+ GALLEN_DBGLOCAL_RUNLOG(21);
+ next_marker->collision_test = true;
+ }
+ else {
+ GALLEN_DBGLOCAL_RUNLOG(22);
+ next_marker->collision_test = false;
+ }
+
+ dev_dbg(fb_data->dev,
+ "In IRQ, collision_test = %d\n",
+ next_marker->collision_test);
+
+ /* Found marker to signal - remove from list */
+ list_del_init(&next_marker->full_list);
+
+ /* Signal completion of update */
+ dev_dbg(fb_data->dev, "Signaling marker "
+ "for dry-run - %d\n",
+ next_marker->update_marker);
+
+ DBG_MSG("%s()[%d]:update_complete @ marker %d\n",\
+ __FUNCTION__,__LINE__,next_marker->update_marker);
+ complete(&next_marker->update_completion);
+
+ update_is_valid = false;
+ }
+ } else if (epdc_lut_cancelled && !epdc_collision) {
+ GALLEN_DBGLOCAL_RUNLOG(23);
+
+ /*
+ * Note: The update may be cancelled (void) if all
+ * pixels collided. In that case we handle it as a
+ * collision, not a cancel.
+ */
+
+ /* Clear LUT status (might be set if no AUTOWV used) */
+
+ /*
+ * Disable and clear IRQ for the LUT used.
+ * Even though LUT is cancelled in HW, the LUT
+ * complete bit may be set if AUTOWV not used.
+ */
+ epdc_lut_complete_intr(fb_data->rev,
+ fb_data->cur_update->lut_num, false);
+ epdc_clear_lut_complete_irq(fb_data->rev,
+ fb_data->cur_update->lut_num);
+
+ fb_data->lut_update_order[fb_data->cur_update->lut_num] = 0;
+
+ /* Signal completion if submit workqueue needs a LUT */
+ if (fb_data->waiting_for_lut) {
+ GALLEN_DBGLOCAL_RUNLOG(24);
+ complete(&fb_data->update_res_free);
+ fb_data->waiting_for_lut = false;
+ }
+
+ list_for_each_entry_safe(next_marker, temp,
+ &fb_data->cur_update->update_desc->upd_marker_list,
+ upd_list) {
+
+ GALLEN_DBGLOCAL_RUNLOG(25);
+
+ /* Del from per-update & full list */
+ list_del_init(&next_marker->upd_list);
+ list_del_init(&next_marker->full_list);
+
+ /* Signal completion of update */
+ dev_dbg(fb_data->dev,
+ "Signaling marker (cancelled) %d\n",
+ next_marker->update_marker);
+ if (next_marker->waiting) {
+ GALLEN_DBGLOCAL_RUNLOG(26);
+ DBG_MSG("%s()[%d]:update_complete @ marker %d\n",\
+ __FUNCTION__,__LINE__,next_marker->update_marker);
+ complete(&next_marker->update_completion);
+ }
+ else {
+ GALLEN_DBGLOCAL_RUNLOG(27);
+ kfree(next_marker);
+ }
+ }
+
+ update_is_valid = false;
+ } else if (fb_data->cur_update->update_desc->wb_pause) {
+ GALLEN_DBGLOCAL_RUNLOG(28);
+ dev_dbg(fb_data->dev, "\nShould be paused??\n");
+ /* Disable UPD_DONE interrupt */
+ epdc_upd_done_intr(false);
+
+ /* Need to schedule task to process WB
+ before we can complete the update */
+ queue_work(fb_data->epdc_aa_workqueue,
+ &fb_data->epdc_aa_work);
+
+ update_paused = true;
+ } else {
+ dev_dbg(fb_data->dev,
+ "\nNo pausing this time folks!\n");
+ }
+
+ if (epdc_collision & update_is_valid) {
+ /* aa update (no dry-run), collision occurred */
+
+ /* Check list of colliding LUTs, and add to our collision mask */
+ fb_data->cur_update->collision_mask =
+ fb_data->epdc_colliding_luts;
+
+ /* Clear collisions that completed since WB began */
+ fb_data->cur_update->collision_mask &=
+ ~fb_data->luts_complete_wb;
+
+ dev_dbg(fb_data->dev, "Collision mask = 0x%llx\n",
+ fb_data->epdc_colliding_luts);
+
+ /* For EPDC 2.0 and later, minimum collision bounds
+ are provided by HW. Recompute new bounds here. */
+ if ((fb_data->upd_scheme != UPDATE_SCHEME_SNAPSHOT)
+ && (fb_data->rev >= 20)) {
+ u32 xres, yres, rotate;
+ int start, end;
+ struct mxcfb_rect *cur_upd_rect =
+ &fb_data->cur_update->update_desc->upd_data.update_region;
+
+ GALLEN_DBGLOCAL_RUNLOG(29);
+
+ /* Get collision region coords from EPDC */
+ coll_coord = __raw_readl(EPDC_UPD_COL_CORD);
+ coll_size = __raw_readl(EPDC_UPD_COL_SIZE);
+ coll_region.left =
+ (coll_coord & EPDC_UPD_COL_CORD_XCORD_MASK)
+ >> EPDC_UPD_COL_CORD_XCORD_OFFSET;
+ coll_region.top =
+ (coll_coord & EPDC_UPD_COL_CORD_YCORD_MASK)
+ >> EPDC_UPD_COL_CORD_YCORD_OFFSET;
+ coll_region.width =
+ (coll_size & EPDC_UPD_COL_SIZE_WIDTH_MASK)
+ >> EPDC_UPD_COL_SIZE_WIDTH_OFFSET;
+ coll_region.height =
+ (coll_size & EPDC_UPD_COL_SIZE_HEIGHT_MASK)
+ >> EPDC_UPD_COL_SIZE_HEIGHT_OFFSET;
+ dev_dbg(fb_data->dev, "Coll region: l = %d, "
+ "t = %d, w = %d, h = %d\n",
+ coll_region.left, coll_region.top,
+ coll_region.width, coll_region.height);
+
+ /* Convert coords back to orig orientation */
+ switch (fb_data->epdc_fb_var.rotate) {
+ case FB_ROTATE_CW:
+ GALLEN_DBGLOCAL_RUNLOG(30);
+ xres = fb_data->epdc_fb_var.yres;
+ yres = fb_data->epdc_fb_var.xres;
+ rotate = FB_ROTATE_CCW;
+ break;
+ case FB_ROTATE_UD:
+ GALLEN_DBGLOCAL_RUNLOG(31);
+ xres = fb_data->epdc_fb_var.xres;
+ yres = fb_data->epdc_fb_var.yres;
+ rotate = FB_ROTATE_UD;
+ break;
+ case FB_ROTATE_CCW:
+ GALLEN_DBGLOCAL_RUNLOG(32);
+ xres = fb_data->epdc_fb_var.yres;
+ yres = fb_data->epdc_fb_var.xres;
+ rotate = FB_ROTATE_CW;
+ break;
+ default:
+ GALLEN_DBGLOCAL_RUNLOG(33);
+ xres = fb_data->epdc_fb_var.xres;
+ yres = fb_data->epdc_fb_var.yres;
+ rotate = FB_ROTATE_UR;
+ break;
+ }
+ adjust_coordinates(xres, yres, rotate,
+ &coll_region, cur_upd_rect);
+
+ dev_dbg(fb_data->dev, "Adj coll region: l = %d, "
+ "t = %d, w = %d, h = %d\n",
+ cur_upd_rect->left, cur_upd_rect->top,
+ cur_upd_rect->width,
+ cur_upd_rect->height);
+ }
+
+ /*
+ * If we collide with newer updates, then
+ * we don't need to re-submit the update. The
+ * idea is that the newer updates should take
+ * precedence anyways, so we don't want to
+ * overwrite them.
+ */
+ for (temp_mask = fb_data->cur_update->collision_mask, lut = 0;
+ temp_mask != 0;
+ lut++, temp_mask = temp_mask >> 1) {
+ GALLEN_DBGLOCAL_RUNLOG(34);
+ if (!(temp_mask & 0x1)) {
+ GALLEN_DBGLOCAL_RUNLOG(35);
+ continue;
+ }
+
+ if (fb_data->lut_update_order[lut] >=
+ fb_data->cur_update->update_desc->update_order) {
+ dev_dbg(fb_data->dev,
+ "Ignoring collision with"
+ "newer update.\n");
+ ignore_collision = true;
+ break;
+ }
+ }
+
+ if (!ignore_collision) {
+ GALLEN_DBGLOCAL_RUNLOG(36);
+ free_update = false;
+ /*
+ * If update has markers, clear the LUTs to
+ * avoid signalling that they have completed.
+ */
+ list_for_each_entry_safe(next_marker, temp,
+ &fb_data->cur_update->update_desc->upd_marker_list,
+ upd_list)
+ next_marker->lut_num = INVALID_LUT;
+
+ /* Move to collision list */
+ list_add_tail(&fb_data->cur_update->list,
+ &fb_data->upd_buf_collision_list);
+ }
+ }
+
+ /* Do we need to free the current update descriptor? */
+ if (free_update && !update_paused) {
+ GALLEN_DBGLOCAL_RUNLOG(37);
+ /* Handle condition where WB & LUT are both complete */
+ if (wb_lut_done) {
+ GALLEN_DBGLOCAL_RUNLOG(38);
+ list_for_each_entry_safe(next_marker, temp,
+ &fb_data->cur_update->update_desc->upd_marker_list,
+ upd_list) {
+
+
+ GALLEN_DBGLOCAL_RUNLOG(39);
+
+ /* Del from per-update & full list */
+ list_del_init(&next_marker->upd_list);
+ list_del_init(&next_marker->full_list);
+
+ /* Signal completion of update */
+ dev_dbg(fb_data->dev,
+ "Signaling marker (wb) %d\n",
+ next_marker->update_marker);
+ if (next_marker->waiting) {
+ GALLEN_DBGLOCAL_RUNLOG(40);
+ DBG_MSG("%s()[%d]:update_complete @ marker %d\n",\
+ __FUNCTION__,__LINE__,next_marker->update_marker);
+ complete(&next_marker->update_completion);
+ }
+ else {
+ GALLEN_DBGLOCAL_RUNLOG(41);
+ kfree(next_marker);
+ }
+ }
+ }
+
+ /* Free marker list and update descriptor */
+ kfree(fb_data->cur_update->update_desc);
+
+ /* Add to free buffer list */
+ list_add_tail(&fb_data->cur_update->list,
+ &fb_data->upd_buf_free_list);
+
+ /* Check to see if all updates have completed */
+ if (list_empty(&fb_data->upd_pending_list) &&
+ is_free_list_full(fb_data) &&
+ !epdc_luts_active) {
+
+ GALLEN_DBGLOCAL_RUNLOG(42);
+
+ fb_data->updates_active = false;
+
+ if (fb_data->pwrdown_delay !=
+ FB_POWERDOWN_DISABLE) {
+ GALLEN_DBGLOCAL_RUNLOG(43);
+ /*
+ * Set variable to prevent overlapping
+ * enable/disable requests
+ */
+ fb_data->powering_down = true;
+
+ /* Schedule EPDC disable */
+ schedule_delayed_work(&fb_data->epdc_done_work,
+ msecs_to_jiffies(fb_data->pwrdown_delay));
+
+ /* Reset counter to reduce chance of overflow */
+ fb_data->order_cnt = 0;
+ }
+
+ if (fb_data->waiting_for_idle) {
+ GALLEN_DBGLOCAL_RUNLOG(44);
+ complete(&fb_data->updates_done);
+ }
+ }
+ }
+
+ if (!update_paused) {
+ /* Clear current update */
+ fb_data->cur_update = NULL;
+
+ /* Clear IRQ for working buffer */
+ epdc_working_buf_intr(false);
+ epdc_clear_working_buf_irq();
+ }
+ }
+
+ if (fb_data->upd_scheme != UPDATE_SCHEME_SNAPSHOT) {
+ GALLEN_DBGLOCAL_RUNLOG(45);
+ /* Queued update scheme processing */
+
+ /* Schedule task to submit collision and pending update */
+ if (!fb_data->powering_down) {
+ GALLEN_DBGLOCAL_RUNLOG(46);
+ queue_work(fb_data->epdc_submit_workqueue,
+ &fb_data->epdc_submit_work);
+ }
+
+ /* Release buffer queues */
+ mutex_unlock(&fb_data->queue_mutex);
+
+ GALLEN_DBGLOCAL_ESC();
+ return;
+ }
+
+ /* Snapshot update scheme processing */
+
+ /* Check to see if any LUTs are free */
+ if (!epdc_luts_avail) {
+ dev_dbg(fb_data->dev, "No luts available.\n");
+ mutex_unlock(&fb_data->queue_mutex);
+ GALLEN_DBGLOCAL_ESC();
+ return;
+ }
+
+ epdc_next_lut_15 = epdc_choose_next_lut(fb_data->rev, &next_lut);
+ /* Check to see if there is a valid LUT to use */
+ if (epdc_next_lut_15 && fb_data->tce_prevent && (fb_data->rev < 20)) {
+ dev_dbg(fb_data->dev, "Must wait for LUT15\n");
+ mutex_unlock(&fb_data->queue_mutex);
+ GALLEN_DBGLOCAL_ESC();
+ return;
+ }
+
+ /*
+ * Are any of our collision updates able to go now?
+ * Go through all updates in the collision list and check to see
+ * if the collision mask has been fully cleared
+ */
+ list_for_each_entry(collision_update,
+ &fb_data->upd_buf_collision_list, list) {
+ GALLEN_DBGLOCAL_RUNLOG(47);
+
+ if (collision_update->collision_mask != 0) {
+ GALLEN_DBGLOCAL_RUNLOG(48);
+ continue;
+ }
+
+ dev_dbg(fb_data->dev, "A collision update is ready to go!\n");
+ /*
+ * We have a collision cleared, so select it
+ * and we will retry the update
+ */
+ fb_data->cur_update = collision_update;
+ list_del_init(&fb_data->cur_update->list);
+ break;
+ }
+
+ /*
+ * If we didn't find a collision update ready to go,
+ * we try to grab one from the update queue
+ */
+ if (fb_data->cur_update == NULL) {
+ GALLEN_DBGLOCAL_RUNLOG(49);
+ /* Is update list empty? */
+ if (list_empty(&fb_data->upd_buf_queue)) {
+ GALLEN_DBGLOCAL_RUNLOG(50);
+ dev_dbg(fb_data->dev, "No pending updates.\n");
+
+ /* No updates pending, so we are done */
+ mutex_unlock(&fb_data->queue_mutex);
+ GALLEN_DBGLOCAL_ESC();
+ return;
+ } else {
+ GALLEN_DBGLOCAL_RUNLOG(51);
+ dev_dbg(fb_data->dev, "Found a pending update!\n");
+
+ /* Process next item in update list */
+ fb_data->cur_update =
+ list_entry(fb_data->upd_buf_queue.next,
+ struct update_data_list, list);
+ list_del_init(&fb_data->cur_update->list);
+ }
+ }
+
+ /* Use LUT selected above */
+ fb_data->cur_update->lut_num = next_lut;
+
+ /* Associate LUT with update markers */
+ list_for_each_entry_safe(next_marker, temp,
+ &fb_data->cur_update->update_desc->upd_marker_list, upd_list)
+ next_marker->lut_num = fb_data->cur_update->lut_num;
+
+ /* Mark LUT as containing new update */
+ fb_data->lut_update_order[fb_data->cur_update->lut_num] =
+ fb_data->cur_update->update_desc->update_order;
+
+ /* Enable Collision and WB complete IRQs */
+ epdc_working_buf_intr(true);
+ epdc_lut_complete_intr(fb_data->rev, fb_data->cur_update->lut_num, true);
+
+ /* Program EPDC update to process buffer */
+ next_upd_region =
+ &fb_data->cur_update->update_desc->upd_data.update_region;
+ if (fb_data->cur_update->update_desc->upd_data.temp
+ != TEMP_USE_AMBIENT) {
+ GALLEN_DBGLOCAL_RUNLOG(52);
+ fb_data->temp_index = mxc_epdc_fb_get_temp_index(fb_data,
+ fb_data->cur_update->update_desc->upd_data.temp);
+ }
+ epdc_set_temp(fb_data->temp_index);
+ epdc_set_update_addr(fb_data->cur_update->phys_addr +
+ fb_data->cur_update->update_desc->epdc_offs);
+ epdc_set_update_coord(next_upd_region->left, next_upd_region->top);
+ epdc_set_update_dimensions(next_upd_region->width,
+ next_upd_region->height);
+ if (fb_data->rev > 20) {
+ GALLEN_DBGLOCAL_RUNLOG(54);
+ epdc_set_update_stride(fb_data->cur_update->update_desc->epdc_stride);
+ }
+ if (fb_data->wv_modes_update &&
+ (fb_data->cur_update->update_desc->upd_data.waveform_mode
+ == WAVEFORM_MODE_AUTO)) {
+
+ GALLEN_DBGLOCAL_RUNLOG(55);
+ epdc_set_update_waveform(&fb_data->wv_modes);
+ fb_data->wv_modes_update = false;
+ }
+
+ epdc_submit_update(fb_data->cur_update->lut_num,
+ fb_data->cur_update->update_desc->upd_data.waveform_mode,
+ fb_data->cur_update->update_desc->upd_data.update_mode,
+ false, fb_data->cur_update->update_desc->wb_pause,
+ false, 0);
+
+ /* Release buffer queues */
+ mutex_unlock(&fb_data->queue_mutex);
+ GALLEN_DBGLOCAL_END();
+ return;
+}
+
+static void draw_mode0(struct mxc_epdc_fb_data *fb_data)
+{
+ u32 *upd_buf_ptr;
+ int i;
+ struct fb_var_screeninfo *screeninfo = &fb_data->epdc_fb_var;
+ u32 xres, yres;
+
+ GALLEN_DBGLOCAL_BEGIN();
+
+ upd_buf_ptr = (u32 *)fb_data->info.screen_base;
+
+ epdc_working_buf_intr(true);
+ epdc_lut_complete_intr(fb_data->rev, 0, true);
+
+ /* Use unrotated (native) width/height */
+ if ((screeninfo->rotate == FB_ROTATE_CW) ||
+ (screeninfo->rotate == FB_ROTATE_CCW)) {
+ GALLEN_DBGLOCAL_RUNLOG(0);
+ xres = screeninfo->yres;
+ yres = screeninfo->xres;
+ } else {
+ GALLEN_DBGLOCAL_RUNLOG(1);
+ xres = screeninfo->xres;
+ yres = screeninfo->yres;
+ }
+
+ /* Program EPDC update to process buffer */
+ epdc_set_update_addr(fb_data->phys_start);
+ epdc_set_update_coord(0, 0);
+ epdc_set_update_dimensions(xres, yres);
+ if (fb_data->rev > 20) {
+ GALLEN_DBGLOCAL_RUNLOG(2);
+ epdc_set_update_stride(0);
+ }
+ epdc_submit_update(0, fb_data->wv_modes.mode_init, UPDATE_MODE_FULL,
+ false, false, true, 0xFF);
+
+ dev_dbg(fb_data->dev, "Mode0 update - Waiting for LUT to complete...\n");
+
+ /* Will timeout after ~4-5 seconds */
+
+ for (i = 0; i < 40; i++) {
+ if (!epdc_is_lut_active(0)) {
+ dev_dbg(fb_data->dev, "Mode0 init complete\n");
+ GALLEN_DBGLOCAL_ESC();
+ return;
+ }
+ msleep(100);
+ }
+
+ dev_err(fb_data->dev, "Mode0 init failed!\n");
+
+ GALLEN_DBGLOCAL_END();
+ return;
+}
+
+
+static void mxc_epdc_fb_fw_handler(const struct firmware *fw,
+ void *context)
+{
+ struct mxc_epdc_fb_data *fb_data = context;
+ int ret;
+ struct mxcfb_waveform_data_file *wv_file;
+ int wv_data_offs;
+ int i;
+ struct mxcfb_update_data update;
+ struct mxcfb_update_marker_data upd_marker_data;
+ struct fb_var_screeninfo *screeninfo = &fb_data->epdc_fb_var;
+ u32 xres, yres;
+ struct clk *epdc_parent;
+ unsigned long rounded_parent_rate, epdc_pix_rate,
+ rounded_pix_clk, target_pix_clk;
+
+// unsigned char bModeVersion ;
+// unsigned char bWFM_REV ;
+
+#ifdef FW_IN_RAM //[
+ struct firmware ram_fw;
+#endif //] FW_IN_RAM
+
+ GALLEN_DBGLOCAL_BEGIN();
+ //msleep(10);// GALLEN DBG
+
+ if (fw == NULL) {
+ GALLEN_DBGLOCAL_RUNLOG(0);
+
+ /* If default FW file load failed, we give up */
+ if (fb_data->fw_default_load) {
+ GALLEN_DBGLOCAL_ESC();
+ return;
+ }
+
+ /* Try to load default waveform */
+ dev_dbg(fb_data->dev,
+ "Can't find firmware. Trying fallback fw\n");
+ fb_data->fw_default_load = true;
+#ifdef FW_IN_RAM//[ gallen modify 20110609 : waveform pass from bootloader in RAM .
+ {
+
+ GALLEN_DBGLOCAL_RUNLOG(2);
+
+ ram_fw.size = gdwWF_size;
+ ram_fw.data = (u8*)gpbWF_vaddr;
+ //fw.page = 0;
+ printk("[%s]:fw p=%p,size=%u\n",__FUNCTION__,ram_fw.data,ram_fw.size);
+ //mxc_epdc_fb_fw_handler(&fw,fb_data);
+ ret = 0;
+ fw=&ram_fw;
+ }
+#else //][!FW_IN_RAM befrom modify ...
+
+
+ GALLEN_DBGLOCAL_RUNLOG(3);
+ ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+ "imx/epdc.fw", fb_data->dev, GFP_KERNEL, fb_data,
+ mxc_epdc_fb_fw_handler);
+ if (ret)
+ dev_err(fb_data->dev,
+ "Failed request_firmware_nowait err %d\n", ret);
+#endif //]FW_IN_RAM
+
+
+
+ GALLEN_DBGLOCAL_ESC();
+ return;
+ }
+
+
+ gbModeVersion = *(fw->data+0x10);
+ gbWFM_REV = *(fw->data+0x16);
+ gbFPL_Platform=*(fw->data+0x0d);
+
+
+ if((0x20==gbModeVersion)||(0x19==gbModeVersion)||(0x18==gbModeVersion))
+ {
+ GALLEN_DBGLOCAL_RUNLOG(6);
+ fb_data->wv_modes.mode_gl16 = 3; /* GL16 mode */
+ fb_data->wv_modes.mode_a2 = 6; /* A2 mode */
+ if(0x18==gbModeVersion) {
+ fb_data->wv_modes.mode_gc4 = 2; /* GC4 mode */
+ }
+ else {
+ fb_data->wv_modes.mode_gc4 = 7; /* GC4 mode */
+ }
+ fb_data->wv_modes.mode_du = 1; /* DU mode */
+ fb_data->wv_modes.mode_aa = 4; /* REAGL mode */
+ fb_data->wv_modes.mode_aad = 5; /*REAGL-D mode */
+ }
+ else if(0x13==gbModeVersion) {
+ fb_data->wv_modes.mode_du = 1; /* DU mode */
+ fb_data->wv_modes.mode_a2 = 4; /* A2 mode */
+ fb_data->wv_modes.mode_gl16 = 3; /* GL16 mode as GLR16*/
+ fb_data->wv_modes.mode_aa = 3; /* REAGL mode as GLR16*/
+ fb_data->wv_modes.mode_aad = 2; /*REAGL-D mode as GC16*/
+ }
+ else if(0x23==gbModeVersion) {
+ // AD type non regal waveform .
+ fb_data->wv_modes.mode_du = 1; /* DU mode */
+ fb_data->wv_modes.mode_a2 = 4; /* A2 mode */
+ fb_data->wv_modes.mode_gl16 = 3; /* GL16 mode */
+ fb_data->wv_modes.mode_aa = 3; /* REAGL mode as GLR16*/
+ fb_data->wv_modes.mode_aad = 2; /*REAGL-D mode as GC16*/
+ }
+ else if(0x4==gbModeVersion) {
+ fb_data->wv_modes.mode_gl16 = 5; /* GL16 mode */
+ fb_data->wv_modes.mode_aa = 5; /* REAGL mode */
+ fb_data->wv_modes.mode_aad = 5; /* REAGL-D mode */
+ fb_data->wv_modes.mode_a2 = 4; /* A2 mode */
+ }
+ else if(0x15==gbModeVersion||0x9==gbModeVersion) {
+ // WY type .
+ fb_data->wv_modes.mode_gl16 = 3; /* GL16 mode */
+ fb_data->wv_modes.mode_aa = 2; /* REAGL mode */
+ fb_data->wv_modes.mode_aad = 2; /* REAGL-D mode */
+ fb_data->wv_modes.mode_a2 = 4; /* A2 mode */
+
+ // when mode version is 0x15
+ // GC4=5
+ // GL4=6
+
+ }
+ else {
+ fb_data->wv_modes.mode_gl16 = 2; /* GL16 mode */
+ fb_data->wv_modes.mode_aa = 2; /* REAGL mode */
+ fb_data->wv_modes.mode_aad = 2; /* REAGL-D mode */
+ fb_data->wv_modes.mode_a2 = 1; /* A2 mode as DU */
+ }
+#ifdef NO_CUS_REAGL_MODE//[
+ fb_data->wv_modes.mode_aa = fb_data->wv_modes.mode_gl16; /* REAGL mode */
+ fb_data->wv_modes.mode_aad = fb_data->wv_modes.mode_gc16; /* REAGL-D mode */
+#endif //]NO_CUS_REAGL_MODE
+ fb_data->wv_modes_update = true;
+
+ giNTX_waveform_modeA[NTX_WFM_MODE_INIT] = fb_data->wv_modes.mode_init;
+ giNTX_waveform_modeA[NTX_WFM_MODE_DU] = fb_data->wv_modes.mode_du;
+ giNTX_waveform_modeA[NTX_WFM_MODE_GC16] = fb_data->wv_modes.mode_gc16;
+ giNTX_waveform_modeA[NTX_WFM_MODE_GC4] = fb_data->wv_modes.mode_gc4;
+ giNTX_waveform_modeA[NTX_WFM_MODE_A2] = fb_data->wv_modes.mode_a2;
+ giNTX_waveform_modeA[NTX_WFM_MODE_GL16] = fb_data->wv_modes.mode_gl16;
+ giNTX_waveform_modeA[NTX_WFM_MODE_GLR16] = fb_data->wv_modes.mode_aa;
+ giNTX_waveform_modeA[NTX_WFM_MODE_GLD16] = fb_data->wv_modes.mode_aad;
+
+
+
+ wv_file = (struct mxcfb_waveform_data_file *)fw->data;
+
+ if(1==gptHWCFG->m_val.bDisplayResolution) {
+ printk("%s(%d):EPD 1024x758 \n",__FILE__,__LINE__);
+ fb_data->cur_mode = &fb_data->pdata->epdc_mode[2];//
+ }
+ else if(3==gptHWCFG->m_val.bDisplayResolution) {
+ printk("%s(%d):EPD 1440x1080 \n",__FILE__,__LINE__);
+ fb_data->cur_mode = &fb_data->pdata->epdc_mode[5];//
+ }
+ else if(5==gptHWCFG->m_val.bDisplayResolution) {
+ printk("%s(%d):EPD 1448x1072 \n",__FILE__,__LINE__);
+ fb_data->cur_mode = &fb_data->pdata->epdc_mode[6];//
+ }
+ else if(6==gptHWCFG->m_val.bDisplayResolution) {
+ printk("%s(%d):EPD 1600x1200 \n",__FILE__,__LINE__);
+ fb_data->cur_mode = &fb_data->pdata->epdc_mode[7];//
+ }
+ else if(2==gptHWCFG->m_val.bDisplayResolution) {
+ printk("%s(%d):EPD 1024x768 \n",__FILE__,__LINE__);
+ fb_data->cur_mode = &fb_data->pdata->epdc_mode[3];//
+ }
+ else
+ {
+ unsigned char *pbWFHdr = (unsigned char *)(fw->data);
+ //if(*(pbWFHdr+0x15)==0xd5)// AMEPD is ED060SCQ .
+ if(gptHWCFG->m_val.bPCB>=46)
+ {
+ printk("%s(%d):using parameters of ED060SCQ \n",__FILE__,__LINE__);
+ fb_data->cur_mode = &fb_data->pdata->epdc_mode[8];// .
+ }
+ else {
+ switch (*(pbWFHdr+0x0d)) {// FPL platform detect .
+ case 0x06: // V220 .
+ case 0x09: // V320 .
+ if(27==gptHWCFG->m_val.bPCB) {
+ printk("%s(%d):V220 for E50610 FPL platform \n",__FILE__,__LINE__);
+ fb_data->cur_mode = &fb_data->pdata->epdc_mode[4];//v220 parameters for E50612 .
+ }
+ else {
+ printk("%s(%d):V220 FPL platform \n",__FILE__,__LINE__);
+ fb_data->cur_mode = &fb_data->pdata->epdc_mode[0];//v220 parameters
+ }
+ break;
+ case 0x03: // V110 .
+ case 0x04: // V110A .
+ printk("%s(%d):V110/V110A FPL platform \n",__FILE__,__LINE__);
+ ASSERT(fb_data->pdata->num_modes>1);
+ fb_data->cur_mode = &fb_data->pdata->epdc_mode[1];//v110 parameters
+ break;
+ default:
+ ERR_MSG("%s(%d):new FPL platform=0x%x ,please modify source to support this \n",__FILE__,__LINE__,*(pbWFHdr+0x0d));
+ break;
+ }
+ }
+ }
+
+
+ //msleep(10);// GALLEN DBG .
+
+ /* Get size and allocate temperature range table */
+ fb_data->trt_entries = wv_file->wdh.trc + 1;
+ fb_data->temp_range_bounds = kzalloc(fb_data->trt_entries+1, GFP_KERNEL);
+
+ /* Copy TRT data */
+ memcpy(fb_data->temp_range_bounds, &wv_file->data, fb_data->trt_entries+1);
+
+ for (i = 0; i <= fb_data->trt_entries; i++) {
+ GALLEN_DBGLOCAL_RUNLOG(4);
+ //dev_dbg(fb_data->dev, "trt entry #%d = 0x%x\n", i, *((u8 *)&wv_file->data + i));
+ DBG_MSG("trt entry #%d = 0x%x(%d~%d)\n",
+ i, *((u8 *)&wv_file->data + i),fb_data->temp_range_bounds[i],fb_data->temp_range_bounds[i+1]-1);
+ }
+
+
+ /* Set default temperature index using TRT and room temp */
+ fb_data->temp_index = mxc_epdc_fb_get_temp_index(fb_data, DEFAULT_TEMP);
+
+ /* Get offset and size for waveform data */
+ wv_data_offs = sizeof(wv_file->wdh) + fb_data->trt_entries + 1;
+ fb_data->waveform_buffer_size = fw->size - wv_data_offs;
+
+ /* process 2nd generation waveform data which may contain
+ * the voltage control data, advance waveform data,
+ * and extra waveform data
+ */
+
+ {
+ int awv, wmc, wtrc, xwia;
+ u64 longOffset;
+ u32 bufferSize;
+ u8 *fwDataBuffer = (u8 *)(fw->data) + wv_data_offs;
+ u8 *waveform_xwi_buffer = NULL;
+
+ wtrc = wv_file->wdh.trc + 1;
+ wmc = wv_file->wdh.mc + 1;
+ awv = wv_file->wdh.awv;
+ xwia = wv_file->wdh.xwia;
+ memcpy (&longOffset,fwDataBuffer,8);
+// printk("%s() : awv=0x%02X\n",__FUNCTION__,awv);
+ if ((unsigned) longOffset > (8*wmc))
+ {
+ u64 avcOffset, acdOffset, acdMagic, xwiOffset;
+ avcOffset = acdOffset = acdMagic = xwiOffset = 0l;
+ /* look at the advance waveform flags */
+ switch ( awv ) {
+ case 0 : /* voltage control flag is set */
+ if (xwia > 0) {
+ /* extra waveform information */
+ memcpy (&xwiOffset,fwDataBuffer + (8*wmc),8);
+ bufferSize = (unsigned)(fb_data->waveform_buffer_size - xwiOffset);
+ fb_data->waveform_xwi_buffer = kmalloc(bufferSize, GFP_KERNEL);
+ memcpy(fb_data->waveform_xwi_buffer, fwDataBuffer+xwiOffset, bufferSize );
+ fb_data->waveform_buffer_size = xwiOffset;
+ }
+ break;
+ case 1 : /* voltage control flag is set */
+ memcpy (&avcOffset,fwDataBuffer + (8*wmc),8);
+ if (xwia > 0) {
+ /* extra waveform information */
+ memcpy (&xwiOffset,fwDataBuffer + (8*wmc) +8,8);
+ bufferSize = (unsigned)(fb_data->waveform_buffer_size - xwiOffset);
+ fb_data->waveform_xwi_buffer = kmalloc(bufferSize, GFP_KERNEL);
+ /* voltage control data */
+ memcpy(fb_data->waveform_xwi_buffer, fwDataBuffer+xwiOffset, bufferSize );
+ bufferSize = (unsigned)(xwiOffset - avcOffset);
+ fb_data->waveform_vcd_buffer = kmalloc(bufferSize, GFP_KERNEL);
+ memcpy(fb_data->waveform_vcd_buffer, fwDataBuffer+avcOffset, bufferSize );
+ }
+ else {
+ /* voltage control data */
+ bufferSize = (unsigned)(fb_data->waveform_buffer_size - avcOffset);
+ fb_data->waveform_vcd_buffer = kmalloc(bufferSize, GFP_KERNEL);
+ memcpy(fb_data->waveform_vcd_buffer, fwDataBuffer+avcOffset, bufferSize );
+ }
+ fb_data->waveform_buffer_size = avcOffset;
+ break;
+ case 2 : /* voltage control flag is set */
+ memcpy (&acdOffset,fwDataBuffer + (8*wmc),8);
+ memcpy (&acdMagic,fwDataBuffer + (8*wmc) + 8,8);
+ if (xwia > 0) {
+ /* extra waveform information */
+ memcpy (&xwiOffset,fwDataBuffer + (8*wmc) + 16,8);
+ bufferSize = (unsigned)(fb_data->waveform_buffer_size - xwiOffset);
+ fb_data->waveform_xwi_buffer = kmalloc(bufferSize, GFP_KERNEL);
+ memcpy(fb_data->waveform_xwi_buffer, fwDataBuffer+xwiOffset, bufferSize );
+ /* algorithm control data */
+ bufferSize = (unsigned)(xwiOffset - acdOffset);
+ fb_data->waveform_acd_buffer = kmalloc(bufferSize, GFP_KERNEL);
+ memcpy(fb_data->waveform_acd_buffer, fwDataBuffer+acdOffset, bufferSize );
+ }
+ else {
+ /* algorithm control data */
+ bufferSize = (unsigned)(fb_data->waveform_buffer_size - acdOffset);
+ fb_data->waveform_acd_buffer = kmalloc(bufferSize, GFP_KERNEL);
+ memcpy(fb_data->waveform_acd_buffer, fwDataBuffer+acdOffset, bufferSize );
+ }
+ fb_data->waveform_buffer_size = acdOffset;
+ break;
+ case 3 : /* voltage control flag is set */
+ memcpy (&avcOffset,fwDataBuffer + (8*wmc),8);
+ memcpy (&acdOffset,fwDataBuffer + (8*wmc) + 8,8);
+ memcpy (&acdMagic,fwDataBuffer + (8*wmc) + 16,8);
+ if (xwia > 0) {
+ /* extra waveform information */
+ memcpy (&xwiOffset,fwDataBuffer + (8*wmc) + 24,8);
+ bufferSize = (unsigned)(fb_data->waveform_buffer_size - xwiOffset);
+ fb_data->waveform_xwi_buffer = kmalloc(bufferSize, GFP_KERNEL);
+ memcpy(fb_data->waveform_xwi_buffer, fwDataBuffer+xwiOffset, bufferSize );
+ /* algorithm control data */
+ bufferSize = (unsigned)(xwiOffset - acdOffset);
+ fb_data->waveform_acd_buffer = kmalloc(bufferSize, GFP_KERNEL);
+ memcpy(fb_data->waveform_acd_buffer, fwDataBuffer+acdOffset, bufferSize );
+ /* voltage control data */
+ bufferSize = (unsigned)(acdOffset - avcOffset);
+ fb_data->waveform_vcd_buffer = kmalloc(bufferSize, GFP_KERNEL);
+ memcpy(fb_data->waveform_vcd_buffer, fwDataBuffer+avcOffset, bufferSize );
+ }
+ else {
+ /* algorithm control data */
+ bufferSize = (unsigned)(fb_data->waveform_buffer_size - acdOffset);
+ fb_data->waveform_acd_buffer = kmalloc(bufferSize, GFP_KERNEL);
+ memcpy(fb_data->waveform_acd_buffer, fwDataBuffer+avcOffset, bufferSize );
+ /* voltage control data */
+ bufferSize = (unsigned)(acdOffset - avcOffset);
+ fb_data->waveform_vcd_buffer = kmalloc(bufferSize, GFP_KERNEL);
+ memcpy(fb_data->waveform_vcd_buffer, fwDataBuffer+avcOffset, bufferSize );
+ }
+ fb_data->waveform_buffer_size = avcOffset;
+ break;
+ }
+ if (acdMagic) fb_data->waveform_magic_number = acdMagic;
+ /* store the waveform mode count and waveform temperature range count
+ */
+ fb_data->waveform_mc = wmc;
+ fb_data->waveform_trc =wtrc;
+ }
+ }
+
+ /* get the extra waveform info and display it - This can be removed! It is here for illustration only */
+ if (fb_data->waveform_xwi_buffer) {
+ //char *xwiString;
+ unsigned strLength = mxc_epdc_fb_fetch_wxi_data(fb_data->waveform_xwi_buffer, NULL);
+
+ dev_info(fb_data->dev, " --- Extra Waveform Data length: %d bytes---\n",strLength);
+ if (strLength > 0) {
+ //xwiString = (char *) kmalloc(strLength + 1, GFP_KERNEL);
+ fb_data->waveform_xwi_string = (char *) kmalloc(strLength + 1, GFP_KERNEL);
+ if (mxc_epdc_fb_fetch_wxi_data(fb_data->waveform_xwi_buffer, fb_data->waveform_xwi_string) > 0)
+ {
+ fb_data->waveform_xwi_string[strLength+1] = '\0';
+ fb_data->waveform_xwi_string_length = strLength;
+ //xwiString[strLength+1] = '\0';
+ dev_info(fb_data->dev, " Extra Waveform Data: %s\n",fb_data->waveform_xwi_string);
+ }
+ else
+ dev_err(fb_data->dev, " *** Extra Waveform Data checksum error ***\n");
+
+ //kfree(xwiString);
+ }
+ }
+
+ /* show the vcd */
+ if (fb_data->waveform_vcd_buffer) {
+ {
+ struct epd_vc_data vcd;
+ /* fetch and display the voltage control data for waveform mode 0, temp range 0 */
+ fetch_Epdc_Pmic_Voltages(&vcd, fb_data, 0, 0);
+ }
+ }
+
+ /* Allocate memory for waveform data */
+ fb_data->waveform_buffer_virt = dma_alloc_coherent(fb_data->dev,
+ fb_data->waveform_buffer_size,
+ &fb_data->waveform_buffer_phys,
+ GFP_DMA);
+ if (fb_data->waveform_buffer_virt == NULL) {
+ dev_err(fb_data->dev, "Can't allocate mem for waveform!\n");
+ GALLEN_DBGLOCAL_ESC();
+ return;
+ }
+
+ DBG_MSG("[%s]waveform:vir_p=%p,phy_p=%p,sz=%d,trt_entries=%d\n",__FUNCTION__,\
+ fb_data->waveform_buffer_virt,fb_data->waveform_buffer_phys,\
+ fb_data->waveform_buffer_size,fb_data->trt_entries);
+
+ memcpy(fb_data->waveform_buffer_virt, (u8 *)(fw->data) + wv_data_offs,
+ fb_data->waveform_buffer_size);
+
+ /* Read field to determine if 4-bit or 5-bit mode */
+ if ((wv_file->wdh.luts & 0xC) == 0x4)
+ fb_data->buf_pix_fmt = EPDC_FORMAT_BUF_PIXEL_FORMAT_P5N;
+ else
+ fb_data->buf_pix_fmt = EPDC_FORMAT_BUF_PIXEL_FORMAT_P4N;
+
+#ifdef FW_IN_RAM//[
+#else//][!FW_IN_RAM
+
+ release_firmware(fw);
+#endif //]FW_IN_RAM
+
+
+ /* Enable clocks to access EPDC regs */
+ clk_enable(fb_data->epdc_clk_axi);
+
+ target_pix_clk = fb_data->cur_mode->vmode->pixclock;
+ /* Enable pix clk for EPDC */
+ clk_enable(fb_data->epdc_clk_pix);
+ rounded_pix_clk = clk_round_rate(fb_data->epdc_clk_pix, target_pix_clk);
+
+ if (((rounded_pix_clk >= target_pix_clk + target_pix_clk/100) ||
+ (rounded_pix_clk <= target_pix_clk - target_pix_clk/100))) {
+ GALLEN_DBGLOCAL_RUNLOG(5);
+ /* Can't get close enough without changing parent clk */
+ epdc_parent = clk_get_parent(fb_data->epdc_clk_pix);
+ rounded_parent_rate = clk_round_rate(epdc_parent, target_pix_clk);
+
+ epdc_pix_rate = target_pix_clk;
+ while (epdc_pix_rate < rounded_parent_rate) {
+ GALLEN_DBGLOCAL_RUNLOG(6);
+ epdc_pix_rate *= 2;
+ }
+ clk_set_rate(epdc_parent, epdc_pix_rate);
+
+ rounded_pix_clk = clk_round_rate(fb_data->epdc_clk_pix, target_pix_clk);
+ if (((rounded_pix_clk >= target_pix_clk + target_pix_clk/100) ||
+ (rounded_pix_clk <= target_pix_clk - target_pix_clk/100))) {
+ /* Still can't get a good clock, provide warning */
+ dev_err(fb_data->dev, "Unable to get an accurate EPDC pix clk"
+ "desired = %lu, actual = %lu\n", target_pix_clk,
+ rounded_pix_clk);
+
+ GALLEN_DBGLOCAL_RUNLOG(7);
+ }
+ }
+
+ clk_set_rate(fb_data->epdc_clk_pix, rounded_pix_clk);
+
+ epdc_init_sequence(fb_data);
+
+ /* Disable clocks */
+ clk_disable(fb_data->epdc_clk_axi);
+ clk_disable(fb_data->epdc_clk_pix);
+
+ fb_data->hw_ready = true;
+ fb_data->hw_initializing = false;
+
+ /* Use unrotated (native) width/height */
+ if ((screeninfo->rotate == FB_ROTATE_CW) ||
+ (screeninfo->rotate == FB_ROTATE_CCW)) {
+
+ GALLEN_DBGLOCAL_RUNLOG(8);
+ xres = screeninfo->yres;
+ yres = screeninfo->xres;
+ } else {
+ GALLEN_DBGLOCAL_RUNLOG(9);
+ xres = screeninfo->xres;
+ yres = screeninfo->yres;
+ }
+
+#if 0 //[ gallen remove .
+
+ update.update_region.left = 0;
+ update.update_region.width = xres;
+ update.update_region.top = 0;
+ update.update_region.height = yres;
+ update.update_mode = UPDATE_MODE_FULL;
+ update.waveform_mode = WAVEFORM_MODE_AUTO;
+ update.update_marker = INIT_UPDATE_MARKER;
+ update.temp = TEMP_USE_AMBIENT;
+ update.flags = 0;
+
+ upd_marker_data.update_marker = update.update_marker;
+
+ mxc_epdc_fb_send_update(&update, &fb_data->info);
+
+ /* Block on initial update */
+ ret = mxc_epdc_fb_wait_update_complete(&upd_marker_data,
+ &fb_data->info);
+ if (ret < 0)
+ dev_err(fb_data->dev,
+ "Wait for initial update complete failed."
+ " Error = 0x%x", ret);
+#endif //]
+
+ //msleep(10);// GALLEN DBG .
+ k_fake_s1d13522_init((unsigned char *)gpbLOGO_vaddr);
+ //k_fake_s1d13522_init(0);
+ GALLEN_DBGLOCAL_END();
+}
+
+static int mxc_epdc_fb_init_hw(struct fb_info *info)
+{
+ struct mxc_epdc_fb_data *fb_data = (struct mxc_epdc_fb_data *)info;
+ int ret;
+
+ /*
+ * Create fw search string based on ID string in selected videomode.
+ * Format is "imx/epdc_[panel string].fw"
+ */
+ if (fb_data->cur_mode) {
+ strcat(fb_data->fw_str, "imx/epdc_");
+ strcat(fb_data->fw_str, fb_data->cur_mode->vmode->name);
+ strcat(fb_data->fw_str, ".fw");
+ }
+
+ fb_data->fw_default_load = false;
+#ifdef FW_IN_RAM//[ gallen modify 20110609 : waveform pass from bootloader in RAM .
+ #if 1
+ //queue_work(fb_data->epdc_firmware_workqueue,&fb_data->epdc_firmware_work);
+ schedule_delayed_work(&fb_data->epdc_firmware_work,0);
+
+ ret = 0;
+ #else
+ {
+
+ struct firmware fw;
+
+ fw.size = gdwWF_size;
+ fw.data = gpbWF_vaddr;
+ //fw.page = 0;
+ printk("[%s]:fw p=%p,size=%u\n",__FUNCTION__,fw.data,fw.size);
+ mxc_epdc_fb_fw_handler(&fw,fb_data);
+ ret = 0;
+ }
+ #endif
+#else //][ !FW_IN_RAM befrom modify ...
+
+ ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+ fb_data->fw_str, fb_data->dev, GFP_KERNEL,
+ fb_data, mxc_epdc_fb_fw_handler);
+ if (ret)
+ dev_dbg(fb_data->dev,
+ "Failed request_firmware_nowait err %d\n", ret);
+#endif //] FW_IN_RAM
+ return ret;
+}
+
+static ssize_t store_update(struct device *device,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mxcfb_update_data update;
+ struct fb_info *info = dev_get_drvdata(device);
+ struct mxc_epdc_fb_data *fb_data = (struct mxc_epdc_fb_data *)info;
+
+ if (strncmp(buf, "direct", 6) == 0)
+ update.waveform_mode = fb_data->wv_modes.mode_du;
+ else if (strncmp(buf, "gc16", 4) == 0)
+ update.waveform_mode = fb_data->wv_modes.mode_gc16;
+ else if (strncmp(buf, "gc4", 3) == 0)
+ update.waveform_mode = fb_data->wv_modes.mode_gc4;
+
+ /* Now, request full screen update */
+ update.update_region.left = 0;
+ update.update_region.width = fb_data->epdc_fb_var.xres;
+ update.update_region.top = 0;
+ update.update_region.height = fb_data->epdc_fb_var.yres;
+ update.update_mode = UPDATE_MODE_FULL;
+ update.temp = TEMP_USE_AMBIENT;
+ update.update_marker = 0;
+ update.flags = 0;
+
+ mxc_epdc_fb_send_update(&update, info);
+
+ return count;
+}
+
+static struct device_attribute fb_attrs[] = {
+ __ATTR(update, S_IRUGO|S_IWUSR, NULL, store_update),
+};
+
+int __devinit mxc_epdc_fb_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct mxc_epdc_fb_data *fb_data;
+ struct resource *res;
+ struct fb_info *info;
+ char *options, *opt;
+ char *panel_str = NULL;
+ char name[] = "mxcepdcfb";
+ struct fb_videomode *vmode;
+ int xres_virt, yres_virt, buf_size;
+ int xres_virt_rot, yres_virt_rot, pix_size_rot;
+ struct fb_var_screeninfo *var_info;
+ struct fb_fix_screeninfo *fix_info;
+ struct pxp_config_data *pxp_conf;
+ struct pxp_proc_data *proc_data;
+ struct scatterlist *sg;
+ struct update_data_list *upd_list;
+ struct update_data_list *plist, *temp_list;
+ int i;
+ unsigned long x_mem_size = 0;
+ u32 val;
+
+ fb_data = (struct mxc_epdc_fb_data *)framebuffer_alloc(
+ sizeof(struct mxc_epdc_fb_data), &pdev->dev);
+ if (fb_data == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+
+ /* Get platform data and check validity */
+ fb_data->pdata = pdev->dev.platform_data;
+ if ((fb_data->pdata == NULL) || (fb_data->pdata->num_modes < 1)
+ || (fb_data->pdata->epdc_mode == NULL)
+ || (fb_data->pdata->epdc_mode->vmode == NULL)) {
+ ret = -EINVAL;
+ goto out_fbdata;
+ }
+
+ DBG_MSG("[%s]epdc_fb_data@%p size=%d,pdata@%p\n",
+ __FUNCTION__,fb_data,sizeof(struct mxc_epdc_fb_data),
+ fb_data->pdata);
+
+ if (fb_get_options(name, &options)) {
+ ret = -ENODEV;
+ goto out_fbdata;
+ }
+
+ fb_data->tce_prevent = 1;
+
+ if (options)
+ while ((opt = strsep(&options, ",")) != NULL) {
+ if (!*opt)
+ continue;
+
+ if (!strncmp(opt, "bpp=", 4))
+ fb_data->default_bpp =
+ simple_strtoul(opt + 4, NULL, 0);
+ else if (!strncmp(opt, "x_mem=", 6))
+ x_mem_size = memparse(opt + 6, NULL);
+ else if (!strncmp(opt, "tce_prevent", 11))
+ fb_data->tce_prevent = 1;
+ else if (panel_str == NULL)
+ panel_str = opt;
+ else dev_err(fb_data->dev, " --> invalid epdc options - %s\n", opt);
+ }
+
+ fb_data->dev = &pdev->dev;
+
+ if (!fb_data->default_bpp) {
+ fb_data->default_bpp = default_bpp;
+ }
+
+ /* Set default (first defined mode) before searching for a match */
+ if(1==gptHWCFG->m_val.bDisplayResolution) {
+ printk("%s(%d):EPD 1024x758 \n",__FILE__,__LINE__);
+ fb_data->cur_mode = &fb_data->pdata->epdc_mode[2];//
+ }
+ else if(3==gptHWCFG->m_val.bDisplayResolution) {
+ printk("%s(%d):EPD 1440x1080 \n",__FILE__,__LINE__);
+ fb_data->cur_mode = &fb_data->pdata->epdc_mode[5];//
+ }
+ else if(5==gptHWCFG->m_val.bDisplayResolution) {
+ printk("%s(%d):EPD 1448x1072 \n",__FILE__,__LINE__);
+ fb_data->cur_mode = &fb_data->pdata->epdc_mode[6];//
+ }
+ else if(6==gptHWCFG->m_val.bDisplayResolution) {
+ printk("%s(%d):EPD 1600x1200 \n",__FILE__,__LINE__);
+ fb_data->cur_mode = &fb_data->pdata->epdc_mode[7];//
+ }
+ else if(2==gptHWCFG->m_val.bDisplayResolution) {
+ printk("%s(%d):EPD 1024x768 \n",__FILE__,__LINE__);
+ fb_data->cur_mode = &fb_data->pdata->epdc_mode[3];//
+ }
+ else {
+ fb_data->cur_mode = &fb_data->pdata->epdc_mode[0];
+ }
+
+ if(panel_str) {
+ printk("-> requested panel string %s\n",panel_str);
+ for (i = 0; i < fb_data->pdata->num_modes; i++) {
+ printk("-> epdc modes[%d] - %s\n", i, fb_data->pdata->epdc_mode[i].vmode->name);
+
+ if (!strcmp(fb_data->pdata->epdc_mode[i].vmode->name,
+ panel_str)) {
+ fb_data->cur_mode =
+ &fb_data->pdata->epdc_mode[i];
+ break;
+ }
+ }
+ }
+
+ vmode = fb_data->cur_mode->vmode;
+
+ platform_set_drvdata(pdev, fb_data);
+ info = &fb_data->info;
+
+ /* Allocate color map for the FB */
+ ret = fb_alloc_cmap(&info->cmap, 256, 0);
+ if (ret)
+ goto out_fbdata;
+
+ dev_dbg(&pdev->dev, "resolution %dx%d, bpp %d\n",
+ vmode->xres, vmode->yres, fb_data->default_bpp);
+
+ DBG_MSG("resolution %dx%d,bpp %d\n",vmode->xres, vmode->yres, fb_data->default_bpp);
+ /*
+ * GPU alignment restrictions dictate framebuffer parameters:
+ * - 32-byte alignment for buffer width
+ * - 128-byte alignment for buffer height
+ * => 4K buffer alignment for buffer start
+ */
+ xres_virt = ALIGN(vmode->xres, 32);
+ yres_virt = ALIGN(vmode->yres, 128);
+ fb_data->max_pix_size = PAGE_ALIGN(xres_virt * yres_virt);
+
+ /*
+ * Have to check to see if aligned buffer size when rotated
+ * is bigger than when not rotated, and use the max
+ */
+ xres_virt_rot = ALIGN(vmode->yres, 32);
+ yres_virt_rot = ALIGN(vmode->xres, 128);
+ pix_size_rot = PAGE_ALIGN(xres_virt_rot * yres_virt_rot);
+ fb_data->max_pix_size = (fb_data->max_pix_size > pix_size_rot) ?
+ fb_data->max_pix_size : pix_size_rot;
+
+ buf_size = fb_data->max_pix_size * fb_data->default_bpp/8;
+
+ /* Compute the number of screens needed based on X memory requested */
+ if (x_mem_size > 0) {
+ fb_data->num_screens = DIV_ROUND_UP(x_mem_size, buf_size);
+ if (fb_data->num_screens < NUM_SCREENS_MIN)
+ fb_data->num_screens = NUM_SCREENS_MIN;
+ else if (buf_size * fb_data->num_screens > SZ_16M)
+ fb_data->num_screens = SZ_16M / buf_size;
+ } else
+ fb_data->num_screens = NUM_SCREENS_MIN;
+
+ fb_data->map_size = buf_size * fb_data->num_screens;
+ dev_dbg(&pdev->dev, "memory to allocate: %d\n", fb_data->map_size);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ ret = -ENODEV;
+ goto out_cmap;
+ }
+
+ epdc_base = ioremap(res->start, SZ_4K);
+ if (epdc_base == NULL) {
+ ret = -ENOMEM;
+ goto out_cmap;
+ }
+
+ /* Allocate FB memory */
+
+ if(0==gptHWCFG->m_val.bUIStyle) {
+ info->screen_base = dma_alloc_writecombine(&pdev->dev,
+ fb_data->map_size<<1,
+ &fb_data->phys_start,
+ GFP_DMA);
+ }
+ else {
+ info->screen_base = dma_alloc_writecombine(&pdev->dev,
+ fb_data->map_size,
+ &fb_data->phys_start,
+ GFP_DMA);
+ }
+
+ if (info->screen_base == NULL) {
+ ret = -ENOMEM;
+ goto out_mapregs;
+ }
+ dev_dbg(&pdev->dev, "allocated at %p:0x%x\n", info->screen_base,
+ fb_data->phys_start);
+
+ var_info = &info->var;
+ var_info->activate = FB_ACTIVATE_TEST;
+ var_info->bits_per_pixel = fb_data->default_bpp;
+ var_info->xres = vmode->xres;
+ var_info->yres = vmode->yres;
+ var_info->xres_virtual = xres_virt;
+ /* Additional screens allow for panning and buffer flipping */
+ var_info->yres_virtual = yres_virt * fb_data->num_screens;
+
+ //var_info->pixclock = vmode->pixclock;
+ // Should be (1000000000000LL/vmode->pixclock) but the kernel does not have long long division library!
+ var_info->pixclock = 1000000000/(vmode->pixclock/1000);
+
+ var_info->left_margin = vmode->left_margin;
+ var_info->right_margin = vmode->right_margin;
+ var_info->upper_margin = vmode->upper_margin;
+ var_info->lower_margin = vmode->lower_margin;
+ var_info->hsync_len = vmode->hsync_len;
+ var_info->vsync_len = vmode->vsync_len;
+ var_info->vmode = FB_VMODE_NONINTERLACED;
+
+ if (2 == gptHWCFG->m_val.bUIStyle) { //Android
+ if (5 == gptHWCFG->m_val.bDisplayPanel || 10 == gptHWCFG->m_val.bDisplayPanel || 12 == gptHWCFG->m_val.bDisplayPanel) { //6 top, 6.8 bottom, 13.3 left
+ var_info->rotate = FB_ROTATE_UD;
+ }
+ }
+
+ switch (fb_data->default_bpp) {
+ case 32:
+ case 24:
+ var_info->red.offset = 16;
+ var_info->red.length = 8;
+ var_info->green.offset = 8;
+ var_info->green.length = 8;
+ var_info->blue.offset = 0;
+ var_info->blue.length = 8;
+ break;
+
+ case 16:
+ var_info->red.offset = 11;
+ var_info->red.length = 5;
+ var_info->green.offset = 5;
+ var_info->green.length = 6;
+ var_info->blue.offset = 0;
+ var_info->blue.length = 5;
+ break;
+
+ case 8:
+ /*
+ * For 8-bit grayscale, R, G, and B offset are equal.
+ *
+ */
+ var_info->grayscale = GRAYSCALE_8BIT;
+
+ var_info->red.length = 8;
+ var_info->red.offset = 0;
+ var_info->red.msb_right = 0;
+ var_info->green.length = 8;
+ var_info->green.offset = 0;
+ var_info->green.msb_right = 0;
+ var_info->blue.length = 8;
+ var_info->blue.offset = 0;
+ var_info->blue.msb_right = 0;
+ break;
+
+ default:
+ dev_err(&pdev->dev, "unsupported bitwidth %d\n",
+ fb_data->default_bpp);
+ ret = -EINVAL;
+ goto out_dma_fb;
+ }
+
+ fix_info = &info->fix;
+
+ strcpy(fix_info->id, "mxc_epdc_fb");
+ fix_info->type = FB_TYPE_PACKED_PIXELS;
+ fix_info->visual = FB_VISUAL_TRUECOLOR;
+ fix_info->xpanstep = 0;
+ fix_info->ypanstep = 0;
+ fix_info->ywrapstep = 0;
+ fix_info->accel = FB_ACCEL_NONE;
+ fix_info->smem_start = fb_data->phys_start;
+ fix_info->smem_len = fb_data->map_size;
+ fix_info->ypanstep = 0;
+
+ fb_data->native_width = vmode->xres;
+ fb_data->native_height = vmode->yres;
+
+ info->fbops = &mxc_epdc_fb_ops;
+
+ //DBG_MSG("%s():info@%p,info->fbops@%p,info->fbops->fbmmap@%p\n",
+ // __FUNCTION__,info,info->fbops,info->fbops->fb_mmap);
+ info->var.activate = FB_ACTIVATE_NOW;
+ info->pseudo_palette = fb_data->pseudo_palette;
+ info->screen_size = info->fix.smem_len;
+ info->flags = FBINFO_FLAG_DEFAULT;
+
+ mxc_epdc_fb_set_fix(info);
+
+ fb_data->auto_mode = AUTO_UPDATE_MODE_REGION_MODE;
+ fb_data->upd_scheme = UPDATE_SCHEME_QUEUE_AND_MERGE;
+ //fb_data->upd_scheme = UPDATE_SCHEME_SNAPSHOT;
+
+ /* Initialize our internal copy of the screeninfo */
+ fb_data->epdc_fb_var = *var_info;
+ fb_data->fb_offset = 0;
+ fb_data->eof_sync_period = 0;
+
+ fb_data->epdc_clk_axi = clk_get(fb_data->dev, "epdc_axi");
+ if (IS_ERR(fb_data->epdc_clk_axi)) {
+ dev_err(&pdev->dev, "Unable to get EPDC AXI clk."
+ "err = 0x%x\n", (int)fb_data->epdc_clk_axi);
+ ret = -ENODEV;
+ goto out_dma_fb;
+ }
+ fb_data->epdc_clk_pix = clk_get(fb_data->dev, "epdc_pix");
+ if (IS_ERR(fb_data->epdc_clk_pix)) {
+ dev_err(&pdev->dev, "Unable to get EPDC pix clk."
+ "err = 0x%x\n", (int)fb_data->epdc_clk_pix);
+ ret = -ENODEV;
+ goto out_dma_fb;
+ }
+
+ clk_enable(fb_data->epdc_clk_axi);
+ val = __raw_readl(EPDC_VERSION);
+ clk_disable(fb_data->epdc_clk_axi);
+ fb_data->rev = ((val & EPDC_VERSION_MAJOR_MASK) >>
+ EPDC_VERSION_MAJOR_OFFSET) * 10
+ + ((val & EPDC_VERSION_MINOR_MASK) >>
+ EPDC_VERSION_MINOR_OFFSET);
+ dev_dbg(&pdev->dev, "EPDC version = %d\n", fb_data->rev);
+
+ if (fb_data->rev < 20) {
+ fb_data->num_luts = EPDC_V1_NUM_LUTS;
+ fb_data->max_num_updates = EPDC_V1_MAX_NUM_UPDATES;
+ } else {
+ fb_data->num_luts = EPDC_V2_NUM_LUTS;
+ fb_data->max_num_updates = EPDC_V2_MAX_NUM_UPDATES;
+ if (vmode->xres > EPDC_V2_MAX_UPDATE_WIDTH)
+ fb_data->restrict_width = true;
+ }
+ fb_data->max_num_buffers = EPDC_MAX_NUM_BUFFERS;
+
+ /*
+ * Initialize lists for pending updates,
+ * active update requests, update collisions,
+ * and freely available updates.
+ */
+ INIT_LIST_HEAD(&fb_data->upd_pending_list);
+ INIT_LIST_HEAD(&fb_data->upd_buf_queue);
+ INIT_LIST_HEAD(&fb_data->upd_buf_free_list);
+ INIT_LIST_HEAD(&fb_data->upd_buf_collision_list);
+
+ /* Allocate update buffers and add them to the list */
+ for (i = 0; i < fb_data->max_num_updates; i++) {
+ upd_list = kzalloc(sizeof(*upd_list), GFP_KERNEL);
+ if (upd_list == NULL) {
+ ret = -ENOMEM;
+ goto out_upd_lists;
+ }
+
+ /* Add newly allocated buffer to free list */
+ list_add(&upd_list->list, &fb_data->upd_buf_free_list);
+ }
+
+ fb_data->virt_addr_updbuf =
+ kzalloc(sizeof(void *) * fb_data->max_num_buffers, GFP_KERNEL);
+ fb_data->phys_addr_updbuf =
+ kzalloc(sizeof(dma_addr_t) * fb_data->max_num_buffers,
+ GFP_KERNEL);
+ for (i = 0; i < fb_data->max_num_buffers; i++) {
+ /*
+ * Allocate memory for PxP output buffer.
+ * Each update buffer is 1 byte per pixel, and can
+ * be as big as the full-screen frame buffer
+ */
+ fb_data->virt_addr_updbuf[i] =
+ kmalloc(fb_data->max_pix_size, GFP_KERNEL);
+ fb_data->phys_addr_updbuf[i] =
+ virt_to_phys(fb_data->virt_addr_updbuf[i]);
+ if (fb_data->virt_addr_updbuf[i] == NULL) {
+ ret = -ENOMEM;
+ goto out_upd_buffers;
+ }
+
+ dev_dbg(fb_data->info.device, "allocated %d bytes @ 0x%08X\n",
+ fb_data->max_pix_size, fb_data->phys_addr_updbuf[i]);
+ }
+
+ /* Counter indicating which update buffer should be used next. */
+ fb_data->upd_buffer_num = 0;
+
+ /*
+ * Allocate memory for PxP SW workaround buffer
+ * These buffers are used to hold copy of the update region,
+ * before sending it to PxP for processing.
+ */
+ fb_data->virt_addr_copybuf =
+ dma_alloc_coherent(fb_data->info.device, fb_data->max_pix_size*2,
+ &fb_data->phys_addr_copybuf, GFP_DMA);
+ if (fb_data->virt_addr_copybuf == NULL) {
+ ret = -ENOMEM;
+ goto out_upd_buffers;
+ }
+
+ fb_data->working_buffer_size = vmode->yres * vmode->xres * 2;
+ /* Allocate EPDC working buffer A (main WB Processing buffer) */
+ fb_data->working_buffer_A_virt =
+ kmalloc(fb_data->working_buffer_size, GFP_KERNEL);
+ fb_data->working_buffer_A_phys =
+ virt_to_phys(fb_data->working_buffer_A_virt);
+ if (fb_data->working_buffer_A_virt == NULL) {
+ dev_err(&pdev->dev, "Can't allocate mem for working buf A!\n");
+ ret = -ENOMEM;
+ goto out_copybuffer;
+ }
+
+ /* Allocate EPDC working buffer B (post-waveform algorithm) */
+ fb_data->working_buffer_B_virt =
+ kmalloc(fb_data->working_buffer_size, GFP_KERNEL);
+ fb_data->working_buffer_B_phys =
+ virt_to_phys(fb_data->working_buffer_B_virt);
+ if (fb_data->working_buffer_B_virt == NULL) {
+ dev_err(&pdev->dev, "Can't allocate mem for working buf B!\n");
+ ret = -ENOMEM;
+ goto out_dma_work_buf_A;
+ }
+
+ /* Initialize EPDC pins */
+ if (fb_data->pdata->get_pins)
+ fb_data->pdata->get_pins();
+
+ fb_data->in_init = false;
+
+ fb_data->hw_ready = false;
+ fb_data->hw_initializing = false;
+
+ /*
+ * Set default waveform mode values.
+ * Should be overwritten via ioctl.
+ */
+ fb_data->wv_modes.mode_init = 0;
+ fb_data->wv_modes.mode_du = 1;
+ fb_data->wv_modes.mode_gc4 = 2;
+ fb_data->wv_modes.mode_gc8 = 2;
+ fb_data->wv_modes.mode_gc16 = 2;
+ fb_data->wv_modes.mode_gc32 = 2;
+ /*
+ * advance algorithm flow
+ */
+ fb_data->wv_modes.mode_aa = 3; /* default REAGL mode */
+ fb_data->wv_modes.mode_aad = 3; /* default REAGL-D mode */
+ fb_data->wv_modes.mode_gl16 = 2; /* default GL16 mode */
+ fb_data->wv_modes.mode_a2 = 1; /* default A2 mode */
+
+ fb_data->wv_modes_update = true;
+
+ /* Initialize marker list */
+ INIT_LIST_HEAD(&fb_data->full_marker_list);
+
+ /* Initialize all LUTs to inactive */
+ fb_data->lut_update_order =
+ kzalloc(fb_data->num_luts * sizeof(u32 *), GFP_KERNEL);
+ for (i = 0; i < fb_data->num_luts; i++)
+ fb_data->lut_update_order[i] = 0;
+
+ INIT_DELAYED_WORK(&fb_data->epdc_done_work, epdc_done_work_func);
+
+ fb_data->epdc_submit_workqueue = alloc_workqueue("EPDC Submit",
+ WQ_MEM_RECLAIM | WQ_HIGHPRI |
+ WQ_CPU_INTENSIVE | WQ_UNBOUND, 1);
+ INIT_WORK(&fb_data->epdc_submit_work, epdc_submit_work_func);
+
+ fb_data->epdc_intr_workqueue = alloc_workqueue("EPDC Interrupt",
+ WQ_MEM_RECLAIM | WQ_HIGHPRI |
+ WQ_CPU_INTENSIVE | WQ_UNBOUND, 1);
+ INIT_WORK(&fb_data->epdc_intr_work, epdc_intr_work_func);
+ fb_data->epdc_aa_workqueue = alloc_workqueue("EPDC AA",
+ WQ_MEM_RECLAIM | WQ_HIGHPRI |
+ WQ_CPU_INTENSIVE | WQ_UNBOUND, 1);
+ INIT_WORK(&fb_data->epdc_aa_work, epdc_aa_work_func);
+
+#ifdef FW_IN_RAM//[
+ //fb_data->epdc_firmware_workqueue = alloc_workqueue("EPDC Firmware",0, 1);
+ //INIT_WORK(&fb_data->epdc_firmware_work, epdc_firmware_work_func);
+ sema_init(&fb_data->firmware_work_lock,1);
+ INIT_DELAYED_WORK(&fb_data->epdc_firmware_work, epdc_firmware_work_func);
+#endif //]FW_IN_RAM
+
+ /* Retrieve EPDC IRQ num */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "cannot get IRQ resource\n");
+ ret = -ENODEV;
+ goto out_dma_work_buf_B;
+ }
+ fb_data->epdc_irq = res->start;
+
+ /* Register IRQ handler */
+ ret = request_irq(fb_data->epdc_irq, mxc_epdc_irq_handler, 0,
+ "fb_dma", fb_data);
+ if (ret) {
+ dev_err(&pdev->dev, "request_irq (%d) failed with error %d\n",
+ fb_data->epdc_irq, ret);
+ ret = -ENODEV;
+ goto out_dma_work_buf_B;
+ }
+
+ info->fbdefio = &mxc_epdc_fb_defio;
+#ifdef CONFIG_FB_MXC_EINK_AUTO_UPDATE_MODE
+ fb_deferred_io_init(info);
+#endif
+
+#ifdef USE_BSP_PMIC//[
+ /* get pmic regulators */
+ fb_data->display_regulator = regulator_get(NULL, "DISPLAY");
+ if (IS_ERR(fb_data->display_regulator)) {
+ dev_err(&pdev->dev, "Unable to get display PMIC regulator."
+ "err = 0x%x\n", (int)fb_data->display_regulator);
+ ret = -ENODEV;
+ goto out_irq;
+ }
+ fb_data->vcom_regulator = regulator_get(NULL, "VCOM");
+ if (IS_ERR(fb_data->vcom_regulator)) {
+ regulator_put(fb_data->display_regulator);
+ dev_err(&pdev->dev, "Unable to get VCOM regulator."
+ "err = 0x%x\n", (int)fb_data->vcom_regulator);
+ ret = -ENODEV;
+ goto out_regulator1;
+ }
+
+ /* save the nominal vcom value */
+ fb_data->wfm = 0; /* initial waveform mode should be INIT */
+ vcom_nominal = regulator_get_voltage(fb_data->vcom_regulator); /* save the vcom_nominal value in uV */
+
+ fb_data->v3p3_regulator = regulator_get(NULL, "V3P3");
+ if (IS_ERR(fb_data->v3p3_regulator)) {
+ regulator_put(fb_data->vcom_regulator);
+ regulator_put(fb_data->display_regulator);
+ dev_err(&pdev->dev, "Unable to get V3P3 regulator."
+ "err = 0x%x\n", (int)fb_data->vcom_regulator);
+ ret = -ENODEV;
+ goto out_regulator2;
+ }
+#else //][!USE_BSP_PMIC
+ if( NTXHWCFG_TST_FLAG(gptHWCFG->m_val.bPCB_Flags,4)||
+ NTXHWCFG_TST_FLAG(gptHWCFG->m_val.bPCB_Flags,5))
+ {
+ // Panel is designed for low voltage or VDD source is not comes from PMIC .
+ fb_data->vdd_1v8_regulator = regulator_get(NULL, "ldo_1v8");
+ if (IS_ERR(fb_data->vdd_1v8_regulator)) {
+ dev_err(&pdev->dev, "Unable to get vdd 1.8v regulator."
+ "err = 0x%x\n", (int)fb_data->vdd_1v8_regulator);
+ ret = -ENODEV;
+ goto out_irq;
+ }
+ regulator_enable(fb_data->vdd_1v8_regulator);
+ }
+
+
+ fb_data->wfm = 0; /* initial waveform mode should be INIT */
+#endif //]USE_BSP_PMIC
+
+ if (device_create_file(info->dev, &fb_attrs[0]))
+ dev_err(&pdev->dev, "Unable to create file from fb_attrs\n");
+
+ fb_data->cur_update = NULL;
+
+ mutex_init(&fb_data->queue_mutex);
+ mutex_init(&fb_data->pxp_mutex);
+ mutex_init(&fb_data->power_mutex);
+
+ /*
+ * Fill out PxP config data structure based on FB info and
+ * processing tasks required
+ */
+ pxp_conf = &fb_data->pxp_conf;
+ proc_data = &pxp_conf->proc_data;
+
+ /* Initialize non-channel-specific PxP parameters */
+ proc_data->drect.left = proc_data->srect.left = 0;
+ proc_data->drect.top = proc_data->srect.top = 0;
+ proc_data->drect.width = proc_data->srect.width = fb_data->info.var.xres;
+ proc_data->drect.height = proc_data->srect.height = fb_data->info.var.yres;
+ proc_data->scaling = 0;
+ proc_data->hflip = 0;
+ proc_data->vflip = 0;
+ proc_data->rotate = 0;
+ proc_data->bgcolor = 0;
+ proc_data->overlay_state = 0;
+ proc_data->lut_transform = PXP_LUT_NONE;
+ proc_data->lut_map = NULL;
+
+ /*
+ * We initially configure PxP for RGB->YUV conversion,
+ * and only write out Y component of the result.
+ */
+
+ /*
+ * Initialize S0 channel parameters
+ * Parameters should match FB format/width/height
+ */
+ pxp_conf->s0_param.pixel_fmt = PXP_PIX_FMT_RGB565;
+ pxp_conf->s0_param.width = fb_data->info.var.xres_virtual;
+ pxp_conf->s0_param.height = fb_data->info.var.yres;
+ pxp_conf->s0_param.color_key = -1;
+ pxp_conf->s0_param.color_key_enable = false;
+
+ /*
+ * Initialize OL0 channel parameters
+ * No overlay will be used for PxP operation
+ */
+ for (i = 0; i < 8; i++) {
+ pxp_conf->ol_param[i].combine_enable = false;
+ pxp_conf->ol_param[i].width = 0;
+ pxp_conf->ol_param[i].height = 0;
+ pxp_conf->ol_param[i].pixel_fmt = PXP_PIX_FMT_RGB565;
+ pxp_conf->ol_param[i].color_key_enable = false;
+ pxp_conf->ol_param[i].color_key = -1;
+ pxp_conf->ol_param[i].global_alpha_enable = false;
+ pxp_conf->ol_param[i].global_alpha = 0;
+ pxp_conf->ol_param[i].local_alpha_enable = false;
+ }
+
+ /*
+ * Initialize Output channel parameters
+ * Output is Y-only greyscale
+ * Output width/height will vary based on update region size
+ */
+ pxp_conf->out_param.width = fb_data->info.var.xres;
+ pxp_conf->out_param.height = fb_data->info.var.yres;
+ pxp_conf->out_param.stride = pxp_conf->out_param.width;
+ pxp_conf->out_param.pixel_fmt = PXP_PIX_FMT_GREY;
+
+ /* Initialize color map for conversion of 8-bit gray pixels */
+ fb_data->pxp_conf.proc_data.lut_map = kmalloc(256, GFP_KERNEL);
+ if (fb_data->pxp_conf.proc_data.lut_map == NULL) {
+ dev_err(&pdev->dev, "Can't allocate mem for lut map!\n");
+ ret = -ENOMEM;
+ goto out_regulator3;
+ }
+ for (i = 0; i < 256; i++)
+ fb_data->pxp_conf.proc_data.lut_map[i] = i;
+
+ fb_data->pxp_conf.proc_data.lut_map_updated = true;
+
+ /*
+ * Ensure this is set to NULL here...we will initialize pxp_chan
+ * later in our thread.
+ */
+ fb_data->pxp_chan = NULL;
+
+ /* Initialize Scatter-gather list containing 2 buffer addresses. */
+ sg = fb_data->sg;
+ sg_init_table(sg, 2);
+
+ /*
+ * For use in PxP transfers:
+ * sg[0] holds the FB buffer pointer
+ * sg[1] holds the Output buffer pointer (configured before TX request)
+ */
+ sg_dma_address(&sg[0]) = info->fix.smem_start;
+ sg_set_page(&sg[0], virt_to_page(info->screen_base),
+ info->fix.smem_len, offset_in_page(info->screen_base));
+
+ fb_data->order_cnt = 0;
+ fb_data->waiting_for_wb = false;
+ fb_data->waiting_for_lut = false;
+ fb_data->waiting_for_lut15 = false;
+ fb_data->waiting_for_idle = false;
+ fb_data->blank = FB_BLANK_UNBLANK;
+ fb_data->power_state = POWER_STATE_OFF;
+ fb_data->powering_down = false;
+ fb_data->wait_for_powerdown = false;
+ fb_data->updates_active = false;
+#ifdef CONFIG_ANDROID//[
+ fb_data->pwrdown_delay = 20;
+#else //][!CONFIG_ANDROID
+ fb_data->pwrdown_delay = 20;
+#endif //] CONFIG_ANDROID
+
+#if 0
+ set_aad_update_counter(0);
+#endif
+ fake_s1d13522_parse_epd_cmdline();
+
+#if 1//[ compatibility for mx5 .
+ if(sysfs_create_link(&fb_data->dev->parent->kobj,&fb_data->dev->kobj,"mxc_epdc_fb")) {
+ ERR_MSG("create link 'mxc_epdc_fb' fail !\n");
+ }
+#endif //]
+
+
+ g_fb_data = fb_data;
+#if defined(DEFAULT_PANEL_HW_INIT) //[
+ ret = mxc_epdc_fb_init_hw((struct fb_info *)fb_data);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to initialize HW!\n");
+ }
+#endif //]
+
+ /* Register FB */
+ ret = register_framebuffer(info);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "register_framebuffer failed with error %d\n", ret);
+ goto out_lutmap;
+ }
+
+ g_fb_data = fb_data;
+
+ goto out;
+
+out_lutmap:
+ kfree(fb_data->pxp_conf.proc_data.lut_map);
+out_regulator3:
+#ifdef USE_BSP_PMIC//[
+ regulator_put(fb_data->v3p3_regulator);
+#endif //] USE_BSP_PMIC
+out_regulator2:
+#ifdef USE_BSP_PMIC//[
+ regulator_put(fb_data->vcom_regulator);
+#endif //] USE_BSP_PMIC
+out_regulator1:
+#ifdef USE_BSP_PMIC//[
+ regulator_put(fb_data->display_regulator);
+#endif //] USE_BSP_PMIC
+out_irq:
+ free_irq(fb_data->epdc_irq, fb_data);
+out_dma_work_buf_B:
+ kfree(fb_data->working_buffer_B_virt);
+out_dma_work_buf_A:
+ kfree(fb_data->working_buffer_A_virt);
+ if (fb_data->pdata->put_pins)
+ fb_data->pdata->put_pins();
+out_copybuffer:
+ dma_free_writecombine(&pdev->dev, fb_data->max_pix_size*2,
+ fb_data->virt_addr_copybuf,
+ fb_data->phys_addr_copybuf);
+out_upd_buffers:
+ for (i = 0; i < fb_data->max_num_buffers; i++)
+ if (fb_data->virt_addr_updbuf[i] != NULL)
+ kfree(fb_data->virt_addr_updbuf[i]);
+ if (fb_data->virt_addr_updbuf != NULL)
+ kfree(fb_data->virt_addr_updbuf);
+ if (fb_data->phys_addr_updbuf != NULL)
+ kfree(fb_data->phys_addr_updbuf);
+out_upd_lists:
+ list_for_each_entry_safe(plist, temp_list, &fb_data->upd_buf_free_list,
+ list) {
+ list_del(&plist->list);
+ kfree(plist);
+ }
+out_dma_fb:
+
+ if(0==gptHWCFG->m_val.bUIStyle) {
+ dma_free_writecombine(&pdev->dev, fb_data->map_size<<1, info->screen_base,
+ fb_data->phys_start);
+ }
+ else {
+ dma_free_writecombine(&pdev->dev, fb_data->map_size, info->screen_base,
+ fb_data->phys_start);
+ }
+
+out_mapregs:
+ iounmap(epdc_base);
+out_cmap:
+ fb_dealloc_cmap(&info->cmap);
+out_fbdata:
+ kfree(fb_data);
+out:
+ if(NTXHWCFG_TST_FLAG(gptHWCFG->m_val.bPCB_Flags,4)) {
+ // PCB is designed for low voltage .
+ if (!IS_ERR(fb_data->vdd_1v8_regulator)) {
+ regulator_put(fb_data->vdd_1v8_regulator);
+ }
+ }
+ return ret;
+}
+
+static int mxc_epdc_fb_remove(struct platform_device *pdev)
+{
+ struct update_data_list *plist, *temp_list;
+ struct mxc_epdc_fb_data *fb_data = platform_get_drvdata(pdev);
+ int i;
+
+ /* restore the vcom_nominal value */
+#ifdef USE_BSP_PMIC //[
+ regulator_set_voltage(fb_data->vcom_regulator, vcom_nominal, vcom_nominal);
+#else //][!USE_BSP_PMIC
+ {
+ int vcom_mV=vcom_nominal/1000;
+ if(8==gptHWCFG->m_val.bDisplayCtrl) {
+ fp9928_vcom_set(vcom_mV,0);
+ }
+ else {
+ tps65185_vcom_set(vcom_mV,0);
+ }
+ }
+#endif //] USB_BSP_PMIC
+
+ mxc_epdc_fb_blank(FB_BLANK_POWERDOWN, &fb_data->info);
+
+ flush_workqueue(fb_data->epdc_submit_workqueue);
+ destroy_workqueue(fb_data->epdc_submit_workqueue);
+
+#ifdef FW_IN_RAM //[
+ flush_workqueue(fb_data->epdc_firmware_workqueue);
+ destroy_workqueue(fb_data->epdc_firmware_workqueue);
+#endif //]FW_IN_RAM
+
+#ifdef USE_BSP_PMIC//[
+ regulator_put(fb_data->display_regulator);
+ regulator_put(fb_data->vcom_regulator);
+ regulator_put(fb_data->v3p3_regulator);
+#endif //]USE_BSP_PMIC
+
+ unregister_framebuffer(&fb_data->info);
+ free_irq(fb_data->epdc_irq, fb_data);
+
+ for (i = 0; i < fb_data->max_num_buffers; i++)
+ if (fb_data->virt_addr_updbuf[i] != NULL)
+ kfree(fb_data->virt_addr_updbuf[i]);
+ if (fb_data->virt_addr_updbuf != NULL)
+ kfree(fb_data->virt_addr_updbuf);
+ if (fb_data->phys_addr_updbuf != NULL)
+ kfree(fb_data->phys_addr_updbuf);
+
+ kfree(fb_data->working_buffer_A_virt);
+ kfree(fb_data->working_buffer_B_virt);
+ if (fb_data->waveform_buffer_virt != NULL)
+ dma_free_writecombine(&pdev->dev, fb_data->waveform_buffer_size,
+ fb_data->waveform_buffer_virt,
+ fb_data->waveform_buffer_phys);
+ if (fb_data->virt_addr_copybuf != NULL)
+ dma_free_writecombine(&pdev->dev, fb_data->max_pix_size*2,
+ fb_data->virt_addr_copybuf,
+ fb_data->phys_addr_copybuf);
+
+ if (fb_data->temp_range_bounds) kfree(fb_data->temp_range_bounds);
+ kfree(fb_data->pxp_conf.proc_data.lut_map);
+
+ /* release gen2 waveform buffers */
+ if (fb_data->waveform_vcd_buffer) kfree (fb_data->waveform_vcd_buffer);
+ if (fb_data->waveform_acd_buffer) kfree (fb_data->waveform_acd_buffer);
+ if (fb_data->waveform_xwi_buffer) kfree (fb_data->waveform_xwi_buffer);
+ if (fb_data->waveform_xwi_string) kfree (fb_data->waveform_xwi_string);
+
+ list_for_each_entry_safe(plist, temp_list, &fb_data->upd_buf_free_list,
+ list) {
+ list_del(&plist->list);
+ kfree(plist);
+ }
+#ifdef CONFIG_FB_MXC_EINK_AUTO_UPDATE_MODE
+ fb_deferred_io_cleanup(&fb_data->info);
+#endif
+
+ dma_free_writecombine(&pdev->dev, fb_data->map_size, fb_data->info.screen_base,
+ fb_data->phys_start);
+
+ if (fb_data->pdata->put_pins)
+ fb_data->pdata->put_pins();
+
+ /* Release PxP-related resources */
+ if (fb_data->pxp_chan != NULL)
+ dma_release_channel(&fb_data->pxp_chan->dma_chan);
+
+ iounmap(epdc_base);
+
+ fb_dealloc_cmap(&fb_data->info.cmap);
+
+ framebuffer_release(&fb_data->info);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+//#define SKIP_SUSPEND
+//
+#ifndef DO_NOT_POWEROFF //[
+ #define EPD_PMIC_SUSPEND 1
+#endif //] DO_NOT_POWEROFF
+
+#ifndef CONFIG_ANDROID //[
+ #define EPD_FL_SUSPEND 1
+#endif //]CONFIG_ANDROID
+
+//#define EPD_SUSPEND_BLANK 1
+
+static int mxc_epdc_fb_suspend(struct platform_device *pdev, pm_message_t state)
+{
+
+ GALLEN_DBGLOCAL_BEGIN();
+
+ struct mxc_epdc_fb_data *data = platform_get_drvdata(pdev);
+ int ret=0;
+
+#ifdef SKIP_SUSPEND //[
+ return 0;
+#endif //] SKIP_SUSPEND
+
+
+#ifdef EPD_FL_SUSPEND //[
+ if(0!=gptHWCFG->m_val.bFrontLight) {
+ extern int FL_suspend(void);
+ GALLEN_DBGLOCAL_RUNLOG(0);
+
+ if(FL_suspend()<0) {
+ GALLEN_DBGLOCAL_RUNLOG(1);
+ printk(KERN_WARNING"%s(%d) : F.L suspend fail !\n",__FILE__,__LINE__);
+ ret = -1;
+ goto out;
+ }
+ }
+#endif //] EPD_FL_SUSPEND
+
+#ifdef EPD_SUSPEND_BLANK//[
+ data->pwrdown_delay = FB_POWERDOWN_DISABLE;
+ ret = mxc_epdc_fb_blank(FB_BLANK_POWERDOWN, &data->info);
+ if (ret) {
+ GALLEN_DBGLOCAL_RUNLOG(2);
+ goto out;
+ }
+#endif //]EPD_SUSPEND_BLANK
+
+ mxc_epdc_fb_flush_updates(data);
+
+ if(data->power_state != POWER_STATE_OFF) {
+ printk("%s():skip suspend cause EPD in POWER OFF state !!\n",__FUNCTION__);
+ ret = -2;
+ goto out;
+ }
+
+#ifdef EPD_PMIC_SUSPEND//[
+ if(8==gptHWCFG->m_val.bDisplayCtrl) {
+
+ if(fp9928_suspend()>=0) {
+ ret = 0;
+ }
+ else {
+ ret = -1;
+ }
+
+ }
+ else
+ if(6==gptHWCFG->m_val.bDisplayCtrl||7==gptHWCFG->m_val.bDisplayCtrl) {
+ GALLEN_DBGLOCAL_RUNLOG(3);
+ if(tps65185_suspend()>=0) {
+ GALLEN_DBGLOCAL_RUNLOG(4);
+ ret = 0;
+ }
+ else {
+ GALLEN_DBGLOCAL_RUNLOG(5);
+ ret = -1;
+ }
+ }
+ else {
+ GALLEN_DBGLOCAL_RUNLOG(6);
+ //lm75_suspend ();
+ }
+#endif //]EPD_PMIC_SUSPEND
+
+#if 0
+ if(NTXHWCFG_TST_FLAG(gptHWCFG->m_val.bPCB_Flags,4)) {
+ // PCB is designed for low voltage .
+ regulator_disable(data->vdd_1v8_regulator);
+ }
+#endif
+
+out:
+ GALLEN_DBGLOCAL_ESC();
+ return ret;
+ GALLEN_DBGLOCAL_END();
+}
+
+static int mxc_epdc_fb_resume(struct platform_device *pdev)
+{
+ struct mxc_epdc_fb_data *data = platform_get_drvdata(pdev);
+
+
+#ifdef SKIP_SUSPEND //[
+ return 0;
+#endif //] SKIP_SUSPEND
+
+ GALLEN_DBGLOCAL_BEGIN();
+
+#if 0
+ if(NTXHWCFG_TST_FLAG(gptHWCFG->m_val.bPCB_Flags,4)) {
+ // PCB is designed for low voltage .
+ regulator_enable(data->vdd_1v8_regulator);
+ }
+#endif
+
+#ifdef EPD_PMIC_SUSPEND //[
+ if(8==gptHWCFG->m_val.bDisplayCtrl) {
+ fp9928_resume();
+ }
+ else
+ if(6==gptHWCFG->m_val.bDisplayCtrl||7==gptHWCFG->m_val.bDisplayCtrl) {
+ tps65185_resume();
+ }
+ else {
+ //lm75_resume();
+ }
+#endif //]EPD_PMIC_SUSPEND
+
+ gdwLastUpdateJiffies = 0;
+
+#ifdef EPD_SUSPEND_BLANK//[
+ mxc_epdc_fb_blank(FB_BLANK_UNBLANK, &data->info);
+#endif//] EPD_SUSPEND_BLANK
+
+ if(mx6sl_revision() >= IMX_CHIP_REVISION_1_2) {
+ // the gp maybe powered down on chip TO1.2 ,we should reinit epd registers again .
+ epdc_init_settings(data);
+ }
+
+ data->updates_active = false;
+
+ GALLEN_DBGLOCAL_END();
+ return 0;
+}
+
+static int mxc_epdc_fb_shutdown(struct platform_device *pdev)
+{
+#if 1
+ struct mxc_epdc_fb_data *fb_data = platform_get_drvdata(pdev);
+ GALLEN_DBGLOCAL_BEGIN();
+
+ mxc_epdc_fb_flush_updates(fb_data);
+
+ //fb_data->powering_down = true;
+ //epdc_powerdown(fb_data);
+
+ if(8==gptHWCFG->m_val.bDisplayCtrl) {
+ GALLEN_DBGLOCAL_RUNLOG(0);
+ fp9928_shutdown();
+ }
+ else
+ {
+ GALLEN_DBGLOCAL_RUNLOG(1);
+ tps65185_shutdown();
+ }
+
+ GALLEN_DBGLOCAL_END();
+ return 0;
+#else
+ struct mxc_epdc_fb_data *fb_data = platform_get_drvdata(pdev);
+
+ /* Disable power to the EPD panel */
+ if (regulator_is_enabled(fb_data->vcom_regulator))
+ regulator_disable(fb_data->vcom_regulator);
+ if (regulator_is_enabled(fb_data->display_regulator))
+ regulator_disable(fb_data->display_regulator);
+
+ /* Disable clocks to EPDC */
+ clk_enable(fb_data->epdc_clk_axi);
+ clk_enable(fb_data->epdc_clk_pix);
+ __raw_writel(EPDC_CTRL_CLKGATE, EPDC_CTRL_SET);
+ clk_disable(fb_data->epdc_clk_pix);
+ clk_disable(fb_data->epdc_clk_axi);
+
+ /* Disable pins used by EPDC (to prevent leakage current) */
+ if (fb_data->pdata->disable_pins)
+ fb_data->pdata->disable_pins();
+
+ /* turn off the V3p3 */
+ if (regulator_is_enabled(fb_data->v3p3_regulator))
+ regulator_disable(fb_data->v3p3_regulator);
+ return 0;
+#endif
+
+}
+#else
+#define mxc_epdc_fb_suspend NULL
+#define mxc_epdc_fb_resume NULL
+#define mxc_epdc_fb_shutdown NULL
+#endif
+
+static struct platform_driver mxc_epdc_fb_driver = {
+ .probe = mxc_epdc_fb_probe,
+ .remove = mxc_epdc_fb_remove,
+ .suspend = mxc_epdc_fb_suspend,
+ .resume = mxc_epdc_fb_resume,
+ .shutdown = mxc_epdc_fb_shutdown,
+ .driver = {
+ .name = "imx_epdc_fb",
+ .owner = THIS_MODULE,
+ },
+};
+
+/* Callback function triggered after PxP receives an EOF interrupt */
+static void pxp_dma_done(void *arg)
+{
+ struct pxp_tx_desc *tx_desc = to_tx_desc(arg);
+ struct dma_chan *chan = tx_desc->txd.chan;
+ struct pxp_channel *pxp_chan = to_pxp_channel(chan);
+ struct mxc_epdc_fb_data *fb_data = pxp_chan->client;
+
+ GALLEN_DBGLOCAL_BEGIN();
+
+ /* This call will signal wait_for_completion_timeout() in send_buffer_to_pxp */
+ complete(&fb_data->pxp_tx_cmpl);
+ GALLEN_DBGLOCAL_END();
+}
+
+static bool chan_filter(struct dma_chan *chan, void *arg)
+{
+ if (imx_dma_is_pxp(chan))
+ return true;
+ else
+ return false;
+}
+
+/* Function to request PXP DMA channel */
+static int pxp_chan_init(struct mxc_epdc_fb_data *fb_data)
+{
+ dma_cap_mask_t mask;
+ struct dma_chan *chan;
+
+ /*
+ * Request a free channel
+ */
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ dma_cap_set(DMA_PRIVATE, mask);
+ chan = dma_request_channel(mask, chan_filter, NULL);
+ if (!chan) {
+ dev_err(fb_data->dev, "Unsuccessfully received channel!!!!\n");
+ return -EBUSY;
+ }
+
+ fb_data->pxp_chan = to_pxp_channel(chan);
+ fb_data->pxp_chan->client = fb_data;
+
+ init_completion(&fb_data->pxp_tx_cmpl);
+
+ return 0;
+}
+
+/*
+ * Function to call PxP DMA driver and send our latest FB update region
+ * through the PxP and out to an intermediate buffer.
+ * Note: This is a blocking call, so upon return the PxP tx should be complete.
+ */
+static int pxp_process_update(struct mxc_epdc_fb_data *fb_data,
+ u32 src_width, u32 src_height,
+ struct mxcfb_rect *update_region)
+{
+ dma_cookie_t cookie;
+ struct scatterlist *sg = fb_data->sg;
+ struct dma_chan *dma_chan;
+ struct pxp_tx_desc *desc;
+ struct dma_async_tx_descriptor *txd;
+ struct pxp_config_data *pxp_conf = &fb_data->pxp_conf;
+ struct pxp_proc_data *proc_data = &fb_data->pxp_conf.proc_data;
+ int i, ret;
+ int length;
+
+ GALLEN_DBGLOCAL_BEGIN();
+
+ dev_dbg(fb_data->dev, "Starting PxP Send Buffer\n");
+
+ /* First, check to see that we have acquired a PxP Channel object */
+ if (fb_data->pxp_chan == NULL) {
+ GALLEN_DBGLOCAL_RUNLOG(0);
+ /*
+ * PxP Channel has not yet been created and initialized,
+ * so let's go ahead and try
+ */
+ ret = pxp_chan_init(fb_data);
+ if (ret) {
+
+ /*
+ * PxP channel init failed, and we can't use the
+ * PxP until the PxP DMA driver has loaded, so we abort
+ */
+ dev_err(fb_data->dev, "PxP chan init failed\n");
+ GALLEN_DBGLOCAL_ESC();
+ return -ENODEV;
+ }
+ }
+
+ /*
+ * Init completion, so that we
+ * can be properly informed of the completion
+ * of the PxP task when it is done.
+ */
+ init_completion(&fb_data->pxp_tx_cmpl);
+
+ dma_chan = &fb_data->pxp_chan->dma_chan;
+
+ txd = dma_chan->device->device_prep_slave_sg(dma_chan, sg, 2,
+ DMA_TO_DEVICE,
+ DMA_PREP_INTERRUPT);
+ if (!txd) {
+ dev_err(fb_data->info.device,
+ "Error preparing a DMA transaction descriptor.\n");
+ GALLEN_DBGLOCAL_ESC();
+ return -EIO;
+ }
+
+ txd->callback_param = txd;
+ txd->callback = pxp_dma_done;
+
+ /*
+ * Configure PxP for processing of new update region
+ * The rest of our config params were set up in
+ * probe() and should not need to be changed.
+ */
+ pxp_conf->s0_param.width = src_width;
+ pxp_conf->s0_param.height = src_height;
+ proc_data->srect.top = update_region->top;
+ proc_data->srect.left = update_region->left;
+ proc_data->srect.width = update_region->width;
+ proc_data->srect.height = update_region->height;
+
+ /*
+ * Because only YUV/YCbCr image can be scaled, configure
+ * drect equivalent to srect, as such do not perform scaling.
+ */
+ proc_data->drect.top = 0;
+ proc_data->drect.left = 0;
+ proc_data->drect.width = proc_data->srect.width;
+ proc_data->drect.height = proc_data->srect.height;
+
+ /* PXP expects rotation in terms of degrees */
+ proc_data->rotate = fb_data->epdc_fb_var.rotate * 90;
+ if (proc_data->rotate > 270) {
+ GALLEN_DBGLOCAL_RUNLOG(1);
+ proc_data->rotate = 0;
+ }
+
+ pxp_conf->out_param.width = update_region->width;
+ pxp_conf->out_param.height = update_region->height;
+
+ if ((proc_data->rotate == 90) || (proc_data->rotate == 270)) {
+ GALLEN_DBGLOCAL_RUNLOG(2);
+ pxp_conf->out_param.stride = update_region->height;
+ }
+ else {
+ GALLEN_DBGLOCAL_RUNLOG(3);
+ pxp_conf->out_param.stride = update_region->width;
+ }
+
+ /* For EPDC v2.0, we need output to be 64-bit
+ * aligned since EPDC stride does not work. */
+ if (fb_data->rev <= 20) {
+ GALLEN_DBGLOCAL_RUNLOG(4);
+ pxp_conf->out_param.stride = ALIGN(pxp_conf->out_param.stride, 8);
+ }
+
+
+ desc = to_tx_desc(txd);
+ length = desc->len;
+ for (i = 0; i < length; i++) {
+ GALLEN_DBGLOCAL_RUNLOG(5);
+ if (i == 0) {/* S0 */
+ GALLEN_DBGLOCAL_RUNLOG(6);
+ memcpy(&desc->proc_data, proc_data, sizeof(struct pxp_proc_data));
+ pxp_conf->s0_param.paddr = sg_dma_address(&sg[0]);
+ memcpy(&desc->layer_param.s0_param, &pxp_conf->s0_param,
+ sizeof(struct pxp_layer_param));
+ } else if (i == 1) {
+ GALLEN_DBGLOCAL_RUNLOG(7);
+ pxp_conf->out_param.paddr = sg_dma_address(&sg[1]);
+ memcpy(&desc->layer_param.out_param, &pxp_conf->out_param,
+ sizeof(struct pxp_layer_param));
+ }
+ /* TODO: OverLay */
+
+ desc = desc->next;
+ }
+
+ /* Submitting our TX starts the PxP processing task */
+ cookie = txd->tx_submit(txd);
+ if (cookie < 0) {
+ dev_err(fb_data->info.device, "Error sending FB through PxP\n");
+ GALLEN_DBGLOCAL_ESC();
+ return -EIO;
+ }
+
+ fb_data->txd = txd;
+
+ /* trigger ePxP */
+ dma_async_issue_pending(dma_chan);
+
+ GALLEN_DBGLOCAL_END();
+ return 0;
+}
+
+static int pxp_complete_update(struct mxc_epdc_fb_data *fb_data, u32 *hist_stat)
+{
+ int ret;
+ /*
+ * Wait for completion event, which will be set
+ * through our TX callback function.
+ */
+ ret = wait_for_completion_timeout(&fb_data->pxp_tx_cmpl, HZ / 10);
+ if (ret <= 0) {
+ dev_info(fb_data->info.device,
+ "PxP operation failed due to %s\n",
+ ret < 0 ? "user interrupt" : "timeout");
+ dma_release_channel(&fb_data->pxp_chan->dma_chan);
+ fb_data->pxp_chan = NULL;
+ return ret ? : -ETIMEDOUT;
+ }
+
+ if ((fb_data->pxp_conf.proc_data.lut_transform & EPDC_FLAG_USE_CMAP) &&
+ fb_data->pxp_conf.proc_data.lut_map_updated)
+ fb_data->pxp_conf.proc_data.lut_map_updated = false;
+
+ *hist_stat = to_tx_desc(fb_data->txd)->hist_status;
+ dma_release_channel(&fb_data->pxp_chan->dma_chan);
+ fb_data->pxp_chan = NULL;
+
+ dev_dbg(fb_data->dev, "TX completed\n");
+
+ return 0;
+}
+
+/*
+ * Different dithering algorithm can be used. We chose
+ * to implement Bill Atkinson's algorithm as an example
+ * Thanks Bill Atkinson for his dithering algorithm.
+ */
+
+/*
+ * Dithering algorithm implementation - Y8->Y1 version 1.0 for i.MX
+ */
+static void do_dithering_processing_Y1_v1_0(
+ unsigned char *update_region_ptr,
+ struct mxcfb_rect *update_region,
+ unsigned long update_region_stride,
+ int *err_dist)
+{
+
+ /* create a temp error distribution array */
+ int bwPix;
+ int y;
+ int col;
+ int *err_dist_l0, *err_dist_l1, *err_dist_l2, distrib_error;
+ int width_3 = update_region->width + 3;
+ char *y8buf;
+ int x_offset = 0;
+
+ /* prime a few elements the error distribution array */
+ for (y = 0; y < update_region->height; y++) {
+ /* Dithering the Y8 in sbuf to BW suitable for A2 waveform */
+ err_dist_l0 = err_dist + (width_3) * (y % 3);
+ err_dist_l1 = err_dist + (width_3) * ((y + 1) % 3);
+ err_dist_l2 = err_dist + (width_3) * ((y + 2) % 3);
+
+ y8buf = update_region_ptr + x_offset;
+
+ /* scan the line and convert the Y8 to BW */
+ for (col = 1; col <= update_region->width; col++) {
+ bwPix = *(err_dist_l0 + col) + *y8buf;
+
+ if (bwPix >= 128) {
+ *y8buf++ = 0xf0;
+ distrib_error = (bwPix - 255) >> 3;
+ } else {
+ *y8buf++ = 0;
+ distrib_error = bwPix >> 3;
+ }
+
+ /* modify the error distribution buffer */
+ *(err_dist_l0 + col + 2) += distrib_error;
+ *(err_dist_l1 + col + 1) += distrib_error;
+ *(err_dist_l0 + col + 1) += distrib_error;
+ *(err_dist_l1 + col - 1) += distrib_error;
+ *(err_dist_l1 + col) += distrib_error;
+ *(err_dist_l2 + col) = distrib_error;
+ }
+ x_offset += update_region_stride;
+ }
+
+ flush_cache_all();
+ outer_flush_all();
+}
+
+/*
+ * Dithering algorithm implementation - Y8->Y4 version 1.0 for i.MX
+ */
+
+static void do_dithering_processing_Y4_v1_0(
+ unsigned char *update_region_ptr,
+ struct mxcfb_rect *update_region,
+ unsigned long update_region_stride,
+ int *err_dist)
+{
+
+ /* create a temp error distribution array */
+ int gcPix;
+ int y;
+ int col;
+ int *err_dist_l0, *err_dist_l1, *err_dist_l2, distrib_error;
+ int width_3 = update_region->width + 3;
+ char *y8buf;
+ int x_offset = 0;
+
+ /* prime a few elements the error distribution array */
+ for (y = 0; y < update_region->height; y++) {
+ /* Dithering the Y8 in sbuf to Y4 */
+ err_dist_l0 = err_dist + (width_3) * (y % 3);
+ err_dist_l1 = err_dist + (width_3) * ((y + 1) % 3);
+ err_dist_l2 = err_dist + (width_3) * ((y + 2) % 3);
+
+ y8buf = update_region_ptr + x_offset;
+
+ /* scan the line and convert the Y8 to Y4 */
+ for (col = 1; col <= update_region->width; col++) {
+ gcPix = *(err_dist_l0 + col) + *y8buf;
+
+ if (gcPix > 255)
+ gcPix = 255;
+ else if (gcPix < 0)
+ gcPix = 0;
+
+ distrib_error = (*y8buf - (gcPix & 0xf0)) >> 3;
+
+ *y8buf++ = gcPix & 0xf0;
+
+ /* modify the error distribution buffer */
+ *(err_dist_l0 + col + 2) += distrib_error;
+ *(err_dist_l1 + col + 1) += distrib_error;
+ *(err_dist_l0 + col + 1) += distrib_error;
+ *(err_dist_l1 + col - 1) += distrib_error;
+ *(err_dist_l1 + col) += distrib_error;
+ *(err_dist_l2 + col) = distrib_error;
+ }
+ x_offset += update_region_stride;
+ }
+
+ flush_cache_all();
+ outer_flush_all();
+}
+
+int mxc_epdc_fb_ep1v8_output(int iIsOutput)
+{
+ if(!g_fb_data) {
+ printk(KERN_ERR"%s cannot set without initialized !!\n",__FUNCTION__);
+ return -1;
+ }
+
+ if(NTXHWCFG_TST_FLAG(gptHWCFG->m_val.bPCB_Flags,4)) {
+ // PCB is designed for low voltage .
+ if (!IS_ERR(g_fb_data->vdd_1v8_regulator)) {
+ if(iIsOutput) {
+ regulator_enable(g_fb_data->vdd_1v8_regulator);
+ }
+ else {
+ regulator_disable(g_fb_data->vdd_1v8_regulator);
+ }
+ }
+ else {
+ printk(KERN_ERR"%s():regulator \"ldo_1v8\" not avalible !\n",__FUNCTION__);
+ }
+ }
+ return 0;
+}
+
+static int __init mxc_epdc_fb_init(void)
+{
+ return platform_driver_register(&mxc_epdc_fb_driver);
+}
+//late_initcall(mxc_epdc_fb_init);
+module_init(mxc_epdc_fb_init);
+
+
+static void __exit mxc_epdc_fb_exit(void)
+{
+ platform_driver_unregister(&mxc_epdc_fb_driver);
+}
+module_exit(mxc_epdc_fb_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC EPDC framebuffer driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("fb");
diff --git a/drivers/video/mxc/mxc_ipuv3_fb.c b/drivers/video/mxc/mxc_ipuv3_fb.c
new file mode 100644
index 00000000..f806f948
--- /dev/null
+++ b/drivers/video/mxc/mxc_ipuv3_fb.c
@@ -0,0 +1,2647 @@
+/*
+ * Copyright 2004-2013 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @defgroup Framebuffer Framebuffer Driver for SDC and ADC.
+ */
+
+/*!
+ * @file mxcfb.c
+ *
+ * @brief MXC Frame buffer driver for SDC
+ *
+ * @ingroup Framebuffer
+ */
+
+/*!
+ * Include files
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/fb.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/io.h>
+#include <linux/ipu.h>
+#include <linux/mxcfb.h>
+#include <linux/uaccess.h>
+#include <linux/fsl_devices.h>
+#include <linux/earlysuspend.h>
+#include <linux/workqueue.h>
+#include <asm/mach-types.h>
+#include <mach/ipu-v3.h>
+#include "mxc_dispdrv.h"
+
+/*
+ * Driver name
+ */
+#define MXCFB_NAME "mxc_sdc_fb"
+
+/* Display port number */
+#define MXCFB_PORT_NUM 2
+/*!
+ * Structure containing the MXC specific framebuffer information.
+ */
+struct mxcfb_info {
+ spinlock_t lock;
+ struct fb_info *fbi;
+ int default_bpp;
+ int cur_blank;
+ int next_blank;
+ ipu_channel_t ipu_ch;
+ int ipu_id;
+ int ipu_di;
+ u32 ipu_di_pix_fmt;
+ bool ipu_int_clk;
+ bool overlay;
+ bool alpha_chan_en;
+ bool late_init;
+ bool first_set_par;
+ dma_addr_t alpha_phy_addr0;
+ dma_addr_t alpha_phy_addr1;
+ void *alpha_virt_addr0;
+ void *alpha_virt_addr1;
+ uint32_t alpha_mem_len;
+ uint32_t ipu_ch_irq;
+ uint32_t ipu_ch_nf_irq;
+ uint32_t ipu_vsync_pre_irq;
+ uint32_t ipu_alp_ch_irq;
+ uint32_t cur_ipu_buf;
+ uint32_t cur_ipu_alpha_buf;
+
+ u32 pseudo_palette[16];
+
+ bool mode_found;
+ struct completion flip_complete;
+ struct completion alpha_flip_complete;
+ struct completion vsync_complete;
+
+ void *ipu;
+ struct fb_info *ovfbi;
+
+ struct mxc_dispdrv_handle *dispdrv;
+
+ struct fb_var_screeninfo cur_var;
+
+ int vsync_pre_report_active;
+ ktime_t vsync_pre_timestamp;
+ struct workqueue_struct *vsync_pre_queue;
+ struct work_struct vsync_pre_work;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend fbdrv_earlysuspend;
+#endif
+};
+
+struct mxcfb_pfmt {
+ u32 fb_pix_fmt;
+ int bpp;
+ struct fb_bitfield red;
+ struct fb_bitfield green;
+ struct fb_bitfield blue;
+ struct fb_bitfield transp;
+};
+
+static const struct mxcfb_pfmt mxcfb_pfmts[] = {
+ /* pixel bpp red green blue transp */
+ {IPU_PIX_FMT_RGB565, 16, {11, 5, 0}, { 5, 6, 0}, { 0, 5, 0}, { 0, 0, 0} },
+ {IPU_PIX_FMT_RGB24, 24, { 0, 8, 0}, { 8, 8, 0}, {16, 8, 0}, { 0, 0, 0} },
+ {IPU_PIX_FMT_BGR24, 24, {16, 8, 0}, { 8, 8, 0}, { 0, 8, 0}, { 0, 0, 0} },
+ {IPU_PIX_FMT_RGB32, 32, { 0, 8, 0}, { 8, 8, 0}, {16, 8, 0}, {24, 8, 0} },
+ {IPU_PIX_FMT_BGR32, 32, {16, 8, 0}, { 8, 8, 0}, { 0, 8, 0}, {24, 8, 0} },
+ {IPU_PIX_FMT_ABGR32, 32, {24, 8, 0}, {16, 8, 0}, { 8, 8, 0}, { 0, 8, 0} },
+};
+
+struct mxcfb_alloc_list {
+ struct list_head list;
+ dma_addr_t phy_addr;
+ void *cpu_addr;
+ u32 size;
+};
+
+enum {
+ BOTH_ON,
+ SRC_ON,
+ TGT_ON,
+ BOTH_OFF
+};
+
+static bool g_dp_in_use[2];
+LIST_HEAD(fb_alloc_list);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void mxcfb_early_suspend(struct early_suspend *h);
+static void mxcfb_later_resume(struct early_suspend *h);
+#endif
+
+/* Return default standard(RGB) pixel format */
+static uint32_t bpp_to_pixfmt(int bpp)
+{
+ uint32_t pixfmt = 0;
+
+ switch (bpp) {
+ case 24:
+ pixfmt = IPU_PIX_FMT_BGR24;
+ break;
+ case 32:
+ pixfmt = IPU_PIX_FMT_BGR32;
+ break;
+ case 16:
+ pixfmt = IPU_PIX_FMT_RGB565;
+ break;
+ }
+ return pixfmt;
+}
+
+static inline int bitfield_is_equal(struct fb_bitfield f1,
+ struct fb_bitfield f2)
+{
+ return !memcmp(&f1, &f2, sizeof(f1));
+}
+
+static int pixfmt_to_var(uint32_t pixfmt, struct fb_var_screeninfo *var)
+{
+ int i, ret = -1;
+
+ for (i = 0; i < ARRAY_SIZE(mxcfb_pfmts); i++) {
+ if (pixfmt == mxcfb_pfmts[i].fb_pix_fmt) {
+ var->red = mxcfb_pfmts[i].red;
+ var->green = mxcfb_pfmts[i].green;
+ var->blue = mxcfb_pfmts[i].blue;
+ var->transp = mxcfb_pfmts[i].transp;
+ var->bits_per_pixel = mxcfb_pfmts[i].bpp;
+ ret = 0;
+ break;
+ }
+ }
+ return ret;
+}
+
+static int bpp_to_var(int bpp, struct fb_var_screeninfo *var)
+{
+ uint32_t pixfmt = 0;
+
+ pixfmt = bpp_to_pixfmt(bpp);
+ if (pixfmt)
+ return pixfmt_to_var(pixfmt, var);
+ else
+ return -1;
+}
+
+static int check_var_pixfmt(struct fb_var_screeninfo *var)
+{
+ int i, ret = -1;
+
+ for (i = 0; i < ARRAY_SIZE(mxcfb_pfmts); i++) {
+ if (bitfield_is_equal(var->red, mxcfb_pfmts[i].red) &&
+ bitfield_is_equal(var->green, mxcfb_pfmts[i].green) &&
+ bitfield_is_equal(var->blue, mxcfb_pfmts[i].blue) &&
+ bitfield_is_equal(var->transp, mxcfb_pfmts[i].transp) &&
+ var->bits_per_pixel == mxcfb_pfmts[i].bpp) {
+ ret = 0;
+ break;
+ }
+ }
+ return ret;
+}
+
+static uint32_t fbi_to_pixfmt(struct fb_info *fbi)
+{
+ int i;
+ uint32_t pixfmt = 0;
+
+ if (fbi->var.nonstd)
+ return fbi->var.nonstd;
+
+ for (i = 0; i < ARRAY_SIZE(mxcfb_pfmts); i++) {
+ if (bitfield_is_equal(fbi->var.red, mxcfb_pfmts[i].red) &&
+ bitfield_is_equal(fbi->var.green, mxcfb_pfmts[i].green) &&
+ bitfield_is_equal(fbi->var.blue, mxcfb_pfmts[i].blue) &&
+ bitfield_is_equal(fbi->var.transp, mxcfb_pfmts[i].transp)) {
+ pixfmt = mxcfb_pfmts[i].fb_pix_fmt;
+ break;
+ }
+ }
+
+ if (pixfmt == 0)
+ dev_err(fbi->device, "cannot get pixel format\n");
+
+ return pixfmt;
+}
+
+static struct fb_info *found_registered_fb(ipu_channel_t ipu_ch, int ipu_id)
+{
+ int i;
+ struct mxcfb_info *mxc_fbi;
+ struct fb_info *fbi = NULL;
+
+ for (i = 0; i < num_registered_fb; i++) {
+ mxc_fbi =
+ ((struct mxcfb_info *)(registered_fb[i]->par));
+
+ if ((mxc_fbi->ipu_ch == ipu_ch) &&
+ (mxc_fbi->ipu_id == ipu_id)) {
+ fbi = registered_fb[i];
+ break;
+ }
+ }
+ return fbi;
+}
+
+static irqreturn_t mxcfb_irq_handler(int irq, void *dev_id);
+static irqreturn_t mxcfb_nf_irq_handler(int irq, void *dev_id);
+static irqreturn_t mxcfb_vsync_pre_irq_handler(int irq, void *dev_id);
+static int mxcfb_blank(int blank, struct fb_info *info);
+static int mxcfb_map_video_memory(struct fb_info *fbi);
+static int mxcfb_unmap_video_memory(struct fb_info *fbi);
+
+/*
+ * Set fixed framebuffer parameters based on variable settings.
+ *
+ * @param info framebuffer information pointer
+ */
+static int mxcfb_set_fix(struct fb_info *info)
+{
+ struct fb_fix_screeninfo *fix = &info->fix;
+ struct fb_var_screeninfo *var = &info->var;
+
+ fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
+
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->accel = FB_ACCEL_NONE;
+ fix->visual = FB_VISUAL_TRUECOLOR;
+ fix->xpanstep = 1;
+ fix->ywrapstep = 1;
+ fix->ypanstep = 1;
+
+ return 0;
+}
+
+static int _setup_disp_channel1(struct fb_info *fbi)
+{
+ ipu_channel_params_t params;
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
+
+ memset(&params, 0, sizeof(params));
+
+ if (mxc_fbi->ipu_ch == MEM_DC_SYNC) {
+ params.mem_dc_sync.di = mxc_fbi->ipu_di;
+ if (fbi->var.vmode & FB_VMODE_INTERLACED)
+ params.mem_dc_sync.interlaced = true;
+ params.mem_dc_sync.out_pixel_fmt = mxc_fbi->ipu_di_pix_fmt;
+ params.mem_dc_sync.in_pixel_fmt = fbi_to_pixfmt(fbi);
+ } else {
+ params.mem_dp_bg_sync.di = mxc_fbi->ipu_di;
+ if (fbi->var.vmode & FB_VMODE_INTERLACED)
+ params.mem_dp_bg_sync.interlaced = true;
+ params.mem_dp_bg_sync.out_pixel_fmt = mxc_fbi->ipu_di_pix_fmt;
+ params.mem_dp_bg_sync.in_pixel_fmt = fbi_to_pixfmt(fbi);
+ if (mxc_fbi->alpha_chan_en)
+ params.mem_dp_bg_sync.alpha_chan_en = true;
+ }
+ ipu_init_channel(mxc_fbi->ipu, mxc_fbi->ipu_ch, &params);
+
+ return 0;
+}
+
+static int _setup_disp_channel2(struct fb_info *fbi)
+{
+ int retval = 0;
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
+ int fb_stride;
+ unsigned long base;
+ unsigned int fr_xoff, fr_yoff, fr_w, fr_h;
+
+ switch (fbi_to_pixfmt(fbi)) {
+ case IPU_PIX_FMT_YUV420P2:
+ case IPU_PIX_FMT_YVU420P:
+ case IPU_PIX_FMT_NV12:
+ case IPU_PIX_FMT_YUV422P:
+ case IPU_PIX_FMT_YVU422P:
+ case IPU_PIX_FMT_YUV420P:
+ case IPU_PIX_FMT_YUV444P:
+ fb_stride = fbi->var.xres_virtual;
+ break;
+ default:
+ fb_stride = fbi->fix.line_length;
+ }
+
+ base = fbi->fix.smem_start;
+ fr_xoff = fbi->var.xoffset;
+ fr_w = fbi->var.xres_virtual;
+ if (!(fbi->var.vmode & FB_VMODE_YWRAP)) {
+ dev_dbg(fbi->device, "Y wrap disabled\n");
+ fr_yoff = fbi->var.yoffset % fbi->var.yres;
+ fr_h = fbi->var.yres;
+ base += fbi->fix.line_length * fbi->var.yres *
+ (fbi->var.yoffset / fbi->var.yres);
+ } else {
+ dev_dbg(fbi->device, "Y wrap enabled\n");
+ fr_yoff = fbi->var.yoffset;
+ fr_h = fbi->var.yres_virtual;
+ }
+ base += fr_yoff * fb_stride + fr_xoff;
+
+ mxc_fbi->cur_ipu_buf = 2;
+ init_completion(&mxc_fbi->flip_complete);
+ /*
+ * We don't need to wait for vsync at the first time
+ * we do pan display after fb is initialized, as IPU will
+ * switch to the newly selected buffer automatically,
+ * so we call complete() for both mxc_fbi->flip_complete
+ * and mxc_fbi->alpha_flip_complete.
+ */
+ complete(&mxc_fbi->flip_complete);
+ if (mxc_fbi->alpha_chan_en) {
+ mxc_fbi->cur_ipu_alpha_buf = 1;
+ init_completion(&mxc_fbi->alpha_flip_complete);
+ complete(&mxc_fbi->alpha_flip_complete);
+ }
+
+ retval = ipu_init_channel_buffer(mxc_fbi->ipu,
+ mxc_fbi->ipu_ch, IPU_INPUT_BUFFER,
+ fbi_to_pixfmt(fbi),
+ fbi->var.xres, fbi->var.yres,
+ fb_stride,
+ fbi->var.rotate,
+ base,
+ base,
+ fbi->var.accel_flags &
+ FB_ACCEL_DOUBLE_FLAG ? 0 : base,
+ 0, 0);
+ if (retval) {
+ dev_err(fbi->device,
+ "ipu_init_channel_buffer error %d\n", retval);
+ return retval;
+ }
+
+ /* update u/v offset */
+ ipu_update_channel_offset(mxc_fbi->ipu, mxc_fbi->ipu_ch,
+ IPU_INPUT_BUFFER,
+ fbi_to_pixfmt(fbi),
+ fr_w,
+ fr_h,
+ fr_w,
+ 0, 0,
+ fr_yoff,
+ fr_xoff);
+
+ if (mxc_fbi->alpha_chan_en) {
+ retval = ipu_init_channel_buffer(mxc_fbi->ipu,
+ mxc_fbi->ipu_ch,
+ IPU_ALPHA_IN_BUFFER,
+ IPU_PIX_FMT_GENERIC,
+ fbi->var.xres, fbi->var.yres,
+ fbi->var.xres,
+ fbi->var.rotate,
+ mxc_fbi->alpha_phy_addr1,
+ mxc_fbi->alpha_phy_addr0,
+ 0,
+ 0, 0);
+ if (retval) {
+ dev_err(fbi->device,
+ "ipu_init_channel_buffer error %d\n", retval);
+ return retval;
+ }
+ }
+
+ return retval;
+}
+
+static bool mxcfb_need_to_set_par(struct fb_info *fbi)
+{
+ struct mxcfb_info *mxc_fbi = fbi->par;
+
+ if ((fbi->var.activate & FB_ACTIVATE_FORCE) &&
+ (fbi->var.activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW)
+ return true;
+
+ /*
+ * Ignore xoffset and yoffset update,
+ * because pan display handles this case.
+ */
+ mxc_fbi->cur_var.xoffset = fbi->var.xoffset;
+ mxc_fbi->cur_var.yoffset = fbi->var.yoffset;
+
+ return !!memcmp(&mxc_fbi->cur_var, &fbi->var,
+ sizeof(struct fb_var_screeninfo));
+}
+
+/*
+ * Set framebuffer parameters and change the operating mode.
+ *
+ * @param info framebuffer information pointer
+ */
+static int mxcfb_set_par(struct fb_info *fbi)
+{
+ int retval = 0;
+ u32 mem_len, alpha_mem_len;
+ ipu_di_signal_cfg_t sig_cfg;
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
+
+ int16_t ov_pos_x = 0, ov_pos_y = 0;
+ int ov_pos_ret = 0;
+ struct mxcfb_info *mxc_fbi_fg = NULL;
+ bool ovfbi_enable = false;
+
+ if (mxc_fbi->ovfbi)
+ mxc_fbi_fg = (struct mxcfb_info *)mxc_fbi->ovfbi->par;
+
+ if (mxc_fbi->ovfbi && mxc_fbi_fg)
+ if (mxc_fbi_fg->next_blank == FB_BLANK_UNBLANK)
+ ovfbi_enable = true;
+
+ if (!mxcfb_need_to_set_par(fbi))
+ return 0;
+
+ dev_dbg(fbi->device, "Reconfiguring framebuffer\n");
+
+ if (fbi->var.xres == 0 || fbi->var.yres == 0)
+ return 0;
+
+ if (ovfbi_enable) {
+ ov_pos_ret = ipu_disp_get_window_pos(
+ mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch,
+ &ov_pos_x, &ov_pos_y);
+ if (ov_pos_ret < 0)
+ dev_err(fbi->device, "Get overlay pos failed, dispdrv:%s.\n",
+ mxc_fbi->dispdrv->drv->name);
+
+ ipu_clear_irq(mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch_irq);
+ ipu_disable_irq(mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch_irq);
+ ipu_clear_irq(mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch_nf_irq);
+ ipu_disable_irq(mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch_nf_irq);
+ ipu_disable_channel(mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch, true);
+ ipu_uninit_channel(mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch);
+ }
+
+ ipu_clear_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq);
+ ipu_disable_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq);
+ ipu_clear_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_nf_irq);
+ ipu_disable_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_nf_irq);
+ ipu_disable_channel(mxc_fbi->ipu, mxc_fbi->ipu_ch, true);
+ ipu_uninit_channel(mxc_fbi->ipu, mxc_fbi->ipu_ch);
+
+ /*
+ * Disable IPU hsp clock if it is enabled for an
+ * additional time in ipu common driver.
+ */
+ if (mxc_fbi->first_set_par && mxc_fbi->late_init)
+ ipu_disable_hsp_clk(mxc_fbi->ipu);
+
+ mxcfb_set_fix(fbi);
+
+ mem_len = fbi->var.yres_virtual * fbi->fix.line_length;
+ if (!fbi->fix.smem_start || (mem_len > fbi->fix.smem_len)) {
+ if (fbi->fix.smem_start)
+ mxcfb_unmap_video_memory(fbi);
+
+ if (mxcfb_map_video_memory(fbi) < 0)
+ return -ENOMEM;
+ }
+
+ if (mxc_fbi->first_set_par) {
+ /*
+ * Clear the screen in case uboot fb pixel format is not
+ * the same to kernel fb pixel format.
+ */
+ if (mxc_fbi->late_init)
+ memset((char *)fbi->screen_base, 0, fbi->fix.smem_len);
+
+ mxc_fbi->first_set_par = false;
+ }
+
+ if (mxc_fbi->alpha_chan_en) {
+ alpha_mem_len = fbi->var.xres * fbi->var.yres;
+ if ((!mxc_fbi->alpha_phy_addr0 && !mxc_fbi->alpha_phy_addr1) ||
+ (alpha_mem_len > mxc_fbi->alpha_mem_len)) {
+ if (mxc_fbi->alpha_phy_addr0)
+ dma_free_coherent(fbi->device,
+ mxc_fbi->alpha_mem_len,
+ mxc_fbi->alpha_virt_addr0,
+ mxc_fbi->alpha_phy_addr0);
+ if (mxc_fbi->alpha_phy_addr1)
+ dma_free_coherent(fbi->device,
+ mxc_fbi->alpha_mem_len,
+ mxc_fbi->alpha_virt_addr1,
+ mxc_fbi->alpha_phy_addr1);
+
+ mxc_fbi->alpha_virt_addr0 =
+ dma_alloc_coherent(fbi->device,
+ alpha_mem_len,
+ &mxc_fbi->alpha_phy_addr0,
+ GFP_DMA | GFP_KERNEL);
+
+ mxc_fbi->alpha_virt_addr1 =
+ dma_alloc_coherent(fbi->device,
+ alpha_mem_len,
+ &mxc_fbi->alpha_phy_addr1,
+ GFP_DMA | GFP_KERNEL);
+ if (mxc_fbi->alpha_virt_addr0 == NULL ||
+ mxc_fbi->alpha_virt_addr1 == NULL) {
+ dev_err(fbi->device, "mxcfb: dma alloc for"
+ " alpha buffer failed.\n");
+ if (mxc_fbi->alpha_virt_addr0)
+ dma_free_coherent(fbi->device,
+ mxc_fbi->alpha_mem_len,
+ mxc_fbi->alpha_virt_addr0,
+ mxc_fbi->alpha_phy_addr0);
+ if (mxc_fbi->alpha_virt_addr1)
+ dma_free_coherent(fbi->device,
+ mxc_fbi->alpha_mem_len,
+ mxc_fbi->alpha_virt_addr1,
+ mxc_fbi->alpha_phy_addr1);
+ return -ENOMEM;
+ }
+ mxc_fbi->alpha_mem_len = alpha_mem_len;
+ }
+ }
+
+ if (mxc_fbi->next_blank != FB_BLANK_UNBLANK)
+ return retval;
+
+ if (mxc_fbi->dispdrv && mxc_fbi->dispdrv->drv->setup) {
+ retval = mxc_fbi->dispdrv->drv->setup(mxc_fbi->dispdrv, fbi);
+ if (retval < 0) {
+ dev_err(fbi->device, "setup error, dispdrv:%s.\n",
+ mxc_fbi->dispdrv->drv->name);
+ return -EINVAL;
+ }
+ }
+
+ _setup_disp_channel1(fbi);
+ if (ovfbi_enable)
+ _setup_disp_channel1(mxc_fbi->ovfbi);
+
+ if (!mxc_fbi->overlay) {
+ uint32_t out_pixel_fmt;
+
+ memset(&sig_cfg, 0, sizeof(sig_cfg));
+ if (fbi->var.vmode & FB_VMODE_INTERLACED)
+ sig_cfg.interlaced = true;
+ out_pixel_fmt = mxc_fbi->ipu_di_pix_fmt;
+ if (fbi->var.vmode & FB_VMODE_ODD_FLD_FIRST) /* PAL */
+ sig_cfg.odd_field_first = true;
+ if (mxc_fbi->ipu_int_clk)
+ sig_cfg.int_clk = true;
+ if (fbi->var.sync & FB_SYNC_HOR_HIGH_ACT)
+ sig_cfg.Hsync_pol = true;
+ if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT)
+ sig_cfg.Vsync_pol = true;
+ if (!(fbi->var.sync & FB_SYNC_CLK_LAT_FALL))
+ sig_cfg.clk_pol = true;
+ if (fbi->var.sync & FB_SYNC_DATA_INVERT)
+ sig_cfg.data_pol = true;
+ if (!(fbi->var.sync & FB_SYNC_OE_LOW_ACT))
+ sig_cfg.enable_pol = true;
+ if (fbi->var.sync & FB_SYNC_CLK_IDLE_EN)
+ sig_cfg.clkidle_en = true;
+
+ dev_dbg(fbi->device, "pixclock = %ul Hz\n",
+ (u32) (PICOS2KHZ(fbi->var.pixclock) * 1000UL));
+
+ if (ipu_init_sync_panel(mxc_fbi->ipu, mxc_fbi->ipu_di,
+ (PICOS2KHZ(fbi->var.pixclock)) * 1000UL,
+ fbi->var.xres, fbi->var.yres,
+ out_pixel_fmt,
+ fbi->var.left_margin,
+ fbi->var.hsync_len,
+ fbi->var.right_margin,
+ fbi->var.upper_margin,
+ fbi->var.vsync_len,
+ fbi->var.lower_margin,
+ 0, sig_cfg) != 0) {
+ dev_err(fbi->device,
+ "mxcfb: Error initializing panel.\n");
+ return -EINVAL;
+ }
+
+ fbi->mode =
+ (struct fb_videomode *)fb_match_mode(&fbi->var,
+ &fbi->modelist);
+
+ ipu_disp_set_window_pos(mxc_fbi->ipu, mxc_fbi->ipu_ch, 0, 0);
+ }
+
+ retval = _setup_disp_channel2(fbi);
+ if (retval) {
+ ipu_uninit_channel(mxc_fbi->ipu, mxc_fbi->ipu_ch);
+ return retval;
+ }
+
+ if (ovfbi_enable) {
+ if (ov_pos_ret >= 0)
+ ipu_disp_set_window_pos(
+ mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch,
+ ov_pos_x, ov_pos_y);
+ retval = _setup_disp_channel2(mxc_fbi->ovfbi);
+ if (retval) {
+ ipu_uninit_channel(mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch);
+ ipu_uninit_channel(mxc_fbi->ipu, mxc_fbi->ipu_ch);
+ return retval;
+ }
+ }
+
+ ipu_enable_channel(mxc_fbi->ipu, mxc_fbi->ipu_ch);
+ if (ovfbi_enable)
+ ipu_enable_channel(mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch);
+
+ if (mxc_fbi->dispdrv && mxc_fbi->dispdrv->drv->enable) {
+ retval = mxc_fbi->dispdrv->drv->enable(mxc_fbi->dispdrv);
+ if (retval < 0) {
+ dev_err(fbi->device, "enable error, dispdrv:%s.\n",
+ mxc_fbi->dispdrv->drv->name);
+ return -EINVAL;
+ }
+ }
+
+ mxc_fbi->cur_var = fbi->var;
+
+ return retval;
+}
+
+static int _swap_channels(struct fb_info *fbi_from,
+ struct fb_info *fbi_to, bool both_on)
+{
+ int retval, tmp;
+ ipu_channel_t old_ch;
+ struct fb_info *ovfbi;
+ struct mxcfb_info *mxc_fbi_from = (struct mxcfb_info *)fbi_from->par;
+ struct mxcfb_info *mxc_fbi_to = (struct mxcfb_info *)fbi_to->par;
+
+ if (both_on) {
+ ipu_disable_channel(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch, true);
+ ipu_uninit_channel(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch);
+ }
+
+ /* switch the mxc fbi parameters */
+ old_ch = mxc_fbi_from->ipu_ch;
+ mxc_fbi_from->ipu_ch = mxc_fbi_to->ipu_ch;
+ mxc_fbi_to->ipu_ch = old_ch;
+ tmp = mxc_fbi_from->ipu_ch_irq;
+ mxc_fbi_from->ipu_ch_irq = mxc_fbi_to->ipu_ch_irq;
+ mxc_fbi_to->ipu_ch_irq = tmp;
+ tmp = mxc_fbi_from->ipu_ch_nf_irq;
+ mxc_fbi_from->ipu_ch_nf_irq = mxc_fbi_to->ipu_ch_nf_irq;
+ mxc_fbi_to->ipu_ch_nf_irq = tmp;
+ ovfbi = mxc_fbi_from->ovfbi;
+ mxc_fbi_from->ovfbi = mxc_fbi_to->ovfbi;
+ mxc_fbi_to->ovfbi = ovfbi;
+
+ _setup_disp_channel1(fbi_from);
+ retval = _setup_disp_channel2(fbi_from);
+ if (retval)
+ return retval;
+
+ /* switch between dp and dc, disable old idmac, enable new idmac */
+ retval = ipu_swap_channel(mxc_fbi_from->ipu, old_ch, mxc_fbi_from->ipu_ch);
+ ipu_uninit_channel(mxc_fbi_from->ipu, old_ch);
+
+ if (both_on) {
+ _setup_disp_channel1(fbi_to);
+ retval = _setup_disp_channel2(fbi_to);
+ if (retval)
+ return retval;
+ ipu_enable_channel(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch);
+ }
+
+ return retval;
+}
+
+static int swap_channels(struct fb_info *fbi_from)
+{
+ int i;
+ int swap_mode;
+ ipu_channel_t ch_to;
+ struct mxcfb_info *mxc_fbi_from = (struct mxcfb_info *)fbi_from->par;
+ struct fb_info *fbi_to = NULL;
+ struct mxcfb_info *mxc_fbi_to;
+
+ /* what's the target channel? */
+ if (mxc_fbi_from->ipu_ch == MEM_BG_SYNC)
+ ch_to = MEM_DC_SYNC;
+ else
+ ch_to = MEM_BG_SYNC;
+
+ fbi_to = found_registered_fb(ch_to, mxc_fbi_from->ipu_id);
+ if (!fbi_to)
+ return -1;
+ mxc_fbi_to = (struct mxcfb_info *)fbi_to->par;
+
+ ipu_clear_irq(mxc_fbi_from->ipu, mxc_fbi_from->ipu_ch_irq);
+ ipu_clear_irq(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch_irq);
+ ipu_free_irq(mxc_fbi_from->ipu, mxc_fbi_from->ipu_ch_irq, fbi_from);
+ ipu_free_irq(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch_irq, fbi_to);
+ ipu_clear_irq(mxc_fbi_from->ipu, mxc_fbi_from->ipu_ch_nf_irq);
+ ipu_clear_irq(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch_nf_irq);
+ ipu_free_irq(mxc_fbi_from->ipu, mxc_fbi_from->ipu_ch_nf_irq, fbi_from);
+ ipu_free_irq(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch_nf_irq, fbi_to);
+
+ if (mxc_fbi_from->cur_blank == FB_BLANK_UNBLANK) {
+ if (mxc_fbi_to->cur_blank == FB_BLANK_UNBLANK)
+ swap_mode = BOTH_ON;
+ else
+ swap_mode = SRC_ON;
+ } else {
+ if (mxc_fbi_to->cur_blank == FB_BLANK_UNBLANK)
+ swap_mode = TGT_ON;
+ else
+ swap_mode = BOTH_OFF;
+ }
+
+ switch (swap_mode) {
+ case BOTH_ON:
+ /* disable target->switch src->enable target */
+ _swap_channels(fbi_from, fbi_to, true);
+ break;
+ case SRC_ON:
+ /* just switch src */
+ _swap_channels(fbi_from, fbi_to, false);
+ break;
+ case TGT_ON:
+ /* just switch target */
+ _swap_channels(fbi_to, fbi_from, false);
+ break;
+ case BOTH_OFF:
+ /* switch directly, no more need to do */
+ mxc_fbi_to->ipu_ch = mxc_fbi_from->ipu_ch;
+ mxc_fbi_from->ipu_ch = ch_to;
+ i = mxc_fbi_from->ipu_ch_irq;
+ mxc_fbi_from->ipu_ch_irq = mxc_fbi_to->ipu_ch_irq;
+ mxc_fbi_to->ipu_ch_irq = i;
+ i = mxc_fbi_from->ipu_ch_nf_irq;
+ mxc_fbi_from->ipu_ch_nf_irq = mxc_fbi_to->ipu_ch_nf_irq;
+ mxc_fbi_to->ipu_ch_nf_irq = i;
+ break;
+ default:
+ break;
+ }
+
+ if (ipu_request_irq(mxc_fbi_from->ipu, mxc_fbi_from->ipu_ch_irq,
+ mxcfb_irq_handler, IPU_IRQF_ONESHOT,
+ MXCFB_NAME, fbi_from) != 0) {
+ dev_err(fbi_from->device, "Error registering irq %d\n",
+ mxc_fbi_from->ipu_ch_irq);
+ return -EBUSY;
+ }
+ ipu_disable_irq(mxc_fbi_from->ipu, mxc_fbi_from->ipu_ch_irq);
+ if (ipu_request_irq(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch_irq,
+ mxcfb_irq_handler, IPU_IRQF_ONESHOT,
+ MXCFB_NAME, fbi_to) != 0) {
+ dev_err(fbi_to->device, "Error registering irq %d\n",
+ mxc_fbi_to->ipu_ch_irq);
+ return -EBUSY;
+ }
+ ipu_disable_irq(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch_irq);
+ if (ipu_request_irq(mxc_fbi_from->ipu, mxc_fbi_from->ipu_ch_nf_irq,
+ mxcfb_nf_irq_handler, IPU_IRQF_ONESHOT,
+ MXCFB_NAME, fbi_from) != 0) {
+ dev_err(fbi_from->device, "Error registering irq %d\n",
+ mxc_fbi_from->ipu_ch_nf_irq);
+ return -EBUSY;
+ }
+ ipu_disable_irq(mxc_fbi_from->ipu, mxc_fbi_from->ipu_ch_nf_irq);
+ if (ipu_request_irq(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch_nf_irq,
+ mxcfb_nf_irq_handler, IPU_IRQF_ONESHOT,
+ MXCFB_NAME, fbi_to) != 0) {
+ dev_err(fbi_to->device, "Error registering irq %d\n",
+ mxc_fbi_to->ipu_ch_nf_irq);
+ return -EBUSY;
+ }
+ ipu_disable_irq(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch_nf_irq);
+
+ return 0;
+}
+
+/*
+ * Check framebuffer variable parameters and adjust to valid values.
+ *
+ * @param var framebuffer variable parameters
+ *
+ * @param info framebuffer information pointer
+ */
+static int mxcfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ u32 vtotal;
+ u32 htotal;
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)info->par;
+
+
+ if (var->xres == 0 || var->yres == 0)
+ return 0;
+
+ /* fg should not bigger than bg */
+ if (mxc_fbi->ipu_ch == MEM_FG_SYNC) {
+ struct fb_info *fbi_tmp;
+ int bg_xres = 0, bg_yres = 0;
+ int16_t pos_x, pos_y;
+
+ bg_xres = var->xres;
+ bg_yres = var->yres;
+
+ fbi_tmp = found_registered_fb(MEM_BG_SYNC, mxc_fbi->ipu_id);
+ if (fbi_tmp) {
+ bg_xres = fbi_tmp->var.xres;
+ bg_yres = fbi_tmp->var.yres;
+ }
+
+ ipu_disp_get_window_pos(mxc_fbi->ipu, mxc_fbi->ipu_ch, &pos_x, &pos_y);
+
+ if ((var->xres + pos_x) > bg_xres)
+ var->xres = bg_xres - pos_x;
+ if ((var->yres + pos_y) > bg_yres)
+ var->yres = bg_yres - pos_y;
+ }
+
+ if (var->rotate > IPU_ROTATE_VERT_FLIP)
+ var->rotate = IPU_ROTATE_NONE;
+
+ if (var->xres_virtual < var->xres)
+ var->xres_virtual = var->xres;
+
+ if (var->yres_virtual < var->yres)
+ var->yres_virtual = var->yres * 3;
+
+ if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) &&
+ (var->bits_per_pixel != 16) && (var->bits_per_pixel != 12) &&
+ (var->bits_per_pixel != 8))
+ var->bits_per_pixel = 16;
+
+ if (check_var_pixfmt(var))
+ /* Fall back to default */
+ bpp_to_var(var->bits_per_pixel, var);
+
+ if (var->pixclock < 1000) {
+ htotal = var->xres + var->right_margin + var->hsync_len +
+ var->left_margin;
+ vtotal = var->yres + var->lower_margin + var->vsync_len +
+ var->upper_margin;
+ var->pixclock = (vtotal * htotal * 6UL) / 100UL;
+ var->pixclock = KHZ2PICOS(var->pixclock);
+ dev_dbg(info->device,
+ "pixclock set for 60Hz refresh = %u ps\n",
+ var->pixclock);
+ }
+
+ var->height = -1;
+ var->width = -1;
+ var->grayscale = 0;
+
+ return 0;
+}
+
+static inline u_int _chan_to_field(u_int chan, struct fb_bitfield *bf)
+{
+ chan &= 0xffff;
+ chan >>= 16 - bf->length;
+ return chan << bf->offset;
+}
+
+static int mxcfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int trans, struct fb_info *fbi)
+{
+ unsigned int val;
+ int ret = 1;
+
+ /*
+ * If greyscale is true, then we convert the RGB value
+ * to greyscale no matter what visual we are using.
+ */
+ if (fbi->var.grayscale)
+ red = green = blue = (19595 * red + 38470 * green +
+ 7471 * blue) >> 16;
+ switch (fbi->fix.visual) {
+ case FB_VISUAL_TRUECOLOR:
+ /*
+ * 16-bit True Colour. We encode the RGB value
+ * according to the RGB bitfield information.
+ */
+ if (regno < 16) {
+ u32 *pal = fbi->pseudo_palette;
+
+ val = _chan_to_field(red, &fbi->var.red);
+ val |= _chan_to_field(green, &fbi->var.green);
+ val |= _chan_to_field(blue, &fbi->var.blue);
+
+ pal[regno] = val;
+ ret = 0;
+ }
+ break;
+
+ case FB_VISUAL_STATIC_PSEUDOCOLOR:
+ case FB_VISUAL_PSEUDOCOLOR:
+ break;
+ }
+
+ return ret;
+}
+
+static void mxcfb_vsync_pre_work(struct work_struct *work)
+{
+ struct mxcfb_info *mxc_fbi =
+ container_of(work, struct mxcfb_info, vsync_pre_work);
+ char *envp[2];
+ char buf[64];
+ unsigned long flags;
+
+ spin_lock_irqsave(&mxc_fbi->lock, flags);
+ snprintf(buf, sizeof(buf), "VSYNC=%llu",
+ ktime_to_ns(mxc_fbi->vsync_pre_timestamp));
+ spin_unlock_irqrestore(&mxc_fbi->lock, flags);
+
+ envp[0] = buf;
+ envp[1] = NULL;
+ kobject_uevent_env(&mxc_fbi->fbi->dev->kobj, KOBJ_CHANGE, envp);
+}
+
+static void mxcfb_enable_vsync_pre(struct mxcfb_info *mxc_fbi)
+{
+ ipu_clear_irq(mxc_fbi->ipu, mxc_fbi->ipu_vsync_pre_irq);
+ ipu_enable_irq(mxc_fbi->ipu, mxc_fbi->ipu_vsync_pre_irq);
+}
+
+static void mxcfb_disable_vsync_pre(struct mxcfb_info *mxc_fbi)
+{
+ ipu_disable_irq(mxc_fbi->ipu, mxc_fbi->ipu_vsync_pre_irq);
+}
+
+/*
+ * Function to handle custom ioctls for MXC framebuffer.
+ *
+ * @param inode inode struct
+ *
+ * @param file file struct
+ *
+ * @param cmd Ioctl command to handle
+ *
+ * @param arg User pointer to command arguments
+ *
+ * @param fbi framebuffer information pointer
+ */
+static int mxcfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
+{
+ int retval = 0;
+ int __user *argp = (void __user *)arg;
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
+
+ switch (cmd) {
+ case MXCFB_SET_GBL_ALPHA:
+ {
+ struct mxcfb_gbl_alpha ga;
+
+ if (copy_from_user(&ga, (void *)arg, sizeof(ga))) {
+ retval = -EFAULT;
+ break;
+ }
+
+ if (ipu_disp_set_global_alpha(mxc_fbi->ipu,
+ mxc_fbi->ipu_ch,
+ (bool)ga.enable,
+ ga.alpha)) {
+ retval = -EINVAL;
+ break;
+ }
+
+ if (ga.enable)
+ mxc_fbi->alpha_chan_en = false;
+
+ if (ga.enable)
+ dev_dbg(fbi->device,
+ "Set global alpha of %s to %d\n",
+ fbi->fix.id, ga.alpha);
+ break;
+ }
+ case MXCFB_SET_LOC_ALPHA:
+ {
+ struct mxcfb_loc_alpha la;
+
+ if (copy_from_user(&la, (void *)arg, sizeof(la))) {
+ retval = -EFAULT;
+ break;
+ }
+
+ if (ipu_disp_set_global_alpha(mxc_fbi->ipu, mxc_fbi->ipu_ch,
+ !(bool)la.enable, 0)) {
+ retval = -EINVAL;
+ break;
+ }
+
+ if (la.enable && !la.alpha_in_pixel) {
+ struct fb_info *fbi_tmp;
+ ipu_channel_t ipu_ch;
+
+ mxc_fbi->alpha_chan_en = true;
+
+ if (mxc_fbi->ipu_ch == MEM_FG_SYNC)
+ ipu_ch = MEM_BG_SYNC;
+ else if (mxc_fbi->ipu_ch == MEM_BG_SYNC)
+ ipu_ch = MEM_FG_SYNC;
+ else {
+ retval = -EINVAL;
+ break;
+ }
+
+ fbi_tmp = found_registered_fb(ipu_ch, mxc_fbi->ipu_id);
+ if (fbi_tmp)
+ ((struct mxcfb_info *)(fbi_tmp->par))->alpha_chan_en = false;
+ } else
+ mxc_fbi->alpha_chan_en = false;
+
+ fbi->var.activate = (fbi->var.activate & ~FB_ACTIVATE_MASK) |
+ FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;
+ mxcfb_set_par(fbi);
+
+ la.alpha_phy_addr0 = mxc_fbi->alpha_phy_addr0;
+ la.alpha_phy_addr1 = mxc_fbi->alpha_phy_addr1;
+ if (copy_to_user((void *)arg, &la, sizeof(la))) {
+ retval = -EFAULT;
+ break;
+ }
+
+ if (la.enable)
+ dev_dbg(fbi->device,
+ "Enable DP local alpha for %s\n",
+ fbi->fix.id);
+ break;
+ }
+ case MXCFB_SET_LOC_ALP_BUF:
+ {
+ unsigned long base;
+ uint32_t ipu_alp_ch_irq;
+
+ if (!(((mxc_fbi->ipu_ch == MEM_FG_SYNC) ||
+ (mxc_fbi->ipu_ch == MEM_BG_SYNC)) &&
+ (mxc_fbi->alpha_chan_en))) {
+ dev_err(fbi->device,
+ "Should use background or overlay "
+ "framebuffer to set the alpha buffer "
+ "number\n");
+ return -EINVAL;
+ }
+
+ if (get_user(base, argp))
+ return -EFAULT;
+
+ if (base != mxc_fbi->alpha_phy_addr0 &&
+ base != mxc_fbi->alpha_phy_addr1) {
+ dev_err(fbi->device,
+ "Wrong alpha buffer physical address "
+ "%lu\n", base);
+ return -EINVAL;
+ }
+
+ if (mxc_fbi->ipu_ch == MEM_FG_SYNC)
+ ipu_alp_ch_irq = IPU_IRQ_FG_ALPHA_SYNC_EOF;
+ else
+ ipu_alp_ch_irq = IPU_IRQ_BG_ALPHA_SYNC_EOF;
+
+ retval = wait_for_completion_timeout(
+ &mxc_fbi->alpha_flip_complete, HZ/2);
+ if (retval == 0) {
+ dev_err(fbi->device, "timeout when waiting for alpha flip irq\n");
+ retval = -ETIMEDOUT;
+ break;
+ }
+
+ mxc_fbi->cur_ipu_alpha_buf =
+ !mxc_fbi->cur_ipu_alpha_buf;
+ if (ipu_update_channel_buffer(mxc_fbi->ipu, mxc_fbi->ipu_ch,
+ IPU_ALPHA_IN_BUFFER,
+ mxc_fbi->
+ cur_ipu_alpha_buf,
+ base) == 0) {
+ ipu_select_buffer(mxc_fbi->ipu, mxc_fbi->ipu_ch,
+ IPU_ALPHA_IN_BUFFER,
+ mxc_fbi->cur_ipu_alpha_buf);
+ ipu_clear_irq(mxc_fbi->ipu, ipu_alp_ch_irq);
+ ipu_enable_irq(mxc_fbi->ipu, ipu_alp_ch_irq);
+ } else {
+ dev_err(fbi->device,
+ "Error updating %s SDC alpha buf %d "
+ "to address=0x%08lX\n",
+ fbi->fix.id,
+ mxc_fbi->cur_ipu_alpha_buf, base);
+ }
+ break;
+ }
+ case MXCFB_SET_CLR_KEY:
+ {
+ struct mxcfb_color_key key;
+ if (copy_from_user(&key, (void *)arg, sizeof(key))) {
+ retval = -EFAULT;
+ break;
+ }
+ retval = ipu_disp_set_color_key(mxc_fbi->ipu, mxc_fbi->ipu_ch,
+ key.enable,
+ key.color_key);
+ dev_dbg(fbi->device, "Set color key to 0x%08X\n",
+ key.color_key);
+ break;
+ }
+ case MXCFB_SET_GAMMA:
+ {
+ struct mxcfb_gamma gamma;
+ if (copy_from_user(&gamma, (void *)arg, sizeof(gamma))) {
+ retval = -EFAULT;
+ break;
+ }
+ retval = ipu_disp_set_gamma_correction(mxc_fbi->ipu,
+ mxc_fbi->ipu_ch,
+ gamma.enable,
+ gamma.constk,
+ gamma.slopek);
+ break;
+ }
+ case MXCFB_WAIT_FOR_VSYNC:
+ {
+ if (mxc_fbi->ipu_ch == MEM_FG_SYNC) {
+ /* BG should poweron */
+ struct mxcfb_info *bg_mxcfbi = NULL;
+ struct fb_info *fbi_tmp;
+
+ fbi_tmp = found_registered_fb(MEM_BG_SYNC, mxc_fbi->ipu_id);
+ if (fbi_tmp)
+ bg_mxcfbi = ((struct mxcfb_info *)(fbi_tmp->par));
+
+ if (!bg_mxcfbi) {
+ retval = -EINVAL;
+ break;
+ }
+ if (bg_mxcfbi->cur_blank != FB_BLANK_UNBLANK) {
+ retval = -EINVAL;
+ break;
+ }
+ }
+ if (mxc_fbi->cur_blank != FB_BLANK_UNBLANK) {
+ retval = -EINVAL;
+ break;
+ }
+
+ init_completion(&mxc_fbi->vsync_complete);
+ ipu_clear_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_nf_irq);
+ ipu_enable_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_nf_irq);
+ retval = wait_for_completion_interruptible_timeout(
+ &mxc_fbi->vsync_complete, 1 * HZ);
+ if (retval == 0) {
+ dev_err(fbi->device,
+ "MXCFB_WAIT_FOR_VSYNC: timeout %d\n",
+ retval);
+ retval = -ETIME;
+ } else if (retval > 0) {
+ retval = 0;
+ }
+ break;
+ }
+ case FBIO_ALLOC:
+ {
+ int size;
+ struct mxcfb_alloc_list *mem;
+
+ mem = kzalloc(sizeof(*mem), GFP_KERNEL);
+ if (mem == NULL)
+ return -ENOMEM;
+
+ if (get_user(size, argp))
+ return -EFAULT;
+
+ mem->size = PAGE_ALIGN(size);
+
+ mem->cpu_addr = dma_alloc_coherent(fbi->device, size,
+ &mem->phy_addr,
+ GFP_KERNEL);
+ if (mem->cpu_addr == NULL) {
+ kfree(mem);
+ return -ENOMEM;
+ }
+
+ list_add(&mem->list, &fb_alloc_list);
+
+ dev_dbg(fbi->device, "allocated %d bytes @ 0x%08X\n",
+ mem->size, mem->phy_addr);
+
+ if (put_user(mem->phy_addr, argp))
+ return -EFAULT;
+
+ break;
+ }
+ case FBIO_FREE:
+ {
+ unsigned long offset;
+ struct mxcfb_alloc_list *mem;
+
+ if (get_user(offset, argp))
+ return -EFAULT;
+
+ retval = -EINVAL;
+ list_for_each_entry(mem, &fb_alloc_list, list) {
+ if (mem->phy_addr == offset) {
+ list_del(&mem->list);
+ dma_free_coherent(fbi->device,
+ mem->size,
+ mem->cpu_addr,
+ mem->phy_addr);
+ kfree(mem);
+ retval = 0;
+ break;
+ }
+ }
+
+ break;
+ }
+ case MXCFB_SET_OVERLAY_POS:
+ {
+ struct mxcfb_pos pos;
+ struct fb_info *bg_fbi = NULL;
+ struct mxcfb_info *bg_mxcfbi = NULL;
+
+ if (mxc_fbi->ipu_ch != MEM_FG_SYNC) {
+ dev_err(fbi->device, "Should use the overlay "
+ "framebuffer to set the position of "
+ "the overlay window\n");
+ retval = -EINVAL;
+ break;
+ }
+
+ if (copy_from_user(&pos, (void *)arg, sizeof(pos))) {
+ retval = -EFAULT;
+ break;
+ }
+
+ bg_fbi = found_registered_fb(MEM_BG_SYNC, mxc_fbi->ipu_id);
+ if (bg_fbi)
+ bg_mxcfbi = ((struct mxcfb_info *)(bg_fbi->par));
+
+ if (bg_fbi == NULL) {
+ dev_err(fbi->device, "Cannot find the "
+ "background framebuffer\n");
+ retval = -ENOENT;
+ break;
+ }
+
+ /* if fb is unblank, check if the pos fit the display */
+ if (mxc_fbi->cur_blank == FB_BLANK_UNBLANK) {
+ if (fbi->var.xres + pos.x > bg_fbi->var.xres) {
+ if (bg_fbi->var.xres < fbi->var.xres)
+ pos.x = 0;
+ else
+ pos.x = bg_fbi->var.xres - fbi->var.xres;
+ }
+ if (fbi->var.yres + pos.y > bg_fbi->var.yres) {
+ if (bg_fbi->var.yres < fbi->var.yres)
+ pos.y = 0;
+ else
+ pos.y = bg_fbi->var.yres - fbi->var.yres;
+ }
+ }
+
+ retval = ipu_disp_set_window_pos(mxc_fbi->ipu, mxc_fbi->ipu_ch,
+ pos.x, pos.y);
+
+ if (copy_to_user((void *)arg, &pos, sizeof(pos))) {
+ retval = -EFAULT;
+ break;
+ }
+ break;
+ }
+ case MXCFB_GET_FB_IPU_CHAN:
+ {
+ struct mxcfb_info *mxc_fbi =
+ (struct mxcfb_info *)fbi->par;
+
+ if (put_user(mxc_fbi->ipu_ch, argp))
+ return -EFAULT;
+ break;
+ }
+ case MXCFB_GET_DIFMT:
+ {
+ struct mxcfb_info *mxc_fbi =
+ (struct mxcfb_info *)fbi->par;
+
+ if (put_user(mxc_fbi->ipu_di_pix_fmt, argp))
+ return -EFAULT;
+ break;
+ }
+ case MXCFB_GET_FB_IPU_DI:
+ {
+ struct mxcfb_info *mxc_fbi =
+ (struct mxcfb_info *)fbi->par;
+
+ if (put_user(mxc_fbi->ipu_di, argp))
+ return -EFAULT;
+ break;
+ }
+ case MXCFB_GET_FB_BLANK:
+ {
+ struct mxcfb_info *mxc_fbi =
+ (struct mxcfb_info *)fbi->par;
+
+ if (put_user(mxc_fbi->cur_blank, argp))
+ return -EFAULT;
+ break;
+ }
+ case MXCFB_SET_DIFMT:
+ {
+ struct mxcfb_info *mxc_fbi =
+ (struct mxcfb_info *)fbi->par;
+
+ if (get_user(mxc_fbi->ipu_di_pix_fmt, argp))
+ return -EFAULT;
+
+ break;
+ }
+ case MXCFB_ENABLE_VSYNC_EVENT:
+ {
+ int enable;
+ if (get_user(enable, (int __user *)arg))
+ return -EFAULT;
+
+ if (mxc_fbi->ipu_ch == MEM_FG_SYNC)
+ return -EINVAL;
+
+ /* Only can control the vsync state when screen is not blank */
+ if (mxc_fbi->vsync_pre_report_active != enable &&
+ mxc_fbi->cur_blank == FB_BLANK_UNBLANK) {
+ if (enable)
+ mxcfb_enable_vsync_pre(mxc_fbi);
+ else
+ mxcfb_disable_vsync_pre(mxc_fbi);
+ }
+
+ mxc_fbi->vsync_pre_report_active = enable;
+ break;
+ }
+ case MXCFB_CSC_UPDATE:
+ {
+ struct mxcfb_csc_matrix csc;
+
+ if (copy_from_user(&csc, (void *) arg, sizeof(csc)))
+ return -EFAULT;
+
+ if ((mxc_fbi->ipu_ch != MEM_FG_SYNC) &&
+ (mxc_fbi->ipu_ch != MEM_BG_SYNC) &&
+ (mxc_fbi->ipu_ch != MEM_BG_ASYNC0))
+ return -EFAULT;
+ ipu_set_csc_coefficients(mxc_fbi->ipu, mxc_fbi->ipu_ch,
+ csc.param);
+ }
+ default:
+ retval = -EINVAL;
+ }
+ return retval;
+}
+
+/*
+ * mxcfb_blank():
+ * Blank the display.
+ */
+static int mxcfb_blank(int blank, struct fb_info *info)
+{
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)info->par;
+ int ret = 0;
+
+ dev_dbg(info->device, "blank = %d\n", blank);
+
+ if (mxc_fbi->cur_blank == blank)
+ return 0;
+
+ mxc_fbi->next_blank = blank;
+
+ switch (blank) {
+ case FB_BLANK_POWERDOWN:
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_HSYNC_SUSPEND:
+ case FB_BLANK_NORMAL:
+ if (mxc_fbi->dispdrv && mxc_fbi->dispdrv->drv->disable)
+ mxc_fbi->dispdrv->drv->disable(mxc_fbi->dispdrv);
+ ipu_disable_channel(mxc_fbi->ipu, mxc_fbi->ipu_ch, true);
+ if (mxc_fbi->ipu_di >= 0)
+ ipu_uninit_sync_panel(mxc_fbi->ipu, mxc_fbi->ipu_di);
+ ipu_uninit_channel(mxc_fbi->ipu, mxc_fbi->ipu_ch);
+ break;
+ case FB_BLANK_UNBLANK:
+ info->var.activate = (info->var.activate & ~FB_ACTIVATE_MASK) |
+ FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;
+ ret = mxcfb_set_par(info);
+ break;
+ }
+ if (!ret)
+ mxc_fbi->cur_blank = blank;
+ return ret;
+}
+
+/*
+ * Pan or Wrap the Display
+ *
+ * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
+ *
+ * @param var Variable screen buffer information
+ * @param info Framebuffer information pointer
+ */
+static int
+mxcfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)info->par,
+ *mxc_graphic_fbi = NULL;
+ u_int y_bottom;
+ unsigned int fr_xoff, fr_yoff, fr_w, fr_h;
+ unsigned long base, active_alpha_phy_addr = 0;
+ bool loc_alpha_en = false;
+ int fb_stride;
+ int i;
+ int ret;
+
+ /* no pan display during fb blank */
+ if (mxc_fbi->ipu_ch == MEM_FG_SYNC) {
+ struct mxcfb_info *bg_mxcfbi = NULL;
+ struct fb_info *fbi_tmp;
+
+ fbi_tmp = found_registered_fb(MEM_BG_SYNC, mxc_fbi->ipu_id);
+ if (fbi_tmp)
+ bg_mxcfbi = ((struct mxcfb_info *)(fbi_tmp->par));
+ if (!bg_mxcfbi)
+ return -EINVAL;
+ if (bg_mxcfbi->cur_blank != FB_BLANK_UNBLANK)
+ return -EINVAL;
+ }
+ if (mxc_fbi->cur_blank != FB_BLANK_UNBLANK)
+ return -EINVAL;
+
+ y_bottom = var->yoffset;
+
+ if (y_bottom > info->var.yres_virtual)
+ return -EINVAL;
+
+ switch (fbi_to_pixfmt(info)) {
+ case IPU_PIX_FMT_YUV420P2:
+ case IPU_PIX_FMT_YVU420P:
+ case IPU_PIX_FMT_NV12:
+ case IPU_PIX_FMT_YUV422P:
+ case IPU_PIX_FMT_YVU422P:
+ case IPU_PIX_FMT_YUV420P:
+ case IPU_PIX_FMT_YUV444P:
+ fb_stride = info->var.xres_virtual;
+ break;
+ default:
+ fb_stride = info->fix.line_length;
+ }
+
+ base = info->fix.smem_start;
+ fr_xoff = var->xoffset;
+ fr_w = info->var.xres_virtual;
+ if (!(var->vmode & FB_VMODE_YWRAP)) {
+ dev_dbg(info->device, "Y wrap disabled\n");
+ fr_yoff = var->yoffset % info->var.yres;
+ fr_h = info->var.yres;
+ base += info->fix.line_length * info->var.yres *
+ (var->yoffset / info->var.yres);
+ } else {
+ dev_dbg(info->device, "Y wrap enabled\n");
+ fr_yoff = var->yoffset;
+ fr_h = info->var.yres_virtual;
+ }
+ base += fr_yoff * fb_stride + fr_xoff;
+
+ /* Check if DP local alpha is enabled and find the graphic fb */
+ if (mxc_fbi->ipu_ch == MEM_BG_SYNC || mxc_fbi->ipu_ch == MEM_FG_SYNC) {
+ for (i = 0; i < num_registered_fb; i++) {
+ char bg_id[] = "DISP3 BG";
+ char fg_id[] = "DISP3 FG";
+ char *idstr = registered_fb[i]->fix.id;
+ bg_id[4] += mxc_fbi->ipu_id;
+ fg_id[4] += mxc_fbi->ipu_id;
+ if ((strcmp(idstr, bg_id) == 0 ||
+ strcmp(idstr, fg_id) == 0) &&
+ ((struct mxcfb_info *)
+ (registered_fb[i]->par))->alpha_chan_en) {
+ loc_alpha_en = true;
+ mxc_graphic_fbi = (struct mxcfb_info *)
+ (registered_fb[i]->par);
+ active_alpha_phy_addr =
+ mxc_fbi->cur_ipu_alpha_buf ?
+ mxc_graphic_fbi->alpha_phy_addr1 :
+ mxc_graphic_fbi->alpha_phy_addr0;
+ dev_dbg(info->device, "Updating SDC alpha "
+ "buf %d address=0x%08lX\n",
+ !mxc_fbi->cur_ipu_alpha_buf,
+ active_alpha_phy_addr);
+ break;
+ }
+ }
+ }
+
+ ret = wait_for_completion_timeout(&mxc_fbi->flip_complete, HZ/2);
+ if (ret == 0) {
+ dev_err(info->device, "timeout when waiting for flip irq\n");
+ return -ETIMEDOUT;
+ }
+
+ ++mxc_fbi->cur_ipu_buf;
+ mxc_fbi->cur_ipu_buf %= 3;
+ mxc_fbi->cur_ipu_alpha_buf = !mxc_fbi->cur_ipu_alpha_buf;
+
+ dev_dbg(info->device, "Updating SDC %s buf %d address=0x%08lX\n",
+ info->fix.id, mxc_fbi->cur_ipu_buf, base);
+
+ if (ipu_update_channel_buffer(mxc_fbi->ipu, mxc_fbi->ipu_ch, IPU_INPUT_BUFFER,
+ mxc_fbi->cur_ipu_buf, base) == 0) {
+ /* Update the DP local alpha buffer only for graphic plane */
+ if (loc_alpha_en && mxc_graphic_fbi == mxc_fbi &&
+ ipu_update_channel_buffer(mxc_graphic_fbi->ipu, mxc_graphic_fbi->ipu_ch,
+ IPU_ALPHA_IN_BUFFER,
+ mxc_fbi->cur_ipu_alpha_buf,
+ active_alpha_phy_addr) == 0) {
+ ipu_select_buffer(mxc_graphic_fbi->ipu, mxc_graphic_fbi->ipu_ch,
+ IPU_ALPHA_IN_BUFFER,
+ mxc_fbi->cur_ipu_alpha_buf);
+ }
+
+ /* update u/v offset */
+ ipu_update_channel_offset(mxc_fbi->ipu, mxc_fbi->ipu_ch,
+ IPU_INPUT_BUFFER,
+ fbi_to_pixfmt(info),
+ fr_w,
+ fr_h,
+ fr_w,
+ 0, 0,
+ fr_yoff,
+ fr_xoff);
+
+ ipu_select_buffer(mxc_fbi->ipu, mxc_fbi->ipu_ch, IPU_INPUT_BUFFER,
+ mxc_fbi->cur_ipu_buf);
+ ipu_clear_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq);
+ ipu_enable_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq);
+ } else {
+ dev_err(info->device,
+ "Error updating SDC buf %d to address=0x%08lX, "
+ "current buf %d, buf0 ready %d, buf1 ready %d, "
+ "buf2 ready %d\n", mxc_fbi->cur_ipu_buf, base,
+ ipu_get_cur_buffer_idx(mxc_fbi->ipu, mxc_fbi->ipu_ch,
+ IPU_INPUT_BUFFER),
+ ipu_check_buffer_ready(mxc_fbi->ipu, mxc_fbi->ipu_ch,
+ IPU_INPUT_BUFFER, 0),
+ ipu_check_buffer_ready(mxc_fbi->ipu, mxc_fbi->ipu_ch,
+ IPU_INPUT_BUFFER, 1),
+ ipu_check_buffer_ready(mxc_fbi->ipu, mxc_fbi->ipu_ch,
+ IPU_INPUT_BUFFER, 2));
+ ++mxc_fbi->cur_ipu_buf;
+ mxc_fbi->cur_ipu_buf %= 3;
+ ++mxc_fbi->cur_ipu_buf;
+ mxc_fbi->cur_ipu_buf %= 3;
+ mxc_fbi->cur_ipu_alpha_buf = !mxc_fbi->cur_ipu_alpha_buf;
+ ipu_clear_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq);
+ ipu_enable_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq);
+ return -EBUSY;
+ }
+
+ dev_dbg(info->device, "Update complete\n");
+
+ info->var.yoffset = var->yoffset;
+
+ return 0;
+}
+
+/*
+ * Function to handle custom mmap for MXC framebuffer.
+ *
+ * @param fbi framebuffer information pointer
+ *
+ * @param vma Pointer to vm_area_struct
+ */
+static int mxcfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma)
+{
+ bool found = false;
+ u32 len;
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ struct mxcfb_alloc_list *mem;
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
+
+ if (offset < fbi->fix.smem_len) {
+ /* mapping framebuffer memory */
+ len = fbi->fix.smem_len - offset;
+ vma->vm_pgoff = (fbi->fix.smem_start + offset) >> PAGE_SHIFT;
+ } else if ((vma->vm_pgoff ==
+ (mxc_fbi->alpha_phy_addr0 >> PAGE_SHIFT)) ||
+ (vma->vm_pgoff ==
+ (mxc_fbi->alpha_phy_addr1 >> PAGE_SHIFT))) {
+ len = mxc_fbi->alpha_mem_len;
+ } else {
+ list_for_each_entry(mem, &fb_alloc_list, list) {
+ if (offset == mem->phy_addr) {
+ found = true;
+ len = mem->size;
+ break;
+ }
+ }
+ if (!found)
+ return -EINVAL;
+ }
+
+ len = PAGE_ALIGN(len);
+ if (vma->vm_end - vma->vm_start > len)
+ return -EINVAL;
+
+ /* make buffers bufferable */
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+ vma->vm_flags |= VM_IO | VM_RESERVED;
+
+ if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
+ vma->vm_end - vma->vm_start, vma->vm_page_prot)) {
+ dev_dbg(fbi->device, "mmap remap_pfn_range failed\n");
+ return -ENOBUFS;
+ }
+
+ return 0;
+}
+
+/*!
+ * This structure contains the pointers to the control functions that are
+ * invoked by the core framebuffer driver to perform operations like
+ * blitting, rectangle filling, copy regions and cursor definition.
+ */
+static struct fb_ops mxcfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_set_par = mxcfb_set_par,
+ .fb_check_var = mxcfb_check_var,
+ .fb_setcolreg = mxcfb_setcolreg,
+ .fb_pan_display = mxcfb_pan_display,
+ .fb_ioctl = mxcfb_ioctl,
+ .fb_mmap = mxcfb_mmap,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_blank = mxcfb_blank,
+};
+
+static irqreturn_t mxcfb_irq_handler(int irq, void *dev_id)
+{
+ struct fb_info *fbi = dev_id;
+ struct mxcfb_info *mxc_fbi = fbi->par;
+
+ complete(&mxc_fbi->flip_complete);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mxcfb_nf_irq_handler(int irq, void *dev_id)
+{
+ struct fb_info *fbi = dev_id;
+ struct mxcfb_info *mxc_fbi = fbi->par;
+
+ complete(&mxc_fbi->vsync_complete);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mxcfb_vsync_pre_irq_handler(int irq, void *dev_id)
+{
+ struct fb_info *fbi = dev_id;
+ struct mxcfb_info *mxc_fbi = fbi->par;
+
+ spin_lock(&mxc_fbi->lock);
+ mxc_fbi->vsync_pre_timestamp = ktime_get();
+ spin_unlock(&mxc_fbi->lock);
+ queue_work(mxc_fbi->vsync_pre_queue, &mxc_fbi->vsync_pre_work);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mxcfb_alpha_irq_handler(int irq, void *dev_id)
+{
+ struct fb_info *fbi = dev_id;
+ struct mxcfb_info *mxc_fbi = fbi->par;
+
+ complete(&mxc_fbi->alpha_flip_complete);
+ return IRQ_HANDLED;
+}
+
+/*
+ * Suspends the framebuffer and blanks the screen. Power management support
+ */
+static int mxcfb_core_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct fb_info *fbi = platform_get_drvdata(pdev);
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
+ int saved_blank;
+#ifdef CONFIG_FB_MXC_LOW_PWR_DISPLAY
+ void *fbmem;
+#endif
+
+ if (mxc_fbi->ovfbi) {
+ struct mxcfb_info *mxc_fbi_fg =
+ (struct mxcfb_info *)mxc_fbi->ovfbi->par;
+
+ console_lock();
+ fb_set_suspend(mxc_fbi->ovfbi, 1);
+ saved_blank = mxc_fbi_fg->cur_blank;
+ mxcfb_blank(FB_BLANK_POWERDOWN, mxc_fbi->ovfbi);
+ mxc_fbi_fg->next_blank = saved_blank;
+ console_unlock();
+ }
+
+ console_lock();
+ fb_set_suspend(fbi, 1);
+ saved_blank = mxc_fbi->cur_blank;
+ mxcfb_blank(FB_BLANK_POWERDOWN, fbi);
+ mxc_fbi->next_blank = saved_blank;
+ console_unlock();
+
+ return 0;
+}
+
+/*
+ * Suspends the framebuffer and blanks the screen. Power management support
+ */
+static int mxcfb_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct fb_info *fbi = platform_get_drvdata(pdev);
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
+
+ if (strstr(mxc_fbi->dispdrv->drv->name, "hdmi"))
+ return mxcfb_core_suspend(pdev, state);
+
+ return 0;
+}
+
+/*
+ * Resumes the framebuffer and unblanks the screen. Power management support
+ */
+static int mxcfb_core_resume(struct platform_device *pdev)
+{
+ struct fb_info *fbi = platform_get_drvdata(pdev);
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
+
+ console_lock();
+ mxcfb_blank(mxc_fbi->next_blank, fbi);
+ fb_set_suspend(fbi, 0);
+ console_unlock();
+
+ if (mxc_fbi->ovfbi) {
+ struct mxcfb_info *mxc_fbi_fg =
+ (struct mxcfb_info *)mxc_fbi->ovfbi->par;
+ console_lock();
+ mxcfb_blank(mxc_fbi_fg->next_blank, mxc_fbi->ovfbi);
+ fb_set_suspend(mxc_fbi->ovfbi, 0);
+ console_unlock();
+ }
+
+ return 0;
+}
+
+/*
+ * Resumes the framebuffer and unblanks the screen. Power management support
+ */
+static int mxcfb_resume(struct platform_device *pdev)
+{
+ struct fb_info *fbi = platform_get_drvdata(pdev);
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
+
+ if (strstr(mxc_fbi->dispdrv->drv->name, "hdmi"))
+ return mxcfb_core_resume(pdev);
+
+ return 0;
+}
+
+/*
+ * Main framebuffer functions
+ */
+
+/*!
+ * Allocates the DRAM memory for the frame buffer. This buffer is remapped
+ * into a non-cached, non-buffered, memory region to allow palette and pixel
+ * writes to occur without flushing the cache. Once this area is remapped,
+ * all virtual memory access to the video memory should occur at the new region.
+ *
+ * @param fbi framebuffer information pointer
+ *
+ * @return Error code indicating success or failure
+ */
+static int mxcfb_map_video_memory(struct fb_info *fbi)
+{
+ if (fbi->fix.smem_len < fbi->var.yres_virtual * fbi->fix.line_length)
+ fbi->fix.smem_len = fbi->var.yres_virtual *
+ fbi->fix.line_length;
+
+ fbi->screen_base = dma_alloc_writecombine(fbi->device,
+ fbi->fix.smem_len,
+ (dma_addr_t *)&fbi->fix.smem_start,
+ GFP_DMA | GFP_KERNEL);
+ if (fbi->screen_base == 0) {
+ dev_err(fbi->device, "Unable to allocate framebuffer memory\n");
+ fbi->fix.smem_len = 0;
+ fbi->fix.smem_start = 0;
+ return -EBUSY;
+ }
+
+ dev_dbg(fbi->device, "allocated fb @ paddr=0x%08X, size=%d.\n",
+ (uint32_t) fbi->fix.smem_start, fbi->fix.smem_len);
+
+ fbi->screen_size = fbi->fix.smem_len;
+
+ /* Clear the screen */
+ memset((char *)fbi->screen_base, 0, fbi->fix.smem_len);
+
+ return 0;
+}
+
+/*!
+ * De-allocates the DRAM memory for the frame buffer.
+ *
+ * @param fbi framebuffer information pointer
+ *
+ * @return Error code indicating success or failure
+ */
+static int mxcfb_unmap_video_memory(struct fb_info *fbi)
+{
+ dma_free_writecombine(fbi->device, fbi->fix.smem_len,
+ fbi->screen_base, fbi->fix.smem_start);
+ fbi->screen_base = 0;
+ fbi->fix.smem_start = 0;
+ fbi->fix.smem_len = 0;
+ return 0;
+}
+
+/*!
+ * Initializes the framebuffer information pointer. After allocating
+ * sufficient memory for the framebuffer structure, the fields are
+ * filled with custom information passed in from the configurable
+ * structures. This includes information such as bits per pixel,
+ * color maps, screen width/height and RGBA offsets.
+ *
+ * @return Framebuffer structure initialized with our information
+ */
+static struct fb_info *mxcfb_init_fbinfo(struct device *dev, struct fb_ops *ops)
+{
+ struct fb_info *fbi;
+ struct mxcfb_info *mxcfbi;
+
+ /*
+ * Allocate sufficient memory for the fb structure
+ */
+ fbi = framebuffer_alloc(sizeof(struct mxcfb_info), dev);
+ if (!fbi)
+ return NULL;
+
+ mxcfbi = (struct mxcfb_info *)fbi->par;
+
+ fbi->var.activate = FB_ACTIVATE_NOW;
+
+ fbi->fbops = ops;
+ fbi->flags = FBINFO_FLAG_DEFAULT;
+ fbi->pseudo_palette = mxcfbi->pseudo_palette;
+
+ /*
+ * Allocate colormap
+ */
+ fb_alloc_cmap(&fbi->cmap, 16, 0);
+
+ return fbi;
+}
+
+static ssize_t show_disp_chan(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct mxcfb_info *mxcfbi = (struct mxcfb_info *)info->par;
+
+ if (mxcfbi->ipu_ch == MEM_BG_SYNC)
+ return sprintf(buf, "2-layer-fb-bg\n");
+ else if (mxcfbi->ipu_ch == MEM_FG_SYNC)
+ return sprintf(buf, "2-layer-fb-fg\n");
+ else if (mxcfbi->ipu_ch == MEM_DC_SYNC)
+ return sprintf(buf, "1-layer-fb\n");
+ else
+ return sprintf(buf, "err: no display chan\n");
+}
+
+static ssize_t swap_disp_chan(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct mxcfb_info *mxcfbi = (struct mxcfb_info *)info->par;
+ struct mxcfb_info *fg_mxcfbi = NULL;
+
+ console_lock();
+ /* swap only happen between DP-BG and DC, while DP-FG disable */
+ if (((mxcfbi->ipu_ch == MEM_BG_SYNC) &&
+ (strstr(buf, "1-layer-fb") != NULL)) ||
+ ((mxcfbi->ipu_ch == MEM_DC_SYNC) &&
+ (strstr(buf, "2-layer-fb-bg") != NULL))) {
+ struct fb_info *fbi_fg;
+
+ fbi_fg = found_registered_fb(MEM_FG_SYNC, mxcfbi->ipu_id);
+ if (fbi_fg)
+ fg_mxcfbi = (struct mxcfb_info *)fbi_fg->par;
+
+ if (!fg_mxcfbi ||
+ fg_mxcfbi->cur_blank == FB_BLANK_UNBLANK) {
+ dev_err(dev,
+ "Can not switch while fb2(fb-fg) is on.\n");
+ console_unlock();
+ return count;
+ }
+
+ if (swap_channels(info) < 0)
+ dev_err(dev, "Swap display channel failed.\n");
+ }
+
+ console_unlock();
+ return count;
+}
+static DEVICE_ATTR(fsl_disp_property, 644, show_disp_chan, swap_disp_chan);
+
+static ssize_t show_disp_dev(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct mxcfb_info *mxcfbi = (struct mxcfb_info *)info->par;
+
+ if (mxcfbi->ipu_ch == MEM_FG_SYNC)
+ return sprintf(buf, "overlay\n");
+ else
+ return sprintf(buf, "%s\n", mxcfbi->dispdrv->drv->name);
+}
+static DEVICE_ATTR(fsl_disp_dev_property, S_IRUGO, show_disp_dev, NULL);
+
+static int mxcfb_dispdrv_init(struct platform_device *pdev,
+ struct fb_info *fbi)
+{
+ struct ipuv3_fb_platform_data *plat_data = pdev->dev.platform_data;
+ struct mxcfb_info *mxcfbi = (struct mxcfb_info *)fbi->par;
+ struct mxc_dispdrv_setting setting;
+ char disp_dev[32], *default_dev = "lcd";
+ int ret = 0;
+
+ setting.if_fmt = plat_data->interface_pix_fmt;
+ setting.dft_mode_str = plat_data->mode_str;
+ setting.default_bpp = plat_data->default_bpp;
+ if (!setting.default_bpp)
+ setting.default_bpp = 16;
+ setting.fbi = fbi;
+ if (!strlen(plat_data->disp_dev)) {
+ memcpy(disp_dev, default_dev, strlen(default_dev));
+ disp_dev[strlen(default_dev)] = '\0';
+ } else {
+ memcpy(disp_dev, plat_data->disp_dev,
+ strlen(plat_data->disp_dev));
+ disp_dev[strlen(plat_data->disp_dev)] = '\0';
+ }
+
+ dev_info(&pdev->dev, "register mxc display driver %s\n", disp_dev);
+
+ mxcfbi->dispdrv = mxc_dispdrv_gethandle(disp_dev, &setting);
+ if (IS_ERR(mxcfbi->dispdrv)) {
+ ret = PTR_ERR(mxcfbi->dispdrv);
+ dev_err(&pdev->dev, "NO mxc display driver found!\n");
+ return ret;
+ } else {
+ /* fix-up */
+ mxcfbi->ipu_di_pix_fmt = setting.if_fmt;
+ mxcfbi->default_bpp = setting.default_bpp;
+
+ /* setting */
+ mxcfbi->ipu_id = setting.dev_id;
+ mxcfbi->ipu_di = setting.disp_id;
+ }
+
+ return ret;
+}
+
+/*
+ * Parse user specified options (`video=trident:')
+ * example:
+ * video=mxcfb0:dev=lcd,800x480M-16@55,if=RGB565,bpp=16,noaccel
+ * video=mxcfb0:dev=lcd,800x480M-16@55,if=RGB565,fbpix=RGB565
+ */
+static int mxcfb_option_setup(struct platform_device *pdev, struct fb_info *fbi)
+{
+ struct ipuv3_fb_platform_data *pdata = pdev->dev.platform_data;
+ char *options, *opt, *fb_mode_str = NULL;
+ char name[] = "mxcfb0";
+ uint32_t fb_pix_fmt = 0;
+
+ name[5] += pdev->id;
+ if (fb_get_options(name, &options)) {
+ dev_err(&pdev->dev, "Can't get fb option for %s!\n", name);
+ return -ENODEV;
+ }
+
+ if (!options || !*options)
+ return 0;
+
+ while ((opt = strsep(&options, ",")) != NULL) {
+ if (!*opt)
+ continue;
+
+ if (!strncmp(opt, "dev=", 4)) {
+ memcpy(pdata->disp_dev, opt + 4, strlen(opt) - 4);
+ pdata->disp_dev[strlen(opt) - 4] = '\0';
+ } else if (!strncmp(opt, "if=", 3)) {
+ if (!strncmp(opt+3, "RGB24", 5))
+ pdata->interface_pix_fmt = IPU_PIX_FMT_RGB24;
+ else if (!strncmp(opt+3, "BGR24", 5))
+ pdata->interface_pix_fmt = IPU_PIX_FMT_BGR24;
+ else if (!strncmp(opt+3, "GBR24", 5))
+ pdata->interface_pix_fmt = IPU_PIX_FMT_GBR24;
+ else if (!strncmp(opt+3, "RGB565", 6))
+ pdata->interface_pix_fmt = IPU_PIX_FMT_RGB565;
+ else if (!strncmp(opt+3, "RGB666", 6))
+ pdata->interface_pix_fmt = IPU_PIX_FMT_RGB666;
+ else if (!strncmp(opt+3, "YUV444", 6))
+ pdata->interface_pix_fmt = IPU_PIX_FMT_YUV444;
+ else if (!strncmp(opt+3, "LVDS666", 7))
+ pdata->interface_pix_fmt = IPU_PIX_FMT_LVDS666;
+ else if (!strncmp(opt+3, "YUYV16", 6))
+ pdata->interface_pix_fmt = IPU_PIX_FMT_YUYV;
+ else if (!strncmp(opt+3, "UYVY16", 6))
+ pdata->interface_pix_fmt = IPU_PIX_FMT_UYVY;
+ else if (!strncmp(opt+3, "YVYU16", 6))
+ pdata->interface_pix_fmt = IPU_PIX_FMT_YVYU;
+ else if (!strncmp(opt+3, "VYUY16", 6))
+ pdata->interface_pix_fmt = IPU_PIX_FMT_VYUY;
+ } else if (!strncmp(opt, "fbpix=", 6)) {
+ if (!strncmp(opt+6, "RGB24", 5))
+ fb_pix_fmt = IPU_PIX_FMT_RGB24;
+ else if (!strncmp(opt+6, "BGR24", 5))
+ fb_pix_fmt = IPU_PIX_FMT_BGR24;
+ else if (!strncmp(opt+6, "RGB32", 5))
+ fb_pix_fmt = IPU_PIX_FMT_RGB32;
+ else if (!strncmp(opt+6, "BGR32", 5))
+ fb_pix_fmt = IPU_PIX_FMT_BGR32;
+ else if (!strncmp(opt+6, "ABGR32", 6))
+ fb_pix_fmt = IPU_PIX_FMT_ABGR32;
+ else if (!strncmp(opt+6, "RGB565", 6))
+ fb_pix_fmt = IPU_PIX_FMT_RGB565;
+
+ if (fb_pix_fmt) {
+ pixfmt_to_var(fb_pix_fmt, &fbi->var);
+ pdata->default_bpp =
+ fbi->var.bits_per_pixel;
+ }
+ } else if (!strncmp(opt, "int_clk", 7)) {
+ pdata->int_clk = true;
+ continue;
+ } else if (!strncmp(opt, "bpp=", 4)) {
+ /* bpp setting cannot overwirte fbpix setting */
+ if (fb_pix_fmt)
+ continue;
+
+ pdata->default_bpp =
+ simple_strtoul(opt + 4, NULL, 0);
+
+ fb_pix_fmt = bpp_to_pixfmt(pdata->default_bpp);
+ if (fb_pix_fmt)
+ pixfmt_to_var(fb_pix_fmt, &fbi->var);
+ } else
+ fb_mode_str = opt;
+ }
+
+ if (fb_mode_str)
+ pdata->mode_str = fb_mode_str;
+
+ return 0;
+}
+
+static int mxcfb_register(struct fb_info *fbi)
+{
+ struct mxcfb_info *mxcfbi = (struct mxcfb_info *)fbi->par;
+ struct fb_videomode m;
+ int ret = 0;
+ char bg0_id[] = "DISP3 BG";
+ char bg1_id[] = "DISP3 BG - DI1";
+ char fg_id[] = "DISP3 FG";
+
+ if (mxcfbi->ipu_di == 0) {
+ bg0_id[4] += mxcfbi->ipu_id;
+ strcpy(fbi->fix.id, bg0_id);
+ } else if (mxcfbi->ipu_di == 1) {
+ bg1_id[4] += mxcfbi->ipu_id;
+ strcpy(fbi->fix.id, bg1_id);
+ } else { /* Overlay */
+ fg_id[4] += mxcfbi->ipu_id;
+ strcpy(fbi->fix.id, fg_id);
+ }
+
+ mxcfb_check_var(&fbi->var, fbi);
+
+ mxcfb_set_fix(fbi);
+
+ /* Added first mode to fbi modelist. */
+ if (!fbi->modelist.next || !fbi->modelist.prev)
+ INIT_LIST_HEAD(&fbi->modelist);
+ fb_var_to_videomode(&m, &fbi->var);
+ fb_add_videomode(&m, &fbi->modelist);
+
+ if (ipu_request_irq(mxcfbi->ipu, mxcfbi->ipu_ch_irq,
+ mxcfb_irq_handler, IPU_IRQF_ONESHOT, MXCFB_NAME, fbi) != 0) {
+ dev_err(fbi->device, "Error registering EOF irq handler.\n");
+ ret = -EBUSY;
+ goto err0;
+ }
+ ipu_disable_irq(mxcfbi->ipu, mxcfbi->ipu_ch_irq);
+ if (ipu_request_irq(mxcfbi->ipu, mxcfbi->ipu_ch_nf_irq,
+ mxcfb_nf_irq_handler, IPU_IRQF_ONESHOT, MXCFB_NAME, fbi) != 0) {
+ dev_err(fbi->device, "Error registering NFACK irq handler.\n");
+ ret = -EBUSY;
+ goto err1;
+ }
+ ipu_disable_irq(mxcfbi->ipu, mxcfbi->ipu_ch_nf_irq);
+
+ if (mxcfbi->ipu_alp_ch_irq != -1)
+ if (ipu_request_irq(mxcfbi->ipu, mxcfbi->ipu_alp_ch_irq,
+ mxcfb_alpha_irq_handler, IPU_IRQF_ONESHOT,
+ MXCFB_NAME, fbi) != 0) {
+ dev_err(fbi->device, "Error registering alpha irq "
+ "handler.\n");
+ ret = -EBUSY;
+ goto err2;
+ }
+
+ if (!mxcfbi->late_init) {
+ fbi->var.activate |= FB_ACTIVATE_FORCE;
+ console_lock();
+ fbi->flags |= FBINFO_MISC_USEREVENT;
+ ret = fb_set_var(fbi, &fbi->var);
+ fbi->flags &= ~FBINFO_MISC_USEREVENT;
+ console_unlock();
+ if (ret < 0) {
+ dev_err(fbi->device, "Error fb_set_var ret:%d\n", ret);
+ goto err3;
+ }
+
+ if (mxcfbi->next_blank == FB_BLANK_UNBLANK) {
+ console_lock();
+ ret = fb_blank(fbi, FB_BLANK_UNBLANK);
+ console_unlock();
+ if (ret < 0) {
+ dev_err(fbi->device,
+ "Error fb_blank ret:%d\n", ret);
+ goto err4;
+ }
+ }
+ } else {
+ /*
+ * Setup the channel again though bootloader
+ * has done this, then set_par() can stop the
+ * channel neatly and re-initialize it .
+ */
+ if (mxcfbi->next_blank == FB_BLANK_UNBLANK) {
+ console_lock();
+ _setup_disp_channel1(fbi);
+ ipu_enable_channel(mxcfbi->ipu, mxcfbi->ipu_ch);
+ console_unlock();
+ }
+ }
+
+
+ ret = register_framebuffer(fbi);
+ if (ret < 0)
+ goto err5;
+
+ return ret;
+err5:
+ if (mxcfbi->next_blank == FB_BLANK_UNBLANK) {
+ console_lock();
+ if (!mxcfbi->late_init)
+ fb_blank(fbi, FB_BLANK_POWERDOWN);
+ else {
+ ipu_disable_channel(mxcfbi->ipu, mxcfbi->ipu_ch,
+ true);
+ ipu_uninit_channel(mxcfbi->ipu, mxcfbi->ipu_ch);
+ }
+ console_unlock();
+ }
+err4:
+err3:
+ if (mxcfbi->ipu_alp_ch_irq != -1)
+ ipu_free_irq(mxcfbi->ipu, mxcfbi->ipu_alp_ch_irq, fbi);
+err2:
+ ipu_free_irq(mxcfbi->ipu, mxcfbi->ipu_ch_nf_irq, fbi);
+err1:
+ ipu_free_irq(mxcfbi->ipu, mxcfbi->ipu_ch_irq, fbi);
+err0:
+ return ret;
+}
+
+static void mxcfb_unregister(struct fb_info *fbi)
+{
+ struct mxcfb_info *mxcfbi = (struct mxcfb_info *)fbi->par;
+
+ if (mxcfbi->ipu_alp_ch_irq != -1)
+ ipu_free_irq(mxcfbi->ipu, mxcfbi->ipu_alp_ch_irq, fbi);
+ if (mxcfbi->ipu_ch_irq)
+ ipu_free_irq(mxcfbi->ipu, mxcfbi->ipu_ch_irq, fbi);
+ if (mxcfbi->ipu_ch_nf_irq)
+ ipu_free_irq(mxcfbi->ipu, mxcfbi->ipu_ch_nf_irq, fbi);
+
+ unregister_framebuffer(fbi);
+}
+
+static int mxcfb_setup_overlay(struct platform_device *pdev,
+ struct fb_info *fbi_bg, struct resource *res)
+{
+ struct fb_info *ovfbi;
+ struct mxcfb_info *mxcfbi_bg = (struct mxcfb_info *)fbi_bg->par;
+ struct mxcfb_info *mxcfbi_fg;
+ int ret = 0;
+
+ ovfbi = mxcfb_init_fbinfo(&pdev->dev, &mxcfb_ops);
+ if (!ovfbi) {
+ ret = -ENOMEM;
+ goto init_ovfbinfo_failed;
+ }
+ mxcfbi_fg = (struct mxcfb_info *)ovfbi->par;
+
+ mxcfbi_fg->ipu = ipu_get_soc(mxcfbi_bg->ipu_id);
+ if (IS_ERR(mxcfbi_fg->ipu)) {
+ ret = -ENODEV;
+ goto get_ipu_failed;
+ }
+ mxcfbi_fg->ipu_id = mxcfbi_bg->ipu_id;
+ mxcfbi_fg->ipu_ch_irq = IPU_IRQ_FG_SYNC_EOF;
+ mxcfbi_fg->ipu_ch_nf_irq = IPU_IRQ_FG_SYNC_NFACK;
+ mxcfbi_fg->ipu_alp_ch_irq = IPU_IRQ_FG_ALPHA_SYNC_EOF;
+ /* Vsync-pre irq is invalid for overlay channel. */
+ mxcfbi_fg->ipu_vsync_pre_irq = -1;
+ mxcfbi_fg->ipu_ch = MEM_FG_SYNC;
+ mxcfbi_fg->ipu_di = -1;
+ mxcfbi_fg->ipu_di_pix_fmt = mxcfbi_bg->ipu_di_pix_fmt;
+ mxcfbi_fg->overlay = true;
+ mxcfbi_fg->cur_blank = mxcfbi_fg->next_blank = FB_BLANK_POWERDOWN;
+
+ /* Need dummy values until real panel is configured */
+ ovfbi->var.xres = 240;
+ ovfbi->var.yres = 320;
+
+ if (res && res->start && res->end) {
+ ovfbi->fix.smem_len = res->end - res->start + 1;
+ ovfbi->fix.smem_start = res->start;
+ ovfbi->screen_base = ioremap(
+ ovfbi->fix.smem_start,
+ ovfbi->fix.smem_len);
+ }
+
+ ret = mxcfb_register(ovfbi);
+ if (ret < 0)
+ goto register_ov_failed;
+
+ mxcfbi_bg->ovfbi = ovfbi;
+
+ return ret;
+
+register_ov_failed:
+get_ipu_failed:
+ fb_dealloc_cmap(&ovfbi->cmap);
+ framebuffer_release(ovfbi);
+init_ovfbinfo_failed:
+ return ret;
+}
+
+static void mxcfb_unsetup_overlay(struct fb_info *fbi_bg)
+{
+ struct mxcfb_info *mxcfbi_bg = (struct mxcfb_info *)fbi_bg->par;
+ struct fb_info *ovfbi = mxcfbi_bg->ovfbi;
+
+ mxcfb_unregister(ovfbi);
+
+ if (&ovfbi->cmap)
+ fb_dealloc_cmap(&ovfbi->cmap);
+ framebuffer_release(ovfbi);
+}
+
+static bool ipu_usage[2][2];
+static int ipu_test_set_usage(int ipu, int di)
+{
+ if (ipu_usage[ipu][di])
+ return -EBUSY;
+ else
+ ipu_usage[ipu][di] = true;
+ return 0;
+}
+
+static void ipu_clear_usage(int ipu, int di)
+{
+ ipu_usage[ipu][di] = false;
+}
+
+/*!
+ * Probe routine for the framebuffer driver. It is called during the
+ * driver binding process. The following functions are performed in
+ * this routine: Framebuffer initialization, Memory allocation and
+ * mapping, Framebuffer registration, IPU initialization.
+ *
+ * @return Appropriate error code to the kernel common code
+ */
+static int mxcfb_probe(struct platform_device *pdev)
+{
+ struct ipuv3_fb_platform_data *plat_data = pdev->dev.platform_data;
+ struct fb_info *fbi;
+ struct mxcfb_info *mxcfbi;
+ struct resource *res;
+ struct device *disp_dev;
+ char buf[32];
+ int ret = 0;
+
+ /* Initialize FB structures */
+ fbi = mxcfb_init_fbinfo(&pdev->dev, &mxcfb_ops);
+ if (!fbi) {
+ ret = -ENOMEM;
+ goto init_fbinfo_failed;
+ }
+
+ ret = mxcfb_option_setup(pdev, fbi);
+ if (ret)
+ goto get_fb_option_failed;
+
+ mxcfbi = (struct mxcfb_info *)fbi->par;
+ spin_lock_init(&mxcfbi->lock);
+ mxcfbi->fbi = fbi;
+ mxcfbi->ipu_int_clk = plat_data->int_clk;
+ mxcfbi->late_init = plat_data->late_init;
+ mxcfbi->first_set_par = true;
+ ret = mxcfb_dispdrv_init(pdev, fbi);
+ if (ret < 0)
+ goto init_dispdrv_failed;
+
+ ret = ipu_test_set_usage(mxcfbi->ipu_id, mxcfbi->ipu_di);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "ipu%d-di%d already in use\n",
+ mxcfbi->ipu_id, mxcfbi->ipu_di);
+ goto ipu_in_busy;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res && res->start && res->end) {
+ fbi->fix.smem_len = res->end - res->start + 1;
+ fbi->fix.smem_start = res->start;
+ fbi->screen_base = ioremap(fbi->fix.smem_start, fbi->fix.smem_len);
+ /* Do not clear the fb content drawn in bootloader. */
+ if (!mxcfbi->late_init)
+ memset(fbi->screen_base, 0, fbi->fix.smem_len);
+ }
+
+ mxcfbi->ipu = ipu_get_soc(mxcfbi->ipu_id);
+ if (IS_ERR(mxcfbi->ipu)) {
+ ret = -ENODEV;
+ goto get_ipu_failed;
+ }
+
+ /* first user uses DP with alpha feature */
+ if (!g_dp_in_use[mxcfbi->ipu_id]) {
+ mxcfbi->ipu_ch_irq = IPU_IRQ_BG_SYNC_EOF;
+ mxcfbi->ipu_ch_nf_irq = IPU_IRQ_BG_SYNC_NFACK;
+ mxcfbi->ipu_alp_ch_irq = IPU_IRQ_BG_ALPHA_SYNC_EOF;
+ mxcfbi->ipu_vsync_pre_irq = mxcfbi->ipu_di ?
+ IPU_IRQ_VSYNC_PRE_1 :
+ IPU_IRQ_VSYNC_PRE_0;
+ mxcfbi->ipu_ch = MEM_BG_SYNC;
+ /* Unblank the primary fb only by default */
+ if (pdev->id == 0)
+ mxcfbi->cur_blank = mxcfbi->next_blank = FB_BLANK_UNBLANK;
+ else
+ mxcfbi->cur_blank = mxcfbi->next_blank = FB_BLANK_POWERDOWN;
+
+ ret = mxcfb_register(fbi);
+ if (ret < 0)
+ goto mxcfb_register_failed;
+
+ ipu_disp_set_global_alpha(mxcfbi->ipu, mxcfbi->ipu_ch,
+ true, 0x80);
+ ipu_disp_set_color_key(mxcfbi->ipu, mxcfbi->ipu_ch, false, 0);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ ret = mxcfb_setup_overlay(pdev, fbi, res);
+
+ if (ret < 0) {
+ mxcfb_unregister(fbi);
+ goto mxcfb_setupoverlay_failed;
+ }
+
+ g_dp_in_use[mxcfbi->ipu_id] = true;
+
+ ret = device_create_file(mxcfbi->ovfbi->dev,
+ &dev_attr_fsl_disp_property);
+ if (ret)
+ dev_err(mxcfbi->ovfbi->dev, "Error %d on creating "
+ "file for disp property\n",
+ ret);
+
+ ret = device_create_file(mxcfbi->ovfbi->dev,
+ &dev_attr_fsl_disp_dev_property);
+ if (ret)
+ dev_err(mxcfbi->ovfbi->dev, "Error %d on creating "
+ "file for disp device "
+ "propety\n", ret);
+ } else {
+ mxcfbi->ipu_ch_irq = IPU_IRQ_DC_SYNC_EOF;
+ mxcfbi->ipu_ch_nf_irq = IPU_IRQ_DC_SYNC_NFACK;
+ mxcfbi->ipu_alp_ch_irq = -1;
+ mxcfbi->ipu_vsync_pre_irq = mxcfbi->ipu_di ?
+ IPU_IRQ_VSYNC_PRE_1 :
+ IPU_IRQ_VSYNC_PRE_0;
+ mxcfbi->ipu_ch = MEM_DC_SYNC;
+ mxcfbi->cur_blank = mxcfbi->next_blank = FB_BLANK_POWERDOWN;
+
+ ret = mxcfb_register(fbi);
+ if (ret < 0)
+ goto mxcfb_register_failed;
+ }
+
+ platform_set_drvdata(pdev, fbi);
+
+ ret = device_create_file(fbi->dev, &dev_attr_fsl_disp_property);
+ if (ret)
+ dev_err(&pdev->dev, "Error %d on creating file for disp "
+ "property\n", ret);
+
+ ret = device_create_file(fbi->dev, &dev_attr_fsl_disp_dev_property);
+ if (ret)
+ dev_err(&pdev->dev, "Error %d on creating file for disp "
+ " device propety\n", ret);
+
+ disp_dev = mxc_dispdrv_getdev(mxcfbi->dispdrv);
+ if (disp_dev) {
+ ret = sysfs_create_link(&fbi->dev->kobj,
+ &disp_dev->kobj, "disp_dev");
+ if (ret)
+ dev_err(&pdev->dev,
+ "Error %d on creating file\n", ret);
+ }
+
+ INIT_WORK(&mxcfbi->vsync_pre_work, mxcfb_vsync_pre_work);
+
+ snprintf(buf, sizeof(buf), "mxcfb%d-vsync-pre", fbi->node);
+ mxcfbi->vsync_pre_queue = create_singlethread_workqueue(buf);
+ if (mxcfbi->vsync_pre_queue == NULL) {
+ dev_err(fbi->device,
+ "Failed to alloc vsync-pre workqueue\n");
+ ret = -ENOMEM;
+ goto workqueue_alloc_failed;
+ }
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ mxcfbi->fbdrv_earlysuspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
+ mxcfbi->fbdrv_earlysuspend.suspend = mxcfb_early_suspend;
+ mxcfbi->fbdrv_earlysuspend.resume = mxcfb_later_resume;
+ mxcfbi->fbdrv_earlysuspend.data = pdev;
+ register_early_suspend(&mxcfbi->fbdrv_earlysuspend);
+#endif
+
+#ifdef CONFIG_LOGO
+ fb_prepare_logo(fbi, 0);
+ fb_show_logo(fbi, 0);
+#endif
+
+ return 0;
+
+workqueue_alloc_failed:
+mxcfb_setupoverlay_failed:
+mxcfb_register_failed:
+get_ipu_failed:
+ ipu_clear_usage(mxcfbi->ipu_id, mxcfbi->ipu_di);
+ipu_in_busy:
+init_dispdrv_failed:
+ fb_dealloc_cmap(&fbi->cmap);
+ framebuffer_release(fbi);
+get_fb_option_failed:
+init_fbinfo_failed:
+ return ret;
+}
+
+static int mxcfb_remove(struct platform_device *pdev)
+{
+ struct fb_info *fbi = platform_get_drvdata(pdev);
+ struct mxcfb_info *mxc_fbi = fbi->par;
+
+ if (!fbi)
+ return 0;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&mxc_fbi->fbdrv_earlysuspend);
+#endif
+
+ device_remove_file(fbi->dev, &dev_attr_fsl_disp_dev_property);
+ device_remove_file(fbi->dev, &dev_attr_fsl_disp_property);
+ mxcfb_blank(FB_BLANK_POWERDOWN, fbi);
+ mxcfb_unregister(fbi);
+ mxcfb_unmap_video_memory(fbi);
+
+ if (mxc_fbi->ovfbi) {
+ device_remove_file(mxc_fbi->ovfbi->dev,
+ &dev_attr_fsl_disp_dev_property);
+ device_remove_file(mxc_fbi->ovfbi->dev,
+ &dev_attr_fsl_disp_property);
+ mxcfb_blank(FB_BLANK_POWERDOWN, mxc_fbi->ovfbi);
+ mxcfb_unsetup_overlay(fbi);
+ mxcfb_unmap_video_memory(mxc_fbi->ovfbi);
+ }
+
+ ipu_clear_usage(mxc_fbi->ipu_id, mxc_fbi->ipu_di);
+ if (&fbi->cmap)
+ fb_dealloc_cmap(&fbi->cmap);
+ framebuffer_release(fbi);
+ return 0;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxcfb_driver = {
+ .driver = {
+ .name = MXCFB_NAME,
+ },
+ .probe = mxcfb_probe,
+ .remove = mxcfb_remove,
+ .suspend = mxcfb_suspend,
+ .resume = mxcfb_resume,
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void mxcfb_early_suspend(struct early_suspend *h)
+{
+ struct platform_device *pdev = (struct platform_device *)h->data;
+ struct fb_info *fbi = platform_get_drvdata(pdev);
+ struct mxcfb_info *mxcfbi = (struct mxcfb_info *)fbi->par;
+ pm_message_t state = { .event = PM_EVENT_SUSPEND };
+ struct fb_event event;
+ int blank = FB_BLANK_POWERDOWN;
+
+ if (mxcfbi->ipu_ch == MEM_FG_SYNC)
+ return;
+
+ if (strstr(mxcfbi->dispdrv->drv->name, "hdmi")) {
+ /* Only black the hdmi fb due to audio dependency */
+ memset(fbi->screen_base, 0, fbi->fix.smem_len);
+ return;
+ }
+
+ mxcfb_core_suspend(pdev, state);
+ event.info = fbi;
+ event.data = &blank;
+ fb_notifier_call_chain(FB_EVENT_BLANK, &event);
+}
+
+static void mxcfb_later_resume(struct early_suspend *h)
+{
+ struct platform_device *pdev = (struct platform_device *)h->data;
+ struct fb_info *fbi = platform_get_drvdata(pdev);
+ struct mxcfb_info *mxcfbi = (struct mxcfb_info *)fbi->par;
+ struct fb_event event;
+
+ if (mxcfbi->ipu_ch == MEM_FG_SYNC)
+ return;
+
+ /* HDMI resume function has been called */
+ if (strstr(mxcfbi->dispdrv->drv->name, "hdmi"))
+ return;
+
+ mxcfb_core_resume(pdev);
+ event.info = fbi;
+ event.data = &mxcfbi->next_blank;
+ fb_notifier_call_chain(FB_EVENT_BLANK, &event);
+}
+#endif
+
+/*!
+ * Main entry function for the framebuffer. The function registers the power
+ * management callback functions with the kernel and also registers the MXCFB
+ * callback functions with the core Linux framebuffer driver \b fbmem.c
+ *
+ * @return Error code indicating success or failure
+ */
+int __init mxcfb_init(void)
+{
+ return platform_driver_register(&mxcfb_driver);
+}
+
+void mxcfb_exit(void)
+{
+ platform_driver_unregister(&mxcfb_driver);
+}
+
+module_init(mxcfb_init);
+module_exit(mxcfb_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC framebuffer driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("fb");
diff --git a/drivers/video/mxc/mxc_lcdif.c b/drivers/video/mxc/mxc_lcdif.c
new file mode 100644
index 00000000..5cbdc73c
--- /dev/null
+++ b/drivers/video/mxc/mxc_lcdif.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/mxcfb.h>
+#include <linux/fsl_devices.h>
+#include "mxc_dispdrv.h"
+
+struct mxc_lcdif_data {
+ struct platform_device *pdev;
+ struct mxc_dispdrv_handle *disp_lcdif;
+};
+
+#define DISPDRV_LCD "lcd"
+
+static struct fb_videomode lcdif_modedb[] = {
+ {
+ /* 800x480 @ 57 Hz , pixel clk @ 27MHz */
+ "CLAA-WVGA", 57, 800, 480, 37037, 40, 60, 10, 10, 20, 10,
+ FB_SYNC_CLK_LAT_FALL,
+ FB_VMODE_NONINTERLACED,
+ 0,},
+ {
+ /* 800x480 @ 60 Hz , pixel clk @ 32MHz */
+ "SEIKO-WVGA", 60, 800, 480, 29850, 89, 164, 23, 10, 10, 10,
+ FB_SYNC_CLK_LAT_FALL,
+ FB_VMODE_NONINTERLACED,
+ 0,},
+};
+static int lcdif_modedb_sz = ARRAY_SIZE(lcdif_modedb);
+
+static int lcdif_init(struct mxc_dispdrv_handle *disp,
+ struct mxc_dispdrv_setting *setting)
+{
+ int ret, i;
+ struct mxc_lcdif_data *lcdif = mxc_dispdrv_getdata(disp);
+ struct fsl_mxc_lcd_platform_data *plat_data
+ = lcdif->pdev->dev.platform_data;
+ struct fb_videomode *modedb = lcdif_modedb;
+ int modedb_sz = lcdif_modedb_sz;
+
+ /* use platform defined ipu/di */
+ setting->dev_id = plat_data->ipu_id;
+ setting->disp_id = plat_data->disp_id;
+
+ ret = fb_find_mode(&setting->fbi->var, setting->fbi, setting->dft_mode_str,
+ modedb, modedb_sz, NULL, setting->default_bpp);
+ if (!ret) {
+ fb_videomode_to_var(&setting->fbi->var, &modedb[0]);
+ setting->if_fmt = plat_data->default_ifmt;
+ }
+
+ INIT_LIST_HEAD(&setting->fbi->modelist);
+ for (i = 0; i < modedb_sz; i++) {
+ struct fb_videomode m;
+ fb_var_to_videomode(&m, &setting->fbi->var);
+ if (fb_mode_is_equal(&m, &modedb[i])) {
+ fb_add_videomode(&modedb[i],
+ &setting->fbi->modelist);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+void lcdif_deinit(struct mxc_dispdrv_handle *disp)
+{
+ /*TODO*/
+}
+
+static struct mxc_dispdrv_driver lcdif_drv = {
+ .name = DISPDRV_LCD,
+ .init = lcdif_init,
+ .deinit = lcdif_deinit,
+};
+
+static int mxc_lcdif_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct mxc_lcdif_data *lcdif;
+
+ lcdif = kzalloc(sizeof(struct mxc_lcdif_data), GFP_KERNEL);
+ if (!lcdif) {
+ ret = -ENOMEM;
+ goto alloc_failed;
+ }
+
+ lcdif->pdev = pdev;
+ lcdif->disp_lcdif = mxc_dispdrv_register(&lcdif_drv);
+ mxc_dispdrv_setdata(lcdif->disp_lcdif, lcdif);
+
+ dev_set_drvdata(&pdev->dev, lcdif);
+
+alloc_failed:
+ return ret;
+}
+
+static int mxc_lcdif_remove(struct platform_device *pdev)
+{
+ struct mxc_lcdif_data *lcdif = dev_get_drvdata(&pdev->dev);
+
+ mxc_dispdrv_puthandle(lcdif->disp_lcdif);
+ mxc_dispdrv_unregister(lcdif->disp_lcdif);
+ kfree(lcdif);
+ return 0;
+}
+
+static struct platform_driver mxc_lcdif_driver = {
+ .driver = {
+ .name = "mxc_lcdif",
+ },
+ .probe = mxc_lcdif_probe,
+ .remove = mxc_lcdif_remove,
+};
+
+static int __init mxc_lcdif_init(void)
+{
+ return platform_driver_register(&mxc_lcdif_driver);
+}
+
+static void __exit mxc_lcdif_exit(void)
+{
+ platform_driver_unregister(&mxc_lcdif_driver);
+}
+
+module_init(mxc_lcdif_init);
+module_exit(mxc_lcdif_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("i.MX ipuv3 LCD extern port driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/mxc/mxc_spdc_fb.c b/drivers/video/mxc/mxc_spdc_fb.c
new file mode 100644
index 00000000..0c27a32b
--- /dev/null
+++ b/drivers/video/mxc/mxc_spdc_fb.c
@@ -0,0 +1,4180 @@
+/*
+ * Copyright (C) 2012 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+/*
+ * Based on MXC EPDC Driver, Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+#include "mxc_spdc_fb.h"
+
+#define MERGE_OK 0
+#define MERGE_FAIL 1
+#define MERGE_BLOCK 2
+
+#define SPDC_DEFAULT_TEMP 30
+#define TEMP_NO_SET 0xFF
+#define POWER_STATE_OFF 0
+#define POWER_STATE_ON 1
+#define POWER_READY_OFF false
+#define POWER_READY_ON true
+
+#define INIT_UPDATE_MARKER 0x12345678
+#define PAN_UPDATE_MARKER 0x12345679
+
+#define SPDC_MAX_NUM_UPDATES 32
+#define SPDC_MAX_NUM_BUFFERS 2
+#define SPDC_MAX_NUM_PREPROCESS 15
+#define NUM_SCREENS_MIN 2
+#define SPDC_DEFAULT_BPP 16
+
+mxc_spdc_t *g_spdc_fb_data;
+
+static int mxc_spdc_fb_send_update(struct mxcfb_update_data *upd_data,
+ struct fb_info *info);
+static int mxc_spdc_fb_wait_update_complete(struct mxcfb_update_marker_data
+ *marker_data, struct fb_info *info);
+
+static const struct mxc_spdc_resolution_map_para spdc_gray_res_map[] = {
+/*define Gray Mode resolution mapping*/
+ {0x18, 600, 800, PORTRAIT},
+ {0x19, 768, 1024, PORTRAIT},
+ {0x1a, 0, 0, RESERVED},
+ {0x1b, 600, 1024, PORTRAIT},
+ {0x1c, 825, 1200, PORTRAIT},
+ {0x1d, 1024, 1280, PORTRAIT},
+ {0x1e, 1200, 1600, PORTRAIT},
+ {0x10, 800, 1024, PORTRAIT},
+ {0x11, 825, 1280, PORTRAIT},
+ {0x12, 800, 1280, PORTRAIT},
+ {0x13, 768, 1280, PORTRAIT},
+ {0x14, 960, 1280, PORTRAIT},
+ {0x0, 800, 600, LANDSCAPE},
+ {0x1, 1024, 768, LANDSCAPE},
+ {0x2, 0, 0, RESERVED},
+ {0x3, 1024, 600, LANDSCAPE},
+ {0x4, 1200, 825, LANDSCAPE},
+ {0x5, 1280, 1024, LANDSCAPE},
+ {0x6, 1600, 1200, LANDSCAPE},
+ {0x7, 1024, 800, LANDSCAPE},
+ {0x8, 1280, 825, LANDSCAPE},
+ {0x9, 1280, 800, LANDSCAPE},
+ {0xa, 1280, 768, LANDSCAPE},
+ {0xb, 1280, 960, LANDSCAPE},
+ {0xFFFF, 800, 600, LANDSCAPE},
+};
+
+static const struct mxc_spdc_resolution_map_para spdc_rgbw_res_map[] = {
+/*define RGBW Mode resolution mapping*/
+ {0x18, 300, 400, PORTRAIT},
+ {0x19, 384, 512, PORTRAIT},
+ {0x1a, 0, 0, RESERVED},
+ {0x1b, 300, 512, PORTRAIT},
+ {0x1c, 0, 0, RESERVED},
+ {0x1d, 512, 640, PORTRAIT},
+ {0x1e, 600, 800, PORTRAIT},
+ {0x10, 400, 512, PORTRAIT},
+ {0x11, 0, 0, RESERVED},
+ {0x12, 400, 640, PORTRAIT},
+ {0x13, 384, 640, PORTRAIT},
+ {0x14, 480, 640, PORTRAIT},
+ {0x0, 400, 300, LANDSCAPE},
+ {0x1, 512, 384, LANDSCAPE},
+ {0x2, 0, 0, RESERVED},
+ {0x3, 512, 300, LANDSCAPE},
+ {0x4, 0, 0, RESERVED},
+ {0x5, 640, 512, LANDSCAPE},
+ {0x6, 800, 600, LANDSCAPE},
+ {0x7, 512, 400, LANDSCAPE},
+ {0x8, 0, 0, RESERVED},
+ {0x9, 640, 400, LANDSCAPE},
+ {0xa, 640, 384, LANDSCAPE},
+ {0xb, 640, 480, LANDSCAPE},
+ {0xFFFF, 400, 300, LANDSCAPE},
+};
+
+static void get_panel_init_set(struct imx_spdc_panel_init_set*
+ panel_set, u32 *val)
+{
+ *val = panel_set->yoe_pol |
+ (panel_set->dual_gate << 1) |
+ (panel_set->ud << 7) |
+ (panel_set->rl << 8) |
+ (panel_set->data_filter_n << 9) |
+ (panel_set->power_ready << 10) |
+ (panel_set->rgbw_mode_enable << 11) |
+ (panel_set->hburst_len_en << 13) |
+ ((panel_set->resolution & 0x1F) << 2);
+}
+
+static inline void spdc_intr_enable(mxc_spdc_t *fb_data, u32 int_type)
+{
+ u32 status;
+
+ status = __raw_readl(fb_data->hwp + SPDC_INT_ENABLE);
+ status |= (int_type & SPDC_IRQ_ALL_MASK);
+ __raw_writel(status, fb_data->hwp + SPDC_INT_ENABLE);
+}
+
+static bool spdc_is_update_finish(mxc_spdc_t *fb_data)
+{
+ u32 val = __raw_readl(fb_data->hwp + SPDC_INT_STA_CLR);
+ bool is_finish = (val & SPDC_IRQ_STA_FRAME_UPDATE) ? true : false;
+
+ return is_finish;
+}
+
+static int spdc_get_intr_stat(mxc_spdc_t *fb_data)
+{
+ u32 status = __raw_readl(fb_data->hwp + SPDC_INT_STA_CLR);
+ return status & 0xF;
+}
+
+static inline void
+spdc_intr_stat_clear(mxc_spdc_t *fb_data, u32 int_type)
+{
+ /* write 1 to clear status */
+ u32 status = (int_type & SPDC_IRQ_STA_ALL_MASK);
+ __raw_writel(status, fb_data->hwp + SPDC_INT_STA_CLR);
+}
+
+static inline void spdc_set_nextbuf_addr(mxc_spdc_t *fb_data)
+{
+ u32 addr = fb_data->fresh_param.buf_addr.next_buf_phys_addr;
+ __raw_writel(addr, fb_data->hwp + SPDC_NEXT_BUF);
+ dev_dbg(fb_data->dev, "add: 0x%x\n", addr);
+}
+
+static inline void spdc_set_curbuf_addr(mxc_spdc_t *fb_data)
+{
+ u32 addr = fb_data->fresh_param.buf_addr.cur_buf_phys_addr;
+ __raw_writel(addr, fb_data->hwp + SPDC_CURRENT_BUF);
+}
+
+static inline void spdc_set_prebuf_addr(mxc_spdc_t *fb_data)
+{
+ u32 addr = fb_data->fresh_param.buf_addr.pre_buf_phys_addr;
+ __raw_writel(addr, fb_data->hwp + SPDC_PRE_BUF);
+}
+
+static inline void spdc_set_cntbuf_addr(mxc_spdc_t *fb_data)
+{
+ u32 addr = fb_data->fresh_param.buf_addr.frm_cnt_buf_phys_addr;
+ __raw_writel(addr, fb_data->hwp + SPDC_CNT_BUF);
+}
+
+static inline void spdc_set_lutbuf_addr(mxc_spdc_t *fb_data)
+{
+ u32 addr = fb_data->fresh_param.buf_addr.lut_buf_phys_addr;
+ __raw_writel(addr, fb_data->hwp + SPDC_LUT_BUF);
+}
+
+static inline void spdc_set_update_coord(mxc_spdc_t *fb_data)
+{
+ u32 x = fb_data->fresh_param.update_region.left;
+ u32 y = fb_data->fresh_param.update_region.top;
+
+ if (!x)
+ x++;
+ if (!y)
+ y++;
+
+ dev_dbg(fb_data->dev, "x:%d, y:%d\n", x, y);
+ x = (u32)(((x & SPDC_UPDATE_X_Y_MAX_SIZE) << 16) |
+ (y & SPDC_UPDATE_X_Y_MAX_SIZE));
+ __raw_writel(x, fb_data->hwp + SPDC_UPDATA_X_Y);
+}
+
+static inline void spdc_set_update_dimensions(mxc_spdc_t *fb_data)
+{
+ u32 w = fb_data->fresh_param.update_region.width;
+ u32 h = fb_data->fresh_param.update_region.height;
+
+ if (!w)
+ w++;
+ if (!h)
+ h++;
+
+ dev_dbg(fb_data->dev, "w:%d, h:%d\n", w, h);
+ w = (u32)(((w & SPDC_UPDATE_W_H_MAX_SIZE) << 16) |
+ (h & SPDC_UPDATE_W_H_MAX_SIZE));
+ __raw_writel(w, fb_data->hwp + SPDC_UPDATE_W_H);
+}
+
+static inline void spdc_set_update_temper(mxc_spdc_t *fb_data)
+{
+ s8 temper = (s8)(fb_data->fresh_param.temper & 0xFF) << 1;
+
+ if (temper > -110 && temper < 200)
+ __raw_writel(temper, fb_data->hwp + SPDC_TEMP_INFO);
+ else
+ __raw_writel(SPDC_DEFAULT_TEMP, fb_data->hwp + SPDC_TEMP_INFO);
+}
+
+static inline void spdc_trigger_update(mxc_spdc_t *fb_data)
+{
+ u32 val;
+ struct partial_refresh_param *fresh_param = &fb_data->fresh_param;
+
+ if ((fresh_param->wave_mode & SPDC_WAV_MODE_MASK) && fresh_param->flash)
+ val = SPDC_DISP_TRIGGER_FLASH;
+ else
+ val = 0;
+
+ val |= fresh_param->wave_mode << 1;
+ val |= SPDC_DISP_TRIGGER_ENABLE;
+ dev_dbg(fb_data->dev, "wave:%d\n", fresh_param->wave_mode);
+ __raw_writel(val, fb_data->hwp + SPDC_DISP_TRIGGER);
+}
+
+static bool is_lut_checksum_ok(mxc_spdc_t *fb_data)
+{
+ u32 status;
+
+ status = __raw_readl(fb_data->hwp + SPDC_STATUS);
+ status &= SPDC_IRQ_STA_ERR;
+
+ return status ? true : false;
+}
+
+static void spdc_clk_gate(mxc_spdc_t *fb_data, bool enable)
+{
+ if (enable)
+ __raw_writel(SPDC_SW_GATE_CLK_ENABLE,
+ fb_data->hwp + SPDC_SW_GATE_CLK);
+ else
+ __raw_writel(~SPDC_SW_GATE_CLK_ENABLE,
+ fb_data->hwp + SPDC_SW_GATE_CLK);
+}
+
+static int update_panel_init_set(mxc_spdc_t *fb_data)
+{
+ int ret = 0;
+ u32 init_val;
+
+ get_panel_init_set(&fb_data->panel_set, &init_val);
+ dev_dbg(fb_data->dev, "panel init setting:%x\n", init_val);
+
+ __raw_writel(init_val, fb_data->hwp + SPDC_PANEL_INIT_SET);
+
+ /*wait init setting update finish*/
+ ret = wait_for_completion_timeout(&fb_data->init_finish,
+ msecs_to_jiffies(4000));
+ if (!ret)
+ dev_err(fb_data->dev, "Timed out for init setting!\n");
+
+ return ret;
+}
+
+static void spdc_panel_pwr_on(mxc_spdc_t *fb_data)
+{
+ fb_data->panel_set.power_ready = POWER_READY_ON;
+}
+
+static void spdc_panel_pwr_down(mxc_spdc_t *fb_data)
+{
+ fb_data->panel_set.power_ready = POWER_READY_OFF;
+}
+
+static void spdc_powerdown(mxc_spdc_t *fb_data)
+{
+ mutex_lock(&fb_data->power_mutex);
+
+ /* If powering_down has been cleared, a powerup
+ * request is pre-empting this powerdown request.
+ */
+ if (!fb_data->powering_down
+ || (fb_data->power_state == POWER_STATE_OFF)) {
+ mutex_unlock(&fb_data->power_mutex);
+ return;
+ }
+
+ dev_dbg(fb_data->dev, "spdc Powerdown\n");
+
+ /* Disable power to the AUO panel */
+ regulator_disable(fb_data->vcom_regulator);
+ regulator_disable(fb_data->display_regulator);
+
+ /*enable spdc clock gating*/
+ spdc_clk_gate(fb_data, true);
+ clk_disable(fb_data->spdc_clk_pix);
+ clk_disable(fb_data->spdc_clk_axi);
+
+ /* Disable pins used by SPDC (to prevent leakage current) */
+ if (fb_data->pdata->disable_pins)
+ fb_data->pdata->disable_pins();
+
+ /* turn off the V3p3 */
+ regulator_disable(fb_data->v3p3_regulator);
+
+ fb_data->power_state = POWER_STATE_OFF;
+ fb_data->powering_down = false;
+ spdc_panel_pwr_down(fb_data);
+
+ if (fb_data->wait_for_powerdown) {
+ fb_data->wait_for_powerdown = false;
+ complete(&fb_data->powerdown_compl);
+ }
+
+ mutex_unlock(&fb_data->power_mutex);
+}
+
+static void spdc_powerup(mxc_spdc_t *fb_data)
+{
+ int ret = 0;
+ mutex_lock(&fb_data->power_mutex);
+
+ /*
+ * If power down request is pending, clear
+ * powering_down to cancel the request.
+ */
+ if (fb_data->powering_down)
+ fb_data->powering_down = false;
+
+ if (fb_data->power_state == POWER_STATE_ON) {
+ mutex_unlock(&fb_data->power_mutex);
+ return;
+ }
+
+ dev_dbg(fb_data->dev, "spdc Powerup\n");
+
+ /* Enable the v3p3 regulator */
+ ret = regulator_enable(fb_data->v3p3_regulator);
+ if (IS_ERR((void *)ret)) {
+ dev_err(fb_data->dev, "Unable to enable V3P3 regulator."
+ "err = 0x%x\n", ret);
+ mutex_unlock(&fb_data->power_mutex);
+ return;
+ }
+
+ msleep(1);
+
+ /* Enable pins used by SPDC */
+ if (fb_data->pdata->enable_pins)
+ fb_data->pdata->enable_pins();
+
+ /* Enable clocks to SPDC */
+ clk_enable(fb_data->spdc_clk_axi);
+ clk_enable(fb_data->spdc_clk_pix);
+
+ /*disable spdc gate*/
+ spdc_clk_gate(fb_data, false);
+
+ /* Enable power to the EPD panel */
+ ret = regulator_enable(fb_data->display_regulator);
+ if (IS_ERR((void *)ret)) {
+ dev_err(fb_data->dev, "Unable to enable DISPLAY regulator."
+ "err = 0x%x\n", ret);
+ mutex_unlock(&fb_data->power_mutex);
+ return;
+ }
+ ret = regulator_enable(fb_data->vcom_regulator);
+ if (IS_ERR((void *)ret)) {
+ dev_err(fb_data->dev, "Unable to enable VCOM regulator."
+ "err = 0x%x\n", ret);
+ mutex_unlock(&fb_data->power_mutex);
+ return;
+ }
+
+ fb_data->power_state = POWER_STATE_ON;
+ spdc_panel_pwr_on(fb_data);
+
+ mutex_unlock(&fb_data->power_mutex);
+}
+
+#ifdef DEBUG
+static void
+check_waveform(u32 *wv_buf_orig, u32 *wv_buf_cur, u32 wv_buf_size)
+{
+ int i;
+ bool is_mismatch = false;
+ for (i = 0; i < wv_buf_size; i++) {
+ if (wv_buf_orig[i] != wv_buf_cur[i]) {
+ is_mismatch = true;
+ printk(KERN_ERR "Waveform mismatch!\n");
+ }
+ }
+
+ if (!is_mismatch)
+ printk(KERN_DEBUG "No mismatches!\n");
+}
+#else
+static void
+check_waveform(u32 *wv_buf_orig, u32 *wv_buf_cur, u32 wv_buf_size) {}
+#endif
+
+static void get_spdc_version(mxc_spdc_t *fb_data)
+{
+ struct mxc_spdc_version *spdc_ver = &fb_data->spdc_ver;
+ u32 disp_id, tcon_id;
+
+ disp_id = __raw_readl(fb_data->hwp + SPDC_DISP_VER);
+ tcon_id = __raw_readl(fb_data->hwp + SPDC_TCON_VER);
+
+ spdc_ver->disp_ver.product_id = disp_id & 0xFFFF;
+ spdc_ver->disp_ver.lut_ver = (disp_id >> 16) & 0xFF;
+ spdc_ver->disp_ver.epd_type = (disp_id >> 24) & 0xFF;
+ spdc_ver->tcon_ver = tcon_id & 0xFF;
+
+ dev_info(fb_data->dev, "EPD type ID:%x, Tcon ID:%x\n",
+ spdc_ver->disp_ver.product_id, spdc_ver->tcon_ver);
+}
+
+static void spdc_set_update_concurrency(mxc_spdc_t *fb_data)
+{
+ u32 concur_mode;
+
+ concur_mode = fb_data->fresh_param.concur & 0xFF;
+ concur_mode |= (SPDC_LUT_MODE_OFFSET << 8);
+
+ __raw_writel(concur_mode, fb_data->hwp + SPDC_LUT_PARA_UPDATE);
+}
+
+static bool is_preprocess_list_full(mxc_spdc_t *fb_data)
+{
+ /* Check to see if preprocess are full in this list */
+ if (fb_data->upd_preprocess_num >= SPDC_MAX_NUM_PREPROCESS)
+ return true;
+ else
+ return false;
+}
+
+static void spdc_submit_update(mxc_spdc_t *fb_data)
+{
+ fb_data->updates_active = true;
+
+ spdc_set_nextbuf_addr(fb_data);
+ spdc_set_update_coord(fb_data);
+ spdc_set_update_dimensions(fb_data);
+ spdc_set_update_temper(fb_data);
+ spdc_trigger_update(fb_data);
+}
+
+static int spdc_init_sequence(mxc_spdc_t *fb_data)
+{
+ struct fb_var_screeninfo *screeninfo = &fb_data->spdc_fb_var;
+ struct spdc_buffer_addr *buf_addr = &fb_data->fresh_param.buf_addr;
+ struct imx_spdc_panel_init_set *init_set =
+ fb_data->pdata->spdc_mode->init_set;
+ u32 xres, yres;
+ int ret = -EFAULT;
+
+ /*init spdc power*/
+ spdc_powerup(fb_data);
+
+ /* enable all interrupt */
+ spdc_intr_stat_clear(fb_data, SPDC_INT_STA_CLR);
+ spdc_intr_enable(fb_data, SPDC_IRQ_ALL_MASK);
+ /* set ACC concurrency update mode */
+ if (fb_data->fresh_param.concur)
+ spdc_set_update_concurrency(fb_data);
+
+ /* program SPDC register and trigger to process buffer*/
+ buf_addr->next_buf_phys_addr = fb_data->phy_next_buf;
+ buf_addr->cur_buf_phys_addr = fb_data->phy_current_buf;
+ buf_addr->pre_buf_phys_addr = fb_data->phy_pre_buf;
+ buf_addr->frm_cnt_buf_phys_addr = fb_data->phy_cnt_buf;
+ buf_addr->lut_buf_phys_addr = fb_data->phy_lut_buf;
+
+ /* Use unrotated (native) width/height */
+ if ((screeninfo->rotate == FB_ROTATE_CW) ||
+ (screeninfo->rotate == FB_ROTATE_CCW)) {
+ xres = screeninfo->yres;
+ yres = screeninfo->xres;
+ } else {
+ xres = screeninfo->xres;
+ yres = screeninfo->yres;
+ }
+ fb_data->fresh_param.update_region.left = 0;
+ fb_data->fresh_param.update_region.top = 0;
+ fb_data->fresh_param.update_region.width = xres;
+ fb_data->fresh_param.update_region.height = yres;
+
+ /* set panel temperature as environment temperature */
+ fb_data->fresh_param.temper = SPDC_DEFAULT_TEMP;
+ /* set waveform mode */
+ fb_data->fresh_param.wave_mode = SPDC_WAV_MODE_DEFAULT;
+
+ spdc_set_update_coord(fb_data);
+ spdc_set_update_dimensions(fb_data);
+
+ spdc_set_update_temper(fb_data);
+
+ spdc_set_nextbuf_addr(fb_data);
+ spdc_set_curbuf_addr(fb_data);
+ spdc_set_prebuf_addr(fb_data);
+ spdc_set_cntbuf_addr(fb_data);
+
+ /* load waveform*/
+ spdc_set_lutbuf_addr(fb_data);
+ ret = wait_for_completion_timeout(&fb_data->lut_down,
+ msecs_to_jiffies(4000));
+ if (!ret) {
+ dev_err(fb_data->dev,
+ "Timed out for lut!\n");
+ return ret;
+ }
+
+ /* modify lut to fix DC com fading issue,
+ * and fix mode4 close over push
+ */
+ __raw_writel(0x00003906, fb_data->hwp + SPDC_LUT_PARA_UPDATE);
+ __raw_writel(0x00003300, fb_data->hwp + SPDC_LUT_PARA_UPDATE);
+
+ /* init SPDC setting, the setting get from platform data */
+ fb_data->panel_set.yoe_pol = init_set->yoe_pol;
+ fb_data->panel_set.dual_gate = init_set->dual_gate;
+ fb_data->panel_set.ud = init_set->ud;
+ fb_data->panel_set.rl = init_set->rl;
+ fb_data->panel_set.data_filter_n = init_set->data_filter_n;
+ fb_data->panel_set.rgbw_mode_enable = init_set->rgbw_mode_enable;
+ fb_data->panel_set.hburst_len_en = init_set->hburst_len_en;
+ ret = update_panel_init_set(fb_data);
+
+ return ret;
+}
+
+static u32 mxc_spdc_partial_refresh_low(mxc_spdc_t *fb_data, void *buffer)
+{
+ u8 *fresh_addr;
+ void *pattern = buffer;
+ struct partial_refresh_param *fresh_param = &fb_data->fresh_param;
+ u32 fresh_size;
+ int ret = 0;
+
+ fb_data->updates_active = true;
+
+ fresh_addr = (u8 *)(fb_data->virt_start) +
+ (fresh_param->update_region.top * fresh_param->stride) +
+ ((fresh_param->update_region.left * fb_data->default_bpp) >> 3);
+ fresh_size = (u32)((fresh_param->update_region.width *
+ fresh_param->update_region.height * fb_data->default_bpp) >> 3);
+
+ if (buffer != NULL) {
+ while (fresh_size > 0) {
+ memcpy((void *)fresh_addr, pattern,
+ fresh_param->update_region.width);
+ fresh_size -= fresh_param->update_region.width;
+ fresh_addr += fresh_param->update_region.top *
+ fresh_param->stride;
+ pattern += fresh_param->update_region.width;
+ }
+ }
+
+ /* program SPDC register and trigger to process buffer*/
+ fb_data->fresh_param.buf_addr.next_buf_phys_addr =
+ fb_data->info.fix.smem_start + fb_data->fb_offset;
+ fb_data->fresh_param.wave_mode = fb_data->wv_modes.mode_init;
+ spdc_submit_update(fb_data);
+
+ ret = wait_for_completion_timeout(&fb_data->update_finish,
+ msecs_to_jiffies(3000));
+ if (!ret) {
+ dev_err(fb_data->dev,
+ "display update timeout!\n");
+ return -ETIMEDOUT;
+ }
+
+ return ret;
+}
+
+static u32 spdc_fb_dev_init(mxc_spdc_t *fb_data)
+{
+ fb_data->auto_mode = AUTO_UPDATE_MODE_REGION_MODE;
+ fb_data->fresh_param.wave_mode = SPDC_WAV_MODE_0;
+ fb_data->operation_mode = SPDC_NO_OPERATION;
+ fb_data->is_deep_fresh = false;
+
+ /* Init the concurrency update */
+ fb_data->fresh_param.concur = 0;
+ fb_data->upd_preprocess_num = 0;
+ fb_data->submit_upd_sta = 0;
+
+ fb_data->fresh_param.temper = SPDC_DEFAULT_TEMP;
+
+ return 0;
+}
+
+/**
+ * mxc_spdc_device_is_busy - check spdc device busy status.
+ * Returns 0 if spdc device is idle.
+ */
+static int mxc_spdc_device_is_busy(mxc_spdc_t *fb_data)
+{
+ u32 status;
+ u32 orig_jiffies = jiffies;
+
+ while (1) {
+ status = __raw_readl(fb_data->hwp + SPDC_STATUS);
+ if ((status & SPDC_PANEL_STAUTS_BUSY) &&
+ ((status & 0xF0) == SPDC_TCON_STATUS_IDLE))
+ break;
+
+ if (signal_pending(current)) {
+ dev_dbg(fb_data->dev, "SPDC Interrupted\n");
+ return -EINTR;
+ }
+
+ if (time_after(jiffies, orig_jiffies +
+ msecs_to_jiffies(3000))) {
+ dev_dbg(fb_data->dev, "SPDC is busy\n");
+ return -ETIMEDOUT;
+ }
+
+ schedule();
+ }
+
+ return 0;
+}
+
+static bool is_free_list_full(mxc_spdc_t *fb_data)
+{
+ int count = 0;
+ struct update_data_list *plist;
+
+ /* Count buffers in free buffer list */
+ list_for_each_entry(plist, &fb_data->upd_buf_free_list, list)
+ count++;
+
+ /* Check to see if all buffers are in this list */
+ if (count == fb_data->max_num_updates)
+ return true;
+ else
+ return false;
+}
+
+static void spdc_draw_mode0(mxc_spdc_t *fb_data)
+{
+ struct mxcfb_update_data update;
+ struct mxcfb_update_marker_data upd_marker_data;
+ struct fb_var_screeninfo *screeninfo = &fb_data->spdc_fb_var;
+ u32 xres, yres;
+ int ret;
+
+ fb_data->fresh_param.buf_addr.next_buf_phys_addr =
+ fb_data->phys_start;
+
+ fb_data->hw_ready = true;
+ fb_data->hw_initializing = false;
+
+ /* Use unrotated (native) width/height */
+ if ((screeninfo->rotate == FB_ROTATE_CW) ||
+ (screeninfo->rotate == FB_ROTATE_CCW)) {
+ xres = screeninfo->yres;
+ yres = screeninfo->xres;
+ } else {
+ xres = screeninfo->xres;
+ yres = screeninfo->yres;
+ }
+
+ update.update_region.left = 0;
+ update.update_region.width = xres;
+ update.update_region.top = 0;
+ update.update_region.height = yres;
+ update.update_mode = UPDATE_MODE_FULL;
+ update.waveform_mode = fb_data->wv_modes.mode_init;
+ update.update_marker = INIT_UPDATE_MARKER;
+ update.temp = SPDC_DEFAULT_TEMP;
+ update.flags = 0;
+
+ upd_marker_data.update_marker = update.update_marker;
+
+ mxc_spdc_fb_send_update(&update, &fb_data->info);
+
+ /* Block on initial update */
+ ret = mxc_spdc_fb_wait_update_complete(&upd_marker_data,
+ &fb_data->info);
+ if (ret < 0)
+ dev_err(fb_data->dev,
+ "Wait for update complete failed, Err:%d", ret);
+}
+
+static void
+spdc_fb_fw_handler(const struct firmware *fw, void *context)
+{
+ mxc_spdc_t *fb_data = (mxc_spdc_t *)context;
+ struct clk *spdc_parent;
+ unsigned long rounded_parent_rate, spdc_pix_rate,
+ rounded_pix_clk, target_pix_clk;
+ u8 *wv_file;
+ int ret;
+
+ if (fw == NULL) {
+ /* If default FW file load failed, we give up */
+ if (fb_data->fw_default_load)
+ return;
+
+ /* Try to load default waveform */
+ fb_data->fw_default_load = true;
+
+ ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+ fb_data->fw_str, fb_data->dev, GFP_KERNEL,
+ fb_data, spdc_fb_fw_handler);
+ if (ret) {
+ dev_err(fb_data->dev,
+ "Failed to load waveform image with err %d\n", ret);
+ return;
+ }
+ }
+
+ wv_file = (u8 *)fw->data;
+ memcpy(fb_data->virt_lut_buf, wv_file, fw->size);
+
+ check_waveform((u32 *)wv_file, (u32 *)fb_data->virt_lut_buf,
+ fw->size / 4);
+ release_firmware(fw);
+
+ /* Enable clocks to access SPDC regs */
+ clk_enable(fb_data->spdc_clk_axi);
+
+ target_pix_clk = fb_data->cur_mode->vmode->pixclock;
+ /* Enable pix clk for SPDC */
+ clk_enable(fb_data->spdc_clk_pix);
+ rounded_pix_clk = clk_round_rate(fb_data->spdc_clk_pix, target_pix_clk);
+
+ if (((rounded_pix_clk >= target_pix_clk + target_pix_clk/100) ||
+ (rounded_pix_clk <= target_pix_clk - target_pix_clk/100))) {
+ /* Can't get close enough without changing parent clk */
+ spdc_parent = clk_get_parent(fb_data->spdc_clk_pix);
+ rounded_parent_rate =
+ clk_round_rate(spdc_parent, target_pix_clk);
+
+ spdc_pix_rate = target_pix_clk;
+ while (spdc_pix_rate < rounded_parent_rate)
+ spdc_pix_rate *= 2;
+ clk_set_rate(spdc_parent, spdc_pix_rate);
+
+ rounded_pix_clk =
+ clk_round_rate(fb_data->spdc_clk_pix, target_pix_clk);
+ if (((rounded_pix_clk >= target_pix_clk + target_pix_clk/100) ||
+ (rounded_pix_clk <= target_pix_clk - target_pix_clk/100)))
+ /* Still can't get a good clock, provide warning */
+ dev_err(fb_data->dev,
+ "Unable to get an accurate SPDC pix clk"
+ "desired = %lu, actual = %lu\n", target_pix_clk,
+ rounded_pix_clk);
+ }
+
+ clk_set_rate(fb_data->spdc_clk_pix, rounded_pix_clk);
+ /* Disable clocks */
+ clk_disable(fb_data->spdc_clk_axi);
+ clk_disable(fb_data->spdc_clk_pix);
+
+ if (!spdc_init_sequence(fb_data))
+ return;
+
+ /* display log on picture */
+ spdc_draw_mode0(fb_data);
+}
+
+
+static int spdc_fb_init_hw(struct fb_info *info)
+{
+ mxc_spdc_t *fb_data = (mxc_spdc_t *)info;
+ int ret;
+
+ fb_data->fw_default_load = false;
+ /*
+ * Create fw search string based on ID string in selected videomode.
+ * Format is "imx/spdc_[wave_timing].fw: spdc_pvi.fw, spdc_auo.fw"
+ */
+ if (fb_data->cur_mode) {
+ memset(fb_data->fw_str, 0, sizeof(fb_data->fw_str));
+ strcat(fb_data->fw_str, "imx/spdc_");
+ strcat(fb_data->fw_str, fb_data->cur_mode->wave_timing);
+ strcat(fb_data->fw_str, ".fw");
+ } else
+ strcat(fb_data->fw_str, "imx/spdc_pvi.fw");
+
+ ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+ fb_data->fw_str, fb_data->dev, GFP_KERNEL,
+ fb_data, spdc_fb_fw_handler);
+ if (ret) {
+ dev_err(fb_data->dev,
+ "Failed to load waveform image with err %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int mxc_spdc_partial_refresh(mxc_spdc_t *fb_data, void *buffer)
+{
+ int ret = 0;
+ struct partial_refresh_param *fresh_param = &fb_data->fresh_param;
+
+ if (!fb_data->panel_set.power_ready)
+ spdc_powerup(fb_data);
+
+ if (!fresh_param->blocking) {
+ if (mxc_spdc_device_is_busy(fb_data)) {
+ dev_err(fb_data->dev, "spdc busy!\n");
+ return (u32) -1;
+ }
+ } else {
+ while (mxc_spdc_device_is_busy(fb_data)) {
+ dev_err(fb_data->dev, "Waiting for spdc idle..\n");
+ msleep(500);
+ }
+ }
+
+ ret = mxc_spdc_partial_refresh_low(fb_data, buffer);
+
+ return ret;
+}
+
+static int mxc_operaton_update(mxc_spdc_t *fb_data)
+{
+ int ret = 0;
+ struct partial_refresh_param *fresh_param = &fb_data->fresh_param;
+ u32 operation_mode = fb_data->operation_mode;
+
+ if (!fb_data->panel_set.power_ready)
+ spdc_powerup(fb_data);
+
+ if (operation_mode != SPDC_SW_TCON_RESET) {
+ if (!fresh_param->blocking) {
+ if (mxc_spdc_device_is_busy(fb_data)) {
+ dev_err(fb_data->dev, "spdc busy\n");
+ return (u32) -1;
+ }
+ } else {
+ while (mxc_spdc_device_is_busy(fb_data)) {
+ dev_err(fb_data->dev, "Waiting spdc idle...\n");
+ msleep(500);
+ }
+ }
+ } else
+ operation_mode = SPDC_SW_TCON_RESET_SET;
+
+ /* don't add to queue list */
+ mutex_lock(&fb_data->queue_mutex);
+ __raw_writel(operation_mode, fb_data->hwp + SPDC_OPERATE);
+ mutex_unlock(&fb_data->queue_mutex);
+
+ if (operation_mode == SPDC_SW_TCON_RESET_SET) {
+ dev_dbg(fb_data->dev, "reinit hw\n");
+ mdelay(500);
+
+ fb_data->hw_ready = false;
+ fb_data->operation_mode = SPDC_NO_OPERATION;
+ ret = spdc_fb_init_hw(&fb_data->info);
+ if (ret && !fb_data->hw_ready)
+ dev_err(fb_data->dev, "Failed to init HW!\n");
+ }
+
+ return ret;
+}
+
+static int mxc_spdc_refresh_display(mxc_spdc_t *fb_data)
+{
+ struct partial_refresh_param *fresh_param = &fb_data->fresh_param;
+ u32 operation_mode = fb_data->operation_mode;
+ int ret = 0;
+
+ fresh_param->update_region.left = 0;
+ fresh_param->update_region.top = 0;
+ fresh_param->update_region.width = fb_data->spdc_fb_var.xres;
+ fresh_param->update_region.height = fb_data->spdc_fb_var.yres;
+ fresh_param->stride = (fb_data->spdc_fb_var.xres *
+ fb_data->spdc_fb_var.bits_per_pixel) >> 3;
+
+ if (operation_mode && operation_mode < SPDC_FULL_REFRESH)
+ ret = mxc_operaton_update(fb_data);
+ else
+ ret = mxc_spdc_partial_refresh(fb_data, NULL);
+
+ return ret;
+}
+
+static void mxc_spdc_find_match_mode(mxc_spdc_t *fb_data)
+{
+ struct imx_spdc_fb_mode *spdc_mode =
+ &fb_data->pdata->spdc_mode[0];
+ const struct mxc_spdc_resolution_map_para *spdc_res_map;
+ u32 i = 0;
+ u32 j = 0;
+ u32 default_mode = 0xFF;
+
+ if (fb_data->panel_set.rgbw_mode_enable)
+ spdc_res_map = &spdc_rgbw_res_map[0];
+ else
+ spdc_res_map = &spdc_gray_res_map[0];
+
+ while (spdc_mode != NULL) {
+ while (spdc_res_map[j].resolution != 0xFFFF) {
+ if (spdc_mode->vmode->xres == spdc_res_map[j].res_x
+ && spdc_mode->vmode->yres == spdc_res_map[j].res_y) {
+ fb_data->panel_set.resolution =
+ spdc_res_map[j].resolution;
+ default_mode = i;
+ break;
+ }
+ j++;
+ }
+
+ if (default_mode != 0xFF)
+ break;
+ j = 0;
+ i++;
+ spdc_mode = &fb_data->pdata->spdc_mode[i];
+ }
+
+ fb_data->cur_mode = spdc_mode;
+}
+
+static int mxc_spdc_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
+{
+ unsigned long start = vma->vm_start;
+ unsigned long size = vma->vm_end - vma->vm_start;
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ unsigned long page, pos;
+
+ if (offset + size > info->fix.smem_len)
+ return -EINVAL;
+
+ pos = (unsigned long)info->fix.smem_start + offset;
+
+ /* make buffers bufferable */
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+ vma->vm_flags |= VM_RESERVED | VM_IO;
+
+ while (size > 0) {
+ page = pos;
+ if (io_remap_pfn_range(vma, start, page >> PAGE_SHIFT,
+ PAGE_SIZE, vma->vm_page_prot))
+ return -EAGAIN;
+
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ if (size > PAGE_SIZE)
+ size -= PAGE_SIZE;
+ else
+ size = 0;
+ }
+
+ return 0;
+}
+
+static inline u_int _chan_to_field(u_int chan, struct fb_bitfield *bf)
+{
+ chan &= 0xffff;
+ chan >>= 16 - bf->length;
+ return chan << bf->offset;
+}
+
+static int mxc_spdc_fb_setcolreg(u_int regno, u_int red, u_int green,
+ u_int blue, u_int transp, struct fb_info *info)
+{
+ if (regno >= 256) /* no. of hw registers */
+ return 1;
+
+ /* grayscale works only partially under directcolor */
+ if (info->var.grayscale) {
+ /* grayscale = 0.30*R + 0.59*G + 0.11*B */
+ red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
+ }
+
+#define CNVT_TOHW(val, width) ((((val)<<(width))+0x7FFF-(val))>>16)
+ switch (info->fix.visual) {
+ case FB_VISUAL_TRUECOLOR:
+ case FB_VISUAL_PSEUDOCOLOR:
+ red = CNVT_TOHW(red, info->var.red.length);
+ green = CNVT_TOHW(green, info->var.green.length);
+ blue = CNVT_TOHW(blue, info->var.blue.length);
+ transp = CNVT_TOHW(transp, info->var.transp.length);
+ break;
+ case FB_VISUAL_DIRECTCOLOR:
+ red = CNVT_TOHW(red, 8); /* expect 8 bit DAC */
+ green = CNVT_TOHW(green, 8);
+ blue = CNVT_TOHW(blue, 8);
+ /* hey, there is bug in transp handling... */
+ transp = CNVT_TOHW(transp, 8);
+ break;
+ }
+#undef CNVT_TOHW
+ /* Truecolor has hardware independent palette */
+ if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
+ if (regno >= 16)
+ return 1;
+
+ ((u32 *) (info->pseudo_palette))[regno] =
+ (red << info->var.red.offset) |
+ (green << info->var.green.offset) |
+ (blue << info->var.blue.offset) |
+ (transp << info->var.transp.offset);
+ }
+
+ return 0;
+}
+
+void mxc_spdc_fb_flush_updates(mxc_spdc_t *fb_data)
+{
+ int ret;
+
+ /* Grab queue lock to prevent any new updates from being submitted */
+ mutex_lock(&fb_data->queue_mutex);
+
+ /*
+ * 3 places to check for updates that are active or pending:
+ * 1) Updates in the pending list
+ * 2) Update buffers in use (e.g., PxP processing)
+ * 3) Active updates to panel - We can key off of SPDC
+ * power state to know if we have active updates.
+ */
+ if (!list_empty(&fb_data->upd_pending_list) ||
+ !is_free_list_full(fb_data) ||
+ (fb_data->updates_active == true)) {
+ /* Initialize event signalling updates are done */
+ init_completion(&fb_data->updates_done);
+ fb_data->waiting_for_idle = true;
+
+ mutex_unlock(&fb_data->queue_mutex);
+ /* Wait for any currently active updates to complete */
+ ret = wait_for_completion_timeout(&fb_data->updates_done,
+ msecs_to_jiffies(8000));
+ if (!ret)
+ dev_err(fb_data->dev,
+ "Flush updates timeout! ret = 0x%x\n", ret);
+
+ mutex_lock(&fb_data->queue_mutex);
+ fb_data->waiting_for_idle = false;
+ }
+
+ mutex_unlock(&fb_data->queue_mutex);
+}
+
+static int mxc_spdc_fb_setcmap(struct fb_cmap *cmap, struct fb_info *info)
+{
+ int count, index, r;
+ u16 *red, *green, *blue, *transp;
+ u16 trans = 0xffff;
+ mxc_spdc_t *fb_data = (mxc_spdc_t *)info;
+ int i;
+
+ dev_dbg(fb_data->dev, "setcmap\n");
+
+ if (info->fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR) {
+ /* Only support an 8-bit, 256 entry lookup */
+ if (cmap->len != 256)
+ return 1;
+
+ mxc_spdc_fb_flush_updates(fb_data);
+
+ mutex_lock(&fb_data->pxp_mutex);
+ /*
+ * Store colormap in pxp_conf structure for later transmit
+ * to PxP during update process to convert gray pixels.
+ *
+ * Since red=blue=green for pseudocolor visuals, we can
+ * just use red values.
+ */
+ for (i = 0; i < 256; i++)
+ fb_data->pxp_conf.proc_data.lut_map[i] =
+ cmap->red[i] & 0xFF;
+
+ fb_data->pxp_conf.proc_data.lut_map_updated = true;
+
+ mutex_unlock(&fb_data->pxp_mutex);
+ } else {
+ red = cmap->red;
+ green = cmap->green;
+ blue = cmap->blue;
+ transp = cmap->transp;
+ index = cmap->start;
+
+ for (count = 0; count < cmap->len; count++) {
+ if (transp)
+ trans = *transp++;
+ r = mxc_spdc_fb_setcolreg(index++, *red++,
+ *green++, *blue++, trans, info);
+ if (r != 0)
+ return r;
+ }
+ }
+
+ return 0;
+}
+
+static void adjust_coordinates(u32 xres, u32 yres, u32 rotation,
+ struct mxcfb_rect *update_region, struct mxcfb_rect *adj_update_region)
+{
+ u32 temp;
+
+ /* If adj_update_region == NULL, pass result back in update_region */
+ /* If adj_update_region == valid, use it to pass back result */
+ if (adj_update_region)
+ switch (rotation) {
+ case FB_ROTATE_UR:
+ adj_update_region->top = update_region->top;
+ adj_update_region->left = update_region->left;
+ adj_update_region->width = update_region->width;
+ adj_update_region->height = update_region->height;
+ break;
+ case FB_ROTATE_CW:
+ adj_update_region->top = update_region->left;
+ adj_update_region->left = yres -
+ (update_region->top + update_region->height);
+ adj_update_region->width = update_region->height;
+ adj_update_region->height = update_region->width;
+ break;
+ case FB_ROTATE_UD:
+ adj_update_region->width = update_region->width;
+ adj_update_region->height = update_region->height;
+ adj_update_region->top = yres -
+ (update_region->top + update_region->height);
+ adj_update_region->left = xres -
+ (update_region->left + update_region->width);
+ break;
+ case FB_ROTATE_CCW:
+ adj_update_region->left = update_region->top;
+ adj_update_region->top = xres -
+ (update_region->left + update_region->width);
+ adj_update_region->width = update_region->height;
+ adj_update_region->height = update_region->width;
+ break;
+ }
+ else
+ switch (rotation) {
+ case FB_ROTATE_UR:
+ /* No adjustment needed */
+ break;
+ case FB_ROTATE_CW:
+ temp = update_region->top;
+ update_region->top = update_region->left;
+ update_region->left = yres -
+ (temp + update_region->height);
+ temp = update_region->width;
+ update_region->width = update_region->height;
+ update_region->height = temp;
+ break;
+ case FB_ROTATE_UD:
+ update_region->top = yres -
+ (update_region->top + update_region->height);
+ update_region->left = xres -
+ (update_region->left + update_region->width);
+ break;
+ case FB_ROTATE_CCW:
+ temp = update_region->left;
+ update_region->left = update_region->top;
+ update_region->top = xres -
+ (temp + update_region->width);
+ temp = update_region->width;
+ update_region->width = update_region->height;
+ update_region->height = temp;
+ break;
+ }
+}
+
+static int mxc_spdc_fb_set_fix(struct fb_info *info)
+{
+ struct fb_fix_screeninfo *fix = &info->fix;
+ struct fb_var_screeninfo *var = &info->var;
+
+ fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
+
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->accel = FB_ACCEL_NONE;
+ if (var->grayscale)
+ fix->visual = FB_VISUAL_STATIC_PSEUDOCOLOR;
+ else
+ fix->visual = FB_VISUAL_TRUECOLOR;
+ fix->xpanstep = 1;
+ fix->ypanstep = 1;
+
+ return 0;
+}
+
+static int mxc_spdc_fb_set_par(struct fb_info *info)
+{
+ mxc_spdc_t *fb_data = (mxc_spdc_t *)info;
+ struct fb_var_screeninfo *screeninfo = &fb_data->info.var;
+ struct imx_spdc_fb_mode *spdc_modes = fb_data->pdata->spdc_mode;
+ struct pxp_config_data *pxp_conf = &fb_data->pxp_conf;
+ struct pxp_proc_data *proc_data = &pxp_conf->proc_data;
+ int i, ret;
+ __u32 xoffset_old, yoffset_old;
+
+ /*
+ * Can't change the FB parameters until current updates have completed.
+ * This function returns when all active updates are done.
+ */
+ mxc_spdc_fb_flush_updates(fb_data);
+
+ mutex_lock(&fb_data->queue_mutex);
+ /*
+ * Set all screeninfo except for xoffset/yoffset
+ * Subsequent call to pan_display will handle those.
+ */
+ xoffset_old = fb_data->spdc_fb_var.xoffset;
+ yoffset_old = fb_data->spdc_fb_var.yoffset;
+ fb_data->spdc_fb_var = *screeninfo;
+ fb_data->spdc_fb_var.xoffset = xoffset_old;
+ fb_data->spdc_fb_var.yoffset = yoffset_old;
+ mutex_unlock(&fb_data->queue_mutex);
+
+ mutex_lock(&fb_data->pxp_mutex);
+
+ /*
+ * Update PxP config data (used to process FB regions for updates)
+ * based on FB info and processing tasks required
+ */
+ /* Initialize non-channel-specific PxP parameters */
+ proc_data->drect.left = proc_data->srect.left = 0;
+ proc_data->drect.top = proc_data->srect.top = 0;
+ proc_data->drect.width = proc_data->srect.width = screeninfo->xres;
+ proc_data->drect.height = proc_data->srect.height = screeninfo->yres;
+ proc_data->scaling = 0;
+ proc_data->hflip = 0;
+ proc_data->vflip = 0;
+ proc_data->rotate = screeninfo->rotate;
+ proc_data->bgcolor = 0;
+ proc_data->overlay_state = 0;
+ proc_data->lut_transform = PXP_LUT_NONE;
+
+ /*
+ * configure S0 channel parameters
+ * Parameters should match FB format/width/height
+ */
+ if (screeninfo->grayscale)
+ pxp_conf->s0_param.pixel_fmt = PXP_PIX_FMT_GY04;
+ else {
+ switch (screeninfo->bits_per_pixel) {
+ case 16:
+ pxp_conf->s0_param.pixel_fmt = PXP_PIX_FMT_RGB565;
+ break;
+ case 24:
+ pxp_conf->s0_param.pixel_fmt = PXP_PIX_FMT_RGB24;
+ break;
+ case 32:
+ pxp_conf->s0_param.pixel_fmt = PXP_PIX_FMT_RGB32;
+ break;
+ default:
+ pxp_conf->s0_param.pixel_fmt = PXP_PIX_FMT_RGB565;
+ break;
+ }
+ }
+ pxp_conf->s0_param.width = screeninfo->xres_virtual;
+ pxp_conf->s0_param.height = screeninfo->yres;
+ pxp_conf->s0_param.color_key = -1;
+ pxp_conf->s0_param.color_key_enable = false;
+
+ /*
+ * Initialize Output channel parameters
+ * Output is Y-only greyscale
+ * Output width/height will vary based on update region size
+ */
+ pxp_conf->out_param.width = screeninfo->xres;
+ pxp_conf->out_param.height = screeninfo->yres;
+ pxp_conf->out_param.pixel_fmt = PXP_PIX_FMT_GY04;
+
+ mutex_unlock(&fb_data->pxp_mutex);
+
+ /* active new config, If HW not yet initialized,
+ * check to see if we are being sent
+ * an initialization request.
+ */
+ if (!fb_data->hw_ready) {
+ struct fb_videomode mode;
+ bool found_match = false;
+ u32 xres_temp;
+
+ fb_var_to_videomode(&mode, screeninfo);
+
+ /* When comparing requested fb mode,
+ * we need to use unrotated dimensions
+ */
+ if ((screeninfo->rotate == FB_ROTATE_CW) ||
+ (screeninfo->rotate == FB_ROTATE_CCW)) {
+ xres_temp = mode.xres;
+ mode.xres = mode.yres;
+ mode.yres = xres_temp;
+ }
+
+ /* Match videomode against spdc modes */
+ for (i = 0; i < fb_data->pdata->num_modes; i++) {
+ if (!fb_mode_is_equal(spdc_modes[i].vmode, &mode))
+ continue;
+ fb_data->cur_mode = &spdc_modes[i];
+ found_match = true;
+ break;
+ }
+
+ if (!found_match) {
+ dev_err(fb_data->dev,
+ "Failed to match requested video mode\n");
+ return EINVAL;
+ }
+
+ /* Initialize SPDC settings and init panel */
+ ret =
+ spdc_fb_init_hw((struct fb_info *)fb_data);
+ if (ret) {
+ dev_err(fb_data->dev,
+ "Failed to load panel waveform data\n");
+ return ret;
+ }
+ }
+
+ mxc_spdc_fb_set_fix(info);
+
+ return 0;
+}
+
+static int
+mxc_spdc_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ mxc_spdc_t *fb_data = (mxc_spdc_t *)info;
+
+ if (!var->xres)
+ var->xres = 1;
+ if (!var->yres)
+ var->yres = 1;
+
+ if (var->xres_virtual < var->xoffset + var->xres)
+ var->xres_virtual = var->xoffset + var->xres;
+ if (var->yres_virtual < var->yoffset + var->yres)
+ var->yres_virtual = var->yoffset + var->yres;
+
+ if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) &&
+ (var->bits_per_pixel != 16) && (var->bits_per_pixel != 8) &&
+ (var->bits_per_pixel != 4))
+ var->bits_per_pixel = SPDC_DEFAULT_BPP;
+
+ switch (var->bits_per_pixel) {
+ case 4:
+ var->red.offset = 0;
+ var->red.length = var->bits_per_pixel;
+ var->green = var->red;
+ var->blue = var->red;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ break;
+ case 8:
+ if (var->grayscale != 0) {
+ var->red.length = 8;
+ var->red.offset = 0;
+ var->red.msb_right = 0;
+
+ var->green = var->red;
+ var->blue = var->red;
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ var->transp.msb_right = 0;
+ } else {
+ var->red.length = 3;
+ var->red.offset = 5;
+ var->red.msb_right = 0;
+
+ var->green.length = 3;
+ var->green.offset = 2;
+ var->green.msb_right = 0;
+
+ var->blue.length = 2;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ var->transp.msb_right = 0;
+ }
+ break;
+ case 16:
+ var->red.length = 5;
+ var->red.offset = 11;
+ var->red.msb_right = 0;
+
+ var->green.length = 6;
+ var->green.offset = 5;
+ var->green.msb_right = 0;
+
+ var->blue.length = 5;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ var->transp.msb_right = 0;
+ break;
+ case 24:
+ var->red.length = 8;
+ var->red.offset = 16;
+ var->red.msb_right = 0;
+
+ var->green.length = 8;
+ var->green.offset = 8;
+ var->green.msb_right = 0;
+
+ var->blue.length = 8;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ var->transp.msb_right = 0;
+ break;
+ case 32:
+ var->red.length = 8;
+ var->red.offset = 16;
+ var->red.msb_right = 0;
+
+ var->green.length = 8;
+ var->green.offset = 8;
+ var->green.msb_right = 0;
+
+ var->blue.length = 8;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 8;
+ var->transp.offset = 24;
+ var->transp.msb_right = 0;
+ break;
+ }
+
+ switch (var->rotate) {
+ case FB_ROTATE_UR:
+ case FB_ROTATE_UD:
+ var->xres = fb_data->native_width;
+ var->yres = fb_data->native_height;
+ break;
+ case FB_ROTATE_CW:
+ case FB_ROTATE_CCW:
+ var->xres = fb_data->native_height;
+ var->yres = fb_data->native_width;
+ break;
+ default:
+ /* Invalid rotation value */
+ var->rotate = 0;
+ dev_dbg(fb_data->dev, "Invalid rotation request\n");
+ return -EINVAL;
+ }
+
+ var->xres_virtual = ALIGN(var->xres, 32);
+ var->yres_virtual = ALIGN(var->yres, 128) * fb_data->num_screens;
+
+ var->height = -1;
+ var->width = -1;
+
+ return 0;
+}
+
+void mxc_spdc_fb_set_waveform_modes(struct mxcfb_waveform_modes *modes,
+ struct fb_info *info)
+{
+ mxc_spdc_t *fb_data = info ?
+ (mxc_spdc_t *)info:g_spdc_fb_data;
+
+ mutex_lock(&fb_data->queue_mutex);
+
+ memcpy(&fb_data->wv_modes, modes, sizeof(struct mxcfb_waveform_modes));
+
+ mutex_unlock(&fb_data->queue_mutex);
+}
+EXPORT_SYMBOL(mxc_spdc_fb_set_waveform_modes);
+
+/* To stick with non-fractional degrees for the sake
+ * of API consistency with EPDC.
+ */
+int mxc_spdc_fb_set_temperature(int temperature, struct fb_info *info)
+{
+ mxc_spdc_t *fb_data = info ?
+ (mxc_spdc_t *)info:g_spdc_fb_data;
+ s8 temper = (s8)(temperature & 0xFF) << 1;
+
+ mutex_lock(&fb_data->queue_mutex);
+
+ if (temper > -110 && temper < 200)
+ __raw_writel(temper, fb_data->hwp + SPDC_TEMP_INFO);
+ else
+ __raw_writel(SPDC_DEFAULT_TEMP, fb_data->hwp + SPDC_TEMP_INFO);
+
+ mutex_unlock(&fb_data->queue_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(mxc_spdc_fb_set_temperature);
+
+
+int mxc_spdc_fb_set_auto_update(u32 auto_mode, struct fb_info *info)
+{
+ mxc_spdc_t *fb_data = info ?
+ (mxc_spdc_t *)info:g_spdc_fb_data;
+
+ dev_dbg(fb_data->dev, "Setting auto update mode to %d\n", auto_mode);
+
+ if ((auto_mode == AUTO_UPDATE_MODE_AUTOMATIC_MODE)
+ || (auto_mode == AUTO_UPDATE_MODE_REGION_MODE))
+ fb_data->auto_mode = auto_mode;
+ else {
+ dev_err(fb_data->dev, "Invalid auto update mode parameter.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(mxc_spdc_fb_set_auto_update);
+
+
+int mxc_spdc_fb_set_upd_scheme(u32 upd_scheme, struct fb_info *info)
+{
+ mxc_spdc_t *fb_data = info ?
+ (mxc_spdc_t *)info:g_spdc_fb_data;
+
+ dev_dbg(fb_data->dev, "Setting optimization level to %d\n", upd_scheme);
+
+ /*
+ * Can't change the scheme until current updates have completed.
+ * This function returns when all active updates are done.
+ */
+ mxc_spdc_fb_flush_updates(fb_data);
+
+ if ((upd_scheme == UPDATE_SCHEME_SNAPSHOT)
+ || (upd_scheme == UPDATE_SCHEME_QUEUE)
+ || (upd_scheme == UPDATE_SCHEME_QUEUE_AND_MERGE))
+ fb_data->upd_scheme = upd_scheme;
+ else {
+ dev_err(fb_data->dev, "Invalid update scheme specified.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(mxc_spdc_fb_set_upd_scheme);
+
+/* Callback function triggered after PxP receives an EOF interrupt */
+static void pxp_dma_done(void *arg)
+{
+ struct pxp_tx_desc *tx_desc = to_tx_desc(arg);
+ struct dma_chan *chan = tx_desc->txd.chan;
+ struct pxp_channel *pxp_chan = to_pxp_channel(chan);
+ mxc_spdc_t *fb_data = pxp_chan->client;
+
+ /* This call will signal wait_for_completion_timeout()
+ * in send_buffer_to_pxp
+ */
+ complete(&fb_data->pxp_tx_cmpl);
+}
+
+static bool chan_filter(struct dma_chan *chan, void *arg)
+{
+ if (imx_dma_is_pxp(chan))
+ return true;
+ else
+ return false;
+}
+
+/* Function to request PXP DMA channel */
+static int pxp_chan_init(mxc_spdc_t *fb_data)
+{
+ dma_cap_mask_t mask;
+ struct dma_chan *chan;
+
+ /*
+ * Request a free channel
+ */
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ dma_cap_set(DMA_PRIVATE, mask);
+ chan = dma_request_channel(mask, chan_filter, NULL);
+ if (!chan) {
+ dev_err(fb_data->dev, "Unsuccessfully received channel!!!!\n");
+ return -EBUSY;
+ }
+
+ fb_data->pxp_chan = to_pxp_channel(chan);
+ fb_data->pxp_chan->client = fb_data;
+
+ init_completion(&fb_data->pxp_tx_cmpl);
+
+ return 0;
+}
+
+/*
+ * Function to call PxP DMA driver and send our latest FB update region
+ * through the PxP and out to an intermediate buffer.
+ * Note: This is a blocking call, so upon return the PxP tx should be complete.
+ */
+static int pxp_process_update(mxc_spdc_t *fb_data,
+ u32 src_width, u32 src_height,
+ struct mxcfb_rect *update_region)
+{
+ dma_cookie_t cookie;
+ struct scatterlist *sg = fb_data->sg;
+ struct dma_chan *dma_chan;
+ struct pxp_tx_desc *desc;
+ struct dma_async_tx_descriptor *txd;
+ struct pxp_config_data *pxp_conf = &fb_data->pxp_conf;
+ struct pxp_proc_data *proc_data = &fb_data->pxp_conf.proc_data;
+ int i, ret;
+ int length;
+
+ /* First, check to see that we have acquired a PxP Channel object */
+ if (fb_data->pxp_chan == NULL) {
+ /*
+ * PxP Channel has not yet been created and initialized,
+ * so let's go ahead and try
+ */
+ ret = pxp_chan_init(fb_data);
+ if (ret) {
+ /*
+ * PxP channel init failed, and we can't use the
+ * PxP until the PxP DMA driver has loaded, so we abort
+ */
+ dev_err(fb_data->dev, "PxP chan init failed\n");
+ return -ENODEV;
+ }
+ }
+
+ /*
+ * Init completion, so that we
+ * can be properly informed of the completion
+ * of the PxP task when it is done.
+ */
+ init_completion(&fb_data->pxp_tx_cmpl);
+
+ dma_chan = &fb_data->pxp_chan->dma_chan;
+
+ txd = dma_chan->device->device_prep_slave_sg(dma_chan, sg, 2,
+ DMA_TO_DEVICE,
+ DMA_PREP_INTERRUPT);
+ if (!txd) {
+ dev_err(fb_data->info.device,
+ "Error preparing a DMA transaction descriptor.\n");
+ return -EIO;
+ }
+
+ txd->callback_param = txd;
+ txd->callback = pxp_dma_done;
+
+ /*
+ * Configure PxP for processing of new update region
+ * The rest of our config params were set up in
+ * probe() and should not need to be changed.
+ */
+ pxp_conf->s0_param.width = src_width;
+ pxp_conf->s0_param.height = src_height;
+ proc_data->srect.top = update_region->top;
+ proc_data->srect.left = update_region->left;
+ proc_data->srect.width = update_region->width;
+ proc_data->srect.height = update_region->height;
+
+ /*
+ * Because only YUV/YCbCr image can be scaled, configure
+ * drect equivalent to srect, as such do not perform scaling.
+ */
+ proc_data->drect.top = 0;
+ proc_data->drect.left = 0;
+ proc_data->drect.width = proc_data->srect.width;
+ proc_data->drect.height = proc_data->srect.height;
+
+ /* PXP expects rotation in terms of degrees */
+ proc_data->rotate = fb_data->spdc_fb_var.rotate * 90;
+ if (proc_data->rotate > 270)
+ proc_data->rotate = 0;
+
+ pxp_conf->out_param.width = update_region->width;
+ pxp_conf->out_param.height = update_region->height;
+
+ if ((proc_data->rotate == 90) || (proc_data->rotate == 270))
+ pxp_conf->out_param.stride = update_region->height;
+ else
+ pxp_conf->out_param.stride = update_region->width;
+
+ desc = to_tx_desc(txd);
+ length = desc->len;
+ for (i = 0; i < length; i++) {
+ if (i == 0) {/* S0 */
+ memcpy(&desc->proc_data, proc_data,
+ sizeof(struct pxp_proc_data));
+ pxp_conf->s0_param.paddr = sg_dma_address(&sg[0]);
+ memcpy(&desc->layer_param.s0_param, &pxp_conf->s0_param,
+ sizeof(struct pxp_layer_param));
+ } else if (i == 1) {
+ pxp_conf->out_param.paddr = sg_dma_address(&sg[1]);
+ memcpy(&desc->layer_param.out_param,
+ &pxp_conf->out_param,
+ sizeof(struct pxp_layer_param));
+ }
+ /* TODO: OverLay */
+
+ desc = desc->next;
+ }
+
+ /* Submitting our TX starts the PxP processing task */
+ cookie = txd->tx_submit(txd);
+ if (cookie < 0) {
+ dev_err(fb_data->info.device, "Error sending FB through PxP\n");
+ return -EIO;
+ }
+
+ fb_data->txd = txd;
+
+ /* trigger ePxP */
+ dma_async_issue_pending(dma_chan);
+
+ return 0;
+}
+
+static int pxp_complete_update(mxc_spdc_t *fb_data, u32 *hist_stat)
+{
+ int ret;
+ /*
+ * Wait for completion event, which will be set
+ * through our TX callback function.
+ */
+ ret = wait_for_completion_timeout(&fb_data->pxp_tx_cmpl, HZ / 10);
+ if (ret <= 0) {
+ dev_info(fb_data->info.device,
+ "PxP operation failed due to %s\n",
+ ret < 0 ? "user interrupt" : "timeout");
+ dma_release_channel(&fb_data->pxp_chan->dma_chan);
+ fb_data->pxp_chan = NULL;
+ return ret ? : -ETIMEDOUT;
+ }
+
+ if ((fb_data->pxp_conf.proc_data.lut_transform & EPDC_FLAG_USE_CMAP) &&
+ fb_data->pxp_conf.proc_data.lut_map_updated)
+ fb_data->pxp_conf.proc_data.lut_map_updated = false;
+
+ *hist_stat = to_tx_desc(fb_data->txd)->hist_status;
+ dma_release_channel(&fb_data->pxp_chan->dma_chan);
+ fb_data->pxp_chan = NULL;
+
+ dev_dbg(fb_data->dev, "TX completed\n");
+
+ return 0;
+}
+
+static void copy_to_next_buffer(mxc_spdc_t *fb_data,
+ struct update_data_list *upd_data_list)
+{
+ struct mxcfb_update_data *upd_data =
+ &upd_data_list->update_desc->upd_data;
+ unsigned char *temp_buf_ptr = fb_data->virt_addr_copybuf;
+ unsigned char *dst_ptr = upd_data_list->virt_addr;
+ struct mxcfb_rect adj_update_region;
+ int dst_stride, left_offs, line_width;
+ int i;
+
+ switch (fb_data->spdc_fb_var.rotate) {
+ case FB_ROTATE_UR:
+ adj_update_region.top = upd_data->update_region.top;
+ adj_update_region.left = upd_data->update_region.left;
+ adj_update_region.width = upd_data->update_region.width;
+ adj_update_region.height = upd_data->update_region.height;
+ dst_stride = fb_data->spdc_fb_var.xres_virtual / 2;
+ break;
+ case FB_ROTATE_CW:
+ adj_update_region.top = upd_data->update_region.left;
+ adj_update_region.left = fb_data->spdc_fb_var.yres -
+ (upd_data->update_region.top +
+ upd_data->update_region.height);
+ adj_update_region.width = upd_data->update_region.height;
+ adj_update_region.height = upd_data->update_region.width;
+ dst_stride = fb_data->spdc_fb_var.yres / 2;
+ break;
+ case FB_ROTATE_UD:
+ adj_update_region.width = upd_data->update_region.width;
+ adj_update_region.height = upd_data->update_region.height;
+ adj_update_region.top = fb_data->spdc_fb_var.yres -
+ (upd_data->update_region.top + upd_data->update_region.height);
+ adj_update_region.left = fb_data->spdc_fb_var.xres -
+ (upd_data->update_region.left +
+ upd_data->update_region.width);
+ dst_stride = fb_data->spdc_fb_var.xres_virtual / 2;
+ break;
+ case FB_ROTATE_CCW:
+ adj_update_region.left = upd_data->update_region.top;
+ adj_update_region.top = fb_data->spdc_fb_var.xres -
+ (upd_data->update_region.left +
+ upd_data->update_region.width);
+ adj_update_region.width = upd_data->update_region.height;
+ adj_update_region.height = upd_data->update_region.width;
+ dst_stride = fb_data->spdc_fb_var.yres / 2;
+ break;
+ }
+
+ /* pxp output Y4 data.
+ * Copy the raw data to related region in next buffer.
+ */
+ left_offs = adj_update_region.left / 2;
+ line_width = adj_update_region.width / 2;
+
+ dst_ptr += (adj_update_region.top * dst_stride + left_offs);
+ for (i = 0; i < adj_update_region.height; i++) {
+ /* Copy the full line */
+ memcpy(dst_ptr, temp_buf_ptr, line_width);
+
+ dst_ptr += dst_stride;
+ temp_buf_ptr += line_width;
+ }
+}
+
+static int spdc_process_update(struct update_data_list *upd_data_list,
+ mxc_spdc_t *fb_data)
+{
+ /* Region of src buffer for update */
+ struct mxcfb_rect *src_upd_region;
+ struct mxcfb_rect pxp_upd_region;
+ struct update_desc_list *upd_desc_list = upd_data_list->update_desc;
+ u32 src_width, src_height;
+ u32 offset_from_4, bytes_per_pixel;
+ u32 post_rotation_xcoord, post_rotation_ycoord, width_pxp_blocks;
+ u32 pxp_input_offs, pxp_output_offs, pxp_output_shift;
+ bool input_unaligned = false;
+ u32 hist_stat = 0;
+ bool use_temp_buf = false;
+ int ret;
+
+ /*
+ * Are we using FB or an alternate (overlay)
+ * buffer for source of update?
+ */
+ if (upd_desc_list->upd_data.flags & EPDC_FLAG_USE_ALT_BUFFER) {
+ src_width = upd_desc_list->upd_data.alt_buffer_data.width;
+ src_height = upd_desc_list->upd_data.alt_buffer_data.height;
+ src_upd_region =
+ &upd_desc_list->upd_data.alt_buffer_data.alt_update_region;
+ } else {
+ src_width = fb_data->spdc_fb_var.xres_virtual;
+ src_height = fb_data->spdc_fb_var.yres;
+ src_upd_region = &upd_desc_list->upd_data.update_region;
+ }
+
+ if (!(src_upd_region->width == fb_data->spdc_fb_var.xres_virtual &&
+ fb_data->spdc_fb_var.rotate == FB_ROTATE_UR))
+ use_temp_buf = true;
+
+ bytes_per_pixel = fb_data->spdc_fb_var.bits_per_pixel / 8;
+
+ /* Grab pxp_mutex here so that we protect access
+ * to copybuf in addition to the PxP structures */
+ mutex_lock(&fb_data->pxp_mutex);
+
+ offset_from_4 = src_upd_region->left & 0x3;
+ input_unaligned = ((offset_from_4 * bytes_per_pixel % 4) != 0) ?
+ true : false;
+
+ if (input_unaligned) {
+ /* Leave a gap between PxP input addr
+ * and update region pixels
+ */
+ pxp_input_offs =
+ (src_upd_region->top * src_width + src_upd_region->left)
+ * bytes_per_pixel & 0xFFFFFFFC;
+ /* Update region left changes to reflect
+ * relative position to input ptr
+ */
+ pxp_upd_region.left = (offset_from_4 * bytes_per_pixel % 4)
+ / bytes_per_pixel;
+ } else {
+ pxp_input_offs =
+ (src_upd_region->top * src_width + src_upd_region->left)
+ * bytes_per_pixel;
+ pxp_upd_region.left = 0;
+ }
+ pxp_upd_region.top = 0;
+
+ /* Update region dimensions to meet 8x8 pixel requirement */
+ if (fb_data->spdc_fb_var.rotate == 0) {
+ pxp_upd_region.width = ALIGN(src_upd_region->width, 8);
+ pxp_upd_region.height = ALIGN(src_upd_region->height, 8);
+ } else {
+ pxp_upd_region.width =
+ ALIGN(src_upd_region->width + pxp_upd_region.left, 8);
+ pxp_upd_region.height = ALIGN(src_upd_region->height, 8);
+ }
+
+ switch (fb_data->spdc_fb_var.rotate) {
+ case FB_ROTATE_UR:
+ default:
+ post_rotation_xcoord = pxp_upd_region.left;
+ post_rotation_ycoord = pxp_upd_region.top;
+ width_pxp_blocks = pxp_upd_region.width;
+ break;
+ case FB_ROTATE_CW:
+ width_pxp_blocks = pxp_upd_region.height;
+ post_rotation_xcoord = width_pxp_blocks -
+ src_upd_region->height;
+ post_rotation_ycoord = pxp_upd_region.left;
+ break;
+ case FB_ROTATE_UD:
+ width_pxp_blocks = pxp_upd_region.width;
+ post_rotation_xcoord = width_pxp_blocks -
+ src_upd_region->width - pxp_upd_region.left;
+ post_rotation_ycoord = pxp_upd_region.height -
+ src_upd_region->height - pxp_upd_region.top;
+ break;
+ case FB_ROTATE_CCW:
+ width_pxp_blocks = pxp_upd_region.height;
+ post_rotation_xcoord = pxp_upd_region.top;
+ post_rotation_ycoord = pxp_upd_region.width -
+ src_upd_region->width - pxp_upd_region.left;
+ break;
+ }
+
+ /* Update region start coord to force PxP to
+ * process full 8x8 regions
+ */
+ pxp_upd_region.top &= ~0x7;
+ pxp_upd_region.left &= ~0x7;
+
+ pxp_output_shift = ALIGN(post_rotation_xcoord, 8)
+ - post_rotation_xcoord;
+ pxp_output_offs = post_rotation_ycoord * width_pxp_blocks
+ + pxp_output_shift;
+ upd_desc_list->spdc_offs = ALIGN(pxp_output_offs, 8);
+
+ /* Source address either comes from alternate buffer
+ provided in update data, or from the framebuffer. */
+ if (upd_desc_list->upd_data.flags & EPDC_FLAG_USE_ALT_BUFFER)
+ sg_dma_address(&fb_data->sg[0]) =
+ upd_desc_list->upd_data.alt_buffer_data.phys_addr
+ + pxp_input_offs;
+ else {
+ sg_dma_address(&fb_data->sg[0]) =
+ fb_data->info.fix.smem_start + fb_data->fb_offset
+ + pxp_input_offs;
+ sg_set_page(&fb_data->sg[0],
+ virt_to_page(fb_data->info.screen_base),
+ fb_data->info.fix.smem_len,
+ offset_in_page(fb_data->info.screen_base));
+ }
+
+ /* Update sg[1] to point to output of PxP proc task */
+ if (!use_temp_buf) {
+ sg_dma_address(&fb_data->sg[1]) = upd_data_list->phys_addr;
+ sg_set_page(&fb_data->sg[1],
+ virt_to_page(upd_data_list->virt_addr),
+ fb_data->max_pix_size,
+ offset_in_page(upd_data_list->virt_addr));
+ } else {
+ sg_dma_address(&fb_data->sg[1]) = fb_data->phys_addr_copybuf;
+ sg_set_page(&fb_data->sg[1],
+ virt_to_page(fb_data->virt_addr_copybuf),
+ fb_data->max_pix_size,
+ offset_in_page(fb_data->virt_addr_copybuf));
+ }
+
+ /*
+ * Set PxP LUT transform type based on update flags.
+ */
+ fb_data->pxp_conf.proc_data.lut_transform = 0;
+ if (upd_desc_list->upd_data.flags & EPDC_FLAG_ENABLE_INVERSION)
+ fb_data->pxp_conf.proc_data.lut_transform |= PXP_LUT_INVERT;
+ if (upd_desc_list->upd_data.flags & EPDC_FLAG_FORCE_MONOCHROME)
+ fb_data->pxp_conf.proc_data.lut_transform |=
+ PXP_LUT_BLACK_WHITE;
+ if (upd_desc_list->upd_data.flags & EPDC_FLAG_USE_CMAP)
+ fb_data->pxp_conf.proc_data.lut_transform |=
+ PXP_LUT_USE_CMAP;
+
+ /*
+ * Toggle inversion processing if 8-bit
+ * inverted is the current pixel format.
+ */
+ if (fb_data->spdc_fb_var.grayscale == GRAYSCALE_4BIT_INVERTED)
+ fb_data->pxp_conf.proc_data.lut_transform ^= PXP_LUT_INVERT;
+
+ /* This is a blocking call, so upon return PxP tx should be done */
+ ret = pxp_process_update(fb_data, src_width, src_height,
+ &pxp_upd_region);
+ if (ret) {
+ dev_err(fb_data->dev, "Unable to submit PxP update task.\n");
+ mutex_unlock(&fb_data->pxp_mutex);
+ return ret;
+ }
+
+ /* If needed, enable SPDC HW while ePxP is processing */
+ if ((fb_data->power_state == POWER_STATE_OFF)
+ || fb_data->powering_down) {
+ spdc_powerup(fb_data);
+ }
+
+ /* This is a blocking call, so upon return PxP tx should be done */
+ ret = pxp_complete_update(fb_data, &hist_stat);
+ if (ret) {
+ dev_err(fb_data->dev, "Unable to complete PxP update task.\n");
+ mutex_unlock(&fb_data->pxp_mutex);
+ return ret;
+ }
+
+ if (use_temp_buf)
+ copy_to_next_buffer(fb_data, upd_data_list);
+
+ mutex_unlock(&fb_data->pxp_mutex);
+
+ /* Update waveform mode from PxP histogram results */
+ if (upd_desc_list->upd_data.waveform_mode == WAVEFORM_MODE_AUTO) {
+ if (hist_stat & 0x1)
+ upd_desc_list->upd_data.waveform_mode =
+ fb_data->wv_modes.mode_du;
+ else if (hist_stat & 0x2)
+ upd_desc_list->upd_data.waveform_mode =
+ fb_data->wv_modes.mode_gc4;
+ else if (hist_stat & 0x4)
+ upd_desc_list->upd_data.waveform_mode =
+ fb_data->wv_modes.mode_gc8;
+ else if (hist_stat & 0x8)
+ upd_desc_list->upd_data.waveform_mode =
+ fb_data->wv_modes.mode_gc16;
+ else
+ upd_desc_list->upd_data.waveform_mode =
+ fb_data->wv_modes.mode_gc32;
+
+ dev_dbg(fb_data->dev, "hist_stat = 0x%x, new waveform = 0x%x\n",
+ hist_stat, upd_desc_list->upd_data.waveform_mode);
+ }
+
+ return 0;
+}
+
+static bool spdc_submit_concur(mxc_spdc_t *fb_data,
+ struct update_desc_list *update_to_concur)
+{
+ struct mxcfb_update_data *a, *b;
+ struct mxcfb_rect *arect, *brect;
+ struct update_data_list *next_upd;
+ int i = 0;
+
+ a = &update_to_concur->upd_data;
+ arect = &update_to_concur->upd_data.update_region;
+
+ list_for_each_entry(next_upd,
+ &fb_data->upd_buf_preprocess_list, list) {
+ b = &next_upd->update_desc->upd_data;
+ brect = &next_upd->update_desc->upd_data.update_region;
+
+ /* Updates with different waveform
+ * must be executed sequentially.
+ */
+ if (a->waveform_mode != b->waveform_mode)
+ break;
+
+ /*
+ * Concurrency update must has no overlay
+ */
+ if (!(arect->left > (brect->left + brect->width) ||
+ brect->left > (arect->left + arect->width) ||
+ arect->top > (brect->top + brect->height) ||
+ brect->top > (arect->top + arect->height)))
+ break;
+
+ i++;
+ }
+
+ if (i != fb_data->upd_preprocess_num)
+ return false;
+
+ return true;
+}
+
+static int spdc_submit_merge(struct update_desc_list *upd_desc_list,
+ struct update_desc_list *update_to_merge)
+{
+ struct mxcfb_update_data *a, *b;
+ struct mxcfb_rect *arect, *brect;
+ struct mxcfb_rect combine;
+ bool use_flags = false;
+
+ a = &upd_desc_list->upd_data;
+ b = &update_to_merge->upd_data;
+ arect = &upd_desc_list->upd_data.update_region;
+ brect = &update_to_merge->upd_data.update_region;
+
+ /*
+ * Updates with different flags must be executed sequentially.
+ * Halt the merge process to ensure this.
+ */
+ if (a->flags != b->flags) {
+ /*
+ * Special exception: if update regions are identical,
+ * we may be able to merge them.
+ */
+ if ((arect->left != brect->left) ||
+ (arect->top != brect->top) ||
+ (arect->width != brect->width) ||
+ (arect->height != brect->height))
+ return MERGE_BLOCK;
+
+ use_flags = true;
+ }
+
+ if (a->waveform_mode != b->waveform_mode)
+ a->waveform_mode = WAVEFORM_MODE_AUTO;
+
+ if (arect->left > (brect->left + brect->width) ||
+ brect->left > (arect->left + arect->width) ||
+ arect->top > (brect->top + brect->height) ||
+ brect->top > (arect->top + arect->height))
+ return MERGE_FAIL;
+
+ combine.left = arect->left < brect->left ? arect->left : brect->left;
+ combine.top = arect->top < brect->top ? arect->top : brect->top;
+ combine.width = (arect->left + arect->width) >
+ (brect->left + brect->width) ?
+ (arect->left + arect->width - combine.left) :
+ (brect->left + brect->width - combine.left);
+ combine.height = (arect->top + arect->height) >
+ (brect->top + brect->height) ?
+ (arect->top + arect->height - combine.top) :
+ (brect->top + brect->height - combine.top);
+
+ *arect = combine;
+
+ /* Use flags of the later update */
+ if (use_flags)
+ a->flags = b->flags;
+
+ /* Merge markers */
+ list_splice_tail(&update_to_merge->upd_marker_list,
+ &upd_desc_list->upd_marker_list);
+
+ return MERGE_OK;
+
+}
+
+static void spdc_submit_work_func(struct work_struct *work)
+{
+ struct update_desc_list *next_desc, *temp_desc;
+ mxc_spdc_t *fb_data =
+ container_of(work, mxc_spdc_t, spdc_submit_work);
+ struct update_data_list *upd_data_list = NULL;
+ struct mxcfb_rect adj_update_region, *upd_region;
+ struct update_marker_data *current_marker;
+ bool end_merge = false;
+ bool is_transform;
+ u32 update_addr;
+
+ /* Protect access to buffer queues and to update HW */
+ mutex_lock(&fb_data->queue_mutex);
+
+ /* get a buffer from free list */
+ if (list_empty(&fb_data->upd_buf_free_list)) {
+ mutex_unlock(&fb_data->queue_mutex);
+ return;
+ }
+
+ if (fb_data->fresh_param.concur == SPDC_LUT_ACC_MODE) {
+ list_for_each_entry_safe(next_desc, temp_desc,
+ &fb_data->upd_pending_list, list) {
+
+ current_marker =
+ list_entry((&next_desc->upd_marker_list)->next,
+ struct update_marker_data, upd_list);
+
+ if (current_marker->update_marker) {
+ fb_data->submit_upd_sta = 0;
+ break;
+ }
+
+ /* require free buffer list */
+ if (list_empty(&fb_data->upd_buf_free_list)) {
+ dev_dbg(fb_data->dev,
+ "buf free list is empty\n");
+ break;
+ }
+
+ upd_data_list =
+ list_entry(fb_data->upd_buf_free_list.next,
+ struct update_data_list, list);
+ upd_data_list->update_desc = next_desc;
+
+ if (!is_preprocess_list_full(fb_data)) {
+ if (fb_data->cur_update == NULL &&
+ !fb_data->upd_preprocess_num)
+ list_del_init(&next_desc->list);
+ else if (spdc_submit_concur(fb_data,
+ next_desc)) {
+ list_del_init(&next_desc->list);
+ list_add_tail(&upd_data_list->list,
+ &fb_data->upd_buf_preprocess_list);
+ fb_data->upd_preprocess_num++;
+ fb_data->submit_upd_sta =
+ SPDC_CONCUR_UPD;
+ } else
+ break;
+ } else
+ break;
+
+ /* submit to pxp process */
+ list_del_init(&upd_data_list->list);
+ goto pxp_process;
+ }
+
+ upd_data_list = NULL;
+ if (fb_data->submit_upd_sta == SPDC_CONCUR_UPD)
+ fb_data->submit_upd_sta |= SPDC_QUEUE_UPD;
+ else
+ fb_data->submit_upd_sta = SPDC_QUEUE_UPD;
+ }
+
+ list_for_each_entry_safe(next_desc, temp_desc,
+ &fb_data->upd_pending_list, list) {
+
+ if (!upd_data_list) {
+
+ if (list_empty(&fb_data->upd_buf_free_list)) {
+ dev_dbg(fb_data->dev,
+ "buf_free_list is empty\n");
+ break;
+ }
+ upd_data_list =
+ list_entry(fb_data->upd_buf_free_list.next,
+ struct update_data_list, list);
+ list_del_init(&upd_data_list->list);
+ upd_data_list->update_desc = next_desc;
+ list_del_init(&next_desc->list);
+
+ if (fb_data->upd_scheme == UPDATE_SCHEME_QUEUE)
+ break;
+ } else {
+ switch (spdc_submit_merge(upd_data_list->update_desc,
+ next_desc)) {
+ case MERGE_OK:
+ dev_dbg(fb_data->dev,
+ "Update merged [queue]\n");
+ list_del_init(&next_desc->list);
+ kfree(next_desc);
+ break;
+ case MERGE_FAIL:
+ dev_dbg(fb_data->dev,
+ "Update not merged [queue]\n");
+ break;
+ case MERGE_BLOCK:
+ dev_dbg(fb_data->dev,
+ "Merge blocked [collision]\n");
+ end_merge = true;
+ break;
+ }
+
+ if (end_merge)
+ break;
+ }
+ }
+
+ /* Is update list empty? */
+ if (!upd_data_list) {
+ mutex_unlock(&fb_data->queue_mutex);
+ return;
+ }
+
+pxp_process:
+ /*
+ * If no processing required, skip update processing
+ * No processing means:
+ * - FB unrotated
+ * - FB pixel format = 4-bit grayscale
+ * - No look-up transformations (inversion, posterization, etc.)
+ */
+ is_transform = upd_data_list->update_desc->upd_data.flags &
+ (EPDC_FLAG_ENABLE_INVERSION |
+ EPDC_FLAG_FORCE_MONOCHROME | EPDC_FLAG_USE_CMAP) ?
+ true : false;
+ if ((fb_data->spdc_fb_var.rotate == FB_ROTATE_UR) &&
+ (fb_data->spdc_fb_var.grayscale == GRAYSCALE_4BIT) &&
+ !is_transform) {
+
+ /* If needed, enable SPDC HW while ePxP is processing */
+ if ((fb_data->power_state == POWER_STATE_OFF)
+ || fb_data->powering_down)
+ spdc_powerup(fb_data);
+
+ /*
+ * Set update buffer pointer to the start of
+ * the update region in the frame buffer.
+ */
+ upd_region =
+ &upd_data_list->update_desc->upd_data.update_region;
+ update_addr = fb_data->info.fix.smem_start +
+ ((upd_region->top * fb_data->info.var.xres_virtual) +
+ upd_region->left) * fb_data->info.var.bits_per_pixel/8;
+ } else {
+
+ /* Select from PxP output buffers */
+ upd_data_list->phys_addr =
+ fb_data->phys_addr_updbuf[fb_data->upd_buffer_num];
+ upd_data_list->virt_addr =
+ fb_data->virt_addr_updbuf[fb_data->upd_buffer_num];
+ fb_data->upd_buffer_num++;
+ if (fb_data->upd_buffer_num > fb_data->max_num_buffers-1)
+ fb_data->upd_buffer_num = 0;
+ dev_dbg(fb_data->dev,
+ "pxp out addr:0x%x\n", upd_data_list->phys_addr);
+
+ /* Release buffer queues */
+ mutex_unlock(&fb_data->queue_mutex);
+
+ /* Perform PXP processing - SPDC power will also be enabled */
+ if (spdc_process_update(upd_data_list, fb_data)) {
+
+ dev_dbg(fb_data->dev, "PXP processing error.\n");
+ /* Protect access to buffer queues and to update HW */
+ mutex_lock(&fb_data->queue_mutex);
+ list_del_init(&upd_data_list->update_desc->list);
+ kfree(upd_data_list->update_desc);
+ upd_data_list->update_desc = NULL;
+
+ /* Add to free buffer list */
+ list_add_tail(&upd_data_list->list,
+ &fb_data->upd_buf_free_list);
+ /* Release buffer queues */
+ mutex_unlock(&fb_data->queue_mutex);
+ return;
+ }
+
+ /* Protect access to buffer queues and to update HW */
+ mutex_lock(&fb_data->queue_mutex);
+
+ /* output Y4 format */
+ update_addr = upd_data_list->phys_addr +
+ + (upd_data_list->update_desc->spdc_offs / 2);
+ }
+
+ /* Get rotation-adjusted coordinates */
+ adjust_coordinates(fb_data->spdc_fb_var.xres,
+ fb_data->spdc_fb_var.yres, fb_data->spdc_fb_var.rotate,
+ &upd_data_list->update_desc->upd_data.update_region,
+ &adj_update_region);
+
+ /*
+ * Is the working buffer idle?
+ * If the working buffer is busy, we must wait for the resource
+ * to become free.
+ */
+ if (fb_data->cur_update != NULL &&
+ fb_data->submit_upd_sta != SPDC_CONCUR_UPD) {
+ /* Initialize event signalling an update resource is free */
+ init_completion(&fb_data->update_res_free);
+
+ fb_data->waiting_for_wb = true;
+
+ /* Leave spinlock while waiting for WB to complete */
+ mutex_unlock(&fb_data->queue_mutex);
+ wait_for_completion(&fb_data->update_res_free);
+ mutex_lock(&fb_data->queue_mutex);
+ }
+
+ if (fb_data->submit_upd_sta != SPDC_CONCUR_UPD)
+ fb_data->cur_update = upd_data_list;
+
+ /* program SPDC register and trigger to process buffer*/
+ fb_data->fresh_param.buf_addr.next_buf_phys_addr = update_addr;
+
+ fb_data->fresh_param.update_region.left = adj_update_region.left;
+ fb_data->fresh_param.update_region.top = adj_update_region.top;
+ fb_data->fresh_param.update_region.width = adj_update_region.width;
+ fb_data->fresh_param.update_region.height = adj_update_region.height;
+ fb_data->fresh_param.temper = upd_data_list->update_desc->upd_data.temp;
+ fb_data->fresh_param.wave_mode =
+ upd_data_list->update_desc->upd_data.waveform_mode;
+
+ spdc_submit_update(fb_data);
+
+ /* Release buffer queues */
+ mutex_unlock(&fb_data->queue_mutex);
+}
+
+int mxc_spdc_fb_send_update(struct mxcfb_update_data *upd_data,
+ struct fb_info *info)
+{
+ mxc_spdc_t *fb_data = info ?
+ (mxc_spdc_t *)info:g_spdc_fb_data;
+ struct update_data_list *upd_data_list = NULL;
+ struct mxcfb_rect *screen_upd_region; /* Region on screen to update */
+ struct update_desc_list *upd_desc;
+ struct update_marker_data *marker_data;
+ int ret;
+
+ /* Has SPDC HW been initialized? */
+ if (!fb_data->hw_ready) {
+ /* Throw message if we are not mid-initialization */
+ if (!fb_data->hw_initializing)
+ dev_err(fb_data->dev, "Display HW not properly"
+ "initialized. Aborting update.\n");
+ return -EPERM;
+ }
+
+ if ((upd_data->waveform_mode > SPDC_WAV_MODE_5) &&
+ (upd_data->waveform_mode != WAVEFORM_MODE_AUTO)) {
+ dev_err(fb_data->dev,
+ "Update waveform mode 0x%x is invalid."
+ " Aborting update.\n",
+ upd_data->waveform_mode);
+ return -EINVAL;
+ }
+ if ((upd_data->update_region.left + upd_data->update_region.width >
+ fb_data->spdc_fb_var.xres + 1) ||
+ (upd_data->update_region.top + upd_data->update_region.height >
+ fb_data->spdc_fb_var.yres + 1)) {
+ dev_err(fb_data->dev,
+ "Update region is outside bounds of framebuffer."
+ "Aborting update.\n");
+ return -EINVAL;
+ }
+ if (upd_data->flags & EPDC_FLAG_USE_ALT_BUFFER) {
+ if ((upd_data->update_region.width !=
+ upd_data->alt_buffer_data.alt_update_region.width) ||
+ (upd_data->update_region.height !=
+ upd_data->alt_buffer_data.alt_update_region.height)) {
+ dev_err(fb_data->dev,
+ "Alternate update region dimensions must "
+ "match screen update region dimensions.\n");
+ return -EINVAL;
+ }
+ /* Validate physical address parameter */
+ if ((upd_data->alt_buffer_data.phys_addr <
+ fb_data->info.fix.smem_start) ||
+ (upd_data->alt_buffer_data.phys_addr >
+ fb_data->info.fix.smem_start + fb_data->map_size)) {
+ dev_err(fb_data->dev,
+ "Invalid physical address for alternate "
+ "buffer. Aborting update...\n");
+ return -EINVAL;
+ }
+ }
+
+ mutex_lock(&fb_data->queue_mutex);
+
+ /*
+ * If we are waiting to go into suspend, or the FB is blanked,
+ * we do not accept new updates
+ */
+ if (fb_data->waiting_for_idle) {
+ dev_dbg(fb_data->dev, "SPDC not active."
+ "Update request abort.\n");
+ mutex_unlock(&fb_data->queue_mutex);
+ return -EPERM;
+ }
+
+ if (fb_data->upd_scheme == UPDATE_SCHEME_SNAPSHOT) {
+ int count = 0;
+ struct update_data_list *plist;
+
+ /* Count buffers in free buffer list */
+ list_for_each_entry(plist, &fb_data->upd_buf_free_list, list)
+ count++;
+
+ /* Use count to determine if we have enough
+ * free buffers to handle this update request */
+ if (count + fb_data->max_num_buffers
+ <= fb_data->max_num_updates) {
+ dev_err(fb_data->dev,
+ "No free intermediate buffers available.\n");
+ mutex_unlock(&fb_data->queue_mutex);
+ return -ENOMEM;
+ }
+
+ /* Grab first available buffer and delete from the free list */
+ upd_data_list =
+ list_entry(fb_data->upd_buf_free_list.next,
+ struct update_data_list, list);
+
+ list_del_init(&upd_data_list->list);
+ }
+
+ /*
+ * Create new update data structure, fill it with new update
+ * data and add it to the list of pending updates
+ */
+ upd_desc = kzalloc(sizeof(struct update_desc_list), GFP_KERNEL);
+ if (!upd_desc) {
+ dev_err(fb_data->dev,
+ "Insufficient system memory for update! Aborting.\n");
+ if (fb_data->upd_scheme == UPDATE_SCHEME_SNAPSHOT) {
+ list_add(&upd_data_list->list,
+ &fb_data->upd_buf_free_list);
+ }
+ mutex_unlock(&fb_data->queue_mutex);
+ return -EPERM;
+ }
+ /* Initialize per-update marker list */
+ INIT_LIST_HEAD(&upd_desc->upd_marker_list);
+ upd_desc->upd_data = *upd_data;
+ list_add_tail(&upd_desc->list, &fb_data->upd_pending_list);
+
+ /* If marker specified, associate it with a completion */
+ if (upd_data->update_marker != 0) {
+
+ /* Allocate new update marker and set it up */
+ marker_data = kzalloc(sizeof(struct update_marker_data),
+ GFP_KERNEL);
+ if (!marker_data) {
+ dev_err(fb_data->dev, "No memory for marker!\n");
+ mutex_unlock(&fb_data->queue_mutex);
+ return -ENOMEM;
+ }
+ list_add_tail(&marker_data->upd_list,
+ &upd_desc->upd_marker_list);
+ marker_data->update_marker = upd_data->update_marker;
+ init_completion(&marker_data->update_completion);
+
+ /* Add marker to master marker list */
+ list_add_tail(&marker_data->full_list,
+ &fb_data->full_marker_list);
+ }
+
+ if (fb_data->upd_scheme != UPDATE_SCHEME_SNAPSHOT) {
+ /* Queued update scheme processing */
+
+ mutex_unlock(&fb_data->queue_mutex);
+
+ /* Signal workqueue to handle new update */
+ queue_work(fb_data->spdc_submit_workqueue,
+ &fb_data->spdc_submit_work);
+
+ return 0;
+ }
+
+ /* Set descriptor for current update, delete from pending list */
+ upd_data_list->update_desc = upd_desc;
+ list_del_init(&upd_desc->list);
+
+ mutex_unlock(&fb_data->queue_mutex);
+
+ /*
+ * Hold on to original screen update region, which we
+ * will ultimately use when telling SPDC where to update on panel
+ */
+ screen_upd_region = &upd_desc->upd_data.update_region;
+
+ /* Select from PxP output buffers */
+ upd_data_list->phys_addr =
+ fb_data->phys_addr_updbuf[fb_data->upd_buffer_num];
+ upd_data_list->virt_addr =
+ fb_data->virt_addr_updbuf[fb_data->upd_buffer_num];
+ fb_data->upd_buffer_num++;
+ if (fb_data->upd_buffer_num > fb_data->max_num_buffers-1)
+ fb_data->upd_buffer_num = 0;
+
+ ret = spdc_process_update(upd_data_list, fb_data);
+ if (ret) {
+ mutex_unlock(&fb_data->pxp_mutex);
+ return ret;
+ }
+
+ /* Pass selected waveform mode back to user */
+ upd_data->waveform_mode = upd_desc->upd_data.waveform_mode;
+
+ /* Get rotation-adjusted coordinates */
+ adjust_coordinates(fb_data->spdc_fb_var.xres,
+ fb_data->spdc_fb_var.yres, fb_data->spdc_fb_var.rotate,
+ &upd_desc->upd_data.update_region, NULL);
+
+ /* Grab lock for queue manipulation and update submission */
+ mutex_lock(&fb_data->queue_mutex);
+
+ /*
+ * Is the working buffer idle?
+ * If either the working buffer is busy, or there are no LUTs available,
+ * then we return and let the ISR handle the update later
+ */
+ if (fb_data->cur_update != NULL) {
+
+ /* Add processed Y buffer to update list */
+ list_add_tail(&upd_data_list->list, &fb_data->upd_buf_queue);
+
+ /* Return and allow the update to be submitted by the ISR. */
+ mutex_unlock(&fb_data->queue_mutex);
+ return 0;
+ }
+
+ /* Save current update */
+ fb_data->cur_update = upd_data_list;
+
+ /* program SPDC register and trigger to process buffer*/
+ fb_data->fresh_param.buf_addr.next_buf_phys_addr =
+ upd_data_list->phys_addr + (upd_data_list->update_desc->spdc_offs / 2);
+
+ fb_data->fresh_param.update_region.left = screen_upd_region->left;
+ fb_data->fresh_param.update_region.top = screen_upd_region->top;
+ fb_data->fresh_param.update_region.width = screen_upd_region->width;
+ fb_data->fresh_param.update_region.height = screen_upd_region->height;
+ fb_data->fresh_param.temper = upd_desc->upd_data.temp;
+ fb_data->fresh_param.wave_mode = upd_desc->upd_data.waveform_mode;
+ spdc_submit_update(fb_data);
+
+ mutex_unlock(&fb_data->queue_mutex);
+ return 0;
+}
+EXPORT_SYMBOL(mxc_spdc_fb_send_update);
+
+/*
+ * return 0 : spdc update is update
+ */
+int
+mxc_spdc_fb_wait_update_complete(struct mxcfb_update_marker_data *marker_data,
+ struct fb_info *info)
+{
+ mxc_spdc_t *fb_data = info ?
+ (mxc_spdc_t *)info:g_spdc_fb_data;
+ struct update_marker_data *next_marker;
+ struct update_marker_data *temp;
+ bool marker_found = false;
+ int ret = 0;
+
+ /* 0 is an invalid update_marker value */
+ if (marker_data->update_marker == 0)
+ return -EINVAL;
+
+ /*
+ * Find completion associated with update_marker requested.
+ * Note: If update completed already, marker will have been
+ * cleared, it won't be found, and function will just return.
+ */
+
+ /* Grab queue lock to protect access to marker list */
+ mutex_lock(&fb_data->queue_mutex);
+
+ list_for_each_entry_safe(next_marker, temp,
+ &fb_data->full_marker_list, full_list) {
+ if (next_marker->update_marker == marker_data->update_marker) {
+ dev_dbg(fb_data->dev, "Waiting for marker %d\n",
+ marker_data->update_marker);
+ next_marker->waiting = true;
+ marker_found = true;
+ break;
+ }
+ }
+
+ mutex_unlock(&fb_data->queue_mutex);
+
+ /*
+ * If marker not found, it has either been signalled already
+ * or the update request failed. In either case, just return.
+ */
+ if (!marker_found)
+ return ret;
+
+ ret = wait_for_completion_timeout(&next_marker->update_completion,
+ msecs_to_jiffies(8000));
+ if (!ret) {
+ dev_err(fb_data->dev,
+ "Timed out waiting for update completion\n");
+ return -ETIMEDOUT;
+ }
+
+ /** since SPDC don't support auto collision detect,
+ * there alway returns no collision
+ */
+ marker_data->collision_test = false;
+
+ /* Free update marker object */
+ kfree(next_marker);
+
+ return ret;
+}
+EXPORT_SYMBOL(mxc_spdc_fb_wait_update_complete);
+
+int mxc_spdc_fb_set_pwrdown_delay(u32 pwrdown_delay,
+ struct fb_info *info)
+{
+ mxc_spdc_t *fb_data = info ?
+ (mxc_spdc_t *)info:g_spdc_fb_data;
+
+ fb_data->pwrdown_delay = pwrdown_delay;
+
+ return 0;
+}
+EXPORT_SYMBOL(mxc_spdc_fb_set_pwrdown_delay);
+
+int mxc_spdc_get_pwrdown_delay(struct fb_info *info)
+{
+ mxc_spdc_t *fb_data = info ?
+ (mxc_spdc_t *)info:g_spdc_fb_data;
+
+ return fb_data->pwrdown_delay;
+}
+EXPORT_SYMBOL(mxc_spdc_get_pwrdown_delay);
+
+static int
+mxc_spdc_fb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
+{
+ mxc_spdc_t *fb_data = (mxc_spdc_t *)info;
+ void __user *argp = (void __user *)arg;
+ int ret = -EINVAL;
+
+ dev_dbg(fb_data->dev, "cmd = %08X, arg = %08X\n", cmd, (u32)arg);
+
+ switch (cmd) {
+ case MXCFB_SET_WAVEFORM_MODES:
+ {
+ struct mxcfb_waveform_modes modes;
+ if (!copy_from_user(&modes, argp, sizeof(modes))) {
+ mxc_spdc_fb_set_waveform_modes(&modes, info);
+ ret = 0;
+ }
+ break;
+ }
+ case MXCFB_SET_TEMPERATURE:
+ {
+ int temperature;
+ if (!get_user(temperature, (int32_t __user *) arg))
+ ret = mxc_spdc_fb_set_temperature(temperature,
+ info);
+ break;
+ }
+ case MXCFB_SET_AUTO_UPDATE_MODE:
+ {
+ u32 auto_mode = 0;
+ if (!get_user(auto_mode, (__u32 __user *) arg))
+ ret = mxc_spdc_fb_set_auto_update(auto_mode,
+ info);
+ break;
+ }
+ case MXCFB_SET_UPDATE_SCHEME:
+ {
+ u32 upd_scheme = 0;
+ if (!get_user(upd_scheme, (__u32 __user *) arg))
+ ret = mxc_spdc_fb_set_upd_scheme(upd_scheme,
+ info);
+ break;
+ }
+ case MXCFB_SEND_UPDATE:
+ {
+ struct mxcfb_update_data upd_data;
+ if (!copy_from_user(&upd_data, argp,
+ sizeof(upd_data))) {
+ ret = mxc_spdc_fb_send_update(&upd_data, info);
+ if (ret == 0 && copy_to_user(argp, &upd_data,
+ sizeof(upd_data)))
+ ret = -EFAULT;
+ } else {
+ ret = -EFAULT;
+ }
+
+ break;
+ }
+ case MXCFB_WAIT_FOR_UPDATE_COMPLETE:
+ {
+ struct mxcfb_update_marker_data upd_marker_data;
+ if (!copy_from_user(&upd_marker_data, argp,
+ sizeof(upd_marker_data))) {
+ ret = mxc_spdc_fb_wait_update_complete(
+ &upd_marker_data, info);
+ if (copy_to_user(argp, &upd_marker_data,
+ sizeof(upd_marker_data)))
+ ret = -EFAULT;
+ } else {
+ ret = -EFAULT;
+ }
+
+ break;
+ }
+ case MXCFB_SET_PWRDOWN_DELAY:
+ {
+ int delay = 0;
+ if (!get_user(delay, (__u32 __user *) arg))
+ ret =
+ mxc_spdc_fb_set_pwrdown_delay(delay, info);
+ break;
+ }
+ case MXCFB_GET_PWRDOWN_DELAY:
+ {
+ int pwrdown_delay = mxc_spdc_get_pwrdown_delay(info);
+ if (put_user(pwrdown_delay, (int __user *)arg))
+ ret = -EFAULT;
+ break;
+ }
+ default:
+ dev_err(fb_data->dev, "IOCTL_CMD: not such command\n");
+ return -ENOTTY;
+ }
+
+ return ret;
+}
+
+static void mxc_spdc_fb_update_pages(mxc_spdc_t *fb_data, u16 y1, u16 y2)
+{
+ struct mxcfb_update_data update;
+
+ /* Do partial screen update, Update full horizontal lines */
+ update.update_region.left = 0;
+ update.update_region.width = fb_data->spdc_fb_var.xres;
+ update.update_region.top = y1;
+ update.update_region.height = y2 - y1;
+ update.waveform_mode = WAVEFORM_MODE_AUTO;
+ update.update_mode = UPDATE_MODE_FULL;
+ update.update_marker = 0;
+ update.temp = SPDC_DEFAULT_TEMP;
+ update.flags = 0;
+
+ mxc_spdc_fb_send_update(&update, &fb_data->info);
+}
+
+/* this is called back from the deferred io workqueue */
+static void mxc_spdc_fb_deferred_io(struct fb_info *info,
+ struct list_head *pagelist)
+{
+ mxc_spdc_t *fb_data = (mxc_spdc_t *)info;
+ struct page *page;
+ unsigned long beg, end;
+ int y1, y2, miny, maxy;
+
+ if (fb_data->auto_mode != AUTO_UPDATE_MODE_AUTOMATIC_MODE)
+ return;
+
+ miny = INT_MAX;
+ maxy = 0;
+ list_for_each_entry(page, pagelist, lru) {
+ beg = page->index << PAGE_SHIFT;
+ end = beg + PAGE_SIZE - 1;
+ y1 = beg / info->fix.line_length;
+ y2 = end / info->fix.line_length;
+ if (y2 >= fb_data->spdc_fb_var.yres)
+ y2 = fb_data->spdc_fb_var.yres - 1;
+ if (miny > y1)
+ miny = y1;
+ if (maxy < y2)
+ maxy = y2;
+ }
+
+ mxc_spdc_fb_update_pages(fb_data, miny, maxy);
+}
+
+static int mxc_spdc_fb_blank(int blank, struct fb_info *info)
+{
+ mxc_spdc_t *fb_data = (mxc_spdc_t *)info;
+ int ret = 0;
+
+ dev_dbg(fb_data->dev, "blank = %d\n", blank);
+
+ if (fb_data->blank == blank)
+ return 0;
+
+ fb_data->blank = blank;
+ switch (blank) {
+ case FB_BLANK_POWERDOWN:
+ mxc_spdc_fb_flush_updates(fb_data);
+ /* Wait for powerdown */
+ mutex_lock(&fb_data->power_mutex);
+ if ((fb_data->power_state == POWER_STATE_ON) &&
+ (fb_data->pwrdown_delay == FB_POWERDOWN_DISABLE)) {
+
+ /* Powerdown disabled, so we disable SPDC manually */
+ int count = 0;
+ int sleep_ms = 10;
+
+ mutex_unlock(&fb_data->power_mutex);
+
+ /* If any active updates, wait for them to complete */
+ while (fb_data->updates_active) {
+ /* Timeout after 1 sec */
+ if ((count * sleep_ms) > 1000)
+ break;
+ msleep(sleep_ms);
+ count++;
+ }
+
+ fb_data->powering_down = true;
+ spdc_powerdown(fb_data);
+ } else if (fb_data->power_state != POWER_STATE_OFF) {
+ fb_data->wait_for_powerdown = true;
+ init_completion(&fb_data->powerdown_compl);
+ mutex_unlock(&fb_data->power_mutex);
+ ret =
+ wait_for_completion_timeout(&fb_data->powerdown_compl,
+ msecs_to_jiffies(5000));
+ if (!ret) {
+ dev_err(fb_data->dev,
+ "No powerdown received!\n");
+ return -ETIMEDOUT;
+ }
+ } else
+ mutex_unlock(&fb_data->power_mutex);
+ break;
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_HSYNC_SUSPEND:
+ case FB_BLANK_NORMAL:
+ mxc_spdc_fb_flush_updates(fb_data);
+ break;
+ }
+
+ return ret;
+}
+
+static int mxc_spdc_fb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ mxc_spdc_t *fb_data = info ?
+ (mxc_spdc_t *)info:g_spdc_fb_data;
+ u_int y_bottom;
+
+ dev_dbg(info->device, "%s: var->yoffset %d, info->var.yoffset %d\n",
+ __func__, var->yoffset, info->var.yoffset);
+ /* check if var is valid; also, xpan is not supported */
+ if (!var || (var->xoffset != info->var.xoffset) ||
+ (var->yoffset + var->yres > var->yres_virtual)) {
+ dev_dbg(info->device, "x panning not supported\n");
+ return -EINVAL;
+ }
+
+ if ((fb_data->spdc_fb_var.xoffset == var->xoffset) &&
+ (fb_data->spdc_fb_var.yoffset == var->yoffset))
+ return 0; /* No change, do nothing */
+
+ y_bottom = var->yoffset;
+
+ if (!(var->vmode & FB_VMODE_YWRAP))
+ y_bottom += var->yres;
+
+ if (y_bottom > info->var.yres_virtual)
+ return -EINVAL;
+
+ mutex_lock(&fb_data->queue_mutex);
+
+ fb_data->fb_offset = (var->yoffset * var->xres_virtual + var->xoffset)
+ * (var->bits_per_pixel) / 8;
+
+ fb_data->spdc_fb_var.xoffset = var->xoffset;
+ fb_data->spdc_fb_var.yoffset = var->yoffset;
+
+ if (var->vmode & FB_VMODE_YWRAP)
+ info->var.vmode |= FB_VMODE_YWRAP;
+ else
+ info->var.vmode &= ~FB_VMODE_YWRAP;
+
+ mutex_unlock(&fb_data->queue_mutex);
+
+ return 0;
+}
+
+static struct fb_ops mxc_spdc_fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = mxc_spdc_fb_check_var,
+ .fb_set_par = mxc_spdc_fb_set_par,
+ .fb_setcmap = mxc_spdc_fb_setcmap,
+ .fb_setcolreg = mxc_spdc_fb_setcolreg,
+ .fb_pan_display = mxc_spdc_fb_pan_display,
+ .fb_ioctl = mxc_spdc_fb_ioctl,
+ .fb_mmap = mxc_spdc_fb_mmap,
+ .fb_blank = mxc_spdc_fb_blank,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+};
+
+static struct fb_deferred_io mxc_spdc_fb_defio = {
+ .delay = HZ,
+ .deferred_io = mxc_spdc_fb_deferred_io,
+};
+
+static void spdc_done_work_func(struct work_struct *work)
+{
+ mxc_spdc_t *fb_data =
+ container_of(work, mxc_spdc_t, spdc_done_work.work);
+ spdc_powerdown(fb_data);
+}
+
+static irqreturn_t mxc_spdc_irq_handler(int irq, void *dev_id)
+{
+ mxc_spdc_t *fb_data = (mxc_spdc_t *)dev_id;
+ ulong flags;
+ u32 int_events;
+ irqreturn_t ret = IRQ_NONE;
+
+ spin_lock_irqsave(&fb_data->lock, flags);
+
+ int_events = spdc_get_intr_stat(fb_data);
+ dev_dbg(fb_data->dev, "spdc int:%x\n", int_events);
+
+ if (int_events & SPDC_IRQ_STA_ERR) {
+ ret = IRQ_HANDLED;
+ spdc_intr_stat_clear(fb_data, SPDC_IRQ_STA_ERR);
+ dev_err(fb_data->dev, "Error IRQ\n");
+ return ret;
+ }
+
+ /*
+ * If we just completed one-time panel init, bypass
+ * queue handling, clear interrupt and return
+ */
+ if (fb_data->operation_mode &&
+ (int_events & SPDC_IRQ_STA_FRAME_UPDATE)) {
+ mutex_lock(&fb_data->queue_mutex);
+ fb_data->updates_active = false;
+ complete(&fb_data->update_finish);
+ spdc_intr_stat_clear(fb_data, SPDC_IRQ_STA_FRAME_UPDATE);
+
+ if ((fb_data->operation_mode == SPDC_DEEP_REFRESH) ||
+ fb_data->is_deep_fresh)
+ fb_data->is_deep_fresh = false;
+ else
+ fb_data->operation_mode = SPDC_NO_OPERATION;
+
+ mutex_unlock(&fb_data->queue_mutex);
+
+ return IRQ_HANDLED;
+ }
+
+ /* waveform loading to SPDC from memory */
+ if (int_events & SPDC_IRQ_STA_LUT_DOWNLOAD) {
+ if (is_lut_checksum_ok(fb_data)) {
+ complete(&fb_data->lut_down);
+ spdc_intr_stat_clear(fb_data,
+ SPDC_IRQ_STA_LUT_DOWNLOAD);
+ } else
+ dev_dbg(fb_data->dev, "Lut checksum is err!\n");
+
+ return IRQ_HANDLED;
+ }
+
+ /* SPDC init setting IRQ */
+ if (int_events & SPDC_IRQ_STA_TCON_INIT) {
+ complete(&fb_data->init_finish);
+ spdc_intr_stat_clear(fb_data, SPDC_IRQ_STA_TCON_INIT);
+
+ return IRQ_HANDLED;
+ }
+
+ spin_unlock_irqrestore(&fb_data->lock, flags);
+
+ if (spdc_is_update_finish(fb_data)) {
+ /* clear interrupt status */
+ spdc_intr_stat_clear(fb_data, SPDC_IRQ_STA_FRAME_UPDATE);
+ queue_work(fb_data->spdc_intr_workqueue,
+ &fb_data->spdc_intr_work);
+ }
+
+ return ret;
+}
+
+static void spdc_intr_work_func(struct work_struct *work)
+{
+ mxc_spdc_t *fb_data =
+ container_of(work, mxc_spdc_t, spdc_intr_work);
+ struct mxcfb_rect *next_upd_region;
+ struct update_marker_data *next_marker;
+ struct update_marker_data *temp;
+ struct update_data_list *next_upd, *temp_upd;
+
+ /* Protect access to buffer queues and to update HW */
+ mutex_lock(&fb_data->queue_mutex);
+
+ /* Check to see if all updates have completed */
+ if (list_empty(&fb_data->upd_pending_list) &&
+ is_free_list_full(fb_data) &&
+ (fb_data->cur_update == NULL)) {
+
+ fb_data->updates_active = false;
+
+ if (fb_data->pwrdown_delay != FB_POWERDOWN_DISABLE) {
+ /*
+ * Set variable to prevent overlapping
+ * enable/disable requests
+ */
+ fb_data->powering_down = true;
+
+ /* Schedule task to disable SPDC HW until next update */
+ schedule_delayed_work(&fb_data->spdc_done_work,
+ msecs_to_jiffies(fb_data->pwrdown_delay));
+
+ /* Reset counter to reduce chance of overflow */
+ fb_data->order_cnt = 0;
+ }
+
+ if (fb_data->waiting_for_idle)
+ complete(&fb_data->updates_done);
+ }
+
+ if (mxc_spdc_device_is_busy(fb_data)) {
+ /* Can't submit another update until SPDC is idle */
+ mutex_unlock(&fb_data->queue_mutex);
+ return;
+ }
+
+ /*
+ * Were we waiting on working buffer?
+ * If so, update queues and check for collisions
+ */
+ if (fb_data->cur_update != NULL) {
+ list_for_each_entry_safe(next_marker, temp,
+ &fb_data->cur_update->update_desc->upd_marker_list,
+ upd_list) {
+
+ /* Del from per-update & full list */
+ list_del_init(&next_marker->upd_list);
+ list_del_init(&next_marker->full_list);
+
+ /* Signal completion of update */
+ dev_dbg(fb_data->dev,
+ "Signaling marker %d\n",
+ next_marker->update_marker);
+ if (next_marker->waiting)
+ complete(&next_marker->update_completion);
+ else
+ kfree(next_marker);
+ }
+
+ /* Free marker list and update descriptor */
+ kfree(fb_data->cur_update->update_desc);
+
+ /* Add to free buffer list */
+ list_add_tail(&fb_data->cur_update->list,
+ &fb_data->upd_buf_free_list);
+
+ if (fb_data->submit_upd_sta & SPDC_CONCUR_UPD) {
+ fb_data->upd_preprocess_num = 0;
+ fb_data->submit_upd_sta &= (~SPDC_CONCUR_UPD);
+ }
+
+ /* free ACC update list */
+ list_for_each_entry_safe(next_upd, temp_upd,
+ &fb_data->upd_buf_preprocess_list, list) {
+ next_marker = list_entry(
+ (&next_upd->update_desc->upd_marker_list)->next,
+ struct update_marker_data, upd_list);
+
+ list_del_init(&next_marker->upd_list);
+ list_del_init(&next_marker->full_list);
+
+ /* Signal completion of update */
+ dev_dbg(fb_data->dev,
+ "Signaling marker %d\n",
+ next_marker->update_marker);
+
+ if (next_marker->waiting)
+ complete(&next_marker->update_completion);
+ else
+ kfree(next_marker);
+
+
+ list_del_init(&next_upd->list);
+ /* Add to free buffer list */
+ list_add_tail(&next_upd->list,
+ &fb_data->upd_buf_free_list);
+ kfree(next_upd->update_desc);
+ }
+
+ /* Check to see if all updates have completed */
+ if (list_empty(&fb_data->upd_pending_list) &&
+ is_free_list_full(fb_data)) {
+
+ fb_data->updates_active = false;
+
+ if (fb_data->pwrdown_delay !=
+ FB_POWERDOWN_DISABLE) {
+ /*
+ * Set variable to prevent overlapping
+ * enable/disable requests
+ */
+ fb_data->powering_down = true;
+
+ /* Schedule SPDC disable */
+ schedule_delayed_work(&fb_data->spdc_done_work,
+ msecs_to_jiffies(fb_data->pwrdown_delay));
+
+ /* Reset counter to reduce chance of overflow */
+ fb_data->order_cnt = 0;
+ }
+
+ if (fb_data->waiting_for_idle)
+ complete(&fb_data->updates_done);
+ }
+
+ /* Signal completion if submit workqueue was waiting on WB */
+ if (fb_data->waiting_for_wb) {
+ dev_dbg(fb_data->dev, "free update_res_free\n");
+ complete(&fb_data->update_res_free);
+ fb_data->waiting_for_wb = false;
+ }
+
+ /* Clear current update */
+ fb_data->cur_update = NULL;
+ }
+
+ if (fb_data->upd_scheme != UPDATE_SCHEME_SNAPSHOT) {
+ /* Queued update scheme processing */
+ /* Schedule task to submit collision and pending update */
+ if (!fb_data->powering_down)
+ queue_work(fb_data->spdc_submit_workqueue,
+ &fb_data->spdc_submit_work);
+
+ /* Release buffer queues */
+ mutex_unlock(&fb_data->queue_mutex);
+ return;
+ }
+
+ /* Snapshot update scheme processing */
+ if (list_empty(&fb_data->upd_buf_queue)) {
+ dev_dbg(fb_data->dev, "No pending updates.\n");
+
+ /* No updates pending, so we are done */
+ mutex_unlock(&fb_data->queue_mutex);
+ return;
+ } else {
+ dev_dbg(fb_data->dev, "Found a pending update!\n");
+
+ /* Process next item in update list */
+ fb_data->cur_update =
+ list_entry(fb_data->upd_buf_queue.next,
+ struct update_data_list, list);
+ list_del_init(&fb_data->cur_update->list);
+ }
+
+ /* program SPDC register and trigger to process buffer*/
+ next_upd_region =
+ &fb_data->cur_update->update_desc->upd_data.update_region;
+ fb_data->fresh_param.buf_addr.next_buf_phys_addr =
+ fb_data->cur_update->phys_addr +
+ (fb_data->cur_update->update_desc->spdc_offs / 2);
+
+ fb_data->fresh_param.update_region.left = next_upd_region->left;
+ fb_data->fresh_param.update_region.top = next_upd_region->top;
+ fb_data->fresh_param.update_region.width = next_upd_region->width;
+ fb_data->fresh_param.update_region.height = next_upd_region->height;
+ fb_data->fresh_param.temper =
+ fb_data->cur_update->update_desc->upd_data.temp;
+ fb_data->fresh_param.wave_mode =
+ fb_data->cur_update->update_desc->upd_data.waveform_mode;
+ spdc_submit_update(fb_data);
+
+ mutex_unlock(&fb_data->queue_mutex);
+
+ return;
+}
+
+/*
+ * Sysfs functions
+ */
+static ssize_t show_update(struct device *device,
+ struct device_attribute *attr, char *buf) {
+ struct fb_info *info = dev_get_drvdata(device);
+ mxc_spdc_t *fb_data = (mxc_spdc_t *)info;
+
+ return sprintf(buf, "mode%d\n", fb_data->fresh_param.wave_mode);
+}
+
+static ssize_t store_update(struct device *device,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mxcfb_update_data update;
+ struct fb_info *info = dev_get_drvdata(device);
+ mxc_spdc_t *fb_data = (mxc_spdc_t *)info;
+ if (strncmp(buf, "direct", 6) == 0)
+ update.waveform_mode = fb_data->wv_modes.mode_du;
+ else if (strncmp(buf, "gc16", 4) == 0)
+ update.waveform_mode = fb_data->wv_modes.mode_gc16;
+ else if (strncmp(buf, "gc4", 3) == 0)
+ update.waveform_mode = fb_data->wv_modes.mode_gc4;
+ else if (strncmp(buf, "init", 4) == 0)
+ update.waveform_mode = fb_data->wv_modes.mode_init;
+ else if (strncmp(buf, "gu", 2) == 0)
+ update.waveform_mode = SPDC_WAV_MODE_1;
+ else if (strncmp(buf, "auto", 4) == 0)
+ update.waveform_mode = WAVEFORM_MODE_AUTO;
+
+ /* Now, request full screen update */
+ update.update_region.left = 0;
+ update.update_region.width = fb_data->spdc_fb_var.xres;
+ update.update_region.top = 0;
+ update.update_region.height = fb_data->spdc_fb_var.yres;
+ update.update_mode = UPDATE_MODE_FULL;
+ update.temp = SPDC_DEFAULT_TEMP;
+ update.update_marker = 0;
+ update.flags = 0;
+ dev_dbg(fb_data->dev, "rotation:%d, gray:%d\n",
+ fb_data->spdc_fb_var.rotate, fb_data->spdc_fb_var.grayscale);
+
+ mxc_spdc_fb_send_update(&update, info);
+
+ return count;
+}
+
+static ssize_t fresh_show(struct device *device, struct device_attribute *attr,
+ char *buf)
+{
+ struct fb_info *info = dev_get_drvdata(device);
+ mxc_spdc_t *fb_data = (mxc_spdc_t *)info;
+
+ return sprintf(buf, "%d\n", fb_data->operation_mode);
+}
+
+static ssize_t fresh_store(struct device *device, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *info = dev_get_drvdata(device);
+ mxc_spdc_t *fb_data = (mxc_spdc_t *)info;
+ int ret, operation;
+
+ ret = kstrtoint(buf, 10, &operation);
+ if (ret)
+ return ret;
+
+ if (operation == SPDC_DEEP_REFRESH)
+ fb_data->is_deep_fresh = true;
+ fb_data->operation_mode = operation;
+ if (operation > SPDC_NO_OPERATION &&
+ operation < SPDC_FULL_REFRESH)
+ mxc_operaton_update(fb_data);
+ else
+ mxc_spdc_refresh_display(fb_data);
+
+ return count;
+}
+
+static ssize_t temp_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *info = dev_get_drvdata(device);
+ mxc_spdc_t *fb_data = (mxc_spdc_t *)info;
+ int temp;
+
+ temp = fb_data->fresh_param.temper >> 1;
+ return sprintf(buf, "%d\n", temp);
+}
+
+static ssize_t initset_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *info = dev_get_drvdata(device);
+ mxc_spdc_t *fb_data = (mxc_spdc_t *)info;
+ u32 init_val;
+
+ get_panel_init_set(&fb_data->panel_set, &init_val);
+ return sprintf(buf, "%x\n", init_val);
+}
+
+static ssize_t concurrency_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *info = dev_get_drvdata(device);
+ mxc_spdc_t *fb_data = (mxc_spdc_t *)info;
+ bool temp;
+
+ temp = (fb_data->fresh_param.concur ? 1 : 0);
+ return sprintf(buf, "%d\n", temp);
+}
+
+static ssize_t concurrency_update(struct device *device,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct fb_info *info = dev_get_drvdata(device);
+ mxc_spdc_t *fb_data = (mxc_spdc_t *)info;
+ int ret, concur;
+
+ ret = kstrtoint(buf, 10, &concur);
+ if (ret)
+ return ret;
+
+ if (fb_data->fresh_param.concur != concur) {
+ fb_data->fresh_param.concur = concur;
+ spdc_set_update_concurrency(fb_data);
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(store_update, 0644, show_update, store_update);
+static DEVICE_ATTR(fresh, 0644, fresh_show, fresh_store);
+static DEVICE_ATTR(temp, 0644, temp_show, NULL);
+static DEVICE_ATTR(initset, 0644, initset_show, NULL);
+static DEVICE_ATTR(concurrency, 0644, concurrency_show, concurrency_update);
+
+static struct attribute *spdc_attributes[] = {
+ &dev_attr_store_update.attr,
+ &dev_attr_fresh.attr,
+ &dev_attr_temp.attr,
+ &dev_attr_initset.attr,
+ &dev_attr_concurrency.attr,
+ NULL
+};
+
+static const struct attribute_group spdc_attr_group = {
+ .attrs = spdc_attributes,
+};
+
+static int __devinit mxc_spdc_fb_probe(struct platform_device *pdev)
+{
+ struct fb_info *info;
+ mxc_spdc_t *fb_data;
+ struct resource *res, *mem;
+ struct fb_videomode *vmode;
+ int xres_virt, yres_virt, buf_size;
+ int xres_virt_rot, yres_virt_rot, pix_size_rot;
+ struct fb_var_screeninfo *var_info;
+ struct fb_fix_screeninfo *fix_info;
+ struct pxp_config_data *pxp_conf;
+ struct pxp_proc_data *proc_data;
+ struct scatterlist *sg;
+ struct update_data_list *upd_list;
+ struct update_data_list *plist, *temp_list;
+ char *options, *opt;
+ char *panel_str = NULL;
+ char name[] = "mxcspdcfb";
+ unsigned long x_mem_size = 0;
+ u32 i, cmap_size;
+ int ret = 0;
+
+ fb_data = (mxc_spdc_t *)framebuffer_alloc(sizeof(mxc_spdc_t),
+ &pdev->dev);
+ if (!fb_data) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ info = &fb_data->info;
+ fb_data->dev = &pdev->dev;
+ platform_set_drvdata(pdev, fb_data);
+
+ fb_data->pdata = pdev->dev.platform_data;
+ if ((fb_data->pdata == NULL) || (fb_data->pdata->num_modes < 1)
+ || (fb_data->pdata->spdc_mode == NULL)
+ || (fb_data->pdata->spdc_mode->vmode == NULL)) {
+ ret = -EINVAL;
+ goto dealloc_fb;
+ }
+
+ /*get panel para from command*/
+ if (fb_get_options(name, &options)) {
+ ret = -ENODEV;
+ goto dealloc_fb;
+ }
+ if (options)
+ while ((opt = strsep(&options, ",")) != NULL) {
+ if (!*opt)
+ continue;
+ if (!strncmp(opt, "bpp=", 4)) {
+ if (kstrtoint(opt + 4, 0,
+ &fb_data->default_bpp) < 0)
+ fb_data->default_bpp = 0;
+ } else if (!strncmp(opt, "x_mem=", 6))
+ x_mem_size = memparse(opt + 6, NULL);
+ else
+ panel_str = opt;
+ }
+
+ if (!fb_data->default_bpp)
+ fb_data->default_bpp = SPDC_DEFAULT_BPP;
+
+ /* Set default (first defined mode) for a match */
+ mxc_spdc_find_match_mode(fb_data);
+
+ if (panel_str)
+ for (i = 0; i < fb_data->pdata->num_modes; i++)
+ if (!strcmp(fb_data->pdata->spdc_mode[i].vmode->name,
+ panel_str)) {
+ fb_data->cur_mode =
+ &fb_data->pdata->spdc_mode[i];
+ break;
+ }
+ vmode = fb_data->cur_mode->vmode;
+
+ /* Allocate color map for the FB */
+ cmap_size = 256;
+ ret = fb_alloc_cmap(&info->cmap, cmap_size, 0);
+ if (ret)
+ goto dealloc_fb;
+
+ /*
+ * GPU alignment restrictions dictate framebuffer parameters:
+ * - 32-byte alignment for buffer width
+ * - 128-byte alignment for buffer height
+ * => 4K buffer alignment for buffer start
+ */
+ xres_virt = ALIGN(vmode->xres, 32);
+ yres_virt = ALIGN(vmode->yres, 128);
+ fb_data->max_pix_size = PAGE_ALIGN(xres_virt * yres_virt);
+
+ /*
+ * Have to check to see if aligned buffer size when rotated
+ * is bigger than when not rotated, and use the max
+ */
+ xres_virt_rot = ALIGN(vmode->yres, 32);
+ yres_virt_rot = ALIGN(vmode->xres, 128);
+ pix_size_rot = PAGE_ALIGN(xres_virt_rot * yres_virt_rot);
+ fb_data->max_pix_size = (fb_data->max_pix_size > pix_size_rot) ?
+ fb_data->max_pix_size : pix_size_rot;
+ buf_size = fb_data->max_pix_size * fb_data->default_bpp/8;
+
+ /* Compute the number of screens needed based on X memory requested */
+ if (x_mem_size > 0) {
+ fb_data->num_screens = DIV_ROUND_UP(x_mem_size, buf_size);
+ if (fb_data->num_screens < NUM_SCREENS_MIN)
+ fb_data->num_screens = NUM_SCREENS_MIN;
+ else if (buf_size * fb_data->num_screens > SZ_16M)
+ fb_data->num_screens = SZ_16M / buf_size;
+ } else
+ fb_data->num_screens = NUM_SCREENS_MIN;
+
+ fb_data->map_size = buf_size * fb_data->num_screens;
+ dev_dbg(&pdev->dev, "memory allocate: %d\n", fb_data->map_size);
+
+ /* get IO memory*/
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "failed to get memory register\n");
+ ret = -ENXIO;
+ goto release_cmap;
+ }
+
+ mem = request_mem_region(res->start, resource_size(res), pdev->name);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "failed to get memory region\n");
+ ret = -ENOENT;
+ goto release_cmap;
+ }
+
+ fb_data->hwp = ioremap(res->start, SZ_4K);
+ if (fb_data->hwp == NULL) {
+ dev_err(&pdev->dev, "ioremap registers failed\n");
+ ret = -ENOENT;
+ goto release_mem;
+ }
+
+ /* Allocate FB memory */
+ fb_data->virt_start = dma_alloc_writecombine(&pdev->dev,
+ fb_data->map_size, &fb_data->phys_start, GFP_DMA);
+ if (fb_data->virt_start == NULL) {
+ dev_err(&pdev->dev, "probe err - dma_alloc for framebuffer\n");
+ ret = -ENOMEM;
+ goto release_hwp;
+ }
+
+ var_info = &info->var;
+ var_info->activate = FB_ACTIVATE_TEST;
+ var_info->bits_per_pixel = fb_data->default_bpp;
+ var_info->xres = vmode->xres;
+ var_info->yres = vmode->yres;
+ var_info->xres_virtual = xres_virt;
+ var_info->yres_virtual = yres_virt * fb_data->num_screens;
+ var_info->pixclock = vmode->pixclock;
+ var_info->left_margin = vmode->left_margin;
+ var_info->right_margin = vmode->right_margin;
+ var_info->upper_margin = vmode->upper_margin;
+ var_info->lower_margin = vmode->lower_margin;
+ var_info->hsync_len = vmode->hsync_len;
+ var_info->vsync_len = vmode->vsync_len;
+ var_info->vmode = FB_VMODE_NONINTERLACED;
+
+ switch (fb_data->default_bpp) {
+ case 32:
+ case 24:
+ var_info->red.offset = 16;
+ var_info->red.length = 8;
+ var_info->green.offset = 8;
+ var_info->green.length = 8;
+ var_info->blue.offset = 0;
+ var_info->blue.length = 8;
+ break;
+ case 16:
+ var_info->red.offset = 11;
+ var_info->red.length = 5;
+ var_info->green.offset = 5;
+ var_info->green.length = 6;
+ var_info->blue.offset = 0;
+ var_info->blue.length = 5;
+ break;
+ case 8:
+ var_info->grayscale = GRAYSCALE_8BIT;
+
+ var_info->red.length = 8;
+ var_info->red.offset = 0;
+ var_info->red.msb_right = 0;
+ var_info->green.length = 8;
+ var_info->green.offset = 0;
+ var_info->green.msb_right = 0;
+ var_info->blue.length = 8;
+ var_info->blue.offset = 0;
+ var_info->blue.msb_right = 0;
+ break;
+ case 4:
+ var_info->grayscale = GRAYSCALE_4BIT;
+
+ var_info->red.length = 4;
+ var_info->red.offset = 0;
+ var_info->red.msb_right = 0;
+ var_info->green.length = 4;
+ var_info->green.offset = 0;
+ var_info->green.msb_right = 0;
+ var_info->blue.length = 4;
+ var_info->blue.offset = 0;
+ var_info->blue.msb_right = 0;
+ break;
+ default:
+ dev_err(&pdev->dev, "unsupported bit-width:%d\n",
+ fb_data->default_bpp);
+ ret = -EINVAL;
+ goto release_dma_fb;
+ }
+
+ fix_info = &info->fix;
+ strcpy(fix_info->id, SPDC_DRIVER_NAME);
+ fix_info->type = FB_TYPE_PACKED_PIXELS;
+ fix_info->visual = FB_VISUAL_TRUECOLOR;
+ fix_info->xpanstep = 0;
+ fix_info->ypanstep = 0;
+ fix_info->ywrapstep = 0;
+ fix_info->accel = FB_ACCEL_NONE;
+ fix_info->smem_start = fb_data->phys_start;
+ fix_info->smem_len = fb_data->map_size;
+ fix_info->ypanstep = 0;
+
+ /* Set up FB info */
+ fb_data->native_width = vmode->xres;
+ fb_data->native_height = vmode->yres;
+ info->screen_base = fb_data->virt_start;
+ info->screen_size = info->fix.smem_len;
+ info->fbops = &mxc_spdc_fb_ops;
+ info->var.activate = FB_ACTIVATE_NOW;
+ info->pseudo_palette = fb_data->pseudo_palette;
+ info->flags = FBINFO_FLAG_DEFAULT;
+
+ mxc_spdc_fb_set_fix(info);
+
+ /* use the same AXI and PIX clock source */
+ fb_data->spdc_clk_axi = clk_get(fb_data->dev, "epdc_axi");
+ if (IS_ERR(fb_data->spdc_clk_axi)) {
+ dev_err(&pdev->dev, "Unable to get AXI clk."
+ "err = 0x%x\n", (int)fb_data->spdc_clk_axi);
+ ret = -ENODEV;
+ goto release_dma_fb;
+ }
+ fb_data->spdc_clk_pix = clk_get(fb_data->dev, "epdc_pix");
+ if (IS_ERR(fb_data->spdc_clk_pix)) {
+ dev_err(&pdev->dev, "Unable to get pix clk."
+ "err = 0x%x\n", (int)fb_data->spdc_clk_pix);
+ ret = -ENODEV;
+ goto release_dma_fb;
+ }
+
+ /*
+ * Initialize update list and allocate buffer.
+ */
+ INIT_LIST_HEAD(&fb_data->upd_pending_list);
+ INIT_LIST_HEAD(&fb_data->upd_buf_queue);
+ INIT_LIST_HEAD(&fb_data->upd_buf_free_list);
+ INIT_LIST_HEAD(&fb_data->upd_buf_preprocess_list);
+ INIT_LIST_HEAD(&fb_data->full_marker_list);
+
+ fb_data->max_num_updates = SPDC_MAX_NUM_UPDATES;
+ fb_data->max_num_buffers = SPDC_MAX_NUM_BUFFERS;
+
+ /* Allocate free buffers */
+ for (i = 0; i < fb_data->max_num_updates; i++) {
+ upd_list = kzalloc(sizeof(*upd_list), GFP_KERNEL);
+ if (upd_list == NULL) {
+ ret = -ENOMEM;
+ goto release_dma_fb;
+ }
+
+ /* Add newly allocated buffer to free list */
+ list_add(&upd_list->list, &fb_data->upd_buf_free_list);
+ }
+
+ /* Allocate PXP output buffer */
+ fb_data->virt_addr_updbuf =
+ kzalloc(sizeof(void *) * fb_data->max_num_buffers, GFP_KERNEL);
+ fb_data->phys_addr_updbuf =
+ kzalloc(sizeof(dma_addr_t) * fb_data->max_num_buffers,
+ GFP_KERNEL);
+ for (i = 0; i < fb_data->max_num_buffers; i++) {
+ /*
+ * Allocate memory for PxP output buffer.
+ * Output raw data is Y4 format.
+ * Each update buffer is 1/2 byte per pixel, and can
+ * be as big as the full-screen frame buffer
+ */
+ fb_data->virt_addr_updbuf[i] =
+ dma_alloc_coherent(fb_data->info.device,
+ fb_data->max_pix_size / 2,
+ &fb_data->phys_addr_updbuf[i], GFP_DMA);
+ if (fb_data->virt_addr_updbuf[i] == NULL) {
+ ret = -ENOMEM;
+ goto release_freebuf_lists;
+ }
+ }
+ /* Counter indicating which update buffer should be used next. */
+ fb_data->upd_buffer_num = 0;
+
+ /* Allocate memory for partical process region buffer.
+ * Output raw data is Y4 format.
+ */
+ fb_data->virt_addr_copybuf =
+ dma_alloc_coherent(fb_data->info.device,
+ fb_data->max_pix_size / 2,
+ &fb_data->phys_addr_copybuf, GFP_DMA);
+ if (fb_data->virt_addr_copybuf == NULL) {
+ ret = -ENOMEM;
+ goto release_output_buf;
+ }
+
+ /* Allocate next & current & privious & count & lut buffers.
+ * next buffer size is Y4 raw data
+ */
+ fb_data->next_buf_size =
+ (fb_data->map_size / fb_data->num_screens) >> 1;
+ fb_data->virt_next_buf =
+ dma_alloc_coherent(&pdev->dev, fb_data->next_buf_size,
+ &fb_data->phy_next_buf, GFP_DMA);
+ if (fb_data->virt_next_buf == NULL) {
+ dev_err(&pdev->dev, "Can't allocate mem for next buf!\n");
+ ret = -ENOMEM;
+ goto release_copy_buf;
+ }
+
+ fb_data->current_buf_size =
+ (fb_data->map_size / fb_data->num_screens) >> 1;
+ fb_data->virt_current_buf =
+ dma_alloc_coherent(&pdev->dev, fb_data->current_buf_size,
+ &fb_data->phy_current_buf, GFP_DMA);
+ if (fb_data->virt_current_buf == NULL) {
+ dev_err(&pdev->dev, "Can't allocate mem for current buf!\n");
+ ret = -ENOMEM;
+ goto release_next_buf;
+ }
+
+ fb_data->pre_buf_size =
+ (fb_data->map_size / fb_data->num_screens) >> 1;
+ fb_data->virt_pre_buf =
+ dma_alloc_coherent(&pdev->dev, fb_data->pre_buf_size,
+ &fb_data->phy_pre_buf, GFP_DMA);
+ if (fb_data->virt_pre_buf == NULL) {
+ dev_err(&pdev->dev, "Can't allocate mem for current buf!\n");
+ ret = -ENOMEM;
+ goto release_current_buf;
+ }
+
+ fb_data->cnt_buf_size = info->var.xres * info->var.yres;
+ fb_data->virt_cnt_buf =
+ dma_alloc_coherent(&pdev->dev, fb_data->cnt_buf_size,
+ &fb_data->phy_cnt_buf, GFP_DMA);
+ if (fb_data->virt_cnt_buf == NULL) {
+ dev_err(&pdev->dev, "Can't allocate mem for current buf!\n");
+ ret = -ENOMEM;
+ goto release_pre_buf;
+ }
+
+ fb_data->lut_buf_size = SZ_1M;
+ fb_data->virt_lut_buf =
+ dma_alloc_coherent(&pdev->dev, fb_data->lut_buf_size,
+ &fb_data->phy_lut_buf, GFP_DMA);
+ if (fb_data->virt_lut_buf == NULL) {
+ dev_err(&pdev->dev, "Can't allocate mem for current buf!\n");
+ ret = -ENOMEM;
+ goto release_cnt_buf;
+ }
+
+ /* Initialize SPDC pins */
+ if (fb_data->pdata->get_pins)
+ fb_data->pdata->get_pins();
+
+ fb_data->hw_ready = false;
+ fb_data->hw_initializing = false;
+
+ /*
+ * Set default waveform mode values.
+ * Should be overwritten via ioctl.
+ */
+ fb_data->wv_modes.mode_init = SPDC_WAV_MODE_DEFAULT;
+ fb_data->wv_modes.mode_du = SPDC_WAV_MODE_2;
+ fb_data->wv_modes.mode_gc4 = SPDC_WAV_MODE_2;
+ fb_data->wv_modes.mode_gc8 = SPDC_WAV_MODE_1;
+ fb_data->wv_modes.mode_gc16 = SPDC_WAV_MODE_1;
+ fb_data->wv_modes.mode_gc32 = SPDC_WAV_MODE_1;
+
+ fb_data->auto_mode = AUTO_UPDATE_MODE_REGION_MODE;
+ fb_data->upd_scheme = UPDATE_SCHEME_QUEUE_AND_MERGE;
+ fb_data->spdc_fb_var = *var_info;
+ fb_data->fb_offset = 0;
+
+ fb_data->blank = FB_BLANK_UNBLANK;
+ fb_data->powering_down = false;
+ fb_data->power_state = POWER_STATE_OFF;
+ fb_data->pwrdown_delay = 0;
+ fb_data->cur_update = NULL;
+ fb_data->waiting_for_idle = false;
+ fb_data->order_cnt = 0;
+ fb_data->waiting_for_wb = false;
+ fb_data->wait_for_powerdown = false;
+ fb_data->updates_active = false;
+
+ spin_lock_init(&fb_data->lock);
+ mutex_init(&fb_data->queue_mutex);
+ mutex_init(&fb_data->pxp_mutex);
+ mutex_init(&fb_data->power_mutex);
+ init_completion(&fb_data->lut_down);
+ init_completion(&fb_data->init_finish);
+ init_completion(&fb_data->update_finish);
+ INIT_DELAYED_WORK(&fb_data->spdc_done_work, spdc_done_work_func);
+
+ fb_data->spdc_submit_workqueue = alloc_workqueue("SPDC Submit",
+ WQ_MEM_RECLAIM | WQ_HIGHPRI |
+ WQ_CPU_INTENSIVE | WQ_UNBOUND, 1);
+ INIT_WORK(&fb_data->spdc_submit_work, spdc_submit_work_func);
+ fb_data->spdc_intr_workqueue = alloc_workqueue("SPDC Interrupt",
+ WQ_MEM_RECLAIM | WQ_HIGHPRI |
+ WQ_CPU_INTENSIVE | WQ_UNBOUND, 1);
+ INIT_WORK(&fb_data->spdc_intr_work, spdc_intr_work_func);
+
+ /* Retrieve spdc IRQ num */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "cannot get IRQ resource\n");
+ ret = -ENODEV;
+ goto release_lut_buf;
+ }
+ fb_data->spdc_irq = res->start;
+ ret = request_irq(fb_data->spdc_irq, mxc_spdc_irq_handler, 0,
+ "fb_dma", fb_data);
+ if (ret) {
+ dev_err(&pdev->dev, "request_irq (%d) failed with error %d\n",
+ fb_data->spdc_irq, ret);
+ ret = -ENODEV;
+ goto release_lut_buf;
+ }
+
+ /* define deferred io */
+ info->fbdefio = &mxc_spdc_fb_defio;
+#ifdef CONFIG_FB_MXC_SIPIX_AUTO_UPDATE_MODE
+ fb_deferred_io_init(info);
+#endif
+
+ /* get pmic regulators */
+ fb_data->display_regulator = regulator_get(NULL, "DISPLAY");
+ if (IS_ERR(fb_data->display_regulator)) {
+ dev_err(&pdev->dev, "Unable to get display PMIC regulator."
+ "err = 0x%x\n", (int)fb_data->display_regulator);
+ ret = -ENODEV;
+ goto release_irq;
+ }
+ fb_data->vcom_regulator = regulator_get(NULL, "VCOM");
+ if (IS_ERR(fb_data->vcom_regulator)) {
+ regulator_put(fb_data->display_regulator);
+ dev_err(&pdev->dev, "Unable to get VCOM regulator."
+ "err = 0x%x\n", (int)fb_data->vcom_regulator);
+ ret = -ENODEV;
+ goto release_regulator1;
+ }
+ fb_data->v3p3_regulator = regulator_get(NULL, "V3P3");
+ if (IS_ERR(fb_data->v3p3_regulator)) {
+ regulator_put(fb_data->vcom_regulator);
+ regulator_put(fb_data->display_regulator);
+ dev_err(&pdev->dev, "Unable to get V3P3 regulator."
+ "err = 0x%x\n", (int)fb_data->vcom_regulator);
+ ret = -ENODEV;
+ goto release_regulator2;
+ }
+
+ /*
+ * Fill out PxP config data structure based on FB info and
+ * processing tasks required
+ */
+ pxp_conf = &fb_data->pxp_conf;
+ proc_data = &pxp_conf->proc_data;
+
+ /* Initialize non-channel-specific PxP parameters */
+ proc_data->drect.left = proc_data->srect.left = 0;
+ proc_data->drect.top = proc_data->srect.top = 0;
+ proc_data->drect.width = fb_data->info.var.xres;
+ proc_data->srect.width = fb_data->info.var.xres;
+ proc_data->drect.height = fb_data->info.var.yres;
+ proc_data->srect.height = fb_data->info.var.yres;
+ proc_data->scaling = 0;
+ proc_data->hflip = 0;
+ proc_data->vflip = 0;
+ proc_data->rotate = 0;
+ proc_data->bgcolor = 0;
+ proc_data->overlay_state = 0;
+ proc_data->lut_transform = PXP_LUT_NONE;
+ proc_data->lut_map = NULL;
+
+ /*
+ * We initially configure PxP for RGB->YUV conversion,
+ * and only write out Y component of the result.
+ */
+
+ /*
+ * Initialize S0 channel parameters
+ * Parameters should match FB format/width/height
+ */
+ pxp_conf->s0_param.pixel_fmt = PXP_PIX_FMT_RGB565;
+ pxp_conf->s0_param.width = fb_data->info.var.xres_virtual;
+ pxp_conf->s0_param.height = fb_data->info.var.yres;
+ pxp_conf->s0_param.color_key = -1;
+ pxp_conf->s0_param.color_key_enable = false;
+
+ /*
+ * Initialize OL0 channel parameters
+ * No overlay will be used for PxP operation
+ */
+ for (i = 0; i < 8; i++) {
+ pxp_conf->ol_param[i].combine_enable = false;
+ pxp_conf->ol_param[i].width = 0;
+ pxp_conf->ol_param[i].height = 0;
+ pxp_conf->ol_param[i].pixel_fmt = PXP_PIX_FMT_RGB565;
+ pxp_conf->ol_param[i].color_key_enable = false;
+ pxp_conf->ol_param[i].color_key = -1;
+ pxp_conf->ol_param[i].global_alpha_enable = false;
+ pxp_conf->ol_param[i].global_alpha = 0;
+ pxp_conf->ol_param[i].local_alpha_enable = false;
+ }
+
+ /*
+ * Initialize Output channel parameters
+ * Output is Y-only greyscale
+ * Output width/height will vary based on update region size
+ */
+ pxp_conf->out_param.width = fb_data->info.var.xres;
+ pxp_conf->out_param.height = fb_data->info.var.yres;
+ pxp_conf->out_param.pixel_fmt = PXP_PIX_FMT_GY04;
+ pxp_conf->out_param.stride = pxp_conf->out_param.width;
+
+ /* Initialize color map for conversion of 8-bit gray pixels */
+ fb_data->pxp_conf.proc_data.lut_map = kmalloc(256, GFP_KERNEL);
+ if (fb_data->pxp_conf.proc_data.lut_map == NULL) {
+ dev_err(&pdev->dev, "Can't allocate mem for lut map!\n");
+ ret = -ENOMEM;
+ goto release_regulator3;
+ }
+ for (i = 0; i < 256; i++)
+ fb_data->pxp_conf.proc_data.lut_map[i] = i;
+
+ fb_data->pxp_conf.proc_data.lut_map_updated = true;
+
+ /*
+ * Ensure this is set to NULL here...we will initialize pxp_chan
+ * later in our thread.
+ */
+ fb_data->pxp_chan = NULL;
+
+ /* Initialize Scatter-gather list containing 2 buffer addresses. */
+ sg = fb_data->sg;
+ sg_init_table(sg, 2);
+
+ /*
+ * For use in PxP transfers:
+ * sg[0] holds the FB buffer pointer
+ * sg[1] holds the Output buffer pointer (configured before TX request)
+ */
+ sg_dma_address(&sg[0]) = info->fix.smem_start;
+ sg_set_page(&sg[0], virt_to_page(info->screen_base),
+ info->fix.smem_len, offset_in_page(info->screen_base));
+
+ /* Register FB */
+ ret = register_framebuffer(info);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "register framebuffer failed\n");
+ goto release_lutmap;
+ }
+
+ ret = sysfs_create_group(&info->device->kobj, &spdc_attr_group);
+ if (ret)
+ dev_err(&pdev->dev, "Unable to create file from fb_attrs\n");
+
+ /* use for spdc test */
+ g_spdc_fb_data = fb_data;
+
+ /* hw init */
+ spdc_fb_dev_init(fb_data);
+
+ /*detect spdc epd disp & tcon version*/
+ get_spdc_version(fb_data);
+
+ goto out;
+
+release_lutmap:
+ kfree(fb_data->pxp_conf.proc_data.lut_map);
+release_regulator3:
+ regulator_put(fb_data->v3p3_regulator);
+release_regulator2:
+ regulator_put(fb_data->vcom_regulator);
+release_regulator1:
+ regulator_put(fb_data->display_regulator);
+release_irq:
+ free_irq(fb_data->spdc_irq, fb_data);
+release_lut_buf:
+ dma_free_coherent(&pdev->dev, fb_data->lut_buf_size,
+ fb_data->virt_lut_buf, fb_data->phy_lut_buf);
+release_cnt_buf:
+ dma_free_coherent(&pdev->dev, fb_data->cnt_buf_size,
+ fb_data->virt_cnt_buf, fb_data->phy_cnt_buf);
+release_pre_buf:
+ dma_free_coherent(&pdev->dev, fb_data->pre_buf_size,
+ fb_data->virt_pre_buf, fb_data->phy_pre_buf);
+release_current_buf:
+ dma_free_coherent(&pdev->dev, fb_data->current_buf_size,
+ fb_data->virt_current_buf, fb_data->phy_current_buf);
+release_next_buf:
+ dma_free_coherent(&pdev->dev, fb_data->next_buf_size,
+ fb_data->virt_next_buf, fb_data->phy_next_buf);
+release_copy_buf:
+ dma_free_writecombine(&pdev->dev, fb_data->max_pix_size / 2,
+ fb_data->virt_addr_copybuf, fb_data->phys_addr_copybuf);
+release_output_buf:
+ for (i = 0; i < fb_data->max_num_buffers; i++)
+ if (fb_data->virt_addr_updbuf[i] != NULL)
+ dma_free_writecombine(&pdev->dev,
+ fb_data->max_pix_size / 2, fb_data->virt_addr_updbuf[i],
+ fb_data->phys_addr_updbuf[i]);
+ if (fb_data->virt_addr_updbuf != NULL)
+ kfree(fb_data->virt_addr_updbuf);
+ if (fb_data->phys_addr_updbuf != NULL)
+ kfree(fb_data->phys_addr_updbuf);
+release_freebuf_lists:
+ list_for_each_entry_safe(plist, temp_list, &fb_data->upd_buf_free_list,
+ list) {
+ list_del(&plist->list);
+ kfree(plist);
+ }
+release_dma_fb:
+ dma_free_writecombine(&pdev->dev,
+ fb_data->map_size, fb_data->virt_start, fb_data->phys_start);
+release_hwp:
+ iounmap(fb_data->hwp);
+release_mem:
+ release_resource(mem);
+release_cmap:
+ fb_dealloc_cmap(&info->cmap);
+dealloc_fb:
+ framebuffer_release(info);
+out:
+ return ret;
+}
+
+static int mxc_spdc_fb_remove(struct platform_device *pdev)
+{
+ struct update_data_list *plist, *temp_list;
+ mxc_spdc_t *fb_data = platform_get_drvdata(pdev);
+ struct fb_info *info = &fb_data->info;
+ int i;
+
+ mxc_spdc_fb_blank(FB_BLANK_POWERDOWN, &fb_data->info);
+
+ flush_workqueue(fb_data->spdc_submit_workqueue);
+ destroy_workqueue(fb_data->spdc_submit_workqueue);
+
+ regulator_put(fb_data->display_regulator);
+ regulator_put(fb_data->vcom_regulator);
+ regulator_put(fb_data->v3p3_regulator);
+
+ for (i = 0; i < fb_data->max_num_buffers; i++)
+ if (fb_data->virt_addr_updbuf[i] != NULL)
+ dma_free_writecombine(&pdev->dev,
+ fb_data->max_pix_size,
+ fb_data->virt_addr_updbuf[i],
+ fb_data->phys_addr_updbuf[i]);
+ if (fb_data->virt_addr_updbuf != NULL)
+ kfree(fb_data->virt_addr_updbuf);
+ if (fb_data->phys_addr_updbuf != NULL)
+ kfree(fb_data->phys_addr_updbuf);
+
+ /* free output temporary buffer */
+ dma_free_writecombine(&pdev->dev, fb_data->max_pix_size / 2,
+ fb_data->virt_addr_copybuf, fb_data->phys_addr_copybuf);
+
+ list_for_each_entry_safe(plist, temp_list,
+ &fb_data->upd_buf_free_list,
+ list) {
+ list_del(&plist->list);
+ kfree(plist);
+ }
+
+#if defined(CONFIG_FB_MXC_SIPIX_AUTO_UPDATE_MODE)
+ fb_deferred_io_cleanup(&fb_data->info);
+#endif
+
+ /* free frame buffer */
+ dma_free_writecombine(&pdev->dev, fb_data->map_size,
+ info->screen_base, fb_data->phys_start);
+
+ /* free SPDC hw allocate buffer */
+ dma_free_coherent(&pdev->dev, fb_data->next_buf_size,
+ fb_data->virt_next_buf, fb_data->phy_next_buf);
+ dma_free_coherent(&pdev->dev, fb_data->current_buf_size,
+ fb_data->virt_current_buf, fb_data->phy_current_buf);
+ dma_free_coherent(&pdev->dev, fb_data->pre_buf_size,
+ fb_data->virt_pre_buf, fb_data->phy_pre_buf);
+ dma_free_coherent(&pdev->dev, fb_data->cnt_buf_size,
+ fb_data->virt_cnt_buf, fb_data->phy_cnt_buf);
+ dma_free_coherent(&pdev->dev, fb_data->lut_buf_size,
+ fb_data->virt_lut_buf, fb_data->phy_lut_buf);
+
+ sysfs_remove_group(&info->device->kobj, &spdc_attr_group);
+ unregister_framebuffer(info);
+
+ if (fb_data->pdata->put_pins)
+ fb_data->pdata->put_pins();
+
+ free_irq(fb_data->spdc_irq, fb_data);
+ iounmap(fb_data->hwp);
+
+ fb_dealloc_cmap(&info->cmap);
+
+ framebuffer_release(info);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int mxc_spdc_fb_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ mxc_spdc_t *data = platform_get_drvdata(pdev);
+ int ret;
+
+ data->pwrdown_delay = FB_POWERDOWN_DISABLE;
+ ret = mxc_spdc_fb_blank(FB_BLANK_POWERDOWN, &data->info);
+
+ return ret;
+}
+
+static int mxc_spdc_fb_resume(struct platform_device *pdev)
+{
+ mxc_spdc_t *data = platform_get_drvdata(pdev);
+
+ mxc_spdc_fb_blank(FB_BLANK_UNBLANK, &data->info);
+ spdc_init_sequence(data);
+
+ return 0;
+}
+#else
+#define mxc_spdc_fb_suspend NULL
+#define mxc_spdc_fb_resume NULL
+#endif
+
+static struct platform_driver mxc_spdc_fb_driver = {
+ .probe = mxc_spdc_fb_probe,
+ .remove = mxc_spdc_fb_remove,
+ .suspend = mxc_spdc_fb_suspend,
+ .resume = mxc_spdc_fb_resume,
+ .driver = {
+ .name = SPDC_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init mxc_spdc_fb_init(void)
+{
+ return platform_driver_register(&mxc_spdc_fb_driver);
+}
+late_initcall(mxc_spdc_fb_init);
+
+static void __exit mxc_spdc_fb_exit(void)
+{
+ platform_driver_unregister(&mxc_spdc_fb_driver);
+}
+module_exit(mxc_spdc_fb_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC SPDC framebuffer driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("fb");
diff --git a/drivers/video/mxc/mxc_spdc_fb.h b/drivers/video/mxc/mxc_spdc_fb.h
new file mode 100644
index 00000000..34373788
--- /dev/null
+++ b/drivers/video/mxc/mxc_spdc_fb.h
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2012 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef __MXC_SPDC_FB_H__
+#define __MXC_SPDC_FB_H__
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/dmaengine.h>
+#include <linux/pxp_dma.h>
+#include <linux/mxcfb.h>
+#include <linux/list.h>
+#include <linux/uaccess.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/regulator/driver.h>
+#include <linux/dmaengine.h>
+#include <linux/gpio.h>
+#include <linux/kthread.h>
+#include <mach/epdc.h>
+#include <mach/dma.h>
+#include <linux/platform_device.h>
+
+#include <linux/fsl_devices.h>
+#include <linux/mxcfb.h>
+
+/*************************************
+*Register addresses
+*************************************/
+#define SPDC_DISP_TRIGGER (0x000)
+#define SPDC_UPDATA_X_Y (0x004)
+#define SPDC_UPDATE_W_H (0x008)
+#define SPDC_LUT_PARA_UPDATE (0x00C)
+#define SPDC_OPERATE (0x010)
+#define SPDC_PANEL_INIT_SET (0x014)
+#define SPDC_TEMP_INFO (0x018)
+#define SPDC_NEXT_BUF (0x01C)
+#define SPDC_CURRENT_BUF (0x020)
+#define SPDC_PRE_BUF (0x024)
+#define SPDC_CNT_BUF (0x028)
+#define SPDC_LUT_BUF (0x02C)
+#define SPDC_INT_ENABLE (0x030)
+#define SPDC_INT_STA_CLR (0x034)
+#define SPDC_INIT_STA_CLR (0x038)
+#define SPDC_STATUS (0x03C)
+#define SPDC_DISP_VER (0x040)
+#define SPDC_TCON_VER (0x044)
+#define SPDC_SW_GATE_CLK (0x048)
+
+/*
+ * Register field definitions
+ */
+enum {
+ SPDC_DISP_TRIGGER_ENABLE = 0x1,
+ SPDC_DISP_TRIGGER_FLASH = 0x10,
+
+ /*SPDC clock gate*/
+ SPDC_SW_GATE_CLK_ENABLE = 0x1,
+
+ /* waveform mode mask */
+ SPDC_WAV_MODE_MASK = (0x7 << 1),
+
+ /* SPDC interrupt IRQ mask define */
+ SPDC_IRQ_FRAME_UPDATE = (0x1 << 0),
+ SPDC_IRQ_TCON_INIT = (0x1 << 1),
+ SPDC_IRQ_LUT_DOWNLOAD = (0x1 << 2),
+ SPDC_IRQ_ERR = (0x1 << 3),
+ SPDC_IRQ_ALL_MASK = 0xF,
+
+ /* SPDC interrupt status */
+ SPDC_IRQ_STA_FRAME_UPDATE = (0x1 << 0),
+ SPDC_IRQ_STA_TCON_INIT = (0x1 << 1),
+ SPDC_IRQ_STA_LUT_DOWNLOAD = (0x1 << 2),
+ SPDC_IRQ_STA_ERR = (0x1 << 3),
+ SPDC_IRQ_STA_ALL_MASK = 0xF,
+
+ /* SPDC update coordinate angle */
+ SPDC_UPDATE_W_H_MAX_SIZE = ((0x1 << 12) - 1),
+ SPDC_UPDATE_X_Y_MAX_SIZE = ((0x1 << 12) - 1),
+
+ /* SPDC TCON status */
+ SPDC_PANEL_STAUTS_BUSY = 0x1,
+ SPDC_TCON_STATUS_IDLE = (0x4 << 4),
+
+ /* SPDC EPD Operation mode */
+ SPDC_NO_OPERATION = 0,
+ SPDC_DISP_REFRESH = 1,
+ SPDC_DEEP_REFRESH = 0x2,
+ SPDC_DISP_RESET = 0x4,
+ SPDC_SW_TCON_RESET = 0x5,
+ SPDC_SW_TCON_RESET_SET = 0x80000000,
+ SPDC_FULL_REFRESH = 0x6,
+
+ /* SPDC Concurrency mechanism: ACC */
+ SPDC_LUT_MODE_OFFSET = 0xb3,
+ SPDC_LUT_ACC_MODE = 0x4,
+
+ /* SPDC waveform */
+ SPDC_WAVEFORM_LUT_OFFSET_ADDR = 0x7600,
+ SPDC_LUT_PARA_LENTH = 0x100,
+ SPDC_LUT_FROM_MEM = 1,
+ SPDC_LUT_TO_MEM = 0,
+
+ /* SPDC submit update status */
+ SPDC_CONCUR_UPD = 0x4,
+ SPDC_QUEUE_UPD = 0x8,
+ SPDC_CONCUR_QUEUE = 0xc,
+};
+
+#define SPDC_DRIVER_NAME "imx_spdc_fb"
+
+/**
+ * SPDC EPD waveform display trigger mode
+ */
+enum {
+ SPDC_WAV_MODE_0 = 0,
+ SPDC_WAV_MODE_1,
+ SPDC_WAV_MODE_2,
+ SPDC_WAV_MODE_3,
+ SPDC_WAV_MODE_4,
+ SPDC_WAV_MODE_5,
+ SPDC_WAV_MODE_DEFAULT = SPDC_WAV_MODE_0,
+};
+
+/**
+ * SPDC controller ip version
+ */
+struct mxc_epd_disp_version {
+ u8 epd_type;
+ u8 lut_ver;
+ u16 product_id;
+};
+
+struct mxc_spdc_version {
+ struct mxc_epd_disp_version disp_ver;
+ u8 tcon_ver;
+};
+
+struct spdc_buffer_addr {
+ u32 next_buf_phys_addr;
+ u32 cur_buf_phys_addr;
+ u32 pre_buf_phys_addr;
+ u32 frm_cnt_buf_phys_addr;
+ u32 lut_buf_phys_addr;
+};
+
+struct partial_refresh_param {
+ struct spdc_buffer_addr buf_addr;
+ struct mxcfb_rect update_region;
+ int temper;
+ u32 blocking;
+ u32 wave_mode;
+ u32 stride;
+ u32 flash; /* only for waveform mode7 */
+ u32 concur; /* Concurrency mechanism: ACC */
+};
+
+/**
+ * SPDC lut data
+ */
+struct mxc_spdc_lut_para {
+ u8 lut_data[SPDC_LUT_PARA_LENTH];
+ u8 lut_addr[SPDC_LUT_PARA_LENTH];
+ bool lut_para_update_sta;
+};
+
+#define PORTRAIT "portrait"
+#define LANDSCAPE "Landscape"
+#define RESERVED "Reserved"
+struct mxc_spdc_resolution_map_para {
+ u16 resolution;
+ u16 res_x;
+ u16 res_y;
+ char res_name[12];
+};
+
+struct update_marker_data {
+ struct list_head full_list;
+ struct list_head upd_list;
+ u32 update_marker;
+ struct completion update_completion;
+ int lut_num;
+ bool collision_test;
+ bool waiting;
+};
+
+struct update_desc_list {
+ struct list_head list;
+ struct mxcfb_update_data upd_data;
+ u32 spdc_offs;
+ u32 spdc_stride;
+ struct list_head upd_marker_list;
+ u32 update_order;
+};
+
+/* This structure represents a list node containing both
+ * a memory region allocated as an output buffer for the PxP
+ * update processing task, and the update
+ * description (mode, region, etc.)
+ */
+struct update_data_list {
+ struct list_head list;
+ dma_addr_t phys_addr;/* Pointer to phys address of processed Y buf */
+ void *virt_addr;
+ struct update_desc_list *update_desc;
+ int lut_num;
+ u64 collision_mask; /* SPDC cannot support collision detect,
+ * align with EPDC driver struct */
+};
+
+typedef struct mxc_spdc_fb_param {
+ struct fb_info info;
+ struct fb_var_screeninfo spdc_fb_var;
+
+ char fw_str[24];
+ u32 pseudo_palette[16];
+ struct mxc_spdc_version spdc_ver;
+ struct imx_spdc_fb_mode *cur_mode;
+ struct imx_spdc_fb_platform_data *pdata;
+ struct partial_refresh_param fresh_param;
+ int blank;
+ bool updates_active;
+
+ u32 fb_offset;
+ u32 max_pix_size;
+ ssize_t map_size;
+ int native_width;
+ int native_height;
+ int default_bpp;
+
+ dma_addr_t phys_start;
+ void *virt_start;
+ struct device *dev;
+
+ int num_screens;
+ u32 order_cnt;
+ int max_num_updates;
+ u32 auto_mode;
+ u32 upd_scheme;
+ u32 operation_mode;
+ bool is_deep_fresh;
+ struct list_head full_marker_list;
+ struct list_head upd_pending_list;
+ struct list_head upd_buf_queue;
+ struct list_head upd_buf_free_list;
+ struct list_head upd_buf_preprocess_list;
+ u32 upd_preprocess_num;
+ int submit_upd_sta;
+ struct update_data_list *cur_update;
+ struct mutex queue_mutex;
+ struct imx_spdc_panel_init_set panel_set;
+ struct mxc_spdc_lut_para lut_para;
+ struct mxcfb_waveform_modes wv_modes;
+
+ dma_addr_t phy_next_buf;
+ dma_addr_t phy_pre_buf;
+ dma_addr_t phy_current_buf;
+ dma_addr_t phy_cnt_buf;
+ dma_addr_t phy_lut_buf;
+ void *virt_next_buf;
+ void *virt_current_buf;
+ void *virt_pre_buf;
+ void *virt_cnt_buf;
+ void *virt_lut_buf;
+ u32 next_buf_size;
+ u32 current_buf_size;
+ u32 pre_buf_size;
+ u32 cnt_buf_size;
+ u32 lut_buf_size;
+
+ dma_addr_t *phys_addr_updbuf;
+ void **virt_addr_updbuf;
+ u32 upd_buffer_num;
+ u32 max_num_buffers;
+
+ /* copy the processed data to next buffer relative region */
+ dma_addr_t phys_addr_copybuf;
+ void *virt_addr_copybuf;
+
+ int spdc_irq;
+ char __iomem *hwp;
+ struct clk *spdc_clk_axi;
+ struct clk *spdc_clk_pix;
+ struct regulator *display_regulator;
+ struct regulator *vcom_regulator;
+ struct regulator *v3p3_regulator;
+
+ spinlock_t lock;
+ int power_state;
+ bool powering_down;
+ int pwrdown_delay;
+ int wait_for_powerdown;
+ struct completion powerdown_compl;
+ struct mutex power_mutex;
+ bool fw_default_load;
+ bool hw_ready;
+ bool hw_initializing;
+ bool waiting_for_idle;
+ bool waiting_for_wb;
+ struct completion update_res_free;
+ struct completion lut_down;
+ struct completion init_finish;
+ struct completion update_finish;
+ struct completion updates_done;
+ struct delayed_work spdc_done_work;
+ struct workqueue_struct *spdc_submit_workqueue;
+ struct work_struct spdc_submit_work;
+ struct workqueue_struct *spdc_intr_workqueue;
+ struct work_struct spdc_intr_work;
+
+ /* FB elements related to PxP DMA */
+ struct completion pxp_tx_cmpl;
+ struct pxp_channel *pxp_chan;
+ struct pxp_config_data pxp_conf;
+ struct dma_async_tx_descriptor *txd;
+ dma_cookie_t cookie;
+ struct scatterlist sg[2];
+ struct mutex pxp_mutex; /* protects access to PxP */
+} mxc_spdc_t;
+
+#endif
diff --git a/drivers/video/mxc/mxcfb.c b/drivers/video/mxc/mxcfb.c
new file mode 100644
index 00000000..4dffee5d
--- /dev/null
+++ b/drivers/video/mxc/mxcfb.c
@@ -0,0 +1,1373 @@
+/*
+ * Copyright 2004-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @defgroup Framebuffer Framebuffer Driver for SDC and ADC.
+ */
+
+/*!
+ * @file mxcfb.c
+ *
+ * @brief MXC Frame buffer driver for SDC
+ *
+ * @ingroup Framebuffer
+ */
+
+/*!
+ * Include files
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/fb.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/io.h>
+#include <linux/ipu.h>
+#include <linux/mxcfb.h>
+#include <linux/uaccess.h>
+#include <asm/mach-types.h>
+
+/*
+ * Driver name
+ */
+#define MXCFB_NAME "mxc_sdc_fb"
+/*!
+ * Structure containing the MXC specific framebuffer information.
+ */
+struct mxcfb_info {
+ int blank;
+ ipu_channel_t ipu_ch;
+ uint32_t ipu_ch_irq;
+ uint32_t cur_ipu_buf;
+
+ u32 pseudo_palette[16];
+
+ struct semaphore flip_sem;
+ spinlock_t fb_lock;
+};
+
+struct mxcfb_data {
+ struct fb_info *fbi;
+ struct fb_info *fbi_ovl;
+ volatile int32_t vsync_flag;
+ wait_queue_head_t vsync_wq;
+ wait_queue_head_t suspend_wq;
+ bool suspended;
+ int backlight_level;
+};
+
+struct mxcfb_alloc_list {
+ struct list_head list;
+ dma_addr_t phy_addr;
+ void *cpu_addr;
+ u32 size;
+};
+
+static struct mxcfb_data mxcfb_drv_data;
+
+static char *fb_mode;
+static unsigned long default_bpp = 16;
+#ifdef CONFIG_FB_MXC_INTERNAL_MEM
+static struct clk *iram_clk;
+#endif
+LIST_HEAD(fb_alloc_list);
+
+static uint32_t bpp_to_pixfmt(int bpp)
+{
+ uint32_t pixfmt = 0;
+ switch (bpp) {
+ case 24:
+ pixfmt = IPU_PIX_FMT_BGR24;
+ break;
+ case 32:
+ pixfmt = IPU_PIX_FMT_BGR32;
+ break;
+ case 16:
+ pixfmt = IPU_PIX_FMT_RGB565;
+ break;
+ }
+ return pixfmt;
+}
+
+extern void gpio_lcd_active(void);
+extern void gpio_lcd_inactive(void);
+static irqreturn_t mxcfb_irq_handler(int irq, void *dev_id);
+static int mxcfb_blank(int blank, struct fb_info *info);
+static int mxcfb_map_video_memory(struct fb_info *fbi, bool use_internal_ram);
+static int mxcfb_unmap_video_memory(struct fb_info *fbi);
+
+/*
+ * Set fixed framebuffer parameters based on variable settings.
+ *
+ * @param info framebuffer information pointer
+ */
+static int mxcfb_set_fix(struct fb_info *info)
+{
+ struct fb_fix_screeninfo *fix = &info->fix;
+ struct fb_var_screeninfo *var = &info->var;
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)info->par;
+
+ if (mxc_fbi->ipu_ch == MEM_SDC_FG)
+ strncpy(fix->id, "DISP3 FG", 8);
+ else
+ strncpy(fix->id, "DISP3 BG", 8);
+
+ fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
+
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->accel = FB_ACCEL_NONE;
+ fix->visual = FB_VISUAL_TRUECOLOR;
+ fix->xpanstep = 1;
+ fix->ypanstep = 1;
+
+ return 0;
+}
+
+/*
+ * Set framebuffer parameters and change the operating mode.
+ *
+ * @param info framebuffer information pointer
+ */
+static int mxcfb_set_par(struct fb_info *fbi)
+{
+ int retval;
+ bool use_iram = false;
+ u32 mem_len;
+ ipu_di_signal_cfg_t sig_cfg;
+ ipu_panel_t mode = IPU_PANEL_TFT;
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
+
+ retval = wait_event_interruptible(mxcfb_drv_data.suspend_wq, (mxcfb_drv_data.suspended == false));
+ if (retval < 0)
+ return retval;
+
+ ipu_disable_irq(mxc_fbi->ipu_ch_irq);
+ ipu_disable_channel(mxc_fbi->ipu_ch, true);
+ ipu_uninit_channel(mxc_fbi->ipu_ch);
+ ipu_clear_irq(mxc_fbi->ipu_ch_irq);
+ mxcfb_set_fix(fbi);
+
+ mem_len = fbi->var.yres_virtual * fbi->fix.line_length;
+ if (mem_len > fbi->fix.smem_len) {
+ if (fbi->fix.smem_start)
+ mxcfb_unmap_video_memory(fbi);
+
+#ifdef CONFIG_FB_MXC_INTERNAL_MEM
+ if (mxc_fbi->ipu_ch == MEM_SDC_BG) {
+ use_iram = true;
+ }
+#endif
+ if (mxcfb_map_video_memory(fbi, use_iram) < 0)
+ return -ENOMEM;
+ }
+
+ ipu_init_channel(mxc_fbi->ipu_ch, NULL);
+
+ /* Clear the screen */
+ memset((char *)fbi->screen_base, 0, fbi->fix.smem_len);
+
+ if (mxc_fbi->ipu_ch == MEM_SDC_BG) {
+ memset(&sig_cfg, 0, sizeof(sig_cfg));
+ if (fbi->var.sync & FB_SYNC_HOR_HIGH_ACT)
+ sig_cfg.Hsync_pol = true;
+ if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT)
+ sig_cfg.Vsync_pol = true;
+ if (!(fbi->var.sync & FB_SYNC_CLK_LAT_FALL))
+ sig_cfg.clk_pol = true;
+ if (fbi->var.sync & FB_SYNC_DATA_INVERT)
+ sig_cfg.data_pol = true;
+ if (!(fbi->var.sync & FB_SYNC_OE_LOW_ACT))
+ sig_cfg.enable_pol = true;
+ if (fbi->var.sync & FB_SYNC_CLK_IDLE_EN)
+ sig_cfg.clkidle_en = true;
+ if (fbi->var.sync & FB_SYNC_SHARP_MODE)
+ mode = IPU_PANEL_SHARP_TFT;
+
+ dev_dbg(fbi->device, "pixclock = %ul Hz\n",
+ (u32) (PICOS2KHZ(fbi->var.pixclock) * 1000UL));
+
+ if (ipu_sdc_init_panel(mode,
+ (PICOS2KHZ(fbi->var.pixclock)) * 1000UL,
+ fbi->var.xres, fbi->var.yres,
+ (fbi->var.sync & FB_SYNC_SWAP_RGB) ?
+ IPU_PIX_FMT_BGR666 : IPU_PIX_FMT_RGB666,
+ fbi->var.left_margin,
+ fbi->var.hsync_len,
+ fbi->var.right_margin,
+ fbi->var.upper_margin,
+ fbi->var.vsync_len,
+ fbi->var.lower_margin, sig_cfg) != 0) {
+ dev_err(fbi->device,
+ "mxcfb: Error initializing panel.\n");
+ return -EINVAL;
+ }
+
+ fbi->mode =
+ (struct fb_videomode *)fb_match_mode(&fbi->var,
+ &fbi->modelist);
+ }
+
+ ipu_disp_set_window_pos(mxc_fbi->ipu_ch, 0, 0);
+
+ mxc_fbi->cur_ipu_buf = 1;
+ sema_init(&mxc_fbi->flip_sem, 1);
+ fbi->var.xoffset = fbi->var.yoffset = 0;
+
+ retval = ipu_init_channel_buffer(mxc_fbi->ipu_ch, IPU_INPUT_BUFFER,
+ bpp_to_pixfmt(fbi->var.bits_per_pixel),
+ fbi->var.xres, fbi->var.yres,
+ fbi->var.xres_virtual,
+ IPU_ROTATE_NONE,
+ fbi->fix.smem_start +
+ (fbi->fix.line_length * fbi->var.yres),
+ fbi->fix.smem_start,
+ 0,
+ 0, 0);
+ if (retval) {
+ dev_err(fbi->device,
+ "ipu_init_channel_buffer error %d\n", retval);
+ return retval;
+ }
+
+ if (mxc_fbi->blank == FB_BLANK_UNBLANK) {
+ ipu_enable_channel(mxc_fbi->ipu_ch);
+ }
+
+ return 0;
+}
+
+/*
+ * Check framebuffer variable parameters and adjust to valid values.
+ *
+ * @param var framebuffer variable parameters
+ *
+ * @param info framebuffer information pointer
+ */
+static int mxcfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ u32 vtotal;
+ u32 htotal;
+
+ if (var->xres_virtual < var->xres)
+ var->xres_virtual = var->xres;
+ if (var->yres_virtual < var->yres)
+ var->yres_virtual = var->yres;
+
+#ifdef CONFIG_FB_MXC_INTERNAL_MEM
+ if ((info->fix.smem_start == FB_RAM_BASE_ADDR) &&
+ ((var->yres_virtual * var->xres_virtual * var->bits_per_pixel / 8) >
+ FB_RAM_SIZE)) {
+ return -EINVAL;
+ }
+#endif
+
+ if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) &&
+ (var->bits_per_pixel != 16)) {
+ var->bits_per_pixel = default_bpp;
+ }
+
+ switch (var->bits_per_pixel) {
+ case 16:
+ var->red.length = 5;
+ var->red.offset = 11;
+ var->red.msb_right = 0;
+
+ var->green.length = 6;
+ var->green.offset = 5;
+ var->green.msb_right = 0;
+
+ var->blue.length = 5;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ var->transp.msb_right = 0;
+ break;
+ case 24:
+ var->red.length = 8;
+ var->red.offset = 16;
+ var->red.msb_right = 0;
+
+ var->green.length = 8;
+ var->green.offset = 8;
+ var->green.msb_right = 0;
+
+ var->blue.length = 8;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ var->transp.msb_right = 0;
+ break;
+ case 32:
+ var->red.length = 8;
+ var->red.offset = 16;
+ var->red.msb_right = 0;
+
+ var->green.length = 8;
+ var->green.offset = 8;
+ var->green.msb_right = 0;
+
+ var->blue.length = 8;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 8;
+ var->transp.offset = 24;
+ var->transp.msb_right = 0;
+ break;
+ }
+
+ if (var->pixclock < 1000) {
+ htotal = var->xres + var->right_margin + var->hsync_len +
+ var->left_margin;
+ vtotal = var->yres + var->lower_margin + var->vsync_len +
+ var->upper_margin;
+ var->pixclock = (vtotal * htotal * 6UL) / 100UL;
+ var->pixclock = KHZ2PICOS(var->pixclock);
+ dev_dbg(info->device,
+ "pixclock set for 60Hz refresh = %u ps\n",
+ var->pixclock);
+ }
+
+ var->height = -1;
+ var->width = -1;
+ var->grayscale = 0;
+
+ /* nonstd used for YUV formats, but only RGB supported */
+ var->nonstd = 0;
+
+ return 0;
+}
+
+static inline u_int _chan_to_field(u_int chan, struct fb_bitfield *bf)
+{
+ chan &= 0xffff;
+ chan >>= 16 - bf->length;
+ return chan << bf->offset;
+}
+static int
+mxcfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int trans, struct fb_info *fbi)
+{
+ unsigned int val;
+ int ret = 1;
+
+ /*
+ * If greyscale is true, then we convert the RGB value
+ * to greyscale no matter what visual we are using.
+ */
+ if (fbi->var.grayscale)
+ red = green = blue = (19595 * red + 38470 * green +
+ 7471 * blue) >> 16;
+ switch (fbi->fix.visual) {
+ case FB_VISUAL_TRUECOLOR:
+ /*
+ * 16-bit True Colour. We encode the RGB value
+ * according to the RGB bitfield information.
+ */
+ if (regno < 16) {
+ u32 *pal = fbi->pseudo_palette;
+
+ val = _chan_to_field(red, &fbi->var.red);
+ val |= _chan_to_field(green, &fbi->var.green);
+ val |= _chan_to_field(blue, &fbi->var.blue);
+
+ pal[regno] = val;
+ ret = 0;
+ }
+ break;
+
+ case FB_VISUAL_STATIC_PSEUDOCOLOR:
+ case FB_VISUAL_PSEUDOCOLOR:
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * Function to handle custom ioctls for MXC framebuffer.
+ *
+ * @param inode inode struct
+ *
+ * @param file file struct
+ *
+ * @param cmd Ioctl command to handle
+ *
+ * @param arg User pointer to command arguments
+ *
+ * @param fbi framebuffer information pointer
+ */
+static int mxcfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
+{
+ int retval = 0;
+ int __user *argp = (void __user *)arg;
+
+ retval = wait_event_interruptible(mxcfb_drv_data.suspend_wq,
+ (mxcfb_drv_data.suspended ==
+ false));
+ if (retval < 0)
+ return retval;
+
+ switch (cmd) {
+ case MXCFB_SET_GBL_ALPHA:
+ {
+ struct mxcfb_gbl_alpha ga;
+ if (copy_from_user(&ga, (void *)arg, sizeof(ga))) {
+ retval = -EFAULT;
+ break;
+ }
+ retval =
+ ipu_sdc_set_global_alpha((bool) ga.enable,
+ ga.alpha);
+ dev_dbg(fbi->device, "Set global alpha to %d\n",
+ ga.alpha);
+ break;
+ }
+ case MXCFB_SET_CLR_KEY:
+ {
+ struct mxcfb_color_key key;
+ if (copy_from_user(&key, (void *)arg, sizeof(key))) {
+ retval = -EFAULT;
+ break;
+ }
+ retval = ipu_sdc_set_color_key(MEM_SDC_BG, key.enable,
+ key.color_key);
+ dev_dbg(fbi->device, "Set color key to 0x%08X\n",
+ key.color_key);
+ break;
+ }
+ case MXCFB_WAIT_FOR_VSYNC:
+ {
+#ifndef CONFIG_ARCH_MX3
+ mxcfb_drv_data.vsync_flag = 0;
+ ipu_enable_irq(IPU_IRQ_SDC_DISP3_VSYNC);
+ if (!wait_event_interruptible_timeout
+ (mxcfb_drv_data.vsync_wq,
+ mxcfb_drv_data.vsync_flag != 0, 1 * HZ)) {
+ dev_err(fbi->device,
+ "MXCFB_WAIT_FOR_VSYNC: timeout\n");
+ retval = -ETIME;
+ break;
+ } else if (signal_pending(current)) {
+ dev_err(fbi->device,
+ "MXCFB_WAIT_FOR_VSYNC: interrupt received\n");
+ retval = -ERESTARTSYS;
+ break;
+ }
+#endif
+ break;
+ }
+ case MXCFB_GET_FB_IPU_CHAN:
+ {
+ struct mxcfb_info *mxc_fbi =
+ (struct mxcfb_info *)fbi->par;
+
+ if (put_user(mxc_fbi->ipu_ch, argp))
+ return -EFAULT;
+
+ break;
+ }
+ default:
+ retval = -EINVAL;
+ }
+ return retval;
+}
+
+/*
+ * Function to handle custom ioctls for MXC framebuffer.
+ *
+ * @param inode inode struct
+ *
+ * @param file file struct
+ *
+ * @param cmd Ioctl command to handle
+ *
+ * @param arg User pointer to command arguments
+ *
+ * @param fbi framebuffer information pointer
+ */
+static int mxcfb_ioctl_ovl(struct fb_info *fbi, unsigned int cmd,
+ unsigned long arg)
+{
+ int retval = 0;
+ int __user *argp = (void __user *)arg;
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
+
+ retval = wait_event_interruptible(mxcfb_drv_data.suspend_wq,
+ (mxcfb_drv_data.suspended == false));
+ if (retval < 0)
+ return retval;
+
+ switch (cmd) {
+ case FBIO_ALLOC:
+ {
+ int size;
+ struct mxcfb_alloc_list *mem;
+
+ mem = kzalloc(sizeof(*mem), GFP_KERNEL);
+ if (mem == NULL)
+ return -ENOMEM;
+
+ if (get_user(size, argp))
+ return -EFAULT;
+
+ mem->size = PAGE_ALIGN(size);
+
+ mem->cpu_addr = dma_alloc_coherent(fbi->device, size,
+ &mem->phy_addr,
+ GFP_DMA);
+ if (mem->cpu_addr == NULL) {
+ kfree(mem);
+ return -ENOMEM;
+ }
+
+ list_add(&mem->list, &fb_alloc_list);
+
+ dev_dbg(fbi->device, "allocated %d bytes @ 0x%08X\n",
+ mem->size, mem->phy_addr);
+
+ if (put_user(mem->phy_addr, argp))
+ return -EFAULT;
+
+ break;
+ }
+ case FBIO_FREE:
+ {
+ unsigned long offset;
+ struct mxcfb_alloc_list *mem;
+
+ if (get_user(offset, argp))
+ return -EFAULT;
+
+ retval = -EINVAL;
+ list_for_each_entry(mem, &fb_alloc_list, list) {
+ if (mem->phy_addr == offset) {
+ list_del(&mem->list);
+ dma_free_coherent(fbi->device,
+ mem->size,
+ mem->cpu_addr,
+ mem->phy_addr);
+ kfree(mem);
+ retval = 0;
+ break;
+ }
+ }
+
+ break;
+ }
+ case MXCFB_SET_OVERLAY_POS:
+ {
+ struct mxcfb_pos pos;
+ if (copy_from_user(&pos, (void *)arg, sizeof(pos))) {
+ retval = -EFAULT;
+ break;
+ }
+ retval = ipu_disp_set_window_pos(mxc_fbi->ipu_ch,
+ pos.x, pos.y);
+ break;
+ }
+ case MXCFB_GET_FB_IPU_CHAN:
+ {
+ struct mxcfb_info *mxc_fbi =
+ (struct mxcfb_info *)fbi->par;
+
+ if (put_user(mxc_fbi->ipu_ch, argp))
+ return -EFAULT;
+
+ break;
+ }
+ default:
+ retval = -EINVAL;
+ }
+ return retval;
+}
+
+/*
+ * mxcfb_blank():
+ * Blank the display.
+ */
+static int mxcfb_blank(int blank, struct fb_info *info)
+{
+ int retval;
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)info->par;
+
+ dev_dbg(info->device, "blank = %d\n", blank);
+
+ if (mxc_fbi->blank == blank)
+ return 0;
+
+ retval = wait_event_interruptible(mxcfb_drv_data.suspend_wq,
+ (mxcfb_drv_data.suspended == false));
+ if (retval < 0)
+ return retval;
+
+ mxc_fbi->blank = blank;
+
+ switch (blank) {
+ case FB_BLANK_POWERDOWN:
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_HSYNC_SUSPEND:
+ case FB_BLANK_NORMAL:
+ ipu_disable_channel(MEM_SDC_BG, true);
+ gpio_lcd_inactive();
+ break;
+ case FB_BLANK_UNBLANK:
+ gpio_lcd_active();
+ ipu_enable_channel(MEM_SDC_BG);
+ break;
+ }
+ return 0;
+}
+
+/*
+ * mxcfb_blank_ovl():
+ * Blank the display.
+ */
+static int mxcfb_blank_ovl(int blank, struct fb_info *info)
+{
+ int retval;
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)info->par;
+
+ dev_dbg(info->device, "ovl blank = %d\n", blank);
+
+ if (mxc_fbi->blank == blank)
+ return 0;
+
+ retval = wait_event_interruptible(mxcfb_drv_data.suspend_wq,
+ (mxcfb_drv_data.suspended == false));
+ if (retval < 0)
+ return retval;
+
+ mxc_fbi->blank = blank;
+
+ switch (blank) {
+ case FB_BLANK_POWERDOWN:
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_HSYNC_SUSPEND:
+ case FB_BLANK_NORMAL:
+ ipu_disable_channel(MEM_SDC_FG, true);
+ break;
+ case FB_BLANK_UNBLANK:
+ ipu_enable_channel(MEM_SDC_FG);
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Pan or Wrap the Display
+ *
+ * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
+ *
+ * @param var Variable screen buffer information
+ * @param info Framebuffer information pointer
+ */
+static int
+mxcfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)info->par;
+ unsigned long lock_flags = 0;
+ int retval;
+ u_int y_bottom;
+ unsigned long base;
+
+ retval = wait_event_interruptible(mxcfb_drv_data.suspend_wq,
+ (mxcfb_drv_data.suspended == false));
+ if (retval < 0)
+ return retval;
+
+ if (var->xoffset > 0) {
+ dev_dbg(info->device, "x panning not supported\n");
+ return -EINVAL;
+ }
+
+ if ((info->var.xoffset == var->xoffset) &&
+ (info->var.yoffset == var->yoffset)) {
+ /* No change, do nothing */
+ return 0;
+ }
+
+ y_bottom = var->yoffset;
+
+ if (!(var->vmode & FB_VMODE_YWRAP)) {
+ y_bottom += var->yres;
+ }
+
+ if (y_bottom > info->var.yres_virtual) {
+ return -EINVAL;
+ }
+
+ base = (var->yoffset * var->xres_virtual + var->xoffset);
+ base *= (var->bits_per_pixel) / 8;
+ base += info->fix.smem_start;
+
+ down(&mxc_fbi->flip_sem);
+
+ spin_lock_irqsave(&mxc_fbi->fb_lock, lock_flags);
+
+ dev_dbg(info->device, "Updating SDC BG buf %d address=0x%08lX\n",
+ mxc_fbi->cur_ipu_buf, base);
+
+ mxc_fbi->cur_ipu_buf = !mxc_fbi->cur_ipu_buf;
+ if (ipu_update_channel_buffer(mxc_fbi->ipu_ch, IPU_INPUT_BUFFER,
+ mxc_fbi->cur_ipu_buf, base) == 0) {
+ ipu_select_buffer(mxc_fbi->ipu_ch, IPU_INPUT_BUFFER,
+ mxc_fbi->cur_ipu_buf);
+ ipu_clear_irq(mxc_fbi->ipu_ch_irq);
+ ipu_enable_irq(mxc_fbi->ipu_ch_irq);
+ } else {
+ dev_err(info->device,
+ "Error updating SDC buf %d to address=0x%08lX\n",
+ mxc_fbi->cur_ipu_buf, base);
+ }
+
+ spin_unlock_irqrestore(&mxc_fbi->fb_lock, lock_flags);
+
+ dev_dbg(info->device, "Update complete\n");
+
+ info->var.xoffset = var->xoffset;
+ info->var.yoffset = var->yoffset;
+
+ if (var->vmode & FB_VMODE_YWRAP) {
+ info->var.vmode |= FB_VMODE_YWRAP;
+ } else {
+ info->var.vmode &= ~FB_VMODE_YWRAP;
+ }
+
+ return 0;
+}
+
+/*
+ * Function to handle custom mmap for MXC framebuffer.
+ *
+ * @param fbi framebuffer information pointer
+ *
+ * @param vma Pointer to vm_area_struct
+ */
+static int mxcfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma)
+{
+ bool found = false;
+ u32 len;
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ struct mxcfb_alloc_list *mem;
+
+ if (offset < fbi->fix.smem_len) {
+ /* mapping framebuffer memory */
+ len = fbi->fix.smem_len - offset;
+ vma->vm_pgoff = (fbi->fix.smem_start + offset) >> PAGE_SHIFT;
+ } else {
+ list_for_each_entry(mem, &fb_alloc_list, list) {
+ if (offset == mem->phy_addr) {
+ found = true;
+ len = mem->size;
+ break;
+ }
+ }
+ if (!found) {
+ return -EINVAL;
+ }
+ }
+
+ len = PAGE_ALIGN(len);
+ if (vma->vm_end - vma->vm_start > len) {
+ return -EINVAL;
+ }
+
+ /* make buffers write-thru cacheable */
+ vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) &
+ ~L_PTE_BUFFERABLE);
+
+ vma->vm_flags |= VM_IO | VM_RESERVED;
+
+ if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
+ vma->vm_end - vma->vm_start, vma->vm_page_prot)) {
+ dev_dbg(fbi->device, "mmap remap_pfn_range failed\n");
+ return -ENOBUFS;
+
+ }
+
+ return 0;
+}
+
+/*!
+ * This structure contains the pointers to the control functions that are
+ * invoked by the core framebuffer driver to perform operations like
+ * blitting, rectangle filling, copy regions and cursor definition.
+ */
+static struct fb_ops mxcfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_set_par = mxcfb_set_par,
+ .fb_check_var = mxcfb_check_var,
+ .fb_setcolreg = mxcfb_setcolreg,
+ .fb_pan_display = mxcfb_pan_display,
+ .fb_ioctl = mxcfb_ioctl,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_blank = mxcfb_blank,
+};
+
+static struct fb_ops mxcfb_ovl_ops = {
+ .owner = THIS_MODULE,
+ .fb_set_par = mxcfb_set_par,
+ .fb_check_var = mxcfb_check_var,
+ .fb_setcolreg = mxcfb_setcolreg,
+ .fb_pan_display = mxcfb_pan_display,
+ .fb_ioctl = mxcfb_ioctl_ovl,
+ .fb_mmap = mxcfb_mmap,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_blank = mxcfb_blank_ovl,
+};
+
+static irqreturn_t mxcfb_vsync_irq_handler(int irq, void *dev_id)
+{
+ struct mxcfb_data *fb_data = dev_id;
+
+ ipu_disable_irq(irq);
+
+ fb_data->vsync_flag = 1;
+ wake_up_interruptible(&fb_data->vsync_wq);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mxcfb_irq_handler(int irq, void *dev_id)
+{
+ struct fb_info *fbi = dev_id;
+ struct mxcfb_info *mxc_fbi = fbi->par;
+
+ up(&mxc_fbi->flip_sem);
+ ipu_disable_irq(irq);
+ return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_PM
+/*
+ * Power management hooks. Note that we won't be called from IRQ context,
+ * unlike the blank functions above, so we may sleep.
+ */
+
+/*
+ * Suspends the framebuffer and blanks the screen. Power management support
+ */
+static int mxcfb_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct mxcfb_data *drv_data = platform_get_drvdata(pdev);
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)drv_data->fbi->par;
+ struct mxcfb_info *mxc_fbi_ovl =
+ (struct mxcfb_info *)drv_data->fbi_ovl->par;
+#ifdef CONFIG_FB_MXC_LOW_PWR_DISPLAY
+ void *fbmem;
+#endif
+
+ drv_data->suspended = true;
+
+ console_lock();
+ fb_set_suspend(drv_data->fbi, 1);
+ fb_set_suspend(drv_data->fbi_ovl, 1);
+ console_unlock();
+
+ if (mxc_fbi_ovl->blank == FB_BLANK_UNBLANK) {
+ ipu_disable_channel(MEM_SDC_FG, true);
+ }
+
+ if (mxc_fbi->blank == FB_BLANK_UNBLANK) {
+#ifdef CONFIG_FB_MXC_LOW_PWR_DISPLAY
+ if (drv_data->fbi->fix.smem_start != FB_RAM_BASE_ADDR) {
+ fbmem = ioremap(FB_RAM_BASE_ADDR, FB_RAM_SIZE);
+ memcpy(fbmem, drv_data->fbi->screen_base, FB_RAM_SIZE);
+ iounmap(fbmem);
+ mxc_fbi->cur_ipu_buf = !mxc_fbi->cur_ipu_buf;
+ ipu_update_channel_buffer(MEM_SDC_BG, IPU_INPUT_BUFFER,
+ mxc_fbi->cur_ipu_buf,
+ FB_RAM_BASE_ADDR);
+ ipu_select_buffer(MEM_SDC_BG, IPU_INPUT_BUFFER,
+ mxc_fbi->cur_ipu_buf);
+ }
+ ipu_lowpwr_display_enable();
+#else
+ ipu_disable_channel(MEM_SDC_BG, true);
+ gpio_lcd_inactive();
+#endif
+ }
+ return 0;
+}
+
+/*
+ * Resumes the framebuffer and unblanks the screen. Power management support
+ */
+static int mxcfb_resume(struct platform_device *pdev)
+{
+ struct mxcfb_data *drv_data = platform_get_drvdata(pdev);
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)drv_data->fbi->par;
+ struct mxcfb_info *mxc_fbi_ovl =
+ (struct mxcfb_info *)drv_data->fbi_ovl->par;
+
+ drv_data->suspended = false;
+
+ if (mxc_fbi->blank == FB_BLANK_UNBLANK) {
+#ifdef CONFIG_FB_MXC_LOW_PWR_DISPLAY
+ ipu_lowpwr_display_disable();
+ if (drv_data->fbi->fix.smem_start != FB_RAM_BASE_ADDR) {
+ mxc_fbi->cur_ipu_buf = !mxc_fbi->cur_ipu_buf;
+ ipu_update_channel_buffer(MEM_SDC_BG, IPU_INPUT_BUFFER,
+ mxc_fbi->cur_ipu_buf,
+ drv_data->fbi->fix.
+ smem_start);
+ ipu_select_buffer(MEM_SDC_BG, IPU_INPUT_BUFFER,
+ mxc_fbi->cur_ipu_buf);
+ }
+#else
+ gpio_lcd_active();
+ ipu_enable_channel(MEM_SDC_BG);
+#endif
+ }
+
+ if (mxc_fbi_ovl->blank == FB_BLANK_UNBLANK) {
+ ipu_enable_channel(MEM_SDC_FG);
+ }
+
+ console_lock();
+ fb_set_suspend(drv_data->fbi, 0);
+ fb_set_suspend(drv_data->fbi_ovl, 0);
+ console_unlock();
+
+ wake_up_interruptible(&drv_data->suspend_wq);
+ return 0;
+}
+#else
+#define mxcfb_suspend NULL
+#define mxcfb_resume NULL
+#endif
+
+/*
+ * Main framebuffer functions
+ */
+
+/*!
+ * Allocates the DRAM memory for the frame buffer. This buffer is remapped
+ * into a non-cached, non-buffered, memory region to allow palette and pixel
+ * writes to occur without flushing the cache. Once this area is remapped,
+ * all virtual memory access to the video memory should occur at the new region.
+ *
+ * @param fbi framebuffer information pointer
+ *
+ * @param use_internal_ram flag on whether to use internal RAM for memory
+ *
+ * @return Error code indicating success or failure
+ */
+static int mxcfb_map_video_memory(struct fb_info *fbi, bool use_internal_ram)
+{
+ int retval = 0;
+
+#ifdef CONFIG_FB_MXC_INTERNAL_MEM
+ if (use_internal_ram) {
+ fbi->fix.smem_len = FB_RAM_SIZE;
+ fbi->fix.smem_start = FB_RAM_BASE_ADDR;
+ if (fbi->fix.smem_len <
+ (fbi->var.yres_virtual * fbi->fix.line_length)) {
+ dev_err(fbi->device,
+ "Not enough internal RAM for framebuffer configuration\n");
+ retval = -EINVAL;
+ goto err0;
+ }
+
+ if (request_mem_region(fbi->fix.smem_start, fbi->fix.smem_len,
+ fbi->device->driver->name) == NULL) {
+ dev_err(fbi->device,
+ "Unable to request internal RAM\n");
+ retval = -ENOMEM;
+ goto err0;
+ }
+
+ fbi->screen_base = ioremap(fbi->fix.smem_start,
+ fbi->fix.smem_len);
+ if (!fbi->screen_base) {
+ dev_err(fbi->device,
+ "Unable to map fb memory to virtual address\n");
+ release_mem_region(fbi->fix.smem_start,
+ fbi->fix.smem_len);
+ retval = -EIO;
+ goto err0;
+ }
+
+ iram_clk = clk_get(NULL, "iram_clk");
+ clk_enable(iram_clk);
+ } else
+#endif
+ {
+ fbi->fix.smem_len = fbi->var.yres_virtual *
+ fbi->fix.line_length;
+ fbi->screen_base =
+ dma_alloc_writecombine(fbi->device,
+ fbi->fix.smem_len,
+ (dma_addr_t *) &fbi->fix.smem_start,
+ GFP_DMA);
+
+ if (fbi->screen_base == 0) {
+ dev_err(fbi->device,
+ "Unable to allocate framebuffer memory\n");
+ retval = -EBUSY;
+ goto err0;
+ }
+ }
+
+ dev_dbg(fbi->device, "allocated fb @ paddr=0x%08X, size=%d.\n",
+ (uint32_t) fbi->fix.smem_start, fbi->fix.smem_len);
+
+ fbi->screen_size = fbi->fix.smem_len;
+
+ /* Clear the screen */
+ memset((char *)fbi->screen_base, 0, fbi->fix.smem_len);
+
+ return 0;
+
+ err0:
+ fbi->fix.smem_len = 0;
+ fbi->fix.smem_start = 0;
+ fbi->screen_base = NULL;
+ return retval;
+}
+
+/*!
+ * De-allocates the DRAM memory for the frame buffer.
+ *
+ * @param fbi framebuffer information pointer
+ *
+ * @return Error code indicating success or failure
+ */
+static int mxcfb_unmap_video_memory(struct fb_info *fbi)
+{
+#ifdef CONFIG_FB_MXC_INTERNAL_MEM
+ if (fbi->fix.smem_start == FB_RAM_BASE_ADDR) {
+ iounmap(fbi->screen_base);
+ release_mem_region(fbi->fix.smem_start, fbi->fix.smem_len);
+ fbi->fix.smem_start = 0;
+ fbi->fix.smem_len = 0;
+ clk_disable(iram_clk);
+ } else
+#endif
+ {
+ dma_free_writecombine(fbi->device, fbi->fix.smem_len,
+ fbi->screen_base, fbi->fix.smem_start);
+ }
+ fbi->screen_base = 0;
+ fbi->fix.smem_start = 0;
+ fbi->fix.smem_len = 0;
+ return 0;
+}
+
+/*!
+ * Initializes the framebuffer information pointer. After allocating
+ * sufficient memory for the framebuffer structure, the fields are
+ * filled with custom information passed in from the configurable
+ * structures. This includes information such as bits per pixel,
+ * color maps, screen width/height and RGBA offsets.
+ *
+ * @return Framebuffer structure initialized with our information
+ */
+static struct fb_info *mxcfb_init_fbinfo(struct device *dev, struct fb_ops *ops)
+{
+ struct fb_info *fbi;
+ struct mxcfb_info *mxcfbi;
+
+ /*
+ * Allocate sufficient memory for the fb structure
+ */
+ fbi = framebuffer_alloc(sizeof(struct mxcfb_info), dev);
+ if (!fbi)
+ return NULL;
+
+ mxcfbi = (struct mxcfb_info *)fbi->par;
+
+ fbi->var.activate = FB_ACTIVATE_NOW;
+
+ fbi->fbops = ops;
+ fbi->flags = FBINFO_FLAG_DEFAULT;
+ fbi->pseudo_palette = mxcfbi->pseudo_palette;
+
+ spin_lock_init(&mxcfbi->fb_lock);
+
+ /*
+ * Allocate colormap
+ */
+ fb_alloc_cmap(&fbi->cmap, 16, 0);
+
+ return fbi;
+}
+
+/*!
+ * Probe routine for the framebuffer driver. It is called during the
+ * driver binding process. The following functions are performed in
+ * this routine: Framebuffer initialization, Memory allocation and
+ * mapping, Framebuffer registration, IPU initialization.
+ *
+ * @return Appropriate error code to the kernel common code
+ */
+static int mxcfb_probe(struct platform_device *pdev)
+{
+ char *mode = pdev->dev.platform_data;
+ struct fb_info *fbi;
+ struct mxcfb_info *mxcfbi;
+ struct fb_info *fbi_ovl;
+ int ret = 0;
+
+ /*
+ * Initialize FB structures
+ */
+ fbi = mxcfb_init_fbinfo(&pdev->dev, &mxcfb_ops);
+ if (!fbi) {
+ ret = -ENOMEM;
+ goto err0;
+ }
+ mxcfbi = (struct mxcfb_info *)fbi->par;
+
+ mxcfbi->ipu_ch_irq = IPU_IRQ_SDC_BG_EOF;
+ mxcfbi->cur_ipu_buf = 0;
+ mxcfbi->ipu_ch = MEM_SDC_BG;
+
+ ipu_sdc_set_global_alpha(true, 0xFF);
+ ipu_sdc_set_color_key(MEM_SDC_BG, false, 0);
+
+ if (ipu_request_irq(IPU_IRQ_SDC_BG_EOF, mxcfb_irq_handler, 0,
+ MXCFB_NAME, fbi) != 0) {
+ dev_err(&pdev->dev, "Error registering BG irq handler.\n");
+ ret = -EBUSY;
+ goto err1;
+ }
+ ipu_disable_irq(IPU_IRQ_SDC_BG_EOF);
+
+ if (fb_mode == NULL) {
+ fb_mode = mode;
+ }
+
+ if (!fb_find_mode(&fbi->var, fbi, fb_mode, mxcfb_modedb,
+ mxcfb_modedb_sz, NULL, default_bpp)) {
+ ret = -EBUSY;
+ goto err2;
+ }
+ fb_videomode_to_modelist(mxcfb_modedb, mxcfb_modedb_sz, &fbi->modelist);
+
+ /* Default Y virtual size is 2x panel size */
+#ifndef CONFIG_FB_MXC_INTERNAL_MEM
+ fbi->var.yres_virtual = fbi->var.yres * 2;
+#endif
+
+ mxcfb_drv_data.fbi = fbi;
+ mxcfb_drv_data.backlight_level = 255;
+ mxcfb_drv_data.suspended = false;
+ init_waitqueue_head(&mxcfb_drv_data.suspend_wq);
+
+ mxcfbi->blank = FB_BLANK_NORMAL;
+ ret = mxcfb_set_par(fbi);
+ if (ret < 0) {
+ goto err2;
+ }
+ mxcfb_blank(FB_BLANK_UNBLANK, fbi);
+
+ /*
+ * Register framebuffer
+ */
+ ret = register_framebuffer(fbi);
+ if (ret < 0) {
+ goto err2;
+ }
+
+ /*
+ * Initialize Overlay FB structures
+ */
+ fbi_ovl = mxcfb_init_fbinfo(&pdev->dev, &mxcfb_ovl_ops);
+ if (!fbi_ovl) {
+ ret = -ENOMEM;
+ goto err3;
+ }
+ mxcfb_drv_data.fbi_ovl = fbi_ovl;
+ mxcfbi = (struct mxcfb_info *)fbi_ovl->par;
+
+ mxcfbi->ipu_ch_irq = IPU_IRQ_SDC_FG_EOF;
+ mxcfbi->cur_ipu_buf = 0;
+ mxcfbi->ipu_ch = MEM_SDC_FG;
+
+ if (ipu_request_irq(IPU_IRQ_SDC_FG_EOF, mxcfb_irq_handler, 0,
+ MXCFB_NAME, fbi_ovl) != 0) {
+ dev_err(fbi->device, "Error registering FG irq handler.\n");
+ ret = -EBUSY;
+ goto err4;
+ }
+ ipu_disable_irq(mxcfbi->ipu_ch_irq);
+
+ /* Default Y virtual size is 2x panel size */
+ fbi_ovl->var = fbi->var;
+ fbi_ovl->var.yres_virtual = fbi->var.yres * 2;
+
+ /* Overlay is blanked by default */
+ mxcfbi->blank = FB_BLANK_NORMAL;
+
+ ret = mxcfb_set_par(fbi_ovl);
+ if (ret < 0) {
+ goto err5;
+ }
+
+ /*
+ * Register overlay framebuffer
+ */
+ ret = register_framebuffer(fbi_ovl);
+ if (ret < 0) {
+ goto err5;
+ }
+
+ platform_set_drvdata(pdev, &mxcfb_drv_data);
+
+ init_waitqueue_head(&mxcfb_drv_data.vsync_wq);
+ if (!cpu_is_mx31() && !cpu_is_mx32()) {
+ ret = ipu_request_irq(IPU_IRQ_SDC_DISP3_VSYNC,
+ mxcfb_vsync_irq_handler,
+ 0, MXCFB_NAME,
+ &mxcfb_drv_data);
+ if (ret < 0)
+ goto err6;
+ ipu_disable_irq(IPU_IRQ_SDC_DISP3_VSYNC);
+ }
+
+ printk(KERN_INFO "mxcfb: fb registered, using mode %s\n", fb_mode);
+ return 0;
+
+ err6:
+ unregister_framebuffer(fbi_ovl);
+ err5:
+ ipu_free_irq(IPU_IRQ_SDC_FG_EOF, fbi_ovl);
+ err4:
+ fb_dealloc_cmap(&fbi_ovl->cmap);
+ framebuffer_release(fbi_ovl);
+ err3:
+ unregister_framebuffer(fbi);
+ err2:
+ ipu_free_irq(IPU_IRQ_SDC_BG_EOF, fbi);
+ err1:
+ fb_dealloc_cmap(&fbi->cmap);
+ framebuffer_release(fbi);
+ err0:
+ printk(KERN_ERR "mxcfb: failed to register fb\n");
+ return ret;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxcfb_driver = {
+ .driver = {
+ .name = MXCFB_NAME,
+ },
+ .probe = mxcfb_probe,
+ .suspend = mxcfb_suspend,
+ .resume = mxcfb_resume,
+};
+
+/*
+ * Parse user specified options (`video=trident:')
+ * example:
+ * video=trident:800x600,bpp=16,noaccel
+ */
+int mxcfb_setup(char *options)
+{
+ char *opt;
+ if (!options || !*options)
+ return 0;
+ while ((opt = strsep(&options, ",")) != NULL) {
+ if (!*opt)
+ continue;
+ if (!strncmp(opt, "bpp=", 4))
+ default_bpp = simple_strtoul(opt + 4, NULL, 0);
+ else
+ fb_mode = opt;
+ }
+ return 0;
+}
+
+/*!
+ * Main entry function for the framebuffer. The function registers the power
+ * management callback functions with the kernel and also registers the MXCFB
+ * callback functions with the core Linux framebuffer driver \b fbmem.c
+ *
+ * @return Error code indicating success or failure
+ */
+int __init mxcfb_init(void)
+{
+ int ret = 0;
+#ifndef MODULE
+ char *option = NULL;
+#endif
+
+#ifndef MODULE
+ if (fb_get_options("mxcfb", &option))
+ return -ENODEV;
+ mxcfb_setup(option);
+#endif
+
+ ret = platform_driver_register(&mxcfb_driver);
+ return ret;
+}
+
+void mxcfb_exit(void)
+{
+ struct fb_info *fbi = mxcfb_drv_data.fbi;
+
+ if (fbi) {
+ mxcfb_unmap_video_memory(fbi);
+
+ if (&fbi->cmap)
+ fb_dealloc_cmap(&fbi->cmap);
+
+ unregister_framebuffer(fbi);
+ framebuffer_release(fbi);
+ }
+
+ fbi = mxcfb_drv_data.fbi_ovl;
+ if (fbi) {
+ mxcfb_unmap_video_memory(fbi);
+
+ if (&fbi->cmap)
+ fb_dealloc_cmap(&fbi->cmap);
+
+ unregister_framebuffer(fbi);
+ framebuffer_release(fbi);
+ }
+#ifndef CONFIG_ARCH_MX3
+ ipu_free_irq(IPU_IRQ_SDC_DISP3_VSYNC, &mxcfb_drv_data);
+#endif
+
+ platform_driver_unregister(&mxcfb_driver);
+}
+
+module_init(mxcfb_init);
+module_exit(mxcfb_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC framebuffer driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("fb");
diff --git a/drivers/video/mxc/mxcfb_ch7026.c b/drivers/video/mxc/mxcfb_ch7026.c
new file mode 100644
index 00000000..6668b88b
--- /dev/null
+++ b/drivers/video/mxc/mxcfb_ch7026.c
@@ -0,0 +1,370 @@
+/*
+ * Copyright 2009-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @defgroup Framebuffer Framebuffer Driver for SDC and ADC.
+ */
+
+/*!
+ * @file mxcfb_epson_vga.c
+ *
+ * @brief MXC Frame buffer driver for SDC
+ *
+ * @ingroup Framebuffer
+ */
+
+/*!
+ * Include files
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/i2c.h>
+#include <linux/mxcfb.h>
+#include <linux/ipu.h>
+#include <linux/fsl_devices.h>
+#include <mach/hardware.h>
+
+static struct i2c_client *ch7026_client;
+
+static int lcd_init(void);
+static void lcd_poweron(struct fb_info *info);
+static void lcd_poweroff(void);
+
+static void (*lcd_reset) (void);
+static struct regulator *io_reg;
+static struct regulator *core_reg;
+static struct regulator *analog_reg;
+
+ /* 8 800x600-60 VESA */
+static struct fb_videomode mode = {
+ NULL, 60, 800, 600, 25000, 88, 40, 23, 1, 128, 4,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA
+};
+
+static void lcd_init_fb(struct fb_info *info)
+{
+ struct fb_var_screeninfo var;
+
+ memset(&var, 0, sizeof(var));
+
+ fb_videomode_to_var(&var, &mode);
+
+ var.activate = FB_ACTIVATE_ALL;
+
+ console_lock();
+ info->flags |= FBINFO_MISC_USEREVENT;
+ fb_set_var(info, &var);
+ fb_blank(info, FB_BLANK_UNBLANK);
+ info->flags &= ~FBINFO_MISC_USEREVENT;
+ console_unlock();
+}
+
+static int lcd_fb_event(struct notifier_block *nb, unsigned long val, void *v)
+{
+ struct fb_event *event = v;
+
+ if (strcmp(event->info->fix.id, "DISP3 BG - DI1"))
+ return 0;
+
+ switch (val) {
+ case FB_EVENT_FB_REGISTERED:
+ lcd_init_fb(event->info);
+ lcd_poweron(event->info);
+ break;
+ case FB_EVENT_BLANK:
+ if (*((int *)event->data) == FB_BLANK_UNBLANK)
+ lcd_poweron(event->info);
+ else
+ lcd_poweroff();
+ break;
+ }
+ return 0;
+}
+
+static struct notifier_block nb = {
+ .notifier_call = lcd_fb_event,
+};
+
+/*!
+ * This function is called whenever the SPI slave device is detected.
+ *
+ * @param spi the SPI slave device
+ *
+ * @return Returns 0 on SUCCESS and error on FAILURE.
+ */
+static int __devinit lcd_probe(struct device *dev)
+{
+ int ret = 0;
+ int i;
+ struct mxc_lcd_platform_data *plat = dev->platform_data;
+
+ if (plat) {
+
+ io_reg = regulator_get(dev, plat->io_reg);
+ if (!IS_ERR(io_reg)) {
+ regulator_set_voltage(io_reg, 1800000, 1800000);
+ regulator_enable(io_reg);
+ } else {
+ io_reg = NULL;
+ }
+
+ core_reg = regulator_get(dev, plat->core_reg);
+ if (!IS_ERR(core_reg)) {
+ regulator_set_voltage(core_reg, 2500000, 2500000);
+ regulator_enable(core_reg);
+ } else {
+ core_reg = NULL;
+ }
+ analog_reg = regulator_get(dev, plat->analog_reg);
+ if (!IS_ERR(analog_reg)) {
+ regulator_set_voltage(analog_reg, 2775000, 2775000);
+ regulator_enable(analog_reg);
+ } else {
+ analog_reg = NULL;
+ }
+ msleep(100);
+
+ lcd_reset = plat->reset;
+ if (lcd_reset)
+ lcd_reset();
+ }
+
+ for (i = 0; i < num_registered_fb; i++) {
+ if (strcmp(registered_fb[i]->fix.id, "DISP3 BG - DI1") == 0) {
+ ret = lcd_init();
+ if (ret < 0)
+ goto err;
+
+ lcd_init_fb(registered_fb[i]);
+ fb_show_logo(registered_fb[i], 0);
+ lcd_poweron(registered_fb[i]);
+ }
+ }
+
+ fb_register_client(&nb);
+ return 0;
+err:
+ if (io_reg)
+ regulator_disable(io_reg);
+ if (core_reg)
+ regulator_disable(core_reg);
+ if (analog_reg)
+ regulator_disable(analog_reg);
+
+ return ret;
+}
+
+static int __devinit ch7026_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ ch7026_client = client;
+
+ return lcd_probe(&client->dev);
+}
+
+static int __devexit ch7026_remove(struct i2c_client *client)
+{
+ fb_unregister_client(&nb);
+ lcd_poweroff();
+ regulator_put(io_reg);
+ regulator_put(core_reg);
+ regulator_put(analog_reg);
+
+ return 0;
+}
+
+static int ch7026_suspend(struct i2c_client *client, pm_message_t message)
+{
+ return 0;
+}
+
+static int ch7026_resume(struct i2c_client *client)
+{
+ return 0;
+}
+
+u8 reg_init[][2] = {
+ { 0x02, 0x01 },
+ { 0x02, 0x03 },
+ { 0x03, 0x00 },
+ { 0x06, 0x6B },
+ { 0x08, 0x08 },
+ { 0x09, 0x80 },
+ { 0x0C, 0x0A },
+ { 0x0D, 0x89 },
+ { 0x0F, 0x23 },
+ { 0x10, 0x20 },
+ { 0x11, 0x20 },
+ { 0x12, 0x40 },
+ { 0x13, 0x28 },
+ { 0x14, 0x80 },
+ { 0x15, 0x52 },
+ { 0x16, 0x58 },
+ { 0x17, 0x74 },
+ { 0x19, 0x01 },
+ { 0x1A, 0x04 },
+ { 0x1B, 0x23 },
+ { 0x1C, 0x20 },
+ { 0x1D, 0x20 },
+ { 0x1F, 0x28 },
+ { 0x20, 0x80 },
+ { 0x21, 0x12 },
+ { 0x22, 0x58 },
+ { 0x23, 0x74 },
+ { 0x25, 0x01 },
+ { 0x26, 0x04 },
+ { 0x37, 0x20 },
+ { 0x39, 0x20 },
+ { 0x3B, 0x20 },
+ { 0x41, 0xA2 },
+ { 0x4D, 0x03 },
+ { 0x4E, 0x13 },
+ { 0x4F, 0xB1 },
+ { 0x50, 0x3B },
+ { 0x51, 0x54 },
+ { 0x52, 0x12 },
+ { 0x53, 0x13 },
+ { 0x55, 0xE5 },
+ { 0x5E, 0x80 },
+ { 0x69, 0x64 },
+ { 0x7D, 0x62 },
+ { 0x04, 0x00 },
+ { 0x06, 0x69 },
+
+ /*
+ NOTE: The following five repeated sentences are used here to wait memory initial complete, please don't remove...(you could refer to Appendix A of programming guide document (CH7025(26)B Programming Guide Rev2.03.pdf) for detailed information about memory initialization!
+ */
+ { 0x03, 0x00 },
+ { 0x03, 0x00 },
+ { 0x03, 0x00 },
+ { 0x03, 0x00 },
+ { 0x03, 0x00 },
+
+ { 0x06, 0x68 },
+ { 0x02, 0x02 },
+ { 0x02, 0x03 },
+};
+
+#define REGMAP_LENGTH (sizeof(reg_init) / (2*sizeof(u8)))
+
+/*
+ * Send init commands to L4F00242T03
+ *
+ */
+static int lcd_init(void)
+{
+ int i;
+ int dat;
+
+ dev_dbg(&ch7026_client->dev, "initializing CH7026\n");
+
+ /* read device ID */
+ msleep(100);
+ dat = i2c_smbus_read_byte_data(ch7026_client, 0x00);
+ dev_dbg(&ch7026_client->dev, "read id = 0x%02X\n", dat);
+ if (dat != 0x54)
+ return -ENODEV;
+
+ for (i = 0; i < REGMAP_LENGTH; ++i) {
+ if (i2c_smbus_write_byte_data
+ (ch7026_client, reg_init[i][0], reg_init[i][1]) < 0)
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int lcd_on;
+/*
+ * Send Power On commands to L4F00242T03
+ *
+ */
+static void lcd_poweron(struct fb_info *info)
+{
+ u16 data[4];
+ u32 refresh;
+
+ if (lcd_on)
+ return;
+
+ dev_dbg(&ch7026_client->dev, "turning on LCD\n");
+
+ data[0] = PICOS2KHZ(info->var.pixclock) / 10;
+ data[2] = info->var.hsync_len + info->var.left_margin +
+ info->var.xres + info->var.right_margin;
+ data[3] = info->var.vsync_len + info->var.upper_margin +
+ info->var.yres + info->var.lower_margin;
+
+ refresh = data[2] * data[3];
+ refresh = (PICOS2KHZ(info->var.pixclock) * 1000) / refresh;
+ data[1] = refresh * 100;
+
+ lcd_on = 1;
+}
+
+/*
+ * Send Power Off commands to L4F00242T03
+ *
+ */
+static void lcd_poweroff(void)
+{
+ if (!lcd_on)
+ return;
+
+ dev_dbg(&ch7026_client->dev, "turning off LCD\n");
+
+ lcd_on = 0;
+}
+
+static const struct i2c_device_id ch7026_id[] = {
+ {"ch7026", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, ch7026_id);
+
+static struct i2c_driver ch7026_driver = {
+ .driver = {
+ .name = "ch7026",
+ },
+ .probe = ch7026_probe,
+ .remove = ch7026_remove,
+ .suspend = ch7026_suspend,
+ .resume = ch7026_resume,
+ .id_table = ch7026_id,
+};
+
+static int __init ch7026_init(void)
+{
+ return i2c_add_driver(&ch7026_driver);
+}
+
+static void __exit ch7026_exit(void)
+{
+ i2c_del_driver(&ch7026_driver);
+}
+
+module_init(ch7026_init);
+module_exit(ch7026_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("CH7026 VGA driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/mxc/mxcfb_claa_wvga.c b/drivers/video/mxc/mxcfb_claa_wvga.c
new file mode 100644
index 00000000..3dbad0dc
--- /dev/null
+++ b/drivers/video/mxc/mxcfb_claa_wvga.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright 2008-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @defgroup Framebuffer Framebuffer Driver for SDC and ADC.
+ */
+
+/*!
+ * @file mxcfb_claa_wvga.c
+ *
+ * @brief MXC Frame buffer driver for SDC
+ *
+ * @ingroup Framebuffer
+ */
+
+/*!
+ * Include files
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fb.h>
+#include <linux/fsl_devices.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/mxcfb.h>
+#include <linux/regulator/consumer.h>
+#include <mach/hardware.h>
+
+static void lcd_poweron(void);
+static void lcd_poweroff(void);
+
+static struct platform_device *plcd_dev;
+static struct regulator *io_reg;
+static struct regulator *core_reg;
+static int lcd_on;
+
+static struct fb_videomode video_modes[] = {
+ {
+ /* 800x480 @ 57 Hz , pixel clk @ 27MHz */
+ "CLAA-WVGA", 57, 800, 480, 37037, 40, 60, 10, 10, 20, 10,
+ FB_SYNC_CLK_LAT_FALL,
+ FB_VMODE_NONINTERLACED,
+ 0,},
+};
+
+static void lcd_init_fb(struct fb_info *info)
+{
+ struct fb_var_screeninfo var;
+
+ memset(&var, 0, sizeof(var));
+
+ fb_videomode_to_var(&var, &video_modes[0]);
+
+ var.activate = FB_ACTIVATE_ALL;
+ var.yres_virtual = var.yres * 3;
+
+ console_lock();
+ info->flags |= FBINFO_MISC_USEREVENT;
+ fb_set_var(info, &var);
+ info->flags &= ~FBINFO_MISC_USEREVENT;
+ console_unlock();
+}
+
+static int lcd_fb_event(struct notifier_block *nb, unsigned long val, void *v)
+{
+ struct fb_event *event = v;
+
+ if (strcmp(event->info->fix.id, "DISP3 BG") &&
+ strcmp(event->info->fix.id, "mxc_elcdif_fb"))
+ return 0;
+
+ switch (val) {
+ case FB_EVENT_FB_REGISTERED:
+ lcd_init_fb(event->info);
+ fb_show_logo(event->info, 0);
+ lcd_poweron();
+ break;
+ case FB_EVENT_BLANK:
+ if ((event->info->var.xres != 800) ||
+ (event->info->var.yres != 480)) {
+ break;
+ }
+ if (*((int *)event->data) == FB_BLANK_UNBLANK) {
+ lcd_poweron();
+ } else {
+ lcd_poweroff();
+ }
+ break;
+ }
+ return 0;
+}
+
+static struct notifier_block nb = {
+ .notifier_call = lcd_fb_event,
+};
+
+/*!
+ * This function is called whenever the SPI slave device is detected.
+ *
+ * @param spi the SPI slave device
+ *
+ * @return Returns 0 on SUCCESS and error on FAILURE.
+ */
+static int __devinit lcd_probe(struct platform_device *pdev)
+{
+ int i;
+ struct mxc_lcd_platform_data *plat = pdev->dev.platform_data;
+
+ if (plat) {
+ if (plat->reset)
+ plat->reset();
+
+ io_reg = regulator_get(&pdev->dev, plat->io_reg);
+ if (IS_ERR(io_reg))
+ io_reg = NULL;
+ core_reg = regulator_get(&pdev->dev, plat->core_reg);
+ if (!IS_ERR(core_reg)) {
+ regulator_set_voltage(io_reg, 1800000, 1800000);
+ } else {
+ core_reg = NULL;
+ }
+ }
+
+ for (i = 0; i < num_registered_fb; i++) {
+ if (strcmp(registered_fb[i]->fix.id, "DISP3 BG") == 0 ||
+ strcmp(registered_fb[i]->fix.id, "mxc_elcdif_fb") == 0) {
+ lcd_init_fb(registered_fb[i]);
+ fb_show_logo(registered_fb[i], 0);
+ lcd_poweron();
+ } else if (strcmp(registered_fb[i]->fix.id, "DISP3 FG") == 0) {
+ lcd_init_fb(registered_fb[i]);
+ }
+ }
+
+ fb_register_client(&nb);
+
+ plcd_dev = pdev;
+
+ return 0;
+}
+
+static int __devexit lcd_remove(struct platform_device *pdev)
+{
+ fb_unregister_client(&nb);
+ lcd_poweroff();
+ if (io_reg)
+ regulator_put(io_reg);
+ if (core_reg)
+ regulator_put(core_reg);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int lcd_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ return 0;
+}
+
+static int lcd_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+#else
+#define lcd_suspend NULL
+#define lcd_resume NULL
+#endif
+
+/*!
+ * platform driver structure for CLAA WVGA
+ */
+static struct platform_driver lcd_driver = {
+ .driver = {
+ .name = "lcd_claa"},
+ .probe = lcd_probe,
+ .remove = __devexit_p(lcd_remove),
+ .suspend = lcd_suspend,
+ .resume = lcd_resume,
+};
+
+/*
+ * Send Power On commands to L4F00242T03
+ *
+ */
+static void lcd_poweron(void)
+{
+ if (lcd_on)
+ return;
+
+ dev_dbg(&plcd_dev->dev, "turning on LCD\n");
+ if (core_reg)
+ regulator_enable(core_reg);
+ if (io_reg)
+ regulator_enable(io_reg);
+ lcd_on = 1;
+}
+
+/*
+ * Send Power Off commands to L4F00242T03
+ *
+ */
+static void lcd_poweroff(void)
+{
+ lcd_on = 0;
+ dev_dbg(&plcd_dev->dev, "turning off LCD\n");
+ if (io_reg)
+ regulator_disable(io_reg);
+ if (core_reg)
+ regulator_disable(core_reg);
+}
+
+static int __init claa_lcd_init(void)
+{
+ return platform_driver_register(&lcd_driver);
+}
+
+static void __exit claa_lcd_exit(void)
+{
+ platform_driver_unregister(&lcd_driver);
+}
+
+module_init(claa_lcd_init);
+module_exit(claa_lcd_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("CLAA WVGA LCD init driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/mxc/mxcfb_epson.c b/drivers/video/mxc/mxcfb_epson.c
new file mode 100644
index 00000000..f7d191fa
--- /dev/null
+++ b/drivers/video/mxc/mxcfb_epson.c
@@ -0,0 +1,1153 @@
+/*
+ * Copyright 2004-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file mxcfb_epson.c
+ *
+ * @brief MXC Frame buffer driver for ADC
+ *
+ * @ingroup Framebuffer
+ */
+
+/*!
+ * Include files
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/fb.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <mach/hardware.h>
+#include <asm/io.h>
+#include <asm/mach-types.h>
+#include <asm/uaccess.h>
+#include <mach/ipu.h>
+#include <mach/mxcfb.h>
+
+#define PARTIAL_REFRESH
+#define MXCFB_REFRESH_DEFAULT MXCFB_REFRESH_PARTIAL
+/*
+ * Driver name
+ */
+#define MXCFB_NAME "MXCFB_EPSON"
+
+#define MXCFB_SCREEN_TOP_OFFSET 0
+#define MXCFB_SCREEN_LEFT_OFFSET 2
+#define MXCFB_SCREEN_WIDTH 176
+#define MXCFB_SCREEN_HEIGHT 220
+
+/*!
+ * Enum defining Epson panel commands.
+ */
+enum {
+ DISON = 0xAF,
+ DISOFF = 0xAE,
+ DISCTL = 0xCA,
+ SD_CSET = 0x15,
+ SD_PSET = 0x75,
+ DATCTL = 0xBC,
+ SLPIN = 0x95,
+ SLPOUT = 0x94,
+ DISNOR = 0xA6,
+ RAMWR = 0x5C,
+ VOLCTR = 0xC6,
+ GCP16 = 0xCC,
+ GCP64 = 0xCB,
+};
+
+struct mxcfb_info {
+ int open_count;
+ int blank;
+ uint32_t disp_num;
+
+ u32 pseudo_palette[16];
+
+ int32_t cur_update_mode;
+ dma_addr_t alloc_start_paddr;
+ void *alloc_start_vaddr;
+ u32 alloc_size;
+ uint32_t snoop_window_size;
+};
+
+struct mxcfb_data {
+ struct fb_info *fbi;
+ volatile int32_t vsync_flag;
+ wait_queue_head_t vsync_wq;
+ wait_queue_head_t suspend_wq;
+ bool suspended;
+};
+
+static struct mxcfb_data mxcfb_drv_data;
+static unsigned long default_bpp = 16;
+
+void slcd_gpio_config(void);
+extern void gpio_lcd_active(void);
+static int mxcfb_blank(int blank, struct fb_info *fbi);
+
+static uint32_t bpp_to_pixfmt(int bpp)
+{
+ uint32_t pixfmt = 0;
+ switch (bpp) {
+ case 24:
+ pixfmt = IPU_PIX_FMT_BGR24;
+ break;
+ case 32:
+ pixfmt = IPU_PIX_FMT_BGR32;
+ break;
+ case 16:
+ pixfmt = IPU_PIX_FMT_RGB565;
+ break;
+ }
+ return pixfmt;
+}
+
+/*!
+ * This function sets display region in the Epson panel
+ *
+ * @param disp display panel to config
+ * @param x1 x-coordinate of one vertex.
+ * @param x2 x-coordinate of second vertex.
+ * @param y1 y-coordinate of one vertex.
+ * @param y2 y-coordinate of second vertex.
+ */
+void set_panel_region(int disp, uint32_t x1, uint32_t x2,
+ uint32_t y1, uint32_t y2)
+{
+ uint32_t param[8];
+
+ memset(param, 0, sizeof(uint32_t) * 8);
+ param[0] = x1;
+ param[2] = x2;
+ param[4] = y1;
+ param[6] = y2;
+
+ /* SD_CSET */
+ ipu_adc_write_cmd(disp, CMD, SD_CSET, param, 4);
+
+ /* SD_PSET */
+ ipu_adc_write_cmd(disp, CMD, SD_PSET, &(param[4]), 4);
+}
+
+/*!
+ * Function to create and initiate template command buffer for ADC. This
+ * template will be written to Panel memory.
+ */
+static void init_channel_template(int disp)
+{
+ /* template command buffer for ADC is 32 */
+ uint32_t tempCmd[TEMPLATE_BUF_SIZE];
+ uint32_t i = 0;
+
+ memset(tempCmd, 0, sizeof(uint32_t) * TEMPLATE_BUF_SIZE);
+ /* setup update display region */
+ /* whole the screen during init */
+ /*WRITE Y COORDINATE CMND */
+ tempCmd[i++] = ipu_adc_template_gen(WR_CMND, 0, SINGLE_STEP, SD_PSET);
+ /*WRITE Y START ADDRESS CMND LSB[22:8] */
+ tempCmd[i++] = ipu_adc_template_gen(WR_YADDR, 1, SINGLE_STEP, 0x01);
+ /*WRITE Y START ADDRESS CMND MSB[22:16] */
+ tempCmd[i++] = ipu_adc_template_gen(WR_YADDR, 1, SINGLE_STEP, 0x09);
+ /*WRITE Y STOP ADDRESS CMND LSB */
+ tempCmd[i++] = ipu_adc_template_gen(WR_CMND, 1, SINGLE_STEP,
+ MXCFB_SCREEN_HEIGHT - 1);
+ /*WRITE Y STOP ADDRESS CMND MSB */
+ tempCmd[i++] = ipu_adc_template_gen(WR_CMND, 1, SINGLE_STEP, 0);
+ /*WRITE X COORDINATE CMND */
+ tempCmd[i++] = ipu_adc_template_gen(WR_CMND, 0, SINGLE_STEP, SD_CSET);
+ /*WRITE X ADDRESS CMND LSB[7:0] */
+ tempCmd[i++] = ipu_adc_template_gen(WR_XADDR, 1, SINGLE_STEP, 0x01);
+ /*WRITE X ADDRESS CMND MSB[22:8] */
+ tempCmd[i++] = ipu_adc_template_gen(WR_CMND, 1, SINGLE_STEP, 0);
+ /*WRITE X STOP ADDRESS CMND LSB */
+ tempCmd[i++] = ipu_adc_template_gen(WR_CMND, 1, SINGLE_STEP,
+ MXCFB_SCREEN_WIDTH + 1);
+ /*WRITE X STOP ADDRESS CMND MSB */
+ tempCmd[i++] = ipu_adc_template_gen(WR_CMND, 1, SINGLE_STEP, 0);
+ /*WRITE MEMORY CMND MSB */
+ tempCmd[i++] = ipu_adc_template_gen(WR_CMND, 0, SINGLE_STEP, RAMWR);
+ /*WRITE DATA CMND and STP */
+ tempCmd[i++] = ipu_adc_template_gen(WR_DATA, 1, STOP, 0);
+
+ ipu_adc_write_template(disp, tempCmd, true);
+}
+
+/*!
+ * Function to initialize the panel. First it resets the panel and then
+ * initilizes panel.
+ */
+static void _init_panel(int disp)
+{
+ uint32_t cmd_param;
+ uint32_t i;
+
+ gpio_lcd_active();
+ slcd_gpio_config();
+
+ /* DATCTL */
+#ifdef CONFIG_FB_MXC_ASYNC_PANEL_IFC_16_BIT
+ /* 16-bit 565 mode */
+ cmd_param = 0x28;
+#else
+ /* 8-bit 666 mode */
+ cmd_param = 0x08;
+#endif
+ ipu_adc_write_cmd(disp, CMD, DATCTL, &cmd_param, 1);
+
+ /* Sleep OUT */
+ ipu_adc_write_cmd(disp, CMD, SLPOUT, 0, 0);
+
+ /* Set display to white
+ Setup page and column addresses */
+ set_panel_region(disp, MXCFB_SCREEN_LEFT_OFFSET,
+ MXCFB_SCREEN_WIDTH + MXCFB_SCREEN_LEFT_OFFSET - 1,
+ 0, MXCFB_SCREEN_HEIGHT - 1);
+ /* Do RAM write cmd */
+ ipu_adc_write_cmd(disp, CMD, RAMWR, 0, 0);
+#ifdef CONFIG_FB_MXC_ASYNC_PANEL_IFC_16_BIT
+ for (i = 0; i < (MXCFB_SCREEN_WIDTH * MXCFB_SCREEN_HEIGHT); i++)
+#else
+ for (i = 0; i < (MXCFB_SCREEN_WIDTH * MXCFB_SCREEN_HEIGHT * 3); i++)
+#endif
+ ipu_adc_write_cmd(disp, DAT, 0xFFFF, 0, 0);
+
+ /* Pause 80 ms */
+ mdelay(80);
+
+ /* Display ON */
+ ipu_adc_write_cmd(disp, CMD, DISON, 0, 0);
+ /* Pause 200 ms */
+ mdelay(200);
+
+ pr_debug("initialized panel\n");
+}
+
+#ifdef PARTIAL_REFRESH
+static irqreturn_t mxcfb_sys2_eof_irq_handler(int irq, void *dev_id)
+{
+ ipu_channel_params_t params;
+ struct fb_info *fbi = dev_id;
+ struct mxcfb_info *mxc_fbi = fbi->par;
+ uint32_t stat[2], seg_size;
+ uint32_t lsb, msb;
+ uint32_t update_height, start_line, start_addr, end_line, end_addr;
+ uint32_t stride_pixels = (fbi->fix.line_length * 8) /
+ fbi->var.bits_per_pixel;
+
+ ipu_adc_get_snooping_status(&stat[0], &stat[1]);
+
+ if (!stat[0] && !stat[1]) {
+ dev_err(fbi->device, "error no bus snooping bits set\n");
+ return IRQ_HANDLED;
+ }
+ ipu_disable_irq(IPU_IRQ_ADC_SYS2_EOF);
+
+ lsb = ffs(stat[0]);
+ if (lsb) {
+ lsb--;
+ } else {
+ lsb = ffs(stat[1]);
+ lsb += 32 - 1;
+ }
+ msb = fls(stat[1]);
+ if (msb) {
+ msb += 32;
+ } else {
+ msb = fls(stat[0]);
+ }
+
+ seg_size = mxc_fbi->snoop_window_size / 64;
+
+ start_addr = lsb * seg_size; /* starting address offset */
+ start_line = start_addr / fbi->fix.line_length;
+ start_addr = start_line * fbi->fix.line_length; /* Addr aligned to line */
+ start_addr += fbi->fix.smem_start;
+
+ end_addr = msb * seg_size; /* ending address offset */
+ end_line = end_addr / fbi->fix.line_length;
+ end_line++;
+
+ if (end_line > fbi->var.yres) {
+ end_line = fbi->var.yres;
+ }
+
+ update_height = end_line - start_line;
+ dev_dbg(fbi->device, "updating rows %d to %d, start addr = 0x%08X\n",
+ start_line, end_line, start_addr);
+
+ ipu_uninit_channel(ADC_SYS1);
+ params.adc_sys1.disp = mxc_fbi->disp_num;
+ params.adc_sys1.ch_mode = WriteTemplateNonSeq;
+ params.adc_sys1.out_left = MXCFB_SCREEN_LEFT_OFFSET;
+ params.adc_sys1.out_top = start_line;
+ ipu_init_channel(ADC_SYS1, &params);
+
+ ipu_init_channel_buffer(ADC_SYS1, IPU_INPUT_BUFFER,
+ bpp_to_pixfmt(fbi->var.bits_per_pixel),
+ MXCFB_SCREEN_WIDTH,
+ update_height,
+ stride_pixels,
+ IPU_ROTATE_NONE, (dma_addr_t) start_addr, 0,
+ 0, 0);
+ ipu_enable_channel(ADC_SYS1);
+ ipu_select_buffer(ADC_SYS1, IPU_INPUT_BUFFER, 0);
+ ipu_enable_irq(IPU_IRQ_ADC_SYS1_EOF);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mxcfb_sys1_eof_irq_handler(int irq, void *dev_id)
+{
+ ipu_disable_irq(IPU_IRQ_ADC_SYS1_EOF);
+ ipu_disable_channel(ADC_SYS1, false);
+
+ ipu_enable_channel(ADC_SYS2);
+ ipu_enable_irq(IPU_IRQ_ADC_SYS2_EOF);
+
+ return IRQ_HANDLED;
+}
+#endif
+
+/*!
+ * Function to initialize Asynchronous Display Controller. It also initilizes
+ * the ADC System 1 channel. Configure ADC display 0 parallel interface for
+ * the panel.
+ *
+ * @param fbi framebuffer information pointer
+ */
+static void mxcfb_init_panel(struct fb_info *fbi)
+{
+ int msb;
+ int panel_stride;
+ ipu_channel_params_t params;
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
+
+#ifdef CONFIG_FB_MXC_ASYNC_PANEL_IFC_16_BIT
+ uint32_t pix_fmt = IPU_PIX_FMT_RGB565;
+ ipu_adc_sig_cfg_t sig = { 0, 0, 0, 0, 0, 0, 0, 0,
+ IPU_ADC_BURST_WCS,
+ IPU_ADC_IFC_MODE_SYS80_TYPE2,
+ 16, 0, 0, IPU_ADC_SER_NO_RW
+ };
+ mxc_fbi->disp_num = DISP0;
+#elif defined(CONFIG_FB_MXC_ASYNC_PANEL_IFC_8_BIT)
+ uint32_t pix_fmt = IPU_PIX_FMT_RGB666;
+ ipu_adc_sig_cfg_t sig = { 0, 0, 0, 0, 0, 0, 0, 0,
+ IPU_ADC_BURST_WCS,
+ IPU_ADC_IFC_MODE_SYS80_TYPE2,
+ 8, 0, 0, IPU_ADC_SER_NO_RW
+ };
+ mxc_fbi->disp_num = DISP0;
+#else
+ uint32_t pix_fmt = IPU_PIX_FMT_RGB565;
+ ipu_adc_sig_cfg_t sig = { 0, 1, 0, 0, 0, 0, 0, 0,
+ IPU_ADC_BURST_SERIAL,
+ IPU_ADC_IFC_MODE_5WIRE_SERIAL_CLK,
+ 16, 0, 0, IPU_ADC_SER_NO_RW
+ };
+ fbi->disp_num = DISP1;
+#endif
+
+#ifdef PARTIAL_REFRESH
+ if (ipu_request_irq(IPU_IRQ_ADC_SYS2_EOF, mxcfb_sys2_eof_irq_handler, 0,
+ MXCFB_NAME, fbi) != 0) {
+ dev_err(fbi->device, "Error registering SYS2 irq handler.\n");
+ return;
+ }
+
+ if (ipu_request_irq(IPU_IRQ_ADC_SYS1_EOF, mxcfb_sys1_eof_irq_handler, 0,
+ MXCFB_NAME, fbi) != 0) {
+ dev_err(fbi->device, "Error registering SYS1 irq handler.\n");
+ return;
+ }
+ ipu_disable_irq(IPU_IRQ_ADC_SYS1_EOF);
+ ipu_disable_irq(IPU_IRQ_ADC_SYS2_EOF);
+#endif
+ /* Init DI interface */
+ msb = fls(MXCFB_SCREEN_WIDTH);
+ if (!(MXCFB_SCREEN_WIDTH & ((1UL << msb) - 1)))
+ msb--; /* Already aligned to power 2 */
+ panel_stride = 1UL << msb;
+ ipu_adc_init_panel(mxc_fbi->disp_num,
+ MXCFB_SCREEN_WIDTH + MXCFB_SCREEN_LEFT_OFFSET,
+ MXCFB_SCREEN_HEIGHT,
+ pix_fmt, panel_stride, sig, XY, 0, VsyncInternal);
+
+ ipu_adc_init_ifc_timing(mxc_fbi->disp_num, true,
+ 190, 17, 104, 190, 5000000);
+ ipu_adc_init_ifc_timing(mxc_fbi->disp_num, false, 123, 17, 68, 0, 0);
+
+ /* Needed to turn on ADC clock for panel init */
+ memset(&params, 0, sizeof(params));
+ params.adc_sys1.disp = mxc_fbi->disp_num;
+ params.adc_sys1.ch_mode = WriteTemplateNonSeq;
+ params.adc_sys1.out_left = MXCFB_SCREEN_LEFT_OFFSET;
+ params.adc_sys1.out_top = MXCFB_SCREEN_TOP_OFFSET;
+ ipu_init_channel(ADC_SYS1, &params);
+
+ _init_panel(mxc_fbi->disp_num);
+ init_channel_template(mxc_fbi->disp_num);
+}
+
+int mxcfb_set_refresh_mode(struct fb_info *fbi, int mode,
+ struct mxcfb_rect *update_region)
+{
+ unsigned long start_addr;
+ int ret_mode;
+ uint32_t dummy;
+ ipu_channel_params_t params;
+ struct mxcfb_rect rect;
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
+ uint32_t stride_pixels = (fbi->fix.line_length * 8) /
+ fbi->var.bits_per_pixel;
+ uint32_t memsize = fbi->fix.smem_len;
+
+ if (mxc_fbi->cur_update_mode == mode)
+ return mode;
+
+ ret_mode = mxc_fbi->cur_update_mode;
+
+ ipu_disable_irq(IPU_IRQ_ADC_SYS1_EOF);
+ ipu_adc_set_update_mode(ADC_SYS1, IPU_ADC_REFRESH_NONE, 0, 0, 0);
+#ifdef PARTIAL_REFRESH
+ ipu_disable_irq(IPU_IRQ_ADC_SYS2_EOF);
+ ipu_adc_set_update_mode(ADC_SYS2, IPU_ADC_REFRESH_NONE, 0, 0, 0);
+#endif
+
+ ipu_disable_channel(ADC_SYS1, true);
+ ipu_clear_irq(IPU_IRQ_ADC_SYS1_EOF);
+#ifdef PARTIAL_REFRESH
+ ipu_disable_channel(ADC_SYS2, true);
+ ipu_clear_irq(IPU_IRQ_ADC_SYS2_EOF);
+#endif
+ ipu_adc_get_snooping_status(&dummy, &dummy);
+
+ mxc_fbi->cur_update_mode = mode;
+
+ switch (mode) {
+ case MXCFB_REFRESH_OFF:
+ if (ipu_adc_set_update_mode(ADC_SYS1, IPU_ADC_REFRESH_NONE,
+ 0, 0, 0) < 0)
+ dev_err(fbi->device, "Error enabling auto refesh.\n");
+ if (ipu_adc_set_update_mode(ADC_SYS2, IPU_ADC_REFRESH_NONE,
+ 0, 0, 0) < 0)
+ dev_err(fbi->device, "Error enabling auto refesh.\n");
+#if 0
+ ipu_init_channel_buffer(ADC_SYS2, IPU_INPUT_BUFFER,
+ bpp_to_pixfmt(fbi->var.bits_per_pixel),
+ 1, 1, 4,
+ IPU_ROTATE_NONE,
+ fbi->fix.smem_start,
+ fbi->fix.smem_start, 0, 0);
+ ipu_enable_channel(ADC_SYS2);
+ ipu_select_buffer(ADC_SYS2, IPU_INPUT_BUFFER, 0);
+ ipu_select_buffer(ADC_SYS2, IPU_INPUT_BUFFER, 1);
+ msleep(10);
+#endif
+ ipu_uninit_channel(ADC_SYS1);
+#ifdef PARTIAL_REFRESH
+ ipu_uninit_channel(ADC_SYS2);
+#endif
+ break;
+ case MXCFB_REFRESH_PARTIAL:
+#ifdef PARTIAL_REFRESH
+ ipu_adc_get_snooping_status(&dummy, &dummy);
+
+ params.adc_sys2.disp = DISP0;
+ params.adc_sys2.ch_mode = WriteTemplateNonSeq;
+ params.adc_sys2.out_left = 0;
+ params.adc_sys2.out_top = 0;
+ ipu_init_channel(ADC_SYS2, &params);
+
+ if (ipu_adc_set_update_mode(ADC_SYS1, IPU_ADC_REFRESH_NONE,
+ 0, 0, 0) < 0) {
+ dev_err(fbi->device, "Error enabling auto refesh.\n");
+ }
+ if (ipu_adc_set_update_mode
+ (ADC_SYS2, IPU_ADC_AUTO_REFRESH_SNOOP, 30,
+ fbi->fix.smem_start, &memsize) < 0) {
+ dev_err(fbi->device, "Error enabling auto refesh.\n");
+ }
+ mxc_fbi->snoop_window_size = memsize;
+
+ ipu_init_channel_buffer(ADC_SYS2, IPU_INPUT_BUFFER,
+ bpp_to_pixfmt(fbi->var.bits_per_pixel),
+ 1, 1, 4,
+ IPU_ROTATE_NONE,
+ fbi->fix.smem_start, 0, 0, 0);
+
+ params.adc_sys1.disp = mxc_fbi->disp_num;
+ params.adc_sys1.ch_mode = WriteTemplateNonSeq;
+ params.adc_sys1.out_left = MXCFB_SCREEN_LEFT_OFFSET;
+ params.adc_sys1.out_top = MXCFB_SCREEN_TOP_OFFSET;
+ ipu_init_channel(ADC_SYS1, &params);
+
+ ipu_init_channel_buffer(ADC_SYS1, IPU_INPUT_BUFFER,
+ bpp_to_pixfmt(fbi->var.bits_per_pixel),
+ MXCFB_SCREEN_WIDTH, MXCFB_SCREEN_HEIGHT,
+ stride_pixels, IPU_ROTATE_NONE,
+ fbi->fix.smem_start, 0, 0, 0);
+ ipu_enable_channel(ADC_SYS1);
+ ipu_select_buffer(ADC_SYS1, IPU_INPUT_BUFFER, 0);
+ ipu_enable_irq(IPU_IRQ_ADC_SYS1_EOF);
+ break;
+#endif
+ case MXCFB_REFRESH_AUTO:
+ if (update_region == NULL) {
+ update_region = &rect;
+ rect.top = 0;
+ rect.left = 0;
+ rect.height = MXCFB_SCREEN_HEIGHT;
+ rect.width = MXCFB_SCREEN_WIDTH;
+ }
+ params.adc_sys1.disp = mxc_fbi->disp_num;
+ params.adc_sys1.ch_mode = WriteTemplateNonSeq;
+ params.adc_sys1.out_left = MXCFB_SCREEN_LEFT_OFFSET +
+ update_region->left;
+ params.adc_sys1.out_top = MXCFB_SCREEN_TOP_OFFSET +
+ update_region->top;
+ ipu_init_channel(ADC_SYS1, &params);
+
+ /* Address aligned to line */
+ start_addr = update_region->top * fbi->fix.line_length;
+ start_addr += fbi->fix.smem_start;
+ start_addr += update_region->left * fbi->var.bits_per_pixel / 8;
+
+ ipu_init_channel_buffer(ADC_SYS1, IPU_INPUT_BUFFER,
+ bpp_to_pixfmt(fbi->var.bits_per_pixel),
+ update_region->width,
+ update_region->height, stride_pixels,
+ IPU_ROTATE_NONE, start_addr, 0, 0, 0);
+ ipu_enable_channel(ADC_SYS1);
+ ipu_select_buffer(ADC_SYS1, IPU_INPUT_BUFFER, 0);
+
+ if (ipu_adc_set_update_mode
+ (ADC_SYS1, IPU_ADC_AUTO_REFRESH_SNOOP, 30,
+ fbi->fix.smem_start, &memsize) < 0)
+ dev_err(fbi->device, "Error enabling auto refesh.\n");
+
+ mxc_fbi->snoop_window_size = memsize;
+
+ break;
+ }
+ return ret_mode;
+}
+
+/*
+ * Open the main framebuffer.
+ *
+ * @param fbi framebuffer information pointer
+ *
+ * @param user Set if opened by user or clear if opened by kernel
+ */
+static int mxcfb_open(struct fb_info *fbi, int user)
+{
+ int retval = 0;
+ struct mxcfb_info *mxc_fbi = fbi->par;
+
+ retval = wait_event_interruptible(mxcfb_drv_data.suspend_wq,
+ (mxcfb_drv_data.suspended == false));
+ if (retval < 0)
+ return retval;
+
+ mxc_fbi->open_count++;
+
+ retval = mxcfb_blank(FB_BLANK_UNBLANK, fbi);
+ return retval;
+}
+
+/*
+ * Close the main framebuffer.
+ *
+ * @param fbi framebuffer information pointer
+ *
+ * @param user Set if opened by user or clear if opened by kernel
+ */
+static int mxcfb_release(struct fb_info *fbi, int user)
+{
+ int retval = 0;
+ struct mxcfb_info *mxc_fbi = fbi->par;
+
+ retval = wait_event_interruptible(mxcfb_drv_data.suspend_wq,
+ (mxcfb_drv_data.suspended == false));
+ if (retval < 0)
+ return retval;
+
+ --mxc_fbi->open_count;
+ if (mxc_fbi->open_count == 0) {
+ retval = mxcfb_blank(FB_BLANK_POWERDOWN, fbi);
+ }
+ return retval;
+}
+
+/*
+ * Set fixed framebuffer parameters based on variable settings.
+ *
+ * @param info framebuffer information pointer
+ */
+static int mxcfb_set_fix(struct fb_info *info)
+{
+ struct fb_fix_screeninfo *fix = &info->fix;
+ struct fb_var_screeninfo *var = &info->var;
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)info->par;
+
+ /* Set framebuffer id to IPU display number. */
+ strcpy(fix->id, "DISP0 FB");
+ fix->id[4] = '0' + mxc_fbi->disp_num;
+
+ /* Init settings based on the panel size */
+ fix->line_length = MXCFB_SCREEN_WIDTH * var->bits_per_pixel / 8;
+
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->accel = FB_ACCEL_NONE;
+ fix->visual = FB_VISUAL_TRUECOLOR;
+ fix->xpanstep = 0;
+ fix->ypanstep = 0;
+
+ return 0;
+}
+
+/*
+ * Set framebuffer parameters and change the operating mode.
+ *
+ * @param info framebuffer information pointer
+ */
+static int mxcfb_set_par(struct fb_info *fbi)
+{
+ int retval = 0;
+ int mode;
+
+ retval = wait_event_interruptible(mxcfb_drv_data.suspend_wq,
+ (mxcfb_drv_data.suspended == false));
+ if (retval < 0)
+ return retval;
+
+ mode = mxcfb_set_refresh_mode(fbi, MXCFB_REFRESH_OFF, NULL);
+
+ mxcfb_set_fix(fbi);
+
+ if (mode != MXCFB_REFRESH_OFF) {
+#ifdef PARTIAL_REFRESH
+ mxcfb_set_refresh_mode(fbi, MXCFB_REFRESH_PARTIAL, NULL);
+#else
+ mxcfb_set_refresh_mode(fbi, MXCFB_REFRESH_AUTO, NULL);
+#endif
+ }
+ return 0;
+}
+
+/*
+ * Check framebuffer variable parameters and adjust to valid values.
+ *
+ * @param var framebuffer variable parameters
+ *
+ * @param info framebuffer information pointer
+ */
+static int mxcfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi)
+{
+ if (var->xres > MXCFB_SCREEN_WIDTH)
+ var->xres = MXCFB_SCREEN_WIDTH;
+ if (var->yres > MXCFB_SCREEN_HEIGHT)
+ var->yres = MXCFB_SCREEN_HEIGHT;
+ if (var->xres_virtual < var->xres)
+ var->xres_virtual = var->xres;
+ if (var->yres_virtual < var->yres)
+ var->yres_virtual = var->yres;
+
+ if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) &&
+ (var->bits_per_pixel != 16)) {
+ var->bits_per_pixel = default_bpp;
+ }
+
+ switch (var->bits_per_pixel) {
+ case 16:
+ var->red.length = 5;
+ var->red.offset = 11;
+ var->red.msb_right = 0;
+
+ var->green.length = 6;
+ var->green.offset = 5;
+ var->green.msb_right = 0;
+
+ var->blue.length = 5;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ var->transp.msb_right = 0;
+ break;
+ case 24:
+ var->red.length = 8;
+ var->red.offset = 16;
+ var->red.msb_right = 0;
+
+ var->green.length = 8;
+ var->green.offset = 8;
+ var->green.msb_right = 0;
+
+ var->blue.length = 8;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ var->transp.msb_right = 0;
+ break;
+ case 32:
+ var->red.length = 8;
+ var->red.offset = 16;
+ var->red.msb_right = 0;
+
+ var->green.length = 8;
+ var->green.offset = 8;
+ var->green.msb_right = 0;
+
+ var->blue.length = 8;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 8;
+ var->transp.offset = 24;
+ var->transp.msb_right = 0;
+ break;
+ }
+
+ var->height = -1;
+ var->width = -1;
+ var->grayscale = 0;
+ var->nonstd = 0;
+
+ var->pixclock = -1;
+ var->left_margin = -1;
+ var->right_margin = -1;
+ var->upper_margin = -1;
+ var->lower_margin = -1;
+ var->hsync_len = -1;
+ var->vsync_len = -1;
+
+ var->vmode = FB_VMODE_NONINTERLACED;
+ var->sync = 0;
+
+ return 0;
+}
+
+static inline u_int _chan_to_field(u_int chan, struct fb_bitfield *bf)
+{
+ chan &= 0xffff;
+ chan >>= 16 - bf->length;
+ return chan << bf->offset;
+}
+
+static int
+mxcfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int trans, struct fb_info *fbi)
+{
+ unsigned int val;
+ int ret = 1;
+
+ /*
+ * If greyscale is true, then we convert the RGB value
+ * to greyscale no matter what visual we are using.
+ */
+ if (fbi->var.grayscale)
+ red = green = blue = (19595 * red + 38470 * green +
+ 7471 * blue) >> 16;
+ switch (fbi->fix.visual) {
+ case FB_VISUAL_TRUECOLOR:
+ /*
+ * 16-bit True Colour. We encode the RGB value
+ * according to the RGB bitfield information.
+ */
+ if (regno < 16) {
+ u32 *pal = fbi->pseudo_palette;
+
+ val = _chan_to_field(red, &fbi->var.red);
+ val |= _chan_to_field(green, &fbi->var.green);
+ val |= _chan_to_field(blue, &fbi->var.blue);
+
+ pal[regno] = val;
+ ret = 0;
+ }
+ break;
+
+ case FB_VISUAL_STATIC_PSEUDOCOLOR:
+ case FB_VISUAL_PSEUDOCOLOR:
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * mxcfb_blank():
+ * Blank the display.
+ */
+static int mxcfb_blank(int blank, struct fb_info *fbi)
+{
+ int retval = 0;
+ struct mxcfb_info *mxc_fbi = fbi->par;
+
+ dev_dbg(fbi->device, "blank = %d\n", blank);
+
+ retval = wait_event_interruptible(mxcfb_drv_data.suspend_wq,
+ (mxcfb_drv_data.suspended == false));
+ if (retval < 0)
+ return retval;
+
+ mxc_fbi->blank = blank;
+
+ switch (blank) {
+ case FB_BLANK_POWERDOWN:
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_HSYNC_SUSPEND:
+ case FB_BLANK_NORMAL:
+ mxcfb_set_refresh_mode(fbi, MXCFB_REFRESH_OFF, NULL);
+ break;
+ case FB_BLANK_UNBLANK:
+ mxcfb_set_refresh_mode(fbi, MXCFB_REFRESH_DEFAULT, NULL);
+ break;
+ }
+ return 0;
+}
+
+/*!
+ * This structure contains the pointers to the control functions that are
+ * invoked by the core framebuffer driver to perform operations like
+ * blitting, rectangle filling, copy regions and cursor definition.
+ */
+static struct fb_ops mxcfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_open = mxcfb_open,
+ .fb_release = mxcfb_release,
+ .fb_set_par = mxcfb_set_par,
+ .fb_check_var = mxcfb_check_var,
+ .fb_setcolreg = mxcfb_setcolreg,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_blank = mxcfb_blank,
+};
+
+/*!
+ * Allocates the DRAM memory for the frame buffer. This buffer is remapped
+ * into a non-cached, non-buffered, memory region to allow palette and pixel
+ * writes to occur without flushing the cache. Once this area is remapped,
+ * all virtual memory access to the video memory should occur at the new region.
+ *
+ * @param fbi framebuffer information pointer
+ *
+ * @return Error code indicating success or failure
+ */
+static int mxcfb_map_video_memory(struct fb_info *fbi)
+{
+ u32 msb;
+ u32 offset;
+ struct mxcfb_info *mxcfbi = fbi->par;
+
+ fbi->fix.smem_len = fbi->var.xres_virtual * fbi->var.yres_virtual * 4;
+
+ /* Set size to power of 2. */
+ msb = fls(fbi->fix.smem_len);
+ if (!(fbi->fix.smem_len & ((1UL << msb) - 1)))
+ msb--; /* Already aligned to power 2 */
+ if (msb < 11)
+ msb = 11;
+ mxcfbi->alloc_size = (1UL << msb) * 2;
+
+ mxcfbi->alloc_start_vaddr = dma_alloc_coherent(fbi->device,
+ mxcfbi->alloc_size,
+ &mxcfbi->
+ alloc_start_paddr,
+ GFP_KERNEL | GFP_DMA);
+
+ if (mxcfbi->alloc_start_vaddr == 0) {
+ dev_err(fbi->device, "Unable to allocate framebuffer memory\n");
+ return -ENOMEM;
+ }
+ dev_dbg(fbi->device, "allocated fb memory @ paddr=0x%08X, size=%d.\n",
+ (uint32_t) mxcfbi->alloc_start_paddr, mxcfbi->alloc_size);
+
+ offset =
+ ((mxcfbi->alloc_size / 2) - 1) & ~((mxcfbi->alloc_size / 2) - 1);
+ fbi->fix.smem_start = mxcfbi->alloc_start_paddr + offset;
+ dev_dbg(fbi->device, "aligned fb start @ paddr=0x%08lX, size=%u.\n",
+ fbi->fix.smem_start, fbi->fix.smem_len);
+
+ fbi->screen_base = mxcfbi->alloc_start_vaddr + offset;
+
+ /* Clear the screen */
+ memset(fbi->screen_base, 0, fbi->fix.smem_len);
+ return 0;
+}
+
+/*!
+ * De-allocates the DRAM memory for the frame buffer.
+ *
+ * @param fbi framebuffer information pointer
+ *
+ * @return Error code indicating success or failure
+ */
+static int mxcfb_unmap_video_memory(struct fb_info *fbi)
+{
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
+
+ dma_free_coherent(fbi->device, mxc_fbi->alloc_size,
+ mxc_fbi->alloc_start_vaddr,
+ mxc_fbi->alloc_start_paddr);
+ return 0;
+}
+
+/*!
+ * Initializes the framebuffer information pointer. After allocating
+ * sufficient memory for the framebuffer structure, the fields are
+ * filled with custom information passed in from the configurable
+ * structures. This includes information such as bits per pixel,
+ * color maps, screen width/height and RGBA offsets.
+ *
+ * @return Framebuffer structure initialized with our information
+ */
+static struct fb_info *mxcfb_init_fbinfo(struct device *dev, struct fb_ops *ops)
+{
+ struct fb_info *fbi;
+ struct mxcfb_info *mxcfbi;
+
+ /*
+ * Allocate sufficient memory for the fb structure
+ */
+ fbi = framebuffer_alloc(sizeof(struct mxcfb_info), dev);
+ if (!fbi)
+ return NULL;
+
+ mxcfbi = (struct mxcfb_info *)fbi->par;
+
+ /*
+ * Fill in fb_info structure information
+ */
+ fbi->var.xres = fbi->var.xres_virtual = MXCFB_SCREEN_WIDTH;
+ fbi->var.yres = fbi->var.yres_virtual = MXCFB_SCREEN_HEIGHT;
+ fbi->var.activate = FB_ACTIVATE_NOW;
+ mxcfb_check_var(&fbi->var, fbi);
+
+ mxcfb_set_fix(fbi);
+
+ fbi->fbops = ops;
+ fbi->flags = FBINFO_FLAG_DEFAULT;
+ fbi->pseudo_palette = mxcfbi->pseudo_palette;
+
+ /*
+ * Allocate colormap
+ */
+ fb_alloc_cmap(&fbi->cmap, 16, 0);
+
+ return fbi;
+}
+
+/*!
+ * Probe routine for the framebuffer driver. It is called during the
+ * driver binding process. The following functions are performed in
+ * this routine: Framebuffer initialization, Memory allocation and
+ * mapping, Framebuffer registration, IPU initialization.
+ *
+ * @return Appropriate error code to the kernel common code
+ */
+static int mxcfb_probe(struct platform_device *pdev)
+{
+ struct fb_info *fbi;
+ struct mxcfb_info *mxc_fbi;
+ int ret;
+
+ platform_set_drvdata(pdev, &mxcfb_drv_data);
+
+ /*
+ * Initialize FB structures
+ */
+ fbi = mxcfb_init_fbinfo(&pdev->dev, &mxcfb_ops);
+ if (!fbi) {
+ ret = -ENOMEM;
+ goto err0;
+ }
+ mxcfb_drv_data.fbi = fbi;
+ mxc_fbi = fbi->par;
+
+ mxcfb_drv_data.suspended = false;
+ init_waitqueue_head(&mxcfb_drv_data.suspend_wq);
+
+ /*
+ * Allocate memory
+ */
+ ret = mxcfb_map_video_memory(fbi);
+ if (ret < 0) {
+ goto err1;
+ }
+
+ mxcfb_init_panel(fbi);
+
+ /*
+ * Register framebuffer
+ */
+ ret = register_framebuffer(fbi);
+ if (ret < 0) {
+ goto err2;
+ }
+
+ dev_info(&pdev->dev, "%s registered\n", MXCFB_NAME);
+
+ return 0;
+
+ err2:
+ mxcfb_unmap_video_memory(fbi);
+ err1:
+ if (&fbi->cmap)
+ fb_dealloc_cmap(&fbi->cmap);
+ framebuffer_release(fbi);
+ err0:
+ return ret;
+}
+
+#ifdef CONFIG_PM
+/*!
+ * Power management hooks. Note that we won't be called from IRQ context,
+ * unlike the blank functions above, so we may sleep.
+ */
+
+/*!
+ * Suspends the framebuffer and blanks the screen. Power management support
+ *
+ * @param pdev pointer to device structure.
+ * @param state state of the device.
+ *
+ * @return success
+ */
+static int mxcfb_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct mxcfb_data *drv_data = platform_get_drvdata(pdev);
+ struct fb_info *fbi = drv_data->fbi;
+ struct mxcfb_info *mxc_fbi = fbi->par;
+
+ drv_data->suspended = true;
+
+ if (mxc_fbi->blank == FB_BLANK_UNBLANK)
+ mxcfb_set_refresh_mode(fbi, MXCFB_REFRESH_OFF, NULL);
+ /* Display OFF */
+ ipu_adc_write_cmd(mxc_fbi->disp_num, CMD, DISOFF, 0, 0);
+
+ return 0;
+}
+
+/*!
+ * Resumes the framebuffer and unblanks the screen. Power management support
+ *
+ * @param pdev pointer to device structure.
+ *
+ * @return success
+ */
+static int mxcfb_resume(struct platform_device *pdev)
+{
+ struct mxcfb_data *drv_data = platform_get_drvdata(pdev);
+ struct fb_info *fbi = drv_data->fbi;
+ struct mxcfb_info *mxc_fbi = fbi->par;
+
+ /* Display ON */
+ ipu_adc_write_cmd(mxc_fbi->disp_num, CMD, DISON, 0, 0);
+ drv_data->suspended = false;
+
+ if (mxc_fbi->blank == FB_BLANK_UNBLANK)
+ mxcfb_set_refresh_mode(fbi, MXCFB_REFRESH_DEFAULT, NULL);
+ wake_up_interruptible(&drv_data->suspend_wq);
+
+ return 0;
+}
+#else
+#define mxcfb_suspend NULL
+#define mxcfb_resume NULL
+#endif
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxcfb_driver = {
+ .driver = {
+ .name = MXCFB_NAME,
+ },
+ .probe = mxcfb_probe,
+ .suspend = mxcfb_suspend,
+ .resume = mxcfb_resume,
+};
+
+/*!
+ * Device definition for the Framebuffer
+ */
+static struct platform_device mxcfb_device = {
+ .name = MXCFB_NAME,
+ .id = 0,
+ .dev = {
+ .coherent_dma_mask = 0xFFFFFFFF,
+ }
+};
+
+/*!
+ * Main entry function for the framebuffer. The function registers the power
+ * management callback functions with the kernel and also registers the MXCFB
+ * callback functions with the core Linux framebuffer driver \b fbmem.c
+ *
+ * @return Error code indicating success or failure
+ */
+static int mxcfb_init(void)
+{
+ int ret = 0;
+
+ ret = platform_driver_register(&mxcfb_driver);
+ if (ret == 0) {
+ ret = platform_device_register(&mxcfb_device);
+ if (ret != 0) {
+ platform_driver_unregister(&mxcfb_driver);
+ }
+ }
+ return ret;
+}
+
+static void mxcfb_exit(void)
+{
+ struct fb_info *fbi = dev_get_drvdata(&mxcfb_device.dev);
+
+ if (fbi) {
+ mxcfb_unmap_video_memory(fbi);
+
+ if (&fbi->cmap)
+ fb_dealloc_cmap(&fbi->cmap);
+
+ unregister_framebuffer(fbi);
+ framebuffer_release(fbi);
+ }
+
+ platform_device_unregister(&mxcfb_device);
+ platform_driver_unregister(&mxcfb_driver);
+}
+
+module_init(mxcfb_init);
+module_exit(mxcfb_exit);
+
+EXPORT_SYMBOL(mxcfb_set_refresh_mode);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC Epson framebuffer driver");
+MODULE_SUPPORTED_DEVICE("fb");
diff --git a/drivers/video/mxc/mxcfb_epson_vga.c b/drivers/video/mxc/mxcfb_epson_vga.c
new file mode 100644
index 00000000..d7cf7d37
--- /dev/null
+++ b/drivers/video/mxc/mxcfb_epson_vga.c
@@ -0,0 +1,362 @@
+/*
+ * Copyright 2007-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @defgroup Framebuffer Framebuffer Driver for SDC and ADC.
+ */
+
+/*!
+ * @file mxcfb_epson_vga.c
+ *
+ * @brief MXC Frame buffer driver for SDC
+ *
+ * @ingroup Framebuffer
+ */
+
+/*!
+ * Include files
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/mxcfb.h>
+#include <linux/ipu.h>
+#include <linux/fsl_devices.h>
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+
+static struct spi_device *lcd_spi;
+static struct device *lcd_dev;
+
+static void lcd_init(void);
+static void lcd_poweron(void);
+static void lcd_poweroff(void);
+
+static void (*lcd_reset) (void);
+static struct regulator *io_reg;
+static struct regulator *core_reg;
+
+static struct fb_videomode video_modes[] = {
+ {
+ /* 480x640 @ 60 Hz */
+ "Epson-VGA", 60, 480, 640, 41701, 60, 41, 10, 5, 20, 10,
+ 0,
+ FB_VMODE_NONINTERLACED,
+ 0,},
+};
+
+static void lcd_init_fb(struct fb_info *info)
+{
+ struct fb_var_screeninfo var;
+
+ memset(&var, 0, sizeof(var));
+
+ fb_videomode_to_var(&var, &video_modes[0]);
+
+ if (machine_is_mx31_3ds()) {
+ var.upper_margin = 0;
+ var.left_margin = 0;
+ }
+
+ var.activate = FB_ACTIVATE_ALL;
+ var.yres_virtual = var.yres * 3;
+
+ console_lock();
+ info->flags |= FBINFO_MISC_USEREVENT;
+ fb_set_var(info, &var);
+ info->flags &= ~FBINFO_MISC_USEREVENT;
+ console_unlock();
+}
+
+static int lcd_fb_event(struct notifier_block *nb, unsigned long val, void *v)
+{
+ struct fb_event *event = v;
+
+ if (strcmp(event->info->fix.id, "DISP3 BG")) {
+ return 0;
+ }
+
+ switch (val) {
+ case FB_EVENT_FB_REGISTERED:
+ lcd_init_fb(event->info);
+ lcd_poweron();
+ break;
+ case FB_EVENT_BLANK:
+ if ((event->info->var.xres != 480) ||
+ (event->info->var.yres != 640)) {
+ break;
+ }
+ if (*((int *)event->data) == FB_BLANK_UNBLANK) {
+ lcd_poweron();
+ } else {
+ lcd_poweroff();
+ }
+ break;
+ }
+ return 0;
+}
+
+static struct notifier_block nb = {
+ .notifier_call = lcd_fb_event,
+};
+
+/*!
+ * This function is called whenever the SPI slave device is detected.
+ *
+ * @param spi the SPI slave device
+ *
+ * @return Returns 0 on SUCCESS and error on FAILURE.
+ */
+static int __devinit lcd_probe(struct device *dev)
+{
+ int i;
+ struct mxc_lcd_platform_data *plat = dev->platform_data;
+
+ lcd_dev = dev;
+
+ if (plat) {
+ io_reg = regulator_get(dev, plat->io_reg);
+ if (!IS_ERR(io_reg)) {
+ regulator_set_voltage(io_reg, 1800000, 1800000);
+ regulator_enable(io_reg);
+ }
+ core_reg = regulator_get(dev, plat->core_reg);
+ if (!IS_ERR(core_reg)) {
+ regulator_set_voltage(core_reg, 2800000, 2800000);
+ regulator_enable(core_reg);
+ }
+
+ lcd_reset = plat->reset;
+ if (lcd_reset)
+ lcd_reset();
+ }
+
+ lcd_init();
+
+ for (i = 0; i < num_registered_fb; i++) {
+ if (strcmp(registered_fb[i]->fix.id, "DISP3 BG") == 0) {
+ lcd_init_fb(registered_fb[i]);
+ fb_show_logo(registered_fb[i], 0);
+ lcd_poweron();
+ }
+ }
+
+ fb_register_client(&nb);
+
+ return 0;
+}
+
+static int __devinit lcd_plat_probe(struct platform_device *pdev)
+{
+ ipu_adc_sig_cfg_t sig;
+ ipu_channel_params_t param;
+
+ memset(&sig, 0, sizeof(sig));
+ sig.ifc_width = 9;
+ sig.clk_pol = 1;
+ ipu_init_async_panel(0, IPU_PANEL_SERIAL, 90, IPU_PIX_FMT_GENERIC, sig);
+
+ memset(&param, 0, sizeof(param));
+ ipu_init_channel(DIRECT_ASYNC1, &param);
+
+ return lcd_probe(&pdev->dev);
+}
+
+static int __devinit lcd_spi_probe(struct spi_device *spi)
+{
+ lcd_spi = spi;
+
+ spi->bits_per_word = 9;
+ spi_setup(spi);
+
+ return lcd_probe(&spi->dev);
+}
+
+static int __devexit lcd_remove(struct device *dev)
+{
+ fb_unregister_client(&nb);
+ lcd_poweroff();
+ regulator_put(io_reg);
+ regulator_put(core_reg);
+
+ return 0;
+}
+
+static int __devexit lcd_spi_remove(struct spi_device *spi)
+{
+ int ret = lcd_remove(&spi->dev);
+ lcd_spi = NULL;
+ return ret;
+}
+
+static int __devexit lcd_plat_remove(struct platform_device *pdev)
+{
+ return lcd_remove(&pdev->dev);
+}
+
+static int lcd_suspend(struct spi_device *spi, pm_message_t message)
+{
+ lcd_poweroff();
+ return 0;
+}
+
+static int lcd_resume(struct spi_device *spi)
+{
+ if (lcd_reset)
+ lcd_reset();
+
+ lcd_init();
+ lcd_poweron();
+ return 0;
+}
+
+/*!
+ * spi driver structure for LTV350QV
+ */
+static struct spi_driver lcd_spi_dev_driver = {
+
+ .driver = {
+ .name = "lcd_spi",
+ .owner = THIS_MODULE,
+ },
+ .probe = lcd_spi_probe,
+ .remove = __devexit_p(lcd_spi_remove),
+ .suspend = lcd_suspend,
+ .resume = lcd_resume,
+};
+
+static struct platform_driver lcd_plat_driver = {
+ .driver = {
+ .name = "lcd_spi",
+ .owner = THIS_MODULE,
+ },
+ .probe = lcd_plat_probe,
+ .remove = __devexit_p(lcd_plat_remove),
+};
+
+#define param(x) ((x) | 0x100)
+
+/*
+ * Send init commands to L4F00242T03
+ *
+ */
+static void lcd_init(void)
+{
+ const u16 cmd[] = { 0x36, param(0), 0x3A, param(0x60) };
+
+ dev_dbg(lcd_dev, "initializing LCD\n");
+ if (lcd_spi) {
+ spi_write(lcd_spi, (const u8 *)cmd, ARRAY_SIZE(cmd));
+ } else {
+ ipu_disp_direct_write(DIRECT_ASYNC1, 0x36, 0);
+ ipu_disp_direct_write(DIRECT_ASYNC1, 0x100, 0);
+ ipu_disp_direct_write(DIRECT_ASYNC1, 0x3A, 0);
+ ipu_disp_direct_write(DIRECT_ASYNC1, 0x160, 0);
+ msleep(1);
+ ipu_uninit_channel(DIRECT_ASYNC1);
+ }
+}
+
+static int lcd_on;
+/*
+ * Send Power On commands to L4F00242T03
+ *
+ */
+static void lcd_poweron(void)
+{
+ const u16 slpout = 0x11;
+ const u16 dison = 0x29;
+ ipu_channel_params_t param;
+ if (lcd_on)
+ return;
+
+ dev_dbg(lcd_dev, "turning on LCD\n");
+
+ if (lcd_spi) {
+ msleep(60);
+ spi_write(lcd_spi, (const u8 *)&slpout, 1);
+ msleep(60);
+ spi_write(lcd_spi, (const u8 *)&dison, 1);
+ } else {
+ memset(&param, 0, sizeof(param));
+ ipu_init_channel(DIRECT_ASYNC1, &param);
+ ipu_disp_direct_write(DIRECT_ASYNC1, slpout, 0);
+ msleep(60);
+ ipu_disp_direct_write(DIRECT_ASYNC1, dison, 0);
+ msleep(1);
+ ipu_uninit_channel(DIRECT_ASYNC1);
+ }
+ lcd_on = 1;
+}
+
+/*
+ * Send Power Off commands to L4F00242T03
+ *
+ */
+static void lcd_poweroff(void)
+{
+ const u16 slpin = 0x10;
+ const u16 disoff = 0x28;
+ ipu_channel_params_t param;
+ if (!lcd_on)
+ return;
+
+ dev_dbg(lcd_dev, "turning off LCD\n");
+
+ if (lcd_spi) {
+ msleep(60);
+ spi_write(lcd_spi, (const u8 *)&disoff, 1);
+ msleep(60);
+ spi_write(lcd_spi, (const u8 *)&slpin, 1);
+ } else {
+ memset(&param, 0, sizeof(param));
+ ipu_init_channel(DIRECT_ASYNC1, &param);
+ ipu_disp_direct_write(DIRECT_ASYNC1, disoff, 0);
+ msleep(60);
+ ipu_disp_direct_write(DIRECT_ASYNC1, slpin, 0);
+ msleep(1);
+ ipu_uninit_channel(DIRECT_ASYNC1);
+ }
+ lcd_on = 0;
+}
+
+static int __init epson_lcd_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&lcd_plat_driver);
+ if (ret)
+ return ret;
+
+ return spi_register_driver(&lcd_spi_dev_driver);
+
+}
+
+static void __exit epson_lcd_exit(void)
+{
+ spi_unregister_driver(&lcd_spi_dev_driver);
+}
+
+module_init(epson_lcd_init);
+module_exit(epson_lcd_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Epson VGA LCD init driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/mxc/mxcfb_hx8369_wvga.c b/drivers/video/mxc/mxcfb_hx8369_wvga.c
new file mode 100644
index 00000000..08649d38
--- /dev/null
+++ b/drivers/video/mxc/mxcfb_hx8369_wvga.c
@@ -0,0 +1,454 @@
+/*
+ * Copyright (C) 2011-2012 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/io.h>
+#include <linux/bitops.h>
+#include <linux/spinlock.h>
+#include <linux/mxcfb.h>
+#include <linux/backlight.h>
+#include <video/mipi_display.h>
+
+#include <mach/hardware.h>
+#include <mach/clock.h>
+#include <mach/mipi_dsi.h>
+
+#include "mipi_dsi.h"
+
+#define MIPI_DSI_MAX_RET_PACK_SIZE (0x4)
+
+#define HX8369BL_MAX_BRIGHT (255)
+#define HX8369BL_DEF_BRIGHT (255)
+
+#define HX8369_MAX_DPHY_CLK (800)
+#define HX8369_ONE_DATA_LANE (0x1)
+#define HX8369_TWO_DATA_LANE (0x2)
+
+#define HX8369_CMD_SETEXTC (0xB9)
+#define HX8369_CMD_SETEXTC_LEN (0x4)
+#define HX8369_CMD_SETEXTC_PARAM_1 (0x6983ff)
+
+#define HX8369_CMD_GETHXID (0xF4)
+#define HX8369_CMD_GETHXID_LEN (0x4)
+#define HX8369_ID (0x69)
+#define HX8369_ID_MASK (0xFF)
+
+#define HX8369_CMD_SETDISP (0xB2)
+#define HX8369_CMD_SETDISP_LEN (16)
+#define HX8369_CMD_SETDISP_1_HALT (0x00)
+#define HX8369_CMD_SETDISP_2_RES_MODE (0x23)
+#define HX8369_CMD_SETDISP_3_BP (0x03)
+#define HX8369_CMD_SETDISP_4_FP (0x03)
+#define HX8369_CMD_SETDISP_5_SAP (0x70)
+#define HX8369_CMD_SETDISP_6_GENON (0x00)
+#define HX8369_CMD_SETDISP_7_GENOFF (0xff)
+#define HX8369_CMD_SETDISP_8_RTN (0x00)
+#define HX8369_CMD_SETDISP_9_TEI (0x00)
+#define HX8369_CMD_SETDISP_10_TEP_UP (0x00)
+#define HX8369_CMD_SETDISP_11_TEP_LOW (0x00)
+#define HX8369_CMD_SETDISP_12_BP_PE (0x03)
+#define HX8369_CMD_SETDISP_13_FP_PE (0x03)
+#define HX8369_CMD_SETDISP_14_RTN_PE (0x00)
+#define HX8369_CMD_SETDISP_15_GON (0x01)
+
+#define HX8369_CMD_SETCYC (0xB4)
+#define HX8369_CMD_SETCYC_LEN (6)
+#define HX8369_CMD_SETCYC_PARAM_1 (0x5f1d00)
+#define HX8369_CMD_SETCYC_PARAM_2 (0x060e)
+
+#define HX8369_CMD_SETGIP (0xD5)
+#define HX8369_CMD_SETGIP_LEN (27)
+#define HX8369_CMD_SETGIP_PARAM_1 (0x030400)
+#define HX8369_CMD_SETGIP_PARAM_2 (0x1c050100)
+#define HX8369_CMD_SETGIP_PARAM_3 (0x00030170)
+#define HX8369_CMD_SETGIP_PARAM_4 (0x51064000)
+#define HX8369_CMD_SETGIP_PARAM_5 (0x41000007)
+#define HX8369_CMD_SETGIP_PARAM_6 (0x07075006)
+#define HX8369_CMD_SETGIP_PARAM_7 (0x040f)
+
+#define HX8369_CMD_SETPOWER (0xB1)
+#define HX8369_CMD_SETPOWER_LEN (20)
+#define HX8369_CMD_SETPOWER_PARAM_1 (0x340001)
+#define HX8369_CMD_SETPOWER_PARAM_2 (0x0f0f0006)
+#define HX8369_CMD_SETPOWER_PARAM_3 (0x3f3f322a)
+#define HX8369_CMD_SETPOWER_PARAM_4 (0xe6013a07)
+#define HX8369_CMD_SETPOWER_PARAM_5 (0xe6e6e6e6)
+
+#define HX8369_CMD_SETVCOM (0xB6)
+#define HX8369_CMD_SETVCOM_LEN (3)
+#define HX8369_CMD_SETVCOM_PARAM_1 (0x5656)
+
+#define HX8369_CMD_SETPANEL (0xCC)
+#define HX8369_CMD_SETPANEL_PARAM_1 (0x02)
+
+#define HX8369_CMD_SETGAMMA (0xE0)
+#define HX8369_CMD_SETGAMMA_LEN (35)
+#define HX8369_CMD_SETGAMMA_PARAM_1 (0x221d00)
+#define HX8369_CMD_SETGAMMA_PARAM_2 (0x2e3f3d38)
+#define HX8369_CMD_SETGAMMA_PARAM_3 (0x0f0d064a)
+#define HX8369_CMD_SETGAMMA_PARAM_4 (0x16131513)
+#define HX8369_CMD_SETGAMMA_PARAM_5 (0x1d001910)
+#define HX8369_CMD_SETGAMMA_PARAM_6 (0x3f3d3822)
+#define HX8369_CMD_SETGAMMA_PARAM_7 (0x0d064a2e)
+#define HX8369_CMD_SETGAMMA_PARAM_8 (0x1315130f)
+#define HX8369_CMD_SETGAMMA_PARAM_9 (0x191016)
+
+#define HX8369_CMD_SETMIPI (0xBA)
+#define HX8369_CMD_SETMIPI_LEN (14)
+#define HX8369_CMD_SETMIPI_PARAM_1 (0xc6a000)
+#define HX8369_CMD_SETMIPI_PARAM_2 (0x10000a00)
+#define HX8369_CMD_SETMIPI_ONELANE (0x10 << 24)
+#define HX8369_CMD_SETMIPI_TWOLANE (0x11 << 24)
+#define HX8369_CMD_SETMIPI_PARAM_3 (0x00026f30)
+#define HX8369_CMD_SETMIPI_PARAM_4 (0x4018)
+
+#define HX8369_CMD_SETPIXEL_FMT (0x3A)
+#define HX8369_CMD_SETPIXEL_FMT_24BPP (0x77)
+#define HX8369_CMD_SETPIXEL_FMT_18BPP (0x66)
+#define HX8369_CMD_SETPIXEL_FMT_16BPP (0x55)
+
+#define HX8369_CMD_SETCLUMN_ADDR (0x2A)
+#define HX8369_CMD_SETCLUMN_ADDR_LEN (5)
+#define HX8369_CMD_SETCLUMN_ADDR_PARAM_1 (0xdf0000)
+#define HX8369_CMD_SETCLUMN_ADDR_PARAM_2 (0x01)
+
+#define HX8369_CMD_SETPAGE_ADDR (0x2B)
+#define HX8369_CMD_SETPAGE_ADDR_LEN (5)
+#define HX8369_CMD_SETPAGE_ADDR_PARAM_1 (0x1f0000)
+#define HX8369_CMD_SETPAGE_ADDR_PARAM_2 (0x03)
+
+#define HX8369_CMD_WRT_DISP_BRIGHT (0x51)
+#define HX8369_CMD_WRT_DISP_BRIGHT_PARAM_1 (0xFF)
+
+#define HX8369_CMD_WRT_CABC_MIN_BRIGHT (0x5E)
+#define HX8369_CMD_WRT_CABC_MIN_BRIGHT_PARAM_1 (0x20)
+
+#define HX8369_CMD_WRT_CABC_CTRL (0x55)
+#define HX8369_CMD_WRT_CABC_CTRL_PARAM_1 (0x1)
+
+#define HX8369_CMD_WRT_CTRL_DISP (0x53)
+#define HX8369_CMD_WRT_CTRL_DISP_PARAM_1 (0x24)
+
+#define CHECK_RETCODE(ret) \
+do { \
+ if (ret < 0) { \
+ dev_err(&mipi_dsi->pdev->dev, \
+ "%s ERR: ret:%d, line:%d.\n", \
+ __func__, ret, __LINE__); \
+ return ret; \
+ } \
+} while (0)
+
+static int hx8369bl_brightness;
+static int mipid_init_backlight(struct mipi_dsi_info *mipi_dsi);
+
+static struct fb_videomode truly_lcd_modedb[] = {
+ {
+ "TRULY-WVGA", 64, 480, 800, 37880,
+ 8, 8,
+ 6, 6,
+ 8, 6,
+ FB_SYNC_OE_LOW_ACT,
+ FB_VMODE_NONINTERLACED,
+ 0,
+ },
+};
+
+static struct mipi_lcd_config lcd_config = {
+ .virtual_ch = 0x0,
+ .data_lane_num = HX8369_TWO_DATA_LANE,
+ .max_phy_clk = HX8369_MAX_DPHY_CLK,
+ .dpi_fmt = MIPI_RGB888,
+};
+void mipid_hx8369_get_lcd_videomode(struct fb_videomode **mode, int *size,
+ struct mipi_lcd_config **data)
+{
+ if (cpu_is_mx6dl())
+ truly_lcd_modedb[0].pixclock = 37037; /* 27M clock*/
+ *mode = &truly_lcd_modedb[0];
+ *size = ARRAY_SIZE(truly_lcd_modedb);
+ *data = &lcd_config;
+}
+
+int mipid_hx8369_lcd_setup(struct mipi_dsi_info *mipi_dsi)
+{
+ u32 buf[DSI_CMD_BUF_MAXSIZE];
+ int err;
+
+ dev_dbg(&mipi_dsi->pdev->dev, "MIPI DSI LCD setup.\n");
+ buf[0] = HX8369_CMD_SETEXTC | (HX8369_CMD_SETEXTC_PARAM_1 << 8);
+ err = mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_LONG_WRITE,
+ buf, HX8369_CMD_SETEXTC_LEN);
+ CHECK_RETCODE(err);
+ buf[0] = MIPI_DSI_MAX_RET_PACK_SIZE;
+ err = mipi_dsi_pkt_write(mipi_dsi,
+ MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE,
+ buf, 0);
+ CHECK_RETCODE(err);
+ buf[0] = HX8369_CMD_GETHXID;
+ err = mipi_dsi_pkt_read(mipi_dsi,
+ MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM,
+ buf, HX8369_CMD_GETHXID_LEN);
+ if (!err && ((buf[0] & HX8369_ID_MASK) == HX8369_ID)) {
+ dev_info(&mipi_dsi->pdev->dev,
+ "MIPI DSI LCD ID:0x%x.\n", buf[0]);
+ } else {
+ dev_err(&mipi_dsi->pdev->dev,
+ "mipi_dsi_pkt_read err:%d, data:0x%x.\n",
+ err, buf[0]);
+ dev_info(&mipi_dsi->pdev->dev,
+ "MIPI DSI LCD not detected!\n");
+ return err;
+ }
+
+ /* set LCD resolution as 480RGBx800, DPI interface,
+ * display operation mode: RGB data bypass GRAM mode.
+ */
+ buf[0] = HX8369_CMD_SETDISP | (HX8369_CMD_SETDISP_1_HALT << 8) |
+ (HX8369_CMD_SETDISP_2_RES_MODE << 16) |
+ (HX8369_CMD_SETDISP_3_BP << 24);
+ buf[1] = HX8369_CMD_SETDISP_4_FP | (HX8369_CMD_SETDISP_5_SAP << 8) |
+ (HX8369_CMD_SETDISP_6_GENON << 16) |
+ (HX8369_CMD_SETDISP_7_GENOFF << 24);
+ buf[2] = HX8369_CMD_SETDISP_8_RTN | (HX8369_CMD_SETDISP_9_TEI << 8) |
+ (HX8369_CMD_SETDISP_10_TEP_UP << 16) |
+ (HX8369_CMD_SETDISP_11_TEP_LOW << 24);
+ buf[3] = HX8369_CMD_SETDISP_12_BP_PE |
+ (HX8369_CMD_SETDISP_13_FP_PE << 8) |
+ (HX8369_CMD_SETDISP_14_RTN_PE << 16) |
+ (HX8369_CMD_SETDISP_15_GON << 24);
+ err = mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_LONG_WRITE,
+ buf, HX8369_CMD_SETDISP_LEN);
+ CHECK_RETCODE(err);
+
+ /* Set display waveform cycle */
+ buf[0] = HX8369_CMD_SETCYC | (HX8369_CMD_SETCYC_PARAM_1 << 8);
+ buf[1] = HX8369_CMD_SETCYC_PARAM_2;
+ err = mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_LONG_WRITE,
+ buf, HX8369_CMD_SETCYC_LEN);
+ CHECK_RETCODE(err);
+
+ /* Set GIP timing output control */
+ buf[0] = HX8369_CMD_SETGIP | (HX8369_CMD_SETGIP_PARAM_1 << 8);
+ buf[1] = HX8369_CMD_SETGIP_PARAM_2;
+ buf[2] = HX8369_CMD_SETGIP_PARAM_3;
+ buf[3] = HX8369_CMD_SETGIP_PARAM_4;
+ buf[4] = HX8369_CMD_SETGIP_PARAM_5;
+ buf[5] = HX8369_CMD_SETGIP_PARAM_6;
+ buf[6] = HX8369_CMD_SETGIP_PARAM_7;
+ err = mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_LONG_WRITE, buf,
+ HX8369_CMD_SETGIP_LEN);
+ CHECK_RETCODE(err);
+
+ /* Set power: standby, DC etc. */
+ buf[0] = HX8369_CMD_SETPOWER | (HX8369_CMD_SETPOWER_PARAM_1 << 8);
+ buf[1] = HX8369_CMD_SETPOWER_PARAM_2;
+ buf[2] = HX8369_CMD_SETPOWER_PARAM_3;
+ buf[3] = HX8369_CMD_SETPOWER_PARAM_4;
+ buf[4] = HX8369_CMD_SETPOWER_PARAM_5;
+ err = mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_LONG_WRITE, buf,
+ HX8369_CMD_SETPOWER_LEN);
+ CHECK_RETCODE(err);
+
+ /* Set VCOM voltage. */
+ buf[0] = HX8369_CMD_SETVCOM | (HX8369_CMD_SETVCOM_PARAM_1 << 8);
+ err = mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_LONG_WRITE, buf,
+ HX8369_CMD_SETVCOM_LEN);
+ CHECK_RETCODE(err);
+
+ /* Set Panel: BGR/RGB or Inversion. */
+ buf[0] = HX8369_CMD_SETPANEL | (HX8369_CMD_SETPANEL_PARAM_1 << 8);
+ err = mipi_dsi_pkt_write(mipi_dsi,
+ MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM, buf, 0);
+ CHECK_RETCODE(err);
+
+ /* Set gamma curve related setting */
+ buf[0] = HX8369_CMD_SETGAMMA | (HX8369_CMD_SETGAMMA_PARAM_1 << 8);
+ buf[1] = HX8369_CMD_SETGAMMA_PARAM_2;
+ buf[2] = HX8369_CMD_SETGAMMA_PARAM_3;
+ buf[3] = HX8369_CMD_SETGAMMA_PARAM_4;
+ buf[4] = HX8369_CMD_SETGAMMA_PARAM_5;
+ buf[5] = HX8369_CMD_SETGAMMA_PARAM_6;
+ buf[7] = HX8369_CMD_SETGAMMA_PARAM_7;
+ buf[7] = HX8369_CMD_SETGAMMA_PARAM_8;
+ buf[8] = HX8369_CMD_SETGAMMA_PARAM_9;
+ err = mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_LONG_WRITE, buf,
+ HX8369_CMD_SETGAMMA_LEN);
+ CHECK_RETCODE(err);
+
+ /* Set MIPI: DPHYCMD & DSICMD, data lane number */
+ buf[0] = HX8369_CMD_SETMIPI | (HX8369_CMD_SETMIPI_PARAM_1 << 8);
+ buf[1] = HX8369_CMD_SETMIPI_PARAM_2;
+ buf[2] = HX8369_CMD_SETMIPI_PARAM_3;
+ if (lcd_config.data_lane_num == HX8369_ONE_DATA_LANE)
+ buf[2] |= HX8369_CMD_SETMIPI_ONELANE;
+ else
+ buf[2] |= HX8369_CMD_SETMIPI_TWOLANE;
+ buf[3] = HX8369_CMD_SETMIPI_PARAM_4;
+ err = mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_LONG_WRITE, buf,
+ HX8369_CMD_SETMIPI_LEN);
+ CHECK_RETCODE(err);
+
+ /* Set pixel format:24bpp */
+ buf[0] = HX8369_CMD_SETPIXEL_FMT;
+ switch (lcd_config.dpi_fmt) {
+ case MIPI_RGB565_PACKED:
+ case MIPI_RGB565_LOOSELY:
+ case MIPI_RGB565_CONFIG3:
+ buf[0] |= (HX8369_CMD_SETPIXEL_FMT_16BPP << 8);
+ break;
+
+ case MIPI_RGB666_LOOSELY:
+ case MIPI_RGB666_PACKED:
+ buf[0] |= (HX8369_CMD_SETPIXEL_FMT_18BPP << 8);
+ break;
+
+ case MIPI_RGB888:
+ buf[0] |= (HX8369_CMD_SETPIXEL_FMT_24BPP << 8);
+ break;
+
+ default:
+ buf[0] |= (HX8369_CMD_SETPIXEL_FMT_24BPP << 8);
+ break;
+ }
+ err = mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM,
+ buf, 0);
+ CHECK_RETCODE(err);
+
+ /* Set column address: 0~479 */
+ buf[0] = HX8369_CMD_SETCLUMN_ADDR |
+ (HX8369_CMD_SETCLUMN_ADDR_PARAM_1 << 8);
+ buf[1] = HX8369_CMD_SETCLUMN_ADDR_PARAM_2;
+ err = mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_LONG_WRITE,
+ buf, HX8369_CMD_SETCLUMN_ADDR_LEN);
+ CHECK_RETCODE(err);
+
+ /* Set page address: 0~799 */
+ buf[0] = HX8369_CMD_SETPAGE_ADDR |
+ (HX8369_CMD_SETPAGE_ADDR_PARAM_1 << 8);
+ buf[1] = HX8369_CMD_SETPAGE_ADDR_PARAM_2;
+ err = mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_LONG_WRITE,
+ buf, HX8369_CMD_SETPAGE_ADDR_LEN);
+ CHECK_RETCODE(err);
+
+ /* Set display brightness related */
+ buf[0] = HX8369_CMD_WRT_DISP_BRIGHT |
+ (HX8369_CMD_WRT_DISP_BRIGHT_PARAM_1 << 8);
+ err = mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM,
+ buf, 0);
+ CHECK_RETCODE(err);
+
+ buf[0] = HX8369_CMD_WRT_CABC_CTRL |
+ (HX8369_CMD_WRT_CABC_CTRL_PARAM_1 << 8);
+ err = mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM,
+ buf, 0);
+ CHECK_RETCODE(err);
+
+ buf[0] = HX8369_CMD_WRT_CTRL_DISP |
+ (HX8369_CMD_WRT_CTRL_DISP_PARAM_1 << 8);
+ err = mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM,
+ buf, 0);
+ CHECK_RETCODE(err);
+
+ /* exit sleep mode and set display on */
+ buf[0] = MIPI_DCS_EXIT_SLEEP_MODE;
+ err = mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM,
+ buf, 0);
+ CHECK_RETCODE(err);
+ /* To allow time for the supply voltages
+ * and clock circuits to stabilize.
+ */
+ msleep(5);
+ buf[0] = MIPI_DCS_SET_DISPLAY_ON;
+ err = mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM,
+ buf, 0);
+ CHECK_RETCODE(err);
+
+ err = mipid_init_backlight(mipi_dsi);
+ return err;
+}
+
+static int mipid_bl_update_status(struct backlight_device *bl)
+{
+ u32 buf;
+ int brightness = bl->props.brightness;
+ struct mipi_dsi_info *mipi_dsi = bl_get_data(bl);
+
+ if (bl->props.power != FB_BLANK_UNBLANK ||
+ bl->props.fb_blank != FB_BLANK_UNBLANK)
+ brightness = 0;
+
+ buf = HX8369_CMD_WRT_DISP_BRIGHT |
+ ((brightness & HX8369BL_MAX_BRIGHT) << 8);
+ mipi_dsi_pkt_write(mipi_dsi, MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM,
+ &buf, 0);
+
+ hx8369bl_brightness = brightness & HX8369BL_MAX_BRIGHT;
+
+ dev_dbg(&bl->dev, "mipid backlight bringtness:%d.\n", brightness);
+ return 0;
+}
+
+static int mipid_bl_get_brightness(struct backlight_device *bl)
+{
+ return hx8369bl_brightness;
+}
+
+static int mipi_bl_check_fb(struct backlight_device *bl, struct fb_info *fbi)
+{
+ return 0;
+}
+
+static const struct backlight_ops mipid_lcd_bl_ops = {
+ .update_status = mipid_bl_update_status,
+ .get_brightness = mipid_bl_get_brightness,
+ .check_fb = mipi_bl_check_fb,
+};
+
+static int mipid_init_backlight(struct mipi_dsi_info *mipi_dsi)
+{
+ struct backlight_properties props;
+ struct backlight_device *bl;
+
+ if (mipi_dsi->bl) {
+ pr_debug("mipid backlight already init!\n");
+ return 0;
+ }
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.max_brightness = HX8369BL_MAX_BRIGHT;
+ props.type = BACKLIGHT_RAW;
+ bl = backlight_device_register("mipid-bl", &mipi_dsi->pdev->dev,
+ mipi_dsi, &mipid_lcd_bl_ops, &props);
+ if (IS_ERR(bl)) {
+ pr_err("error %ld on backlight register\n", PTR_ERR(bl));
+ return PTR_ERR(bl);
+ }
+ mipi_dsi->bl = bl;
+ bl->props.power = FB_BLANK_UNBLANK;
+ bl->props.fb_blank = FB_BLANK_UNBLANK;
+ bl->props.brightness = HX8369BL_DEF_BRIGHT;
+
+ mipid_bl_update_status(bl);
+ return 0;
+}
diff --git a/drivers/video/mxc/mxcfb_modedb.c b/drivers/video/mxc/mxcfb_modedb.c
new file mode 100644
index 00000000..acaaef2c
--- /dev/null
+++ b/drivers/video/mxc/mxcfb_modedb.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2007-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/kernel.h>
+#include <linux/mxcfb.h>
+
+struct fb_videomode mxcfb_modedb[] = {
+ {
+ /* 240x320 @ 60 Hz */
+ "Sharp-QVGA", 60, 240, 320, 185925, 9, 16, 7, 9, 1, 1,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_SHARP_MODE |
+ FB_SYNC_DATA_INVERT | FB_SYNC_CLK_IDLE_EN,
+ FB_VMODE_NONINTERLACED,
+ 0,},
+ {
+ /* 240x33 @ 60 Hz */
+ "Sharp-CLI", 60, 240, 33, 185925, 9, 16, 7, 9 + 287, 1, 1,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_SHARP_MODE |
+ FB_SYNC_DATA_INVERT | FB_SYNC_CLK_IDLE_EN,
+ FB_VMODE_NONINTERLACED,
+ 0,},
+ {
+ /* 640x480 @ 60 Hz */
+ "NEC-VGA", 60, 640, 480, 38255, 144, 0, 34, 40, 1, 1,
+ FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED,
+ 0,},
+ {
+ /* 640x480 @ 60 Hz */
+ "CPT-VGA", 60, 640, 480, 39683, 45, 114, 33, 11, 1, 1,
+ FB_SYNC_CLK_LAT_FALL,
+ FB_VMODE_NONINTERLACED,
+ 0,},
+ {
+ /* NTSC TV output */
+ "TV-NTSC", 60, 640, 480, 37538,
+ 38, 858 - 640 - 38 - 3,
+ 36, 518 - 480 - 36 - 1,
+ 3, 1,
+ 0,
+ FB_VMODE_NONINTERLACED,
+ 0,},
+ {
+ /* PAL TV output */
+ "TV-PAL", 50, 640, 480, 37538,
+ 38, 960 - 640 - 38 - 32,
+ 32, 555 - 480 - 32 - 3,
+ 32, 3,
+ 0,
+ FB_VMODE_NONINTERLACED,
+ 0,},
+ {
+ /* TV output VGA mode, 640x480 @ 65 Hz */
+ "TV-VGA", 60, 640, 480, 40574, 35, 45, 9, 1, 46, 5,
+ 0, FB_VMODE_NONINTERLACED, 0,
+ },
+};
+
+int mxcfb_modedb_sz = ARRAY_SIZE(mxcfb_modedb);
diff --git a/drivers/video/mxc/mxcfb_seiko_wvga.c b/drivers/video/mxc/mxcfb_seiko_wvga.c
new file mode 100644
index 00000000..6e9abaf8
--- /dev/null
+++ b/drivers/video/mxc/mxcfb_seiko_wvga.c
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2011-2012 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*!
+ * @defgroup Framebuffer Framebuffer Driver for SDC and ADC.
+ */
+
+/*!
+ * @file mxcfb_seiko_wvga.c
+ *
+ * @brief MXC Frame buffer driver for SDC
+ *
+ * @ingroup Framebuffer
+ */
+
+/*!
+ * Include files
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fb.h>
+#include <linux/fsl_devices.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/mxcfb.h>
+#include <linux/regulator/consumer.h>
+#include <mach/hardware.h>
+
+static void lcd_poweron(void);
+static void lcd_poweroff(void);
+
+static struct platform_device *plcd_dev;
+static struct regulator *io_reg;
+static struct regulator *core_reg;
+static int lcd_on;
+
+static struct fb_videomode video_modes[] = {
+ {
+ /* 800x480 @ 57 Hz , pixel clk @ 32MHz */
+ "SEIKO-WVGA", 60, 800, 480, 29850, 99, 164, 33, 10, 10, 10,
+ FB_SYNC_CLK_LAT_FALL,
+ FB_VMODE_NONINTERLACED,
+ 0,},
+};
+
+static void lcd_init_fb(struct fb_info *info)
+{
+ struct fb_var_screeninfo var;
+
+ memset(&var, 0, sizeof(var));
+
+ fb_videomode_to_var(&var, &video_modes[0]);
+
+ var.activate = FB_ACTIVATE_ALL;
+ var.yres_virtual = var.yres * 3;
+
+ console_lock();
+ info->flags |= FBINFO_MISC_USEREVENT;
+ fb_set_var(info, &var);
+ info->flags &= ~FBINFO_MISC_USEREVENT;
+ console_unlock();
+}
+
+static int lcd_fb_event(struct notifier_block *nb, unsigned long val, void *v)
+{
+ struct fb_event *event = v;
+
+ if (strcmp(event->info->fix.id, "mxc_elcdif_fb"))
+ return 0;
+
+ switch (val) {
+ case FB_EVENT_BLANK:
+ if ((event->info->var.xres != 800) ||
+ (event->info->var.yres != 480)) {
+ break;
+ }
+ if (*((int *)event->data) == FB_BLANK_UNBLANK)
+ lcd_poweron();
+ else
+ lcd_poweroff();
+ break;
+ }
+ return 0;
+}
+
+static struct notifier_block nb = {
+ .notifier_call = lcd_fb_event,
+};
+
+/*!
+ * This function is called whenever the platform device is detected.
+ *
+ * @param pdev the platform device
+ *
+ * @return Returns 0 on SUCCESS and error on FAILURE.
+ */
+static int __devinit lcd_probe(struct platform_device *pdev)
+{
+ int i;
+ struct fsl_mxc_lcd_platform_data *plat = pdev->dev.platform_data;
+
+ if (plat) {
+ if (plat->reset)
+ plat->reset();
+
+ io_reg = regulator_get(&pdev->dev, plat->io_reg);
+ if (IS_ERR(io_reg))
+ io_reg = NULL;
+ core_reg = regulator_get(&pdev->dev, plat->core_reg);
+ if (!IS_ERR(core_reg))
+ regulator_set_voltage(io_reg, 1800000, 1800000);
+ else
+ core_reg = NULL;
+ }
+
+ for (i = 0; i < num_registered_fb; i++) {
+ if (strcmp(registered_fb[i]->fix.id, "mxc_elcdif_fb") == 0) {
+ lcd_init_fb(registered_fb[i]);
+ fb_show_logo(registered_fb[i], 0);
+ lcd_poweron();
+ }
+ }
+
+ fb_register_client(&nb);
+
+ plcd_dev = pdev;
+
+ return 0;
+}
+
+static int __devexit lcd_remove(struct platform_device *pdev)
+{
+ fb_unregister_client(&nb);
+ lcd_poweroff();
+ if (io_reg)
+ regulator_put(io_reg);
+ if (core_reg)
+ regulator_put(core_reg);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int lcd_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ return 0;
+}
+
+static int lcd_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+#else
+#define lcd_suspend NULL
+#define lcd_resume NULL
+#endif
+
+/*!
+ * platform driver structure for SEIKO WVGA
+ */
+static struct platform_driver lcd_driver = {
+ .driver = {
+ .name = "lcd_seiko"},
+ .probe = lcd_probe,
+ .remove = __devexit_p(lcd_remove),
+ .suspend = lcd_suspend,
+ .resume = lcd_resume,
+};
+
+/*
+ * Send Power
+ *
+ */
+static void lcd_poweron(void)
+{
+ if (lcd_on)
+ return;
+
+ dev_dbg(&plcd_dev->dev, "turning on LCD\n");
+ if (core_reg)
+ regulator_enable(core_reg);
+ if (io_reg)
+ regulator_enable(io_reg);
+ lcd_on = 1;
+}
+
+/*
+ * Send Power Off
+ *
+ */
+static void lcd_poweroff(void)
+{
+ lcd_on = 0;
+ dev_dbg(&plcd_dev->dev, "turning off LCD\n");
+ if (io_reg)
+ regulator_disable(io_reg);
+ if (core_reg)
+ regulator_disable(core_reg);
+}
+
+static int __init seiko_wvga_lcd_init(void)
+{
+ return platform_driver_register(&lcd_driver);
+}
+
+static void __exit seiko_wvga_lcd_exit(void)
+{
+ platform_driver_unregister(&lcd_driver);
+}
+
+module_init(seiko_wvga_lcd_init);
+module_exit(seiko_wvga_lcd_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("SEIKO WVGA LCD init driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/mxc/mxcfb_sii902x.c b/drivers/video/mxc/mxcfb_sii902x.c
new file mode 100644
index 00000000..b71afc6a
--- /dev/null
+++ b/drivers/video/mxc/mxcfb_sii902x.c
@@ -0,0 +1,1312 @@
+/*
+ * Copyright (C) 2010-2012 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*!
+ * @defgroup Framebuffer Framebuffer Driver for SDC and ADC.
+ */
+
+/*!
+ * @file mxcfb_sii902x.c
+ *
+ * @brief MXC Frame buffer driver for SII902x
+ *
+ * @ingroup Framebuffer
+ */
+
+/*!
+ * Include files
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/i2c.h>
+#include <linux/ipu.h>
+#include <linux/mxcfb.h>
+#include <linux/fsl_devices.h>
+#include <linux/interrupt.h>
+#include <linux/uaccess.h>
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <mach/mxc_edid.h>
+#include "mxc_dispdrv.h"
+
+#define DISPDRV_SII "sii902x_hdmi"
+
+#define TPI_PIX_CLK_LSB (0x00)
+#define TPI_PIX_CLK_MSB (0x01)
+#define TPI_VERT_FREQ_LSB (0x02)
+#define TPI_VERT_FREQ_MSB (0x03)
+#define TPI_TOTAL_PIX_LSB (0x04)
+#define TPI_TOTAL_PIX_MSB (0x05)
+#define TPI_TOTAL_LINES_LSB (0x06)
+#define TPI_TOTAL_LINES_MSB (0x07)
+#define TPI_PIX_REPETITION (0x08)
+#define TPI_INPUT_FORMAT_REG (0x09)
+#define TPI_OUTPUT_FORMAT_REG (0x0A)
+
+#define TPI_AVI_BYTE_0 (0x0C)
+#define TPI_AVI_BYTE_1 (0x0D)
+#define TPI_AVI_BYTE_2 (0x0E)
+#define TPI_AVI_BYTE_3 (0x0F)
+#define TPI_AVI_BYTE_4 (0x10)
+#define TPI_AVI_BYTE_5 (0x11)
+
+#define TPI_END_TOP_BAR_LSB (0x12)
+#define TPI_END_TOP_BAR_MSB (0x13)
+
+#define TPI_START_BTM_BAR_LSB (0x14)
+#define TPI_START_BTM_BAR_MSB (0x15)
+
+#define TPI_END_LEFT_BAR_LSB (0x16)
+#define TPI_END_LEFT_BAR_MSB (0x17)
+
+#define TPI_END_RIGHT_BAR_LSB (0x18)
+#define TPI_END_RIGHT_BAR_MSB (0x19)
+
+#define TPI_SYSTEM_CONTROL_DATA_REG (0x1A)
+#define TPI_DEVICE_ID (0x1B)
+#define TPI_DEVICE_REV_ID (0x1C)
+#define TPI_RESERVED2 (0x1D)
+#define TPI_DEVICE_POWER_STATE_CTRL_REG (0x1E)
+
+#define TPI_I2S_EN (0x1F)
+#define TPI_I2S_IN_CFG (0x20)
+#define TPI_I2S_CHST_0 (0x21)
+#define TPI_I2S_CHST_1 (0x22)
+#define TPI_I2S_CHST_2 (0x23)
+#define TPI_I2S_CHST_3 (0x24)
+#define TPI_I2S_CHST_4 (0x25)
+
+#define TPI_AUDIO_HANDLING (0x25)
+#define TPI_AUDIO_INTERFACE_REG (0x26)
+#define TPI_AUDIO_SAMPLE_CTRL (0x27)
+
+#define TPI_INTERRUPT_ENABLE_REG (0x3C)
+#define TPI_INTERRUPT_STATUS_REG (0x3D)
+
+#define TPI_INTERNAL_PAGE_REG 0xBC
+#define TPI_INDEXED_OFFSET_REG 0xBD
+#define TPI_INDEXED_VALUE_REG 0xBE
+
+#define MISC_INFO_FRAMES_CTRL (0xBF)
+#define MISC_INFO_FRAMES_TYPE (0xC0)
+#define EN_AND_RPT_AUDIO 0xC2
+#define DISABLE_AUDIO 0x02
+
+#define TPI_ENABLE (0xC7)
+
+#define INDEXED_PAGE_0 0x01
+#define INDEXED_PAGE_1 0x02
+#define INDEXED_PAGE_2 0x03
+
+#define HOT_PLUG_EVENT 0x01
+#define RX_SENSE_EVENT 0x02
+#define HOT_PLUG_STATE 0x04
+#define RX_SENSE_STATE 0x08
+
+#define OUTPUT_MODE_MASK (0x01)
+#define OUTPUT_MODE_DVI (0x00)
+#define OUTPUT_MODE_HDMI (0x01)
+
+#define LINK_INTEGRITY_MODE_MASK 0x40
+#define LINK_INTEGRITY_STATIC (0x00)
+#define LINK_INTEGRITY_DYNAMIC (0x40)
+
+#define TMDS_OUTPUT_CONTROL_MASK 0x10
+#define TMDS_OUTPUT_CONTROL_ACTIVE (0x00)
+#define TMDS_OUTPUT_CONTROL_POWER_DOWN (0x10)
+
+#define AV_MUTE_MASK 0x08
+#define AV_MUTE_NORMAL (0x00)
+#define AV_MUTE_MUTED (0x08)
+
+#define TX_POWER_STATE_MASK 0x3
+#define TX_POWER_STATE_D0 (0x00)
+#define TX_POWER_STATE_D1 (0x01)
+#define TX_POWER_STATE_D2 (0x02)
+#define TX_POWER_STATE_D3 (0x03)
+
+#define AUDIO_MUTE_MASK 0x10
+#define AUDIO_MUTE_NORMAL (0x00)
+#define AUDIO_MUTE_MUTED (0x10)
+
+#define AUDIO_SEL_MASK 0xC0
+#define AUD_IF_SPDIF 0x40
+#define AUD_IF_I2S 0x80
+#define AUD_IF_DSD 0xC0
+#define AUD_IF_HBR 0x04
+
+#define REFER_TO_STREAM_HDR 0x00
+
+#define AUD_PASS_BASIC 0x00
+#define AUD_PASS_ALL 0x01
+#define AUD_DOWN_SAMPLE 0x02
+#define AUD_DO_NOT_CHECK 0x03
+
+#define BITS_IN_RGB 0x00
+#define BITS_IN_YCBCR444 0x01
+#define BITS_IN_YCBCR422 0x02
+
+#define BITS_IN_AUTO_RANGE 0x00
+#define BITS_IN_FULL_RANGE 0x04
+#define BITS_IN_LTD_RANGE 0x08
+
+#define BIT_EN_DITHER_10_8 0x40
+#define BIT_EXTENDED_MODE 0x80
+
+#define SII_EDID_LEN 512
+#define SIZE_AVI_INFOFRAME 0x0E
+#define SIZE_AUDIO_INFOFRAME 0x0F
+
+#define _4_To_3 0x10
+#define _16_To_9 0x20
+#define SAME_AS_AR 0x08
+
+struct sii902x_data {
+ struct platform_device *pdev;
+ struct i2c_client *client;
+ struct mxc_dispdrv_handle *disp_hdmi;
+ struct regulator *io_reg;
+ struct regulator *analog_reg;
+ struct delayed_work det_work;
+ struct fb_info *fbi;
+ struct mxc_edid_cfg edid_cfg;
+ bool cable_plugin;
+ bool rx_powerup;
+ bool need_mode_change;
+ u8 edid[SII_EDID_LEN];
+ struct notifier_block nb;
+
+ u8 power_state;
+ u8 tpivmode[3];
+ u8 pixrep;
+
+ /* SII902x video setting:
+ * 1. hdmi video fmt:
+ * 0 = CEA-861 VIC; 1 = HDMI_VIC; 2 = 3D
+ * 2. vic: video mode index
+ * 3. aspect ratio:
+ * 4x3 or 16x9
+ * 4. color space:
+ * 0 = RGB; 1 = YCbCr4:4:4; 2 = YCbCr4:2:2_16bits;
+ * 3 = YCbCr4:2:2_8bits;4 = xvYCC4:4:4
+ * 5. color depth:
+ * 0 = 8bits; 1 = 10bits; 2 = 12bits; 3 = 16bits
+ * 6. colorimetry:
+ * 0 = 601; 1 = 709
+ * 7. syncmode:
+ * 0 = external HS/VS/DE; 1 = external HS/VS and internal DE;
+ * 2 = embedded sync
+ */
+#define VMD_HDMIFORMAT_CEA_VIC 0x00
+#define VMD_HDMIFORMAT_HDMI_VIC 0x01
+#define VMD_HDMIFORMAT_3D 0x02
+#define VMD_HDMIFORMAT_PC 0x03
+ u8 hdmi_vid_fmt;
+ u8 vic;
+#define VMD_ASPECT_RATIO_4x3 0x01
+#define VMD_ASPECT_RATIO_16x9 0x02
+ u8 aspect_ratio;
+#define RGB 0
+#define YCBCR444 1
+#define YCBCR422_16BITS 2
+#define YCBCR422_8BITS 3
+#define XVYCC444 4
+ u8 icolor_space;
+ u8 ocolor_space;
+#define VMD_COLOR_DEPTH_8BIT 0x00
+#define VMD_COLOR_DEPTH_10BIT 0x01
+#define VMD_COLOR_DEPTH_12BIT 0x02
+#define VMD_COLOR_DEPTH_16BIT 0x03
+ u8 color_depth;
+#define COLORIMETRY_601 0
+#define COLORIMETRY_709 1
+ u8 colorimetry;
+#define EXTERNAL_HSVSDE 0
+#define INTERNAL_DE 1
+#define EMBEDDED_SYNC 2
+ u8 syncmode;
+ u8 threeDstruct;
+ u8 threeDextdata;
+
+#define AMODE_I2S 0
+#define AMODE_SPDIF 1
+#define AMODE_HBR 2
+#define AMODE_DSD 3
+ u8 audio_mode;
+#define ACHANNEL_2CH 1
+#define ACHANNEL_3CH 2
+#define ACHANNEL_4CH 3
+#define ACHANNEL_5CH 4
+#define ACHANNEL_6CH 5
+#define ACHANNEL_7CH 6
+#define ACHANNEL_8CH 7
+ u8 audio_channels;
+ u8 audiofs;
+ u8 audio_word_len;
+ u8 audio_i2s_fmt;
+};
+
+static __attribute__ ((unused)) void dump_regs(struct sii902x_data *sii902x,
+ u8 reg, int len)
+{
+ u8 buf[50];
+ int i;
+
+ i2c_smbus_read_i2c_block_data(sii902x->client, reg, len, buf);
+ for (i = 0; i < len; i++)
+ dev_dbg(&sii902x->client->dev, "reg[0x%02X]: 0x%02X\n",
+ i+reg, buf[i]);
+}
+
+static ssize_t sii902x_show_name(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sii902x_data *sii902x = dev_get_drvdata(dev);
+
+ strcpy(buf, sii902x->fbi->fix.id);
+ sprintf(buf+strlen(buf), "\n");
+
+ return strlen(buf);
+}
+
+static DEVICE_ATTR(fb_name, S_IRUGO, sii902x_show_name, NULL);
+
+static ssize_t sii902x_show_state(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sii902x_data *sii902x = dev_get_drvdata(dev);
+
+ if (sii902x->cable_plugin == false)
+ strcpy(buf, "plugout\n");
+ else
+ strcpy(buf, "plugin\n");
+
+ return strlen(buf);
+}
+
+static DEVICE_ATTR(cable_state, S_IRUGO, sii902x_show_state, NULL);
+
+static ssize_t sii902x_show_edid(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sii902x_data *sii902x = dev_get_drvdata(dev);
+ int i, j, len = 0;
+
+ for (j = 0; j < SII_EDID_LEN/16; j++) {
+ for (i = 0; i < 16; i++)
+ len += sprintf(buf+len, "0x%02X ",
+ sii902x->edid[j*16 + i]);
+ len += sprintf(buf+len, "\n");
+ }
+
+ return len;
+}
+
+static DEVICE_ATTR(edid, S_IRUGO, sii902x_show_edid, NULL);
+
+/*------------------------------------------------------------------------------
+ * Function Description: Write "0" to all bits in TPI offset "Offset" that are set
+ * to "1" in "Pattern"; Leave all other bits in "Offset"
+ * unchanged.
+ *-----------------------------------------------------------------------------
+ */
+void read_clr_write_tpi(struct i2c_client *client, u8 offset, u8 mask)
+{
+ u8 tmp;
+
+ tmp = i2c_smbus_read_byte_data(client, offset);
+ tmp &= ~mask;
+ i2c_smbus_write_byte_data(client, offset, tmp);
+}
+
+/*------------------------------------------------------------------------------
+ * Function Description: Write "1" to all bits in TPI offset "Offset" that are set
+ * to "1" in "Pattern"; Leave all other bits in "Offset"
+ * unchanged.
+ *-----------------------------------------------------------------------------
+ */
+void read_set_write_tpi(struct i2c_client *client, u8 offset, u8 mask)
+{
+ u8 tmp;
+
+ tmp = i2c_smbus_read_byte_data(client, offset);
+ tmp |= mask;
+ i2c_smbus_write_byte_data(client, offset, tmp);
+}
+
+/*------------------------------------------------------------------------------
+ * Function Description: Write "Value" to all bits in TPI offset "Offset" that are set
+ * to "1" in "Mask"; Leave all other bits in "Offset"
+ * unchanged.
+ *----------------------------------------------------------------------------
+ */
+void read_modify_tpi(struct i2c_client *client, u8 offset, u8 mask, u8 value)
+{
+ u8 tmp;
+
+ tmp = i2c_smbus_read_byte_data(client, offset);
+ tmp &= ~mask;
+ tmp |= (value & mask);
+ i2c_smbus_write_byte_data(client, offset, tmp);
+}
+
+/*------------------------------------------------------------------------------
+ * Function Description: Read an indexed register value
+ * Write:
+ * 1. 0xBC => Internal page num
+ * 2. 0xBD => Indexed register offset
+ * Read:
+ * 3. 0xBE => Returns the indexed register value
+ *----------------------------------------------------------------------------
+ */
+int read_idx_reg(struct i2c_client *client, u8 page, u8 regoffset)
+{
+ i2c_smbus_write_byte_data(client, TPI_INTERNAL_PAGE_REG, page);
+ i2c_smbus_write_byte_data(client, TPI_INDEXED_OFFSET_REG, regoffset);
+ return i2c_smbus_read_byte_data(client, TPI_INDEXED_VALUE_REG);
+}
+
+/*------------------------------------------------------------------------------
+ * Function Description: Write a value to an indexed register
+ *
+ * Write:
+ * 1. 0xBC => Internal page num
+ * 2. 0xBD => Indexed register offset
+ * 3. 0xBE => Set the indexed register value
+ *------------------------------------------------------------------------------
+ */
+void write_idx_reg(struct i2c_client *client, u8 page, u8 regoffset, u8 regval)
+{
+ i2c_smbus_write_byte_data(client, TPI_INTERNAL_PAGE_REG, page);
+ i2c_smbus_write_byte_data(client, TPI_INDEXED_OFFSET_REG, regoffset);
+ i2c_smbus_write_byte_data(client, TPI_INDEXED_VALUE_REG, regval);
+}
+
+/*------------------------------------------------------------------------------
+ * Function Description: Write "Value" to all bits in TPI offset "Offset" that are set
+ * to "1" in "Mask"; Leave all other bits in "Offset"
+ * unchanged.
+ *----------------------------------------------------------------------------
+ */
+void read_modify_idx_reg(struct i2c_client *client, u8 page, u8 regoffset, u8 mask, u8 value)
+{
+ u8 tmp;
+
+ i2c_smbus_write_byte_data(client, TPI_INTERNAL_PAGE_REG, page);
+ i2c_smbus_write_byte_data(client, TPI_INDEXED_OFFSET_REG, regoffset);
+ tmp = i2c_smbus_read_byte_data(client, TPI_INDEXED_VALUE_REG);
+ tmp &= ~mask;
+ tmp |= (value & mask);
+ i2c_smbus_write_byte_data(client, TPI_INDEXED_VALUE_REG, tmp);
+}
+
+static void sii902x_set_powerstate(struct sii902x_data *sii902x, u8 state)
+{
+ if (sii902x->power_state != state) {
+ read_modify_tpi(sii902x->client, TPI_DEVICE_POWER_STATE_CTRL_REG,
+ TX_POWER_STATE_MASK, state);
+ sii902x->power_state = state;
+ }
+}
+
+static void sii902x_setAVI(struct sii902x_data *sii902x)
+{
+ u8 avi_data[SIZE_AVI_INFOFRAME];
+ u8 tmp;
+ int i;
+
+ dev_dbg(&sii902x->client->dev, "set AVI frame\n");
+
+ memset(avi_data, 0, SIZE_AVI_INFOFRAME);
+
+ if (sii902x->edid_cfg.cea_ycbcr444)
+ tmp = 2;
+ else if (sii902x->edid_cfg.cea_ycbcr422)
+ tmp = 1;
+ else
+ tmp = 0;
+
+ /* AVI byte1: Y1Y0 (output format) */
+ avi_data[1] = (tmp << 5) & 0x60;
+ /* A0 = 1; Active format identification data is present in the AVI InfoFrame.
+ * S1:S0 = 00;
+ */
+ avi_data[1] |= 0x10;
+
+ if (sii902x->ocolor_space == XVYCC444) {
+ avi_data[2] = 0xC0;
+ if (sii902x->colorimetry == COLORIMETRY_601)
+ avi_data[3] &= ~0x70;
+ else if (sii902x->colorimetry == COLORIMETRY_709)
+ avi_data[3] = (avi_data[3] & ~0x70) | 0x10;
+ } else if (sii902x->ocolor_space != RGB) {
+ if (sii902x->colorimetry == COLORIMETRY_709)
+ avi_data[2] = 0x80;/* AVI byte2: C1C0*/
+ else if (sii902x->colorimetry == COLORIMETRY_601)
+ avi_data[2] = 0x40;/* AVI byte2: C1C0 */
+ } else {/* Carries no data */
+ /* AVI Byte2: C1C0 */
+ avi_data[2] &= ~0xc0; /* colorimetry = 0 */
+ avi_data[3] &= ~0x70; /* Extended colorimetry = 0 */
+ }
+
+ avi_data[4] = sii902x->vic;
+
+ /* Set the Aspect Ration info into the Infoframe Byte 2 */
+ if (sii902x->aspect_ratio == VMD_ASPECT_RATIO_16x9)
+ avi_data[2] |= _16_To_9; /* AVI Byte2: M1M0 */
+ else
+ avi_data[2] |= _4_To_3;
+
+ avi_data[2] |= SAME_AS_AR; /* AVI Byte2: R3..R1 - Set to "Same as Picture Aspect Ratio" */
+ avi_data[5] = sii902x->pixrep; /* AVI Byte5: Pixel Replication - PR3..PR0 */
+
+ /* Calculate AVI InfoFrame ChecKsum */
+ avi_data[0] = 0x82 + 0x02 + 0x0D;
+ for (i = 1; i < SIZE_AVI_INFOFRAME; i++)
+ avi_data[0] += avi_data[i];
+ avi_data[0] = 0x100 - avi_data[0];
+
+ /* Write the Inforframe data to the TPI Infoframe registers */
+ for (i = 0; i < SIZE_AVI_INFOFRAME; i++)
+ i2c_smbus_write_byte_data(sii902x->client,
+ TPI_AVI_BYTE_0 + i, avi_data[i]);
+
+ dump_regs(sii902x, TPI_AVI_BYTE_0, SIZE_AVI_INFOFRAME);
+}
+
+#define TYPE_AUDIO_INFOFRAMES 0x84
+#define AUDIO_INFOFRAMES_VERSION 0x01
+#define AUDIO_INFOFRAMES_LENGTH 0x0A
+/*------------------------------------------------------------------------------
+* Function Description: Load Audio InfoFrame data into registers and send to sink
+*
+* Accepts: (1) Channel count
+* (2) speaker configuration per CEA-861D Tables 19, 20
+* (3) Coding type: 0x09 for DSD Audio. 0 (refer to stream header) for all the rest
+* (4) Sample Frequency. Non zero for HBR only
+* (5) Audio Sample Length. Non zero for HBR only.
+*------------------------------------------------------------------------------
+*/
+static void sii902x_setAIF(struct sii902x_data *sii902x,
+ u8 codingtype, u8 sample_size, u8 sample_freq,
+ u8 speaker_cfg)
+{
+ u8 aif_data[SIZE_AUDIO_INFOFRAME];
+ u8 channel_count = sii902x->audio_channels & 0x07;
+ int i;
+
+ dev_dbg(&sii902x->client->dev, "set AIF frame\n");
+
+ memset(aif_data, 0, SIZE_AUDIO_INFOFRAME);
+
+ /* Disbale MPEG/Vendor Specific InfoFrames */
+ i2c_smbus_write_byte_data(sii902x->client, MISC_INFO_FRAMES_CTRL, DISABLE_AUDIO);
+
+ aif_data[0] = TYPE_AUDIO_INFOFRAMES;
+ aif_data[1] = AUDIO_INFOFRAMES_VERSION;
+ aif_data[2] = AUDIO_INFOFRAMES_LENGTH;
+ /* Calculate checksum - 0x84 + 0x01 + 0x0A */
+ aif_data[3] = TYPE_AUDIO_INFOFRAMES +
+ AUDIO_INFOFRAMES_VERSION + AUDIO_INFOFRAMES_LENGTH;
+
+ aif_data[4] = channel_count; /* 0 for "Refer to Stream Header" or for 2 Channels. 0x07 for 8 Channels*/
+ aif_data[4] |= (codingtype << 4); /* 0xC7[7:4] == 0b1001 for DSD Audio */
+ aif_data[5] = ((sample_freq & 0x07) << 2) | (sample_size & 0x03);
+ aif_data[7] = speaker_cfg;
+
+ for (i = 4; i < SIZE_AUDIO_INFOFRAME; i++)
+ aif_data[3] += aif_data[i];
+
+ aif_data[3] = 0x100 - aif_data[3];
+
+ /* Re-enable Audio InfoFrame transmission and repeat */
+ i2c_smbus_write_byte_data(sii902x->client, MISC_INFO_FRAMES_CTRL, EN_AND_RPT_AUDIO);
+
+ for (i = 0; i < SIZE_AUDIO_INFOFRAME; i++)
+ i2c_smbus_write_byte_data(sii902x->client,
+ MISC_INFO_FRAMES_TYPE + i, aif_data[i]);
+
+ dump_regs(sii902x, MISC_INFO_FRAMES_TYPE, SIZE_AUDIO_INFOFRAME);
+}
+
+static void sii902x_setaudio(struct sii902x_data *sii902x)
+{
+ dev_dbg(&sii902x->client->dev, "set audio\n");
+
+ /* mute audio */
+ read_modify_tpi(sii902x->client, TPI_AUDIO_INTERFACE_REG,
+ AUDIO_MUTE_MASK, AUDIO_MUTE_MUTED);
+ if (sii902x->audio_mode == AMODE_I2S) {
+ read_modify_tpi(sii902x->client, TPI_AUDIO_INTERFACE_REG,
+ AUDIO_SEL_MASK, AUD_IF_I2S);
+ i2c_smbus_write_byte_data(sii902x->client, TPI_AUDIO_HANDLING,
+ 0x08 | AUD_DO_NOT_CHECK);
+ } else {
+ read_modify_tpi(sii902x->client, TPI_AUDIO_INTERFACE_REG,
+ AUDIO_SEL_MASK, AUD_IF_SPDIF);
+ i2c_smbus_write_byte_data(sii902x->client, TPI_AUDIO_HANDLING,
+ AUD_PASS_BASIC);
+ }
+
+ if (sii902x->audio_channels == ACHANNEL_2CH)
+ read_clr_write_tpi(sii902x->client, TPI_AUDIO_INTERFACE_REG, 0x20);
+ else
+ read_set_write_tpi(sii902x->client, TPI_AUDIO_INTERFACE_REG, 0x20);
+
+ if (sii902x->audio_mode == AMODE_I2S) {
+ /* I2S - Map channels */
+ i2c_smbus_write_byte_data(sii902x->client, TPI_I2S_EN, 0x80);
+
+ if (sii902x->audio_channels > ACHANNEL_2CH)
+ i2c_smbus_write_byte_data(sii902x->client, TPI_I2S_EN, 0x91);
+
+ if (sii902x->audio_channels > ACHANNEL_4CH)
+ i2c_smbus_write_byte_data(sii902x->client, TPI_I2S_EN, 0xA2);
+
+ if (sii902x->audio_channels > ACHANNEL_6CH)
+ i2c_smbus_write_byte_data(sii902x->client, TPI_I2S_EN, 0xB3);
+
+ /* I2S - Stream Header Settings */
+ i2c_smbus_write_byte_data(sii902x->client, TPI_I2S_CHST_0, 0x00);
+ i2c_smbus_write_byte_data(sii902x->client, TPI_I2S_CHST_1, 0x00);
+ i2c_smbus_write_byte_data(sii902x->client, TPI_I2S_CHST_2, 0x00);
+ i2c_smbus_write_byte_data(sii902x->client, TPI_I2S_CHST_3, sii902x->audiofs);
+ i2c_smbus_write_byte_data(sii902x->client, TPI_I2S_CHST_4,
+ (sii902x->audiofs << 4) | sii902x->audio_word_len);
+
+ /* added for 16bit auido noise issue */
+ write_idx_reg(sii902x->client, INDEXED_PAGE_1, 0x24, sii902x->audio_word_len);
+
+ /* I2S - Input Configuration */
+ i2c_smbus_write_byte_data(sii902x->client, TPI_I2S_IN_CFG, sii902x->audio_i2s_fmt);
+ }
+
+ i2c_smbus_write_byte_data(sii902x->client, TPI_AUDIO_SAMPLE_CTRL, REFER_TO_STREAM_HDR);
+
+ sii902x_setAIF(sii902x, REFER_TO_STREAM_HDR, REFER_TO_STREAM_HDR, REFER_TO_STREAM_HDR, 0x00);
+
+ /* unmute audio */
+ read_modify_tpi(sii902x->client, TPI_AUDIO_INTERFACE_REG, AUDIO_MUTE_MASK, AUDIO_MUTE_NORMAL);
+}
+
+static void sii902x_setup(struct sii902x_data *sii902x, struct fb_info *fbi)
+{
+ u16 data[4];
+ u32 refresh;
+ u8 *tmp;
+ mm_segment_t old_fs;
+ unsigned int fmt;
+ int i;
+
+ dev_dbg(&sii902x->client->dev, "setup..\n");
+
+ sii902x->vic = mxc_edid_var_to_vic(&fbi->var);
+
+ /* set TPI video mode */
+ data[0] = PICOS2KHZ(fbi->var.pixclock) / 10;
+ data[2] = fbi->var.hsync_len + fbi->var.left_margin +
+ fbi->var.xres + fbi->var.right_margin;
+ data[3] = fbi->var.vsync_len + fbi->var.upper_margin +
+ fbi->var.yres + fbi->var.lower_margin;
+ refresh = data[2] * data[3];
+ refresh = (PICOS2KHZ(fbi->var.pixclock) * 1000) / refresh;
+ data[1] = refresh * 100;
+ tmp = (u8 *)data;
+ for (i = 0; i < 8; i++)
+ i2c_smbus_write_byte_data(sii902x->client, i, tmp[i]);
+
+ dump_regs(sii902x, 0, 8);
+
+ if (fbi->fbops->fb_ioctl) {
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ fbi->fbops->fb_ioctl(fbi, MXCFB_GET_DIFMT, (unsigned long)&fmt);
+ set_fs(old_fs);
+ if (fmt == IPU_PIX_FMT_VYU444) {
+ sii902x->icolor_space = YCBCR444;
+ dev_dbg(&sii902x->client->dev, "input color space YUV\n");
+ } else {
+ sii902x->icolor_space = RGB;
+ dev_dbg(&sii902x->client->dev, "input color space RGB\n");
+ }
+ }
+
+ /* reg 0x08: input bus/pixel: full pixel wide (24bit), rising edge */
+ sii902x->tpivmode[0] = 0x70;
+ /* reg 0x09: Set input format */
+ if (sii902x->icolor_space == RGB)
+ sii902x->tpivmode[1] =
+ (((BITS_IN_RGB | BITS_IN_AUTO_RANGE) & ~BIT_EN_DITHER_10_8) & ~BIT_EXTENDED_MODE);
+ else if (sii902x->icolor_space == YCBCR444)
+ sii902x->tpivmode[1] =
+ (((BITS_IN_YCBCR444 | BITS_IN_AUTO_RANGE) & ~BIT_EN_DITHER_10_8) & ~BIT_EXTENDED_MODE);
+ else if ((sii902x->icolor_space == YCBCR422_16BITS) || (sii902x->icolor_space == YCBCR422_8BITS))
+ sii902x->tpivmode[1] =
+ (((BITS_IN_YCBCR422 | BITS_IN_AUTO_RANGE) & ~BIT_EN_DITHER_10_8) & ~BIT_EXTENDED_MODE);
+ /* reg 0x0a: set output format to RGB */
+ sii902x->tpivmode[2] = 0x00;
+
+ if (fbi->var.vmode & FB_VMODE_ASPECT_16_9)
+ sii902x->aspect_ratio = VMD_ASPECT_RATIO_16x9;
+ else if (fbi->var.vmode & FB_VMODE_ASPECT_4_3)
+ sii902x->aspect_ratio = VMD_ASPECT_RATIO_4x3;
+ else if (fbi->var.xres/16 == fbi->var.yres/9)
+ sii902x->aspect_ratio = VMD_ASPECT_RATIO_16x9;
+ else
+ sii902x->aspect_ratio = VMD_ASPECT_RATIO_4x3;
+
+ if ((sii902x->vic == 6) || (sii902x->vic == 7) ||
+ (sii902x->vic == 21) || (sii902x->vic == 22) ||
+ (sii902x->vic == 2) || (sii902x->vic == 3) ||
+ (sii902x->vic == 17) || (sii902x->vic == 18)) {
+ sii902x->tpivmode[2] &= ~0x10; /*BT.601*/
+ sii902x->colorimetry = COLORIMETRY_601;
+ sii902x->aspect_ratio = VMD_ASPECT_RATIO_4x3;
+ } else {
+ sii902x->tpivmode[2] |= 0x10; /*BT.709*/
+ sii902x->colorimetry = COLORIMETRY_709;
+ }
+
+ if ((sii902x->vic == 10) || (sii902x->vic == 11) ||
+ (sii902x->vic == 12) || (sii902x->vic == 13) ||
+ (sii902x->vic == 14) || (sii902x->vic == 15) ||
+ (sii902x->vic == 25) || (sii902x->vic == 26) ||
+ (sii902x->vic == 27) || (sii902x->vic == 28) ||
+ (sii902x->vic == 29) || (sii902x->vic == 30) ||
+ (sii902x->vic == 35) || (sii902x->vic == 36) ||
+ (sii902x->vic == 37) || (sii902x->vic == 38))
+ sii902x->pixrep = 1;
+ else
+ sii902x->pixrep = 0;
+
+ dev_dbg(&sii902x->client->dev, "vic %d\n", sii902x->vic);
+ dev_dbg(&sii902x->client->dev, "pixrep %d\n", sii902x->pixrep);
+ if (sii902x->aspect_ratio == VMD_ASPECT_RATIO_4x3) {
+ dev_dbg(&sii902x->client->dev, "aspect 4:3\n");
+ } else {
+ dev_dbg(&sii902x->client->dev, "aspect 16:9\n");
+ }
+ if (sii902x->colorimetry == COLORIMETRY_601) {
+ dev_dbg(&sii902x->client->dev, "COLORIMETRY_601\n");
+ } else {
+ dev_dbg(&sii902x->client->dev, "COLORIMETRY_709\n");
+ }
+ dev_dbg(&sii902x->client->dev, "hdmi capbility %d\n", sii902x->edid_cfg.hdmi_cap);
+
+ sii902x->ocolor_space = RGB;
+ if (sii902x->edid_cfg.hdmi_cap) {
+ if (sii902x->edid_cfg.cea_ycbcr444) {
+ sii902x->ocolor_space = YCBCR444;
+ sii902x->tpivmode[2] |= 0x1; /*Ycbcr444*/
+ } else if (sii902x->edid_cfg.cea_ycbcr422) {
+ sii902x->ocolor_space = YCBCR422_8BITS;
+ sii902x->tpivmode[2] |= 0x2; /*Ycbcr422*/
+ }
+ }
+
+ dev_dbg(&sii902x->client->dev, "write reg 0x08 0X%2X\n", sii902x->tpivmode[0]);
+ dev_dbg(&sii902x->client->dev, "write reg 0x09 0X%2X\n", sii902x->tpivmode[1]);
+ dev_dbg(&sii902x->client->dev, "write reg 0x0a 0X%2X\n", sii902x->tpivmode[2]);
+
+ i2c_smbus_write_byte_data(sii902x->client, TPI_PIX_REPETITION, sii902x->tpivmode[0]);
+ i2c_smbus_write_byte_data(sii902x->client, TPI_INPUT_FORMAT_REG, sii902x->tpivmode[1]);
+ i2c_smbus_write_byte_data(sii902x->client, TPI_OUTPUT_FORMAT_REG, sii902x->tpivmode[2]);
+
+ /* goto state D0*/
+ sii902x_set_powerstate(sii902x, TX_POWER_STATE_D0);
+
+ if (sii902x->edid_cfg.hdmi_cap) {
+ sii902x_setAVI(sii902x);
+ sii902x_setaudio(sii902x);
+ } else {
+ /* set last byte of TPI AVI InfoFrame for TPI AVI I/O format to take effect ?? */
+ i2c_smbus_write_byte_data(sii902x->client, TPI_END_RIGHT_BAR_MSB, 0x00);
+
+ /* mute audio */
+ read_modify_tpi(sii902x->client, TPI_AUDIO_INTERFACE_REG,
+ AUDIO_MUTE_MASK, AUDIO_MUTE_MUTED);
+ }
+}
+
+#ifdef CONFIG_FB_MODE_HELPERS
+static int sii902x_read_edid(struct sii902x_data *sii902x,
+ struct fb_info *fbi)
+{
+ int old, dat, ret, cnt = 100;
+ unsigned short addr = 0x50;
+ u8 edid_old[SII_EDID_LEN];
+
+ old = i2c_smbus_read_byte_data(sii902x->client, TPI_SYSTEM_CONTROL_DATA_REG);
+
+ i2c_smbus_write_byte_data(sii902x->client, TPI_SYSTEM_CONTROL_DATA_REG, old | 0x4);
+ do {
+ cnt--;
+ msleep(10);
+ dat = i2c_smbus_read_byte_data(sii902x->client, TPI_SYSTEM_CONTROL_DATA_REG);
+ } while ((!(dat & 0x2)) && cnt);
+
+ if (!cnt) {
+ ret = -1;
+ goto done;
+ }
+
+ i2c_smbus_write_byte_data(sii902x->client, TPI_SYSTEM_CONTROL_DATA_REG, old | 0x06);
+
+ /* save old edid */
+ memcpy(edid_old, sii902x->edid, SII_EDID_LEN);
+
+ /* edid reading */
+ ret = mxc_edid_read(sii902x->client->adapter, addr,
+ sii902x->edid, &sii902x->edid_cfg, fbi);
+
+ cnt = 100;
+ do {
+ cnt--;
+ i2c_smbus_write_byte_data(sii902x->client, TPI_SYSTEM_CONTROL_DATA_REG, old & ~0x6);
+ msleep(10);
+ dat = i2c_smbus_read_byte_data(sii902x->client, TPI_SYSTEM_CONTROL_DATA_REG);
+ } while ((dat & 0x6) && cnt);
+
+ if (!cnt)
+ ret = -1;
+
+done:
+ i2c_smbus_write_byte_data(sii902x->client, TPI_SYSTEM_CONTROL_DATA_REG, old);
+
+ if (!memcmp(edid_old, sii902x->edid, SII_EDID_LEN))
+ ret = -2;
+ return ret;
+}
+#else
+static int sii902x_read_edid(struct sii902x_data *sii902x,
+ struct fb_info *fbi)
+{
+ return -1;
+}
+#endif
+
+static void sii902x_enable_tmds(struct sii902x_data *sii902x)
+{
+ /* goto state D0*/
+ sii902x_set_powerstate(sii902x, TX_POWER_STATE_D0);
+
+ /* Turn on DVI or HDMI */
+ if (sii902x->edid_cfg.hdmi_cap)
+ read_modify_tpi(sii902x->client, TPI_SYSTEM_CONTROL_DATA_REG,
+ OUTPUT_MODE_MASK, OUTPUT_MODE_HDMI);
+ else
+ read_modify_tpi(sii902x->client, TPI_SYSTEM_CONTROL_DATA_REG,
+ OUTPUT_MODE_MASK, OUTPUT_MODE_DVI);
+
+ read_modify_tpi(sii902x->client, TPI_SYSTEM_CONTROL_DATA_REG,
+ LINK_INTEGRITY_MODE_MASK | TMDS_OUTPUT_CONTROL_MASK | AV_MUTE_MASK,
+ LINK_INTEGRITY_DYNAMIC | TMDS_OUTPUT_CONTROL_ACTIVE | AV_MUTE_NORMAL);
+
+ i2c_smbus_write_byte_data(sii902x->client, TPI_PIX_REPETITION,
+ sii902x->tpivmode[0]);
+}
+
+static void sii902x_disable_tmds(struct sii902x_data *sii902x)
+{
+ read_modify_tpi(sii902x->client, TPI_SYSTEM_CONTROL_DATA_REG,
+ TMDS_OUTPUT_CONTROL_MASK | AV_MUTE_MASK | OUTPUT_MODE_MASK,
+ TMDS_OUTPUT_CONTROL_POWER_DOWN | AV_MUTE_MUTED | OUTPUT_MODE_DVI);
+
+ /* goto state D2*/
+ sii902x_set_powerstate(sii902x, TX_POWER_STATE_D2);
+}
+
+static void sii902x_poweron(struct sii902x_data *sii902x)
+{
+ struct fsl_mxc_lcd_platform_data *plat = sii902x->client->dev.platform_data;
+
+ dev_dbg(&sii902x->client->dev, "power on\n");
+
+ /* Enable pins to HDMI */
+ if (plat->enable_pins)
+ plat->enable_pins();
+
+ if (sii902x->rx_powerup)
+ sii902x_enable_tmds(sii902x);
+}
+
+static void sii902x_poweroff(struct sii902x_data *sii902x)
+{
+ struct fsl_mxc_lcd_platform_data *plat = sii902x->client->dev.platform_data;
+
+ dev_dbg(&sii902x->client->dev, "power off\n");
+
+ /* Disable pins to HDMI */
+ if (plat->disable_pins)
+ plat->disable_pins();
+
+ if (sii902x->rx_powerup)
+ sii902x_disable_tmds(sii902x);
+}
+
+static void sii902x_rx_powerup(struct sii902x_data *sii902x)
+{
+
+ dev_dbg(&sii902x->client->dev, "rx power up\n");
+
+ if (sii902x->need_mode_change) {
+ sii902x->fbi->var.activate |= FB_ACTIVATE_FORCE;
+ console_lock();
+ sii902x->fbi->flags |= FBINFO_MISC_USEREVENT;
+ fb_set_var(sii902x->fbi, &sii902x->fbi->var);
+ sii902x->fbi->flags &= ~FBINFO_MISC_USEREVENT;
+ console_unlock();
+ sii902x->need_mode_change = false;
+ }
+
+ sii902x_enable_tmds(sii902x);
+
+ sii902x->rx_powerup = true;
+}
+
+static void sii902x_rx_powerdown(struct sii902x_data *sii902x)
+{
+ dev_dbg(&sii902x->client->dev, "rx power down\n");
+
+ sii902x_disable_tmds(sii902x);
+
+ sii902x->rx_powerup = false;
+}
+
+static int sii902x_cable_connected(struct sii902x_data *sii902x)
+{
+ int ret;
+
+ dev_dbg(&sii902x->client->dev, "cable connected\n");
+
+ sii902x->cable_plugin = true;
+
+ /* edid read */
+ ret = sii902x_read_edid(sii902x, sii902x->fbi);
+ if (ret == -1)
+ dev_err(&sii902x->client->dev,
+ "read edid fail\n");
+ else if (ret == -2)
+ dev_info(&sii902x->client->dev,
+ "same edid\n");
+ else {
+ if (sii902x->fbi->monspecs.modedb_len > 0) {
+ int i;
+ const struct fb_videomode *mode;
+ struct fb_videomode m;
+
+ fb_destroy_modelist(&sii902x->fbi->modelist);
+
+ for (i = 0; i < sii902x->fbi->monspecs.modedb_len; i++) {
+ /*FIXME now we do not support interlaced mode */
+ if (!(sii902x->fbi->monspecs.modedb[i].vmode & FB_VMODE_INTERLACED))
+ fb_add_videomode(&sii902x->fbi->monspecs.modedb[i],
+ &sii902x->fbi->modelist);
+ }
+
+ fb_var_to_videomode(&m, &sii902x->fbi->var);
+ mode = fb_find_nearest_mode(&m,
+ &sii902x->fbi->modelist);
+
+ fb_videomode_to_var(&sii902x->fbi->var, mode);
+ sii902x->need_mode_change = true;
+ }
+ }
+
+ /* ?? remain it for control back door register */
+ read_modify_idx_reg(sii902x->client, INDEXED_PAGE_0, 0x0a, 0x08, 0x08);
+
+ return 0;
+}
+
+static void sii902x_cable_disconnected(struct sii902x_data *sii902x)
+{
+ dev_dbg(&sii902x->client->dev, "cable disconnected\n");
+ sii902x_rx_powerdown(sii902x);
+ sii902x->cable_plugin = false;
+}
+
+static void det_worker(struct work_struct *work)
+{
+ struct delayed_work *delay_work = to_delayed_work(work);
+ struct sii902x_data *sii902x =
+ container_of(delay_work, struct sii902x_data, det_work);
+ int status;
+ char event_string[16];
+ char *envp[] = { event_string, NULL };
+
+ status = i2c_smbus_read_byte_data(sii902x->client, TPI_INTERRUPT_STATUS_REG);
+
+ /* check cable status */
+ if (status & HOT_PLUG_EVENT) {
+ /* cable connection changes */
+ if ((status & HOT_PLUG_STATE) != sii902x->cable_plugin) {
+ if (status & HOT_PLUG_STATE) {
+ sprintf(event_string, "EVENT=plugin");
+ sii902x_cable_connected(sii902x);
+ } else {
+ sprintf(event_string, "EVENT=plugout");
+ sii902x_cable_disconnected(sii902x);
+ }
+ kobject_uevent_env(&sii902x->pdev->dev.kobj, KOBJ_CHANGE, envp);
+ }
+ }
+
+ /* check rx power */
+ if (((status & RX_SENSE_STATE) >> 3) != sii902x->rx_powerup) {
+ if (sii902x->cable_plugin) {
+ if (status & RX_SENSE_STATE)
+ sii902x_rx_powerup(sii902x);
+ else
+ sii902x_rx_powerdown(sii902x);
+ }
+ }
+
+ /* clear interrupt pending status */
+ i2c_smbus_write_byte_data(sii902x->client, TPI_INTERRUPT_STATUS_REG, status);
+}
+
+static irqreturn_t sii902x_detect_handler(int irq, void *data)
+{
+ struct sii902x_data *sii902x = data;
+
+ schedule_delayed_work(&(sii902x->det_work), msecs_to_jiffies(20));
+
+ return IRQ_HANDLED;
+}
+
+static int sii902x_fb_event(struct notifier_block *nb, unsigned long val, void *v)
+{
+ struct fb_event *event = v;
+ struct fb_info *fbi = event->info;
+ struct sii902x_data *sii902x = container_of(nb, struct sii902x_data, nb);
+
+ if (strcmp(event->info->fix.id, sii902x->fbi->fix.id))
+ return 0;
+
+ switch (val) {
+ case FB_EVENT_MODE_CHANGE:
+ sii902x_setup(sii902x, fbi);
+ break;
+ case FB_EVENT_BLANK:
+ if (*((int *)event->data) == FB_BLANK_UNBLANK)
+ sii902x_poweron(sii902x);
+ else
+ sii902x_poweroff(sii902x);
+ break;
+ }
+ return 0;
+}
+
+static int sii902x_TPI_init(struct i2c_client *client)
+{
+ struct fsl_mxc_lcd_platform_data *plat = client->dev.platform_data;
+ u8 devid = 0;
+ u16 wid = 0;
+
+ if (plat->reset)
+ plat->reset();
+
+ /* sii902x back door register - Set terminations to default */
+ i2c_smbus_write_byte_data(client, 0x82, 0x25);
+ /* sii902x back door register - HW debounce to 64ms (0x14) */
+ i2c_smbus_write_byte_data(client, 0x7c, 0x14);
+
+ /* Set 902x in hardware TPI mode on and jump out of D3 state */
+ if (i2c_smbus_write_byte_data(client, TPI_ENABLE, 0x00) < 0) {
+ dev_err(&client->dev,
+ "cound not find device\n");
+ return -ENODEV;
+ }
+
+ msleep(100);
+
+ /* read device ID */
+ devid = read_idx_reg(client, INDEXED_PAGE_0, 0x03);
+ wid = devid;
+ wid <<= 8;
+ devid = read_idx_reg(client, INDEXED_PAGE_0, 0x02);
+ wid |= devid;
+ devid = i2c_smbus_read_byte_data(client, TPI_DEVICE_ID);
+
+ if (devid == 0xB0)
+ dev_info(&client->dev, "found device %04X", wid);
+ else {
+ dev_err(&client->dev, "cound not find device\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int sii902x_disp_init(struct mxc_dispdrv_handle *disp,
+ struct mxc_dispdrv_setting *setting)
+{
+ int ret = 0;
+ struct sii902x_data *sii902x = mxc_dispdrv_getdata(disp);
+ struct fsl_mxc_lcd_platform_data *plat = sii902x->client->dev.platform_data;
+ bool found = false;
+ static bool inited;
+
+ if (inited)
+ return -EBUSY;
+
+ inited = true;
+
+ setting->dev_id = plat->ipu_id;
+ setting->disp_id = plat->disp_id;
+ setting->if_fmt = IPU_PIX_FMT_RGB24;
+
+ sii902x->fbi = setting->fbi;
+ sii902x->power_state = TX_POWER_STATE_D2;
+ sii902x->icolor_space = RGB;
+ sii902x->audio_mode = AMODE_SPDIF;
+ sii902x->audio_channels = ACHANNEL_2CH;
+
+ sii902x->pdev = platform_device_register_simple("sii902x", 0, NULL, 0);
+ if (IS_ERR(sii902x->pdev)) {
+ dev_err(&sii902x->client->dev,
+ "Unable to register Sii902x as a platform device\n");
+ ret = PTR_ERR(sii902x->pdev);
+ goto register_pltdev_failed;
+ }
+
+ if (plat->io_reg) {
+ sii902x->io_reg = regulator_get(&sii902x->client->dev, plat->io_reg);
+ if (!IS_ERR(sii902x->io_reg)) {
+ regulator_set_voltage(sii902x->io_reg, 3300000, 3300000);
+ regulator_enable(sii902x->io_reg);
+ }
+ }
+ if (plat->analog_reg) {
+ sii902x->analog_reg = regulator_get(&sii902x->client->dev, plat->analog_reg);
+ if (!IS_ERR(sii902x->analog_reg)) {
+ regulator_set_voltage(sii902x->analog_reg, 1300000, 1300000);
+ regulator_enable(sii902x->analog_reg);
+ }
+ }
+
+ /* Claim HDMI pins */
+ if (plat->get_pins)
+ if (!plat->get_pins()) {
+ ret = -EACCES;
+ goto get_pins_failed;
+ }
+
+ ret = sii902x_TPI_init(sii902x->client);
+ if (ret < 0)
+ goto init_failed;
+
+ /* try to read edid */
+ ret = sii902x_read_edid(sii902x, sii902x->fbi);
+ if (ret < 0)
+ dev_warn(&sii902x->client->dev, "Can not read edid\n");
+ else {
+ INIT_LIST_HEAD(&sii902x->fbi->modelist);
+ if (sii902x->fbi->monspecs.modedb_len > 0) {
+ int i;
+ const struct fb_videomode *mode;
+ struct fb_videomode m;
+
+ for (i = 0; i < sii902x->fbi->monspecs.modedb_len; i++) {
+ /*FIXME now we do not support interlaced mode */
+ if (!(sii902x->fbi->monspecs.modedb[i].vmode
+ & FB_VMODE_INTERLACED))
+ fb_add_videomode(
+ &sii902x->fbi->monspecs.modedb[i],
+ &sii902x->fbi->modelist);
+ }
+
+ fb_find_mode(&sii902x->fbi->var, sii902x->fbi, setting->dft_mode_str,
+ NULL, 0, NULL, setting->default_bpp);
+
+ fb_var_to_videomode(&m, &sii902x->fbi->var);
+ mode = fb_find_nearest_mode(&m,
+ &sii902x->fbi->modelist);
+ fb_videomode_to_var(&sii902x->fbi->var, mode);
+ found = true;
+ }
+
+ }
+
+ if (!found) {
+ ret = fb_find_mode(&sii902x->fbi->var, sii902x->fbi, setting->dft_mode_str,
+ NULL, 0, NULL, setting->default_bpp);
+ if (!ret) {
+ ret = -EINVAL;
+ goto find_mode_failed;
+ }
+ }
+
+ if (sii902x->client->irq) {
+ ret = request_irq(sii902x->client->irq, sii902x_detect_handler,
+ IRQF_TRIGGER_FALLING,
+ "SII902x_det", sii902x);
+ if (ret < 0)
+ dev_warn(&sii902x->client->dev,
+ "cound not request det irq %d\n",
+ sii902x->client->irq);
+ else {
+ /*enable cable hot plug irq*/
+ i2c_smbus_write_byte_data(sii902x->client,
+ TPI_INTERRUPT_ENABLE_REG,
+ HOT_PLUG_EVENT | RX_SENSE_EVENT);
+ INIT_DELAYED_WORK(&(sii902x->det_work), det_worker);
+ /*clear hot plug event status*/
+ i2c_smbus_write_byte_data(sii902x->client,
+ TPI_INTERRUPT_STATUS_REG,
+ HOT_PLUG_EVENT | RX_SENSE_EVENT);
+ }
+
+ ret = device_create_file(&sii902x->pdev->dev, &dev_attr_fb_name);
+ if (ret < 0)
+ dev_warn(&sii902x->client->dev,
+ "cound not create sys node for fb name\n");
+ ret = device_create_file(&sii902x->pdev->dev, &dev_attr_cable_state);
+ if (ret < 0)
+ dev_warn(&sii902x->client->dev,
+ "cound not create sys node for cable state\n");
+ ret = device_create_file(&sii902x->pdev->dev, &dev_attr_edid);
+ if (ret < 0)
+ dev_warn(&sii902x->client->dev,
+ "cound not create sys node for edid\n");
+
+ dev_set_drvdata(&sii902x->pdev->dev, sii902x);
+ }
+
+ sii902x->nb.notifier_call = sii902x_fb_event;
+ ret = fb_register_client(&sii902x->nb);
+ if (ret < 0)
+ goto reg_fbclient_failed;
+
+ return ret;
+
+reg_fbclient_failed:
+find_mode_failed:
+init_failed:
+get_pins_failed:
+ platform_device_unregister(sii902x->pdev);
+register_pltdev_failed:
+ return ret;
+}
+
+static void sii902x_disp_deinit(struct mxc_dispdrv_handle *disp)
+{
+ struct sii902x_data *sii902x = mxc_dispdrv_getdata(disp);
+ struct fsl_mxc_lcd_platform_data *plat = sii902x->client->dev.platform_data;
+
+ if (sii902x->client->irq)
+ free_irq(sii902x->client->irq, sii902x);
+
+ fb_unregister_client(&sii902x->nb);
+
+ sii902x_poweroff(sii902x);
+
+ /* Release HDMI pins */
+ if (plat->put_pins)
+ plat->put_pins();
+
+ platform_device_unregister(sii902x->pdev);
+
+ kfree(sii902x);
+}
+
+static struct mxc_dispdrv_driver sii902x_drv = {
+ .name = DISPDRV_SII,
+ .init = sii902x_disp_init,
+ .deinit = sii902x_disp_deinit,
+};
+
+static int __devinit sii902x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct sii902x_data *sii902x;
+ int ret = 0;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE | I2C_FUNC_I2C))
+ return -ENODEV;
+
+ sii902x = kzalloc(sizeof(struct sii902x_data), GFP_KERNEL);
+ if (!sii902x) {
+ ret = -ENOMEM;
+ goto alloc_failed;
+ }
+
+ sii902x->client = client;
+
+ sii902x->disp_hdmi = mxc_dispdrv_register(&sii902x_drv);
+ mxc_dispdrv_setdata(sii902x->disp_hdmi, sii902x);
+
+ i2c_set_clientdata(client, sii902x);
+
+alloc_failed:
+ return ret;
+}
+
+static int __devexit sii902x_remove(struct i2c_client *client)
+{
+ struct sii902x_data *sii902x = i2c_get_clientdata(client);
+
+ mxc_dispdrv_puthandle(sii902x->disp_hdmi);
+ mxc_dispdrv_unregister(sii902x->disp_hdmi);
+ kfree(sii902x);
+ return 0;
+}
+
+static const struct i2c_device_id sii902x_id[] = {
+ { "sii902x", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, sii902x_id);
+
+static struct i2c_driver sii902x_i2c_driver = {
+ .driver = {
+ .name = "sii902x",
+ },
+ .probe = sii902x_probe,
+ .remove = sii902x_remove,
+ .id_table = sii902x_id,
+};
+
+static int __init sii902x_init(void)
+{
+ return i2c_add_driver(&sii902x_i2c_driver);
+}
+
+static void __exit sii902x_exit(void)
+{
+ i2c_del_driver(&sii902x_i2c_driver);
+}
+
+module_init(sii902x_init);
+module_exit(sii902x_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("SII902x DVI/HDMI driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/mxc/mxcfb_sii902x_elcdif.c b/drivers/video/mxc/mxcfb_sii902x_elcdif.c
new file mode 100644
index 00000000..ecba5b8b
--- /dev/null
+++ b/drivers/video/mxc/mxcfb_sii902x_elcdif.c
@@ -0,0 +1,538 @@
+/*
+ * Copyright (C) 2010-2013 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*!
+ * @defgroup Framebuffer Framebuffer Driver for SDC and ADC.
+ */
+
+/*!
+ * @file mxcfb_sii902x_elcdif.c
+ *
+ * @brief MXC ELCDIF Frame buffer driver for SII902x
+ *
+ * @ingroup Framebuffer
+ */
+
+/*!
+ * Include files
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/i2c.h>
+#include <linux/mxcfb.h>
+#include <linux/fsl_devices.h>
+#include <linux/interrupt.h>
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <mach/mxc_edid.h>
+
+#define SII_EDID_LEN 512
+#define MXC_ENABLE 1
+#define MXC_DISABLE 2
+
+struct sii902x_data {
+ struct platform_device *pdev;
+ struct i2c_client *client;
+ struct delayed_work det_work;
+ struct fb_info *fbi;
+ struct mxc_edid_cfg edid_cfg;
+ u8 cable_plugin;
+ u8 edid[SII_EDID_LEN];
+ bool waiting_for_fb;
+} sii902x;
+
+static void sii902x_poweron(void);
+static void sii902x_poweroff(void);
+static void (*sii902x_reset) (void);
+
+#ifdef DEBUG
+static void dump_fb_videomode(struct fb_videomode *m)
+{
+ pr_debug("fb_videomode = %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
+ m->refresh, m->xres, m->yres, m->pixclock, m->left_margin,
+ m->right_margin, m->upper_margin, m->lower_margin,
+ m->hsync_len, m->vsync_len, m->sync, m->vmode, m->flag);
+}
+#else
+static void dump_fb_videomode(struct fb_videomode *m)
+{}
+#endif
+
+static __attribute__ ((unused)) void dump_regs(u8 reg, int len)
+{
+ u8 buf[50];
+ int i;
+
+ i2c_smbus_read_i2c_block_data(sii902x.client, reg, len, buf);
+ for (i = 0; i < len; i++)
+ dev_dbg(&sii902x.client->dev, "reg[0x%02X]: 0x%02X\n",
+ i+reg, buf[i]);
+}
+
+static ssize_t sii902x_show_name(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ strcpy(buf, sii902x.fbi->fix.id);
+ sprintf(buf+strlen(buf), "\n");
+
+ return strlen(buf);
+}
+
+static DEVICE_ATTR(fb_name, S_IRUGO, sii902x_show_name, NULL);
+
+static ssize_t sii902x_show_state(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (sii902x.cable_plugin == 0)
+ strcpy(buf, "plugout\n");
+ else
+ strcpy(buf, "plugin\n");
+
+ return strlen(buf);
+}
+
+static DEVICE_ATTR(cable_state, S_IRUGO, sii902x_show_state, NULL);
+
+static ssize_t sii902x_show_edid(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int i, j, len = 0;
+
+ for (j = 0; j < SII_EDID_LEN/16; j++) {
+ for (i = 0; i < 16; i++)
+ len += sprintf(buf+len, "0x%02X ",
+ sii902x.edid[j*16 + i]);
+ len += sprintf(buf+len, "\n");
+ }
+
+ return len;
+}
+
+static DEVICE_ATTR(edid, S_IRUGO, sii902x_show_edid, NULL);
+
+static void sii902x_setup(struct fb_info *fbi)
+{
+ u16 data[4];
+ u32 refresh;
+ u8 *tmp;
+ int i;
+
+ dev_dbg(&sii902x.client->dev, "Sii902x: setup..\n");
+
+ /* Power up */
+ i2c_smbus_write_byte_data(sii902x.client, 0x1E, 0x00);
+
+ /* set TPI video mode */
+ data[0] = PICOS2KHZ(fbi->var.pixclock) / 10;
+ data[2] = fbi->var.hsync_len + fbi->var.left_margin +
+ fbi->var.xres + fbi->var.right_margin;
+ data[3] = fbi->var.vsync_len + fbi->var.upper_margin +
+ fbi->var.yres + fbi->var.lower_margin;
+ refresh = data[2] * data[3];
+ refresh = (PICOS2KHZ(fbi->var.pixclock) * 1000) / refresh;
+ data[1] = refresh * 100;
+ tmp = (u8 *)data;
+ for (i = 0; i < 8; i++)
+ i2c_smbus_write_byte_data(sii902x.client, i, tmp[i]);
+
+ /* input bus/pixel: full pixel wide (24bit), rising edge */
+ i2c_smbus_write_byte_data(sii902x.client, 0x08, 0x70);
+ /* Set input format to RGB */
+ i2c_smbus_write_byte_data(sii902x.client, 0x09, 0x00);
+ /* set output format to RGB */
+ i2c_smbus_write_byte_data(sii902x.client, 0x0A, 0x00);
+ /* audio setup */
+ i2c_smbus_write_byte_data(sii902x.client, 0x25, 0x00);
+ i2c_smbus_write_byte_data(sii902x.client, 0x26, 0x40);
+ i2c_smbus_write_byte_data(sii902x.client, 0x27, 0x00);
+}
+
+#ifdef CONFIG_FB_MODE_HELPERS
+static int sii902x_read_edid(struct fb_info *fbi)
+{
+ int old, dat, ret, cnt = 100;
+ unsigned short addr = 0x50;
+
+ dev_dbg(&sii902x.pdev->dev, "%s\n", __func__);
+
+ old = i2c_smbus_read_byte_data(sii902x.client, 0x1A);
+
+ i2c_smbus_write_byte_data(sii902x.client, 0x1A, old | 0x4);
+ do {
+ cnt--;
+ msleep(10);
+ dat = i2c_smbus_read_byte_data(sii902x.client, 0x1A);
+ } while ((!(dat & 0x2)) && cnt);
+
+ if (!cnt) {
+ ret = -1;
+ goto done;
+ }
+
+ i2c_smbus_write_byte_data(sii902x.client, 0x1A, old | 0x06);
+
+ /* edid reading */
+ ret = mxc_edid_read(sii902x.client->adapter, addr,
+ sii902x.edid, &sii902x.edid_cfg, fbi);
+
+ cnt = 100;
+ do {
+ cnt--;
+ i2c_smbus_write_byte_data(sii902x.client, 0x1A, old & ~0x6);
+ msleep(10);
+ dat = i2c_smbus_read_byte_data(sii902x.client, 0x1A);
+ } while ((dat & 0x6) && cnt);
+
+ if (!cnt)
+ ret = -1;
+
+done:
+
+ i2c_smbus_write_byte_data(sii902x.client, 0x1A, old);
+ return ret;
+}
+#else
+static int sii902x_read_edid(struct fb_info *fbi)
+{
+ return -1;
+}
+#endif
+
+static void det_worker(struct work_struct *work)
+{
+ int dat;
+ char event_string[16];
+ char *envp[] = { event_string, NULL };
+
+ dev_dbg(&sii902x.pdev->dev, "%s\n", __func__);
+
+ dat = i2c_smbus_read_byte_data(sii902x.client, 0x3D);
+ if (dat & 0x1) {
+ /* cable connection changes */
+ if (dat & 0x4) {
+ sii902x.cable_plugin = 1;
+ dev_dbg(&sii902x.pdev->dev, "EVENT=plugin\n");
+ sprintf(event_string, "EVENT=plugin");
+
+ if (sii902x_read_edid(sii902x.fbi) < 0)
+ dev_err(&sii902x.client->dev,
+ "Sii902x: read edid fail\n");
+ else {
+ if (sii902x.fbi->monspecs.modedb_len > 0) {
+
+ int i;
+ const struct fb_videomode *mode;
+ struct fb_videomode m;
+
+ fb_destroy_modelist(&sii902x.fbi->modelist);
+
+ for (i = 0; i < sii902x.fbi->monspecs.modedb_len; i++) {
+ /*FIXME now we do not support interlaced mode */
+ mode = &sii902x.fbi->monspecs.modedb[i];
+
+ if (!(mode->vmode & FB_VMODE_INTERLACED)) {
+
+ dev_dbg(&sii902x.pdev->dev, "Added mode %d:", i);
+ dev_dbg(&sii902x.pdev->dev,
+ "xres = %d, yres = %d, freq = %d, vmode = %d, flag = %d\n",
+ mode->xres, mode->yres, mode->refresh,
+ mode->vmode, mode->flag);
+
+ fb_add_videomode(mode, &sii902x.fbi->modelist);
+ }
+ }
+
+ fb_var_to_videomode(&m, &sii902x.fbi->var);
+ dump_fb_videomode(&m);
+
+ mode = fb_find_nearest_mode(&m,
+ &sii902x.fbi->modelist);
+
+ fb_videomode_to_var(&sii902x.fbi->var, mode);
+
+ sii902x.fbi->var.activate |= FB_ACTIVATE_FORCE;
+ console_lock();
+ sii902x.fbi->flags |= FBINFO_MISC_USEREVENT;
+ fb_set_var(sii902x.fbi, &sii902x.fbi->var);
+ sii902x.fbi->flags &= ~FBINFO_MISC_USEREVENT;
+ console_unlock();
+ }
+ /* Power on sii902x */
+ sii902x_poweron();
+ }
+ } else {
+ sii902x.cable_plugin = 0;
+ dev_dbg(&sii902x.pdev->dev, "EVENT=plugout\n");
+ sprintf(event_string, "EVENT=plugout");
+ /* Power off sii902x */
+ sii902x_poweroff();
+ }
+ kobject_uevent_env(&sii902x.pdev->dev.kobj, KOBJ_CHANGE, envp);
+ }
+ i2c_smbus_write_byte_data(sii902x.client, 0x3D, dat);
+
+ dev_dbg(&sii902x.pdev->dev, "exit %s\n", __func__);
+
+}
+
+static irqreturn_t sii902x_detect_handler(int irq, void *data)
+{
+ if (sii902x.fbi)
+ schedule_delayed_work(&(sii902x.det_work), msecs_to_jiffies(20));
+ else
+ sii902x.waiting_for_fb = true;
+
+ return IRQ_HANDLED;
+}
+
+static int sii902x_fb_event(struct notifier_block *nb, unsigned long val, void *v)
+{
+ struct fb_event *event = v;
+ struct fb_info *fbi = event->info;
+
+ dev_dbg(&sii902x.pdev->dev, "%s\n", __func__);
+
+ switch (val) {
+ case FB_EVENT_FB_REGISTERED:
+ if (sii902x.fbi == NULL) {
+ sii902x.fbi = fbi;
+ if (sii902x.waiting_for_fb)
+ det_worker(NULL);
+ }
+ fb_show_logo(fbi, 0);
+ break;
+ case FB_EVENT_MODE_CHANGE:
+ sii902x_setup(fbi);
+ break;
+ case FB_EVENT_BLANK:
+ if (*((int *)event->data) == FB_BLANK_UNBLANK) {
+ dev_dbg(&sii902x.pdev->dev, "FB_BLANK_UNBLANK\n");
+ sii902x_poweron();
+ } else {
+ dev_dbg(&sii902x.pdev->dev, "FB_BLANK_BLANK\n");
+ sii902x_poweroff();
+ }
+ break;
+ }
+ return 0;
+}
+
+static struct notifier_block nb = {
+ .notifier_call = sii902x_fb_event,
+};
+
+static int __devinit sii902x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int i, dat, ret;
+ struct fsl_mxc_lcd_platform_data *plat = client->dev.platform_data;
+ struct fb_info edid_fbi;
+
+ dev_dbg(&sii902x.pdev->dev, "%s\n", __func__);;
+
+ sii902x.client = client;
+
+ /* Claim HDMI pins */
+ if (plat->get_pins)
+ if (!plat->get_pins())
+ return -EACCES;
+
+ if (plat->reset) {
+ sii902x_reset = plat->reset;
+ sii902x_reset();
+ }
+
+ /* Set 902x in hardware TPI mode on and jump out of D3 state */
+ if (i2c_smbus_write_byte_data(sii902x.client, 0xc7, 0x00) < 0) {
+ dev_err(&sii902x.client->dev,
+ "Sii902x: cound not find device\n");
+ return -ENODEV;
+ }
+
+ /* read device ID */
+ for (i = 10; i > 0; i--) {
+ dat = i2c_smbus_read_byte_data(sii902x.client, 0x1B);
+ printk(KERN_DEBUG "Sii902x: read id = 0x%02X", dat);
+ if (dat == 0xb0) {
+ dat = i2c_smbus_read_byte_data(sii902x.client, 0x1C);
+ printk(KERN_DEBUG "-0x%02X", dat);
+ dat = i2c_smbus_read_byte_data(sii902x.client, 0x1D);
+ printk(KERN_DEBUG "-0x%02X", dat);
+ dat = i2c_smbus_read_byte_data(sii902x.client, 0x30);
+ printk(KERN_DEBUG "-0x%02X\n", dat);
+ break;
+ }
+ }
+ if (i == 0) {
+ dev_err(&sii902x.client->dev,
+ "Sii902x: cound not find device\n");
+ return -ENODEV;
+ }
+
+ /* try to read edid */
+ ret = sii902x_read_edid(&edid_fbi);
+ if (ret < 0)
+ dev_warn(&sii902x.client->dev, "Can not read edid\n");
+
+ if (ret >= 0)
+ mxcfb_elcdif_register_mode(edid_fbi.monspecs.modedb,
+ edid_fbi.monspecs.modedb_len, MXC_DISP_DDC_DEV);
+
+ if (sii902x.client->irq) {
+ ret = request_irq(sii902x.client->irq, sii902x_detect_handler,
+ IRQF_TRIGGER_FALLING,
+ "SII902x_det", &sii902x);
+ if (ret < 0)
+ dev_warn(&sii902x.client->dev,
+ "Sii902x: cound not request det irq %d\n",
+ sii902x.client->irq);
+ else {
+ /*enable cable hot plug irq*/
+ i2c_smbus_write_byte_data(sii902x.client, 0x3c, 0x01);
+ INIT_DELAYED_WORK(&(sii902x.det_work), det_worker);
+ }
+ ret = device_create_file(&sii902x.pdev->dev, &dev_attr_fb_name);
+ if (ret < 0)
+ dev_warn(&sii902x.client->dev,
+ "Sii902x: cound not create sys node for fb name\n");
+ ret = device_create_file(&sii902x.pdev->dev, &dev_attr_cable_state);
+ if (ret < 0)
+ dev_warn(&sii902x.client->dev,
+ "Sii902x: cound not create sys node for cable state\n");
+ ret = device_create_file(&sii902x.pdev->dev, &dev_attr_edid);
+ if (ret < 0)
+ dev_warn(&sii902x.client->dev,
+ "Sii902x: cound not create sys node for edid\n");
+
+ }
+
+ fb_register_client(&nb);
+
+ dev_dbg(&sii902x.pdev->dev, "%s exit\n", __func__);;
+
+ return 0;
+}
+
+static int __devexit sii902x_remove(struct i2c_client *client)
+{
+ struct fsl_mxc_lcd_platform_data *plat = sii902x.client->dev.platform_data;
+
+ dev_dbg(&sii902x.pdev->dev, "%s\n", __func__);
+
+ fb_unregister_client(&nb);
+ sii902x_poweroff();
+
+ /* Release HDMI pins */
+ if (plat->put_pins)
+ plat->put_pins();
+
+ return 0;
+}
+
+static void sii902x_poweron(void)
+{
+ struct fsl_mxc_lcd_platform_data *plat = sii902x.client->dev.platform_data;
+
+ dev_dbg(&sii902x.pdev->dev, "%s\n", __func__);
+
+ /* Enable pins to HDMI */
+ if (plat->enable_pins)
+ plat->enable_pins();
+
+ /* Turn on DVI or HDMI */
+ if (sii902x.edid_cfg.hdmi_cap)
+ i2c_smbus_write_byte_data(sii902x.client, 0x1A, 0x01);
+ else
+ i2c_smbus_write_byte_data(sii902x.client, 0x1A, 0x00);
+ return;
+}
+
+static void sii902x_poweroff(void)
+{
+ struct fsl_mxc_lcd_platform_data *plat = sii902x.client->dev.platform_data;
+
+ dev_dbg(&sii902x.pdev->dev, "%s\n", __func__);
+
+ /* disable tmds before changing resolution */
+ if (sii902x.edid_cfg.hdmi_cap)
+ i2c_smbus_write_byte_data(sii902x.client, 0x1A, 0x11);
+ else
+ i2c_smbus_write_byte_data(sii902x.client, 0x1A, 0x10);
+
+ /* Disable pins to HDMI */
+ if (plat->disable_pins)
+ plat->disable_pins();
+
+ return;
+}
+
+static const struct i2c_device_id sii902x_id[] = {
+ { "sii902x", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, sii902x_id);
+
+static struct i2c_driver sii902x_i2c_driver = {
+ .driver = {
+ .name = "sii902x",
+ },
+ .probe = sii902x_probe,
+ .remove = sii902x_remove,
+ .id_table = sii902x_id,
+};
+
+static int __init sii902x_init(void)
+{
+ int ret;
+
+ memset(&sii902x, 0, sizeof(sii902x));
+
+ sii902x.pdev = platform_device_register_simple("sii902x", 0, NULL, 0);
+ if (IS_ERR(sii902x.pdev)) {
+ printk(KERN_ERR
+ "Unable to register Sii902x as a platform device\n");
+ ret = PTR_ERR(sii902x.pdev);
+ goto err;
+ }
+
+ return i2c_add_driver(&sii902x_i2c_driver);
+err:
+ return ret;
+}
+
+static void __exit sii902x_exit(void)
+{
+ i2c_del_driver(&sii902x_i2c_driver);
+ platform_device_unregister(sii902x.pdev);
+}
+
+module_init(sii902x_init);
+module_exit(sii902x_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("SII902x DVI/HDMI driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/mxc/ntx_hwconfig.h b/drivers/video/mxc/ntx_hwconfig.h
new file mode 120000
index 00000000..cf827696
--- /dev/null
+++ b/drivers/video/mxc/ntx_hwconfig.h
@@ -0,0 +1 @@
+../../../arch/arm/mach-mx6/ntx_hwconfig.h \ No newline at end of file
diff --git a/drivers/video/mxc/ntx_s1d13521fb.h b/drivers/video/mxc/ntx_s1d13521fb.h
new file mode 100644
index 00000000..e9994a20
--- /dev/null
+++ b/drivers/video/mxc/ntx_s1d13521fb.h
@@ -0,0 +1,169 @@
+//-----------------------------------------------------------------------------
+//
+// linux/drivers/video/epson/s1d13521fb.h --
+// Function header for Epson S1D13521 controller frame buffer drivers.
+//
+// Copyright(c) Seiko Epson Corporation 2000-2008.
+// All rights reserved.
+//
+// This file is subject to the terms and conditions of the GNU General Public
+// License. See the file COPYING in the main directory of this archive for
+// more details.
+//
+//----------------------------------------------------------------------------
+
+#ifndef __NTX_S1D13521FB_H__
+#define __NTX_S1D13521FB_H__
+
+ #include <linux/kernel.h>
+ #include <linux/fb.h>
+
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ //
+ //
+ //
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ #define _LOOP_TIMEOUT_READY 500000
+
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ //
+ //
+ //
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+//#define __EB600__
+//#define __EB500__
+ //#define __EB600EM__
+ //#define __EB600E__
+ #define __COOKIE__
+
+ //#if (_DEVICE_CODE == _DEVICE_CODE_EB600)
+ #if defined( __EB600__)
+ #define _DEIVE_NAME "EB600"
+ //#elif (_DEVICE_CODE == _DEVICE_CODE_EB500)
+ #elif defined( __EB500__)
+ #define _DEIVE_NAME "EB500"
+ //#elif (_DEVICE_CODE == _DEVICE_CODE_EB600E)
+ #elif defined( __EB600E__)
+ #define _DEIVE_NAME "EB600E"
+ //#elif (_DEVICE_CODE == _DEVICE_CODE_COOKIE)
+ #elif defined( __COOKIE__)
+ #define _DEIVE_NAME "COOKIE"
+ //#elif (_DEVICE_CODE == _DEVICE_CODE_EB600EM)
+ #elif defined( __EB600EM__)
+ #define _DEIVE_NAME "EB600EM"
+ #else
+ #error "What device!!!!!!!"
+ #endif
+
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ //
+ //
+ //
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+// #define __TURN_ON_FORCE_DISPLAY__
+// #define __TURN_ON_EPSON_INIT__
+
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ //
+ //
+ //
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+// #define __5_INCH__
+ #define __6_INCH__
+// #define __8_INCH__
+// #define __9D7_INCH__
+
+// #define _WAVE_FORM_BASE 0x886
+ #define _WAVE_FORM_BASE 0x1000
+
+ #define _S1D13522_WAVE_FORM_BASE 0xb0ea
+ #define _S1D13522_WAVE_FORM_BASE_H 0x0003
+
+#if defined(__5_INCH__)
+ #define _PANEL_WIDTH 600
+ #define _PANEL_HEIGHT 800
+
+ #define S1D_DISPLAY_WIDTH 600
+ #define S1D_DISPLAY_HEIGHT 800
+
+ #define _INIT_HSIZE 800
+ #define _INIT_VSIZE 600
+ #define _INIT_FSLEN 4
+ #define _INIT_FBLEN 4
+ #define _INIT_FELEN 10
+ #define _INIT_LSLEN 10
+ #define _INIT_LBLEN 4
+ #define _INIT_LELEN 100
+ #define _INIT_PIXCLKDIV 6
+ #define _INIT_SDRV_CFG (100 | (1<< 8) | (1<<9))
+ #define _INIT_GDRV_CFG 0x2
+ #define _INIT_LUTIDXFMT (4 | (1<<7))
+
+ #define _INIT_ROTMODE 3 // rotation mode = 270 degrees
+
+#elif defined(__6_INCH__)
+ #define _PANEL_WIDTH _INIT_VSIZE
+ #define _PANEL_HEIGHT _INIT_HSIZE
+
+ #define S1D_DISPLAY_WIDTH _INIT_VSIZE
+ #define S1D_DISPLAY_HEIGHT _INIT_HSIZE
+
+ //#define _INIT_HSIZE 800 // max panel original width .
+ //#define _INIT_VSIZE 600 // max panel original height .
+ #define _INIT_HSIZE 1600 // max panel original width .
+ #define _INIT_VSIZE 1200 // max panel original height .
+
+ #define _INIT_FSLEN 4
+ #define _INIT_FBLEN 4
+ #define _INIT_FELEN 10
+ #define _INIT_LSLEN 10
+ #define _INIT_LBLEN 4
+ #define _INIT_LELEN 100
+ #define _INIT_PIXCLKDIV 6
+ #define _INIT_SDRV_CFG (100 | (1<< 8) | (1<<9))
+ #define _INIT_GDRV_CFG 0x2
+ #define _INIT_LUTIDXFMT (4 | (1<<7))
+
+ #define _INIT_ROTMODE 3 // rotation mode = 270 degrees
+ //#define _INIT_ROTMODE 1 // rotation mode = 270 degrees
+
+#elif defined(__8_INCH__)
+#elif defined(__9D7_INCH__)
+ #define _PANEL_WIDTH 1200
+ #define _PANEL_HEIGHT 826
+
+ #define S1D_DISPLAY_WIDTH 1200
+ #define S1D_DISPLAY_HEIGHT 826
+
+ #define _INIT_HSIZE 1200
+ #define _INIT_VSIZE 826
+ #define _INIT_FSLEN 0
+ #define _INIT_FBLEN 4
+ #define _INIT_FELEN 4
+ #define _INIT_LSLEN 4
+ #define _INIT_LBLEN 10
+ #define _INIT_LELEN 60
+ #define _INIT_PIXCLKDIV 3
+ #define _INIT_SDRV_CFG (100 | (1<< 8) | (1<<9))
+ #define _INIT_GDRV_CFG 0x2
+ #define _INIT_LUTIDXFMT (4 | (1<<7))
+
+ #define _INIT_ROTMODE 0 // rotation mode = 270 degrees
+//#define _INIT_ROTMODE 1 // rotation mode = 270 degrees
+#else
+ #error "give me panel size"
+
+#endif
+ #define _PANEL_PEXIL_BPP 8
+ #define _PANEL_TOTAL_PEXIL (_PANEL_WIDTH*_PANEL_HEIGHT)
+ #define _PANEL_BUFFER_SIZE (_PANEL_TOTAL_PEXIL*_PANEL_PEXIL_BPP)/8
+ #define S1D_DISPLAY_BPP 4
+
+#define SHOW_PROGRESS_BAR
+// Joseph 20101116
+#define _PROGRESS_BAR_X_ 360
+#define _PROGRESS_BAR_Y_ 740
+#define _PROGRESS_BAR_ICONS_ 7
+
+#define __LINUX_VERSION__ "2.6.18.2-ntx-cookie-v2.9"
+#endif //__NTX_S1D13521FB_H__
diff --git a/drivers/video/mxc/s1d13521.h b/drivers/video/mxc/s1d13521.h
new file mode 100644
index 00000000..305ac0d3
--- /dev/null
+++ b/drivers/video/mxc/s1d13521.h
@@ -0,0 +1,268 @@
+/*===============================================================================
+** Generic Header information generated by 13521CFG.EXE (Build 4)
+** (C)SEIKO EPSON CORPORATION 2002-2007. All rights reserved.
+**
+** DISPLAYS WxH FREQ SUBTYPE
+** ------------- ----------- ------- -------------------------------------------
+** *LCD1=Parallel 800x600 NA EPD Panel
+**
+** DIMENSIONS WxHxBPP @ STRIDE START SADDR ADDITIONAL
+** ----------- ---------------------- ------- --------- -----------------------
+** *Main 800x600x4 @ 800 NA 000EA600h LUTAuto=on
+**
+** CLOCKS FREQ SOURCE
+** ------------- ----------- ---------------------------------------------------
+** INCLK 132.000 MHz PLL
+** SYSCLK 66.000 MHz PLL/2
+** PCLK 26.400 MHz PLL/5
+** SPICLK 13.200 MHz SYSCLK/5
+** I2CCLK 4.125 MHz PLL/16
+** SDRAMCLK 132.000 MHz PLL
+** SDRAMREFCLK 63.954 KHz CLKI/516
+**
+** This file defines the configuration environment and registers,
+** which can be used by any software, such as display drivers.
+**
+** Note: If you transfer this file to any non-PC system, use ASCII
+** mode (not BINARY) to maintain system-specific line terminators.
+**===============================================================================*/
+#ifndef __S1D13521_H__
+ #define __S1D13521_H__
+
+ #include "ntx_s1d13521fb.h"
+
+
+//#define IMX233_EPD
+#define _S1D13522_
+
+#ifdef IMX233_EPD
+void gpio_epd_init(void);
+void gpio_epd_cmd_write(unsigned short w);
+void gpio_epd_data_write(unsigned short w);
+unsigned short gpio_epd_data_read(void);
+unsigned short gpio_epd_hrdy(void);
+int gpio_epd_data_write_buffer (unsigned short *pBuffer, int length);
+
+#define EPD_WRITE_CMD(a) gpio_epd_cmd_write(a)
+#define EPD_WRITE_DATA(a) gpio_epd_data_write(a)
+#define EPD_READ_DATA() gpio_epd_data_read()
+#endif
+
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ //
+ //
+ //
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ /////////////////////////////////////////////////////////////////////////
+ //
+ //
+ //
+ /////////////////////////////////////////////////////////////////////////
+ #define _EPSON_REG_STATUS 0x00a
+ #define _EPSON_REG_STATUS_PLL_LOCK 0x0001
+ #define _EPSON_REG_STATUS_SDRAM_INITED 0x0002
+ #define _EPSON_REG_STATUS_DPE_BUSY 0x0004
+ #define _EPSON_REG_STATUS_HOST_MEM_BUSY 0x0008
+ #define _EPSON_REG_STATUS_SDRAM_BUSY 0x0010
+ #define _EPSON_REG_STATUS_HOST_BUS_BUSY 0x0020
+ #define _EPSON_REG_STATUS_SPI_BUSY 0x0040
+ #define _EPSON_REG_STATUS_I2C_BUSY 0x0080
+ #define _EPSON_REG_STATUS_PWR_SEQ 0x0100
+ #define _EPSON_REG_STATUS_SDRAM_SELF_REFRESH 0x0200
+ #define _EPSON_REG_STATUS_PM_STATE 0x0c00
+ #define _EPSON_REG_STATUS_PM_STATE_UNINIT 0x0000
+ #define _EPSON_REG_STATUS_PM_STATE_RUN 0x0400
+ #define _EPSON_REG_STATUS_PM_STATE_STANDBY 0x0800
+ #define _EPSON_REG_STATUS_PM_STATE_SLEEP 0x0c00
+ #define _EPSON_REG_STATUS_OSC_BYPASS 0x1000
+ #define _EPSON_REG_STATUS_PM_BUSY 0x4000
+ #define _EPSON_REG_STATUS_3W_BUSY 0x8000
+
+ /////////////////////////////////////////////////////////////////////////
+ //
+ //
+ //
+ /////////////////////////////////////////////////////////////////////////
+ #define _EPSON_REG_WRITE_IMAGE 0x154
+
+ /////////////////////////////////////////////////////////////////////////
+ //
+ //
+ //
+ /////////////////////////////////////////////////////////////////////////
+ #define _EPSON_REG_SPI_READ_DATA 0x200
+ #define _EPSON_REG_SPI_WRITE_DATA 0x202
+ #define _EPSON_REG_SPI_WRITE_DATA_ENABLE _BIT8
+ #define _EPSON_REG_SPI_CONTROL 0x204
+ #define _EPSON_REG_SPI_CONTROL_ENBALE _BIT0
+ #define _EPSON_REG_SPI_CONTROL_DISBALE 0
+ #define _EPSON_REG_SPI_CONTROL_CLK_POL _BIT1
+ #define _EPSON_REG_SPI_CONTROL_CLK_PHASE _BIT2
+ #define _EPSON_REG_SPI_CONTROL_CLK_DIVIDER (_BIT3+_BIT4+_BIT5)
+ #define _EPSON_REG_SPI_CONTROL_CLK_DIVIDER_2_1 (0 << 3)
+ #define _EPSON_REG_SPI_CONTROL_CLK_DIVIDER_3_1 (1 << 3)
+ #define _EPSON_REG_SPI_CONTROL_CLK_DIVIDER_4_1 (2 << 3)
+ #define _EPSON_REG_SPI_CONTROL_CLK_DIVIDER_5_1 (3 << 3)
+ #define _EPSON_REG_SPI_CONTROL_CLK_DIVIDER_6_1 (4 << 3)
+ #define _EPSON_REG_SPI_CONTROL_CLK_DIVIDER_7_1 (5 << 3)
+ #define _EPSON_REG_SPI_CONTROL_CLK_DIVIDER_8_1 (6 << 3)
+ #define _EPSON_REG_SPI_CONTROL_CLK_DIVIDER_9_1 (7 << 3)
+ #define _EPSON_REG_SPI_CONTROL_SPEED_HIGH _BIT6
+ #define _EPSON_REG_SPI_CONTROL_ACCESS_BY_DPE _BIT7
+ #define _EPSON_REG_SPI_CONTROL_ACCESS_BY_HOST 0
+ #define _EPSON_REG_SPI_STATUS 0x206
+ #define _EPSON_REG_SPI_STATUS_READ_READY _BIT0
+ #define _EPSON_REG_SPI_STATUS_READ_OVERRUN _BIT1
+ #define _EPSON_REG_SPI_STATUS_WRITE_EMPTY _BIT2
+ #define _EPSON_REG_SPI_STATUS_BUSY _BIT3
+ #define _EPSON_REG_SPI_CS_CONTROL 0x208
+ #define _EPSON_REG_SPI_CS_CONTROL_ENABLE _BIT0
+
+ /////////////////////////////////////////////////////////////////////////
+ //
+ //
+ //
+ /////////////////////////////////////////////////////////////////////////
+ #define _EPSON_REG_THERMAL_SENSOR_CFG 0x210
+ #define _EPSON_REG_THERMAL_SENSOR_STATUS 0x212
+ #define _EPSON_REG_THERMAL_SENSOR_STATUS_BUSY _BIT0
+ #define _EPSON_REG_THERMAL_SENSOR_STATUS_ID _BIT1
+ #define _EPSON_REG_THERMAL_SENSOR_STATUS_SCL _BIT4
+ #define _EPSON_REG_THERMAL_SENSOR_STATUS_SDA _BIT5
+ #define _EPSON_REG_THERMAL_SENSOR_TRIGGER 0x214
+ #define _EPSON_REG_THERMAL_SENSOR_TRIGGER_READ _BIT0
+ #define _EPSON_REG_THERMAL_SENSOR_DATA 0x216
+
+ /////////////////////////////////////////////////////////////////////////
+ //
+ //
+ //
+ /////////////////////////////////////////////////////////////////////////
+ #define _EPSON_REG_SOURCE_DRV_CFG 0x30c
+
+ /////////////////////////////////////////////////////////////////////////
+ //
+ //
+ //
+ /////////////////////////////////////////////////////////////////////////
+ #define _EPSON_REG_GATE_DRV_CFG 0x30e
+
+ /////////////////////////////////////////////////////////////////////////
+ //
+ //
+ //
+ /////////////////////////////////////////////////////////////////////////
+ #define _EPSON_REG_DPE_CONTROL 0x330
+ #define _EPSON_REG_DPE_CONTROL_LUT_FORMAT_P2N (0 << 0)
+ #define _EPSON_REG_DPE_CONTROL_LUT_FORMAT_P3N (2 << 0)
+ #define _EPSON_REG_DPE_CONTROL_LUT_FORMAT_P4N (4 << 0)
+ #define _EPSON_REG_DPE_CONTROL_LUT_FORMAT_P5N (6 << 0)
+
+ #define _EPSON_REG_DPE_CONTROL_AUTO_WAVEFORM_OFF (0 << 6)
+ #define _EPSON_REG_DPE_CONTROL_AUTO_WAVEFORM_ON (1 << 6)
+ #define _EPSON_REG_DPE_CONTROL_AUTO_LUT_OFF (0 << 7)
+ #define _EPSON_REG_DPE_CONTROL_AUTO_LUT_ON (1 << 7)
+
+ #define _EPSON_REG_DPE_CONTROL_LAST_WAVEFORM_MODE (0xf << 8)
+
+ #define _EPSON_REG_DPE_CONTROL_RESET_OFF (0 << 15)
+ #define _EPSON_REG_DPE_CONTROL_RESET_ON (1 << 15)
+
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ //
+ //
+ //
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ #define S1D_13521
+
+ #define S1D_DISPLAY_BPP 4
+// #define S1D_DISPLAY_BPP 8
+#if 1
+// #define S1D_DISPLAY_SCANLINE_BYTES 600
+ #define S1D_DISPLAY_FRAME_RATE 0
+// #define S1D_PHYSICAL_REG_ADDR 0x00000000L
+ #define S1D_PHYSICAL_REG_ADDR 0xA0000000L
+ #define S1D_PHYSICAL_REG_SIZE 90L
+ #define S1D_PHYSICAL_VMEM_REQUIRED 640000L
+ #define S1D_PALETTE_SIZE 256
+ #define S1D_POWER_DELAY_OFF 0
+ #define S1D_POWER_DELAY_ON 0
+ #define S1D_HWBLT
+ #define S1D_SWBLT
+#endif
+ #define S1D_DISPLAY_PCLK 26400000L
+
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ //
+ //
+ //
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+#if 0
+ #define BS60_INIT_LUTIDXFMT (_EPSON_REG_DPE_CONTROL_LUT_FORMAT_P4N | \
+ _EPSON_REG_DPE_CONTROL_AUTO_LUT_ON)
+#endif
+
+
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ //
+ //
+ //
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ #define WF_MODE_INIT 0
+ #define WF_MODE_MU 1
+ #define WF_MODE_GU 2
+ #define WF_MODE_GC 3
+ #define WF_MODE_PU 4
+
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ //
+ //
+ //
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ typedef unsigned short S1D_INDEX;
+ typedef unsigned short S1D_VALUE;
+
+ #define S1D_INSTANTIATE_REGISTERS(scope_prefix,variable_name) \
+ scope_prefix S1D_VALUE variable_name[] = \
+ { \
+ INIT_SYS_RUN, 0, \
+ INIT_DSPE_CFG, 5, _INIT_HSIZE, \
+ _INIT_VSIZE, \
+ _INIT_SDRV_CFG, \
+ _INIT_GDRV_CFG, \
+ _INIT_LUTIDXFMT, \
+ INIT_DSPE_TMG, 5, _INIT_FSLEN, \
+ (_INIT_FELEN<<8)|_INIT_FBLEN, \
+ _INIT_LSLEN, \
+ (_INIT_LELEN<<8)|_INIT_LBLEN, \
+ _INIT_PIXCLKDIV, \
+ RD_WFM_INFO, 2, _WAVE_FORM_BASE, 0, \
+ UPD_GDRV_CLR, 0, \
+ WAIT_DSPE_TRG, 0, \
+ INIT_ROTMODE, 1, (_INIT_ROTMODE << 8) \
+ }
+
+#if 0 // for s1d13522 auto boot mode.
+ #define S1D13522_INSTANTIATE_REGISTERS(scope_prefix_1,s1d13522_variable_name) \
+ scope_prefix_1 S1D_VALUE s1d13522_variable_name[] = \
+ { \
+ INIT_SYS_RUN, 0, \
+ INIT_DSPE_CFG, 5, _INIT_HSIZE, \
+ _INIT_VSIZE, \
+ _INIT_SDRV_CFG, \
+ _INIT_GDRV_CFG, \
+ _INIT_LUTIDXFMT, \
+ INIT_DSPE_TMG, 5, _INIT_FSLEN, \
+ (_INIT_FELEN<<8)|_INIT_FBLEN, \
+ _INIT_LSLEN, \
+ (_INIT_LELEN<<8)|_INIT_LBLEN, \
+ _INIT_PIXCLKDIV, \
+ RD_WFM_INFO, 2, _S1D13522_WAVE_FORM_BASE, _S1D13522_WAVE_FORM_BASE_H, \
+ UPD_GDRV_CLR, 0, \
+ WAIT_DSPE_TRG, 0, \
+ INIT_ROTMODE, 1, (_INIT_ROTMODE << 8) \
+ }
+#endif
+
+
+#endif // __S1D13521_H__
diff --git a/drivers/video/mxc/s1d13521_debug.h b/drivers/video/mxc/s1d13521_debug.h
new file mode 100644
index 00000000..2a0cd5af
--- /dev/null
+++ b/drivers/video/mxc/s1d13521_debug.h
@@ -0,0 +1,53 @@
+//-----------------------------------------------------------------------------
+//
+// linux/drivers/video/epson/s1d13521fb.h --
+// Function header for Epson S1D13521 controller frame buffer drivers.
+//
+// Copyright(c) Seiko Epson Corporation 2000-2008.
+// All rights reserved.
+//
+// This file is subject to the terms and conditions of the GNU General Public
+// License. See the file COPYING in the main directory of this archive for
+// more details.
+//
+//----------------------------------------------------------------------------
+
+#ifndef __S1D13521_DEBUG_H__
+ #define __S1D13521_DEBUG_H__
+
+ #include <linux/kernel.h>
+
+ //-----------------------------------------------------------------------------
+ // Global Function Prototypes
+ //-----------------------------------------------------------------------------
+
+// #undef CONFIG_FB_EPSON_DEBUG_PRINTK
+
+ #ifdef CONFIG_FB_EPSON_DEBUG_PRINTK
+// #define dbg_info(fmt, args...) do { printk(KERN_INFO fmt, ## args); } while (0)
+ #define dbg_info(fmt, args...) do { printk(KERN_INFO fmt, ## args); } while (0)
+ #define DEBUGMSG(con, fmt, args...) do { if(con != 0) printk(fmt, ## args); } while (0)
+ #define DEBUGMSG_FUNC_ENTER(con) do { if(con != 0) printk("%s]+++\n", __FUNCTION__); } while (0)
+ #define DEBUGMSG_FUNC_EXIT(con) do { if(con != 0) printk("%s]---\n", __FUNCTION__); } while (0)
+ #define DEBUGMSG_FUNC_EXIT_CODE(con, x) do { if(con != 0) printk("%s]--- ret=%d\n", __FUNCTION__, x); } while (0)
+ #else
+ #define dbg_info(fmt, args...) do { } while (0)
+ #define DEBUGMSG(con, fmt, args...) do { } while (0)
+ #define DEBUGMSG_FUNC_ENTER(con) do { } while (0)
+ #define DEBUGMSG_FUNC_EXIT(con) do { } while (0)
+ #define DEBUGMSG_FUNC_EXIT_CODE(con, code) do { } while (0)
+ #endif
+
+ #ifdef CONFIG_FB_EPSON_DEBUG_PRINTK
+ #define assert(expr) \
+ if(!(expr)) { \
+ printk( "Assertion failed! %s,%s,%s,line=%d\n",\
+ #expr,__FILE__,__FUNCTION__,__LINE__); \
+ BUG(); \
+ }
+ #else
+ #define assert(expr)
+ #endif
+
+
+#endif //__S1D13521_DEBUG_H__
diff --git a/drivers/video/mxc/s1d13521fb.h b/drivers/video/mxc/s1d13521fb.h
new file mode 100644
index 00000000..2ae49b63
--- /dev/null
+++ b/drivers/video/mxc/s1d13521fb.h
@@ -0,0 +1,214 @@
+//-----------------------------------------------------------------------------
+//
+// linux/drivers/video/epson/s1d13521fb.h --
+// Function header for Epson S1D13521 controller frame buffer drivers.
+//
+// Copyright(c) Seiko Epson Corporation 2000-2008.
+// All rights reserved.
+//
+// This file is subject to the terms and conditions of the GNU General Public
+// License. See the file COPYING in the main directory of this archive for
+// more details.
+//
+//----------------------------------------------------------------------------
+
+#ifndef __S1D13521FB_H__
+ #define __S1D13521FB_H__
+
+ #include <linux/kernel.h>
+ #include <linux/fb.h>
+ #include "DataType.h"
+
+ #include "s1d13521ioctl.h"
+ #include "s1d13521.h"
+
+ #include "s1d13521_debug.h"
+
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ //
+ //
+ //
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ #define HRDY_TIMEOUT_MS 5000
+
+ //#define CONFIG_FB_EPSON_PROC
+
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ //
+ //
+ //
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ // In Indirect Mode, a copy of the framebuffer is kept in system memory.
+ // A timer periodically writes this copy to the "real" framebuffer in
+ // hardware. This copy is called a virtual framebuffer.
+
+ //----------------------------------------------------------------------------
+ // Global structures used by s1d13521fb frame buffer code
+ //----------------------------------------------------------------------------
+ typedef struct
+ {
+ volatile unsigned char *RegAddr;
+ unsigned RegAddrMappedSize;
+ volatile unsigned char *DataPort;
+ unsigned DataPortSzie;
+ volatile unsigned char *CommandPort;
+ unsigned CommandPortSzie;
+
+ u32 VirtualFramebufferAddr;
+ int blank_mode;
+ u32 pseudo_palette[16];
+ }FB_INFO_S1D13521;
+
+ extern struct fb_fix_screeninfo s1d13521fb_fix;
+ extern struct fb_info s1d13521_fb;
+ extern FB_INFO_S1D13521 s1d13521fb_info;
+ extern char *s1d13521fb_version;
+
+ //-----------------------------------------------------------------------------
+ // Global Function Prototypes
+ //-----------------------------------------------------------------------------
+
+ #ifdef CONFIG_FB_EPSON_PROC
+ int __devinit s1d13521proc_init(void);
+ void __devexit s1d13521proc_terminate(void);
+ #endif
+
+ #ifdef CONFIG_FB_EPSON_PCI
+ int __devinit s1d13521pci_init(long *physicalAddress);
+ void __devexit s1d13521pci_terminate(void);
+ #endif
+
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ //
+ //
+ //
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ typedef struct TAG_ST_A_GPIO_INFO{
+ const UINT32 Info;
+ const char *Name;
+ } ST_A_GPIO_INFO, *PST_A_GPIO_INFO;
+
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ //
+ //
+ //
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ typedef enum {
+ _EPSON_ERROR_SUCCESS=0,
+ _EPSON_ERROR_NOT_READY,
+ _EPSON_ERROR_TIMEOUT,
+ _EPSON_ERROR_WRITE_FAIL,
+ } EN_EPSON_ERROR_CODE;
+
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ //
+ //
+ //
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ typedef struct TAG_ST_DESC_ACCESS_BUF {
+ UINT32 Len;
+ PUINT pData;
+ } ST_DESC_ACCESS_BUF, *PST_DESC_ACCESS_BUF;
+
+ #pragma pack(1)
+ typedef struct TAG_ST_COMMAND_PACKAGE {
+ ST_DESC_ACCESS_BUF Write;
+ ST_DESC_ACCESS_BUF Read;
+ UINT16 Command;
+ } ST_COMMAND_PACKAGE, *PST_COMMAND_PACKAGE;
+ #pragma pack()
+
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ //
+ //
+ //
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ typedef struct TAG_ST_PARTIAL_RECT {
+ UINT16 StartX;
+ UINT16 StartY;
+ UINT16 Width;
+ UINT16 Height;
+ } ST_PARTIAL_RECT, *PST_PARTIAL_RECT;
+
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ //
+ //
+ //
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ int __devinit s1d13521if_InterfaceInit(FB_INFO_S1D13521 *info);
+ void __devexit s1d13521if_InterfaceTerminate(FB_INFO_S1D13521 *info);
+
+ int BusIssueCmd(unsigned ioctlcmd,s1d13521_ioctl_cmd_params *params,int numparams);
+
+ void BusIssueWriteBuf(u16 *ptr16, unsigned copysize16);
+ void BusIssueReadBuf(u16 *ptr16, unsigned copysize16);
+
+ u16 BusIssueReadReg(u16 Index);
+ EN_EPSON_ERROR_CODE BusIssueWriteReg(u16 Index, u16 Value);
+ EN_EPSON_ERROR_CODE BusIssueWriteRegX(u16 Index, u16 Value);
+ EN_EPSON_ERROR_CODE BusIssueCmdX(UINT16 Cmd);
+
+ EN_EPSON_ERROR_CODE BusIssueWriteRegBuf(u16 Index, PUINT16 pData, UINT32 Length);
+ EN_EPSON_ERROR_CODE BusWaitForHRDY(void);
+ void BusIssueDoRefreshDisplay(unsigned cmd,unsigned mode);
+ void BusIssueInitDisplay(void);
+ void BusIssueInitRegisters(void);
+
+ int pvi_GoToSleep(void);
+ int pvi_GoToNormal(void);
+
+
+// int s1d13521if_CmdPackage(PST_COMMAND_PACKAGE pCmdPkg);
+
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ //
+ //
+ //
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ int pvi_Init(VOID);
+ VOID pvi_Deinit(VOID);
+
+ int pvi_ioctl_NewImage(PTDisplayCommand puDisplayCommand);
+ int pvi_ioctl_StopNewImage(PTDisplayCommand puDisplayCommand);
+ int pvi_ioctl_DisplayImage(PTDisplayCommand puDisplayCommand);
+ int pvi_ioctl_PartialImage(PTDisplayCommand puDisplayCommand);
+ int pvi_ioctl_DisplayPartial(PTDisplayCommand puDisplayCommand);
+ int pvi_ioctl_Reset(PTDisplayCommand puDisplayCommand);
+ int pvi_ioctl_SetDepth(PTDisplayCommand puDisplayCommand);
+ int pvi_ioctl_EraseDisplay(PTDisplayCommand puDisplayCommand);
+ int pvi_ioctl_Rotate(PTDisplayCommand puDisplayCommand);
+ int pvi_ioctl_Positive(PTDisplayCommand puDisplayCommand);
+ int pvi_ioctl_Negative(PTDisplayCommand puDisplayCommand);
+ int pvi_ioctl_GoToNormal(PTDisplayCommand puDisplayCommand);
+ int pvi_ioctl_GoToSleep(PTDisplayCommand puDisplayCommand);
+ int pvi_ioctl_GoToStandBy(PTDisplayCommand puDisplayCommand);
+ int pvi_ioctl_WriteToFlash(PTDisplayCommand puDisplayCommand);
+ int pvi_ioctl_ReadFromFlash(PTDisplayCommand puDisplayCommand);
+ int pvi_ioctl_Init(PTDisplayCommand puDisplayCommand);
+ int pvi_ioctl_AutoRefreshOn(PTDisplayCommand puDisplayCommand);
+ int pvi_ioctl_AutoRefreshOff(PTDisplayCommand puDisplayCommand);
+ int pvi_ioctl_SetRefresh(PTDisplayCommand puDisplayCommand);
+ int pvi_ioctl_ForcedRefresh(PTDisplayCommand puDisplayCommand);
+ int pvi_ioctl_GetRefresh(PTDisplayCommand puDisplayCommand);
+ int pvi_ioctl_RestoreImage(PTDisplayCommand puDisplayCommand);
+ int pvi_ioctl_ControllerVersion(PTDisplayCommand puDisplayCommand);
+ int pvi_ioctl_SoftwareVersion(PTDisplayCommand puDisplayCommand);
+ int pvi_ioctl_DisplaySize(PTDisplayCommand puDisplayCommand);
+ int pvi_ioctl_GetStatus(PTDisplayCommand puDisplayCommand);
+ int pvi_ioctl_Temperature(PTDisplayCommand puDisplayCommand);
+ int pvi_ioctl_WriteRegister(PTDisplayCommand puDisplayCommand);
+ int pvi_ioctl_ReadRegister(PTDisplayCommand puDisplayCommand);
+
+ int pvi_SwitchCommand(PTDisplayCommand pDisplayCommand);
+ //int Epson_displayCMD(PST_IMAGE_PGM PST);
+ //int Epson_displayCMD_MEM(PST_IMAGE_PGM_MEM PST); //KEG 20090914
+ //int Epson_LoadImageArea(PTloadImageArea area); // KEG 20090814
+
+ //BOOL BusIssueFlashOperation(PS1D13532_FLASH_PACKAGE pFlashControl);
+ BOOL BusIssueReset(void);
+ //int ImagePGM(PST_IMAGE_PGM pPGM);
+
+ VOID DeviceReset(VOID);
+
+
+#endif //__S1D13521FB_H__
diff --git a/drivers/video/mxc/s1d13521ioctl.h b/drivers/video/mxc/s1d13521ioctl.h
new file mode 100644
index 00000000..0def971c
--- /dev/null
+++ b/drivers/video/mxc/s1d13521ioctl.h
@@ -0,0 +1,409 @@
+//-----------------------------------------------------------------------------
+//
+// linux/drivers/video/epson/s1d1352ioctl.h -- IOCTL definitions for Epson
+// S1D13521 controller frame buffer driver.
+//
+// Copyright(c) Seiko Epson Corporation 2008.
+// All rights reserved.
+//
+// This file is subject to the terms and conditions of the GNU General Public
+// License. See the file COPYING in the main directory of this archive for
+// more details.
+//
+//----------------------------------------------------------------------------
+#ifndef __EPSON_IOCTL_H__
+ #define __EPSON_IOCTL_H__
+
+ #include "ntx_s1d13521fb.h"
+
+/* ioctls
+ 0x45 is 'E' */
+struct s1d13521_ioctl_hwc
+{
+ unsigned addr;
+ unsigned value;
+ void* buffer;
+};
+
+#define S1D13521_REGREAD 0x4540
+#define S1D13521_REGWRITE 0x4541
+#define S1D13521_MEMBURSTREAD 0x4546
+#define S1D13521_MEMBURSTWRITE 0x4547
+#define S1D13521_VBUF_REFRESH 0x4548 // KEG 20090807 unused,
+ // but included to stay compatible with latest epson driver
+
+
+
+#define S1D13521_DISPLAY 0x4539
+#define S1D13521_SETDEPTH 0x4550
+#define S1D13521_DISPLAY_KAY 0x4542 //kay 20090511
+#define S1D13521_FORCE_REFRESH 0x4543
+
+#define S1D13521_FORCE_REFRESH_FULL_AREA 0x4544
+#define S1D13521_FORCE_REFRESH_FULL_FULL 0x4545
+#define S1D13521_FORCE_REFRESH_PART_AREA 0x4556
+#define S1D13521_FORCE_REFRESH_PART_FULL 0x4557
+
+#define S1D13521_FORCE_VFB_TO_SCREEN 0x4560 // KEG 20090805
+#define S1D13521_SET_AUTOREFRESH_TIME 0x4561 // KEG 20090807
+#define S1D13521_GET_AUTOREFRESH_TIME 0x4562 // KEG 20090807
+#define S1D13521_ERASE_SCREEN 0x4563 // KEG 20090807
+#define S1D13521_LOAD_AREA 0x4564 // KEG 20090814
+
+// virtual hardware controll .
+#define EPDC_PWR_CTRL 0x4570 // epdc power controll .
+ #define EPDC_AUTOPWR_INTERVAL_MAX 1
+ #define EPDC_AUTOPWR_INTERVAL_NORMAL 2
+ #define EPDC_PWR_ON 3
+ #define EPDC_PWR_OFF 4
+ #define EPDC_VCOM_ON 5
+ #define EPDC_VCOM_OFF 6
+ #define EPDC_AUTOOFF_ENABLE 7
+ #define EPDC_AUTOOFF_DISABLE 8
+
+#define EPDC_VCOM_SET 0x4571
+#define EPDC_VCOM_SET_TO_FLASH 0x4572
+#define EPDC_VCOM_GET 0x4573
+
+
+
+//#define S1D13521_FLASH 0x0100
+#define S1D13521_FLASH 0x4700
+#define S1D13521_RESET 0x4701
+#define S1D13521_PGM 0x4702
+
+// System commands
+#define INIT_CMD_SET 0x00
+#define INIT_PLL_STANDBY 0x01
+#define RUN_SYS 0x02
+#define STBY 0x04
+#define SLP 0x05
+#define INIT_SYS_RUN 0x06
+#define INIT_SYS_STBY 0x07
+#define INIT_SDRAM 0x08
+#define INIT_DSPE_CFG 0x09
+#define INIT_DSPE_TMG 0x0A
+#define INIT_ROTMODE 0x0B
+//#define SET_REFRESH_CNT 0x0C
+#define INIT_WAVEDEV 0x0C
+
+
+// Register and memory access commands
+#define RD_REG 0x10
+#define WR_REG 0x11
+#define RD_SFM 0x12
+#define WR_SFM 0x13
+#define END_SFM 0x14
+
+// Burst access commands
+#define BST_RD_SDR 0x1C
+#define BST_WR_SDR 0x1D
+#define BST_END_SDR 0x1E
+
+// Image loading commands
+#define LD_IMG 0x20
+#define LD_IMG_AREA 0x22
+#define LD_IMG_END 0x23
+#define LD_IMG_WAIT 0x24
+#define LD_IMG_SETADR 0x25
+#define LD_IMG_DSPEADR 0x26
+
+// Polling commands
+#define WAIT_DSPE_TRG 0x28
+#define WAIT_DSPE_FREND 0x29
+#define WAIT_DSPE_LUTFREE 0x2A
+#define WAIT_DSPE_MLUTFREE 0x2B
+
+// Waveform update commands
+#define RD_WFM_INFO 0x30
+#define UPD_INIT 0x32
+#define UPD_FULL 0x33
+#define UPD_FULL_AREA 0x34
+#define UPD_PART 0x35
+#define UPD_PART_AREA 0x36
+#define UPD_GDRV_CLR 0x37
+#define UPD_SET_IMGADR 0x38
+
+#pragma pack(1)
+
+typedef struct
+{
+ u16 param[5];
+}s1d13521_ioctl_cmd_params;
+
+#pragma pack()
+
+#define S1D13521_INIT_CMD_SET (0x4500 | INIT_CMD_SET)
+#define S1D13521_INIT_PLL_STANDBY (0x4500 | INIT_PLL_STANDBY)
+#define S1D13521_RUN_SYS (0x4500 | RUN_SYS)
+#define S1D13521_STBY (0x4500 | STBY)
+#define S1D13521_SLP (0x4500 | SLP)
+#define S1D13521_INIT_SYS_RUN (0x4500 | INIT_SYS_RUN)
+#define S1D13521_INIT_SYS_STBY (0x4500 | INIT_SYS_STBY)
+#define S1D13521_INIT_SDRAM (0x4500 | INIT_SDRAM)
+#define S1D13521_INIT_DSPE_CFG (0x4500 | INIT_DSPE_CFG)
+#define S1D13521_INIT_DSPE_TMG (0x4500 | INIT_DSPE_TMG)
+#define S1D13521_INIT_ROTMODE (0x4500 | INIT_ROTMODE)
+#define S1D13522_INIT_WAVEDEV (0x4500 | INIT_WAVEDEV)
+#define S1D13521_RD_REG (0x4500 | RD_REG)
+#define S1D13521_WR_REG (0x4500 | WR_REG)
+#define S1D13521_RD_SFM (0x4500 | RD_SFM)
+#define S1D13521_WR_SFM (0x4500 | WR_SFM)
+#define S1D13521_END_SFM (0x4500 | END_SFM)
+
+// Burst access commands
+#define S1D13521_BST_RD_SDR (0x4500 | BST_RD_SDR)
+#define S1D13521_BST_WR_SDR (0x4500 | BST_WR_SDR)
+#define S1D13521_BST_END_SDR (0x4500 | BST_END_SDR)
+
+// Image loading IOCTL commands
+#define S1D13521_LD_IMG (0x4500 | LD_IMG)
+#define S1D13521_LD_IMG_AREA (0x4500 | LD_IMG_AREA)
+#define S1D13521_LD_IMG_END (0x4500 | LD_IMG_END)
+#define S1D13521_LD_IMG_WAIT (0x4500 | LD_IMG_WAIT)
+#define S1D13521_LD_IMG_SETADR (0x4500 | LD_IMG_SETADR)
+#define S1D13521_LD_IMG_DSPEADR (0x4500 | LD_IMG_DSPEADR)
+
+// Polling commands
+#define S1D13521_WAIT_DSPE_TRG (0x4500 | WAIT_DSPE_TRG)
+#define S1D13521_WAIT_DSPE_FREND (0x4500 | WAIT_DSPE_FREND)
+#define S1D13521_WAIT_DSPE_LUTFREE (0x4500 | WAIT_DSPE_LUTFREE)
+#define S1D13521_WAIT_DSPE_MLUTFREE (0x4500 | WAIT_DSPE_MLUTFREE)
+
+// Waveform update IOCTL commands
+#define S1D13521_RD_WFM_INFO (0x4500 | RD_WFM_INFO)
+#define S1D13521_UPD_INIT (0x4500 | UPD_INIT)
+#define S1D13521_UPD_FULL (0x4500 | UPD_FULL)
+#define S1D13521_UPD_FULL_AREA (0x4500 | UPD_FULL_AREA)
+#define S1D13521_UPD_PART (0x4500 | UPD_PART)
+#define S1D13521_UPD_PART_AREA (0x4500 | UPD_PART_AREA)
+#define S1D13521_UPD_GDRV_CLR (0x4500 | UPD_GDRV_CLR)
+#define S1D13521_UPD_SET_IMGADR (0x4500 | UPD_SET_IMGADR)
+
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ //
+ //
+ //
+ //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+ typedef unsigned int HWND;
+// #define MaxBufferSize (((800 * 600) / 2) + 50)
+ #define MaxBufferSize (((_PANEL_WIDTH * _PANEL_HEIGHT) / 2) + 50)
+
+ typedef enum {
+ _STEP_NOR_BYPASS=0,
+ _STEP_NOR_WRITE_AA_INTO_555,
+ _STEP_NOR_WRITE_55_INTO_2AA,
+
+ _STEP_NOR_CMD_PROGRAM,
+ _STEP_NOR_CMD_PROGRAM_AA_WRITE_INTO_555,
+ _STEP_NOR_CMD_PROGRAM_55_WRITE_INTO_2AA,
+ _STEP_NOR_CMD_PROGRAM_DATA,
+
+ _STEP_NOR_CMD_ERASE,
+ _STEP_NOR_CMD_ERASE_WRITE_AA_INTO_555,
+ _STEP_NOR_CMD_ERASE_WRITE_55_INTO_2AA,
+
+ _STEP_NOR_CMD_STATUS,
+
+
+ } STEP_NOR_2_SPI_FLASH;
+
+// #pragma pack(1)
+ typedef struct TAG_ST_PVI_CONFIG {
+ UINT16 CurRotateMode;
+ UINT16 PastRotateMode;
+
+ STEP_NOR_2_SPI_FLASH StepSPI;
+
+ UINT16 ReverseGrade;
+
+ BOOL fAutoRefreshMode;
+ BYTE AutoRefreshTimer;
+
+ UINT16 fNormalMode;
+
+ BYTE Deepth;
+
+ UINT16 StartX;
+ UINT16 StartY;
+ UINT16 Width;
+ UINT16 Height;
+
+ UINT16 Reg0x140;
+ UINT16 Reg0x330;
+
+ } ST_PVI_CONFIG, *PST_PVI_CONFIG;
+
+ #pragma pack()
+
+ typedef struct ST_IMAGE_PGM_TAG{
+ unsigned int StartX;
+ unsigned int StartY;
+ unsigned int Width;
+ unsigned int Height;
+ unsigned int WaveForm;
+ unsigned int LUT_NO;
+ BYTE Data[_PANEL_WIDTH*_PANEL_HEIGHT];
+ } ST_IMAGE_PGM, *PST_IMAGE_PGM;
+
+ typedef struct ST_IMAGE_PGM_800x600_TAG{
+ unsigned int StartX;
+ unsigned int StartY;
+ unsigned int Width;
+ unsigned int Height;
+ unsigned int WaveForm;
+ unsigned int LUT_NO;
+ BYTE Data[800*600];
+ } ST_IMAGE_PGM_800x600, *PST_IMAGE_PGM_800x600;
+
+
+/****kay*******/
+//kay 20090428 for epson memory control
+typedef enum {
+ _EPSON_MEM_CLEAR=0,
+ _EPSON_MEM_CLEAR_BY_INDEX=1,
+ _EPSON_MEM_CLEAR_BY_NAME=2,
+ _EPSON_MEM_SAVE_BY_INDEX=3,
+ _EPSON_MEM_SAVE_BY_NAME=4,
+ _EPSON_MEM_RESTORE_BY_INDEX=5,
+ _EPSON_MEM_RESTORE_BY_NAME=6,
+ _EPSON_MEM_SAVE_BY_NAME_NOT_SHOW=7,
+ _EPSON_MEM_SHOW_PIC=8,
+
+} E_EPSON_MEM_CMD;
+
+#define MAX_EPSON_MEMORY_NAME_LENGTH 64
+#define COUNT_FOR_32M 60
+#define COUNT_FOR_64M 120
+//#define EPSON_MEMORY_CTL_BASE_START 800*600*2
+//#define EPSON_MEMORY_CTL_UNIT_SIZE 800*600
+#define MAX_EPSON_MEMORY_CTL_SIZE COUNT_FOR_32M
+ struct EPSON_MEMORY_CTL {
+ U8 cmd;
+ UINT16 x;
+ UINT16 y;
+ UINT16 width;
+ UINT16 height;
+ UINT16 rotate;
+ U8 index;
+ char name[MAX_EPSON_MEMORY_NAME_LENGTH];
+ U8 waveform;
+ };
+
+
+//
+
+ typedef struct ST_IMAGE_PGM_TAG_MEM{
+ U8 cmd;
+ U8 index;
+ char name[MAX_EPSON_MEMORY_NAME_LENGTH];
+ unsigned int StartX;
+ unsigned int StartY;
+ unsigned int Width;
+ unsigned int Height;
+ unsigned int WaveForm;
+ unsigned int LUT_NO;
+ unsigned int Depth;
+ BYTE Data[_PANEL_WIDTH*_PANEL_HEIGHT];
+ } ST_IMAGE_PGM_MEM, *PST_IMAGE_PGM_MEM;
+ typedef struct ST_IMAGE_PGM_TAG_MEM_KERNEL{
+ U8 cmd;
+ U8 index;
+ char name[MAX_EPSON_MEMORY_NAME_LENGTH];
+ unsigned int StartX;
+ unsigned int StartY;
+ unsigned int Width;
+ unsigned int Height;
+ unsigned int WaveForm;
+ unsigned int LUT_NO;
+ unsigned int Depth;
+// BYTE Data[800*600]; we don't save image data in kernel :P
+ } ST_IMAGE_PGM_MEM_KERNEL, *PST_IMAGE_PGM_MEM_KERNEL;
+
+/*******kay*******/
+
+
+
+ typedef struct TAG_TDisplayCommand{
+ int Owner;
+ BYTE Command;
+ int BytesToWrite ;
+ int BytesToRead;
+ BYTE Data[MaxBufferSize];
+ } TDisplayCommand, *PTDisplayCommand;
+
+ typedef struct TAG_TDisplayCommandSmall{
+ int Owner;
+ BYTE Command;
+ int BytesToWrite ;
+ int BytesToRead;
+ BYTE Data[16];
+ } TDisplayCommandSmall, *PTDisplayCommandSmall;
+
+ typedef struct TAG_TDisplayCommandHeader{
+ int Owner;
+ BYTE Command;
+ int BytesToWrite ;
+ int BytesToRead;
+ } TDisplayCommandHeader, *PTDisplayCommandHeader;
+
+ #define _OFFSET_DISP_COMMAND_FDATA (((PTDisplayCommand) 0)->Data)
+
+ #define dc_NewImage 0xA0
+ #define dc_StopNewImage 0xA1
+ #define dc_DisplayImage 0xA2
+ #define dc_PartialImage 0xB0
+ #define dc_DisplayPartial 0xB1
+ #define dc_DisplayPartialGU 0xB2
+ #define dc_Reset 0xEE
+ #define dc_SetDepth 0xF3
+ #define dc_EraseDisplay 0xA3
+ #define dc_Rotate 0xF5
+ #define dc_Positive 0xF7
+ #define dc_Negative 0xF8
+ #define dc_GoToNormal 0xF0
+ #define dc_GoToSleep 0xF1
+ #define dc_GoToStandBy 0xF2
+ #define dc_WriteToFlash 0x01
+ #define dc_ReadFromFlash 0x02
+ #define dc_Init 0xA4
+ #define dc_AutoRefreshOn 0xF9
+ #define dc_AutoRefreshOff 0xFA
+ #define dc_SetRefresh 0xFB
+ #define dc_ForcedRefresh 0xFC
+ #define dc_GetRefresh 0xFD
+ #define dc_RestoreImage 0xA5
+ #define dc_ControllerVersion 0xE0
+ #define dc_SoftwareVersion 0xE1
+ #define dc_DisplaySize 0xE2
+ #define dc_GetStatus 0xAA
+ #define dc_Temperature 0x21
+ #define dc_WriteRegister 0x10
+ #define dc_ReadRegister 0x11
+ #define dc_Abort 0xA1
+ #define CMD_SendCommand 1
+ #define CMD_EPSON_MEMORY_CTL 3
+
+ typedef struct _S1D13532_FLASH_PACKAGE_TAG {
+ DWORD Command;
+ DWORD StartAddr;
+ DWORD DataLength;
+ BYTE Buf[4];
+ } S1D13532_FLASH_PACKAGE, *PS1D13532_FLASH_PACKAGE;
+
+ #define _FLASH_CMD_GET_INFO 0
+ #define _FLASH_CMD_ERASE 1
+ #define _FLASH_CMD_WRITE 2
+ #define _FLASH_CMD_READ 3
+
+ typedef struct TAG_LOADIMAGEAREA {
+ BYTE cmd;
+ BYTE mode;
+ int XStart;
+ int YStart;
+ int Width;
+ int Height;
+ } TLoadImageArea, *PTloadImageArea;
+
+#endif // __EPSON_IOCTL_H__
+
diff --git a/drivers/video/mxc/tve.c b/drivers/video/mxc/tve.c
new file mode 100644
index 00000000..7a5c3b5d
--- /dev/null
+++ b/drivers/video/mxc/tve.c
@@ -0,0 +1,1310 @@
+/*
+ * Copyright 2008-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file tve.c
+ * @brief Driver for i.MX TV encoder
+ *
+ * @ingroup Framebuffer
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/clk.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/sysfs.h>
+#include <linux/irq.h>
+#include <linux/sysfs.h>
+#include <linux/platform_device.h>
+#include <linux/ipu.h>
+#include <linux/mxcfb.h>
+#include <linux/regulator/consumer.h>
+#include <linux/fsl_devices.h>
+#include <linux/uaccess.h>
+#include <asm/atomic.h>
+#include <mach/hardware.h>
+#include "mxc_dispdrv.h"
+
+#define TVE_ENABLE (1UL)
+#define TVE_DAC_FULL_RATE (0UL<<1)
+#define TVE_DAC_DIV2_RATE (1UL<<1)
+#define TVE_DAC_DIV4_RATE (2UL<<1)
+#define TVE_IPU_CLK_ENABLE (1UL<<3)
+
+#define CD_LM_INT 0x00000001
+#define CD_SM_INT 0x00000002
+#define CD_MON_END_INT 0x00000004
+#define CD_CH_0_LM_ST 0x00000001
+#define CD_CH_0_SM_ST 0x00000010
+#define CD_CH_1_LM_ST 0x00000002
+#define CD_CH_1_SM_ST 0x00000020
+#define CD_CH_2_LM_ST 0x00000004
+#define CD_CH_2_SM_ST 0x00000040
+#define CD_MAN_TRIG 0x00000100
+
+#define TVE_STAND_MASK (0x0F<<8)
+#define TVE_NTSC_STAND (0UL<<8)
+#define TVE_PAL_STAND (3UL<<8)
+#define TVE_HD720P60_STAND (4UL<<8)
+#define TVE_HD720P50_STAND (5UL<<8)
+#define TVE_HD720P30_STAND (6UL<<8)
+#define TVE_HD720P25_STAND (7UL<<8)
+#define TVE_HD720P24_STAND (8UL<<8)
+#define TVE_HD1080I60_STAND (9UL<<8)
+#define TVE_HD1080I50_STAND (10UL<<8)
+#define TVE_HD1035I60_STAND (11UL<<8)
+#define TVE_HD1080P30_STAND (12UL<<8)
+#define TVE_HD1080P25_STAND (13UL<<8)
+#define TVE_HD1080P24_STAND (14UL<<8)
+#define TVE_DAC_SAMPRATE_MASK (0x3<<1)
+#define TVEV2_DATA_SRC_MASK (0x3<<4)
+
+#define TVEV2_DATA_SRC_BUS_1 (0UL<<4)
+#define TVEV2_DATA_SRC_BUS_2 (1UL<<4)
+#define TVEV2_DATA_SRC_EXT (2UL<<4)
+
+#define TVEV2_INP_VIDEO_FORM (1UL<<6)
+#define TVEV2_P2I_CONV_EN (1UL<<7)
+
+#define TVEV2_DAC_GAIN_MASK 0x3F
+#define TVEV2_DAC_TEST_MODE_MASK 0x7
+
+#define TVOUT_FMT_OFF 0
+#define TVOUT_FMT_NTSC 1
+#define TVOUT_FMT_PAL 2
+#define TVOUT_FMT_720P60 3
+#define TVOUT_FMT_720P30 4
+#define TVOUT_FMT_1080I60 5
+#define TVOUT_FMT_1080I50 6
+#define TVOUT_FMT_1080P30 7
+#define TVOUT_FMT_1080P25 8
+#define TVOUT_FMT_1080P24 9
+#define TVOUT_FMT_VGA_SVGA 10
+#define TVOUT_FMT_VGA_XGA 11
+#define TVOUT_FMT_VGA_SXGA 12
+#define TVOUT_FMT_VGA_WSXGA 13
+
+#define DISPDRV_VGA "vga"
+#define DISPDRV_TVE "tve"
+
+struct tve_data {
+ struct platform_device *pdev;
+ int revision;
+ int cur_mode;
+ int output_mode;
+ int detect;
+ void *base;
+ spinlock_t tve_lock;
+ bool inited;
+ int enabled;
+ int irq;
+ struct clk *clk;
+ struct clk *di_clk;
+ struct regulator *dac_reg;
+ struct regulator *dig_reg;
+ struct delayed_work cd_work;
+ struct tve_reg_mapping *regs;
+ struct tve_reg_fields_mapping *reg_fields;
+ struct mxc_dispdrv_handle *disp_tve;
+ struct mxc_dispdrv_handle *disp_vga;
+ struct notifier_block nb;
+};
+
+struct tve_reg_mapping {
+ u32 tve_com_conf_reg;
+ u32 tve_cd_cont_reg;
+ u32 tve_int_cont_reg;
+ u32 tve_stat_reg;
+ u32 tve_mv_cont_reg;
+ u32 tve_tvdac_cont_reg;
+ u32 tve_tst_mode_reg;
+};
+
+struct tve_reg_fields_mapping {
+ u32 cd_en;
+ u32 cd_trig_mode;
+ u32 cd_lm_int;
+ u32 cd_sm_int;
+ u32 cd_mon_end_int;
+ u32 cd_man_trig;
+ u32 sync_ch_mask;
+ u32 tvout_mode_mask;
+ u32 sync_ch_offset;
+ u32 tvout_mode_offset;
+ u32 cd_ch_stat_offset;
+};
+
+static struct tve_reg_mapping tve_regs_v1 = {
+ 0, 0x14, 0x28, 0x2C, 0x48, 0x08, 0x30
+};
+
+static struct tve_reg_fields_mapping tve_reg_fields_v1 = {
+ 1, 2, 1, 2, 4, 0x00010000, 0x7000, 0x70, 12, 4, 8
+};
+
+static struct tve_reg_mapping tve_regs_v2 = {
+ 0, 0x34, 0x64, 0x68, 0xDC, 0x28, 0x6c
+};
+
+static struct tve_reg_fields_mapping tve_reg_fields_v2 = {
+ 1, 2, 1, 2, 4, 0x01000000, 0x700000, 0x7000, 20, 12, 16
+};
+
+static struct fb_videomode video_modes_tve[] = {
+ {
+ /* NTSC TV output */
+ "TV-NTSC", 60, 720, 480, 74074,
+ 122, 15,
+ 18, 26,
+ 1, 1,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_INTERLACED,
+ FB_MODE_IS_DETAILED,},
+ {
+ /* PAL TV output */
+ "TV-PAL", 50, 720, 576, 74074,
+ 132, 11,
+ 22, 26,
+ 1, 1,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_INTERLACED | FB_VMODE_ODD_FLD_FIRST,
+ FB_MODE_IS_DETAILED,},
+ {
+ /* 720p60 TV output */
+ "TV-720P60", 60, 1280, 720, 13468,
+ 260, 109,
+ 25, 4,
+ 1, 1,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED,
+ FB_MODE_IS_DETAILED,},
+ {
+ /* 720p30 TV output */
+ "TV-720P30", 30, 1280, 720, 13468,
+ 260, 1759,
+ 25, 4,
+ 1, 1,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED,
+ FB_MODE_IS_DETAILED,},
+ {
+ /* 1080i60 TV output */
+ "TV-1080I60", 60, 1920, 1080, 13468,
+ 192, 87,
+ 20, 24,
+ 1, 1,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_INTERLACED | FB_VMODE_ODD_FLD_FIRST,
+ FB_MODE_IS_DETAILED,},
+ {
+ /* 1080i50 TV output */
+ "TV-1080I50", 50, 1920, 1080, 13468,
+ 192, 527,
+ 20, 24,
+ 1, 1,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_INTERLACED | FB_VMODE_ODD_FLD_FIRST,
+ FB_MODE_IS_DETAILED,},
+ {
+ /* 1080p30 TV output */
+ "TV-1080P30", 30, 1920, 1080, 13468,
+ 192, 87,
+ 38, 6,
+ 1, 1,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED,
+ FB_MODE_IS_DETAILED,},
+ {
+ /* 1080p25 TV output */
+ "TV-1080P25", 25, 1920, 1080, 13468,
+ 192, 527,
+ 38, 6,
+ 1, 1,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED,
+ FB_MODE_IS_DETAILED,},
+ {
+ /* 1080p24 TV output */
+ "TV-1080P24", 24, 1920, 1080, 13468,
+ 192, 637,
+ 38, 6,
+ 1, 1,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED,
+ FB_MODE_IS_DETAILED,},
+};
+static int tve_modedb_sz = ARRAY_SIZE(video_modes_tve);
+
+static struct fb_videomode video_modes_vga[] = {
+ {
+ /* VGA 800x600 40M pixel clk output */
+ "VGA-SVGA", 60, 800, 600, 25000,
+ 215, 28,
+ 24, 2,
+ 13, 2,
+ 0,
+ FB_VMODE_NONINTERLACED,
+ FB_MODE_IS_DETAILED,},
+ {
+ /* VGA 1024x768 65M pixel clk output */
+ "VGA-XGA", 60, 1024, 768, 15384,
+ 160, 24,
+ 29, 3,
+ 136, 6,
+ 0,
+ FB_VMODE_NONINTERLACED,
+ FB_MODE_IS_DETAILED,},
+ {
+ /* VGA 1280x1024 108M pixel clk output */
+ "VGA-SXGA", 60, 1280, 1024, 9259,
+ 358, 38,
+ 38, 2,
+ 12, 2,
+ 0,
+ FB_VMODE_NONINTERLACED,
+ FB_MODE_IS_DETAILED,},
+ {
+ /* VGA 1680x1050 294M pixel clk output */
+ "VGA-WSXGA+", 60, 1680, 1050, 6796,
+ 288, 104,
+ 33, 2,
+ 184, 2,
+ 0,
+ FB_VMODE_NONINTERLACED,
+ FB_MODE_IS_DETAILED,},
+};
+static int vga_modedb_sz = ARRAY_SIZE(video_modes_vga);
+
+enum tvout_mode {
+ TV_OFF,
+ CVBS0,
+ CVBS2,
+ CVBS02,
+ SVIDEO,
+ SVIDEO_CVBS,
+ YPBPR,
+ TVRGB
+};
+
+static unsigned short tvout_mode_to_channel_map[8] = {
+ 0, /* TV_OFF */
+ 1, /* CVBS0 */
+ 4, /* CVBS2 */
+ 5, /* CVBS02 */
+ 1, /* SVIDEO */
+ 5, /* SVIDEO_CVBS */
+ 1, /* YPBPR */
+ 7 /* TVRGB */
+};
+
+static void tve_dump_regs(struct tve_data *tve)
+{
+ dev_dbg(&tve->pdev->dev, "tve_com_conf_reg 0x%x\n",
+ readl(tve->base + tve->regs->tve_com_conf_reg));
+ dev_dbg(&tve->pdev->dev, "tve_cd_cont_reg 0x%x\n",
+ readl(tve->base + tve->regs->tve_cd_cont_reg));
+ dev_dbg(&tve->pdev->dev, "tve_int_cont_reg 0x%x\n",
+ readl(tve->base + tve->regs->tve_int_cont_reg));
+ dev_dbg(&tve->pdev->dev, "tve_tst_mode_reg 0x%x\n",
+ readl(tve->base + tve->regs->tve_tst_mode_reg));
+ dev_dbg(&tve->pdev->dev, "tve_tvdac_cont_reg0 0x%x\n",
+ readl(tve->base + tve->regs->tve_tvdac_cont_reg));
+ dev_dbg(&tve->pdev->dev, "tve_tvdac_cont_reg1 0x%x\n",
+ readl(tve->base + tve->regs->tve_tvdac_cont_reg + 4));
+ dev_dbg(&tve->pdev->dev, "tve_tvdac_cont_reg2 0x%x\n",
+ readl(tve->base + tve->regs->tve_tvdac_cont_reg + 8));
+}
+
+static int is_vga_enabled(struct tve_data *tve)
+{
+ u32 reg;
+
+ if (tve->revision == 2) {
+ reg = readl(tve->base + tve->regs->tve_tst_mode_reg);
+ if (reg & TVEV2_DAC_TEST_MODE_MASK)
+ return 1;
+ else
+ return 0;
+ }
+ return 0;
+}
+
+static inline int is_vga_mode(int mode)
+{
+ return ((mode == TVOUT_FMT_VGA_SVGA)
+ || (mode == TVOUT_FMT_VGA_XGA)
+ || (mode == TVOUT_FMT_VGA_SXGA)
+ || (mode == TVOUT_FMT_VGA_WSXGA));
+}
+
+static inline int valid_mode(int mode)
+{
+ return (is_vga_mode(mode)
+ || (mode == TVOUT_FMT_NTSC)
+ || (mode == TVOUT_FMT_PAL)
+ || (mode == TVOUT_FMT_720P30)
+ || (mode == TVOUT_FMT_720P60)
+ || (mode == TVOUT_FMT_1080I50)
+ || (mode == TVOUT_FMT_1080I60)
+ || (mode == TVOUT_FMT_1080P24)
+ || (mode == TVOUT_FMT_1080P25)
+ || (mode == TVOUT_FMT_1080P30));
+}
+
+static int get_video_mode(struct fb_info *fbi)
+{
+ int mode;
+
+ if (fb_mode_is_equal(fbi->mode, &video_modes_tve[0])) {
+ mode = TVOUT_FMT_NTSC;
+ } else if (fb_mode_is_equal(fbi->mode, &video_modes_tve[1])) {
+ mode = TVOUT_FMT_PAL;
+ } else if (fb_mode_is_equal(fbi->mode, &video_modes_tve[2])) {
+ mode = TVOUT_FMT_720P60;
+ } else if (fb_mode_is_equal(fbi->mode, &video_modes_tve[3])) {
+ mode = TVOUT_FMT_720P30;
+ } else if (fb_mode_is_equal(fbi->mode, &video_modes_tve[4])) {
+ mode = TVOUT_FMT_1080I60;
+ } else if (fb_mode_is_equal(fbi->mode, &video_modes_tve[5])) {
+ mode = TVOUT_FMT_1080I50;
+ } else if (fb_mode_is_equal(fbi->mode, &video_modes_tve[6])) {
+ mode = TVOUT_FMT_1080P30;
+ } else if (fb_mode_is_equal(fbi->mode, &video_modes_tve[7])) {
+ mode = TVOUT_FMT_1080P25;
+ } else if (fb_mode_is_equal(fbi->mode, &video_modes_tve[8])) {
+ mode = TVOUT_FMT_1080P24;
+ } else if (fb_mode_is_equal(fbi->mode, &video_modes_vga[0])) {
+ mode = TVOUT_FMT_VGA_SVGA;
+ } else if (fb_mode_is_equal(fbi->mode, &video_modes_vga[1])) {
+ mode = TVOUT_FMT_VGA_XGA;
+ } else if (fb_mode_is_equal(fbi->mode, &video_modes_vga[2])) {
+ mode = TVOUT_FMT_VGA_SXGA;
+ } else if (fb_mode_is_equal(fbi->mode, &video_modes_vga[3])) {
+ mode = TVOUT_FMT_VGA_WSXGA;
+ } else {
+ mode = TVOUT_FMT_OFF;
+ }
+ return mode;
+}
+
+static void tve_disable_vga_mode(struct tve_data *tve)
+{
+ if (tve->revision == 2) {
+ u32 reg;
+ /* disable test mode */
+ reg = readl(tve->base + tve->regs->tve_tst_mode_reg);
+ reg = reg & ~TVEV2_DAC_TEST_MODE_MASK;
+ writel(reg, tve->base + tve->regs->tve_tst_mode_reg);
+ }
+}
+
+static void tve_set_tvout_mode(struct tve_data *tve, int mode)
+{
+ u32 conf_reg;
+
+ /* clear sync_ch and tvout_mode fields */
+ conf_reg = readl(tve->base + tve->regs->tve_com_conf_reg);
+ conf_reg &= ~(tve->reg_fields->sync_ch_mask |
+ tve->reg_fields->tvout_mode_mask);
+
+ conf_reg = conf_reg & ~TVE_DAC_SAMPRATE_MASK;
+ if (tve->revision == 2) {
+ conf_reg = (conf_reg & ~TVEV2_DATA_SRC_MASK) |
+ TVEV2_DATA_SRC_BUS_1;
+ conf_reg = conf_reg & ~TVEV2_INP_VIDEO_FORM;
+ conf_reg = conf_reg & ~TVEV2_P2I_CONV_EN;
+ }
+
+ conf_reg |=
+ mode << tve->reg_fields->
+ tvout_mode_offset | tvout_mode_to_channel_map[mode] <<
+ tve->reg_fields->sync_ch_offset;
+ writel(conf_reg, tve->base + tve->regs->tve_com_conf_reg);
+}
+
+static int _is_tvout_mode_hd_compatible(struct tve_data *tve)
+{
+ u32 conf_reg, mode;
+
+ conf_reg = readl(tve->base + tve->regs->tve_com_conf_reg);
+ mode = (conf_reg >> tve->reg_fields->tvout_mode_offset) & 7;
+ if (mode == YPBPR || mode == TVRGB) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int tve_setup_vga(struct tve_data *tve)
+{
+ u32 reg;
+
+ if (tve->revision == 2) {
+ /* set gain */
+ reg = readl(tve->base + tve->regs->tve_tvdac_cont_reg);
+ reg = (reg & ~TVEV2_DAC_GAIN_MASK) | 0xa;
+ writel(reg, tve->base + tve->regs->tve_tvdac_cont_reg);
+ reg = readl(tve->base + tve->regs->tve_tvdac_cont_reg + 4);
+ reg = (reg & ~TVEV2_DAC_GAIN_MASK) | 0xa;
+ writel(reg, tve->base + tve->regs->tve_tvdac_cont_reg + 4);
+ reg = readl(tve->base + tve->regs->tve_tvdac_cont_reg + 8);
+ reg = (reg & ~TVEV2_DAC_GAIN_MASK) | 0xa;
+ writel(reg, tve->base + tve->regs->tve_tvdac_cont_reg + 8);
+
+ /* set tve_com_conf_reg */
+ reg = readl(tve->base + tve->regs->tve_com_conf_reg);
+ reg = (reg & ~TVE_DAC_SAMPRATE_MASK) | TVE_DAC_DIV2_RATE;
+ reg = (reg & ~TVEV2_DATA_SRC_MASK) | TVEV2_DATA_SRC_BUS_2;
+ reg = reg | TVEV2_INP_VIDEO_FORM;
+ reg = reg & ~TVEV2_P2I_CONV_EN;
+ reg = (reg & ~TVE_STAND_MASK) | TVE_HD1080P30_STAND;
+ reg |= TVRGB << tve->reg_fields->tvout_mode_offset |
+ 1 << tve->reg_fields->sync_ch_offset;
+ writel(reg, tve->base + tve->regs->tve_com_conf_reg);
+
+ /* set test mode */
+ reg = readl(tve->base + tve->regs->tve_tst_mode_reg);
+ reg = (reg & ~TVEV2_DAC_TEST_MODE_MASK) | 1;
+ writel(reg, tve->base + tve->regs->tve_tst_mode_reg);
+ }
+
+ return 0;
+}
+
+/**
+ * tve_setup
+ * initial the CH7024 chipset by setting register
+ * @param:
+ * vos: output video format
+ * @return:
+ * 0 successful
+ * otherwise failed
+ */
+static int tve_setup(struct tve_data *tve, int mode)
+{
+ u32 reg;
+ struct clk *tve_parent_clk;
+ unsigned long parent_clock_rate = 216000000, di1_clock_rate = 27000000;
+ unsigned long tve_clock_rate = 216000000;
+ unsigned long lock_flags;
+
+ if (tve->cur_mode == mode)
+ return 0;
+
+ spin_lock_irqsave(&tve->tve_lock, lock_flags);
+
+ switch (mode) {
+ case TVOUT_FMT_PAL:
+ case TVOUT_FMT_NTSC:
+ parent_clock_rate = 216000000;
+ di1_clock_rate = 27000000;
+ break;
+ case TVOUT_FMT_720P60:
+ case TVOUT_FMT_1080I60:
+ case TVOUT_FMT_1080I50:
+ case TVOUT_FMT_720P30:
+ case TVOUT_FMT_1080P30:
+ case TVOUT_FMT_1080P25:
+ case TVOUT_FMT_1080P24:
+ parent_clock_rate = 297000000;
+ tve_clock_rate = 297000000;
+ di1_clock_rate = 74250000;
+ break;
+ case TVOUT_FMT_VGA_SVGA:
+ parent_clock_rate = 160000000;
+ tve_clock_rate = 80000000;
+ di1_clock_rate = 40000000;
+ break;
+ case TVOUT_FMT_VGA_XGA:
+ parent_clock_rate = 520000000;
+ tve_clock_rate = 130000000;
+ di1_clock_rate = 65000000;
+ break;
+ case TVOUT_FMT_VGA_SXGA:
+ parent_clock_rate = 864000000;
+ tve_clock_rate = 216000000;
+ di1_clock_rate = 108000000;
+ break;
+ case TVOUT_FMT_VGA_WSXGA:
+ parent_clock_rate = 588560000;
+ tve_clock_rate = 294280000;
+ di1_clock_rate = 147140000;
+ break;
+ }
+ if (tve->enabled)
+ clk_disable(tve->clk);
+
+ tve_parent_clk = clk_get_parent(tve->clk);
+
+ clk_set_rate(tve_parent_clk, parent_clock_rate);
+
+ tve_clock_rate = clk_round_rate(tve->clk, tve_clock_rate);
+ clk_set_rate(tve->clk, tve_clock_rate);
+
+ clk_enable(tve->clk);
+ di1_clock_rate = clk_round_rate(tve->di_clk, di1_clock_rate);
+ clk_set_rate(tve->di_clk, di1_clock_rate);
+
+ tve->cur_mode = mode;
+
+ /* select output video format */
+ if (mode == TVOUT_FMT_PAL) {
+ tve_disable_vga_mode(tve);
+ tve_set_tvout_mode(tve, YPBPR);
+ reg = readl(tve->base + tve->regs->tve_com_conf_reg);
+ reg = (reg & ~TVE_STAND_MASK) | TVE_PAL_STAND;
+ writel(reg, tve->base + tve->regs->tve_com_conf_reg);
+ dev_dbg(&tve->pdev->dev, "TVE: change to PAL video\n");
+ } else if (mode == TVOUT_FMT_NTSC) {
+ tve_disable_vga_mode(tve);
+ tve_set_tvout_mode(tve, YPBPR);
+ reg = readl(tve->base + tve->regs->tve_com_conf_reg);
+ reg = (reg & ~TVE_STAND_MASK) | TVE_NTSC_STAND;
+ writel(reg, tve->base + tve->regs->tve_com_conf_reg);
+ dev_dbg(&tve->pdev->dev, "TVE: change to NTSC video\n");
+ } else if (mode == TVOUT_FMT_720P60) {
+ tve_disable_vga_mode(tve);
+ if (!_is_tvout_mode_hd_compatible(tve)) {
+ tve_set_tvout_mode(tve, YPBPR);
+ dev_dbg(&tve->pdev->dev, "The TV out mode is HD incompatible. Setting to YPBPR.");
+ }
+ reg = readl(tve->base + tve->regs->tve_com_conf_reg);
+ reg = (reg & ~TVE_STAND_MASK) | TVE_HD720P60_STAND;
+ writel(reg, tve->base + tve->regs->tve_com_conf_reg);
+ dev_dbg(&tve->pdev->dev, "TVE: change to 720P60 video\n");
+ } else if (mode == TVOUT_FMT_720P30) {
+ tve_disable_vga_mode(tve);
+ if (!_is_tvout_mode_hd_compatible(tve)) {
+ tve_set_tvout_mode(tve, YPBPR);
+ dev_dbg(&tve->pdev->dev, "The TV out mode is HD incompatible. Setting to YPBPR.");
+ }
+ reg = readl(tve->base + tve->regs->tve_com_conf_reg);
+ reg = (reg & ~TVE_STAND_MASK) | TVE_HD720P30_STAND;
+ writel(reg, tve->base + tve->regs->tve_com_conf_reg);
+ dev_dbg(&tve->pdev->dev, "TVE: change to 720P30 video\n");
+ } else if (mode == TVOUT_FMT_1080I60) {
+ tve_disable_vga_mode(tve);
+ if (!_is_tvout_mode_hd_compatible(tve)) {
+ tve_set_tvout_mode(tve, YPBPR);
+ dev_dbg(&tve->pdev->dev, "The TV out mode is HD incompatible. Setting to YPBPR.");
+ }
+ reg = readl(tve->base + tve->regs->tve_com_conf_reg);
+ reg = (reg & ~TVE_STAND_MASK) | TVE_HD1080I60_STAND;
+ writel(reg, tve->base + tve->regs->tve_com_conf_reg);
+ dev_dbg(&tve->pdev->dev, "TVE: change to 1080I60 video\n");
+ } else if (mode == TVOUT_FMT_1080I50) {
+ tve_disable_vga_mode(tve);
+ if (!_is_tvout_mode_hd_compatible(tve)) {
+ tve_set_tvout_mode(tve, YPBPR);
+ dev_dbg(&tve->pdev->dev, "The TV out mode is HD incompatible. Setting to YPBPR.");
+ }
+ reg = readl(tve->base + tve->regs->tve_com_conf_reg);
+ reg = (reg & ~TVE_STAND_MASK) | TVE_HD1080I50_STAND;
+ writel(reg, tve->base + tve->regs->tve_com_conf_reg);
+ dev_dbg(&tve->pdev->dev, "TVE: change to 1080I50 video\n");
+ } else if (mode == TVOUT_FMT_1080P30) {
+ tve_disable_vga_mode(tve);
+ if (!_is_tvout_mode_hd_compatible(tve)) {
+ tve_set_tvout_mode(tve, YPBPR);
+ dev_dbg(&tve->pdev->dev, "The TV out mode is HD incompatible. Setting to YPBPR.");
+ }
+ reg = readl(tve->base + tve->regs->tve_com_conf_reg);
+ reg = (reg & ~TVE_STAND_MASK) | TVE_HD1080P30_STAND;
+ writel(reg, tve->base + tve->regs->tve_com_conf_reg);
+ dev_dbg(&tve->pdev->dev, "TVE: change to 1080P30 video\n");
+ } else if (mode == TVOUT_FMT_1080P25) {
+ tve_disable_vga_mode(tve);
+ if (!_is_tvout_mode_hd_compatible(tve)) {
+ tve_set_tvout_mode(tve, YPBPR);
+ dev_dbg(&tve->pdev->dev, "The TV out mode is HD incompatible. Setting to YPBPR.");
+ }
+ reg = readl(tve->base + tve->regs->tve_com_conf_reg);
+ reg = (reg & ~TVE_STAND_MASK) | TVE_HD1080P25_STAND;
+ writel(reg, tve->base + tve->regs->tve_com_conf_reg);
+ dev_dbg(&tve->pdev->dev, "TVE: change to 1080P25 video\n");
+ } else if (mode == TVOUT_FMT_1080P24) {
+ tve_disable_vga_mode(tve);
+ if (!_is_tvout_mode_hd_compatible(tve)) {
+ tve_set_tvout_mode(tve, YPBPR);
+ dev_dbg(&tve->pdev->dev, "The TV out mode is HD incompatible. Setting to YPBPR.");
+ }
+ reg = readl(tve->base + tve->regs->tve_com_conf_reg);
+ reg = (reg & ~TVE_STAND_MASK) | TVE_HD1080P24_STAND;
+ writel(reg, tve->base + tve->regs->tve_com_conf_reg);
+ dev_dbg(&tve->pdev->dev, "TVE: change to 1080P24 video\n");
+ } else if (is_vga_mode(mode)) {
+ /* do not need cable detect */
+ tve_setup_vga(tve);
+ dev_dbg(&tve->pdev->dev, "TVE: change to VGA video\n");
+ } else if (mode == TVOUT_FMT_OFF) {
+ writel(0x0, tve->base + tve->regs->tve_com_conf_reg);
+ dev_dbg(&tve->pdev->dev, "TVE: change to OFF video\n");
+ } else {
+ dev_dbg(&tve->pdev->dev, "TVE: no such video format.\n");
+ }
+
+ if (!tve->enabled)
+ clk_disable(tve->clk);
+
+ spin_unlock_irqrestore(&tve->tve_lock, lock_flags);
+ return 0;
+}
+
+/**
+ * tve_enable
+ * Enable the tve Power to begin TV encoder
+ */
+static void tve_enable(struct tve_data *tve)
+{
+ u32 reg;
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(&tve->tve_lock, lock_flags);
+ if (!tve->enabled) {
+ tve->enabled = 1;
+ clk_enable(tve->clk);
+ reg = readl(tve->base + tve->regs->tve_com_conf_reg);
+ writel(reg | TVE_IPU_CLK_ENABLE | TVE_ENABLE,
+ tve->base + tve->regs->tve_com_conf_reg);
+ dev_dbg(&tve->pdev->dev, "TVE power on.\n");
+ }
+
+ if (is_vga_enabled(tve)) {
+ /* disable interrupt */
+ dev_dbg(&tve->pdev->dev, "TVE VGA disable cable detect.\n");
+ writel(0xffffffff, tve->base + tve->regs->tve_stat_reg);
+ writel(0, tve->base + tve->regs->tve_int_cont_reg);
+ } else {
+ /* enable interrupt */
+ dev_dbg(&tve->pdev->dev, "TVE TVE enable cable detect.\n");
+ writel(0xffffffff, tve->base + tve->regs->tve_stat_reg);
+ writel(CD_SM_INT | CD_LM_INT | CD_MON_END_INT,
+ tve->base + tve->regs->tve_int_cont_reg);
+ }
+
+ spin_unlock_irqrestore(&tve->tve_lock, lock_flags);
+
+ tve_dump_regs(tve);
+}
+
+/**
+ * tve_disable
+ * Disable the tve Power to stop TV encoder
+ */
+static void tve_disable(struct tve_data *tve)
+{
+ u32 reg;
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(&tve->tve_lock, lock_flags);
+ if (tve->enabled) {
+ tve->enabled = 0;
+ reg = readl(tve->base + tve->regs->tve_com_conf_reg);
+ writel(reg & ~TVE_ENABLE & ~TVE_IPU_CLK_ENABLE,
+ tve->base + tve->regs->tve_com_conf_reg);
+ clk_disable(tve->clk);
+ dev_dbg(&tve->pdev->dev, "TVE power off.\n");
+ }
+ spin_unlock_irqrestore(&tve->tve_lock, lock_flags);
+}
+
+static int tve_update_detect_status(struct tve_data *tve)
+{
+ int old_detect = tve->detect;
+ u32 stat_lm, stat_sm, stat;
+ u32 int_ctl;
+ u32 cd_cont_reg;
+ u32 timeout = 40;
+ unsigned long lock_flags;
+ char event_string[16];
+ char *envp[] = { event_string, NULL };
+
+ spin_lock_irqsave(&tve->tve_lock, lock_flags);
+
+ if (!tve->enabled) {
+ dev_warn(&tve->pdev->dev, "Warning: update tve status while it disabled!\n");
+ tve->detect = 0;
+ goto done;
+ }
+
+ int_ctl = readl(tve->base + tve->regs->tve_int_cont_reg);
+ cd_cont_reg = readl(tve->base + tve->regs->tve_cd_cont_reg);
+
+ if ((cd_cont_reg & 0x1) == 0) {
+ dev_warn(&tve->pdev->dev, "Warning: pls enable TVE CD first!\n");
+ goto done;
+ }
+
+ stat = readl(tve->base + tve->regs->tve_stat_reg);
+ while (((stat & CD_MON_END_INT) == 0) && (timeout > 0)) {
+ spin_unlock_irqrestore(&tve->tve_lock, lock_flags);
+ msleep(2);
+ spin_lock_irqsave(&tve->tve_lock, lock_flags);
+ timeout -= 2;
+ if (!tve->enabled) {
+ dev_warn(&tve->pdev->dev, "Warning: update tve status while it disabled!\n");
+ tve->detect = 0;
+ goto done;
+ } else
+ stat = readl(tve->base + tve->regs->tve_stat_reg);
+ }
+ if (((stat & CD_MON_END_INT) == 0) && (timeout <= 0)) {
+ dev_warn(&tve->pdev->dev, "Warning: get detect result without CD_MON_END_INT!\n");
+ goto done;
+ }
+
+ stat = stat >> tve->reg_fields->cd_ch_stat_offset;
+ stat_lm = stat & (CD_CH_0_LM_ST | CD_CH_1_LM_ST | CD_CH_2_LM_ST);
+ if ((stat_lm == (CD_CH_0_LM_ST | CD_CH_1_LM_ST | CD_CH_2_LM_ST)) &&
+ ((stat & (CD_CH_0_SM_ST | CD_CH_1_SM_ST | CD_CH_2_SM_ST)) == 0)
+ ) {
+ tve->detect = 3;
+ tve->output_mode = YPBPR;
+ } else if ((stat_lm == (CD_CH_0_LM_ST | CD_CH_1_LM_ST)) &&
+ ((stat & (CD_CH_0_SM_ST | CD_CH_1_SM_ST)) == 0)) {
+ tve->detect = 4;
+ tve->output_mode = SVIDEO;
+ } else if (stat_lm == CD_CH_0_LM_ST) {
+ stat_sm = stat & CD_CH_0_SM_ST;
+ if (stat_sm != 0) {
+ /* headset */
+ tve->detect = 2;
+ tve->output_mode = TV_OFF;
+ } else {
+ tve->detect = 1;
+ tve->output_mode = CVBS0;
+ }
+ } else if (stat_lm == CD_CH_2_LM_ST) {
+ stat_sm = stat & CD_CH_2_SM_ST;
+ if (stat_sm != 0) {
+ /* headset */
+ tve->detect = 2;
+ tve->output_mode = TV_OFF;
+ } else {
+ tve->detect = 1;
+ tve->output_mode = CVBS2;
+ }
+ } else {
+ /* none */
+ tve->detect = 0;
+ tve->output_mode = TV_OFF;
+ }
+
+ tve_set_tvout_mode(tve, tve->output_mode);
+
+ /* clear interrupt */
+ writel(CD_MON_END_INT | CD_LM_INT | CD_SM_INT,
+ tve->base + tve->regs->tve_stat_reg);
+
+ writel(int_ctl | CD_SM_INT | CD_LM_INT,
+ tve->base + tve->regs->tve_int_cont_reg);
+
+done:
+ spin_unlock_irqrestore(&tve->tve_lock, lock_flags);
+
+ if (old_detect != tve->detect) {
+ sysfs_notify(&tve->pdev->dev.kobj, NULL, "headphone");
+ if (tve->detect == 1)
+ sprintf(event_string, "EVENT=CVBS0");
+ else if (tve->detect == 3)
+ sprintf(event_string, "EVENT=YPBPR");
+ else if (tve->detect == 4)
+ sprintf(event_string, "EVENT=SVIDEO");
+ else
+ sprintf(event_string, "EVENT=NONE");
+ kobject_uevent_env(&tve->pdev->dev.kobj, KOBJ_CHANGE, envp);
+ }
+
+ dev_dbg(&tve->pdev->dev, "detect = %d mode = %d\n",
+ tve->detect, tve->output_mode);
+ return tve->detect;
+}
+
+static void cd_work_func(struct work_struct *work)
+{
+ struct delayed_work *delay_work = to_delayed_work(work);
+ struct tve_data *tve =
+ container_of(delay_work, struct tve_data, cd_work);
+
+ tve_update_detect_status(tve);
+}
+
+static irqreturn_t tve_detect_handler(int irq, void *data)
+{
+ struct tve_data *tve = data;
+
+ u32 int_ctl = readl(tve->base + tve->regs->tve_int_cont_reg);
+
+ /* disable INT first */
+ int_ctl &= ~(CD_SM_INT | CD_LM_INT | CD_MON_END_INT);
+ writel(int_ctl, tve->base + tve->regs->tve_int_cont_reg);
+
+ writel(CD_MON_END_INT | CD_LM_INT | CD_SM_INT,
+ tve->base + tve->regs->tve_stat_reg);
+
+ schedule_delayed_work(&tve->cd_work, msecs_to_jiffies(1000));
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * FB suspend/resume routing
+ */
+static int tve_suspend(struct tve_data *tve)
+{
+ if (tve->enabled) {
+ writel(0, tve->base + tve->regs->tve_int_cont_reg);
+ writel(0, tve->base + tve->regs->tve_cd_cont_reg);
+ writel(0, tve->base + tve->regs->tve_com_conf_reg);
+ clk_disable(tve->clk);
+ }
+ return 0;
+}
+
+static int tve_resume(struct tve_data *tve, struct fb_info *fbi)
+{
+ int mode;
+
+ if (tve->enabled) {
+ clk_enable(tve->clk);
+
+ /* Setup cable detect */
+ if (tve->revision == 1)
+ writel(0x01067701,
+ tve->base + tve->regs->tve_cd_cont_reg);
+ else
+ writel(0x00770601,
+ tve->base + tve->regs->tve_cd_cont_reg);
+
+ if (valid_mode(tve->cur_mode)) {
+ mode = tve->cur_mode;
+ tve_disable(tve);
+ tve->cur_mode = TVOUT_FMT_OFF;
+ tve_setup(tve, mode);
+ }
+ tve_enable(tve);
+ }
+
+ return 0;
+}
+
+int tve_fb_setup(struct tve_data *tve, struct fb_info *fbi)
+{
+ int mode;
+
+ fbi->mode = (struct fb_videomode *)fb_match_mode(&fbi->var,
+ &fbi->modelist);
+
+ if (!fbi->mode) {
+ dev_warn(&tve->pdev->dev, "TVE: can not find mode for xres=%d, yres=%d\n",
+ fbi->var.xres, fbi->var.yres);
+ tve_disable(tve);
+ tve->cur_mode = TVOUT_FMT_OFF;
+ return 0;
+ }
+
+ dev_dbg(&tve->pdev->dev, "TVE: fb mode change event: xres=%d, yres=%d\n",
+ fbi->mode->xres, fbi->mode->yres);
+
+ mode = get_video_mode(fbi);
+ if (mode != TVOUT_FMT_OFF) {
+ tve_disable(tve);
+ tve_setup(tve, mode);
+ tve_enable(tve);
+ } else {
+ tve_disable(tve);
+ tve_setup(tve, mode);
+ }
+
+ return 0;
+}
+
+static inline int tve_disp_setup(struct mxc_dispdrv_handle *disp,
+ struct fb_info *fbi)
+{
+ struct tve_data *tve = mxc_dispdrv_getdata(disp);
+
+ tve_fb_setup(tve, fbi);
+ return 0;
+}
+
+int tve_fb_event(struct notifier_block *nb, unsigned long val, void *v)
+{
+ struct tve_data *tve = container_of(nb, struct tve_data, nb);
+ struct fb_event *event = v;
+ struct fb_info *fbi = event->info;
+
+ /* only work for ipu0 di1*/
+ if (strcmp(fbi->fix.id, "DISP3 BG - DI1"))
+ return 0;
+
+ switch (val) {
+ case FB_EVENT_BLANK:
+ if (fbi->mode == NULL)
+ return 0;
+
+ dev_dbg(&tve->pdev->dev, "TVE: fb blank event\n");
+
+ if (*((int *)event->data) == FB_BLANK_UNBLANK) {
+ int mode;
+ mode = get_video_mode(fbi);
+ if (mode != TVOUT_FMT_OFF) {
+ if (tve->cur_mode != mode) {
+ tve_disable(tve);
+ tve_setup(tve, mode);
+ }
+ tve_enable(tve);
+ } else
+ tve_setup(tve, mode);
+ } else
+ tve_disable(tve);
+ break;
+ case FB_EVENT_SUSPEND:
+ tve_suspend(tve);
+ break;
+ case FB_EVENT_RESUME:
+ tve_resume(tve, fbi);
+ break;
+ }
+ return 0;
+}
+
+static ssize_t show_headphone(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct tve_data *tve = dev_get_drvdata(dev);
+ int detect;
+
+ if (!tve->enabled) {
+ strcpy(buf, "tve power off\n");
+ return strlen(buf);
+ }
+
+ detect = tve_update_detect_status(tve);
+
+ if (detect == 0)
+ strcpy(buf, "none\n");
+ else if (detect == 1)
+ strcpy(buf, "cvbs\n");
+ else if (detect == 2)
+ strcpy(buf, "headset\n");
+ else if (detect == 3)
+ strcpy(buf, "component\n");
+ else
+ strcpy(buf, "svideo\n");
+
+ return strlen(buf);
+}
+
+static DEVICE_ATTR(headphone, S_IRUGO | S_IWUSR, show_headphone, NULL);
+
+static int _tve_get_revision(struct tve_data *tve)
+{
+ u32 conf_reg;
+ u32 rev = 0;
+
+ /* find out TVE rev based on the base addr default value
+ * can be used at the init/probe ONLY */
+ conf_reg = readl(tve->base);
+ switch (conf_reg) {
+ case 0x00842000:
+ rev = 1;
+ break;
+ case 0x00100000:
+ rev = 2;
+ break;
+ }
+ return rev;
+}
+
+static int tve_drv_init(struct mxc_dispdrv_handle *disp, bool vga,
+ struct mxc_dispdrv_setting *setting)
+{
+ int ret;
+ struct tve_data *tve = mxc_dispdrv_getdata(disp);
+ struct fsl_mxc_tve_platform_data *plat_data
+ = tve->pdev->dev.platform_data;
+ struct resource *res;
+ struct fb_videomode *modedb;
+ int modedb_sz;
+ u32 conf_reg;
+
+ if (tve->inited == true)
+ return -ENODEV;
+
+ /*tve&vga only use ipu0 and di1*/
+ setting->dev_id = 0;
+ setting->disp_id = 1;
+
+ res = platform_get_resource(tve->pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ ret = -ENOMEM;
+ goto get_res_failed;
+ }
+
+ tve->irq = platform_get_irq(tve->pdev, 0);
+ if (tve->irq < 0) {
+ ret = tve->irq;
+ goto get_irq_failed;
+ }
+
+ tve->base = ioremap(res->start, res->end - res->start);
+ if (!tve->base) {
+ ret = -ENOMEM;
+ goto ioremap_failed;
+ }
+
+ ret = device_create_file(&tve->pdev->dev, &dev_attr_headphone);
+ if (ret < 0)
+ goto dev_file_create_failed;
+
+ tve->dac_reg = regulator_get(&tve->pdev->dev, plat_data->dac_reg);
+ if (!IS_ERR(tve->dac_reg)) {
+ regulator_set_voltage(tve->dac_reg, 2750000, 2750000);
+ regulator_enable(tve->dac_reg);
+ }
+ tve->dig_reg = regulator_get(&tve->pdev->dev, plat_data->dig_reg);
+ if (!IS_ERR(tve->dig_reg)) {
+ regulator_set_voltage(tve->dig_reg, 1250000, 1250000);
+ regulator_enable(tve->dig_reg);
+ }
+
+ tve->clk = clk_get(&tve->pdev->dev, "tve_clk");
+ if (IS_ERR(tve->clk)) {
+ ret = PTR_ERR(tve->clk);
+ goto get_tveclk_failed;
+ }
+ tve->di_clk = clk_get(NULL, "ipu1_di1_clk");
+ if (IS_ERR(tve->di_clk)) {
+ ret = PTR_ERR(tve->di_clk);
+ goto get_diclk_failed;
+ }
+
+ clk_set_rate(tve->clk, 216000000);
+ clk_set_parent(tve->di_clk, tve->clk);
+ clk_enable(tve->clk);
+
+ tve->revision = _tve_get_revision(tve);
+ if (tve->revision == 1) {
+ tve->regs = &tve_regs_v1;
+ tve->reg_fields = &tve_reg_fields_v1;
+ } else {
+ tve->regs = &tve_regs_v2;
+ tve->reg_fields = &tve_reg_fields_v2;
+ }
+
+ /* adjust video mode for mx37 */
+ if (cpu_is_mx37()) {
+ video_modes_tve[0].left_margin = 121;
+ video_modes_tve[0].right_margin = 16;
+ video_modes_tve[0].upper_margin = 17;
+ video_modes_tve[0].lower_margin = 5;
+ video_modes_tve[1].left_margin = 131;
+ video_modes_tve[1].right_margin = 12;
+ video_modes_tve[1].upper_margin = 21;
+ video_modes_tve[1].lower_margin = 3;
+ }
+
+ if (vga && cpu_is_mx53()) {
+ setting->if_fmt = IPU_PIX_FMT_GBR24;
+ modedb = video_modes_vga;
+ modedb_sz = vga_modedb_sz;
+ } else {
+ setting->if_fmt = IPU_PIX_FMT_YUV444;
+ if (tve->revision == 1) {
+ modedb = video_modes_tve;
+ modedb_sz = 3;
+ } else {
+ modedb = video_modes_tve;
+ modedb_sz = tve_modedb_sz;
+ }
+ }
+
+ fb_videomode_to_modelist(modedb, modedb_sz, &setting->fbi->modelist);
+
+ /* must use spec video mode defined by driver */
+ ret = fb_find_mode(&setting->fbi->var, setting->fbi, setting->dft_mode_str,
+ modedb, modedb_sz, NULL, setting->default_bpp);
+ if (ret != 1)
+ fb_videomode_to_var(&setting->fbi->var, &modedb[0]);
+
+ ret = request_irq(tve->irq, tve_detect_handler, 0, tve->pdev->name, tve);
+ if (ret < 0)
+ goto req_irq_failed;
+
+ /* Setup cable detect, for YPrPb mode, default use channel#-1 for Y */
+ INIT_DELAYED_WORK(&tve->cd_work, cd_work_func);
+ if (tve->revision == 1)
+ writel(0x01067701, tve->base + tve->regs->tve_cd_cont_reg);
+ else
+ writel(0x00770601, tve->base + tve->regs->tve_cd_cont_reg);
+
+ conf_reg = 0;
+ writel(conf_reg, tve->base + tve->regs->tve_com_conf_reg);
+
+ writel(0x00000000, tve->base + tve->regs->tve_mv_cont_reg - 4 * 5);
+ writel(0x00000000, tve->base + tve->regs->tve_mv_cont_reg - 4 * 4);
+ writel(0x00000000, tve->base + tve->regs->tve_mv_cont_reg - 4 * 3);
+ writel(0x00000000, tve->base + tve->regs->tve_mv_cont_reg - 4 * 2);
+ writel(0x00000000, tve->base + tve->regs->tve_mv_cont_reg - 4);
+ writel(0x00000000, tve->base + tve->regs->tve_mv_cont_reg);
+
+ clk_disable(tve->clk);
+
+ tve->nb.notifier_call = tve_fb_event;
+ ret = fb_register_client(&tve->nb);
+ if (ret < 0)
+ goto reg_fbclient_failed;
+
+ dev_set_drvdata(&tve->pdev->dev, tve);
+
+ spin_lock_init(&tve->tve_lock);
+
+ tve->inited = true;
+
+ return 0;
+
+reg_fbclient_failed:
+ free_irq(tve->irq, tve->pdev);
+req_irq_failed:
+get_diclk_failed:
+get_tveclk_failed:
+ device_remove_file(&tve->pdev->dev, &dev_attr_headphone);
+dev_file_create_failed:
+ iounmap(tve->base);
+ioremap_failed:
+get_irq_failed:
+get_res_failed:
+ return ret;
+
+}
+
+static int tvout_init(struct mxc_dispdrv_handle *disp,
+ struct mxc_dispdrv_setting *setting)
+{
+ return tve_drv_init(disp, 0, setting);
+}
+
+static int vga_init(struct mxc_dispdrv_handle *disp,
+ struct mxc_dispdrv_setting *setting)
+{
+ return tve_drv_init(disp, 1, setting);
+}
+
+void tvout_deinit(struct mxc_dispdrv_handle *disp)
+{
+ struct tve_data *tve = mxc_dispdrv_getdata(disp);
+
+ if (tve->enabled)
+ clk_disable(tve->clk);
+
+ fb_unregister_client(&tve->nb);
+ free_irq(tve->irq, tve->pdev);
+ device_remove_file(&tve->pdev->dev, &dev_attr_headphone);
+ iounmap(tve->base);
+}
+
+static struct mxc_dispdrv_driver tve_drv = {
+ .name = DISPDRV_TVE,
+ .init = tvout_init,
+ .deinit = tvout_deinit,
+ .setup = tve_disp_setup,
+};
+
+static struct mxc_dispdrv_driver vga_drv = {
+ .name = DISPDRV_VGA,
+ .init = vga_init,
+ .deinit = tvout_deinit,
+};
+
+static int tve_dispdrv_init(struct tve_data *tve)
+{
+ tve->disp_tve = mxc_dispdrv_register(&tve_drv);
+ mxc_dispdrv_setdata(tve->disp_tve, tve);
+ tve->disp_vga = mxc_dispdrv_register(&vga_drv);
+ mxc_dispdrv_setdata(tve->disp_vga, tve);
+ return 0;
+}
+
+static void tve_dispdrv_deinit(struct tve_data *tve)
+{
+ mxc_dispdrv_puthandle(tve->disp_tve);
+ mxc_dispdrv_puthandle(tve->disp_vga);
+ mxc_dispdrv_unregister(tve->disp_tve);
+ mxc_dispdrv_unregister(tve->disp_vga);
+}
+
+static int tve_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct tve_data *tve;
+
+ tve = kzalloc(sizeof(struct tve_data), GFP_KERNEL);
+ if (!tve) {
+ ret = -ENOMEM;
+ goto alloc_failed;
+ }
+
+ tve->pdev = pdev;
+ ret = tve_dispdrv_init(tve);
+ if (ret < 0)
+ goto dispdrv_init_failed;
+
+ dev_set_drvdata(&pdev->dev, tve);
+
+ return 0;
+
+dispdrv_init_failed:
+ kfree(tve);
+alloc_failed:
+ return ret;
+}
+
+static int tve_remove(struct platform_device *pdev)
+{
+ struct tve_data *tve = dev_get_drvdata(&pdev->dev);
+
+ tve_dispdrv_deinit(tve);
+ kfree(tve);
+ return 0;
+}
+
+static struct platform_driver tve_driver = {
+ .driver = {
+ .name = "mxc_tve",
+ },
+ .probe = tve_probe,
+ .remove = tve_remove,
+};
+
+static int __init tve_init(void)
+{
+ return platform_driver_register(&tve_driver);
+}
+
+static void __exit tve_exit(void)
+{
+ platform_driver_unregister(&tve_driver);
+}
+
+module_init(tve_init);
+module_exit(tve_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("i.MX TV encoder driver");
+MODULE_LICENSE("GPL");