]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
ENGR00141217-5 IPU\VPU\GPU: upgrade to 2.6.38
authorJason Chen <b02280@freescale.com>
Wed, 23 Feb 2011 09:31:54 +0000 (17:31 +0800)
committerLothar Waßmann <LW@KARO-electronics.de>
Fri, 24 May 2013 06:32:31 +0000 (08:32 +0200)
Add drivers/mxc
Add drivers/video/mxc
Add drivers/media/mxc
fb device: change acquire_console_sem to console_lock
And release_console_sem to console_unlock
Add DMA Zone support
Add TVE driver, add regulator
Add hdmi support
Add VPU

Fix build error
ioctl --> unlocked_ioctl
DECLARE_MUTEX --> DEFINE_SEMAPHORE

Signed-off-by: Jason Chen <b02280@freescale.com>
Signed-off-by: Richard Zhao <richard.zhao@freescale.com>
172 files changed:
drivers/Kconfig
drivers/Makefile
drivers/media/video/Kconfig
drivers/media/video/Makefile
drivers/media/video/mxc/capture/Kconfig [new file with mode: 0644]
drivers/media/video/mxc/capture/Makefile [new file with mode: 0644]
drivers/media/video/mxc/capture/adv7180.c [new file with mode: 0644]
drivers/media/video/mxc/capture/csi_v4l2_capture.c [new file with mode: 0644]
drivers/media/video/mxc/capture/fsl_csi.c [new file with mode: 0644]
drivers/media/video/mxc/capture/fsl_csi.h [new file with mode: 0644]
drivers/media/video/mxc/capture/ipu_csi_enc.c [new file with mode: 0644]
drivers/media/video/mxc/capture/ipu_prp_enc.c [new file with mode: 0644]
drivers/media/video/mxc/capture/ipu_prp_sw.h [new file with mode: 0644]
drivers/media/video/mxc/capture/ipu_prp_vf_sdc.c [new file with mode: 0644]
drivers/media/video/mxc/capture/ipu_prp_vf_sdc_bg.c [new file with mode: 0644]
drivers/media/video/mxc/capture/ipu_still.c [new file with mode: 0644]
drivers/media/video/mxc/capture/mt9v111.c [new file with mode: 0644]
drivers/media/video/mxc/capture/mt9v111.h [new file with mode: 0644]
drivers/media/video/mxc/capture/mxc_v4l2_capture.c [new file with mode: 0644]
drivers/media/video/mxc/capture/mxc_v4l2_capture.h [new file with mode: 0644]
drivers/media/video/mxc/capture/ov2640.c [new file with mode: 0644]
drivers/media/video/mxc/capture/ov3640.c [new file with mode: 0644]
drivers/media/video/mxc/capture/ov5640.c [new file with mode: 0644]
drivers/media/video/mxc/capture/ov5642.c [new file with mode: 0644]
drivers/media/video/mxc/capture/sensor_clock.c [new file with mode: 0644]
drivers/media/video/mxc/opl/Makefile [new file with mode: 0644]
drivers/media/video/mxc/opl/hmirror_rotate180_u16.c [new file with mode: 0644]
drivers/media/video/mxc/opl/opl.h [new file with mode: 0644]
drivers/media/video/mxc/opl/opl_mod.c [new file with mode: 0644]
drivers/media/video/mxc/opl/rotate270_u16.c [new file with mode: 0644]
drivers/media/video/mxc/opl/rotate270_u16_qcif.S [new file with mode: 0644]
drivers/media/video/mxc/opl/rotate90_u16.c [new file with mode: 0644]
drivers/media/video/mxc/opl/rotate90_u16_qcif.S [new file with mode: 0644]
drivers/media/video/mxc/opl/vmirror_u16.c [new file with mode: 0644]
drivers/media/video/mxc/output/Kconfig [new file with mode: 0644]
drivers/media/video/mxc/output/Makefile [new file with mode: 0644]
drivers/media/video/mxc/output/mx31_v4l2_wvga_output.c [new file with mode: 0644]
drivers/media/video/mxc/output/mxc_pxp_v4l2.c [new file with mode: 0644]
drivers/media/video/mxc/output/mxc_pxp_v4l2.h [new file with mode: 0644]
drivers/media/video/mxc/output/mxc_v4l2_output.c [new file with mode: 0644]
drivers/media/video/mxc/output/mxc_v4l2_output.h [new file with mode: 0644]
drivers/mxc/Kconfig [new file with mode: 0644]
drivers/mxc/Makefile [new file with mode: 0644]
drivers/mxc/adc/Kconfig [new file with mode: 0644]
drivers/mxc/adc/Makefile [new file with mode: 0644]
drivers/mxc/adc/imx_adc.c [new file with mode: 0644]
drivers/mxc/adc/imx_adc_reg.h [new file with mode: 0644]
drivers/mxc/asrc/Kconfig [new file with mode: 0644]
drivers/mxc/asrc/Makefile [new file with mode: 0644]
drivers/mxc/asrc/mxc_asrc.c [new file with mode: 0644]
drivers/mxc/bt/Kconfig [new file with mode: 0644]
drivers/mxc/bt/Makefile [new file with mode: 0644]
drivers/mxc/bt/mxc_bt.c [new file with mode: 0644]
drivers/mxc/dam/Kconfig [new file with mode: 0644]
drivers/mxc/dam/Makefile [new file with mode: 0644]
drivers/mxc/dam/dam.c [new file with mode: 0644]
drivers/mxc/dam/dam.h [new file with mode: 0644]
drivers/mxc/dam/dam_v1.c [new file with mode: 0644]
drivers/mxc/gps_ioctrl/Kconfig [new file with mode: 0644]
drivers/mxc/gps_ioctrl/Makefile [new file with mode: 0644]
drivers/mxc/gps_ioctrl/agpsgpiodev.c [new file with mode: 0644]
drivers/mxc/gps_ioctrl/agpsgpiodev.h [new file with mode: 0644]
drivers/mxc/hmp4e/Kconfig [new file with mode: 0644]
drivers/mxc/hmp4e/Makefile [new file with mode: 0644]
drivers/mxc/hmp4e/mxc_hmp4e.c [new file with mode: 0644]
drivers/mxc/hmp4e/mxc_hmp4e.h [new file with mode: 0644]
drivers/mxc/hw_event/Kconfig [new file with mode: 0644]
drivers/mxc/hw_event/Makefile [new file with mode: 0644]
drivers/mxc/hw_event/mxc_hw_event.c [new file with mode: 0644]
drivers/mxc/ipu/Kconfig [new file with mode: 0644]
drivers/mxc/ipu/Makefile [new file with mode: 0644]
drivers/mxc/ipu/ipu_adc.c [new file with mode: 0644]
drivers/mxc/ipu/ipu_calc_stripes_sizes.c [new file with mode: 0644]
drivers/mxc/ipu/ipu_common.c [new file with mode: 0644]
drivers/mxc/ipu/ipu_csi.c [new file with mode: 0644]
drivers/mxc/ipu/ipu_device.c [new file with mode: 0644]
drivers/mxc/ipu/ipu_ic.c [new file with mode: 0644]
drivers/mxc/ipu/ipu_param_mem.h [new file with mode: 0644]
drivers/mxc/ipu/ipu_prv.h [new file with mode: 0644]
drivers/mxc/ipu/ipu_regs.h [new file with mode: 0644]
drivers/mxc/ipu/ipu_sdc.c [new file with mode: 0644]
drivers/mxc/ipu/pf/Kconfig [new file with mode: 0644]
drivers/mxc/ipu/pf/Makefile [new file with mode: 0644]
drivers/mxc/ipu/pf/mxc_pf.c [new file with mode: 0644]
drivers/mxc/ipu3/Kconfig [new file with mode: 0644]
drivers/mxc/ipu3/Makefile [new file with mode: 0644]
drivers/mxc/ipu3/ipu_calc_stripes_sizes.c [new file with mode: 0644]
drivers/mxc/ipu3/ipu_capture.c [new file with mode: 0644]
drivers/mxc/ipu3/ipu_common.c [new file with mode: 0644]
drivers/mxc/ipu3/ipu_device.c [new file with mode: 0644]
drivers/mxc/ipu3/ipu_disp.c [new file with mode: 0644]
drivers/mxc/ipu3/ipu_ic.c [new file with mode: 0644]
drivers/mxc/ipu3/ipu_param_mem.h [new file with mode: 0644]
drivers/mxc/ipu3/ipu_prv.h [new file with mode: 0644]
drivers/mxc/ipu3/ipu_regs.h [new file with mode: 0644]
drivers/mxc/mcu_pmic/Kconfig [new file with mode: 0644]
drivers/mxc/mcu_pmic/Makefile [new file with mode: 0644]
drivers/mxc/mcu_pmic/max8660.c [new file with mode: 0644]
drivers/mxc/mcu_pmic/max8660.h [new file with mode: 0644]
drivers/mxc/mcu_pmic/mc9s08dz60.c [new file with mode: 0644]
drivers/mxc/mcu_pmic/mc9s08dz60.h [new file with mode: 0644]
drivers/mxc/mcu_pmic/mcu_pmic_core.c [new file with mode: 0644]
drivers/mxc/mcu_pmic/mcu_pmic_core.h [new file with mode: 0644]
drivers/mxc/mcu_pmic/mcu_pmic_gpio.c [new file with mode: 0644]
drivers/mxc/mlb/Kconfig [new file with mode: 0644]
drivers/mxc/mlb/Makefile [new file with mode: 0644]
drivers/mxc/mlb/mxc_mlb.c [new file with mode: 0644]
drivers/mxc/pmic/Kconfig [new file with mode: 0644]
drivers/mxc/pmic/Makefile [new file with mode: 0644]
drivers/mxc/pmic/core/Makefile [new file with mode: 0644]
drivers/mxc/pmic/core/mc13783.c [new file with mode: 0644]
drivers/mxc/pmic/core/mc13892.c [new file with mode: 0644]
drivers/mxc/pmic/core/mc34704.c [new file with mode: 0644]
drivers/mxc/pmic/core/pmic-dev.c [new file with mode: 0644]
drivers/mxc/pmic/core/pmic.h [new file with mode: 0644]
drivers/mxc/pmic/core/pmic_common.c [new file with mode: 0644]
drivers/mxc/pmic/core/pmic_core_i2c.c [new file with mode: 0644]
drivers/mxc/pmic/core/pmic_core_spi.c [new file with mode: 0644]
drivers/mxc/pmic/core/pmic_event.c [new file with mode: 0644]
drivers/mxc/pmic/core/pmic_external.c [new file with mode: 0644]
drivers/mxc/pmic/mc13783/Kconfig [new file with mode: 0644]
drivers/mxc/pmic/mc13783/Makefile [new file with mode: 0644]
drivers/mxc/pmic/mc13783/pmic_adc.c [new file with mode: 0644]
drivers/mxc/pmic/mc13783/pmic_adc_defs.h [new file with mode: 0644]
drivers/mxc/pmic/mc13783/pmic_audio.c [new file with mode: 0644]
drivers/mxc/pmic/mc13783/pmic_battery.c [new file with mode: 0644]
drivers/mxc/pmic/mc13783/pmic_battery_defs.h [new file with mode: 0644]
drivers/mxc/pmic/mc13783/pmic_convity.c [new file with mode: 0644]
drivers/mxc/pmic/mc13783/pmic_light.c [new file with mode: 0644]
drivers/mxc/pmic/mc13783/pmic_light_defs.h [new file with mode: 0644]
drivers/mxc/pmic/mc13783/pmic_power.c [new file with mode: 0644]
drivers/mxc/pmic/mc13783/pmic_power_defs.h [new file with mode: 0644]
drivers/mxc/pmic/mc13783/pmic_rtc.c [new file with mode: 0644]
drivers/mxc/pmic/mc13783/pmic_rtc_defs.h [new file with mode: 0644]
drivers/mxc/pmic/mc13892/Kconfig [new file with mode: 0644]
drivers/mxc/pmic/mc13892/Makefile [new file with mode: 0644]
drivers/mxc/pmic/mc13892/pmic_adc.c [new file with mode: 0644]
drivers/mxc/pmic/mc13892/pmic_battery.c [new file with mode: 0644]
drivers/mxc/pmic/mc13892/pmic_light.c [new file with mode: 0644]
drivers/mxc/ssi/Kconfig [new file with mode: 0644]
drivers/mxc/ssi/Makefile [new file with mode: 0644]
drivers/mxc/ssi/registers.h [new file with mode: 0644]
drivers/mxc/ssi/ssi.c [new file with mode: 0644]
drivers/mxc/ssi/ssi.h [new file with mode: 0644]
drivers/mxc/ssi/ssi_types.h [new file with mode: 0644]
drivers/mxc/vpu/Kconfig [new file with mode: 0644]
drivers/mxc/vpu/Makefile [new file with mode: 0644]
drivers/mxc/vpu/mxc_vl2cc.c [new file with mode: 0644]
drivers/mxc/vpu/mxc_vpu.c [new file with mode: 0644]
drivers/video/Kconfig
drivers/video/Makefile
drivers/video/mxc/Kconfig [new file with mode: 0644]
drivers/video/mxc/Makefile [new file with mode: 0644]
drivers/video/mxc/ch7024.c [new file with mode: 0644]
drivers/video/mxc/elcdif_regs.h [new file with mode: 0644]
drivers/video/mxc/epdc_regs.h [new file with mode: 0644]
drivers/video/mxc/ldb.c [new file with mode: 0644]
drivers/video/mxc/mx2fb.c [new file with mode: 0644]
drivers/video/mxc/mx2fb.h [new file with mode: 0644]
drivers/video/mxc/mxc_edid.c [new file with mode: 0644]
drivers/video/mxc/mxc_elcdif_fb.c [new file with mode: 0644]
drivers/video/mxc/mxc_epdc_fb.c [new file with mode: 0644]
drivers/video/mxc/mxc_ipuv3_fb.c [new file with mode: 0644]
drivers/video/mxc/mxcfb.c [new file with mode: 0644]
drivers/video/mxc/mxcfb_ch7026.c [new file with mode: 0644]
drivers/video/mxc/mxcfb_claa_wvga.c [new file with mode: 0644]
drivers/video/mxc/mxcfb_epson.c [new file with mode: 0644]
drivers/video/mxc/mxcfb_epson_vga.c [new file with mode: 0644]
drivers/video/mxc/mxcfb_modedb.c [new file with mode: 0644]
drivers/video/mxc/mxcfb_seiko_wvga.c [new file with mode: 0644]
drivers/video/mxc/mxcfb_sii902x.c [new file with mode: 0644]
drivers/video/mxc/tve.c [new file with mode: 0644]

index 3bb154d8c8cc778713c28e0d335282cb715594a2..65f7142817a60410c916b0e1adfd292c41f0b237 100644 (file)
@@ -126,4 +126,6 @@ source "drivers/hwspinlock/Kconfig"
 
 source "drivers/clocksource/Kconfig"
 
+source "drivers/mxc/Kconfig"
+
 endmenu
index 09f3232bcdcddd2ff2ce042cd0a7a977320356c4..3c66d7069ec77035f9ddf03ea864e92d4b9be1b1 100644 (file)
@@ -96,6 +96,7 @@ obj-y                         += lguest/
 obj-$(CONFIG_CPU_FREQ)         += cpufreq/
 obj-$(CONFIG_CPU_IDLE)         += cpuidle/
 obj-$(CONFIG_MMC)              += mmc/
+obj-$(CONFIG_ARCH_MXC)          += mxc/
 obj-$(CONFIG_MEMSTICK)         += memstick/
 obj-y                          += leds/
 obj-$(CONFIG_INFINIBAND)       += infiniband/
index bb53de7fe4087b227f98b63cdc15b035d28f008c..0597976f5e53eeef92a4f74d6bd4989324df6965 100644 (file)
@@ -618,6 +618,56 @@ config VIDEO_W9966
          Check out <file:Documentation/video4linux/w9966.txt> for more
          information.
 
+config VIDEO_MXC_CAMERA
+        tristate "MXC Video For Linux Camera"
+        depends on VIDEO_DEV && ARCH_MXC
+        default y
+        ---help---
+          This is the video4linux2 capture driver based on MXC IPU/eMMA module.
+
+source "drivers/media/video/mxc/capture/Kconfig"
+
+config VIDEO_MXC_OUTPUT
+        tristate "MXC Video For Linux Video Output"
+        depends on VIDEO_DEV && ARCH_MXC
+        default y
+        ---help---
+          This is the video4linux2 output driver based on MXC IPU/eMMA module.
+
+source "drivers/media/video/mxc/output/Kconfig"
+
+config VIDEO_MXS_PXP
+        tristate "MXS PxP"
+        depends on VIDEO_DEV && VIDEO_V4L2 && ARCH_MXS
+        select VIDEOBUF_DMA_CONTIG
+        ---help---
+          This is a video4linux driver for the Freescale PxP
+          (Pixel Pipeline). This module supports output overlay of
+          the MXS framebuffer on a video stream.
+
+          To compile this driver as a module, choose M here: the
+          module will be called pxp.
+
+config VIDEO_MXC_PXP_V4L2
+        tristate "MXC PxP V4L2 driver"
+        depends on VIDEO_DEV && VIDEO_V4L2 && ARCH_MX5
+        select VIDEOBUF_DMA_CONTIG
+        ---help---
+          This is a video4linux driver for the Freescale PxP
+          (Pixel Pipeline). This module supports output overlay of
+          the MXC framebuffer on a video stream.
+
+          To compile this driver as a module, choose M here.
+
+config VIDEO_MXC_OPL
+        tristate
+        depends on VIDEO_DEV && ARCH_MXC
+        default n
+        ---help---
+          This is the ARM9-optimized OPL (Open Primitives Library) software
+          rotation/mirroring implementation. It may be used by eMMA video
+          capture or output device.
+
 source "drivers/media/video/cpia2/Kconfig"
 
 config VIDEO_VINO
index f0fecd6f6a33e818c4f4713d2b62e1ecd762e4a9..e268ad4dfedde270f717caa88cfcf4da979701db 100644 (file)
@@ -87,6 +87,15 @@ obj-$(CONFIG_SOC_CAMERA_TW9910)              += tw9910.o
 
 # And now the v4l2 drivers:
 
+obj-$(CONFIG_VIDEO_MXC_IPU_CAMERA) += mxc/capture/
+obj-$(CONFIG_VIDEO_MXC_CSI_CAMERA) += mxc/capture/
+obj-$(CONFIG_VIDEO_MXC_IPU_OUTPUT) += mxc/output/
+obj-$(CONFIG_VIDEO_MXC_IPUV1_WVGA_OUTPUT) += mxc/output/
+obj-$(CONFIG_VIDEO_MXC_EMMA_OUTPUT) += mxc/output/
+obj-$(CONFIG_VIDEO_MXC_PXP_V4L2) += mxc/output/
+obj-$(CONFIG_VIDEO_MXC_OPL) += mxc/opl/
+obj-$(CONFIG_VIDEO_PXP) += pxp.o
+obj-$(CONFIG_VIDEO_MXS_PXP)     += mxs_pxp.o
 obj-$(CONFIG_VIDEO_BT848) += bt8xx/
 obj-$(CONFIG_VIDEO_ZORAN) += zoran/
 obj-$(CONFIG_VIDEO_CQCAM) += c-qcam.o
diff --git a/drivers/media/video/mxc/capture/Kconfig b/drivers/media/video/mxc/capture/Kconfig
new file mode 100644 (file)
index 0000000..2e3cb56
--- /dev/null
@@ -0,0 +1,125 @@
+if VIDEO_MXC_CAMERA
+
+menu "MXC Camera/V4L2 PRP Features support"
+config VIDEO_MXC_IPU_CAMERA
+       bool
+       depends on VIDEO_MXC_CAMERA && MXC_IPU
+       default y
+
+config VIDEO_MXC_EMMA_CAMERA
+       tristate "MX27 eMMA support"
+       depends on VIDEO_MXC_CAMERA && MXC_EMMA && FB_MXC_SYNC_PANEL
+       select VIDEO_MXC_OPL
+       default y
+
+config VIDEO_MXC_CSI_CAMERA
+       tristate "MX25 CSI camera support"
+       depends on !VIDEO_MXC_EMMA_CAMERA
+
+config VIDEO_MXC_CSI_DMA
+       bool "CSI-DMA Still Image Capture support"
+       depends on VIDEO_MXC_EMMA_CAMERA
+       default n
+       ---help---
+         Use CSI-DMA method instead of CSI-PrP link to capture still image. This allows
+         to use less physical contiguous memory to capture big resolution still image. But
+         with this method the CSC (Color Space Conversion) and resize are not supported.
+         If unsure, say N.
+
+choice
+       prompt "Select Camera/TV Decoder"
+       default MXC_CAMERA_OV3640
+       depends on VIDEO_MXC_CAMERA
+
+config MXC_CAMERA_MC521DA
+       tristate "Magnachip mc521da camera support"
+       select I2C_MXC
+       depends on VIDEO_MXC_EMMA_CAMERA
+       ---help---
+         If you plan to use the mc521da Camera with your MXC system, say Y here.
+
+config MXC_EMMA_CAMERA_MICRON111
+       tristate "Micron mt9v111 camera support with eMMA"
+       select I2C_MXC
+       depends on VIDEO_MXC_EMMA_CAMERA
+       ---help---
+         If you plan to use the mt9v111 Camera with your MXC system, say Y here.
+
+config MXC_CAMERA_OV2640_EMMA
+       tristate "OmniVision ov2640 camera support with eMMA"
+       depends on VIDEO_MXC_EMMA_CAMERA
+       ---help---
+         If you plan to use the ov2640 Camera with your MXC system, say Y here.
+
+config MXC_CAMERA_MICRON111
+       tristate "Micron mt9v111 camera support"
+       select I2C_MXC
+       depends on ! VIDEO_MXC_EMMA_CAMERA
+       ---help---
+         If you plan to use the mt9v111 Camera with your MXC system, say Y here.
+
+config MXC_CAMERA_OV2640
+       tristate "OmniVision ov2640 camera support"
+       depends on !VIDEO_MXC_EMMA_CAMERA
+       ---help---
+         If you plan to use the ov2640 Camera with your MXC system, say Y here.
+
+config MXC_CAMERA_OV3640
+       tristate "OmniVision ov3640 camera support"
+       depends on !VIDEO_MXC_EMMA_CAMERA
+       ---help---
+         If you plan to use the ov3640 Camera with your MXC system, say Y here.
+
+config MXC_CAMERA_OV5640
+       tristate "OmniVision ov5640 camera support"
+       depends on !VIDEO_MXC_EMMA_CAMERA
+       ---help---
+         If you plan to use the ov5640 Camera with your MXC system, say Y here.
+
+config MXC_CAMERA_OV5642
+       tristate "OmniVision ov5642 camera support"
+       depends on !VIDEO_MXC_EMMA_CAMERA
+       ---help---
+         If you plan to use the ov5642 Camera with your MXC system, say Y here.
+
+config MXC_TVIN_ADV7180
+       tristate "Analog Device adv7180 TV Decoder Input support"
+       depends on (MACH_MX35_3DS || MACH_MX51_3DS)
+       ---help---
+         If you plan to use the adv7180 video decoder with your MXC system, say Y here.
+
+endchoice
+
+config MXC_IPU_PRP_VF_SDC
+       tristate "Pre-Processor VF SDC library"
+       depends on VIDEO_MXC_IPU_CAMERA && FB_MXC_SYNC_PANEL
+       default y
+       ---help---
+         Use case PRP_VF_SDC:
+               Preprocessing image from smart sensor for viewfinder and
+               displaying it on synchronous display with SDC use case.
+               If SDC BG is selected, Rotation will not be supported.
+               CSI -> IC (PRP VF) -> MEM
+               MEM -> IC (ROT) -> MEM
+               MEM -> SDC (FG/BG)
+
+config MXC_IPU_PRP_ENC
+       tristate "Pre-processor Encoder library"
+       depends on VIDEO_MXC_IPU_CAMERA
+       default y
+       ---help---
+         Use case PRP_ENC:
+               Preprocessing image from smart sensor for encoder.
+               CSI -> IC (PRP ENC) -> MEM
+
+config MXC_IPU_CSI_ENC
+       tristate "IPU CSI Encoder library"
+       depends on VIDEO_MXC_IPU_CAMERA
+       default y
+       ---help---
+         Use case IPU_CSI_ENC:
+               Get raw image with CSI from smart sensor for encoder.
+               CSI -> MEM
+endmenu
+
+endif
diff --git a/drivers/media/video/mxc/capture/Makefile b/drivers/media/video/mxc/capture/Makefile
new file mode 100644 (file)
index 0000000..fda685d
--- /dev/null
@@ -0,0 +1,44 @@
+ifeq ($(CONFIG_VIDEO_MXC_IPU_CAMERA),y)
+       obj-$(CONFIG_VIDEO_MXC_CAMERA) += mxc_v4l2_capture.o
+       obj-$(CONFIG_MXC_IPU_PRP_VF_SDC) += ipu_prp_vf_sdc.o ipu_prp_vf_sdc_bg.o
+       obj-$(CONFIG_MXC_IPU_PRP_ENC) += ipu_prp_enc.o ipu_still.o
+       obj-$(CONFIG_MXC_IPU_CSI_ENC) += ipu_csi_enc.o ipu_still.o
+endif
+
+obj-$(CONFIG_VIDEO_MXC_CSI_CAMERA) += fsl_csi.o csi_v4l2_capture.o
+
+mx27_capture-objs := mx27_prphw.o mx27_prpsw.o emma_v4l2_capture.o
+obj-$(CONFIG_VIDEO_MXC_EMMA_CAMERA) += mx27_csi.o mx27_capture.o
+
+mc521da_camera-objs := mc521da.o sensor_clock.o
+obj-$(CONFIG_MXC_CAMERA_MC521DA) += mc521da_camera.o
+
+emma_mt9v111_camera-objs := emma_mt9v111.o sensor_clock.o
+obj-$(CONFIG_MXC_EMMA_CAMERA_MICRON111) += emma_mt9v111_camera.o
+
+mt9v111_camera-objs := mt9v111.o sensor_clock.o
+obj-$(CONFIG_MXC_CAMERA_MICRON111) += mt9v111_camera.o
+
+hv7161_camera-objs := hv7161.o sensor_clock.o
+obj-$(CONFIG_MXC_CAMERA_HV7161) += hv7161_camera.o
+
+s5k3aaex_camera-objs := s5k3aaex.o sensor_clock.o
+obj-$(CONFIG_MXC_CAMERA_S5K3AAEX) += s5k3aaex_camera.o
+
+emma_ov2640_camera-objs := emma_ov2640.o sensor_clock.o
+obj-$(CONFIG_MXC_CAMERA_OV2640_EMMA) += emma_ov2640_camera.o
+
+ov2640_camera-objs := ov2640.o sensor_clock.o
+obj-$(CONFIG_MXC_CAMERA_OV2640) += ov2640_camera.o
+
+ov3640_camera-objs := ov3640.o sensor_clock.o
+obj-$(CONFIG_MXC_CAMERA_OV3640) += ov3640_camera.o
+
+ov5640_camera-objs := ov5640.o sensor_clock.o
+obj-$(CONFIG_MXC_CAMERA_OV5640) += ov5640_camera.o
+
+ov5642_camera-objs := ov5642.o sensor_clock.o
+obj-$(CONFIG_MXC_CAMERA_OV5642) += ov5642_camera.o
+
+adv7180_tvin-objs := adv7180.o
+obj-$(CONFIG_MXC_TVIN_ADV7180) += adv7180_tvin.o
diff --git a/drivers/media/video/mxc/capture/adv7180.c b/drivers/media/video/mxc/capture/adv7180.c
new file mode 100644 (file)
index 0000000..3e811d4
--- /dev/null
@@ -0,0 +1,1024 @@
+/*
+ * Copyright 2005-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 adv7180.c
+ *
+ * @brief Analog Device ADV7180 video decoder functions
+ *
+ * @ingroup Camera
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/wait.h>
+#include <linux/videodev2.h>
+#include <linux/workqueue.h>
+#include <linux/regulator/consumer.h>
+#include <linux/fsl_devices.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-int-device.h>
+#include "mxc_v4l2_capture.h"
+
+static struct regulator *dvddio_regulator;
+static struct regulator *dvdd_regulator;
+static struct regulator *avdd_regulator;
+static struct regulator *pvdd_regulator;
+static struct mxc_tvin_platform_data *tvin_plat;
+
+extern void gpio_sensor_active(void);
+extern void gpio_sensor_inactive(void);
+
+static int adv7180_probe(struct i2c_client *adapter,
+                        const struct i2c_device_id *id);
+static int adv7180_detach(struct i2c_client *client);
+
+static const struct i2c_device_id adv7180_id[] = {
+       {"adv7180", 0},
+       {},
+};
+
+MODULE_DEVICE_TABLE(i2c, adv7180_id);
+
+static struct i2c_driver adv7180_i2c_driver = {
+       .driver = {
+                  .owner = THIS_MODULE,
+                  .name = "adv7180",
+                  },
+       .probe = adv7180_probe,
+       .remove = adv7180_detach,
+       .id_table = adv7180_id,
+};
+
+/*!
+ * Maintains the information on the current state of the sesor.
+ */
+struct sensor {
+       struct v4l2_int_device *v4l2_int_device;
+       struct i2c_client *i2c_client;
+       struct v4l2_pix_format pix;
+       struct v4l2_captureparm streamcap;
+       bool on;
+
+       /* control settings */
+       int brightness;
+       int hue;
+       int contrast;
+       int saturation;
+       int red;
+       int green;
+       int blue;
+       int ae_mode;
+
+       v4l2_std_id std_id;
+} adv7180_data;
+
+/*! List of input video formats supported. The video formats is corresponding
+ * with v4l2 id in video_fmt_t
+ */
+typedef enum {
+       ADV7180_NTSC = 0,       /*!< Locked on (M) NTSC video signal. */
+       ADV7180_PAL,            /*!< (B, G, H, I, N)PAL video signal. */
+       ADV7180_NOT_LOCKED,     /*!< Not locked on a signal. */
+} video_fmt_idx;
+
+/*! Number of video standards supported (including 'not locked' signal). */
+#define ADV7180_STD_MAX                (ADV7180_PAL + 1)
+
+/*! Video format structure. */
+typedef struct {
+       int v4l2_id;            /*!< Video for linux ID. */
+       char name[16];          /*!< Name (e.g., "NTSC", "PAL", etc.) */
+       u16 raw_width;          /*!< Raw width. */
+       u16 raw_height;         /*!< Raw height. */
+       u16 active_width;       /*!< Active width. */
+       u16 active_height;      /*!< Active height. */
+} video_fmt_t;
+
+/*! Description of video formats supported.
+ *
+ *  PAL: raw=720x625, active=720x576.
+ *  NTSC: raw=720x525, active=720x480.
+ */
+static video_fmt_t video_fmts[] = {
+       {                       /*! NTSC */
+        .v4l2_id = V4L2_STD_NTSC,
+        .name = "NTSC",
+        .raw_width = 720,      /* SENS_FRM_WIDTH */
+        .raw_height = 525,     /* SENS_FRM_HEIGHT */
+        .active_width = 720,   /* ACT_FRM_WIDTH plus 1 */
+        .active_height = 480,  /* ACT_FRM_WIDTH plus 1 */
+        },
+       {                       /*! (B, G, H, I, N) PAL */
+        .v4l2_id = V4L2_STD_PAL,
+        .name = "PAL",
+        .raw_width = 720,
+        .raw_height = 625,
+        .active_width = 720,
+        .active_height = 576,
+        },
+       {                       /*! Unlocked standard */
+        .v4l2_id = V4L2_STD_ALL,
+        .name = "Autodetect",
+        .raw_width = 720,
+        .raw_height = 625,
+        .active_width = 720,
+        .active_height = 576,
+        },
+};
+
+/*!* Standard index of ADV7180. */
+static video_fmt_idx video_idx = ADV7180_PAL;
+
+/*! @brief This mutex is used to provide mutual exclusion.
+ *
+ *  Create a mutex that can be used to provide mutually exclusive
+ *  read/write access to the globally accessible data structures
+ *  and variables that were defined above.
+ */
+static DECLARE_MUTEX(mutex);
+
+#define IF_NAME                    "adv7180"
+#define ADV7180_INPUT_CTL              0x00    /* Input Control */
+#define ADV7180_STATUS_1               0x10    /* Status #1 */
+#define ADV7180_BRIGHTNESS             0x0a    /* Brightness */
+#define ADV7180_IDENT                  0x11    /* IDENT */
+#define ADV7180_VSYNC_FIELD_CTL_1      0x31    /* VSYNC Field Control #1 */
+#define ADV7180_MANUAL_WIN_CTL         0x3d    /* Manual Window Control */
+#define ADV7180_SD_SATURATION_CB       0xe3    /* SD Saturation Cb */
+#define ADV7180_SD_SATURATION_CR       0xe4    /* SD Saturation Cr */
+#define ADV7180_PWR_MNG                0x0f     /* Power Management */
+
+/* supported controls */
+/* This hasn't been fully implemented yet.
+ * This is how it should work, though. */
+static struct v4l2_queryctrl adv7180_qctrl[] = {
+       {
+       .id = V4L2_CID_BRIGHTNESS,
+       .type = V4L2_CTRL_TYPE_INTEGER,
+       .name = "Brightness",
+       .minimum = 0,           /* check this value */
+       .maximum = 255,         /* check this value */
+       .step = 1,              /* check this value */
+       .default_value = 127,   /* check this value */
+       .flags = 0,
+       }, {
+       .id = V4L2_CID_SATURATION,
+       .type = V4L2_CTRL_TYPE_INTEGER,
+       .name = "Saturation",
+       .minimum = 0,           /* check this value */
+       .maximum = 255,         /* check this value */
+       .step = 0x1,            /* check this value */
+       .default_value = 127,   /* check this value */
+       .flags = 0,
+       }
+};
+
+/***********************************************************************
+ * I2C transfert.
+ ***********************************************************************/
+
+/*! Read one register from a ADV7180 i2c slave device.
+ *
+ *  @param *reg                register in the device we wish to access.
+ *
+ *  @return                   0 if success, an error code otherwise.
+ */
+static inline int adv7180_read(u8 reg)
+{
+       int val;
+       val = i2c_smbus_read_byte_data(adv7180_data.i2c_client, reg);
+       if (val < 0) {
+               dev_dbg(&adv7180_data.i2c_client->dev,
+                       "%s:read reg error: reg=%2x \n", __func__, reg);
+               return -1;
+       }
+       return val;
+}
+
+/*! Write one register of a ADV7180 i2c slave device.
+ *
+ *  @param *reg                register in the device we wish to access.
+ *
+ *  @return                   0 if success, an error code otherwise.
+ */
+static int adv7180_write_reg(u8 reg, u8 val)
+{
+       if (i2c_smbus_write_byte_data(adv7180_data.i2c_client, reg, val) < 0) {
+               dev_dbg(&adv7180_data.i2c_client->dev,
+                       "%s:write reg error:reg=%2x,val=%2x\n", __func__,
+                       reg, val);
+               return -1;
+       }
+       return 0;
+}
+
+/***********************************************************************
+ * mxc_v4l2_capture interface.
+ ***********************************************************************/
+
+/*!
+ * Return attributes of current video standard.
+ * Since this device autodetects the current standard, this function also
+ * sets the values that need to be changed if the standard changes.
+ * There is no set std equivalent function.
+ *
+ *  @return            None.
+ */
+static void adv7180_get_std(v4l2_std_id *std)
+{
+       int tmp;
+       int idx;
+
+       dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180_get_std\n");
+
+       /* Make sure power on */
+       if (tvin_plat->pwdn)
+               tvin_plat->pwdn(0);
+
+       /* Read the AD_RESULT to get the detect output video standard */
+       tmp = adv7180_read(ADV7180_STATUS_1) & 0x70;
+
+       down(&mutex);
+       if (tmp == 0x40) {
+               /* PAL */
+               *std = V4L2_STD_PAL;
+               idx = ADV7180_PAL;
+       } else if (tmp == 0) {
+               /*NTSC*/
+               *std = V4L2_STD_NTSC;
+               idx = ADV7180_NTSC;
+       } else {
+               *std = V4L2_STD_ALL;
+               idx = ADV7180_NOT_LOCKED;
+               dev_dbg(&adv7180_data.i2c_client->dev,
+                       "Got invalid video standard! \n");
+       }
+       up(&mutex);
+
+       /* This assumes autodetect which this device uses. */
+       if (*std != adv7180_data.std_id) {
+               video_idx = idx;
+               adv7180_data.std_id = *std;
+               adv7180_data.pix.width = video_fmts[video_idx].raw_width;
+               adv7180_data.pix.height = video_fmts[video_idx].raw_height;
+       }
+}
+
+/***********************************************************************
+ * IOCTL Functions from v4l2_int_ioctl_desc.
+ ***********************************************************************/
+
+/*!
+ * ioctl_g_ifparm - V4L2 sensor interface handler for vidioc_int_g_ifparm_num
+ * s: pointer to standard V4L2 device structure
+ * p: pointer to standard V4L2 vidioc_int_g_ifparm_num ioctl structure
+ *
+ * Gets slave interface parameters.
+ * Calculates the required xclk value to support the requested
+ * clock parameters in p.  This value is returned in the p
+ * parameter.
+ *
+ * vidioc_int_g_ifparm returns platform-specific information about the
+ * interface settings used by the sensor.
+ *
+ * Called on open.
+ */
+static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p)
+{
+       dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180:ioctl_g_ifparm\n");
+
+       if (s == NULL) {
+               pr_err("   ERROR!! no slave device set!\n");
+               return -1;
+       }
+
+       /* Initialize structure to 0s then set any non-0 values. */
+       memset(p, 0, sizeof(*p));
+       p->if_type = V4L2_IF_TYPE_BT656; /* This is the only possibility. */
+       p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT;
+       p->u.bt656.nobt_hs_inv = 1;
+
+       /* ADV7180 has a dedicated clock so no clock settings needed. */
+
+       return 0;
+}
+
+/*!
+ * Sets the camera power.
+ *
+ * s  pointer to the camera device
+ * on if 1, power is to be turned on.  0 means power is to be turned off
+ *
+ * ioctl_s_power - V4L2 sensor interface handler for vidioc_int_s_power_num
+ * @s: pointer to standard V4L2 device structure
+ * @on: power state to which device is to be set
+ *
+ * Sets devices power state to requrested state, if possible.
+ * This is called on open, close, suspend and resume.
+ */
+static int ioctl_s_power(struct v4l2_int_device *s, int on)
+{
+       struct sensor *sensor = s->priv;
+
+       dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180:ioctl_s_power\n");
+
+       if (on && !sensor->on) {
+               gpio_sensor_active();
+
+               /* Make sure pwoer on */
+               if (tvin_plat->pwdn)
+                       tvin_plat->pwdn(0);
+
+               if (adv7180_write_reg(ADV7180_PWR_MNG, 0) != 0)
+                       return -EIO;
+       } else if (!on && sensor->on) {
+               if (adv7180_write_reg(ADV7180_PWR_MNG, 0x24) != 0)
+                       return -EIO;
+               gpio_sensor_inactive();
+       }
+
+       sensor->on = on;
+
+       return 0;
+}
+
+/*!
+ * ioctl_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure
+ *
+ * Returns the sensor's video CAPTURE parameters.
+ */
+static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+       struct sensor *sensor = s->priv;
+       struct v4l2_captureparm *cparm = &a->parm.capture;
+
+       dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180:ioctl_g_parm\n");
+
+       switch (a->type) {
+       /* These are all the possible cases. */
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               pr_debug("   type is V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
+               memset(a, 0, sizeof(*a));
+               a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+               cparm->capability = sensor->streamcap.capability;
+               cparm->timeperframe = sensor->streamcap.timeperframe;
+               cparm->capturemode = sensor->streamcap.capturemode;
+               break;
+
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+       case V4L2_BUF_TYPE_VBI_OUTPUT:
+       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+               break;
+
+       default:
+               pr_debug("ioctl_g_parm:type is unknown %d\n", a->type);
+               break;
+       }
+
+       return 0;
+}
+
+/*!
+ * ioctl_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure
+ *
+ * Configures the sensor to use the input parameters, if possible.  If
+ * not possible, reverts to the old parameters and returns the
+ * appropriate error code.
+ *
+ * This driver cannot change these settings.
+ */
+static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+       dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180:ioctl_s_parm\n");
+
+       switch (a->type) {
+       /* These are all the possible cases. */
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+       case V4L2_BUF_TYPE_VBI_OUTPUT:
+       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+               break;
+
+       default:
+               pr_debug("   type is unknown - %d\n", a->type);
+               break;
+       }
+
+       return 0;
+}
+
+/*!
+ * ioctl_g_fmt_cap - V4L2 sensor interface handler for ioctl_g_fmt_cap
+ * @s: pointer to standard V4L2 device structure
+ * @f: pointer to standard V4L2 v4l2_format structure
+ *
+ * Returns the sensor's current pixel format in the v4l2_format
+ * parameter.
+ */
+static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
+{
+       struct sensor *sensor = s->priv;
+
+       dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180:ioctl_g_fmt_cap\n");
+
+       switch (f->type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               pr_debug("   Returning size of %dx%d\n",
+                        sensor->pix.width, sensor->pix.height);
+               f->fmt.pix = sensor->pix;
+               break;
+
+       case V4L2_BUF_TYPE_PRIVATE: {
+               v4l2_std_id std;
+               adv7180_get_std(&std);
+               f->fmt.pix.pixelformat = (u32)std;
+               }
+               break;
+
+       default:
+               f->fmt.pix = sensor->pix;
+               break;
+       }
+
+       return 0;
+}
+
+/*!
+ * ioctl_queryctrl - V4L2 sensor interface handler for VIDIOC_QUERYCTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @qc: standard V4L2 VIDIOC_QUERYCTRL ioctl structure
+ *
+ * If the requested control is supported, returns the control information
+ * from the video_control[] array.  Otherwise, returns -EINVAL if the
+ * control is not supported.
+ */
+static int ioctl_queryctrl(struct v4l2_int_device *s,
+                          struct v4l2_queryctrl *qc)
+{
+       int i;
+
+       dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180:ioctl_queryctrl\n");
+
+       for (i = 0; i < ARRAY_SIZE(adv7180_qctrl); i++)
+               if (qc->id && qc->id == adv7180_qctrl[i].id) {
+                       memcpy(qc, &(adv7180_qctrl[i]),
+                               sizeof(*qc));
+                       return 0;
+               }
+
+       return -EINVAL;
+}
+
+/*!
+ * ioctl_g_ctrl - V4L2 sensor interface handler for VIDIOC_G_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @vc: standard V4L2 VIDIOC_G_CTRL ioctl structure
+ *
+ * If the requested control is supported, returns the control's current
+ * value from the video_control[] array.  Otherwise, returns -EINVAL
+ * if the control is not supported.
+ */
+static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc)
+{
+       int ret = 0;
+
+       dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180:ioctl_g_ctrl\n");
+
+       /* Make sure power on */
+       if (tvin_plat->pwdn)
+               tvin_plat->pwdn(0);
+
+       switch (vc->id) {
+       case V4L2_CID_BRIGHTNESS:
+               dev_dbg(&adv7180_data.i2c_client->dev,
+                       "   V4L2_CID_BRIGHTNESS\n");
+               adv7180_data.brightness = adv7180_read(ADV7180_BRIGHTNESS);
+               vc->value = adv7180_data.brightness;
+               break;
+       case V4L2_CID_CONTRAST:
+               dev_dbg(&adv7180_data.i2c_client->dev,
+                       "   V4L2_CID_CONTRAST\n");
+               vc->value = adv7180_data.contrast;
+               break;
+       case V4L2_CID_SATURATION:
+               dev_dbg(&adv7180_data.i2c_client->dev,
+                       "   V4L2_CID_SATURATION\n");
+               adv7180_data.saturation = adv7180_read(ADV7180_SD_SATURATION_CB);
+               vc->value = adv7180_data.saturation;
+               break;
+       case V4L2_CID_HUE:
+               dev_dbg(&adv7180_data.i2c_client->dev,
+                       "   V4L2_CID_HUE\n");
+               vc->value = adv7180_data.hue;
+               break;
+       case V4L2_CID_AUTO_WHITE_BALANCE:
+               dev_dbg(&adv7180_data.i2c_client->dev,
+                       "   V4L2_CID_AUTO_WHITE_BALANCE\n");
+               break;
+       case V4L2_CID_DO_WHITE_BALANCE:
+               dev_dbg(&adv7180_data.i2c_client->dev,
+                       "   V4L2_CID_DO_WHITE_BALANCE\n");
+               break;
+       case V4L2_CID_RED_BALANCE:
+               dev_dbg(&adv7180_data.i2c_client->dev,
+                       "   V4L2_CID_RED_BALANCE\n");
+               vc->value = adv7180_data.red;
+               break;
+       case V4L2_CID_BLUE_BALANCE:
+               dev_dbg(&adv7180_data.i2c_client->dev,
+                       "   V4L2_CID_BLUE_BALANCE\n");
+               vc->value = adv7180_data.blue;
+               break;
+       case V4L2_CID_GAMMA:
+               dev_dbg(&adv7180_data.i2c_client->dev,
+                       "   V4L2_CID_GAMMA\n");
+               break;
+       case V4L2_CID_EXPOSURE:
+               dev_dbg(&adv7180_data.i2c_client->dev,
+                       "   V4L2_CID_EXPOSURE\n");
+               vc->value = adv7180_data.ae_mode;
+               break;
+       case V4L2_CID_AUTOGAIN:
+               dev_dbg(&adv7180_data.i2c_client->dev,
+                       "   V4L2_CID_AUTOGAIN\n");
+               break;
+       case V4L2_CID_GAIN:
+               dev_dbg(&adv7180_data.i2c_client->dev,
+                       "   V4L2_CID_GAIN\n");
+               break;
+       case V4L2_CID_HFLIP:
+               dev_dbg(&adv7180_data.i2c_client->dev,
+                       "   V4L2_CID_HFLIP\n");
+               break;
+       case V4L2_CID_VFLIP:
+               dev_dbg(&adv7180_data.i2c_client->dev,
+                       "   V4L2_CID_VFLIP\n");
+               break;
+       default:
+               dev_dbg(&adv7180_data.i2c_client->dev,
+                       "   Default case\n");
+               vc->value = 0;
+               ret = -EPERM;
+               break;
+       }
+
+       return ret;
+}
+
+/*!
+ * ioctl_s_ctrl - V4L2 sensor interface handler for VIDIOC_S_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure
+ *
+ * If the requested control is supported, sets the control's current
+ * value in HW (and updates the video_control[] array).  Otherwise,
+ * returns -EINVAL if the control is not supported.
+ */
+static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc)
+{
+       int retval = 0;
+       u8 tmp;
+
+       dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180:ioctl_s_ctrl\n");
+
+       /* Make sure power on */
+       if (tvin_plat->pwdn)
+               tvin_plat->pwdn(0);
+
+       switch (vc->id) {
+       case V4L2_CID_BRIGHTNESS:
+               dev_dbg(&adv7180_data.i2c_client->dev,
+                       "   V4L2_CID_BRIGHTNESS\n");
+               tmp = vc->value;
+               adv7180_write_reg(ADV7180_BRIGHTNESS, tmp);
+               adv7180_data.brightness = vc->value;
+               break;
+       case V4L2_CID_CONTRAST:
+               dev_dbg(&adv7180_data.i2c_client->dev,
+                       "   V4L2_CID_CONTRAST\n");
+               break;
+       case V4L2_CID_SATURATION:
+               dev_dbg(&adv7180_data.i2c_client->dev,
+                       "   V4L2_CID_SATURATION\n");
+               tmp = vc->value;
+               adv7180_write_reg(ADV7180_SD_SATURATION_CB, tmp);
+               adv7180_write_reg(ADV7180_SD_SATURATION_CR, tmp);
+               adv7180_data.saturation = vc->value;
+               break;
+       case V4L2_CID_HUE:
+               dev_dbg(&adv7180_data.i2c_client->dev,
+                       "   V4L2_CID_HUE\n");
+               break;
+       case V4L2_CID_AUTO_WHITE_BALANCE:
+               dev_dbg(&adv7180_data.i2c_client->dev,
+                       "   V4L2_CID_AUTO_WHITE_BALANCE\n");
+               break;
+       case V4L2_CID_DO_WHITE_BALANCE:
+               dev_dbg(&adv7180_data.i2c_client->dev,
+                       "   V4L2_CID_DO_WHITE_BALANCE\n");
+               break;
+       case V4L2_CID_RED_BALANCE:
+               dev_dbg(&adv7180_data.i2c_client->dev,
+                       "   V4L2_CID_RED_BALANCE\n");
+               break;
+       case V4L2_CID_BLUE_BALANCE:
+               dev_dbg(&adv7180_data.i2c_client->dev,
+                       "   V4L2_CID_BLUE_BALANCE\n");
+               break;
+       case V4L2_CID_GAMMA:
+               dev_dbg(&adv7180_data.i2c_client->dev,
+                       "   V4L2_CID_GAMMA\n");
+               break;
+       case V4L2_CID_EXPOSURE:
+               dev_dbg(&adv7180_data.i2c_client->dev,
+                       "   V4L2_CID_EXPOSURE\n");
+               break;
+       case V4L2_CID_AUTOGAIN:
+               dev_dbg(&adv7180_data.i2c_client->dev,
+                       "   V4L2_CID_AUTOGAIN\n");
+               break;
+       case V4L2_CID_GAIN:
+               dev_dbg(&adv7180_data.i2c_client->dev,
+                       "   V4L2_CID_GAIN\n");
+               break;
+       case V4L2_CID_HFLIP:
+               dev_dbg(&adv7180_data.i2c_client->dev,
+                       "   V4L2_CID_HFLIP\n");
+               break;
+       case V4L2_CID_VFLIP:
+               dev_dbg(&adv7180_data.i2c_client->dev,
+                       "   V4L2_CID_VFLIP\n");
+               break;
+       default:
+               dev_dbg(&adv7180_data.i2c_client->dev,
+                       "   Default case\n");
+               retval = -EPERM;
+               break;
+       }
+
+       return retval;
+}
+
+/*!
+ * ioctl_g_chip_ident - V4L2 sensor interface handler for
+ *                     VIDIOC_DBG_G_CHIP_IDENT ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @id: pointer to int
+ *
+ * Return 0.
+ */
+static int ioctl_g_chip_ident(struct v4l2_int_device *s, int *id)
+{
+       ((struct v4l2_dbg_chip_ident *)id)->match.type =
+                                       V4L2_CHIP_MATCH_I2C_DRIVER;
+       strcpy(((struct v4l2_dbg_chip_ident *)id)->match.name,
+                                               "adv7180_decoder");
+       ((struct v4l2_dbg_chip_ident *)id)->ident = V4L2_IDENT_ADV7180;
+
+       return 0;
+}
+
+/*!
+ * ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT
+ * @s: pointer to standard V4L2 device structure
+ */
+static int ioctl_init(struct v4l2_int_device *s)
+{
+       dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180:ioctl_init\n");
+       return 0;
+}
+
+/*!
+ * ioctl_dev_init - V4L2 sensor interface handler for vidioc_int_dev_init_num
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Initialise the device when slave attaches to the master.
+ */
+static int ioctl_dev_init(struct v4l2_int_device *s)
+{
+       dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180:ioctl_dev_init\n");
+       return 0;
+}
+
+/*!
+ * This structure defines all the ioctls for this module.
+ */
+static struct v4l2_int_ioctl_desc adv7180_ioctl_desc[] = {
+
+       {vidioc_int_dev_init_num, (v4l2_int_ioctl_func*)ioctl_dev_init},
+
+       /*!
+        * Delinitialise the dev. at slave detach.
+        * The complement of ioctl_dev_init.
+        */
+/*     {vidioc_int_dev_exit_num, (v4l2_int_ioctl_func *)ioctl_dev_exit}, */
+
+       {vidioc_int_s_power_num, (v4l2_int_ioctl_func*)ioctl_s_power},
+       {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func*)ioctl_g_ifparm},
+/*     {vidioc_int_g_needs_reset_num,
+                               (v4l2_int_ioctl_func *)ioctl_g_needs_reset}, */
+/*     {vidioc_int_reset_num, (v4l2_int_ioctl_func *)ioctl_reset}, */
+       {vidioc_int_init_num, (v4l2_int_ioctl_func*)ioctl_init},
+
+       /*!
+        * VIDIOC_ENUM_FMT ioctl for the CAPTURE buffer type.
+        */
+/*     {vidioc_int_enum_fmt_cap_num,
+                               (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap}, */
+
+       /*!
+        * VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type.
+        * This ioctl is used to negotiate the image capture size and
+        * pixel format without actually making it take effect.
+        */
+/*     {vidioc_int_try_fmt_cap_num,
+                               (v4l2_int_ioctl_func *)ioctl_try_fmt_cap}, */
+
+       {vidioc_int_g_fmt_cap_num, (v4l2_int_ioctl_func*)ioctl_g_fmt_cap},
+
+       /*!
+        * If the requested format is supported, configures the HW to use that
+        * format, returns error code if format not supported or HW can't be
+        * correctly configured.
+        */
+/*     {vidioc_int_s_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_s_fmt_cap}, */
+
+       {vidioc_int_g_parm_num, (v4l2_int_ioctl_func*)ioctl_g_parm},
+       {vidioc_int_s_parm_num, (v4l2_int_ioctl_func*)ioctl_s_parm},
+       {vidioc_int_queryctrl_num, (v4l2_int_ioctl_func*)ioctl_queryctrl},
+       {vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func*)ioctl_g_ctrl},
+       {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func*)ioctl_s_ctrl},
+       {vidioc_int_g_chip_ident_num,
+                               (v4l2_int_ioctl_func *)ioctl_g_chip_ident},
+};
+
+static struct v4l2_int_slave adv7180_slave = {
+       .ioctls = adv7180_ioctl_desc,
+       .num_ioctls = ARRAY_SIZE(adv7180_ioctl_desc),
+};
+
+static struct v4l2_int_device adv7180_int_device = {
+       .module = THIS_MODULE,
+       .name = "adv7180",
+       .type = v4l2_int_type_slave,
+       .u = {
+               .slave = &adv7180_slave,
+       },
+};
+
+
+/***********************************************************************
+ * I2C client and driver.
+ ***********************************************************************/
+
+/*! ADV7180 Reset function.
+ *
+ *  @return            None.
+ */
+static void adv7180_hard_reset(bool cvbs)
+{
+       dev_dbg(&adv7180_data.i2c_client->dev,
+               "In adv7180:adv7180_hard_reset\n");
+
+       if (cvbs) {
+               /* Set CVBS input on AIN1 */
+               adv7180_write_reg(ADV7180_INPUT_CTL, 0x00);
+       } else {
+               /*
+                * Set YPbPr input on AIN1,4,5 and normal
+                * operations(autodection of all stds).
+                */
+               adv7180_write_reg(ADV7180_INPUT_CTL, 0x09);
+       }
+
+       /* Datasheet recommends */
+       adv7180_write_reg(ADV7180_VSYNC_FIELD_CTL_1, 0x02);
+       adv7180_write_reg(ADV7180_MANUAL_WIN_CTL, 0xa2);
+}
+
+/*! ADV7180 I2C attach function.
+ *
+ *  @param *adapter    struct i2c_adapter *.
+ *
+ *  @return            Error code indicating success or failure.
+ */
+
+/*!
+ * ADV7180 I2C probe function.
+ * Function set in i2c_driver struct.
+ * Called by insmod.
+ *
+ *  @param *adapter    I2C adapter descriptor.
+ *
+ *  @return            Error code indicating success or failure.
+ */
+static int adv7180_probe(struct i2c_client *client,
+                        const struct i2c_device_id *id)
+{
+       int rev_id;
+       int ret = 0;
+       tvin_plat = client->dev.platform_data;
+
+       dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180_probe\n");
+
+       if (tvin_plat->dvddio_reg) {
+               dvddio_regulator =
+                   regulator_get(&client->dev, tvin_plat->dvddio_reg);
+               if (!IS_ERR_VALUE((unsigned long)dvddio_regulator)) {
+                       regulator_set_voltage(dvddio_regulator, 3300000, 3300000);
+                       if (regulator_enable(dvddio_regulator) != 0)
+                               return -ENODEV;
+               }
+       }
+
+       if (tvin_plat->dvdd_reg) {
+               dvdd_regulator =
+                   regulator_get(&client->dev, tvin_plat->dvdd_reg);
+               if (!IS_ERR_VALUE((unsigned long)dvdd_regulator)) {
+                       regulator_set_voltage(dvdd_regulator, 1800000, 1800000);
+                       if (regulator_enable(dvdd_regulator) != 0)
+                               return -ENODEV;
+               }
+       }
+
+       if (tvin_plat->avdd_reg) {
+               avdd_regulator =
+                   regulator_get(&client->dev, tvin_plat->avdd_reg);
+               if (!IS_ERR_VALUE((unsigned long)avdd_regulator)) {
+                       regulator_set_voltage(avdd_regulator, 1800000, 1800000);
+                       if (regulator_enable(avdd_regulator) != 0)
+                               return -ENODEV;
+               }
+       }
+
+       if (tvin_plat->pvdd_reg) {
+               pvdd_regulator =
+                   regulator_get(&client->dev, tvin_plat->pvdd_reg);
+               if (!IS_ERR_VALUE((unsigned long)pvdd_regulator)) {
+                       regulator_set_voltage(pvdd_regulator, 1800000, 1800000);
+                       if (regulator_enable(pvdd_regulator) != 0)
+                               return -ENODEV;
+               }
+       }
+
+
+       if (tvin_plat->reset)
+               tvin_plat->reset();
+
+       if (tvin_plat->pwdn)
+               tvin_plat->pwdn(0);
+
+       msleep(1);
+
+       /* Set initial values for the sensor struct. */
+       memset(&adv7180_data, 0, sizeof(adv7180_data));
+       adv7180_data.i2c_client = client;
+       adv7180_data.streamcap.timeperframe.denominator = 30;
+       adv7180_data.streamcap.timeperframe.numerator = 1;
+       adv7180_data.std_id = V4L2_STD_ALL;
+       video_idx = ADV7180_NOT_LOCKED;
+       adv7180_data.pix.width = video_fmts[video_idx].raw_width;
+       adv7180_data.pix.height = video_fmts[video_idx].raw_height;
+       adv7180_data.pix.pixelformat = V4L2_PIX_FMT_UYVY;  /* YUV422 */
+       adv7180_data.pix.priv = 1;  /* 1 is used to indicate TV in */
+       adv7180_data.on = true;
+
+       gpio_sensor_active();
+
+       dev_dbg(&adv7180_data.i2c_client->dev,
+               "%s:adv7180 probe i2c address is 0x%02X \n",
+               __func__, adv7180_data.i2c_client->addr);
+
+       /*! Read the revision ID of the tvin chip */
+       rev_id = adv7180_read(ADV7180_IDENT);
+       dev_dbg(&adv7180_data.i2c_client->dev,
+               "%s:Analog Device adv7%2X0 detected! \n", __func__,
+               rev_id);
+
+       /*! ADV7180 initialization. */
+       adv7180_hard_reset(tvin_plat->cvbs);
+
+       pr_debug("   type is %d (expect %d)\n",
+                adv7180_int_device.type, v4l2_int_type_slave);
+       pr_debug("   num ioctls is %d\n",
+                adv7180_int_device.u.slave->num_ioctls);
+
+       /* This function attaches this structure to the /dev/video0 device.
+        * The pointer in priv points to the mt9v111_data structure here.*/
+       adv7180_int_device.priv = &adv7180_data;
+       ret = v4l2_int_device_register(&adv7180_int_device);
+
+       return ret;
+}
+
+/*!
+ * ADV7180 I2C detach function.
+ * Called on rmmod.
+ *
+ *  @param *client     struct i2c_client*.
+ *
+ *  @return            Error code indicating success or failure.
+ */
+static int adv7180_detach(struct i2c_client *client)
+{
+       struct mxc_tvin_platform_data *plat_data = client->dev.platform_data;
+
+       dev_dbg(&adv7180_data.i2c_client->dev,
+               "%s:Removing %s video decoder @ 0x%02X from adapter %s \n",
+               __func__, IF_NAME, client->addr << 1, client->adapter->name);
+
+       if (plat_data->pwdn)
+               plat_data->pwdn(1);
+
+       if (dvddio_regulator) {
+               regulator_disable(dvddio_regulator);
+               regulator_put(dvddio_regulator);
+       }
+
+       if (dvdd_regulator) {
+               regulator_disable(dvdd_regulator);
+               regulator_put(dvdd_regulator);
+       }
+
+       if (avdd_regulator) {
+               regulator_disable(avdd_regulator);
+               regulator_put(avdd_regulator);
+       }
+
+       if (pvdd_regulator) {
+               regulator_disable(pvdd_regulator);
+               regulator_put(pvdd_regulator);
+       }
+
+       v4l2_int_device_unregister(&adv7180_int_device);
+
+       return 0;
+}
+
+/*!
+ * ADV7180 init function.
+ * Called on insmod.
+ *
+ * @return    Error code indicating success or failure.
+ */
+static __init int adv7180_init(void)
+{
+       u8 err = 0;
+
+       dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180_init\n");
+
+       /* Tells the i2c driver what functions to call for this driver. */
+       err = i2c_add_driver(&adv7180_i2c_driver);
+       if (err != 0)
+               pr_err("%s:driver registration failed, error=%d \n",
+                       __func__, err);
+
+       return err;
+}
+
+/*!
+ * ADV7180 cleanup function.
+ * Called on rmmod.
+ *
+ * @return   Error code indicating success or failure.
+ */
+static void __exit adv7180_clean(void)
+{
+       dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180_clean\n");
+       i2c_del_driver(&adv7180_i2c_driver);
+       gpio_sensor_inactive();
+}
+
+module_init(adv7180_init);
+module_exit(adv7180_clean);
+
+MODULE_AUTHOR("Freescale Semiconductor");
+MODULE_DESCRIPTION("Anolog Device ADV7180 video decoder driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/csi_v4l2_capture.c b/drivers/media/video/mxc/capture/csi_v4l2_capture.c
new file mode 100644 (file)
index 0000000..eb824dd
--- /dev/null
@@ -0,0 +1,1466 @@
+/*
+ * 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
+ */
+
+/*!
+ * @file drivers/media/video/mxc/capture/csi_v4l2_capture.c
+ * This file is derived from mxc_v4l2_capture.c
+ *
+ * @brief MX25 Video For Linux 2 driver
+ *
+ * @ingroup MXC_V4L2_CAPTURE
+ */
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/io.h>
+#include <linux/semaphore.h>
+#include <linux/pagemap.h>
+#include <linux/vmalloc.h>
+#include <linux/types.h>
+#include <linux/fb.h>
+#include <linux/dma-mapping.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-int-device.h>
+#include <linux/mxcfb.h>
+#include "mxc_v4l2_capture.h"
+#include "fsl_csi.h"
+
+static int video_nr = -1;
+static cam_data *g_cam;
+
+static int csi_v4l2_master_attach(struct v4l2_int_device *slave);
+static void csi_v4l2_master_detach(struct v4l2_int_device *slave);
+static u8 camera_power(cam_data *cam, bool cameraOn);
+
+/*! Information about this driver. */
+static struct v4l2_int_master csi_v4l2_master = {
+       .attach = csi_v4l2_master_attach,
+       .detach = csi_v4l2_master_detach,
+};
+
+static struct v4l2_int_device csi_v4l2_int_device = {
+       .module = THIS_MODULE,
+       .name = "csi_v4l2_cap",
+       .type = v4l2_int_type_master,
+       .u = {
+             .master = &csi_v4l2_master,
+             },
+};
+
+/*!
+ * Camera V4l2 callback function.
+ *
+ * @param mask            u32
+ * @param dev      void device structure
+ *
+ * @return none
+ */
+static void camera_callback(u32 mask, void *dev)
+{
+       struct mxc_v4l_frame *done_frame;
+       struct mxc_v4l_frame *ready_frame;
+       cam_data *cam;
+
+       cam = (cam_data *) dev;
+       if (cam == NULL)
+               return;
+
+       if (list_empty(&cam->working_q)) {
+               pr_err("ERROR: v4l2 capture: %s: "
+                               "working queue empty\n", __func__);
+               return;
+       }
+
+       done_frame =
+               list_entry(cam->working_q.next, struct mxc_v4l_frame, queue);
+       if (done_frame->buffer.flags & V4L2_BUF_FLAG_QUEUED) {
+               done_frame->buffer.flags |= V4L2_BUF_FLAG_DONE;
+               done_frame->buffer.flags &= ~V4L2_BUF_FLAG_QUEUED;
+               if (list_empty(&cam->ready_q)) {
+                       cam->skip_frame++;
+               } else {
+                       ready_frame = list_entry(cam->ready_q.next,
+                                                struct mxc_v4l_frame, queue);
+                       list_del(cam->ready_q.next);
+                       list_add_tail(&ready_frame->queue, &cam->working_q);
+
+                       if (cam->ping_pong_csi == 1) {
+                               __raw_writel(cam->frame[ready_frame->index].
+                                            paddress, CSI_CSIDMASA_FB1);
+                       } else {
+                               __raw_writel(cam->frame[ready_frame->index].
+                                            paddress, CSI_CSIDMASA_FB2);
+                       }
+               }
+
+               /* Added to the done queue */
+               list_del(cam->working_q.next);
+               list_add_tail(&done_frame->queue, &cam->done_q);
+               cam->enc_counter++;
+               wake_up_interruptible(&cam->enc_queue);
+       } else {
+               pr_err("ERROR: v4l2 capture: %s: "
+                               "buffer not queued\n", __func__);
+       }
+
+       return;
+}
+
+/*!
+ * Make csi ready for capture image.
+ *
+ * @param cam      structure cam_data *
+ *
+ * @return status 0 success
+ */
+static int csi_cap_image(cam_data *cam)
+{
+       unsigned int value;
+
+       value = __raw_readl(CSI_CSICR3);
+       __raw_writel(value | BIT_DMA_REFLASH_RFF | BIT_FRMCNT_RST, CSI_CSICR3);
+       value = __raw_readl(CSI_CSISR);
+       __raw_writel(value, CSI_CSISR);
+
+       return 0;
+}
+
+/***************************************************************************
+ * Functions for handling Frame buffers.
+ **************************************************************************/
+
+/*!
+ * Free frame buffers
+ *
+ * @param cam      Structure cam_data *
+ *
+ * @return status  0 success.
+ */
+static int csi_free_frame_buf(cam_data *cam)
+{
+       int i;
+
+       pr_debug("MVC: In %s\n", __func__);
+
+       for (i = 0; i < FRAME_NUM; i++) {
+               if (cam->frame[i].vaddress != 0) {
+                       dma_free_coherent(0, cam->frame[i].buffer.length,
+                                            cam->frame[i].vaddress,
+                                            cam->frame[i].paddress);
+                       cam->frame[i].vaddress = 0;
+               }
+       }
+
+       return 0;
+}
+
+/*!
+ * Allocate frame buffers
+ *
+ * @param cam      Structure cam_data *
+ * @param count    int number of buffer need to allocated
+ *
+ * @return status  -0 Successfully allocated a buffer, -ENOBUFS        failed.
+ */
+static int csi_allocate_frame_buf(cam_data *cam, int count)
+{
+       int i;
+
+       pr_debug("In MVC:%s- size=%d\n",
+                __func__, cam->v2f.fmt.pix.sizeimage);
+       for (i = 0; i < count; i++) {
+               cam->frame[i].vaddress = dma_alloc_coherent(0, PAGE_ALIGN
+                                                              (cam->v2f.fmt.
+                                                              pix.sizeimage),
+                                                              &cam->frame[i].
+                                                              paddress,
+                                                              GFP_DMA |
+                                                              GFP_KERNEL);
+               if (cam->frame[i].vaddress == 0) {
+                       pr_err("ERROR: v4l2 capture: "
+                              "%s failed.\n", __func__);
+                       csi_free_frame_buf(cam);
+                       return -ENOBUFS;
+               }
+               cam->frame[i].buffer.index = i;
+               cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED;
+               cam->frame[i].buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+               cam->frame[i].buffer.length = PAGE_ALIGN(cam->v2f.fmt.
+                                                        pix.sizeimage);
+               cam->frame[i].buffer.memory = V4L2_MEMORY_MMAP;
+               cam->frame[i].buffer.m.offset = cam->frame[i].paddress;
+               cam->frame[i].index = i;
+       }
+
+       return 0;
+}
+
+/*!
+ * Free frame buffers status
+ *
+ * @param cam    Structure cam_data *
+ *
+ * @return none
+ */
+static void csi_free_frames(cam_data *cam)
+{
+       int i;
+
+       pr_debug("In MVC: %s\n", __func__);
+
+       for (i = 0; i < FRAME_NUM; i++)
+               cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED;
+
+       cam->skip_frame = 0;
+       INIT_LIST_HEAD(&cam->ready_q);
+       INIT_LIST_HEAD(&cam->working_q);
+       INIT_LIST_HEAD(&cam->done_q);
+
+       return;
+}
+
+/*!
+ * Return the buffer status
+ *
+ * @param cam     Structure cam_data *
+ * @param buf      Structure v4l2_buffer *
+ *
+ * @return status  0 success, EINVAL failed.
+ */
+static int csi_v4l2_buffer_status(cam_data *cam, struct v4l2_buffer *buf)
+{
+       pr_debug("In MVC: %s\n", __func__);
+
+       if (buf->index < 0 || buf->index >= FRAME_NUM) {
+               pr_err("ERROR: v4l2 capture: %s buffers "
+                               "not allocated\n", __func__);
+               return -EINVAL;
+       }
+
+       memcpy(buf, &(cam->frame[buf->index].buffer), sizeof(*buf));
+
+       return 0;
+}
+
+/*!
+ * Indicates whether the palette is supported.
+ *
+ * @param palette V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_UYVY or V4L2_PIX_FMT_YUV420
+ *
+ * @return 0 if failed
+ */
+static inline int valid_mode(u32 palette)
+{
+       return (palette == V4L2_PIX_FMT_RGB565) ||
+           (palette == V4L2_PIX_FMT_UYVY) || (palette == V4L2_PIX_FMT_YUV420);
+}
+
+/*!
+ * Start stream I/O
+ *
+ * @param cam      structure cam_data *
+ *
+ * @return status  0 Success
+ */
+static int csi_streamon(cam_data *cam)
+{
+       struct mxc_v4l_frame *frame;
+
+       pr_debug("In MVC: %s\n", __func__);
+
+       if (NULL == cam) {
+               pr_err("ERROR: v4l2 capture: %s cam parameter is NULL\n",
+                               __func__);
+               return -1;
+       }
+
+       /* move the frame from readyq to workingq */
+       if (list_empty(&cam->ready_q)) {
+               pr_err("ERROR: v4l2 capture: %s: "
+                               "ready_q queue empty\n", __func__);
+               return -1;
+       }
+       frame = list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue);
+       list_del(cam->ready_q.next);
+       list_add_tail(&frame->queue, &cam->working_q);
+       __raw_writel(cam->frame[frame->index].paddress, CSI_CSIDMASA_FB1);
+
+       if (list_empty(&cam->ready_q)) {
+               pr_err("ERROR: v4l2 capture: %s: "
+                               "ready_q queue empty\n", __func__);
+               return -1;
+       }
+       frame = list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue);
+       list_del(cam->ready_q.next);
+       list_add_tail(&frame->queue, &cam->working_q);
+       __raw_writel(cam->frame[frame->index].paddress, CSI_CSIDMASA_FB2);
+
+       cam->capture_pid = current->pid;
+       cam->capture_on = true;
+       csi_cap_image(cam);
+       csi_enable_int(1);
+
+       return 0;
+}
+
+/*!
+ * Stop stream I/O
+ *
+ * @param cam      structure cam_data *
+ *
+ * @return status  0 Success
+ */
+static int csi_streamoff(cam_data *cam)
+{
+       unsigned int cr3;
+
+       pr_debug("In MVC: %s\n", __func__);
+
+       if (cam->capture_on == false)
+               return 0;
+
+       csi_disable_int();
+       cam->capture_on = false;
+
+       /* set CSI_CSIDMASA_FB1 and CSI_CSIDMASA_FB2 to default value */
+       __raw_writel(0, CSI_CSIDMASA_FB1);
+       __raw_writel(0, CSI_CSIDMASA_FB2);
+       cr3 = __raw_readl(CSI_CSICR3);
+       __raw_writel(cr3 | BIT_DMA_REFLASH_RFF, CSI_CSICR3);
+
+       csi_free_frames(cam);
+       csi_free_frame_buf(cam);
+
+       return 0;
+}
+
+/*!
+ * start the viewfinder job
+ *
+ * @param cam      structure cam_data *
+ *
+ * @return status  0 Success
+ */
+static int start_preview(cam_data *cam)
+{
+       unsigned long fb_addr = (unsigned long)cam->v4l2_fb.base;
+
+       __raw_writel(fb_addr, CSI_CSIDMASA_FB1);
+       __raw_writel(fb_addr, CSI_CSIDMASA_FB2);
+       __raw_writel(__raw_readl(CSI_CSICR3) | BIT_DMA_REFLASH_RFF, CSI_CSICR3);
+
+       csi_enable_int(0);
+
+       return 0;
+}
+
+/*!
+ * shut down the viewfinder job
+ *
+ * @param cam      structure cam_data *
+ *
+ * @return status  0 Success
+ */
+static int stop_preview(cam_data *cam)
+{
+       csi_disable_int();
+
+       /* set CSI_CSIDMASA_FB1 and CSI_CSIDMASA_FB2 to default value */
+       __raw_writel(0, CSI_CSIDMASA_FB1);
+       __raw_writel(0, CSI_CSIDMASA_FB2);
+       __raw_writel(__raw_readl(CSI_CSICR3) | BIT_DMA_REFLASH_RFF, CSI_CSICR3);
+
+       return 0;
+}
+
+/***************************************************************************
+ * VIDIOC Functions.
+ **************************************************************************/
+
+/*!
+ *
+ * @param cam         structure cam_data *
+ *
+ * @param f           structure v4l2_format *
+ *
+ * @return  status    0 success, EINVAL failed
+ */
+static int csi_v4l2_g_fmt(cam_data *cam, struct v4l2_format *f)
+{
+       int retval = 0;
+
+       switch (f->type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               pr_debug("   type is V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
+               f->fmt.pix = cam->v2f.fmt.pix;
+               break;
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+               pr_debug("   type is V4L2_BUF_TYPE_VIDEO_OVERLAY\n");
+               f->fmt.win = cam->win;
+               break;
+       default:
+               pr_debug("   type is invalid\n");
+               retval = -EINVAL;
+       }
+
+       pr_debug("End of %s: v2f pix widthxheight %d x %d\n",
+                __func__, cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height);
+
+       return retval;
+}
+
+/*!
+ * V4L2 - csi_v4l2_s_fmt function
+ *
+ * @param cam         structure cam_data *
+ *
+ * @param f           structure v4l2_format *
+ *
+ * @return  status    0 success, EINVAL failed
+ */
+static int csi_v4l2_s_fmt(cam_data *cam, struct v4l2_format *f)
+{
+       int retval = 0;
+       int size = 0;
+       int bytesperline = 0;
+       int *width, *height;
+
+       pr_debug("In MVC: %s\n", __func__);
+
+       switch (f->type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               pr_debug("   type=V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
+               if (!valid_mode(f->fmt.pix.pixelformat)) {
+                       pr_err("ERROR: v4l2 capture: %s: format "
+                              "not supported\n", __func__);
+                       return -EINVAL;
+               }
+
+               /* Handle case where size requested is larger than cuurent
+                * camera setting. */
+               if ((f->fmt.pix.width > cam->crop_bounds.width)
+                   || (f->fmt.pix.height > cam->crop_bounds.height)) {
+                       /* Need the logic here, calling vidioc_s_param if
+                        * camera can change. */
+                       pr_debug("csi_v4l2_s_fmt size changed\n");
+               }
+               if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
+                       height = &f->fmt.pix.width;
+                       width = &f->fmt.pix.height;
+               } else {
+                       width = &f->fmt.pix.width;
+                       height = &f->fmt.pix.height;
+               }
+
+               if ((cam->crop_bounds.width / *width > 8) ||
+                   ((cam->crop_bounds.width / *width == 8) &&
+                    (cam->crop_bounds.width % *width))) {
+                       *width = cam->crop_bounds.width / 8;
+                       if (*width % 8)
+                               *width += 8 - *width % 8;
+                       pr_err("ERROR: v4l2 capture: width exceeds limit "
+                              "resize to %d.\n", *width);
+               }
+
+               if ((cam->crop_bounds.height / *height > 8) ||
+                   ((cam->crop_bounds.height / *height == 8) &&
+                    (cam->crop_bounds.height % *height))) {
+                       *height = cam->crop_bounds.height / 8;
+                       if (*height % 8)
+                               *height += 8 - *height % 8;
+                       pr_err("ERROR: v4l2 capture: height exceeds limit "
+                              "resize to %d.\n", *height);
+               }
+
+               switch (f->fmt.pix.pixelformat) {
+               case V4L2_PIX_FMT_RGB565:
+                       size = f->fmt.pix.width * f->fmt.pix.height * 2;
+                       csi_set_16bit_imagpara(f->fmt.pix.width,
+                                              f->fmt.pix.height);
+                       bytesperline = f->fmt.pix.width * 2;
+                       break;
+               case V4L2_PIX_FMT_UYVY:
+                       size = f->fmt.pix.width * f->fmt.pix.height * 2;
+                       csi_set_16bit_imagpara(f->fmt.pix.width,
+                                              f->fmt.pix.height);
+                       bytesperline = f->fmt.pix.width * 2;
+                       break;
+               case V4L2_PIX_FMT_YUV420:
+                       size = f->fmt.pix.width * f->fmt.pix.height * 3 / 2;
+                       csi_set_12bit_imagpara(f->fmt.pix.width,
+                                              f->fmt.pix.height);
+                       bytesperline = f->fmt.pix.width;
+                       break;
+               case V4L2_PIX_FMT_YUV422P:
+               case V4L2_PIX_FMT_RGB24:
+               case V4L2_PIX_FMT_BGR24:
+               case V4L2_PIX_FMT_BGR32:
+               case V4L2_PIX_FMT_RGB32:
+               case V4L2_PIX_FMT_NV12:
+               default:
+                       pr_debug("   case not supported\n");
+                       break;
+               }
+
+               if (f->fmt.pix.bytesperline < bytesperline)
+                       f->fmt.pix.bytesperline = bytesperline;
+               else
+                       bytesperline = f->fmt.pix.bytesperline;
+
+               if (f->fmt.pix.sizeimage < size)
+                       f->fmt.pix.sizeimage = size;
+               else
+                       size = f->fmt.pix.sizeimage;
+
+               cam->v2f.fmt.pix = f->fmt.pix;
+
+               if (cam->v2f.fmt.pix.priv != 0) {
+                       if (copy_from_user(&cam->offset,
+                                          (void *)cam->v2f.fmt.pix.priv,
+                                          sizeof(cam->offset))) {
+                               retval = -EFAULT;
+                               break;
+                       }
+               }
+               break;
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+               pr_debug("   type=V4L2_BUF_TYPE_VIDEO_OVERLAY\n");
+               cam->win = f->fmt.win;
+               break;
+       default:
+               retval = -EINVAL;
+       }
+
+       pr_debug("End of %s: v2f pix widthxheight %d x %d\n",
+                __func__, cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height);
+
+       return retval;
+}
+
+/*!
+ * V4L2 - csi_v4l2_s_param function
+ * Allows setting of capturemode and frame rate.
+ *
+ * @param cam         structure cam_data *
+ * @param parm        structure v4l2_streamparm *
+ *
+ * @return  status    0 success, EINVAL failed
+ */
+static int csi_v4l2_s_param(cam_data *cam, struct v4l2_streamparm *parm)
+{
+       struct v4l2_ifparm ifparm;
+       struct v4l2_format cam_fmt;
+       struct v4l2_streamparm currentparm;
+       int err = 0;
+
+       pr_debug("In %s\n", __func__);
+
+       if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+               pr_err(KERN_ERR "%s invalid type\n", __func__);
+               return -EINVAL;
+       }
+
+       /* Stop the viewfinder */
+       if (cam->overlay_on == true)
+               stop_preview(cam);
+
+       currentparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+       /* First check that this device can support the changes requested. */
+       err = vidioc_int_g_parm(cam->sensor, &currentparm);
+       if (err) {
+               pr_err("%s: vidioc_int_g_parm returned an error %d\n",
+                      __func__, err);
+               goto exit;
+       }
+
+       pr_debug("   Current capabilities are %x\n",
+                currentparm.parm.capture.capability);
+       pr_debug("   Current capturemode is %d  change to %d\n",
+                currentparm.parm.capture.capturemode,
+                parm->parm.capture.capturemode);
+       pr_debug("   Current framerate is %d  change to %d\n",
+                currentparm.parm.capture.timeperframe.denominator,
+                parm->parm.capture.timeperframe.denominator);
+
+       err = vidioc_int_s_parm(cam->sensor, parm);
+       if (err) {
+               pr_err("%s: vidioc_int_s_parm returned an error %d\n",
+                      __func__, err);
+               goto exit;
+       }
+
+       vidioc_int_g_ifparm(cam->sensor, &ifparm);
+       cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       pr_debug("   g_fmt_cap returns widthxheight of input as %d x %d\n",
+                cam_fmt.fmt.pix.width, cam_fmt.fmt.pix.height);
+
+exit:
+       return err;
+}
+
+/*!
+ * Dequeue one V4L capture buffer
+ *
+ * @param cam         structure cam_data *
+ * @param buf         structure v4l2_buffer *
+ *
+ * @return  status    0 success, EINVAL invalid frame number
+ *                    ETIME timeout, ERESTARTSYS interrupted by user
+ */
+static int csi_v4l_dqueue(cam_data *cam, struct v4l2_buffer *buf)
+{
+       int retval = 0;
+       struct mxc_v4l_frame *frame;
+       unsigned long lock_flags;
+
+       if (!wait_event_interruptible_timeout(cam->enc_queue,
+                               cam->enc_counter != 0, 10 * HZ)) {
+               pr_err("ERROR: v4l2 capture: mxc_v4l_dqueue timeout "
+                       "enc_counter %x\n", cam->enc_counter);
+               return -ETIME;
+       } else if (signal_pending(current)) {
+               pr_err("ERROR: v4l2 capture: mxc_v4l_dqueue() "
+                               "interrupt received\n");
+               return -ERESTARTSYS;
+       }
+
+       spin_lock_irqsave(&cam->dqueue_int_lock, lock_flags);
+
+       cam->enc_counter--;
+
+       frame = list_entry(cam->done_q.next, struct mxc_v4l_frame, queue);
+       list_del(cam->done_q.next);
+
+       if (frame->buffer.flags & V4L2_BUF_FLAG_DONE) {
+               frame->buffer.flags &= ~V4L2_BUF_FLAG_DONE;
+       } else if (frame->buffer.flags & V4L2_BUF_FLAG_QUEUED) {
+               pr_err("ERROR: v4l2 capture: VIDIOC_DQBUF: "
+                       "Buffer not filled.\n");
+               frame->buffer.flags &= ~V4L2_BUF_FLAG_QUEUED;
+               retval = -EINVAL;
+       } else if ((frame->buffer.flags & 0x7) == V4L2_BUF_FLAG_MAPPED) {
+               pr_err("ERROR: v4l2 capture: VIDIOC_DQBUF: "
+                       "Buffer not queued.\n");
+               retval = -EINVAL;
+       }
+
+       spin_unlock_irqrestore(&cam->dqueue_int_lock, lock_flags);
+
+       buf->bytesused = cam->v2f.fmt.pix.sizeimage;
+       buf->index = frame->index;
+       buf->flags = frame->buffer.flags;
+       buf->m = cam->frame[frame->index].buffer.m;
+
+       return retval;
+}
+
+/*!
+ * V4L interface - open function
+ *
+ * @param file         structure file *
+ *
+ * @return  status    0 success, ENODEV invalid device instance,
+ *                    ENODEV timeout, ERESTARTSYS interrupted by user
+ */
+static int csi_v4l_open(struct file *file)
+{
+       struct v4l2_ifparm ifparm;
+       struct v4l2_format cam_fmt;
+       struct video_device *dev = video_devdata(file);
+       cam_data *cam = video_get_drvdata(dev);
+       int err = 0;
+
+       pr_debug("   device name is %s\n", dev->name);
+
+       if (!cam) {
+               pr_err("ERROR: v4l2 capture: Internal error, "
+                      "cam_data not found!\n");
+               return -EBADF;
+       }
+
+       down(&cam->busy_lock);
+       err = 0;
+       if (signal_pending(current))
+               goto oops;
+
+       if (cam->open_count++ == 0) {
+               wait_event_interruptible(cam->power_queue,
+                                        cam->low_power == false);
+
+               cam->enc_counter = 0;
+               cam->skip_frame = 0;
+               INIT_LIST_HEAD(&cam->ready_q);
+               INIT_LIST_HEAD(&cam->working_q);
+               INIT_LIST_HEAD(&cam->done_q);
+
+               vidioc_int_g_ifparm(cam->sensor, &ifparm);
+
+               cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+               csi_enable_mclk(CSI_MCLK_I2C, true, true);
+               vidioc_int_init(cam->sensor);
+       }
+
+       file->private_data = dev;
+
+oops:
+       up(&cam->busy_lock);
+       return err;
+}
+
+/*!
+ * V4L interface - close function
+ *
+ * @param file     struct file *
+ *
+ * @return         0 success
+ */
+static int csi_v4l_close(struct file *file)
+{
+       struct video_device *dev = video_devdata(file);
+       int err = 0;
+       cam_data *cam = video_get_drvdata(dev);
+
+       pr_debug("In MVC:%s\n", __func__);
+
+       if (!cam) {
+               pr_err("ERROR: v4l2 capture: Internal error, "
+                      "cam_data not found!\n");
+               return -EBADF;
+       }
+
+       /* for the case somebody hit the ctrl C */
+       if (cam->overlay_pid == current->pid) {
+               err = stop_preview(cam);
+               cam->overlay_on = false;
+       }
+
+       if (--cam->open_count == 0) {
+               wait_event_interruptible(cam->power_queue,
+                                        cam->low_power == false);
+               file->private_data = NULL;
+               csi_enable_mclk(CSI_MCLK_I2C, false, false);
+       }
+
+       return err;
+}
+
+/*
+ * V4L interface - read function
+ *
+ * @param file       struct file *
+ * @param read buf   char *
+ * @param count      size_t
+ * @param ppos       structure loff_t *
+ *
+ * @return           bytes read
+ */
+static ssize_t csi_v4l_read(struct file *file, char *buf, size_t count,
+                           loff_t *ppos)
+{
+       int err = 0;
+       struct video_device *dev = video_devdata(file);
+       cam_data *cam = video_get_drvdata(dev);
+
+       if (down_interruptible(&cam->busy_lock))
+               return -EINTR;
+
+       /* Stop the viewfinder */
+       if (cam->overlay_on == true)
+               stop_preview(cam);
+
+       if (cam->still_buf_vaddr == NULL) {
+               cam->still_buf_vaddr = dma_alloc_coherent(0,
+                                                         PAGE_ALIGN
+                                                         (cam->v2f.fmt.
+                                                          pix.sizeimage),
+                                                         &cam->
+                                                         still_buf[0],
+                                                         GFP_DMA | GFP_KERNEL);
+               if (cam->still_buf_vaddr == NULL) {
+                       pr_err("alloc dma memory failed\n");
+                       return -ENOMEM;
+               }
+               cam->still_counter = 0;
+               __raw_writel(cam->still_buf[0], CSI_CSIDMASA_FB2);
+               __raw_writel(cam->still_buf[0], CSI_CSIDMASA_FB1);
+               __raw_writel(__raw_readl(CSI_CSICR3) | BIT_DMA_REFLASH_RFF,
+                            CSI_CSICR3);
+               __raw_writel(__raw_readl(CSI_CSISR), CSI_CSISR);
+               __raw_writel(__raw_readl(CSI_CSICR3) | BIT_FRMCNT_RST,
+                            CSI_CSICR3);
+               csi_enable_int(1);
+       }
+
+       wait_event_interruptible(cam->still_queue, cam->still_counter);
+       csi_disable_int();
+       err = copy_to_user(buf, cam->still_buf_vaddr,
+                          cam->v2f.fmt.pix.sizeimage);
+
+       if (cam->still_buf_vaddr != NULL) {
+               dma_free_coherent(0, PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage),
+                                 cam->still_buf_vaddr, cam->still_buf[0]);
+               cam->still_buf[0] = 0;
+               cam->still_buf_vaddr = NULL;
+       }
+
+       if (cam->overlay_on == true)
+               start_preview(cam);
+
+       up(&cam->busy_lock);
+       if (err < 0)
+               return err;
+
+       return cam->v2f.fmt.pix.sizeimage - err;
+}
+
+/*!
+ * V4L interface - ioctl function
+ *
+ * @param file       struct file*
+ *
+ * @param ioctlnr    unsigned int
+ *
+ * @param arg        void*
+ *
+ * @return           0 success, ENODEV for invalid device instance,
+ *                   -1 for other errors.
+ */
+static long csi_v4l_do_ioctl(struct file *file,
+                           unsigned int ioctlnr, void *arg)
+{
+       struct video_device *dev = video_devdata(file);
+       cam_data *cam = video_get_drvdata(dev);
+       int retval = 0;
+       unsigned long lock_flags;
+
+       pr_debug("In MVC: %s, %x\n", __func__, ioctlnr);
+       wait_event_interruptible(cam->power_queue, cam->low_power == false);
+       /* make this _really_ smp-safe */
+       if (down_interruptible(&cam->busy_lock))
+               return -EBUSY;
+
+       switch (ioctlnr) {
+               /*!
+                * V4l2 VIDIOC_G_FMT ioctl
+                */
+       case VIDIOC_G_FMT:{
+                       struct v4l2_format *gf = arg;
+                       pr_debug("   case VIDIOC_G_FMT\n");
+                       retval = csi_v4l2_g_fmt(cam, gf);
+                       break;
+               }
+
+               /*!
+                * V4l2 VIDIOC_S_FMT ioctl
+                */
+       case VIDIOC_S_FMT:{
+                       struct v4l2_format *sf = arg;
+                       pr_debug("   case VIDIOC_S_FMT\n");
+                       retval = csi_v4l2_s_fmt(cam, sf);
+                       vidioc_int_s_fmt_cap(cam->sensor, sf);
+                       break;
+               }
+
+               /*!
+                * V4l2 VIDIOC_OVERLAY ioctl
+                */
+       case VIDIOC_OVERLAY:{
+                       int *on = arg;
+                       pr_debug("   case VIDIOC_OVERLAY\n");
+                       if (*on) {
+                               cam->overlay_on = true;
+                               cam->overlay_pid = current->pid;
+                               start_preview(cam);
+                       }
+                       if (!*on) {
+                               stop_preview(cam);
+                               cam->overlay_on = false;
+                       }
+                       break;
+               }
+
+               /*!
+                * V4l2 VIDIOC_G_FBUF ioctl
+                */
+       case VIDIOC_G_FBUF:{
+                       struct v4l2_framebuffer *fb = arg;
+                       *fb = cam->v4l2_fb;
+                       fb->capability = V4L2_FBUF_CAP_EXTERNOVERLAY;
+                       break;
+               }
+
+               /*!
+                * V4l2 VIDIOC_S_FBUF ioctl
+                */
+       case VIDIOC_S_FBUF:{
+                       struct v4l2_framebuffer *fb = arg;
+                       cam->v4l2_fb = *fb;
+                       break;
+               }
+
+       case VIDIOC_G_PARM:{
+                       struct v4l2_streamparm *parm = arg;
+                       pr_debug("   case VIDIOC_G_PARM\n");
+                       vidioc_int_g_parm(cam->sensor, parm);
+                       break;
+               }
+
+       case VIDIOC_S_PARM:{
+                       struct v4l2_streamparm *parm = arg;
+                       pr_debug("   case VIDIOC_S_PARM\n");
+                       retval = csi_v4l2_s_param(cam, parm);
+                       break;
+               }
+
+       case VIDIOC_QUERYCAP:{
+                       struct v4l2_capability *cap = arg;
+                       pr_debug("   case VIDIOC_QUERYCAP\n");
+                       strcpy(cap->driver, "csi_v4l2");
+                       cap->version = KERNEL_VERSION(0, 1, 11);
+                       cap->capabilities = V4L2_CAP_VIDEO_OVERLAY |
+                           V4L2_CAP_VIDEO_OUTPUT_OVERLAY | V4L2_CAP_READWRITE;
+                       cap->card[0] = '\0';
+                       cap->bus_info[0] = '\0';
+                       break;
+               }
+
+       case VIDIOC_S_CROP:
+               pr_debug("   case not supported\n");
+               break;
+
+       case VIDIOC_REQBUFS: {
+               struct v4l2_requestbuffers *req = arg;
+               pr_debug("   case VIDIOC_REQBUFS\n");
+
+               if (req->count > FRAME_NUM) {
+                       pr_err("ERROR: v4l2 capture: VIDIOC_REQBUFS: "
+                                       "not enough buffers\n");
+                       req->count = FRAME_NUM;
+               }
+
+               if ((req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
+                               (req->memory != V4L2_MEMORY_MMAP)) {
+                       pr_err("ERROR: v4l2 capture: VIDIOC_REQBUFS: "
+                                       "wrong buffer type\n");
+                       retval = -EINVAL;
+                       break;
+               }
+
+               csi_streamoff(cam);
+               csi_free_frame_buf(cam);
+               cam->skip_frame = 0;
+               INIT_LIST_HEAD(&cam->ready_q);
+               INIT_LIST_HEAD(&cam->working_q);
+               INIT_LIST_HEAD(&cam->done_q);
+               retval = csi_allocate_frame_buf(cam, req->count);
+               break;
+       }
+
+       case VIDIOC_QUERYBUF: {
+               struct v4l2_buffer *buf = arg;
+               int index = buf->index;
+               pr_debug("   case VIDIOC_QUERYBUF\n");
+
+               if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+                       retval = -EINVAL;
+                       break;
+               }
+
+               memset(buf, 0, sizeof(buf));
+               buf->index = index;
+               retval = csi_v4l2_buffer_status(cam, buf);
+               break;
+       }
+
+       case VIDIOC_QBUF: {
+               struct v4l2_buffer *buf = arg;
+               int index = buf->index;
+               pr_debug("   case VIDIOC_QBUF\n");
+
+               spin_lock_irqsave(&cam->queue_int_lock, lock_flags);
+               cam->frame[index].buffer.m.offset = buf->m.offset;
+               if ((cam->frame[index].buffer.flags & 0x7) ==
+                               V4L2_BUF_FLAG_MAPPED) {
+                       cam->frame[index].buffer.flags |= V4L2_BUF_FLAG_QUEUED;
+                       if (cam->skip_frame > 0) {
+                               list_add_tail(&cam->frame[index].queue,
+                                             &cam->working_q);
+                               cam->skip_frame = 0;
+
+                               if (cam->ping_pong_csi == 1) {
+                                       __raw_writel(cam->frame[index].paddress,
+                                                    CSI_CSIDMASA_FB1);
+                               } else {
+                                       __raw_writel(cam->frame[index].paddress,
+                                                    CSI_CSIDMASA_FB2);
+                               }
+                       } else {
+                               list_add_tail(&cam->frame[index].queue,
+                                             &cam->ready_q);
+                       }
+               } else if (cam->frame[index].buffer.flags &
+                               V4L2_BUF_FLAG_QUEUED) {
+                       pr_err("ERROR: v4l2 capture: VIDIOC_QBUF: "
+                                       "buffer already queued\n");
+                       retval = -EINVAL;
+               } else if (cam->frame[index].buffer.
+                          flags & V4L2_BUF_FLAG_DONE) {
+                       pr_err("ERROR: v4l2 capture: VIDIOC_QBUF: "
+                              "overwrite done buffer.\n");
+                       cam->frame[index].buffer.flags &=
+                           ~V4L2_BUF_FLAG_DONE;
+                       cam->frame[index].buffer.flags |=
+                           V4L2_BUF_FLAG_QUEUED;
+                       retval = -EINVAL;
+               }
+               buf->flags = cam->frame[index].buffer.flags;
+               spin_unlock_irqrestore(&cam->queue_int_lock, lock_flags);
+
+               break;
+       }
+
+       case VIDIOC_DQBUF: {
+               struct v4l2_buffer *buf = arg;
+               pr_debug("   case VIDIOC_DQBUF\n");
+
+               retval = csi_v4l_dqueue(cam, buf);
+
+               break;
+       }
+
+       case VIDIOC_STREAMON: {
+               pr_debug("   case VIDIOC_STREAMON\n");
+               retval = csi_streamon(cam);
+               break;
+       }
+
+       case VIDIOC_STREAMOFF: {
+               pr_debug("   case VIDIOC_STREAMOFF\n");
+               retval = csi_streamoff(cam);
+               break;
+       }
+
+       case VIDIOC_S_CTRL:
+       case VIDIOC_G_STD:
+       case VIDIOC_G_OUTPUT:
+       case VIDIOC_S_OUTPUT:
+       case VIDIOC_ENUMSTD:
+       case VIDIOC_G_CROP:
+       case VIDIOC_CROPCAP:
+       case VIDIOC_S_STD:
+       case VIDIOC_G_CTRL:
+       case VIDIOC_ENUM_FMT:
+       case VIDIOC_TRY_FMT:
+       case VIDIOC_QUERYCTRL:
+       case VIDIOC_ENUMINPUT:
+       case VIDIOC_G_INPUT:
+       case VIDIOC_S_INPUT:
+       case VIDIOC_G_TUNER:
+       case VIDIOC_S_TUNER:
+       case VIDIOC_G_FREQUENCY:
+       case VIDIOC_S_FREQUENCY:
+       case VIDIOC_ENUMOUTPUT:
+       default:
+               pr_debug("   case not supported\n");
+               retval = -EINVAL;
+               break;
+       }
+
+       up(&cam->busy_lock);
+       return retval;
+}
+
+/*
+ * V4L interface - ioctl function
+ *
+ * @return  None
+ */
+static long csi_v4l_ioctl(struct file *file,
+                        unsigned int cmd, unsigned long arg)
+{
+       return video_usercopy(file, cmd, arg, csi_v4l_do_ioctl);
+}
+
+/*!
+ * V4L interface - mmap function
+ *
+ * @param file        structure file *
+ *
+ * @param vma         structure vm_area_struct *
+ *
+ * @return status     0 Success, EINTR busy lock error, ENOBUFS remap_page error
+ */
+static int csi_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       struct video_device *dev = video_devdata(file);
+       unsigned long size;
+       int res = 0;
+       cam_data *cam = video_get_drvdata(dev);
+
+       pr_debug("%s\n", __func__);
+       pr_debug("\npgoff=0x%lx, start=0x%lx, end=0x%lx\n",
+                vma->vm_pgoff, vma->vm_start, vma->vm_end);
+
+       /* make this _really_ smp-safe */
+       if (down_interruptible(&cam->busy_lock))
+               return -EINTR;
+
+       size = vma->vm_end - vma->vm_start;
+       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+       if (remap_pfn_range(vma, vma->vm_start,
+                           vma->vm_pgoff, size, vma->vm_page_prot)) {
+               pr_err("ERROR: v4l2 capture: %s : "
+                      "remap_pfn_range failed\n", __func__);
+               res = -ENOBUFS;
+               goto csi_mmap_exit;
+       }
+
+       vma->vm_flags &= ~VM_IO;        /* using shared anonymous pages */
+
+csi_mmap_exit:
+       up(&cam->busy_lock);
+       return res;
+}
+
+/*!
+ * This structure defines the functions to be called in this driver.
+ */
+static struct v4l2_file_operations csi_v4l_fops = {
+       .owner = THIS_MODULE,
+       .open = csi_v4l_open,
+       .release = csi_v4l_close,
+       .read = csi_v4l_read,
+       .ioctl = csi_v4l_ioctl,
+       .mmap = csi_mmap,
+};
+
+static struct video_device csi_v4l_template = {
+       .name = "Mx25 Camera",
+       .fops = &csi_v4l_fops,
+       .release = video_device_release,
+};
+
+/*!
+ * This function can be used to release any platform data on closing.
+ */
+static void camera_platform_release(struct device *device)
+{
+}
+
+/*! Device Definition for csi v4l2 device */
+static struct platform_device csi_v4l2_devices = {
+       .name = "csi_v4l2",
+       .dev = {
+               .release = camera_platform_release,
+               },
+       .id = 0,
+};
+
+/*!
+ * initialize cam_data structure
+ *
+ * @param cam      structure cam_data *
+ *
+ * @return status  0 Success
+ */
+static void init_camera_struct(cam_data *cam)
+{
+       pr_debug("In MVC: %s\n", __func__);
+
+       /* Default everything to 0 */
+       memset(cam, 0, sizeof(cam_data));
+
+       init_MUTEX(&cam->param_lock);
+       init_MUTEX(&cam->busy_lock);
+
+       cam->video_dev = video_device_alloc();
+       if (cam->video_dev == NULL)
+               return;
+
+       *(cam->video_dev) = csi_v4l_template;
+
+       video_set_drvdata(cam->video_dev, cam);
+       dev_set_drvdata(&csi_v4l2_devices.dev, (void *)cam);
+       cam->video_dev->minor = -1;
+
+       init_waitqueue_head(&cam->enc_queue);
+       init_waitqueue_head(&cam->still_queue);
+
+       cam->streamparm.parm.capture.capturemode = 0;
+
+       cam->standard.index = 0;
+       cam->standard.id = V4L2_STD_UNKNOWN;
+       cam->standard.frameperiod.denominator = 30;
+       cam->standard.frameperiod.numerator = 1;
+       cam->standard.framelines = 480;
+       cam->standard_autodetect = true;
+       cam->streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       cam->streamparm.parm.capture.timeperframe = cam->standard.frameperiod;
+       cam->streamparm.parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+       cam->overlay_on = false;
+       cam->capture_on = false;
+       cam->skip_frame = 0;
+       cam->v4l2_fb.flags = V4L2_FBUF_FLAG_OVERLAY;
+
+       cam->v2f.fmt.pix.sizeimage = 480 * 640 * 2;
+       cam->v2f.fmt.pix.bytesperline = 640 * 2;
+       cam->v2f.fmt.pix.width = 640;
+       cam->v2f.fmt.pix.height = 480;
+       cam->v2f.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
+       cam->win.w.width = 160;
+       cam->win.w.height = 160;
+       cam->win.w.left = 0;
+       cam->win.w.top = 0;
+       cam->still_counter = 0;
+
+       cam->enc_callback = camera_callback;
+       csi_start_callback(cam);
+       init_waitqueue_head(&cam->power_queue);
+       spin_lock_init(&cam->queue_int_lock);
+       spin_lock_init(&cam->dqueue_int_lock);
+}
+
+/*!
+ * camera_power function
+ *    Turns Sensor power On/Off
+ *
+ * @param       cam           cam data struct
+ * @param       cameraOn      true to turn camera on, false to turn off power.
+ *
+ * @return status
+ */
+static u8 camera_power(cam_data *cam, bool cameraOn)
+{
+       pr_debug("In MVC: %s on=%d\n", __func__, cameraOn);
+
+       if (cameraOn == true) {
+               csi_enable_mclk(CSI_MCLK_I2C, true, true);
+               vidioc_int_s_power(cam->sensor, 1);
+       } else {
+               csi_enable_mclk(CSI_MCLK_I2C, false, false);
+               vidioc_int_s_power(cam->sensor, 0);
+       }
+       return 0;
+}
+
+/*!
+ * This function is called to put the sensor in a low power state.
+ * Refer to the document driver-model/driver.txt in the kernel source tree
+ * for more information.
+ *
+ * @param   pdev  the device structure used to give information on which I2C
+ *                to suspend
+ * @param   state the power state the device is entering
+ *
+ * @return  The function returns 0 on success and -1 on failure.
+ */
+static int csi_v4l2_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       cam_data *cam = platform_get_drvdata(pdev);
+
+       pr_debug("In MVC: %s\n", __func__);
+
+       if (cam == NULL)
+               return -1;
+
+       cam->low_power = true;
+
+       if (cam->overlay_on == true)
+               stop_preview(cam);
+
+       camera_power(cam, false);
+
+       return 0;
+}
+
+/*!
+ * This function is called to bring the sensor back from a low power state.
+ * Refer to the document driver-model/driver.txt in the kernel source tree
+ * for more information.
+ *
+ * @param   pdev   the device structure
+ *
+ * @return  The function returns 0 on success and -1 on failure
+ */
+static int csi_v4l2_resume(struct platform_device *pdev)
+{
+       cam_data *cam = platform_get_drvdata(pdev);
+
+       pr_debug("In MVC: %s\n", __func__);
+
+       if (cam == NULL)
+               return -1;
+
+       cam->low_power = false;
+       wake_up_interruptible(&cam->power_queue);
+       camera_power(cam, true);
+
+       if (cam->overlay_on == true)
+               start_preview(cam);
+
+       return 0;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver csi_v4l2_driver = {
+       .driver = {
+                  .name = "csi_v4l2",
+                  },
+       .probe = NULL,
+       .remove = NULL,
+#ifdef CONFIG_PM
+       .suspend = csi_v4l2_suspend,
+       .resume = csi_v4l2_resume,
+#endif
+       .shutdown = NULL,
+};
+
+/*!
+ * Initializes the camera driver.
+ */
+static int csi_v4l2_master_attach(struct v4l2_int_device *slave)
+{
+       cam_data *cam = slave->u.slave->master->priv;
+       struct v4l2_format cam_fmt;
+
+       pr_debug("In MVC: %s\n", __func__);
+       pr_debug("   slave.name = %s\n", slave->name);
+       pr_debug("   master.name = %s\n", slave->u.slave->master->name);
+
+       cam->sensor = slave;
+       if (slave == NULL) {
+               pr_err("ERROR: v4l2 capture: slave parameter not valid.\n");
+               return -1;
+       }
+
+       csi_enable_mclk(CSI_MCLK_I2C, true, true);
+       vidioc_int_dev_init(slave);
+       csi_enable_mclk(CSI_MCLK_I2C, false, false);
+       cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+       /* Used to detect TV in (type 1) vs. camera (type 0) */
+       cam->device_type = cam_fmt.fmt.pix.priv;
+
+       pr_debug("End of %s: v2f pix widthxheight %d x %d\n",
+                __func__, cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height);
+
+       return 0;
+}
+
+/*!
+ * Disconnects the camera driver.
+ */
+static void csi_v4l2_master_detach(struct v4l2_int_device *slave)
+{
+       pr_debug("In MVC: %s\n", __func__);
+
+       vidioc_int_dev_exit(slave);
+}
+
+/*!
+ * Entry point for the V4L2
+ *
+ * @return  Error code indicating success or failure
+ */
+static __init int camera_init(void)
+{
+       u8 err = 0;
+
+       /* Register the device driver structure. */
+       err = platform_driver_register(&csi_v4l2_driver);
+       if (err != 0) {
+               pr_err("ERROR: v4l2 capture:camera_init: "
+                      "platform_driver_register failed.\n");
+               return err;
+       }
+
+       /* Create g_cam and initialize it. */
+       g_cam = kmalloc(sizeof(cam_data), GFP_KERNEL);
+       if (g_cam == NULL) {
+               pr_err("ERROR: v4l2 capture: failed to register camera\n");
+               platform_driver_unregister(&csi_v4l2_driver);
+               return -1;
+       }
+       init_camera_struct(g_cam);
+
+       /* Set up the v4l2 device and register it */
+       csi_v4l2_int_device.priv = g_cam;
+       /* This function contains a bug that won't let this be rmmod'd. */
+       v4l2_int_device_register(&csi_v4l2_int_device);
+
+       /* Register the platform device */
+       err = platform_device_register(&csi_v4l2_devices);
+       if (err != 0) {
+               pr_err("ERROR: v4l2 capture: camera_init: "
+                      "platform_device_register failed.\n");
+               platform_driver_unregister(&csi_v4l2_driver);
+               kfree(g_cam);
+               g_cam = NULL;
+               return err;
+       }
+
+       /* register v4l video device */
+       if (video_register_device(g_cam->video_dev, VFL_TYPE_GRABBER, video_nr)
+           == -1) {
+               platform_device_unregister(&csi_v4l2_devices);
+               platform_driver_unregister(&csi_v4l2_driver);
+               kfree(g_cam);
+               g_cam = NULL;
+               pr_err("ERROR: v4l2 capture: video_register_device failed\n");
+               return -1;
+       }
+       pr_debug("   Video device registered: %s #%d\n",
+                g_cam->video_dev->name, g_cam->video_dev->minor);
+
+       return err;
+}
+
+/*!
+ * Exit and cleanup for the V4L2
+ */
+static void __exit camera_exit(void)
+{
+       pr_debug("In MVC: %s\n", __func__);
+
+       if (g_cam->open_count) {
+               pr_err("ERROR: v4l2 capture:camera open "
+                      "-- setting ops to NULL\n");
+       } else {
+               pr_info("V4L2 freeing image input device\n");
+               v4l2_int_device_unregister(&csi_v4l2_int_device);
+               csi_stop_callback(g_cam);
+               video_unregister_device(g_cam->video_dev);
+               platform_driver_unregister(&csi_v4l2_driver);
+               platform_device_unregister(&csi_v4l2_devices);
+
+               kfree(g_cam);
+               g_cam = NULL;
+       }
+}
+
+module_init(camera_init);
+module_exit(camera_exit);
+
+module_param(video_nr, int, 0444);
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("V4L2 capture driver for Mx25 based cameras");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("video");
diff --git a/drivers/media/video/mxc/capture/fsl_csi.c b/drivers/media/video/mxc/capture/fsl_csi.c
new file mode 100644 (file)
index 0000000..dba35c4
--- /dev/null
@@ -0,0 +1,287 @@
+/*
+ * 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
+ */
+
+/*!
+ * @file fsl_csi.c, this file is derived from mx27_csi.c
+ *
+ * @brief mx25 CMOS Sensor interface functions
+ *
+ * @ingroup CSI
+ */
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <mach/clock.h>
+
+#include "mxc_v4l2_capture.h"
+#include "fsl_csi.h"
+
+static bool g_csi_mclk_on;
+static csi_irq_callback_t g_callback;
+static void *g_callback_data;
+static struct clk csi_mclk;
+
+static irqreturn_t csi_irq_handler(int irq, void *data)
+{
+       cam_data *cam = (cam_data *) data;
+       unsigned long status = __raw_readl(CSI_CSISR);
+       unsigned long cr3 = __raw_readl(CSI_CSICR3);
+       unsigned int frame_count = (cr3 >> 16) & 0xFFFF;
+
+       __raw_writel(status, CSI_CSISR);
+
+       if (status & BIT_SOF_INT) {
+               /* reflash the embeded DMA controller */
+               if (frame_count % 2 == 1)
+                       __raw_writel(cr3 | BIT_DMA_REFLASH_RFF, CSI_CSICR3);
+       }
+
+       if (status & BIT_DMA_TSF_DONE_FB1) {
+               if (cam->capture_on) {
+                       cam->ping_pong_csi = 1;
+                       cam->enc_callback(0, cam);
+               } else {
+                       cam->still_counter++;
+                       wake_up_interruptible(&cam->still_queue);
+               }
+       }
+
+       if (status & BIT_DMA_TSF_DONE_FB2) {
+               if (cam->capture_on) {
+                       cam->ping_pong_csi = 2;
+                       cam->enc_callback(0, cam);
+               } else {
+                       cam->still_counter++;
+                       wake_up_interruptible(&cam->still_queue);
+               }
+       }
+
+       if (g_callback)
+               g_callback(g_callback_data, status);
+
+       pr_debug("CSI status = 0x%08lX\n", status);
+
+       return IRQ_HANDLED;
+}
+
+static void csihw_reset_frame_count(void)
+{
+       __raw_writel(__raw_readl(CSI_CSICR3) | BIT_FRMCNT_RST, CSI_CSICR3);
+}
+
+static void csihw_reset(void)
+{
+       csihw_reset_frame_count();
+       __raw_writel(CSICR1_RESET_VAL, CSI_CSICR1);
+       __raw_writel(CSICR2_RESET_VAL, CSI_CSICR2);
+       __raw_writel(CSICR3_RESET_VAL, CSI_CSICR3);
+}
+
+/*!
+ * csi_init_interface
+ *    Init csi interface
+ */
+void csi_init_interface(void)
+{
+       unsigned int val = 0;
+       unsigned int imag_para;
+
+       val |= BIT_SOF_POL;
+       val |= BIT_REDGE;
+       val |= BIT_GCLK_MODE;
+       val |= BIT_HSYNC_POL;
+       val |= BIT_PACK_DIR;
+       val |= BIT_FCC;
+       val |= BIT_SWAP16_EN;
+       val |= 1 << SHIFT_MCLKDIV;
+       __raw_writel(val, CSI_CSICR1);
+
+       imag_para = (640 << 16) | 960;
+       __raw_writel(imag_para, CSI_CSIIMAG_PARA);
+
+       val = 0x1010;
+       val |= BIT_DMA_REFLASH_RFF;
+       __raw_writel(val, CSI_CSICR3);
+}
+EXPORT_SYMBOL(csi_init_interface);
+
+/*!
+ * csi_enable_mclk
+ *
+ * @param       src         enum define which source to control the clk
+ *                          CSI_MCLK_VF CSI_MCLK_ENC CSI_MCLK_RAW CSI_MCLK_I2C
+ * @param       flag        true to enable mclk, false to disable mclk
+ * @param       wait        true to wait 100ms make clock stable, false not wait
+ *
+ * @return      0 for success
+ */
+int32_t csi_enable_mclk(int src, bool flag, bool wait)
+{
+       if (flag == true) {
+               csi_mclk_enable();
+               if (wait == true)
+                       msleep(10);
+               pr_debug("Enable csi clock from source %d\n", src);
+               g_csi_mclk_on = true;
+       } else {
+               csi_mclk_disable();
+               pr_debug("Disable csi clock from source %d\n", src);
+               g_csi_mclk_on = false;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(csi_enable_mclk);
+
+/*!
+ * csi_read_mclk_flag
+ *
+ * @return  gcsi_mclk_source
+ */
+int csi_read_mclk_flag(void)
+{
+       return 0;
+}
+EXPORT_SYMBOL(csi_read_mclk_flag);
+
+void csi_start_callback(void *data)
+{
+       cam_data *cam = (cam_data *) data;
+
+       if (request_irq(MXC_INT_CSI, csi_irq_handler, 0, "csi", cam) < 0)
+               pr_debug("CSI error: irq request fail\n");
+
+}
+EXPORT_SYMBOL(csi_start_callback);
+
+void csi_stop_callback(void *data)
+{
+       cam_data *cam = (cam_data *) data;
+
+       free_irq(MXC_INT_CSI, cam);
+}
+EXPORT_SYMBOL(csi_stop_callback);
+
+void csi_enable_int(int arg)
+{
+       unsigned long cr1 = __raw_readl(CSI_CSICR1);
+
+       cr1 |= BIT_SOF_INTEN;
+       if (arg == 1) {
+               /* still capture needs DMA intterrupt */
+               cr1 |= BIT_FB1_DMA_DONE_INTEN;
+               cr1 |= BIT_FB2_DMA_DONE_INTEN;
+       }
+       __raw_writel(cr1, CSI_CSICR1);
+}
+EXPORT_SYMBOL(csi_enable_int);
+
+void csi_disable_int(void)
+{
+       unsigned long cr1 = __raw_readl(CSI_CSICR1);
+
+       cr1 &= ~BIT_SOF_INTEN;
+       cr1 &= ~BIT_FB1_DMA_DONE_INTEN;
+       cr1 &= ~BIT_FB2_DMA_DONE_INTEN;
+       __raw_writel(cr1, CSI_CSICR1);
+}
+EXPORT_SYMBOL(csi_disable_int);
+
+void csi_set_16bit_imagpara(int width, int height)
+{
+       int imag_para = 0;
+       unsigned long cr3 = __raw_readl(CSI_CSICR3);
+
+       imag_para = (width << 16) | (height * 2);
+       __raw_writel(imag_para, CSI_CSIIMAG_PARA);
+
+       /* reflash the embeded DMA controller */
+       __raw_writel(cr3 | BIT_DMA_REFLASH_RFF, CSI_CSICR3);
+}
+EXPORT_SYMBOL(csi_set_16bit_imagpara);
+
+void csi_set_12bit_imagpara(int width, int height)
+{
+       int imag_para = 0;
+       unsigned long cr3 = __raw_readl(CSI_CSICR3);
+
+       imag_para = (width << 16) | (height * 3 / 2);
+       __raw_writel(imag_para, CSI_CSIIMAG_PARA);
+
+       /* reflash the embeded DMA controller */
+       __raw_writel(cr3 | BIT_DMA_REFLASH_RFF, CSI_CSICR3);
+}
+EXPORT_SYMBOL(csi_set_12bit_imagpara);
+
+static void csi_mclk_recalc(struct clk *clk)
+{
+       u32 div;
+       unsigned long rate;
+
+       div = (__raw_readl(CSI_CSICR1) & BIT_MCLKDIV) >> SHIFT_MCLKDIV;
+       if (div == 0)
+               div = 1;
+       else
+               div = div * 2;
+
+       rate = clk_get_rate(clk->parent) / div;
+       clk_set_rate(clk, rate);
+}
+
+void csi_mclk_enable(void)
+{
+       __raw_writel(__raw_readl(CSI_CSICR1) | BIT_MCLKEN, CSI_CSICR1);
+}
+
+void csi_mclk_disable(void)
+{
+       __raw_writel(__raw_readl(CSI_CSICR1) & ~BIT_MCLKEN, CSI_CSICR1);
+}
+
+int32_t __init csi_init_module(void)
+{
+       int ret = 0;
+       struct clk *per_clk;
+
+       csihw_reset();
+       csi_init_interface();
+
+       per_clk = clk_get(NULL, "csi_clk");
+       if (IS_ERR(per_clk))
+               return PTR_ERR(per_clk);
+
+       clk_put(per_clk);
+       csi_mclk.parent = per_clk;
+       clk_enable(per_clk);
+       csi_mclk_recalc(&csi_mclk);
+
+       return ret;
+}
+
+void __exit csi_cleanup_module(void)
+{
+       clk_disable(&csi_mclk);
+}
+
+module_init(csi_init_module);
+module_exit(csi_cleanup_module);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("fsl CSI driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/fsl_csi.h b/drivers/media/video/mxc/capture/fsl_csi.h
new file mode 100644 (file)
index 0000000..00c3898
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * 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
+ */
+
+/*!
+ * @file fsl_csi.h
+ *
+ * @brief mx25 CMOS Sensor interface functions
+ *
+ * @ingroup CSI
+ */
+
+#ifndef MX25_CSI_H
+#define MX25_CSI_H
+
+#include <linux/io.h>
+#include <mach/hardware.h>
+
+/* reset values */
+#define CSICR1_RESET_VAL       0x40000800
+#define CSICR2_RESET_VAL       0x0
+#define CSICR3_RESET_VAL       0x0
+
+/* csi control reg 1 */
+#define BIT_SWAP16_EN          (0x1 << 31)
+#define BIT_EXT_VSYNC          (0x1 << 30)
+#define BIT_EOF_INT_EN         (0x1 << 29)
+#define BIT_PRP_IF_EN          (0x1 << 28)
+#define BIT_CCIR_MODE          (0x1 << 27)
+#define BIT_COF_INT_EN         (0x1 << 26)
+#define BIT_SF_OR_INTEN                (0x1 << 25)
+#define BIT_RF_OR_INTEN                (0x1 << 24)
+#define BIT_SFF_DMA_DONE_INTEN  (0x1 << 22)
+#define BIT_STATFF_INTEN       (0x1 << 21)
+#define BIT_FB2_DMA_DONE_INTEN  (0x1 << 20)
+#define BIT_FB1_DMA_DONE_INTEN  (0x1 << 19)
+#define BIT_RXFF_INTEN         (0x1 << 18)
+#define BIT_SOF_POL            (0x1 << 17)
+#define BIT_SOF_INTEN          (0x1 << 16)
+#define BIT_MCLKDIV            (0xF << 12)
+#define BIT_HSYNC_POL          (0x1 << 11)
+#define BIT_CCIR_EN            (0x1 << 10)
+#define BIT_MCLKEN             (0x1 << 9)
+#define BIT_FCC                        (0x1 << 8)
+#define BIT_PACK_DIR           (0x1 << 7)
+#define BIT_CLR_STATFIFO       (0x1 << 6)
+#define BIT_CLR_RXFIFO         (0x1 << 5)
+#define BIT_GCLK_MODE          (0x1 << 4)
+#define BIT_INV_DATA           (0x1 << 3)
+#define BIT_INV_PCLK           (0x1 << 2)
+#define BIT_REDGE              (0x1 << 1)
+#define BIT_PIXEL_BIT          (0x1 << 0)
+
+#define SHIFT_MCLKDIV          12
+
+/* control reg 3 */
+#define BIT_FRMCNT             (0xFFFF << 16)
+#define BIT_FRMCNT_RST         (0x1 << 15)
+#define BIT_DMA_REFLASH_RFF    (0x1 << 14)
+#define BIT_DMA_REFLASH_SFF    (0x1 << 13)
+#define BIT_DMA_REQ_EN_RFF     (0x1 << 12)
+#define BIT_DMA_REQ_EN_SFF     (0x1 << 11)
+#define BIT_STATFF_LEVEL       (0x7 << 8)
+#define BIT_HRESP_ERR_EN       (0x1 << 7)
+#define BIT_RXFF_LEVEL         (0x7 << 4)
+#define BIT_TWO_8BIT_SENSOR    (0x1 << 3)
+#define BIT_ZERO_PACK_EN       (0x1 << 2)
+#define BIT_ECC_INT_EN         (0x1 << 1)
+#define BIT_ECC_AUTO_EN                (0x1 << 0)
+
+#define SHIFT_FRMCNT           16
+
+/* csi status reg */
+#define BIT_SFF_OR_INT         (0x1 << 25)
+#define BIT_RFF_OR_INT         (0x1 << 24)
+#define BIT_DMA_TSF_DONE_SFF   (0x1 << 22)
+#define BIT_STATFF_INT         (0x1 << 21)
+#define BIT_DMA_TSF_DONE_FB2   (0x1 << 20)
+#define BIT_DMA_TSF_DONE_FB1   (0x1 << 19)
+#define BIT_RXFF_INT           (0x1 << 18)
+#define BIT_EOF_INT            (0x1 << 17)
+#define BIT_SOF_INT            (0x1 << 16)
+#define BIT_F2_INT             (0x1 << 15)
+#define BIT_F1_INT             (0x1 << 14)
+#define BIT_COF_INT            (0x1 << 13)
+#define BIT_HRESP_ERR_INT      (0x1 << 7)
+#define BIT_ECC_INT            (0x1 << 1)
+#define BIT_DRDY               (0x1 << 0)
+
+#define CSI_MCLK_VF            1
+#define CSI_MCLK_ENC           2
+#define CSI_MCLK_RAW           4
+#define CSI_MCLK_I2C           8
+#endif
+
+#define CSI_CSICR1             (IO_ADDRESS(CSI_BASE_ADDR))
+#define CSI_CSICR2             (IO_ADDRESS(CSI_BASE_ADDR + 0x4))
+#define CSI_CSICR3             (IO_ADDRESS(CSI_BASE_ADDR + 0x8))
+#define CSI_STATFIFO           (IO_ADDRESS(CSI_BASE_ADDR + 0xC))
+#define CSI_CSIRXFIFO          (IO_ADDRESS(CSI_BASE_ADDR + 0x10))
+#define CSI_CSIRXCNT           (IO_ADDRESS(CSI_BASE_ADDR + 0x14))
+#define CSI_CSISR              (IO_ADDRESS(CSI_BASE_ADDR + 0x18))
+
+#define CSI_CSIDBG             (IO_ADDRESS(CSI_BASE_ADDR + 0x1C))
+#define CSI_CSIDMASA_STATFIFO  (IO_ADDRESS(CSI_BASE_ADDR + 0x20))
+#define CSI_CSIDMATS_STATFIFO  (IO_ADDRESS(CSI_BASE_ADDR + 0x24))
+#define CSI_CSIDMASA_FB1       (IO_ADDRESS(CSI_BASE_ADDR + 0x28))
+#define CSI_CSIDMASA_FB2       (IO_ADDRESS(CSI_BASE_ADDR + 0x2C))
+#define CSI_CSIFBUF_PARA       (IO_ADDRESS(CSI_BASE_ADDR + 0x30))
+#define CSI_CSIIMAG_PARA       (IO_ADDRESS(CSI_BASE_ADDR + 0x34))
+
+#define CSI_CSIRXFIFO_PHYADDR  (CSI_BASE_ADDR + 0x10)
+
+static inline void csi_clear_status(unsigned long status)
+{
+       __raw_writel(status, CSI_CSISR);
+}
+
+struct csi_signal_cfg_t {
+       unsigned data_width:3;
+       unsigned clk_mode:2;
+       unsigned ext_vsync:1;
+       unsigned Vsync_pol:1;
+       unsigned Hsync_pol:1;
+       unsigned pixclk_pol:1;
+       unsigned data_pol:1;
+       unsigned sens_clksrc:1;
+};
+
+struct csi_config_t {
+       /* control reg 1 */
+       unsigned int swap16_en:1;
+       unsigned int ext_vsync:1;
+       unsigned int eof_int_en:1;
+       unsigned int prp_if_en:1;
+       unsigned int ccir_mode:1;
+       unsigned int cof_int_en:1;
+       unsigned int sf_or_inten:1;
+       unsigned int rf_or_inten:1;
+       unsigned int sff_dma_done_inten:1;
+       unsigned int statff_inten:1;
+       unsigned int fb2_dma_done_inten:1;
+       unsigned int fb1_dma_done_inten:1;
+       unsigned int rxff_inten:1;
+       unsigned int sof_pol:1;
+       unsigned int sof_inten:1;
+       unsigned int mclkdiv:4;
+       unsigned int hsync_pol:1;
+       unsigned int ccir_en:1;
+       unsigned int mclken:1;
+       unsigned int fcc:1;
+       unsigned int pack_dir:1;
+       unsigned int gclk_mode:1;
+       unsigned int inv_data:1;
+       unsigned int inv_pclk:1;
+       unsigned int redge:1;
+       unsigned int pixel_bit:1;
+
+       /* control reg 3 */
+       unsigned int frmcnt:16;
+       unsigned int frame_reset:1;
+       unsigned int dma_reflash_rff:1;
+       unsigned int dma_reflash_sff:1;
+       unsigned int dma_req_en_rff:1;
+       unsigned int dma_req_en_sff:1;
+       unsigned int statff_level:3;
+       unsigned int hresp_err_en:1;
+       unsigned int rxff_level:3;
+       unsigned int two_8bit_sensor:1;
+       unsigned int zero_pack_en:1;
+       unsigned int ecc_int_en:1;
+       unsigned int ecc_auto_en:1;
+       /* fifo counter */
+       unsigned int rxcnt;
+};
+
+typedef void (*csi_irq_callback_t) (void *data, unsigned long status);
+
+int32_t csi_enable_mclk(int src, bool flag, bool wait);
+void csi_init_interface(void);
+void csi_set_16bit_imagpara(int width, int height);
+void csi_set_12bit_imagpara(int width, int height);
+int csi_read_mclk_flag(void);
+void csi_start_callback(void *data);
+void csi_stop_callback(void *data);
+void csi_enable_int(int arg);
+void csi_disable_int(void);
+void csi_mclk_enable(void);
+void csi_mclk_disable(void);
diff --git a/drivers/media/video/mxc/capture/ipu_csi_enc.c b/drivers/media/video/mxc/capture/ipu_csi_enc.c
new file mode 100644 (file)
index 0000000..c8a4dab
--- /dev/null
@@ -0,0 +1,357 @@
+/*
+ * 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
+ */
+
+/*!
+ * @file ipu_csi_enc.c
+ *
+ * @brief CSI Use case for video capture
+ *
+ * @ingroup IPU
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/ipu.h>
+#include "mxc_v4l2_capture.h"
+#include "ipu_prp_sw.h"
+
+#ifdef CAMERA_DBG
+       #define CAMERA_TRACE(x) (printk)x
+#else
+       #define CAMERA_TRACE(x)
+#endif
+
+/*
+ * Function definitions
+ */
+
+/*!
+ * csi ENC callback function.
+ *
+ * @param irq       int irq line
+ * @param dev_id    void * device id
+ *
+ * @return status   IRQ_HANDLED for handled
+ */
+static irqreturn_t csi_enc_callback(int irq, void *dev_id)
+{
+       cam_data *cam = (cam_data *) dev_id;
+
+       if (cam->enc_callback == NULL)
+               return IRQ_HANDLED;
+
+       cam->enc_callback(irq, dev_id);
+       return IRQ_HANDLED;
+}
+
+/*!
+ * CSI ENC enable channel setup function
+ *
+ * @param cam       struct cam_data * mxc capture instance
+ *
+ * @return  status
+ */
+static int csi_enc_setup(cam_data *cam)
+{
+       ipu_channel_params_t params;
+       u32 pixel_fmt;
+       int err = 0, sensor_protocol = 0;
+       dma_addr_t dummy = cam->dummy_frame.buffer.m.offset;
+
+       CAMERA_TRACE("In csi_enc_setup\n");
+       if (!cam) {
+               printk(KERN_ERR "cam private is NULL\n");
+               return -ENXIO;
+       }
+
+       memset(&params, 0, sizeof(ipu_channel_params_t));
+       params.csi_mem.csi = cam->csi;
+
+       sensor_protocol = ipu_csi_get_sensor_protocol(cam->csi);
+       switch (sensor_protocol) {
+       case IPU_CSI_CLK_MODE_GATED_CLK:
+       case IPU_CSI_CLK_MODE_NONGATED_CLK:
+       case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE:
+       case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR:
+       case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR:
+               params.csi_mem.interlaced = false;
+               break;
+       case IPU_CSI_CLK_MODE_CCIR656_INTERLACED:
+       case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR:
+       case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR:
+               params.csi_mem.interlaced = true;
+               break;
+       default:
+               printk(KERN_ERR "sensor protocol unsupported\n");
+               return -EINVAL;
+       }
+
+       if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420)
+               pixel_fmt = IPU_PIX_FMT_YUV420P;
+       else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P)
+               pixel_fmt = IPU_PIX_FMT_YUV422P;
+       else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY)
+               pixel_fmt = IPU_PIX_FMT_UYVY;
+       else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
+               pixel_fmt = IPU_PIX_FMT_YUYV;
+       else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_NV12)
+               pixel_fmt = IPU_PIX_FMT_NV12;
+       else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24)
+               pixel_fmt = IPU_PIX_FMT_BGR24;
+       else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24)
+               pixel_fmt = IPU_PIX_FMT_RGB24;
+       else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565)
+               pixel_fmt = IPU_PIX_FMT_RGB565;
+       else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR32)
+               pixel_fmt = IPU_PIX_FMT_BGR32;
+       else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB32)
+               pixel_fmt = IPU_PIX_FMT_RGB32;
+       else {
+               printk(KERN_ERR "format not supported\n");
+               return -EINVAL;
+       }
+
+       ipu_csi_enable_mclk_if(CSI_MCLK_ENC, cam->csi, true, true);
+
+       err = ipu_init_channel(CSI_MEM, &params);
+       if (err != 0) {
+               printk(KERN_ERR "ipu_init_channel %d\n", err);
+               return err;
+       }
+
+       err = ipu_init_channel_buffer(CSI_MEM, IPU_OUTPUT_BUFFER,
+                                     pixel_fmt, cam->v2f.fmt.pix.width,
+                                     cam->v2f.fmt.pix.height,
+                                     cam->v2f.fmt.pix.width, IPU_ROTATE_NONE,
+                                     dummy, dummy,
+                                     cam->offset.u_offset,
+                                     cam->offset.v_offset);
+       if (err != 0) {
+               printk(KERN_ERR "CSI_MEM output buffer\n");
+               return err;
+       }
+       err = ipu_enable_channel(CSI_MEM);
+       if (err < 0) {
+               printk(KERN_ERR "ipu_enable_channel CSI_MEM\n");
+               return err;
+       }
+
+       return err;
+}
+
+/*!
+ * function to update physical buffer address for encorder IDMA channel
+ *
+ * @param eba         physical buffer address for encorder IDMA channel
+ * @param buffer_num  int buffer 0 or buffer 1
+ *
+ * @return  status
+ */
+static int csi_enc_eba_update(dma_addr_t eba, int *buffer_num)
+{
+       int err = 0;
+
+       pr_debug("eba %x\n", eba);
+       err = ipu_update_channel_buffer(CSI_MEM, IPU_OUTPUT_BUFFER,
+                                       *buffer_num, eba);
+       if (err != 0) {
+               ipu_clear_buffer_ready(CSI_MEM, IPU_OUTPUT_BUFFER,
+                                      *buffer_num);
+
+               err = ipu_update_channel_buffer(CSI_MEM, IPU_OUTPUT_BUFFER,
+                                               *buffer_num, eba);
+               if (err != 0) {
+                       pr_err("ERROR: v4l2 capture: fail to update "
+                              "buf%d\n", *buffer_num);
+                       return err;
+               }
+       }
+
+       ipu_select_buffer(CSI_MEM, IPU_OUTPUT_BUFFER, *buffer_num);
+
+       *buffer_num = (*buffer_num == 0) ? 1 : 0;
+
+       return 0;
+}
+
+/*!
+ * Enable encoder task
+ * @param private       struct cam_data * mxc capture instance
+ *
+ * @return  status
+ */
+static int csi_enc_enabling_tasks(void *private)
+{
+       cam_data *cam = (cam_data *) private;
+       int err = 0;
+       CAMERA_TRACE("IPU:In csi_enc_enabling_tasks\n");
+
+       cam->dummy_frame.vaddress = dma_alloc_coherent(0,
+                              PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage),
+                              &cam->dummy_frame.paddress,
+                              GFP_DMA | GFP_KERNEL);
+       if (cam->dummy_frame.vaddress == 0) {
+               pr_err("ERROR: v4l2 capture: Allocate dummy frame "
+                      "failed.\n");
+               return -ENOBUFS;
+       }
+       cam->dummy_frame.buffer.type = V4L2_BUF_TYPE_PRIVATE;
+       cam->dummy_frame.buffer.length =
+           PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage);
+       cam->dummy_frame.buffer.m.offset = cam->dummy_frame.paddress;
+
+       ipu_clear_irq(IPU_IRQ_CSI0_OUT_EOF);
+       err = ipu_request_irq(IPU_IRQ_CSI0_OUT_EOF,
+                             csi_enc_callback, 0, "Mxc Camera", cam);
+       if (err != 0) {
+               printk(KERN_ERR "Error registering rot irq\n");
+               return err;
+       }
+
+       err = csi_enc_setup(cam);
+       if (err != 0) {
+               printk(KERN_ERR "csi_enc_setup %d\n", err);
+               return err;
+       }
+
+       return err;
+}
+
+/*!
+ * Disable encoder task
+ * @param private       struct cam_data * mxc capture instance
+ *
+ * @return  int
+ */
+static int csi_enc_disabling_tasks(void *private)
+{
+       cam_data *cam = (cam_data *) private;
+       int err = 0;
+
+       ipu_free_irq(IPU_IRQ_CSI0_OUT_EOF, cam);
+
+       err = ipu_disable_channel(CSI_MEM, true);
+
+       ipu_uninit_channel(CSI_MEM);
+
+       if (cam->dummy_frame.vaddress != 0) {
+               dma_free_coherent(0, cam->dummy_frame.buffer.length,
+                                 cam->dummy_frame.vaddress,
+                                 cam->dummy_frame.paddress);
+               cam->dummy_frame.vaddress = 0;
+       }
+       ipu_csi_enable_mclk_if(CSI_MCLK_ENC, cam->csi, false, false);
+
+       return err;
+}
+
+/*!
+ * Enable csi
+ * @param private       struct cam_data * mxc capture instance
+ *
+ * @return  status
+ */
+static int csi_enc_enable_csi(void *private)
+{
+       cam_data *cam = (cam_data *) private;
+
+       return ipu_enable_csi(cam->csi);
+}
+
+/*!
+ * Disable csi
+ * @param private       struct cam_data * mxc capture instance
+ *
+ * @return  status
+ */
+static int csi_enc_disable_csi(void *private)
+{
+       cam_data *cam = (cam_data *) private;
+
+       return ipu_disable_csi(cam->csi);
+}
+
+/*!
+ * function to select CSI ENC as the working path
+ *
+ * @param private       struct cam_data * mxc capture instance
+ *
+ * @return  int
+ */
+int csi_enc_select(void *private)
+{
+       cam_data *cam = (cam_data *) private;
+       int err = 0;
+
+       if (cam) {
+               cam->enc_update_eba = csi_enc_eba_update;
+               cam->enc_enable = csi_enc_enabling_tasks;
+               cam->enc_disable = csi_enc_disabling_tasks;
+               cam->enc_enable_csi = csi_enc_enable_csi;
+               cam->enc_disable_csi = csi_enc_disable_csi;
+       } else {
+               err = -EIO;
+       }
+
+       return err;
+}
+
+/*!
+ * function to de-select CSI ENC as the working path
+ *
+ * @param private       struct cam_data * mxc capture instance
+ *
+ * @return  int
+ */
+int csi_enc_deselect(void *private)
+{
+       cam_data *cam = (cam_data *) private;
+       int err = 0;
+
+       if (cam) {
+               cam->enc_update_eba = NULL;
+               cam->enc_enable = NULL;
+               cam->enc_disable = NULL;
+               cam->enc_enable_csi = NULL;
+               cam->enc_disable_csi = NULL;
+       }
+
+       return err;
+}
+
+/*!
+ * Init the Encorder channels
+ *
+ * @return  Error code indicating success or failure
+ */
+__init int csi_enc_init(void)
+{
+       return 0;
+}
+
+/*!
+ * Deinit the Encorder channels
+ *
+ */
+void __exit csi_enc_exit(void)
+{
+}
+
+module_init(csi_enc_init);
+module_exit(csi_enc_exit);
+
+EXPORT_SYMBOL(csi_enc_select);
+EXPORT_SYMBOL(csi_enc_deselect);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("CSI ENC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/ipu_prp_enc.c b/drivers/media/video/mxc/capture/ipu_prp_enc.c
new file mode 100644 (file)
index 0000000..67637dd
--- /dev/null
@@ -0,0 +1,530 @@
+/*
+ * 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 ipu_prp_enc.c
+ *
+ * @brief IPU Use case for PRP-ENC
+ *
+ * @ingroup IPU
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/ipu.h>
+#include "mxc_v4l2_capture.h"
+#include "ipu_prp_sw.h"
+
+#ifdef CAMERA_DBG
+       #define CAMERA_TRACE(x) (printk)x
+#else
+       #define CAMERA_TRACE(x)
+#endif
+
+static ipu_rotate_mode_t grotation = IPU_ROTATE_NONE;
+
+/*
+ * Function definitions
+ */
+
+/*!
+ * IPU ENC callback function.
+ *
+ * @param irq       int irq line
+ * @param dev_id    void * device id
+ *
+ * @return status   IRQ_HANDLED for handled
+ */
+static irqreturn_t prp_enc_callback(int irq, void *dev_id)
+{
+       cam_data *cam = (cam_data *) dev_id;
+
+       if (cam->enc_callback == NULL)
+               return IRQ_HANDLED;
+
+       cam->enc_callback(irq, dev_id);
+
+       return IRQ_HANDLED;
+}
+
+/*!
+ * PrpENC enable channel setup function
+ *
+ * @param cam       struct cam_data * mxc capture instance
+ *
+ * @return  status
+ */
+static int prp_enc_setup(cam_data *cam)
+{
+       ipu_channel_params_t enc;
+       int err = 0;
+       dma_addr_t dummy = 0xdeadbeaf;
+
+       CAMERA_TRACE("In prp_enc_setup\n");
+       if (!cam) {
+               printk(KERN_ERR "cam private is NULL\n");
+               return -ENXIO;
+       }
+       memset(&enc, 0, sizeof(ipu_channel_params_t));
+
+       ipu_csi_get_window_size(&enc.csi_prp_enc_mem.in_width,
+                               &enc.csi_prp_enc_mem.in_height, cam->csi);
+
+       enc.csi_prp_enc_mem.in_pixel_fmt = IPU_PIX_FMT_UYVY;
+       enc.csi_prp_enc_mem.out_width = cam->v2f.fmt.pix.width;
+       enc.csi_prp_enc_mem.out_height = cam->v2f.fmt.pix.height;
+       enc.csi_prp_enc_mem.csi = cam->csi;
+       if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
+               enc.csi_prp_enc_mem.out_width = cam->v2f.fmt.pix.height;
+               enc.csi_prp_enc_mem.out_height = cam->v2f.fmt.pix.width;
+       }
+
+       if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) {
+               enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_YUV420P;
+               pr_info("YUV420\n");
+       } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P) {
+               enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_YUV422P;
+               pr_info("YUV422P\n");
+       } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) {
+               enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_YUYV;
+               pr_info("YUYV\n");
+       } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY) {
+               enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_UYVY;
+               pr_info("UYVY\n");
+       } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_NV12) {
+               enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_NV12;
+               pr_info("NV12\n");
+       } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24) {
+               enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_BGR24;
+               pr_info("BGR24\n");
+       } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24) {
+               enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_RGB24;
+               pr_info("RGB24\n");
+       } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565) {
+               enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_RGB565;
+               pr_info("RGB565\n");
+       } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR32) {
+               enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_BGR32;
+               pr_info("BGR32\n");
+       } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB32) {
+               enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_RGB32;
+               pr_info("RGB32\n");
+       } else {
+               printk(KERN_ERR "format not supported\n");
+               return -EINVAL;
+       }
+
+       err = ipu_init_channel(CSI_PRP_ENC_MEM, &enc);
+       if (err != 0) {
+               printk(KERN_ERR "ipu_init_channel %d\n", err);
+               return err;
+       }
+
+       ipu_csi_enable_mclk_if(CSI_MCLK_ENC, cam->csi, true, true);
+
+       grotation = cam->rotation;
+       if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
+               if (cam->rot_enc_bufs_vaddr[0]) {
+                       dma_free_coherent(0, cam->rot_enc_buf_size[0],
+                                         cam->rot_enc_bufs_vaddr[0],
+                                         cam->rot_enc_bufs[0]);
+               }
+               if (cam->rot_enc_bufs_vaddr[1]) {
+                       dma_free_coherent(0, cam->rot_enc_buf_size[1],
+                                         cam->rot_enc_bufs_vaddr[1],
+                                         cam->rot_enc_bufs[1]);
+               }
+               cam->rot_enc_buf_size[0] =
+                   PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage);
+               cam->rot_enc_bufs_vaddr[0] =
+                   (void *)dma_alloc_coherent(0, cam->rot_enc_buf_size[0],
+                                              &cam->rot_enc_bufs[0],
+                                              GFP_DMA | GFP_KERNEL);
+               if (!cam->rot_enc_bufs_vaddr[0]) {
+                       printk(KERN_ERR "alloc enc_bufs0\n");
+                       return -ENOMEM;
+               }
+               cam->rot_enc_buf_size[1] =
+                   PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage);
+               cam->rot_enc_bufs_vaddr[1] =
+                   (void *)dma_alloc_coherent(0, cam->rot_enc_buf_size[1],
+                                              &cam->rot_enc_bufs[1],
+                                              GFP_DMA | GFP_KERNEL);
+               if (!cam->rot_enc_bufs_vaddr[1]) {
+                       dma_free_coherent(0, cam->rot_enc_buf_size[0],
+                                         cam->rot_enc_bufs_vaddr[0],
+                                         cam->rot_enc_bufs[0]);
+                       cam->rot_enc_bufs_vaddr[0] = NULL;
+                       cam->rot_enc_bufs[0] = 0;
+                       printk(KERN_ERR "alloc enc_bufs1\n");
+                       return -ENOMEM;
+               }
+
+               err = ipu_init_channel_buffer(CSI_PRP_ENC_MEM,
+                                             IPU_OUTPUT_BUFFER,
+                                             enc.csi_prp_enc_mem.out_pixel_fmt,
+                                             enc.csi_prp_enc_mem.out_width,
+                                             enc.csi_prp_enc_mem.out_height,
+                                             enc.csi_prp_enc_mem.out_width,
+                                             IPU_ROTATE_NONE,
+                                             cam->rot_enc_bufs[0],
+                                             cam->rot_enc_bufs[1], 0, 0);
+               if (err != 0) {
+                       printk(KERN_ERR "CSI_PRP_ENC_MEM err\n");
+                       return err;
+               }
+
+               err = ipu_init_channel(MEM_ROT_ENC_MEM, NULL);
+               if (err != 0) {
+                       printk(KERN_ERR "MEM_ROT_ENC_MEM channel err\n");
+                       return err;
+               }
+
+               err = ipu_init_channel_buffer(MEM_ROT_ENC_MEM, IPU_INPUT_BUFFER,
+                                             enc.csi_prp_enc_mem.out_pixel_fmt,
+                                             enc.csi_prp_enc_mem.out_width,
+                                             enc.csi_prp_enc_mem.out_height,
+                                             enc.csi_prp_enc_mem.out_width,
+                                             cam->rotation,
+                                             cam->rot_enc_bufs[0],
+                                             cam->rot_enc_bufs[1], 0, 0);
+               if (err != 0) {
+                       printk(KERN_ERR "MEM_ROT_ENC_MEM input buffer\n");
+                       return err;
+               }
+
+               err =
+                   ipu_init_channel_buffer(MEM_ROT_ENC_MEM, IPU_OUTPUT_BUFFER,
+                                           enc.csi_prp_enc_mem.out_pixel_fmt,
+                                           enc.csi_prp_enc_mem.out_height,
+                                           enc.csi_prp_enc_mem.out_width,
+                                           cam->v2f.fmt.pix.bytesperline /
+                                           bytes_per_pixel(enc.csi_prp_enc_mem.
+                                                           out_pixel_fmt),
+                                           IPU_ROTATE_NONE, dummy, dummy,
+                                           cam->offset.u_offset,
+                                           cam->offset.v_offset);
+               if (err != 0) {
+                       printk(KERN_ERR "MEM_ROT_ENC_MEM output buffer\n");
+                       return err;
+               }
+
+               err = ipu_link_channels(CSI_PRP_ENC_MEM, MEM_ROT_ENC_MEM);
+               if (err < 0) {
+                       printk(KERN_ERR
+                              "link CSI_PRP_ENC_MEM-MEM_ROT_ENC_MEM\n");
+                       return err;
+               }
+
+               err = ipu_enable_channel(CSI_PRP_ENC_MEM);
+               if (err < 0) {
+                       printk(KERN_ERR "ipu_enable_channel CSI_PRP_ENC_MEM\n");
+                       return err;
+               }
+               err = ipu_enable_channel(MEM_ROT_ENC_MEM);
+               if (err < 0) {
+                       printk(KERN_ERR "ipu_enable_channel MEM_ROT_ENC_MEM\n");
+                       return err;
+               }
+
+               ipu_select_buffer(CSI_PRP_ENC_MEM, IPU_OUTPUT_BUFFER, 0);
+               ipu_select_buffer(CSI_PRP_ENC_MEM, IPU_OUTPUT_BUFFER, 1);
+       } else {
+               err =
+                   ipu_init_channel_buffer(CSI_PRP_ENC_MEM, IPU_OUTPUT_BUFFER,
+                                           enc.csi_prp_enc_mem.out_pixel_fmt,
+                                           enc.csi_prp_enc_mem.out_width,
+                                           enc.csi_prp_enc_mem.out_height,
+                                           cam->v2f.fmt.pix.bytesperline /
+                                           bytes_per_pixel(enc.csi_prp_enc_mem.
+                                                           out_pixel_fmt),
+                                           cam->rotation, dummy, dummy,
+                                           cam->offset.u_offset,
+                                           cam->offset.v_offset);
+               if (err != 0) {
+                       printk(KERN_ERR "CSI_PRP_ENC_MEM output buffer\n");
+                       return err;
+               }
+               err = ipu_enable_channel(CSI_PRP_ENC_MEM);
+               if (err < 0) {
+                       printk(KERN_ERR "ipu_enable_channel CSI_PRP_ENC_MEM\n");
+                       return err;
+               }
+       }
+
+       return err;
+}
+
+/*!
+ * function to update physical buffer address for encorder IDMA channel
+ *
+ * @param eba         physical buffer address for encorder IDMA channel
+ * @param buffer_num  int buffer 0 or buffer 1
+ *
+ * @return  status
+ */
+static int prp_enc_eba_update(dma_addr_t eba, int *buffer_num)
+{
+       int err = 0;
+
+       pr_debug("eba %x\n", eba);
+       if (grotation >= IPU_ROTATE_90_RIGHT) {
+               err = ipu_update_channel_buffer(MEM_ROT_ENC_MEM,
+                                               IPU_OUTPUT_BUFFER, *buffer_num,
+                                               eba);
+       } else {
+               err = ipu_update_channel_buffer(CSI_PRP_ENC_MEM,
+                                               IPU_OUTPUT_BUFFER, *buffer_num,
+                                               eba);
+       }
+       if (err != 0) {
+               if (grotation >= IPU_ROTATE_90_RIGHT) {
+                       ipu_clear_buffer_ready(MEM_ROT_ENC_MEM,
+                                              IPU_OUTPUT_BUFFER,
+                                              *buffer_num);
+                       err = ipu_update_channel_buffer(MEM_ROT_ENC_MEM,
+                                                       IPU_OUTPUT_BUFFER,
+                                                       *buffer_num,
+                                                       eba);
+               } else {
+                       ipu_clear_buffer_ready(CSI_PRP_ENC_MEM,
+                                              IPU_OUTPUT_BUFFER,
+                                              *buffer_num);
+                       err = ipu_update_channel_buffer(CSI_PRP_ENC_MEM,
+                                                       IPU_OUTPUT_BUFFER,
+                                                       *buffer_num,
+                                                       eba);
+               }
+
+               if (err != 0) {
+                       pr_err("ERROR: v4l2 capture: fail to update "
+                              "buf%d\n", *buffer_num);
+                       return err;
+               }
+       }
+
+       if (grotation >= IPU_ROTATE_90_RIGHT) {
+               ipu_select_buffer(MEM_ROT_ENC_MEM, IPU_OUTPUT_BUFFER,
+                                 *buffer_num);
+       } else {
+               ipu_select_buffer(CSI_PRP_ENC_MEM, IPU_OUTPUT_BUFFER,
+                                 *buffer_num);
+       }
+
+       *buffer_num = (*buffer_num == 0) ? 1 : 0;
+       return 0;
+}
+
+/*!
+ * Enable encoder task
+ * @param private       struct cam_data * mxc capture instance
+ *
+ * @return  status
+ */
+static int prp_enc_enabling_tasks(void *private)
+{
+       cam_data *cam = (cam_data *) private;
+       int err = 0;
+       CAMERA_TRACE("IPU:In prp_enc_enabling_tasks\n");
+
+       cam->dummy_frame.vaddress = dma_alloc_coherent(0,
+                              PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage),
+                              &cam->dummy_frame.paddress,
+                              GFP_DMA | GFP_KERNEL);
+       if (cam->dummy_frame.vaddress == 0) {
+               pr_err("ERROR: v4l2 capture: Allocate dummy frame "
+                      "failed.\n");
+               return -ENOBUFS;
+       }
+       cam->dummy_frame.buffer.type = V4L2_BUF_TYPE_PRIVATE;
+       cam->dummy_frame.buffer.length =
+           PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage);
+       cam->dummy_frame.buffer.m.offset = cam->dummy_frame.paddress;
+
+       if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
+               err = ipu_request_irq(IPU_IRQ_PRP_ENC_ROT_OUT_EOF,
+                                     prp_enc_callback, 0, "Mxc Camera", cam);
+       } else {
+               err = ipu_request_irq(IPU_IRQ_PRP_ENC_OUT_EOF,
+                                     prp_enc_callback, 0, "Mxc Camera", cam);
+       }
+       if (err != 0) {
+               printk(KERN_ERR "Error registering rot irq\n");
+               return err;
+       }
+
+       err = prp_enc_setup(cam);
+       if (err != 0) {
+               printk(KERN_ERR "prp_enc_setup %d\n", err);
+               return err;
+       }
+
+       return err;
+}
+
+/*!
+ * Disable encoder task
+ * @param private       struct cam_data * mxc capture instance
+ *
+ * @return  int
+ */
+static int prp_enc_disabling_tasks(void *private)
+{
+       cam_data *cam = (cam_data *) private;
+       int err = 0;
+
+       if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
+               ipu_free_irq(IPU_IRQ_PRP_ENC_ROT_OUT_EOF, cam);
+       } else {
+               ipu_free_irq(IPU_IRQ_PRP_ENC_OUT_EOF, cam);
+       }
+
+       if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
+               ipu_unlink_channels(CSI_PRP_ENC_MEM, MEM_ROT_ENC_MEM);
+       }
+
+       err = ipu_disable_channel(CSI_PRP_ENC_MEM, true);
+       if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
+               err |= ipu_disable_channel(MEM_ROT_ENC_MEM, true);
+       }
+
+       ipu_uninit_channel(CSI_PRP_ENC_MEM);
+       if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
+               ipu_uninit_channel(MEM_ROT_ENC_MEM);
+       }
+
+       if (cam->dummy_frame.vaddress != 0) {
+               dma_free_coherent(0, cam->dummy_frame.buffer.length,
+                                 cam->dummy_frame.vaddress,
+                                 cam->dummy_frame.paddress);
+               cam->dummy_frame.vaddress = 0;
+       }
+       ipu_csi_enable_mclk_if(CSI_MCLK_ENC, cam->csi, false, false);
+
+       return err;
+}
+
+/*!
+ * Enable csi
+ * @param private       struct cam_data * mxc capture instance
+ *
+ * @return  status
+ */
+static int prp_enc_enable_csi(void *private)
+{
+       cam_data *cam = (cam_data *) private;
+
+       return ipu_enable_csi(cam->csi);
+}
+
+/*!
+ * Disable csi
+ * @param private       struct cam_data * mxc capture instance
+ *
+ * @return  status
+ */
+static int prp_enc_disable_csi(void *private)
+{
+       cam_data *cam = (cam_data *) private;
+
+       return ipu_disable_csi(cam->csi);
+}
+
+/*!
+ * function to select PRP-ENC as the working path
+ *
+ * @param private       struct cam_data * mxc capture instance
+ *
+ * @return  int
+ */
+int prp_enc_select(void *private)
+{
+       cam_data *cam = (cam_data *) private;
+       int err = 0;
+
+       if (cam) {
+               cam->enc_update_eba = prp_enc_eba_update;
+               cam->enc_enable = prp_enc_enabling_tasks;
+               cam->enc_disable = prp_enc_disabling_tasks;
+               cam->enc_enable_csi = prp_enc_enable_csi;
+               cam->enc_disable_csi = prp_enc_disable_csi;
+       } else {
+               err = -EIO;
+       }
+
+       return err;
+}
+
+/*!
+ * function to de-select PRP-ENC as the working path
+ *
+ * @param private       struct cam_data * mxc capture instance
+ *
+ * @return  int
+ */
+int prp_enc_deselect(void *private)
+{
+       cam_data *cam = (cam_data *) private;
+       int err = 0;
+
+       if (cam) {
+               cam->enc_update_eba = NULL;
+               cam->enc_enable = NULL;
+               cam->enc_disable = NULL;
+               cam->enc_enable_csi = NULL;
+               cam->enc_disable_csi = NULL;
+               if (cam->rot_enc_bufs_vaddr[0]) {
+                       dma_free_coherent(0, cam->rot_enc_buf_size[0],
+                                         cam->rot_enc_bufs_vaddr[0],
+                                         cam->rot_enc_bufs[0]);
+                       cam->rot_enc_bufs_vaddr[0] = NULL;
+                       cam->rot_enc_bufs[0] = 0;
+               }
+               if (cam->rot_enc_bufs_vaddr[1]) {
+                       dma_free_coherent(0, cam->rot_enc_buf_size[1],
+                                         cam->rot_enc_bufs_vaddr[1],
+                                         cam->rot_enc_bufs[1]);
+                       cam->rot_enc_bufs_vaddr[1] = NULL;
+                       cam->rot_enc_bufs[1] = 0;
+               }
+       }
+
+       return err;
+}
+
+/*!
+ * Init the Encorder channels
+ *
+ * @return  Error code indicating success or failure
+ */
+__init int prp_enc_init(void)
+{
+       return 0;
+}
+
+/*!
+ * Deinit the Encorder channels
+ *
+ */
+void __exit prp_enc_exit(void)
+{
+}
+
+module_init(prp_enc_init);
+module_exit(prp_enc_exit);
+
+EXPORT_SYMBOL(prp_enc_select);
+EXPORT_SYMBOL(prp_enc_deselect);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("IPU PRP ENC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/ipu_prp_sw.h b/drivers/media/video/mxc/capture/ipu_prp_sw.h
new file mode 100644 (file)
index 0000000..85dd102
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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 ipu_prp_sw.h
+ *
+ * @brief This file contains the IPU PRP use case driver header.
+ *
+ * @ingroup IPU
+ */
+
+#ifndef _INCLUDE_IPU__PRP_SW_H_
+#define _INCLUDE_IPU__PRP_SW_H_
+
+int csi_enc_select(void *private);
+int csi_enc_deselect(void *private);
+int prp_enc_select(void *private);
+int prp_enc_deselect(void *private);
+int prp_vf_sdc_select(void *private);
+int prp_vf_sdc_select_bg(void *private);
+int prp_vf_sdc_deselect(void *private);
+int prp_vf_sdc_deselect_bg(void *private);
+int prp_still_select(void *private);
+int prp_still_deselect(void *private);
+
+#endif
diff --git a/drivers/media/video/mxc/capture/ipu_prp_vf_sdc.c b/drivers/media/video/mxc/capture/ipu_prp_vf_sdc.c
new file mode 100644 (file)
index 0000000..95b6ee6
--- /dev/null
@@ -0,0 +1,482 @@
+/*
+ * 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 ipu_prp_vf_sdc.c
+ *
+ * @brief IPU Use case for PRP-VF
+ *
+ * @ingroup IPU
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/console.h>
+#include <linux/ipu.h>
+#include <linux/mxcfb.h>
+#include <mach/hardware.h>
+#include "mxc_v4l2_capture.h"
+#include "ipu_prp_sw.h"
+
+#define OVERLAY_FB_SUPPORT_NONSTD      (cpu_is_mx5())
+
+/*
+ * Function definitions
+ */
+
+/*!
+ * prpvf_start - start the vf task
+ *
+ * @param private    cam_data * mxc v4l2 main structure
+ *
+ */
+static int prpvf_start(void *private)
+{
+       struct fb_var_screeninfo fbvar;
+       struct fb_info *fbi = NULL;
+       cam_data *cam = (cam_data *) private;
+       ipu_channel_params_t vf;
+       u32 vf_out_format = 0;
+       u32 size = 2, temp = 0;
+       int err = 0, i = 0;
+       short *tmp, color;
+
+       if (!cam) {
+               printk(KERN_ERR "private is NULL\n");
+               return -EIO;
+       }
+
+       if (cam->overlay_active == true) {
+               pr_debug("already started.\n");
+               return 0;
+       }
+
+       for (i = 0; i < num_registered_fb; i++) {
+               char *idstr = registered_fb[i]->fix.id;
+               if (strcmp(idstr, "DISP3 FG") == 0) {
+                       fbi = registered_fb[i];
+                       break;
+               }
+       }
+
+       if (fbi == NULL) {
+               printk(KERN_ERR "DISP3 FG fb not found\n");
+               return -EPERM;
+       }
+
+       fbvar = fbi->var;
+
+       /* Store the overlay frame buffer's original std */
+       cam->fb_origin_std = fbvar.nonstd;
+
+       if (OVERLAY_FB_SUPPORT_NONSTD) {
+               /* Use DP to do CSC so that we can get better performance */
+               vf_out_format = IPU_PIX_FMT_UYVY;
+               fbvar.nonstd = vf_out_format;
+               color = 0x80;
+       } else {
+               vf_out_format = IPU_PIX_FMT_RGB565;
+               fbvar.nonstd = 0;
+               color = 0x0;
+       }
+
+       fbvar.bits_per_pixel = 16;
+       fbvar.xres = fbvar.xres_virtual = cam->win.w.width;
+       fbvar.yres = cam->win.w.height;
+       fbvar.yres_virtual = cam->win.w.height * 2;
+       fbvar.yoffset = 0;
+       fbvar.activate |= FB_ACTIVATE_FORCE;
+       fb_set_var(fbi, &fbvar);
+
+       ipu_disp_set_window_pos(MEM_FG_SYNC, cam->win.w.left,
+                       cam->win.w.top);
+
+       /* Fill black color for framebuffer */
+       tmp = (short *) fbi->screen_base;
+       for (i = 0; i < (fbi->fix.line_length * fbi->var.yres)/2;
+                       i++, tmp++)
+               *tmp = color;
+
+       console_lock();
+       fb_blank(fbi, FB_BLANK_UNBLANK);
+       console_unlock();
+
+       /* correct display ch buffer address */
+       ipu_update_channel_buffer(MEM_FG_SYNC, IPU_INPUT_BUFFER,
+                               0, fbi->fix.smem_start +
+                               (fbi->fix.line_length * fbvar.yres));
+       ipu_update_channel_buffer(MEM_FG_SYNC, IPU_INPUT_BUFFER,
+                                       1, fbi->fix.smem_start);
+
+       memset(&vf, 0, sizeof(ipu_channel_params_t));
+       ipu_csi_get_window_size(&vf.csi_prp_vf_mem.in_width,
+                               &vf.csi_prp_vf_mem.in_height, cam->csi);
+       vf.csi_prp_vf_mem.in_pixel_fmt = IPU_PIX_FMT_UYVY;
+       vf.csi_prp_vf_mem.out_width = cam->win.w.width;
+       vf.csi_prp_vf_mem.out_height = cam->win.w.height;
+       vf.csi_prp_vf_mem.csi = cam->csi;
+       if (cam->vf_rotation >= IPU_ROTATE_90_RIGHT) {
+               vf.csi_prp_vf_mem.out_width = cam->win.w.height;
+               vf.csi_prp_vf_mem.out_height = cam->win.w.width;
+       }
+       vf.csi_prp_vf_mem.out_pixel_fmt = vf_out_format;
+       size = cam->win.w.width * cam->win.w.height * size;
+
+       err = ipu_init_channel(CSI_PRP_VF_MEM, &vf);
+       if (err != 0)
+               goto out_5;
+
+       ipu_csi_enable_mclk_if(CSI_MCLK_VF, cam->csi, true, true);
+
+       if (cam->vf_bufs_vaddr[0]) {
+               dma_free_coherent(0, cam->vf_bufs_size[0],
+                                 cam->vf_bufs_vaddr[0],
+                                 (dma_addr_t) cam->vf_bufs[0]);
+       }
+       if (cam->vf_bufs_vaddr[1]) {
+               dma_free_coherent(0, cam->vf_bufs_size[1],
+                                 cam->vf_bufs_vaddr[1],
+                                 (dma_addr_t) cam->vf_bufs[1]);
+       }
+       cam->vf_bufs_size[0] = PAGE_ALIGN(size);
+       cam->vf_bufs_vaddr[0] = (void *)dma_alloc_coherent(0,
+                                                          cam->vf_bufs_size[0],
+                                                          (dma_addr_t *) &
+                                                          cam->vf_bufs[0],
+                                                          GFP_DMA |
+                                                          GFP_KERNEL);
+       if (cam->vf_bufs_vaddr[0] == NULL) {
+               printk(KERN_ERR "Error to allocate vf buffer\n");
+               err = -ENOMEM;
+               goto out_4;
+       }
+       cam->vf_bufs_size[1] = PAGE_ALIGN(size);
+       cam->vf_bufs_vaddr[1] = (void *)dma_alloc_coherent(0,
+                                                          cam->vf_bufs_size[1],
+                                                          (dma_addr_t *) &
+                                                          cam->vf_bufs[1],
+                                                          GFP_DMA |
+                                                          GFP_KERNEL);
+       if (cam->vf_bufs_vaddr[1] == NULL) {
+               printk(KERN_ERR "Error to allocate vf buffer\n");
+               err = -ENOMEM;
+               goto out_3;
+       }
+       pr_debug("vf_bufs %x %x\n", cam->vf_bufs[0], cam->vf_bufs[1]);
+
+       if (cam->vf_rotation >= IPU_ROTATE_VERT_FLIP) {
+               err = ipu_init_channel_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER,
+                                             vf_out_format,
+                                             vf.csi_prp_vf_mem.out_width,
+                                             vf.csi_prp_vf_mem.out_height,
+                                             vf.csi_prp_vf_mem.out_width,
+                                             IPU_ROTATE_NONE, cam->vf_bufs[0],
+                                             cam->vf_bufs[1], 0, 0);
+               if (err != 0) {
+                       goto out_3;
+               }
+
+               err = ipu_init_channel(MEM_ROT_VF_MEM, NULL);
+               if (err != 0) {
+                       printk(KERN_ERR "Error MEM_ROT_VF_MEM channel\n");
+                       goto out_3;
+               }
+
+               err = ipu_init_channel_buffer(MEM_ROT_VF_MEM, IPU_INPUT_BUFFER,
+                                             vf_out_format,
+                                             vf.csi_prp_vf_mem.out_width,
+                                             vf.csi_prp_vf_mem.out_height,
+                                             vf.csi_prp_vf_mem.out_width,
+                                             cam->vf_rotation, cam->vf_bufs[0],
+                                             cam->vf_bufs[1], 0, 0);
+               if (err != 0) {
+                       printk(KERN_ERR "Error MEM_ROT_VF_MEM input buffer\n");
+                       goto out_2;
+               }
+
+               if (cam->vf_rotation < IPU_ROTATE_90_RIGHT) {
+                       temp = vf.csi_prp_vf_mem.out_width;
+                       vf.csi_prp_vf_mem.out_width =
+                                               vf.csi_prp_vf_mem.out_height;
+                       vf.csi_prp_vf_mem.out_height = temp;
+               }
+
+               err = ipu_init_channel_buffer(MEM_ROT_VF_MEM, IPU_OUTPUT_BUFFER,
+                                             vf_out_format,
+                                             vf.csi_prp_vf_mem.out_height,
+                                             vf.csi_prp_vf_mem.out_width,
+                                             vf.csi_prp_vf_mem.out_height,
+                                             IPU_ROTATE_NONE,
+                                             fbi->fix.smem_start +
+                                             (fbi->fix.line_length *
+                                              fbi->var.yres),
+                                             fbi->fix.smem_start, 0, 0);
+
+               if (err != 0) {
+                       printk(KERN_ERR "Error MEM_ROT_VF_MEM output buffer\n");
+                       goto out_2;
+               }
+
+               err = ipu_link_channels(CSI_PRP_VF_MEM, MEM_ROT_VF_MEM);
+               if (err < 0) {
+                       printk(KERN_ERR
+                              "Error link CSI_PRP_VF_MEM-MEM_ROT_VF_MEM\n");
+                       goto out_2;
+               }
+
+               err = ipu_link_channels(MEM_ROT_VF_MEM, MEM_FG_SYNC);
+               if (err < 0) {
+                       printk(KERN_ERR
+                              "Error link MEM_ROT_VF_MEM-MEM_FG_SYNC\n");
+                       goto out_1;
+               }
+
+               ipu_enable_channel(CSI_PRP_VF_MEM);
+               ipu_enable_channel(MEM_ROT_VF_MEM);
+
+               ipu_select_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER, 0);
+               ipu_select_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER, 1);
+               ipu_select_buffer(MEM_ROT_VF_MEM, IPU_OUTPUT_BUFFER, 0);
+               ipu_select_buffer(MEM_ROT_VF_MEM, IPU_OUTPUT_BUFFER, 1);
+       } else {
+               err = ipu_init_channel_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER,
+                                             vf_out_format, cam->win.w.width,
+                                             cam->win.w.height,
+                                             cam->win.w.width,
+                                             cam->vf_rotation,
+                                             fbi->fix.smem_start +
+                                             (fbi->fix.line_length *
+                                              fbi->var.yres),
+                                             fbi->fix.smem_start, 0, 0);
+               if (err != 0) {
+                       printk(KERN_ERR "Error initializing CSI_PRP_VF_MEM\n");
+                       goto out_4;
+               }
+
+               err = ipu_link_channels(CSI_PRP_VF_MEM, MEM_FG_SYNC);
+               if (err < 0) {
+                       printk(KERN_ERR "Error linking ipu channels\n");
+                       goto out_4;
+               }
+
+               ipu_enable_channel(CSI_PRP_VF_MEM);
+
+               ipu_select_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER, 0);
+               ipu_select_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER, 1);
+       }
+
+       cam->overlay_active = true;
+       return err;
+
+out_1:
+       ipu_unlink_channels(CSI_PRP_VF_MEM, MEM_ROT_VF_MEM);
+out_2:
+       if (cam->vf_rotation >= IPU_ROTATE_VERT_FLIP) {
+               ipu_uninit_channel(MEM_ROT_VF_MEM);
+       }
+out_3:
+       if (cam->vf_bufs_vaddr[0]) {
+               dma_free_coherent(0, cam->vf_bufs_size[0],
+                                 cam->vf_bufs_vaddr[0],
+                                 (dma_addr_t) cam->vf_bufs[0]);
+               cam->vf_bufs_vaddr[0] = NULL;
+               cam->vf_bufs[0] = 0;
+       }
+       if (cam->vf_bufs_vaddr[1]) {
+               dma_free_coherent(0, cam->vf_bufs_size[1],
+                                 cam->vf_bufs_vaddr[1],
+                                 (dma_addr_t) cam->vf_bufs[1]);
+               cam->vf_bufs_vaddr[1] = NULL;
+               cam->vf_bufs[1] = 0;
+       }
+out_4:
+       ipu_uninit_channel(CSI_PRP_VF_MEM);
+out_5:
+       return err;
+}
+
+/*!
+ * prpvf_stop - stop the vf task
+ *
+ * @param private    cam_data * mxc v4l2 main structure
+ *
+ */
+static int prpvf_stop(void *private)
+{
+       cam_data *cam = (cam_data *) private;
+       int err = 0, i = 0;
+       struct fb_info *fbi = NULL;
+       struct fb_var_screeninfo fbvar;
+
+       if (cam->overlay_active == false)
+               return 0;
+
+       for (i = 0; i < num_registered_fb; i++) {
+               char *idstr = registered_fb[i]->fix.id;
+               if (strcmp(idstr, "DISP3 FG") == 0) {
+                       fbi = registered_fb[i];
+                       break;
+               }
+       }
+
+       if (fbi == NULL) {
+               printk(KERN_ERR "DISP3 FG fb not found\n");
+               return -EPERM;
+       }
+
+       if (cam->vf_rotation >= IPU_ROTATE_VERT_FLIP) {
+               ipu_unlink_channels(CSI_PRP_VF_MEM, MEM_ROT_VF_MEM);
+               ipu_unlink_channels(MEM_ROT_VF_MEM, MEM_FG_SYNC);
+       } else {
+               ipu_unlink_channels(CSI_PRP_VF_MEM, MEM_FG_SYNC);
+       }
+
+       ipu_disable_channel(CSI_PRP_VF_MEM, true);
+
+       if (cam->vf_rotation >= IPU_ROTATE_VERT_FLIP) {
+               ipu_disable_channel(MEM_ROT_VF_MEM, true);
+               ipu_uninit_channel(MEM_ROT_VF_MEM);
+       }
+       ipu_uninit_channel(CSI_PRP_VF_MEM);
+
+       console_lock();
+       fb_blank(fbi, FB_BLANK_POWERDOWN);
+       console_unlock();
+
+       /* Set the overlay frame buffer std to what it is used to be */
+       fbvar = fbi->var;
+       fbvar.nonstd = cam->fb_origin_std;
+       fbvar.activate |= FB_ACTIVATE_FORCE;
+       fb_set_var(fbi, &fbvar);
+
+       ipu_csi_enable_mclk_if(CSI_MCLK_VF, cam->csi, false, false);
+
+       if (cam->vf_bufs_vaddr[0]) {
+               dma_free_coherent(0, cam->vf_bufs_size[0],
+                                 cam->vf_bufs_vaddr[0],
+                                 (dma_addr_t) cam->vf_bufs[0]);
+               cam->vf_bufs_vaddr[0] = NULL;
+               cam->vf_bufs[0] = 0;
+       }
+       if (cam->vf_bufs_vaddr[1]) {
+               dma_free_coherent(0, cam->vf_bufs_size[1],
+                                 cam->vf_bufs_vaddr[1],
+                                 (dma_addr_t) cam->vf_bufs[1]);
+               cam->vf_bufs_vaddr[1] = NULL;
+               cam->vf_bufs[1] = 0;
+       }
+
+       cam->overlay_active = false;
+       return err;
+}
+
+/*!
+ * Enable csi
+ * @param private       struct cam_data * mxc capture instance
+ *
+ * @return  status
+ */
+static int prp_vf_enable_csi(void *private)
+{
+       cam_data *cam = (cam_data *) private;
+
+       return ipu_enable_csi(cam->csi);
+}
+
+/*!
+ * Disable csi
+ * @param private       struct cam_data * mxc capture instance
+ *
+ * @return  status
+ */
+static int prp_vf_disable_csi(void *private)
+{
+       cam_data *cam = (cam_data *) private;
+
+       return ipu_disable_csi(cam->csi);
+}
+
+/*!
+ * function to select PRP-VF as the working path
+ *
+ * @param private    cam_data * mxc v4l2 main structure
+ *
+ * @return  status
+ */
+int prp_vf_sdc_select(void *private)
+{
+       cam_data *cam;
+       int err = 0;
+       if (private) {
+               cam = (cam_data *) private;
+               cam->vf_start_sdc = prpvf_start;
+               cam->vf_stop_sdc = prpvf_stop;
+               cam->vf_enable_csi = prp_vf_enable_csi;
+               cam->vf_disable_csi = prp_vf_disable_csi;
+               cam->overlay_active = false;
+       } else
+               err = -EIO;
+
+       return err;
+}
+
+/*!
+ * function to de-select PRP-VF as the working path
+ *
+ * @param private    cam_data * mxc v4l2 main structure
+ *
+ * @return  int
+ */
+int prp_vf_sdc_deselect(void *private)
+{
+       cam_data *cam;
+       int err = 0;
+       err = prpvf_stop(private);
+
+       if (private) {
+               cam = (cam_data *) private;
+               cam->vf_start_sdc = NULL;
+               cam->vf_stop_sdc = NULL;
+               cam->vf_enable_csi = NULL;
+               cam->vf_disable_csi = NULL;
+       }
+       return err;
+}
+
+/*!
+ * Init viewfinder task.
+ *
+ * @return  Error code indicating success or failure
+ */
+__init int prp_vf_sdc_init(void)
+{
+       return 0;
+}
+
+/*!
+ * Deinit viewfinder task.
+ *
+ * @return  Error code indicating success or failure
+ */
+void __exit prp_vf_sdc_exit(void)
+{
+}
+
+module_init(prp_vf_sdc_init);
+module_exit(prp_vf_sdc_exit);
+
+EXPORT_SYMBOL(prp_vf_sdc_select);
+EXPORT_SYMBOL(prp_vf_sdc_deselect);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("IPU PRP VF SDC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/ipu_prp_vf_sdc_bg.c b/drivers/media/video/mxc/capture/ipu_prp_vf_sdc_bg.c
new file mode 100644 (file)
index 0000000..042d07e
--- /dev/null
@@ -0,0 +1,443 @@
+/*
+ * 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 ipu_prp_vf_sdc_bg.c
+ *
+ * @brief IPU Use case for PRP-VF back-ground
+ *
+ * @ingroup IPU
+ */
+#include <linux/dma-mapping.h>
+#include <linux/fb.h>
+#include <linux/ipu.h>
+#include "mxc_v4l2_capture.h"
+#include "ipu_prp_sw.h"
+
+static int buffer_num;
+static int buffer_ready;
+
+/*
+ * Function definitions
+ */
+
+/*!
+ * SDC V-Sync callback function.
+ *
+ * @param irq       int irq line
+ * @param dev_id    void * device id
+ *
+ * @return status   IRQ_HANDLED for handled
+ */
+static irqreturn_t prpvf_sdc_vsync_callback(int irq, void *dev_id)
+{
+       pr_debug("buffer_ready %d buffer_num %d\n", buffer_ready, buffer_num);
+       if (buffer_ready > 0) {
+               ipu_select_buffer(MEM_ROT_VF_MEM, IPU_OUTPUT_BUFFER, 0);
+               buffer_ready--;
+       }
+
+       return IRQ_HANDLED;
+}
+
+/*!
+ * VF EOF callback function.
+ *
+ * @param irq       int irq line
+ * @param dev_id    void * device id
+ *
+ * @return status   IRQ_HANDLED for handled
+ */
+static irqreturn_t prpvf_vf_eof_callback(int irq, void *dev_id)
+{
+       pr_debug("buffer_ready %d buffer_num %d\n", buffer_ready, buffer_num);
+
+       ipu_select_buffer(MEM_ROT_VF_MEM, IPU_INPUT_BUFFER, buffer_num);
+
+       buffer_num = (buffer_num == 0) ? 1 : 0;
+
+       ipu_select_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER, buffer_num);
+       buffer_ready++;
+       return IRQ_HANDLED;
+}
+
+/*!
+ * prpvf_start - start the vf task
+ *
+ * @param private    cam_data * mxc v4l2 main structure
+ *
+ */
+static int prpvf_start(void *private)
+{
+       cam_data *cam = (cam_data *) private;
+       ipu_channel_params_t vf;
+       u32 format;
+       u32 offset;
+       u32 bpp, size = 3;
+       int err = 0;
+
+       if (!cam) {
+               printk(KERN_ERR "private is NULL\n");
+               return -EIO;
+       }
+
+       if (cam->overlay_active == true) {
+               pr_debug("already start.\n");
+               return 0;
+       }
+
+       format = cam->v4l2_fb.fmt.pixelformat;
+       if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_BGR24) {
+               bpp = 3, size = 3;
+               pr_info("BGR24\n");
+       } else if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_RGB565) {
+               bpp = 2, size = 2;
+               pr_info("RGB565\n");
+       } else if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_BGR32) {
+               bpp = 4, size = 4;
+               pr_info("BGR32\n");
+       } else {
+               printk(KERN_ERR
+                      "unsupported fix format from the framebuffer.\n");
+               return -EINVAL;
+       }
+
+       offset = cam->v4l2_fb.fmt.bytesperline * cam->win.w.top +
+           size * cam->win.w.left;
+
+       if (cam->v4l2_fb.base == 0) {
+               printk(KERN_ERR "invalid frame buffer address.\n");
+       } else {
+               offset += (u32) cam->v4l2_fb.base;
+       }
+
+       memset(&vf, 0, sizeof(ipu_channel_params_t));
+       ipu_csi_get_window_size(&vf.csi_prp_vf_mem.in_width,
+                               &vf.csi_prp_vf_mem.in_height, cam->csi);
+       vf.csi_prp_vf_mem.in_pixel_fmt = IPU_PIX_FMT_UYVY;
+       vf.csi_prp_vf_mem.out_width = cam->win.w.width;
+       vf.csi_prp_vf_mem.out_height = cam->win.w.height;
+       vf.csi_prp_vf_mem.csi = cam->csi;
+       if (cam->vf_rotation >= IPU_ROTATE_90_RIGHT) {
+               vf.csi_prp_vf_mem.out_width = cam->win.w.height;
+               vf.csi_prp_vf_mem.out_height = cam->win.w.width;
+       }
+       vf.csi_prp_vf_mem.out_pixel_fmt = format;
+       size = cam->win.w.width * cam->win.w.height * size;
+
+       err = ipu_init_channel(CSI_PRP_VF_MEM, &vf);
+       if (err != 0)
+               goto out_4;
+
+       ipu_csi_enable_mclk_if(CSI_MCLK_VF, cam->csi, true, true);
+
+       if (cam->vf_bufs_vaddr[0]) {
+               dma_free_coherent(0, cam->vf_bufs_size[0],
+                                 cam->vf_bufs_vaddr[0], cam->vf_bufs[0]);
+       }
+       if (cam->vf_bufs_vaddr[1]) {
+               dma_free_coherent(0, cam->vf_bufs_size[1],
+                                 cam->vf_bufs_vaddr[1], cam->vf_bufs[1]);
+       }
+       cam->vf_bufs_size[0] = PAGE_ALIGN(size);
+       cam->vf_bufs_vaddr[0] = (void *)dma_alloc_coherent(0,
+                                                          cam->vf_bufs_size[0],
+                                                          &cam->vf_bufs[0],
+                                                          GFP_DMA |
+                                                          GFP_KERNEL);
+       if (cam->vf_bufs_vaddr[0] == NULL) {
+               printk(KERN_ERR "Error to allocate vf buffer\n");
+               err = -ENOMEM;
+               goto out_3;
+       }
+       cam->vf_bufs_size[1] = PAGE_ALIGN(size);
+       cam->vf_bufs_vaddr[1] = (void *)dma_alloc_coherent(0,
+                                                          cam->vf_bufs_size[1],
+                                                          &cam->vf_bufs[1],
+                                                          GFP_DMA |
+                                                          GFP_KERNEL);
+       if (cam->vf_bufs_vaddr[1] == NULL) {
+               printk(KERN_ERR "Error to allocate vf buffer\n");
+               err = -ENOMEM;
+               goto out_3;
+       }
+
+       err = ipu_init_channel_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER,
+                                     format, vf.csi_prp_vf_mem.out_width,
+                                     vf.csi_prp_vf_mem.out_height,
+                                     vf.csi_prp_vf_mem.out_width,
+                                     IPU_ROTATE_NONE, cam->vf_bufs[0],
+                                     cam->vf_bufs[1], 0, 0);
+       if (err != 0) {
+               printk(KERN_ERR "Error initializing CSI_PRP_VF_MEM\n");
+               goto out_3;
+       }
+       err = ipu_init_channel(MEM_ROT_VF_MEM, NULL);
+       if (err != 0) {
+               printk(KERN_ERR "Error MEM_ROT_VF_MEM channel\n");
+               goto out_3;
+       }
+
+       err = ipu_init_channel_buffer(MEM_ROT_VF_MEM, IPU_INPUT_BUFFER,
+                                     format, vf.csi_prp_vf_mem.out_width,
+                                     vf.csi_prp_vf_mem.out_height,
+                                     vf.csi_prp_vf_mem.out_width,
+                                     cam->vf_rotation, cam->vf_bufs[0],
+                                     cam->vf_bufs[1], 0, 0);
+       if (err != 0) {
+               printk(KERN_ERR "Error MEM_ROT_VF_MEM input buffer\n");
+               goto out_2;
+       }
+
+       if (cam->vf_rotation >= IPU_ROTATE_90_RIGHT) {
+               err = ipu_init_channel_buffer(MEM_ROT_VF_MEM, IPU_OUTPUT_BUFFER,
+                                             format,
+                                             vf.csi_prp_vf_mem.out_height,
+                                             vf.csi_prp_vf_mem.out_width,
+                                             cam->overlay_fb->var.xres * bpp,
+                                             IPU_ROTATE_NONE, offset, 0, 0, 0);
+
+               if (err != 0) {
+                       printk(KERN_ERR "Error MEM_ROT_VF_MEM output buffer\n");
+                       goto out_2;
+               }
+       } else {
+               err = ipu_init_channel_buffer(MEM_ROT_VF_MEM, IPU_OUTPUT_BUFFER,
+                                             format,
+                                             vf.csi_prp_vf_mem.out_width,
+                                             vf.csi_prp_vf_mem.out_height,
+                                             cam->overlay_fb->var.xres * bpp,
+                                             IPU_ROTATE_NONE, offset, 0, 0, 0);
+               if (err != 0) {
+                       printk(KERN_ERR "Error MEM_ROT_VF_MEM output buffer\n");
+                       goto out_2;
+               }
+       }
+
+       ipu_clear_irq(IPU_IRQ_PRP_VF_OUT_EOF);
+       err = ipu_request_irq(IPU_IRQ_PRP_VF_OUT_EOF, prpvf_vf_eof_callback,
+                             0, "Mxc Camera", cam);
+       if (err != 0) {
+               printk(KERN_ERR
+                      "Error registering IPU_IRQ_PRP_VF_OUT_EOF irq.\n");
+               goto out_2;
+       }
+
+       ipu_clear_irq(IPU_IRQ_BG_SF_END);
+       err = ipu_request_irq(IPU_IRQ_BG_SF_END, prpvf_sdc_vsync_callback,
+                             0, "Mxc Camera", NULL);
+       if (err != 0) {
+               printk(KERN_ERR "Error registering IPU_IRQ_BG_SF_END irq.\n");
+               goto out_1;
+       }
+
+       ipu_enable_channel(CSI_PRP_VF_MEM);
+       ipu_enable_channel(MEM_ROT_VF_MEM);
+
+       buffer_num = 0;
+       buffer_ready = 0;
+       ipu_select_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER, 0);
+
+       cam->overlay_active = true;
+       return err;
+
+      out_1:
+       ipu_free_irq(IPU_IRQ_PRP_VF_OUT_EOF, NULL);
+      out_2:
+       ipu_uninit_channel(MEM_ROT_VF_MEM);
+      out_3:
+       ipu_uninit_channel(CSI_PRP_VF_MEM);
+      out_4:
+       if (cam->vf_bufs_vaddr[0]) {
+               dma_free_coherent(0, cam->vf_bufs_size[0],
+                                 cam->vf_bufs_vaddr[0], cam->vf_bufs[0]);
+               cam->vf_bufs_vaddr[0] = NULL;
+               cam->vf_bufs[0] = 0;
+       }
+       if (cam->vf_bufs_vaddr[1]) {
+               dma_free_coherent(0, cam->vf_bufs_size[1],
+                                 cam->vf_bufs_vaddr[1], cam->vf_bufs[1]);
+               cam->vf_bufs_vaddr[1] = NULL;
+               cam->vf_bufs[1] = 0;
+       }
+       if (cam->rot_vf_bufs_vaddr[0]) {
+               dma_free_coherent(0, cam->rot_vf_buf_size[0],
+                                 cam->rot_vf_bufs_vaddr[0],
+                                 cam->rot_vf_bufs[0]);
+               cam->rot_vf_bufs_vaddr[0] = NULL;
+               cam->rot_vf_bufs[0] = 0;
+       }
+       if (cam->rot_vf_bufs_vaddr[1]) {
+               dma_free_coherent(0, cam->rot_vf_buf_size[1],
+                                 cam->rot_vf_bufs_vaddr[1],
+                                 cam->rot_vf_bufs[1]);
+               cam->rot_vf_bufs_vaddr[1] = NULL;
+               cam->rot_vf_bufs[1] = 0;
+       }
+       return err;
+}
+
+/*!
+ * prpvf_stop - stop the vf task
+ *
+ * @param private    cam_data * mxc v4l2 main structure
+ *
+ */
+static int prpvf_stop(void *private)
+{
+       cam_data *cam = (cam_data *) private;
+
+       if (cam->overlay_active == false)
+               return 0;
+
+       ipu_free_irq(IPU_IRQ_BG_SF_END, NULL);
+
+       ipu_free_irq(IPU_IRQ_PRP_VF_OUT_EOF, cam);
+
+       ipu_disable_channel(CSI_PRP_VF_MEM, true);
+       ipu_disable_channel(MEM_ROT_VF_MEM, true);
+       ipu_uninit_channel(CSI_PRP_VF_MEM);
+       ipu_uninit_channel(MEM_ROT_VF_MEM);
+       ipu_csi_enable_mclk_if(CSI_MCLK_VF, cam->csi, false, false);
+
+       if (cam->vf_bufs_vaddr[0]) {
+               dma_free_coherent(0, cam->vf_bufs_size[0],
+                                 cam->vf_bufs_vaddr[0], cam->vf_bufs[0]);
+               cam->vf_bufs_vaddr[0] = NULL;
+               cam->vf_bufs[0] = 0;
+       }
+       if (cam->vf_bufs_vaddr[1]) {
+               dma_free_coherent(0, cam->vf_bufs_size[1],
+                                 cam->vf_bufs_vaddr[1], cam->vf_bufs[1]);
+               cam->vf_bufs_vaddr[1] = NULL;
+               cam->vf_bufs[1] = 0;
+       }
+       if (cam->rot_vf_bufs_vaddr[0]) {
+               dma_free_coherent(0, cam->rot_vf_buf_size[0],
+                                 cam->rot_vf_bufs_vaddr[0],
+                                 cam->rot_vf_bufs[0]);
+               cam->rot_vf_bufs_vaddr[0] = NULL;
+               cam->rot_vf_bufs[0] = 0;
+       }
+       if (cam->rot_vf_bufs_vaddr[1]) {
+               dma_free_coherent(0, cam->rot_vf_buf_size[1],
+                                 cam->rot_vf_bufs_vaddr[1],
+                                 cam->rot_vf_bufs[1]);
+               cam->rot_vf_bufs_vaddr[1] = NULL;
+               cam->rot_vf_bufs[1] = 0;
+       }
+
+       buffer_num = 0;
+       buffer_ready = 0;
+       cam->overlay_active = false;
+       return 0;
+}
+
+/*!
+ * Enable csi
+ * @param private       struct cam_data * mxc capture instance
+ *
+ * @return  status
+ */
+static int prp_vf_enable_csi(void *private)
+{
+       cam_data *cam = (cam_data *) private;
+
+       return ipu_enable_csi(cam->csi);
+}
+
+/*!
+ * Disable csi
+ * @param private       struct cam_data * mxc capture instance
+ *
+ * @return  status
+ */
+static int prp_vf_disable_csi(void *private)
+{
+       cam_data *cam = (cam_data *) private;
+
+       return ipu_disable_csi(cam->csi);
+}
+
+/*!
+ * function to select PRP-VF as the working path
+ *
+ * @param private    cam_data * mxc v4l2 main structure
+ *
+ * @return  status
+ */
+int prp_vf_sdc_select_bg(void *private)
+{
+       cam_data *cam = (cam_data *) private;
+
+       if (cam) {
+               cam->vf_start_sdc = prpvf_start;
+               cam->vf_stop_sdc = prpvf_stop;
+               cam->vf_enable_csi = prp_vf_enable_csi;
+               cam->vf_disable_csi = prp_vf_disable_csi;
+               cam->overlay_active = false;
+       }
+
+       return 0;
+}
+
+/*!
+ * function to de-select PRP-VF as the working path
+ *
+ * @param private    cam_data * mxc v4l2 main structure
+ *
+ * @return  status
+ */
+int prp_vf_sdc_deselect_bg(void *private)
+{
+       cam_data *cam = (cam_data *) private;
+       int err = 0;
+       err = prpvf_stop(private);
+
+       if (cam) {
+               cam->vf_start_sdc = NULL;
+               cam->vf_stop_sdc = NULL;
+               cam->vf_enable_csi = NULL;
+               cam->vf_disable_csi = NULL;
+       }
+       return err;
+}
+
+/*!
+ * Init viewfinder task.
+ *
+ * @return  Error code indicating success or failure
+ */
+__init int prp_vf_sdc_init_bg(void)
+{
+       return 0;
+}
+
+/*!
+ * Deinit viewfinder task.
+ *
+ * @return  Error code indicating success or failure
+ */
+void __exit prp_vf_sdc_exit_bg(void)
+{
+}
+
+module_init(prp_vf_sdc_init_bg);
+module_exit(prp_vf_sdc_exit_bg);
+
+EXPORT_SYMBOL(prp_vf_sdc_select_bg);
+EXPORT_SYMBOL(prp_vf_sdc_deselect_bg);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("IPU PRP VF SDC Backgroud Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/ipu_still.c b/drivers/media/video/mxc/capture/ipu_still.c
new file mode 100644 (file)
index 0000000..b81891a
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ * 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 ipu_still.c
+ *
+ * @brief IPU Use case for still image capture
+ *
+ * @ingroup IPU
+ */
+
+#include <linux/semaphore.h>
+#include <linux/sched.h>
+#include <linux/ipu.h>
+#include "mxc_v4l2_capture.h"
+#include "ipu_prp_sw.h"
+
+static int callback_eof_flag;
+#ifndef CONFIG_MXC_IPU_V1
+static int buffer_num;
+#endif
+
+#ifdef CONFIG_MXC_IPU_V1
+static int callback_flag;
+/*
+ * Function definitions
+ */
+/*!
+ * CSI EOF callback function.
+ *
+ * @param irq       int irq line
+ * @param dev_id    void * device id
+ *
+ * @return status   IRQ_HANDLED for handled
+ */
+static irqreturn_t prp_csi_eof_callback(int irq, void *dev_id)
+{
+       ipu_select_buffer(CSI_MEM, IPU_OUTPUT_BUFFER,
+                         callback_flag%2 ? 1 : 0);
+       if (callback_flag == 0)
+               ipu_enable_channel(CSI_MEM);
+
+       callback_flag++;
+       return IRQ_HANDLED;
+}
+#endif
+
+/*!
+ * CSI callback function.
+ *
+ * @param irq       int irq line
+ * @param dev_id    void * device id
+ *
+ * @return status   IRQ_HANDLED for handled
+ */
+static irqreturn_t prp_still_callback(int irq, void *dev_id)
+{
+       cam_data *cam = (cam_data *) dev_id;
+
+       callback_eof_flag++;
+       if (callback_eof_flag < 5) {
+#ifndef CONFIG_MXC_IPU_V1
+               buffer_num = (buffer_num == 0) ? 1 : 0;
+               ipu_select_buffer(CSI_MEM, IPU_OUTPUT_BUFFER, buffer_num);
+#endif
+       } else {
+               cam->still_counter++;
+               wake_up_interruptible(&cam->still_queue);
+       }
+
+       return IRQ_HANDLED;
+}
+
+/*!
+ * start csi->mem task
+ * @param private       struct cam_data * mxc capture instance
+ *
+ * @return  status
+ */
+static int prp_still_start(void *private)
+{
+       cam_data *cam = (cam_data *) private;
+       u32 pixel_fmt;
+       int err;
+       ipu_channel_params_t params;
+
+       if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420)
+               pixel_fmt = IPU_PIX_FMT_YUV420P;
+       else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_NV12)
+               pixel_fmt = IPU_PIX_FMT_NV12;
+       else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P)
+               pixel_fmt = IPU_PIX_FMT_YUV422P;
+       else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY)
+               pixel_fmt = IPU_PIX_FMT_UYVY;
+       else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
+               pixel_fmt = IPU_PIX_FMT_YUYV;
+       else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24)
+               pixel_fmt = IPU_PIX_FMT_BGR24;
+       else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24)
+               pixel_fmt = IPU_PIX_FMT_RGB24;
+       else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565)
+               pixel_fmt = IPU_PIX_FMT_RGB565;
+       else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR32)
+               pixel_fmt = IPU_PIX_FMT_BGR32;
+       else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB32)
+               pixel_fmt = IPU_PIX_FMT_RGB32;
+       else {
+               printk(KERN_ERR "format not supported\n");
+               return -EINVAL;
+       }
+
+       ipu_csi_enable_mclk_if(CSI_MCLK_RAW, cam->csi, true, true);
+
+       memset(&params, 0, sizeof(params));
+       err = ipu_init_channel(CSI_MEM, &params);
+       if (err != 0)
+               return err;
+
+       err = ipu_init_channel_buffer(CSI_MEM, IPU_OUTPUT_BUFFER,
+                                     pixel_fmt, cam->v2f.fmt.pix.width,
+                                     cam->v2f.fmt.pix.height,
+                                     cam->v2f.fmt.pix.width, IPU_ROTATE_NONE,
+                                     cam->still_buf[0], cam->still_buf[1],
+                                     0, 0);
+       if (err != 0)
+               return err;
+
+#ifdef CONFIG_MXC_IPU_V1
+       ipu_clear_irq(IPU_IRQ_SENSOR_OUT_EOF);
+       err = ipu_request_irq(IPU_IRQ_SENSOR_OUT_EOF, prp_still_callback,
+                             0, "Mxc Camera", cam);
+       if (err != 0) {
+               printk(KERN_ERR "Error registering irq.\n");
+               return err;
+       }
+       callback_flag = 0;
+       callback_eof_flag = 0;
+       ipu_clear_irq(IPU_IRQ_SENSOR_EOF);
+       err = ipu_request_irq(IPU_IRQ_SENSOR_EOF, prp_csi_eof_callback,
+                             0, "Mxc Camera", NULL);
+       if (err != 0) {
+               printk(KERN_ERR "Error IPU_IRQ_SENSOR_EOF \n");
+               return err;
+       }
+#else
+       callback_eof_flag = 0;
+       buffer_num = 0;
+
+       ipu_clear_irq(IPU_IRQ_CSI0_OUT_EOF);
+       err = ipu_request_irq(IPU_IRQ_CSI0_OUT_EOF, prp_still_callback,
+                             0, "Mxc Camera", cam);
+       if (err != 0) {
+               printk(KERN_ERR "Error registering irq.\n");
+               return err;
+       }
+
+       ipu_select_buffer(CSI_MEM, IPU_OUTPUT_BUFFER, 0);
+       ipu_enable_channel(CSI_MEM);
+       ipu_enable_csi(cam->csi);
+#endif
+
+       return err;
+}
+
+/*!
+ * stop csi->mem encoder task
+ * @param private       struct cam_data * mxc capture instance
+ *
+ * @return  status
+ */
+static int prp_still_stop(void *private)
+{
+       cam_data *cam = (cam_data *) private;
+       int err = 0;
+
+#ifdef CONFIG_MXC_IPU_V1
+       ipu_free_irq(IPU_IRQ_SENSOR_EOF, NULL);
+       ipu_free_irq(IPU_IRQ_SENSOR_OUT_EOF, cam);
+#else
+       ipu_free_irq(IPU_IRQ_CSI0_OUT_EOF, cam);
+#endif
+
+       ipu_disable_csi(cam->csi);
+       ipu_disable_channel(CSI_MEM, true);
+       ipu_uninit_channel(CSI_MEM);
+       ipu_csi_enable_mclk_if(CSI_MCLK_RAW, cam->csi, false, false);
+
+       return err;
+}
+
+/*!
+ * function to select CSI_MEM as the working path
+ *
+ * @param private       struct cam_data * mxc capture instance
+ *
+ * @return  status
+ */
+int prp_still_select(void *private)
+{
+       cam_data *cam = (cam_data *) private;
+
+       if (cam) {
+               cam->csi_start = prp_still_start;
+               cam->csi_stop = prp_still_stop;
+       }
+
+       return 0;
+}
+
+/*!
+ * function to de-select CSI_MEM as the working path
+ *
+ * @param private       struct cam_data * mxc capture instance
+ *
+ * @return  status
+ */
+int prp_still_deselect(void *private)
+{
+       cam_data *cam = (cam_data *) private;
+       int err = 0;
+
+       err = prp_still_stop(cam);
+
+       if (cam) {
+               cam->csi_start = NULL;
+               cam->csi_stop = NULL;
+       }
+
+       return err;
+}
+
+/*!
+ * Init the Encorder channels
+ *
+ * @return  Error code indicating success or failure
+ */
+__init int prp_still_init(void)
+{
+       return 0;
+}
+
+/*!
+ * Deinit the Encorder channels
+ *
+ */
+void __exit prp_still_exit(void)
+{
+}
+
+module_init(prp_still_init);
+module_exit(prp_still_exit);
+
+EXPORT_SYMBOL(prp_still_select);
+EXPORT_SYMBOL(prp_still_deselect);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("IPU PRP STILL IMAGE Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/mt9v111.c b/drivers/media/video/mxc/capture/mt9v111.c
new file mode 100644 (file)
index 0000000..4305c56
--- /dev/null
@@ -0,0 +1,1076 @@
+/*
+ * 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 mt9v111.c
+ *
+ * @brief mt9v111 camera driver functions
+ *
+ * @ingroup Camera
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/clk.h>
+#include <media/v4l2-int-device.h>
+#include "mxc_v4l2_capture.h"
+#include "mt9v111.h"
+
+#ifdef MT9V111_DEBUG
+static u16 testpattern;
+#endif
+
+static mt9v111_conf mt9v111_device;
+
+/*!
+ * Holds the current frame rate.
+ */
+static int reset_frame_rate = MT9V111_FRAME_RATE;
+
+struct sensor {
+       const struct mt9v111_platform_data *platform_data;
+       struct v4l2_int_device *v4l2_int_device;
+       struct i2c_client *i2c_client;
+       struct v4l2_pix_format pix;
+       struct v4l2_captureparm streamcap;
+       bool on;
+
+       /* control settings */
+       int brightness;
+       int hue;
+       int contrast;
+       int saturation;
+       int red;
+       int green;
+       int blue;
+       int ae_mode;
+
+} mt9v111_data;
+
+extern void gpio_sensor_active(void);
+extern void gpio_sensor_inactive(void);
+
+static int mt9v111_probe(struct i2c_client *client,
+                        const struct i2c_device_id *id);
+static int mt9v111_remove(struct i2c_client *client);
+
+static const struct i2c_device_id mt9v111_id[] = {
+       {"mt9v111", 0},
+       {},
+};
+
+MODULE_DEVICE_TABLE(i2c, mt9v111_id);
+
+static struct i2c_driver mt9v111_i2c_driver = {
+       .driver = {
+                  .owner = THIS_MODULE,
+                  .name = "mt9v111",
+                  },
+       .probe = mt9v111_probe,
+       .remove = mt9v111_remove,
+       .id_table = mt9v111_id,
+/* To add power management add .suspend and .resume functions */
+};
+
+/*
+ * Function definitions
+ */
+
+#ifdef MT9V111_DEBUG
+static inline int mt9v111_read_reg(u8 reg)
+{
+       int val = i2c_smbus_read_word_data(mt9v111_data.i2c_client, reg);
+       if (val != -1)
+               val = cpu_to_be16(val);
+       return val;
+}
+#endif
+
+/*!
+ * Writes to the register via I2C.
+ */
+static inline int mt9v111_write_reg(u8 reg, u16 val)
+{
+       pr_debug("In mt9v111_write_reg (0x%x, 0x%x)\n", reg, val);
+       pr_debug("   write reg %x val %x.\n", reg, val);
+
+       return i2c_smbus_write_word_data(mt9v111_data.i2c_client,
+                                        reg, cpu_to_be16(val));
+}
+
+/*!
+ * Initialize mt9v111_sensor_lib
+ * Libarary for Sensor configuration through I2C
+ *
+ * @param       coreReg       Core Registers
+ * @param       ifpReg        IFP Register
+ *
+ * @return status
+ */
+static u8 mt9v111_sensor_lib(mt9v111_coreReg *coreReg, mt9v111_IFPReg *ifpReg)
+{
+       u8 reg;
+       u16 data;
+       u8 error = 0;
+
+       pr_debug("In mt9v111_sensor_lib\n");
+
+       /*
+        * setup to IFP registers
+        */
+       reg = MT9V111I_ADDR_SPACE_SEL;
+       data = ifpReg->addrSpaceSel;
+       mt9v111_write_reg(reg, data);
+
+       /* Operation Mode Control */
+       reg = MT9V111I_MODE_CONTROL;
+       data = ifpReg->modeControl;
+       mt9v111_write_reg(reg, data);
+
+       /* Output format */
+       reg = MT9V111I_FORMAT_CONTROL;
+       data = ifpReg->formatControl;   /* Set bit 12 */
+       mt9v111_write_reg(reg, data);
+
+       /* AE limit 4 */
+       reg = MT9V111I_SHUTTER_WIDTH_LIMIT_AE;
+       data = ifpReg->gainLimitAE;
+       mt9v111_write_reg(reg, data);
+
+       reg = MT9V111I_OUTPUT_FORMAT_CTRL2;
+       data = ifpReg->outputFormatCtrl2;
+       mt9v111_write_reg(reg, data);
+
+       reg = MT9V111I_AE_SPEED;
+       data = ifpReg->AESpeed;
+       mt9v111_write_reg(reg, data);
+
+       /* output image size */
+       reg = MT9V111i_H_PAN;
+       data = 0x8000 | ifpReg->HPan;
+       mt9v111_write_reg(reg, data);
+
+       reg = MT9V111i_H_ZOOM;
+       data = 0x8000 | ifpReg->HZoom;
+       mt9v111_write_reg(reg, data);
+
+       reg = MT9V111i_H_SIZE;
+       data = 0x8000 | ifpReg->HSize;
+       mt9v111_write_reg(reg, data);
+
+       reg = MT9V111i_V_PAN;
+       data = 0x8000 | ifpReg->VPan;
+       mt9v111_write_reg(reg, data);
+
+       reg = MT9V111i_V_ZOOM;
+       data = 0x8000 | ifpReg->VZoom;
+       mt9v111_write_reg(reg, data);
+
+       reg = MT9V111i_V_SIZE;
+       data = 0x8000 | ifpReg->VSize;
+       mt9v111_write_reg(reg, data);
+
+       reg = MT9V111i_H_PAN;
+       data = ~0x8000 & ifpReg->HPan;
+       mt9v111_write_reg(reg, data);
+#if 0
+       reg = MT9V111I_UPPER_SHUTTER_DELAY_LIM;
+       data = ifpReg->upperShutterDelayLi;
+       mt9v111_write_reg(reg, data);
+
+       reg = MT9V111I_SHUTTER_60;
+       data = ifpReg->shutter_width_60;
+       mt9v111_write_reg(reg, data);
+
+       reg = MT9V111I_SEARCH_FLICK_60;
+       data = ifpReg->search_flicker_60;
+       mt9v111_write_reg(reg, data);
+#endif
+
+       /*
+        * setup to sensor core registers
+        */
+       reg = MT9V111I_ADDR_SPACE_SEL;
+       data = coreReg->addressSelect;
+       mt9v111_write_reg(reg, data);
+
+       /* enable changes and put the Sync bit on */
+       reg = MT9V111S_OUTPUT_CTRL;
+       data = MT9V111S_OUTCTRL_SYNC | MT9V111S_OUTCTRL_CHIP_ENABLE | 0x3000;
+       mt9v111_write_reg(reg, data);
+
+       /* min PIXCLK - Default */
+       reg = MT9V111S_PIXEL_CLOCK_SPEED;
+       data = coreReg->pixelClockSpeed;
+       mt9v111_write_reg(reg, data);
+
+       /* Setup image flipping / Dark rows / row/column skip */
+       reg = MT9V111S_READ_MODE;
+       data = coreReg->readMode;
+       mt9v111_write_reg(reg, data);
+
+       /* zoom 0 */
+       reg = MT9V111S_DIGITAL_ZOOM;
+       data = coreReg->digitalZoom;
+       mt9v111_write_reg(reg, data);
+
+       /* min H-blank */
+       reg = MT9V111S_HOR_BLANKING;
+       data = coreReg->horizontalBlanking;
+       mt9v111_write_reg(reg, data);
+
+       /* min V-blank */
+       reg = MT9V111S_VER_BLANKING;
+       data = coreReg->verticalBlanking;
+       mt9v111_write_reg(reg, data);
+
+       reg = MT9V111S_SHUTTER_WIDTH;
+       data = coreReg->shutterWidth;
+       mt9v111_write_reg(reg, data);
+
+       reg = MT9V111S_SHUTTER_DELAY;
+       data = ifpReg->upperShutterDelayLi;
+       mt9v111_write_reg(reg, data);
+
+       /* changes become effective */
+       reg = MT9V111S_OUTPUT_CTRL;
+       data = MT9V111S_OUTCTRL_CHIP_ENABLE | 0x3000;
+       mt9v111_write_reg(reg, data);
+
+       return error;
+}
+
+/*!
+ * MT9V111 frame rate calculate
+ *
+ * @param frame_rate       int *
+ * @param mclk             int
+ * @return  None
+ */
+static void mt9v111_rate_cal(int *frame_rate, int mclk)
+{
+       int num_clock_per_row;
+       int max_rate = 0;
+
+       pr_debug("In mt9v111_rate_cal\n");
+
+       num_clock_per_row = (MT9V111_MAX_WIDTH + 114 + MT9V111_HORZBLANK_MIN)
+                       * 2;
+       max_rate = mclk / (num_clock_per_row *
+                          (MT9V111_MAX_HEIGHT + MT9V111_VERTBLANK_DEFAULT));
+
+       if ((*frame_rate > max_rate) || (*frame_rate == 0)) {
+               *frame_rate = max_rate;
+       }
+
+       mt9v111_device.coreReg->verticalBlanking
+           = mclk / (*frame_rate * num_clock_per_row) - MT9V111_MAX_HEIGHT;
+
+       reset_frame_rate = *frame_rate;
+}
+
+/*!
+ * MT9V111 sensor configuration
+ */
+void mt9v111_config(void)
+{
+       pr_debug("In mt9v111_config\n");
+
+       mt9v111_device.coreReg->addressSelect = MT9V111I_SEL_SCA;
+       mt9v111_device.ifpReg->addrSpaceSel = MT9V111I_SEL_IFP;
+
+       mt9v111_device.coreReg->windowHeight = MT9V111_WINHEIGHT;
+       mt9v111_device.coreReg->windowWidth = MT9V111_WINWIDTH;
+       mt9v111_device.coreReg->zoomColStart = 0;
+       mt9v111_device.coreReg->zomRowStart = 0;
+       mt9v111_device.coreReg->digitalZoom = 0x0;
+
+       mt9v111_device.coreReg->verticalBlanking = MT9V111_VERTBLANK_DEFAULT;
+       mt9v111_device.coreReg->horizontalBlanking = MT9V111_HORZBLANK_MIN;
+       mt9v111_device.coreReg->pixelClockSpeed = 0;
+       mt9v111_device.coreReg->readMode = 0xd0a1;
+
+       mt9v111_device.ifpReg->outputFormatCtrl2 = 0;
+       mt9v111_device.ifpReg->gainLimitAE = 0x300;
+       mt9v111_device.ifpReg->AESpeed = 0x80;
+
+       /* here is the default value */
+       mt9v111_device.ifpReg->formatControl = 0xc800;
+       mt9v111_device.ifpReg->modeControl = 0x708e;
+       mt9v111_device.ifpReg->awbSpeed = 0x4514;
+       mt9v111_device.coreReg->shutterWidth = 0xf8;
+
+       /* output size */
+       mt9v111_device.ifpReg->HPan = 0;
+       mt9v111_device.ifpReg->HZoom = MT9V111_MAX_WIDTH;
+       mt9v111_device.ifpReg->HSize = MT9V111_MAX_WIDTH;
+       mt9v111_device.ifpReg->VPan = 0;
+       mt9v111_device.ifpReg->VZoom = MT9V111_MAX_HEIGHT;
+       mt9v111_device.ifpReg->VSize = MT9V111_MAX_HEIGHT;
+}
+
+/*!
+ * mt9v111 sensor set saturtionn
+ *
+ * @param saturation   int
+
+ * @return  Error code of 0.
+ */
+static int mt9v111_set_saturation(int saturation)
+{
+       u8 reg;
+       u16 data;
+       pr_debug("In mt9v111_set_saturation(%d)\n",
+               saturation);
+
+       switch (saturation) {
+       case 150:
+               mt9v111_device.ifpReg->awbSpeed = 0x6D14;
+               break;
+       case 100:
+               mt9v111_device.ifpReg->awbSpeed = 0x4514;
+               break;
+       case 75:
+               mt9v111_device.ifpReg->awbSpeed = 0x4D14;
+               break;
+       case 50:
+               mt9v111_device.ifpReg->awbSpeed = 0x5514;
+               break;
+       case 37:
+               mt9v111_device.ifpReg->awbSpeed = 0x5D14;
+               break;
+       case 25:
+               mt9v111_device.ifpReg->awbSpeed = 0x6514;
+               break;
+       default:
+               mt9v111_device.ifpReg->awbSpeed = 0x4514;
+               break;
+       }
+
+       reg = MT9V111I_ADDR_SPACE_SEL;
+       data = mt9v111_device.ifpReg->addrSpaceSel;
+       mt9v111_write_reg(reg, data);
+
+       /* Operation Mode Control */
+       reg = MT9V111I_AWB_SPEED;
+       data = mt9v111_device.ifpReg->awbSpeed;
+       mt9v111_write_reg(reg, data);
+
+       return 0;
+}
+
+/*!
+ * mt9v111 sensor set Auto Exposure measurement window mode configuration
+ *
+ * @param ae_mode      int
+ * @return  Error code of 0 (no Error)
+ */
+static int mt9v111_set_ae_mode(int ae_mode)
+{
+       u8 reg;
+       u16 data;
+
+       pr_debug("In mt9v111_set_ae_mode(%d)\n",
+               ae_mode);
+
+       /* Currently this driver only supports auto and manual exposure
+        * modes. */
+       if ((ae_mode > 1) || (ae_mode << 0))
+               return -EPERM;
+
+       /*
+        * The auto exposure is set in bit 14.
+        * Other values are set for:
+        *  -on the fly defect correction is on (bit 13).
+        *  -aperature correction knee enabled (bit 12).
+        *  -ITU_R BT656 synchronization codes are embedded in the image (bit 7)
+        *  -AE measurement window is weighted sum of large and center windows
+        *     (bits 2-3).
+        *  -auto white balance is on (bit 1).
+        *  -normal color processing (bit 4 = 0).
+        */
+       /* V4L2_EXPOSURE_AUTO = 0; needs register setting of 0x708E */
+       /* V4L2_EXPOSURE_MANUAL = 1 needs register setting of 0x308E */
+       mt9v111_device.ifpReg->modeControl &= 0x3fff;
+       mt9v111_device.ifpReg->modeControl |= (ae_mode & 0x03) << 14;
+       mt9v111_data.ae_mode = ae_mode;
+
+       reg = MT9V111I_ADDR_SPACE_SEL;
+       data = mt9v111_device.ifpReg->addrSpaceSel;
+       mt9v111_write_reg(reg, data);
+
+       reg = MT9V111I_MODE_CONTROL;
+       data = mt9v111_device.ifpReg->modeControl;
+       mt9v111_write_reg(reg, data);
+
+       return 0;
+}
+
+/*!
+ * mt9v111 sensor get AE measurement window mode configuration
+ *
+ * @param ae_mode      int *
+ * @return  None
+ */
+static void mt9v111_get_ae_mode(int *ae_mode)
+{
+       pr_debug("In mt9v111_get_ae_mode(%d)\n", *ae_mode);
+
+       if (ae_mode != NULL) {
+               *ae_mode = (mt9v111_device.ifpReg->modeControl & 0xc) >> 2;
+       }
+}
+
+#ifdef MT9V111_DEBUG
+/*!
+ * Set sensor to test mode, which will generate test pattern.
+ *
+ * @return none
+ */
+static void mt9v111_test_pattern(bool flag)
+{
+       u16 data;
+
+       /* switch to sensor registers */
+       mt9v111_write_reg(MT9V111I_ADDR_SPACE_SEL, MT9V111I_SEL_SCA);
+
+       if (flag == true) {
+               testpattern = MT9V111S_OUTCTRL_TEST_MODE;
+
+               data = mt9v111_read_reg(MT9V111S_ROW_NOISE_CTRL) & 0xBF;
+               mt9v111_write_reg(MT9V111S_ROW_NOISE_CTRL, data);
+
+               mt9v111_write_reg(MT9V111S_TEST_DATA, 0);
+
+               /* changes take effect */
+               data = MT9V111S_OUTCTRL_CHIP_ENABLE | testpattern | 0x3000;
+               mt9v111_write_reg(MT9V111S_OUTPUT_CTRL, data);
+       } else {
+               testpattern = 0;
+
+               data = mt9v111_read_reg(MT9V111S_ROW_NOISE_CTRL) | 0x40;
+               mt9v111_write_reg(MT9V111S_ROW_NOISE_CTRL, data);
+
+               /* changes take effect */
+               data = MT9V111S_OUTCTRL_CHIP_ENABLE | testpattern | 0x3000;
+               mt9v111_write_reg(MT9V111S_OUTPUT_CTRL, data);
+       }
+}
+#endif
+
+
+/* --------------- IOCTL functions from v4l2_int_ioctl_desc --------------- */
+
+/*!
+ * ioctl_g_ifparm - V4L2 sensor interface handler for vidioc_int_g_ifparm_num
+ * s: pointer to standard V4L2 device structure
+ * p: pointer to standard V4L2 vidioc_int_g_ifparm_num ioctl structure
+ *
+ * Gets slave interface parameters.
+ * Calculates the required xclk value to support the requested
+ * clock parameters in p.  This value is returned in the p
+ * parameter.
+ *
+ * vidioc_int_g_ifparm returns platform-specific information about the
+ * interface settings used by the sensor.
+ *
+ * Given the image capture format in pix, the nominal frame period in
+ * timeperframe, calculate the required xclk frequency.
+ *
+ * Called on open.
+ */
+static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p)
+{
+       pr_debug("In mt9v111:ioctl_g_ifparm\n");
+
+       if (s == NULL) {
+               pr_err("   ERROR!! no slave device set!\n");
+               return -1;
+       }
+
+       memset(p, 0, sizeof(*p));
+       p->u.bt656.clock_curr = MT9V111_MCLK;
+       p->if_type = V4L2_IF_TYPE_BT656;
+       p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT;
+       p->u.bt656.clock_min = MT9V111_CLK_MIN;
+       p->u.bt656.clock_max = MT9V111_CLK_MAX;
+
+       return 0;
+}
+
+/*!
+ * Sets the camera power.
+ *
+ * s  pointer to the camera device
+ * on if 1, power is to be turned on.  0 means power is to be turned off
+ *
+ * ioctl_s_power - V4L2 sensor interface handler for vidioc_int_s_power_num
+ * @s: pointer to standard V4L2 device structure
+ * @on: power state to which device is to be set
+ *
+ * Sets devices power state to requrested state, if possible.
+ * This is called on suspend and resume.
+ */
+static int ioctl_s_power(struct v4l2_int_device *s, int on)
+{
+       struct sensor *sensor = s->priv;
+
+       pr_debug("In mt9v111:ioctl_s_power\n");
+
+       sensor->on = on;
+
+       if (on)
+               gpio_sensor_active();
+       else
+               gpio_sensor_inactive();
+
+       return 0;
+}
+
+/*!
+ * ioctl_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure
+ *
+ * Returns the sensor's video CAPTURE parameters.
+ */
+static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+       int ret = 0;
+       struct v4l2_captureparm *cparm = &a->parm.capture;
+       /* s->priv points to mt9v111_data */
+
+       pr_debug("In mt9v111:ioctl_g_parm\n");
+
+       switch (a->type) {
+       /* This is the only case currently handled. */
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               pr_debug("   type is V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
+               memset(a, 0, sizeof(*a));
+               a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+               cparm->capability = mt9v111_data.streamcap.capability;
+               cparm->timeperframe =
+                               mt9v111_data.streamcap.timeperframe;
+               cparm->capturemode = mt9v111_data.streamcap.capturemode;
+               ret = 0;
+               break;
+
+       /* These are all the possible cases. */
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+       case V4L2_BUF_TYPE_VBI_OUTPUT:
+       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+               pr_err("   type is not V4L2_BUF_TYPE_VIDEO_CAPTURE " \
+                       "but %d\n", a->type);
+               ret = -EINVAL;
+               break;
+
+       default:
+               pr_err("   type is unknown - %d\n", a->type);
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+/*!
+ * ioctl_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure
+ *
+ * Configures the sensor to use the input parameters, if possible.  If
+ * not possible, reverts to the old parameters and returns the
+ * appropriate error code.
+ */
+static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+       int ret = 0;
+       struct v4l2_captureparm *cparm = &a->parm.capture;
+       /* s->priv points to mt9v111_data */
+
+       pr_debug("In mt9v111:ioctl_s_parm\n");
+
+       switch (a->type) {
+       /* This is the only case currently handled. */
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               pr_debug("   type is V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
+
+               /* Check that the new frame rate is allowed.
+                * Changing the frame rate is not allowed on this
+                *camera. */
+               if (cparm->timeperframe.denominator !=
+                   mt9v111_data.streamcap.timeperframe.denominator) {
+                       pr_err("ERROR: mt9v111: ioctl_s_parm: " \
+                              "This camera does not allow frame rate "
+                              "changes.\n");
+                       ret = -EINVAL;
+               } else {
+                       mt9v111_data.streamcap.timeperframe =
+                                               cparm->timeperframe;
+                     /* Call any camera functions to match settings. */
+               }
+
+               /* Check that new capture mode is supported. */
+               if ((cparm->capturemode != 0) &&
+                   !(cparm->capturemode & V4L2_MODE_HIGHQUALITY)) {
+                       pr_err("ERROR: mt9v111: ioctl_s_parm: " \
+                               "unsupported capture mode\n");
+                       ret  = -EINVAL;
+               } else {
+                       mt9v111_data.streamcap.capturemode =
+                                               cparm->capturemode;
+                     /* Call any camera functions to match settings. */
+                     /* Right now this camera only supports 1 mode. */
+               }
+               break;
+
+       /* These are all the possible cases. */
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+       case V4L2_BUF_TYPE_VBI_OUTPUT:
+       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+               pr_err("   type is not V4L2_BUF_TYPE_VIDEO_CAPTURE " \
+                       "but %d\n", a->type);
+               ret = -EINVAL;
+               break;
+
+       default:
+               pr_err("   type is unknown - %d\n", a->type);
+               ret = -EINVAL;
+               break;
+       }
+
+       return 0;
+}
+
+/*!
+ * ioctl_g_fmt_cap - V4L2 sensor interface handler for ioctl_g_fmt_cap
+ * @s: pointer to standard V4L2 device structure
+ * @f: pointer to standard V4L2 v4l2_format structure
+ *
+ * Returns the sensor's current pixel format in the v4l2_format
+ * parameter.
+ */
+static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
+{
+       struct sensor *sensor = s->priv;
+       /* s->priv points to mt9v111_data */
+
+       pr_debug("In mt9v111:ioctl_g_fmt_cap.\n");
+       pr_debug("   Returning size of %dx%d\n",
+               sensor->pix.width, sensor->pix.height);
+
+       f->fmt.pix = sensor->pix;
+
+       return 0;
+}
+
+/*!
+ * ioctl_queryctrl - V4L2 sensor interface handler for VIDIOC_QUERYCTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @qc: standard V4L2 VIDIOC_QUERYCTRL ioctl structure
+ *
+ * If the requested control is supported, returns the control information
+ * from the video_control[] array.  Otherwise, returns -EINVAL if the
+ * control is not supported.
+ */
+static int ioctl_queryctrl(struct v4l2_int_device *s, struct v4l2_queryctrl *qc)
+{
+       pr_debug("In mt9v111:ioctl_queryctrl\n");
+
+       return 0;
+}
+
+/*!
+ * ioctl_g_ctrl - V4L2 sensor interface handler for VIDIOC_G_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @vc: standard V4L2 VIDIOC_G_CTRL ioctl structure
+ *
+ * If the requested control is supported, returns the control's current
+ * value from the video_control[] array.  Otherwise, returns -EINVAL
+ * if the control is not supported.
+ */
+static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc)
+{
+       pr_debug("In mt9v111:ioctl_g_ctrl\n");
+
+       switch (vc->id) {
+       case V4L2_CID_BRIGHTNESS:
+               pr_debug("   V4L2_CID_BRIGHTNESS\n");
+               vc->value = mt9v111_data.brightness;
+               break;
+       case V4L2_CID_CONTRAST:
+               pr_debug("   V4L2_CID_CONTRAST\n");
+               vc->value = mt9v111_data.contrast;
+               break;
+       case V4L2_CID_SATURATION:
+               pr_debug("   V4L2_CID_SATURATION\n");
+               vc->value = mt9v111_data.saturation;
+               break;
+       case V4L2_CID_HUE:
+               pr_debug("   V4L2_CID_HUE\n");
+               vc->value = mt9v111_data.hue;
+               break;
+       case V4L2_CID_AUTO_WHITE_BALANCE:
+               pr_debug(
+                       "   V4L2_CID_AUTO_WHITE_BALANCE\n");
+               vc->value = 0;
+               break;
+       case V4L2_CID_DO_WHITE_BALANCE:
+               pr_debug(
+                       "   V4L2_CID_DO_WHITE_BALANCE\n");
+               vc->value = 0;
+               break;
+       case V4L2_CID_RED_BALANCE:
+               pr_debug("   V4L2_CID_RED_BALANCE\n");
+               vc->value = mt9v111_data.red;
+               break;
+       case V4L2_CID_BLUE_BALANCE:
+               pr_debug("   V4L2_CID_BLUE_BALANCE\n");
+               vc->value = mt9v111_data.blue;
+               break;
+       case V4L2_CID_GAMMA:
+               pr_debug("   V4L2_CID_GAMMA\n");
+               vc->value = 0;
+               break;
+       case V4L2_CID_EXPOSURE:
+               pr_debug("   V4L2_CID_EXPOSURE\n");
+               vc->value = mt9v111_data.ae_mode;
+               break;
+       case V4L2_CID_AUTOGAIN:
+               pr_debug("   V4L2_CID_AUTOGAIN\n");
+               vc->value = 0;
+               break;
+       case V4L2_CID_GAIN:
+               pr_debug("   V4L2_CID_GAIN\n");
+               vc->value = 0;
+               break;
+       case V4L2_CID_HFLIP:
+               pr_debug("   V4L2_CID_HFLIP\n");
+               vc->value = 0;
+               break;
+       case V4L2_CID_VFLIP:
+               pr_debug("   V4L2_CID_VFLIP\n");
+               vc->value = 0;
+               break;
+       default:
+               pr_debug("   Default case\n");
+               return -EPERM;
+               break;
+       }
+
+       return 0;
+}
+
+/*!
+ * ioctl_s_ctrl - V4L2 sensor interface handler for VIDIOC_S_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure
+ *
+ * If the requested control is supported, sets the control's current
+ * value in HW (and updates the video_control[] array).  Otherwise,
+ * returns -EINVAL if the control is not supported.
+ */
+static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc)
+{
+       int retval = 0;
+
+       pr_debug("In mt9v111:ioctl_s_ctrl %d\n",
+               vc->id);
+
+       switch (vc->id) {
+       case V4L2_CID_BRIGHTNESS:
+               pr_debug("   V4L2_CID_BRIGHTNESS\n");
+               break;
+       case V4L2_CID_CONTRAST:
+               pr_debug("   V4L2_CID_CONTRAST\n");
+               break;
+       case V4L2_CID_SATURATION:
+               pr_debug("   V4L2_CID_SATURATION\n");
+               retval = mt9v111_set_saturation(vc->value);
+               break;
+       case V4L2_CID_HUE:
+               pr_debug("   V4L2_CID_HUE\n");
+               break;
+       case V4L2_CID_AUTO_WHITE_BALANCE:
+               pr_debug(
+                       "   V4L2_CID_AUTO_WHITE_BALANCE\n");
+               break;
+       case V4L2_CID_DO_WHITE_BALANCE:
+               pr_debug(
+                       "   V4L2_CID_DO_WHITE_BALANCE\n");
+               break;
+       case V4L2_CID_RED_BALANCE:
+               pr_debug("   V4L2_CID_RED_BALANCE\n");
+               break;
+       case V4L2_CID_BLUE_BALANCE:
+               pr_debug("   V4L2_CID_BLUE_BALANCE\n");
+               break;
+       case V4L2_CID_GAMMA:
+               pr_debug("   V4L2_CID_GAMMA\n");
+               break;
+       case V4L2_CID_EXPOSURE:
+               pr_debug("   V4L2_CID_EXPOSURE\n");
+               retval = mt9v111_set_ae_mode(vc->value);
+               break;
+       case V4L2_CID_AUTOGAIN:
+               pr_debug("   V4L2_CID_AUTOGAIN\n");
+               break;
+       case V4L2_CID_GAIN:
+               pr_debug("   V4L2_CID_GAIN\n");
+               break;
+       case V4L2_CID_HFLIP:
+               pr_debug("   V4L2_CID_HFLIP\n");
+               break;
+       case V4L2_CID_VFLIP:
+               pr_debug("   V4L2_CID_VFLIP\n");
+               break;
+       default:
+               pr_debug("   Default case\n");
+               retval = -EPERM;
+               break;
+       }
+
+       return retval;
+}
+
+/*!
+ * ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT
+ * @s: pointer to standard V4L2 device structure
+ */
+static int ioctl_init(struct v4l2_int_device *s)
+{
+       pr_debug("In mt9v111:ioctl_init\n");
+
+       return 0;
+}
+
+/*!
+ * ioctl_dev_init - V4L2 sensor interface handler for vidioc_int_dev_init_num
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Initialise the device when slave attaches to the master.
+ */
+static int ioctl_dev_init(struct v4l2_int_device *s)
+{
+       uint32_t clock_rate = MT9V111_MCLK;
+
+       pr_debug("In mt9v111:ioctl_dev_init\n");
+
+       gpio_sensor_active();
+
+       set_mclk_rate(&clock_rate);
+       mt9v111_rate_cal(&reset_frame_rate, clock_rate);
+       mt9v111_sensor_lib(mt9v111_device.coreReg, mt9v111_device.ifpReg);
+
+       return 0;
+}
+
+/*!
+ * This structure defines all the ioctls for this module and links them to the
+ * enumeration.
+ */
+static struct v4l2_int_ioctl_desc mt9v111_ioctl_desc[] = {
+
+       {vidioc_int_dev_init_num, (v4l2_int_ioctl_func *)ioctl_dev_init},
+
+       /*!
+        * Delinitialise the dev. at slave detach.
+        * The complement of ioctl_dev_init.
+        */
+/*     {vidioc_int_dev_exit_num, (v4l2_int_ioctl_func *) ioctl_dev_exit}, */
+
+       {vidioc_int_s_power_num, (v4l2_int_ioctl_func *) ioctl_s_power},
+       {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func *) ioctl_g_ifparm},
+/*     {vidioc_int_g_needs_reset_num,
+                               (v4l2_int_ioctl_func *) ioctl_g_needs_reset}, */
+/*     {vidioc_int_reset_num, (v4l2_int_ioctl_func *) ioctl_reset}, */
+       {vidioc_int_init_num, (v4l2_int_ioctl_func *) ioctl_init},
+
+       /*!
+        * VIDIOC_ENUM_FMT ioctl for the CAPTURE buffer type.
+        */
+/*     {vidioc_int_enum_fmt_cap_num,
+                               (v4l2_int_ioctl_func *) ioctl_enum_fmt_cap}, */
+
+       /*!
+        * VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type.
+        * This ioctl is used to negotiate the image capture size and
+        * pixel format without actually making it take effect.
+        */
+/*     {vidioc_int_try_fmt_cap_num,
+                               (v4l2_int_ioctl_func *) ioctl_try_fmt_cap}, */
+
+       {vidioc_int_g_fmt_cap_num, (v4l2_int_ioctl_func *) ioctl_g_fmt_cap},
+
+       /*!
+        * If the requested format is supported, configures the HW to use that
+        * format, returns error code if format not supported or HW can't be
+        * correctly configured.
+        */
+/*     {vidioc_int_s_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_s_fmt_cap}, */
+
+       {vidioc_int_g_parm_num, (v4l2_int_ioctl_func *) ioctl_g_parm},
+       {vidioc_int_s_parm_num, (v4l2_int_ioctl_func *) ioctl_s_parm},
+/*     {vidioc_int_queryctrl_num, (v4l2_int_ioctl_func *) ioctl_queryctrl}, */
+       {vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func *) ioctl_g_ctrl},
+       {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *) ioctl_s_ctrl},
+};
+
+static struct v4l2_int_slave mt9v111_slave = {
+       .ioctls = mt9v111_ioctl_desc,
+       .num_ioctls = ARRAY_SIZE(mt9v111_ioctl_desc),
+};
+
+static struct v4l2_int_device mt9v111_int_device = {
+       .module = THIS_MODULE,
+       .name = "mt9v111",
+       .type = v4l2_int_type_slave,
+       .u = {
+               .slave = &mt9v111_slave,
+               },
+};
+
+/*!
+ * mt9v111 I2C probe function
+ * Function set in i2c_driver struct.
+ * Called by insmod mt9v111_camera.ko.
+ *
+ * @return  Error code indicating success or failure
+ */
+static int mt9v111_probe(struct i2c_client *client,
+                        const struct i2c_device_id *id)
+{
+       int retval;
+
+       pr_debug("In mt9v111_probe  device id is %s\n", id->name);
+
+       /* Set initial values for the sensor struct. */
+       memset(&mt9v111_data, 0, sizeof(mt9v111_data));
+       mt9v111_data.i2c_client = client;
+       pr_debug("   client name is %s\n", client->name);
+       mt9v111_data.pix.pixelformat = V4L2_PIX_FMT_UYVY;
+       mt9v111_data.pix.width = MT9V111_MAX_WIDTH;
+       mt9v111_data.pix.height = MT9V111_MAX_HEIGHT;
+       mt9v111_data.streamcap.capability = 0; /* No higher resolution or frame
+                                               * frame rate changes supported.
+                                               */
+       mt9v111_data.streamcap.timeperframe.denominator = MT9V111_FRAME_RATE;
+       mt9v111_data.streamcap.timeperframe.numerator = 1;
+
+       mt9v111_int_device.priv = &mt9v111_data;
+
+       pr_debug("   type is %d (expect %d)\n",
+               mt9v111_int_device.type, v4l2_int_type_slave);
+       pr_debug("   num ioctls is %d\n",
+               mt9v111_int_device.u.slave->num_ioctls);
+
+       /* This function attaches this structure to the /dev/video0 device.
+        * The pointer in priv points to the mt9v111_data structure here.*/
+       retval = v4l2_int_device_register(&mt9v111_int_device);
+
+       return retval;
+}
+
+/*!
+ * Function set in i2c_driver struct.
+ * Called on rmmod mt9v111_camera.ko
+ */
+static int mt9v111_remove(struct i2c_client *client)
+{
+       pr_debug("In mt9v111_remove\n");
+
+       v4l2_int_device_unregister(&mt9v111_int_device);
+       return 0;
+}
+
+/*!
+ * MT9V111 init function.
+ * Called by insmod mt9v111_camera.ko.
+ *
+ * @return  Error code indicating success or failure
+ */
+static __init int mt9v111_init(void)
+{
+       u8 err;
+
+       pr_debug("In mt9v111_init\n");
+
+       /* Allocate memory for state structures. */
+       mt9v111_device.coreReg = (mt9v111_coreReg *)
+                               kmalloc(sizeof(mt9v111_coreReg), GFP_KERNEL);
+       if (!mt9v111_device.coreReg)
+               return -1;
+       memset(mt9v111_device.coreReg, 0, sizeof(mt9v111_coreReg));
+
+       mt9v111_device.ifpReg = (mt9v111_IFPReg *)
+                               kmalloc(sizeof(mt9v111_IFPReg), GFP_KERNEL);
+       if (!mt9v111_device.ifpReg) {
+               kfree(mt9v111_device.coreReg);
+               mt9v111_device.coreReg = NULL;
+               return -1;
+       }
+       memset(mt9v111_device.ifpReg, 0, sizeof(mt9v111_IFPReg));
+
+       /* Set contents of the just created structures. */
+       mt9v111_config();
+
+       /* Tells the i2c driver what functions to call for this driver. */
+       err = i2c_add_driver(&mt9v111_i2c_driver);
+       if (err != 0)
+               pr_err("%s:driver registration failed, error=%d \n",
+                      __func__, err);
+
+       return err;
+}
+
+/*!
+ * MT9V111 cleanup function.
+ * Called on rmmod mt9v111_camera.ko
+ *
+ * @return  Error code indicating success or failure
+ */
+static void __exit mt9v111_clean(void)
+{
+       pr_debug("In mt9v111_clean()\n");
+
+       i2c_del_driver(&mt9v111_i2c_driver);
+       gpio_sensor_inactive();
+
+       if (mt9v111_device.coreReg) {
+               kfree(mt9v111_device.coreReg);
+               mt9v111_device.coreReg = NULL;
+       }
+
+       if (mt9v111_device.ifpReg) {
+               kfree(mt9v111_device.ifpReg);
+               mt9v111_device.ifpReg = NULL;
+       }
+}
+
+module_init(mt9v111_init);
+module_exit(mt9v111_clean);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Mt9v111 Camera Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/mt9v111.h b/drivers/media/video/mxc/capture/mt9v111.h
new file mode 100644 (file)
index 0000000..9935209
--- /dev/null
@@ -0,0 +1,431 @@
+/*
+ * 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 Camera Sensor Drivers
+ */
+
+/*!
+ * @file mt9v111.h
+ *
+ * @brief MT9V111 Camera Header file
+ *
+ * This header file contains defines and structures for the iMagic mi8012
+ * aka the Micron mt9v111 camera.
+ *
+ * @ingroup Camera
+ */
+
+#ifndef MT9V111_H_
+#define MT9V111_H_
+
+/*!
+ * Basic camera values
+ */
+#define MT9V111_FRAME_RATE        30
+#define MT9V111_MCLK              27000000 /* Desired clock rate */
+#define MT9V111_CLK_MIN           12000000 /* This clock rate yields 15 fps */
+#define MT9V111_CLK_MAX           27000000
+#define MT9V111_MAX_WIDTH         640      /* Max width for this camera */
+#define MT9V111_MAX_HEIGHT        480      /* Max height for this camera */
+
+/*!
+ * mt9v111 IFP REGISTER BANK MAP
+ */
+#define MT9V111I_ADDR_SPACE_SEL           0x1
+#define MT9V111I_BASE_MAXTRIX_SIGN        0x2
+#define MT9V111I_BASE_MAXTRIX_SCALE15     0x3
+#define MT9V111I_BASE_MAXTRIX_SCALE69     0x4
+#define MT9V111I_APERTURE_GAIN            0x5
+#define MT9V111I_MODE_CONTROL             0x6
+#define MT9V111I_SOFT_RESET               0x7
+#define MT9V111I_FORMAT_CONTROL           0x8
+#define MT9V111I_BASE_MATRIX_CFK1         0x9
+#define MT9V111I_BASE_MATRIX_CFK2         0xa
+#define MT9V111I_BASE_MATRIX_CFK3         0xb
+#define MT9V111I_BASE_MATRIX_CFK4         0xc
+#define MT9V111I_BASE_MATRIX_CFK5         0xd
+#define MT9V111I_BASE_MATRIX_CFK6         0xe
+#define MT9V111I_BASE_MATRIX_CFK7         0xf
+#define MT9V111I_BASE_MATRIX_CFK8         0x10
+#define MT9V111I_BASE_MATRIX_CFK9         0x11
+#define MT9V111I_AWB_POSITION             0x12
+#define MT9V111I_AWB_RED_GAIN             0x13
+#define MT9V111I_AWB_BLUE_GAIN            0x14
+#define MT9V111I_DELTA_MATRIX_CF_SIGN     0x15
+#define MT9V111I_DELTA_MATRIX_CF_D1       0x16
+#define MT9V111I_DELTA_MATRIX_CF_D2       0x17
+#define MT9V111I_DELTA_MATRIX_CF_D3       0x18
+#define MT9V111I_DELTA_MATRIX_CF_D4       0x19
+#define MT9V111I_DELTA_MATRIX_CF_D5       0x1a
+#define MT9V111I_DELTA_MATRIX_CF_D6       0x1b
+#define MT9V111I_DELTA_MATRIX_CF_D7       0x1c
+#define MT9V111I_DELTA_MATRIX_CF_D8       0x1d
+#define MT9V111I_DELTA_MATRIX_CF_D9       0x1e
+#define MT9V111I_LUMINANCE_LIMIT_WB       0x20
+#define MT9V111I_RBG_MANUUAL_WB           0x21
+#define MT9V111I_AWB_RED_LIMIT            0x22
+#define MT9V111I_AWB_BLUE_LIMIT           0x23
+#define MT9V111I_MATRIX_ADJUST_LIMIT      0x24
+#define MT9V111I_AWB_SPEED                0x25
+#define MT9V111I_H_BOUND_AE               0x26
+#define MT9V111I_V_BOUND_AE               0x27
+#define MT9V111I_H_BOUND_AE_CEN_WIN       0x2b
+#define MT9V111I_V_BOUND_AE_CEN_WIN       0x2c
+#define MT9V111I_BOUND_AWB_WIN            0x2d
+#define MT9V111I_AE_PRECISION_TARGET      0x2e
+#define MT9V111I_AE_SPEED                 0x2f
+#define MT9V111I_RED_AWB_MEASURE          0x30
+#define MT9V111I_LUMA_AWB_MEASURE         0x31
+#define MT9V111I_BLUE_AWB_MEASURE         0x32
+#define MT9V111I_LIMIT_SHARP_SATU_CTRL    0x33
+#define MT9V111I_LUMA_OFFSET              0x34
+#define MT9V111I_CLIP_LIMIT_OUTPUT_LUMI   0x35
+#define MT9V111I_GAIN_LIMIT_AE            0x36
+#define MT9V111I_SHUTTER_WIDTH_LIMIT_AE   0x37
+#define MT9V111I_UPPER_SHUTTER_DELAY_LIM  0x39
+#define MT9V111I_OUTPUT_FORMAT_CTRL2      0x3a
+#define MT9V111I_IPF_BLACK_LEVEL_SUB      0x3b
+#define MT9V111I_IPF_BLACK_LEVEL_ADD      0x3c
+#define MT9V111I_ADC_LIMIT_AE_ADJ         0x3d
+#define MT9V111I_GAIN_THRE_CCAM_ADJ       0x3e
+#define MT9V111I_LINEAR_AE                0x3f
+#define MT9V111I_THRESHOLD_EDGE_DEFECT    0x47
+#define MT9V111I_LUMA_SUM_MEASURE         0x4c
+#define MT9V111I_TIME_ADV_SUM_LUMA        0x4d
+#define MT9V111I_MOTION                   0x52
+#define MT9V111I_GAMMA_KNEE_Y12           0x53
+#define MT9V111I_GAMMA_KNEE_Y34           0x54
+#define MT9V111I_GAMMA_KNEE_Y56           0x55
+#define MT9V111I_GAMMA_KNEE_Y78           0x56
+#define MT9V111I_GAMMA_KNEE_Y90           0x57
+#define MT9V111I_GAMMA_VALUE_Y0           0x58
+#define MT9V111I_SHUTTER_60               0x59
+#define MT9V111I_SEARCH_FLICK_60          0x5c
+#define MT9V111I_RATIO_IMAGE_GAIN_BASE    0x5e
+#define MT9V111I_RATIO_IMAGE_GAIN_DELTA   0x5f
+#define MT9V111I_SIGN_VALUE_REG5F         0x60
+#define MT9V111I_AE_GAIN                  0x62
+#define MT9V111I_MAX_GAIN_AE              0x67
+#define MT9V111I_LENS_CORRECT_CTRL        0x80
+#define MT9V111I_SHADING_PARAMETER1       0x81
+#define MT9V111I_SHADING_PARAMETER2       0x82
+#define MT9V111I_SHADING_PARAMETER3       0x83
+#define MT9V111I_SHADING_PARAMETER4       0x84
+#define MT9V111I_SHADING_PARAMETER5       0x85
+#define MT9V111I_SHADING_PARAMETER6       0x86
+#define MT9V111I_SHADING_PARAMETER7       0x87
+#define MT9V111I_SHADING_PARAMETER8       0x88
+#define MT9V111I_SHADING_PARAMETER9       0x89
+#define MT9V111I_SHADING_PARAMETER10      0x8A
+#define MT9V111I_SHADING_PARAMETER11      0x8B
+#define MT9V111I_SHADING_PARAMETER12      0x8C
+#define MT9V111I_SHADING_PARAMETER13      0x8D
+#define MT9V111I_SHADING_PARAMETER14      0x8E
+#define MT9V111I_SHADING_PARAMETER15      0x8F
+#define MT9V111I_SHADING_PARAMETER16      0x90
+#define MT9V111I_SHADING_PARAMETER17      0x91
+#define MT9V111I_SHADING_PARAMETER18      0x92
+#define MT9V111I_SHADING_PARAMETER19      0x93
+#define MT9V111I_SHADING_PARAMETER20      0x94
+#define MT9V111I_SHADING_PARAMETER21      0x95
+#define MT9V111i_FLASH_CTRL               0x98
+#define MT9V111i_LINE_COUNTER             0x99
+#define MT9V111i_FRAME_COUNTER            0x9A
+#define MT9V111i_H_PAN                    0xA5
+#define MT9V111i_H_ZOOM                   0xA6
+#define MT9V111i_H_SIZE                   0xA7
+#define MT9V111i_V_PAN                    0xA8
+#define MT9V111i_V_ZOOM                   0xA9
+#define MT9V111i_V_SIZE                   0xAA
+
+#define MT9V111I_SEL_IFP                  0x1
+#define MT9V111I_SEL_SCA                  0x4
+#define MT9V111I_FC_RGB_OR_YUV            0x1000
+
+/*!
+ * Mt9v111 SENSOR CORE REGISTER BANK MAP
+ */
+#define MT9V111S_ADDR_SPACE_SEL           0x1
+#define MT9V111S_COLUMN_START             0x2
+#define MT9V111S_WIN_HEIGHT               0x3
+#define MT9V111S_WIN_WIDTH                0x4
+#define MT9V111S_HOR_BLANKING             0x5
+#define MT9V111S_VER_BLANKING             0x6
+#define MT9V111S_OUTPUT_CTRL              0x7
+#define MT9V111S_ROW_START                0x8
+#define MT9V111S_SHUTTER_WIDTH            0x9
+#define MT9V111S_PIXEL_CLOCK_SPEED        0xa
+#define MT9V111S_RESTART                  0xb
+#define MT9V111S_SHUTTER_DELAY            0xc
+#define MT9V111S_RESET                    0xd
+#define MT9V111S_COLUMN_START_IN_ZOOM     0x12
+#define MT9V111S_ROW_START_IN_ZOOM        0x13
+#define MT9V111S_DIGITAL_ZOOM             0x1e
+#define MT9V111S_READ_MODE                0x20
+#define MT9V111S_DAC_CTRL                 0x27
+#define MT9V111S_GREEN1_GAIN              0x2b
+#define MT9V111S_BLUE_GAIN                0x2c
+#define MT9V111S_READ_GAIN                0x2d
+#define MT9V111S_GREEN2_GAIN              0x2e
+#define MT9V111S_ROW_NOISE_CTRL           0x30
+#define MT9V111S_DARK_TARGET_W            0x31
+#define MT9V111S_TEST_DATA                0x32
+#define MT9V111S_GLOBAL_GAIN              0x35
+#define MT9V111S_SENSOR_CORE_VERSION      0x36
+#define MT9V111S_DARK_TARGET_WO           0x37
+#define MT9V111S_VERF_DAC                 0x41
+#define MT9V111S_VCM_VCL                  0x42
+#define MT9V111S_DISABLE_BYPASS           0x58
+#define MT9V111S_CALIB_MEAN_TEST          0x59
+#define MT9V111S_DARK_G1_AVE              0x5B
+#define MT9V111S_DARK_G2_AVE              0x5C
+#define MT9V111S_DARK_R_AVE               0x5D
+#define MT9V111S_DARK_B_AVE               0x5E
+#define MT9V111S_CAL_THRESHOLD            0x5f
+#define MT9V111S_CAL_G1                   0x60
+#define MT9V111S_CAL_G2                   0x61
+#define MT9V111S_CAL_CTRL                 0x62
+#define MT9V111S_CAL_R                    0x63
+#define MT9V111S_CAL_B                    0x64
+#define MT9V111S_CHIP_ENABLE              0xF1
+#define MT9V111S_CHIP_VERSION             0xFF
+
+/* OUTPUT_CTRL */
+#define MT9V111S_OUTCTRL_SYNC             0x1
+#define MT9V111S_OUTCTRL_CHIP_ENABLE      0x2
+#define MT9V111S_OUTCTRL_TEST_MODE        0x40
+
+/* READ_MODE */
+#define MT9V111S_RM_NOBADFRAME            0x1
+#define MT9V111S_RM_NODESTRUCT            0x2
+#define MT9V111S_RM_COLUMNSKIP            0x4
+#define MT9V111S_RM_ROWSKIP               0x8
+#define MT9V111S_RM_BOOSTEDRESET          0x1000
+#define MT9V111S_RM_COLUMN_LATE           0x10
+#define MT9V111S_RM_ROW_LATE              0x80
+#define MT9V111S_RM_RIGTH_TO_LEFT         0x4000
+#define MT9V111S_RM_BOTTOM_TO_TOP         0x8000
+
+/*! I2C Slave Address */
+#define MT9V111_I2C_ADDRESS    0x48
+
+/*!
+ * The image resolution enum for the mt9v111 sensor
+ */
+typedef enum {
+       MT9V111_OutputResolution_VGA = 0,       /*!< VGA size */
+       MT9V111_OutputResolution_QVGA,  /*!< QVGA size */
+       MT9V111_OutputResolution_CIF,   /*!< CIF size */
+       MT9V111_OutputResolution_QCIF,  /*!< QCIF size */
+       MT9V111_OutputResolution_QQVGA, /*!< QQVGA size */
+       MT9V111_OutputResolution_SXGA   /*!< SXGA size */
+} MT9V111_OutputResolution;
+
+enum {
+       MT9V111_WINWIDTH = 0x287,
+       MT9V111_WINWIDTH_DEFAULT = 0x287,
+       MT9V111_WINWIDTH_MIN = 0x9,
+
+       MT9V111_WINHEIGHT = 0x1E7,
+       MT9V111_WINHEIGHT_DEFAULT = 0x1E7,
+
+       MT9V111_HORZBLANK_DEFAULT = 0x26,
+       MT9V111_HORZBLANK_MIN = 0x9,
+       MT9V111_HORZBLANK_MAX = 0x3FF,
+
+       MT9V111_VERTBLANK_DEFAULT = 0x4,
+       MT9V111_VERTBLANK_MIN = 0x3,
+       MT9V111_VERTBLANK_MAX = 0xFFF,
+};
+
+/*!
+ * Mt9v111 Core Register structure.
+ */
+typedef struct {
+       u32 addressSelect;      /*!< select address bank for Core Register 0x4 */
+       u32 columnStart;        /*!< Starting Column */
+       u32 windowHeight;       /*!< Window Height */
+       u32 windowWidth;        /*!< Window Width */
+       u32 horizontalBlanking; /*!< Horizontal Blank time, in pixels */
+       u32 verticalBlanking;   /*!< Vertical Blank time, in pixels */
+       u32 outputControl;      /*!< Register to control sensor output */
+       u32 rowStart;           /*!< Starting Row */
+       u32 shutterWidth;
+       u32 pixelClockSpeed;    /*!< pixel date rate */
+       u32 restart;            /*!< Abandon the readout of current frame */
+       u32 shutterDelay;
+       u32 reset;              /*!< reset the sensor to the default mode */
+       u32 zoomColStart;       /*!< Column start in the Zoom mode */
+       u32 zomRowStart;        /*!< Row start in the Zoom mode */
+       u32 digitalZoom;        /*!< 1 means zoom by 2 */
+       u32 readMode;           /*!< Readmode: aspects of the readout of the sensor */
+       u32 dACStandbyControl;
+       u32 green1Gain;         /*!< Gain Settings */
+       u32 blueGain;
+       u32 redGain;
+       u32 green2Gain;
+       u32 rowNoiseControl;
+       u32 darkTargetwNC;
+       u32 testData;           /*!< test mode */
+       u32 globalGain;
+       u32 chipVersion;
+       u32 darkTargetwoNC;
+       u32 vREFDACs;
+       u32 vCMandVCL;
+       u32 disableBypass;
+       u32 calibMeanTest;
+       u32 darkG1average;
+       u32 darkG2average;
+       u32 darkRaverage;
+       u32 darkBaverage;
+       u32 calibThreshold;
+       u32 calibGreen1;
+       u32 calibGreen2;
+       u32 calibControl;
+       u32 calibRed;
+       u32 calibBlue;
+       u32 chipEnable;         /*!< Image core Registers written by image flow processor */
+} mt9v111_coreReg;
+
+/*!
+ * Mt9v111 IFP Register structure.
+ */
+typedef struct {
+       u32 addrSpaceSel;       /*!< select address bank for Core Register 0x1 */
+       u32 baseMaxtrixSign;    /*!< sign of coefficient for base color correction matrix */
+       u32 baseMaxtrixScale15; /*!< scaling of color correction coefficient K1-5 */
+       u32 baseMaxtrixScale69; /*!< scaling of color correction coefficient K6-9 */
+       u32 apertureGain;       /*!< sharpening */
+       u32 modeControl;        /*!< bit 7 CCIR656 sync codes are embedded in the image */
+       u32 softReset;          /*!< Image processing mode: 1 reset mode, 0 operational mode */
+       u32 formatControl;      /*!< bit12 1 for RGB565, 0 for YcrCb */
+       u32 baseMatrixCfk1;     /*!< K1 Color correction coefficient */
+       u32 baseMatrixCfk2;     /*!< K2 Color correction coefficient */
+       u32 baseMatrixCfk3;     /*!< K3 Color correction coefficient */
+       u32 baseMatrixCfk4;     /*!< K4 Color correction coefficient */
+       u32 baseMatrixCfk5;     /*!< K5 Color correction coefficient */
+       u32 baseMatrixCfk6;     /*!< K6 Color correction coefficient */
+       u32 baseMatrixCfk7;     /*!< K7 Color correction coefficient */
+       u32 baseMatrixCfk8;     /*!< K8 Color correction coefficient */
+       u32 baseMatrixCfk9;     /*!< K9 Color correction coefficient */
+       u32 awbPosition;        /*!< Current position of AWB color correction matrix */
+       u32 awbRedGain;         /*!< Current value of AWB red channel gain */
+       u32 awbBlueGain;        /*!< Current value of AWB blue channel gain */
+       u32 deltaMatrixCFSign;  /*!< Sign of coefficients of delta color correction matrix register */
+       u32 deltaMatrixCFD1;    /*!< D1 Delta coefficient */
+       u32 deltaMatrixCFD2;    /*!< D2 Delta coefficient */
+       u32 deltaMatrixCFD3;    /*!< D3 Delta coefficient */
+       u32 deltaMatrixCFD4;    /*!< D4 Delta coefficient */
+       u32 deltaMatrixCFD5;    /*!< D5 Delta coefficient */
+       u32 deltaMatrixCFD6;    /*!< D6 Delta coefficient */
+       u32 deltaMatrixCFD7;    /*!< D7 Delta coefficient */
+       u32 deltaMatrixCFD8;    /*!< D8 Delta coefficient */
+       u32 deltaMatrixCFD9;    /*!< D9 Delta coefficient */
+       u32 lumLimitWB;         /*!< Luminance range of pixels considered in WB statistics */
+       u32 RBGManualWB;        /*!< Red and Blue color channel gains for manual white balance */
+       u32 awbRedLimit;        /*!< Limits on Red channel gain adjustment through AWB */
+       u32 awbBlueLimit;       /*!< Limits on Blue channel gain adjustment through AWB */
+       u32 matrixAdjLimit;     /*!< Limits on color correction matrix adjustment through AWB */
+       u32 awbSpeed;           /*!< AWB speed and color saturation control */
+       u32 HBoundAE;           /*!< Horizontal boundaries of AWB measurement window */
+       u32 VBoundAE;           /*!< Vertical boundaries of AWB measurement window */
+       u32 HBoundAECenWin;     /*!< Horizontal boundaries of AE measurement window for backlight compensation */
+       u32 VBoundAECenWin;     /*!< Vertical boundaries of AE measurement window for backlight compensation */
+       u32 boundAwbWin;        /*!< Boundaries of AWB measurement window */
+       u32 AEPrecisionTarget;  /*!< Auto exposure target and precision control */
+       u32 AESpeed;            /*!< AE speed and sensitivity control register */
+       u32 redAWBMeasure;      /*!< Measure of the red channel value used by AWB */
+       u32 lumaAWBMeasure;     /*!< Measure of the luminance channel value used by AWB */
+       u32 blueAWBMeasure;     /*!< Measure of the blue channel value used by AWB */
+       u32 limitSharpSatuCtrl; /*!< Automatic control of sharpness and color saturation */
+       u32 lumaOffset;         /*!< Luminance offset control (brightness control) */
+       u32 clipLimitOutputLumi;        /*!< Clipping limits for output luminance */
+       u32 gainLimitAE;        /*!< Imager gain limits for AE adjustment */
+       u32 shutterWidthLimitAE;        /*!< Shutter width (exposure time) limits for AE adjustment */
+       u32 upperShutterDelayLi;        /*!< Upper Shutter Delay Limit */
+       u32 outputFormatCtrl2;  /*!< Output Format Control 2
+                                  00 = 16-bit RGB565.
+                                  01 = 15-bit RGB555.
+                                  10 = 12-bit RGB444x.
+                                  11 = 12-bit RGBx444.       */
+       u32 ipfBlackLevelSub;   /*!< IFP black level subtraction */
+       u32 ipfBlackLevelAdd;   /*!< IFP black level addition */
+       u32 adcLimitAEAdj;      /*!< ADC limits for AE adjustment */
+       u32 agimnThreCamAdj;    /*!< Gain threshold for CCM adjustment */
+       u32 linearAE;
+       u32 thresholdEdgeDefect;        /*!< Edge threshold for interpolation and defect correction */
+       u32 lumaSumMeasure;     /*!< Luma measured by AE engine */
+       u32 timeAdvSumLuma;     /*!< Time-averaged luminance value tracked by auto exposure */
+       u32 motion;             /*!< 1 when motion is detected */
+       u32 gammaKneeY12;       /*!< Gamma knee points Y1 and Y2 */
+       u32 gammaKneeY34;       /*!< Gamma knee points Y3 and Y4 */
+       u32 gammaKneeY56;       /*!< Gamma knee points Y5 and Y6 */
+       u32 gammaKneeY78;       /*!< Gamma knee points Y7 and Y8 */
+       u32 gammaKneeY90;       /*!< Gamma knee points Y9 and Y10 */
+       u32 gammaKneeY0;        /*!< Gamma knee point Y0 */
+       u32 shutter_width_60;
+       u32 search_flicker_60;
+       u32 ratioImageGainBase;
+       u32 ratioImageGainDelta;
+       u32 signValueReg5F;
+       u32 aeGain;
+       u32 maxGainAE;
+       u32 lensCorrectCtrl;
+       u32 shadingParameter1;  /*!< Shade Parameters */
+       u32 shadingParameter2;
+       u32 shadingParameter3;
+       u32 shadingParameter4;
+       u32 shadingParameter5;
+       u32 shadingParameter6;
+       u32 shadingParameter7;
+       u32 shadingParameter8;
+       u32 shadingParameter9;
+       u32 shadingParameter10;
+       u32 shadingParameter11;
+       u32 shadingParameter12;
+       u32 shadingParameter13;
+       u32 shadingParameter14;
+       u32 shadingParameter15;
+       u32 shadingParameter16;
+       u32 shadingParameter17;
+       u32 shadingParameter18;
+       u32 shadingParameter19;
+       u32 shadingParameter20;
+       u32 shadingParameter21;
+       u32 flashCtrl;          /*!< Flash control */
+       u32 lineCounter;        /*!< Line counter */
+       u32 frameCounter;       /*!< Frame counter */
+       u32 HPan;               /*!< Horizontal pan in decimation */
+       u32 HZoom;              /*!< Horizontal zoom in decimation */
+       u32 HSize;              /*!< Horizontal output size iIn decimation */
+       u32 VPan;               /*!< Vertical pan in decimation */
+       u32 VZoom;              /*!< Vertical zoom in decimation */
+       u32 VSize;              /*!< Vertical output size in decimation */
+} mt9v111_IFPReg;
+
+/*!
+ * mt9v111 Config structure
+ */
+typedef struct {
+       mt9v111_coreReg *coreReg;       /*!< Sensor Core Register Bank */
+       mt9v111_IFPReg *ifpReg; /*!< IFP Register Bank */
+} mt9v111_conf;
+
+typedef struct {
+       u8 index;
+       u16 width;
+       u16 height;
+} mt9v111_image_format;
+
+#endif                         /* MT9V111_H_  */
diff --git a/drivers/media/video/mxc/capture/mxc_v4l2_capture.c b/drivers/media/video/mxc/capture/mxc_v4l2_capture.c
new file mode 100644 (file)
index 0000000..68da313
--- /dev/null
@@ -0,0 +1,2814 @@
+/*
+ * 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 drivers/media/video/mxc/capture/mxc_v4l2_capture.c
+ *
+ * @brief Mxc Video For Linux 2 driver
+ *
+ * @ingroup MXC_V4L2_CAPTURE
+ */
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/io.h>
+#include <linux/semaphore.h>
+#include <linux/pagemap.h>
+#include <linux/vmalloc.h>
+#include <linux/types.h>
+#include <linux/fb.h>
+#include <linux/dma-mapping.h>
+#include <linux/mxcfb.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-int-device.h>
+#include "mxc_v4l2_capture.h"
+#include "ipu_prp_sw.h"
+
+#define init_MUTEX(sem)         sema_init(sem, 1)
+
+static int video_nr = -1, local_buf_num;
+static cam_data *g_cam;
+
+/*! This data is used for the output to the display. */
+#define MXC_V4L2_CAPTURE_NUM_OUTPUTS   3
+#define MXC_V4L2_CAPTURE_NUM_INPUTS    2
+static struct v4l2_output mxc_capture_outputs[MXC_V4L2_CAPTURE_NUM_OUTPUTS] = {
+       {
+        .index = 0,
+        .name = "DISP3 BG",
+        .type = V4L2_OUTPUT_TYPE_ANALOG,
+        .audioset = 0,
+        .modulator = 0,
+        .std = V4L2_STD_UNKNOWN,
+        },
+       {
+        .index = 1,
+        .name = "DISP3 BG - DI1",
+        .type = V4L2_OUTPUT_TYPE_ANALOG,
+        .audioset = 0,
+        .modulator = 0,
+        .std = V4L2_STD_UNKNOWN,
+        },
+       {
+        .index = 2,
+        .name = "DISP3 FG",
+        .type = V4L2_OUTPUT_TYPE_ANALOG,
+        .audioset = 0,
+        .modulator = 0,
+        .std = V4L2_STD_UNKNOWN,
+        },
+};
+
+static struct v4l2_input mxc_capture_inputs[MXC_V4L2_CAPTURE_NUM_INPUTS] = {
+       {
+        .index = 0,
+        .name = "CSI IC MEM",
+        .type = V4L2_INPUT_TYPE_CAMERA,
+        .audioset = 0,
+        .tuner = 0,
+        .std = V4L2_STD_UNKNOWN,
+        .status = 0,
+        },
+       {
+        .index = 1,
+        .name = "CSI MEM",
+        .type = V4L2_INPUT_TYPE_CAMERA,
+        .audioset = 0,
+        .tuner = 0,
+        .std = V4L2_STD_UNKNOWN,
+        .status = V4L2_IN_ST_NO_POWER,
+        },
+};
+
+/*! List of TV input video formats supported. The video formats is corresponding
+ * to the v4l2_id in video_fmt_t.
+ * Currently, only PAL and NTSC is supported. Needs to be expanded in the
+ * future.
+ */
+typedef enum {
+       TV_NTSC = 0,            /*!< Locked on (M) NTSC video signal. */
+       TV_PAL,                 /*!< (B, G, H, I, N)PAL video signal. */
+       TV_NOT_LOCKED,          /*!< Not locked on a signal. */
+} video_fmt_idx;
+
+/*! Number of video standards supported (including 'not locked' signal). */
+#define TV_STD_MAX             (TV_NOT_LOCKED + 1)
+
+/*! Video format structure. */
+typedef struct {
+       int v4l2_id;            /*!< Video for linux ID. */
+       char name[16];          /*!< Name (e.g., "NTSC", "PAL", etc.) */
+       u16 raw_width;          /*!< Raw width. */
+       u16 raw_height;         /*!< Raw height. */
+       u16 active_width;       /*!< Active width. */
+       u16 active_height;      /*!< Active height. */
+       u16 active_top;         /*!< Active top. */
+       u16 active_left;        /*!< Active left. */
+} video_fmt_t;
+
+/*!
+ * Description of video formats supported.
+ *
+ *  PAL: raw=720x625, active=720x576.
+ *  NTSC: raw=720x525, active=720x480.
+ */
+static video_fmt_t video_fmts[] = {
+       {                       /*! NTSC */
+        .v4l2_id = V4L2_STD_NTSC,
+        .name = "NTSC",
+        .raw_width = 720,              /* SENS_FRM_WIDTH */
+        .raw_height = 525,             /* SENS_FRM_HEIGHT */
+        .active_width = 720,           /* ACT_FRM_WIDTH */
+        .active_height = 480,          /* ACT_FRM_HEIGHT */
+        .active_top = 13,
+        .active_left = 0,
+        },
+       {                       /*! (B, G, H, I, N) PAL */
+        .v4l2_id = V4L2_STD_PAL,
+        .name = "PAL",
+        .raw_width = 720,
+        .raw_height = 625,
+        .active_width = 720,
+        .active_height = 576,
+        .active_top = 0,
+        .active_left = 0,
+        },
+       {                       /*! Unlocked standard */
+        .v4l2_id = V4L2_STD_ALL,
+        .name = "Autodetect",
+        .raw_width = 720,
+        .raw_height = 625,
+        .active_width = 720,
+        .active_height = 576,
+        .active_top = 0,
+        .active_left = 0,
+        },
+};
+
+/*!* Standard index of TV. */
+static video_fmt_idx video_index = TV_NOT_LOCKED;
+
+static int mxc_v4l2_master_attach(struct v4l2_int_device *slave);
+static void mxc_v4l2_master_detach(struct v4l2_int_device *slave);
+static int start_preview(cam_data *cam);
+static int stop_preview(cam_data *cam);
+
+/*! Information about this driver. */
+static struct v4l2_int_master mxc_v4l2_master = {
+       .attach = mxc_v4l2_master_attach,
+       .detach = mxc_v4l2_master_detach,
+};
+
+static struct v4l2_int_device mxc_v4l2_int_device = {
+       .module = THIS_MODULE,
+       .name = "mxc_v4l2_cap",
+       .type = v4l2_int_type_master,
+       .u = {
+               .master = &mxc_v4l2_master,
+               },
+};
+
+/***************************************************************************
+ * Functions for handling Frame buffers.
+ **************************************************************************/
+
+/*!
+ * Free frame buffers
+ *
+ * @param cam      Structure cam_data *
+ *
+ * @return status  0 success.
+ */
+static int mxc_free_frame_buf(cam_data *cam)
+{
+       int i;
+
+       pr_debug("MVC: In mxc_free_frame_buf\n");
+
+       for (i = 0; i < FRAME_NUM; i++) {
+               if (cam->frame[i].vaddress != 0) {
+                       dma_free_coherent(0, cam->frame[i].buffer.length,
+                                         cam->frame[i].vaddress,
+                                         cam->frame[i].paddress);
+                       cam->frame[i].vaddress = 0;
+               }
+       }
+
+       return 0;
+}
+
+/*!
+ * Allocate frame buffers
+ *
+ * @param cam      Structure cam_data*
+ * @param count    int number of buffer need to allocated
+ *
+ * @return status  -0 Successfully allocated a buffer, -ENOBUFS        failed.
+ */
+static int mxc_allocate_frame_buf(cam_data *cam, int count)
+{
+       int i;
+
+       pr_debug("In MVC:mxc_allocate_frame_buf - size=%d\n",
+               cam->v2f.fmt.pix.sizeimage);
+
+       for (i = 0; i < count; i++) {
+               cam->frame[i].vaddress =
+                   dma_alloc_coherent(0,
+                                      PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage),
+                                      &cam->frame[i].paddress,
+                                      GFP_DMA | GFP_KERNEL);
+               if (cam->frame[i].vaddress == 0) {
+                       pr_err("ERROR: v4l2 capture: "
+                               "mxc_allocate_frame_buf failed.\n");
+                       mxc_free_frame_buf(cam);
+                       return -ENOBUFS;
+               }
+               cam->frame[i].buffer.index = i;
+               cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED;
+               cam->frame[i].buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+               cam->frame[i].buffer.length =
+                   PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage);
+               cam->frame[i].buffer.memory = V4L2_MEMORY_MMAP;
+               cam->frame[i].buffer.m.offset = cam->frame[i].paddress;
+               cam->frame[i].index = i;
+       }
+
+       return 0;
+}
+
+/*!
+ * Free frame buffers status
+ *
+ * @param cam    Structure cam_data *
+ *
+ * @return none
+ */
+static void mxc_free_frames(cam_data *cam)
+{
+       int i;
+
+       pr_debug("In MVC:mxc_free_frames\n");
+
+       for (i = 0; i < FRAME_NUM; i++) {
+               cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED;
+       }
+
+       cam->enc_counter = 0;
+       INIT_LIST_HEAD(&cam->ready_q);
+       INIT_LIST_HEAD(&cam->working_q);
+       INIT_LIST_HEAD(&cam->done_q);
+}
+
+/*!
+ * Return the buffer status
+ *
+ * @param cam     Structure cam_data *
+ * @param buf      Structure v4l2_buffer *
+ *
+ * @return status  0 success, EINVAL failed.
+ */
+static int mxc_v4l2_buffer_status(cam_data *cam, struct v4l2_buffer *buf)
+{
+       pr_debug("In MVC:mxc_v4l2_buffer_status\n");
+
+       if (buf->index < 0 || buf->index >= FRAME_NUM) {
+               pr_err("ERROR: v4l2 capture: mxc_v4l2_buffer_status buffers "
+                      "not allocated\n");
+               return -EINVAL;
+       }
+
+       memcpy(buf, &(cam->frame[buf->index].buffer), sizeof(*buf));
+       return 0;
+}
+
+/***************************************************************************
+ * Functions for handling the video stream.
+ **************************************************************************/
+
+/*!
+ * Indicates whether the palette is supported.
+ *
+ * @param palette V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_BGR24 or V4L2_PIX_FMT_BGR32
+ *
+ * @return 0 if failed
+ */
+static inline int valid_mode(u32 palette)
+{
+       return ((palette == V4L2_PIX_FMT_RGB565) ||
+               (palette == V4L2_PIX_FMT_BGR24) ||
+               (palette == V4L2_PIX_FMT_RGB24) ||
+               (palette == V4L2_PIX_FMT_BGR32) ||
+               (palette == V4L2_PIX_FMT_RGB32) ||
+               (palette == V4L2_PIX_FMT_YUV422P) ||
+               (palette == V4L2_PIX_FMT_UYVY) ||
+               (palette == V4L2_PIX_FMT_YUYV) ||
+               (palette == V4L2_PIX_FMT_YUV420) ||
+               (palette == V4L2_PIX_FMT_NV12));
+}
+
+/*!
+ * Start the encoder job
+ *
+ * @param cam      structure cam_data *
+ *
+ * @return status  0 Success
+ */
+static int mxc_streamon(cam_data *cam)
+{
+       struct mxc_v4l_frame *frame;
+       int err = 0;
+
+       pr_debug("In MVC:mxc_streamon\n");
+
+       if (NULL == cam) {
+               pr_err("ERROR! cam parameter is NULL\n");
+               return -1;
+       }
+
+       if (cam->capture_on) {
+               pr_err("ERROR: v4l2 capture: Capture stream has been turned "
+                      " on\n");
+               return -1;
+       }
+
+       if (list_empty(&cam->ready_q)) {
+               pr_err("ERROR: v4l2 capture: mxc_streamon buffer has not been "
+                       "queued yet\n");
+               return -EINVAL;
+       }
+
+       cam->capture_pid = current->pid;
+
+       if (cam->overlay_on == true)
+               stop_preview(cam);
+
+       if (cam->enc_enable) {
+               err = cam->enc_enable(cam);
+               if (err != 0) {
+                       return err;
+               }
+       }
+
+       cam->ping_pong_csi = 0;
+       local_buf_num = 0;
+       if (cam->enc_update_eba) {
+               frame =
+                   list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue);
+               list_del(cam->ready_q.next);
+               list_add_tail(&frame->queue, &cam->working_q);
+               frame->ipu_buf_num = cam->ping_pong_csi;
+               err = cam->enc_update_eba(frame->buffer.m.offset,
+                                         &cam->ping_pong_csi);
+
+               frame =
+                   list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue);
+               list_del(cam->ready_q.next);
+               list_add_tail(&frame->queue, &cam->working_q);
+               frame->ipu_buf_num = cam->ping_pong_csi;
+               err |= cam->enc_update_eba(frame->buffer.m.offset,
+                                          &cam->ping_pong_csi);
+       } else {
+               return -EINVAL;
+       }
+
+       if (cam->overlay_on == true)
+               start_preview(cam);
+
+       if (cam->enc_enable_csi) {
+               err = cam->enc_enable_csi(cam);
+               if (err != 0)
+                       return err;
+       }
+
+       cam->capture_on = true;
+
+       return err;
+}
+
+/*!
+ * Shut down the encoder job
+ *
+ * @param cam      structure cam_data *
+ *
+ * @return status  0 Success
+ */
+static int mxc_streamoff(cam_data *cam)
+{
+       int err = 0;
+
+       pr_debug("In MVC:mxc_streamoff\n");
+
+       if (cam->capture_on == false)
+               return 0;
+
+       if (cam->enc_disable_csi) {
+               err = cam->enc_disable_csi(cam);
+               if (err != 0)
+                       return err;
+       }
+       if (cam->enc_disable)
+               err = cam->enc_disable(cam);
+
+       mxc_free_frames(cam);
+       mxc_capture_inputs[cam->current_input].status |= V4L2_IN_ST_NO_POWER;
+       cam->capture_on = false;
+       return err;
+}
+
+/*!
+ * Valid and adjust the overlay window size, position
+ *
+ * @param cam      structure cam_data *
+ * @param win      struct v4l2_window  *
+ *
+ * @return 0
+ */
+static int verify_preview(cam_data *cam, struct v4l2_window *win)
+{
+       int i = 0, width_bound = 0, height_bound = 0;
+       int *width, *height;
+       unsigned int ipu_ch = CHAN_NONE;
+       struct fb_info *bg_fbi = NULL, *fbi = NULL;
+       bool foregound_fb;
+       mm_segment_t old_fs;
+
+       pr_debug("In MVC: verify_preview\n");
+
+       do {
+               fbi = (struct fb_info *)registered_fb[i];
+               if (fbi == NULL) {
+                       pr_err("ERROR: verify_preview frame buffer NULL.\n");
+                       return -1;
+               }
+
+               /* Which DI supports 2 layers? */
+               if (strncmp(fbi->fix.id, "DISP3 BG", 8) == 0) {
+                       if (fbi->fbops->fb_ioctl) {
+                               old_fs = get_fs();
+                               set_fs(KERNEL_DS);
+                               fbi->fbops->fb_ioctl(fbi, MXCFB_GET_FB_IPU_CHAN,
+                                               (unsigned long)&ipu_ch);
+                               set_fs(old_fs);
+                       }
+                       if (ipu_ch == MEM_BG_SYNC) {
+                               bg_fbi = fbi;
+                               pr_debug("Found background frame buffer.\n");
+                       }
+               }
+
+               /* Found the frame buffer to preview on. */
+               if (strcmp(fbi->fix.id,
+                           mxc_capture_outputs[cam->output].name) == 0) {
+                       if (strcmp(fbi->fix.id, "DISP3 FG") == 0)
+                               foregound_fb = true;
+
+                       cam->overlay_fb = fbi;
+                       break;
+               }
+       } while (++i < FB_MAX);
+
+       if (foregound_fb) {
+               width_bound = bg_fbi->var.xres;
+               height_bound = bg_fbi->var.yres;
+
+               if (win->w.width + win->w.left > bg_fbi->var.xres ||
+                   win->w.height + win->w.top > bg_fbi->var.yres) {
+                       pr_err("ERROR: FG window position exceeds.\n");
+                       return -1;
+               }
+       } else {
+               /* 4 bytes alignment for BG */
+               width_bound = cam->overlay_fb->var.xres;
+               height_bound = cam->overlay_fb->var.yres;
+
+               if (cam->overlay_fb->var.bits_per_pixel == 24) {
+                       win->w.left -= win->w.left % 4;
+               } else if (cam->overlay_fb->var.bits_per_pixel == 16) {
+                       win->w.left -= win->w.left % 2;
+               }
+
+               if (win->w.width + win->w.left > cam->overlay_fb->var.xres)
+                       win->w.width = cam->overlay_fb->var.xres - win->w.left;
+               if (win->w.height + win->w.top > cam->overlay_fb->var.yres)
+                       win->w.height = cam->overlay_fb->var.yres - win->w.top;
+       }
+
+       /* stride line limitation */
+       win->w.height -= win->w.height % 8;
+       win->w.width -= win->w.width % 8;
+
+       if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
+               height = &win->w.width;
+               width = &win->w.height;
+       } else {
+               width = &win->w.width;
+               height = &win->w.height;
+       }
+
+       if (*width == 0 || *height == 0) {
+               pr_err("ERROR: v4l2 capture: width or height"
+                       " too small.\n");
+               return -EINVAL;
+       }
+
+       if ((cam->crop_bounds.width / *width > 8) ||
+           ((cam->crop_bounds.width / *width == 8) &&
+            (cam->crop_bounds.width % *width))) {
+               *width = cam->crop_bounds.width / 8;
+               if (*width % 8)
+                       *width += 8 - *width % 8;
+               if (*width + win->w.left > width_bound) {
+                       pr_err("ERROR: v4l2 capture: width exceeds "
+                               "resize limit.\n");
+                       return -1;
+               }
+               pr_err("ERROR: v4l2 capture: width exceeds limit. "
+                       "Resize to %d.\n",
+                       *width);
+       }
+
+       if ((cam->crop_bounds.height / *height > 8) ||
+           ((cam->crop_bounds.height / *height == 8) &&
+            (cam->crop_bounds.height % *height))) {
+               *height = cam->crop_bounds.height / 8;
+               if (*height % 8)
+                       *height += 8 - *height % 8;
+               if (*height + win->w.top > height_bound) {
+                       pr_err("ERROR: v4l2 capture: height exceeds "
+                               "resize limit.\n");
+                       return -1;
+               }
+               pr_err("ERROR: v4l2 capture: height exceeds limit "
+                       "resize to %d.\n",
+                       *height);
+       }
+
+       return 0;
+}
+
+/*!
+ * start the viewfinder job
+ *
+ * @param cam      structure cam_data *
+ *
+ * @return status  0 Success
+ */
+static int start_preview(cam_data *cam)
+{
+       int err = 0;
+
+       pr_debug("MVC: start_preview\n");
+
+       if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY)
+               err = prp_vf_sdc_select(cam);
+       else if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_PRIMARY)
+               err = prp_vf_sdc_select_bg(cam);
+       if (err != 0)
+               return err;
+
+       err = cam->vf_start_sdc(cam);
+       if (err != 0)
+               return err;
+
+       if (cam->vf_enable_csi)
+               err = cam->vf_enable_csi(cam);
+
+       pr_debug("End of %s: v2f pix widthxheight %d x %d\n",
+                __func__,
+                cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height);
+       pr_debug("End of %s: crop_bounds widthxheight %d x %d\n",
+                __func__,
+                cam->crop_bounds.width, cam->crop_bounds.height);
+       pr_debug("End of %s: crop_defrect widthxheight %d x %d\n",
+                __func__,
+                cam->crop_defrect.width, cam->crop_defrect.height);
+       pr_debug("End of %s: crop_current widthxheight %d x %d\n",
+                __func__,
+                cam->crop_current.width, cam->crop_current.height);
+
+       return err;
+}
+
+/*!
+ * shut down the viewfinder job
+ *
+ * @param cam      structure cam_data *
+ *
+ * @return status  0 Success
+ */
+static int stop_preview(cam_data *cam)
+{
+       int err = 0;
+
+       pr_debug("MVC: stop preview\n");
+
+       if (cam->vf_disable_csi) {
+               err = cam->vf_disable_csi(cam);
+               if (err != 0)
+                       return err;
+       }
+
+       if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY)
+               err = prp_vf_sdc_deselect(cam);
+       else if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_PRIMARY)
+               err = prp_vf_sdc_deselect_bg(cam);
+
+       return err;
+}
+
+/***************************************************************************
+ * VIDIOC Functions.
+ **************************************************************************/
+
+/*!
+ * V4L2 - mxc_v4l2_g_fmt function
+ *
+ * @param cam         structure cam_data *
+ *
+ * @param f           structure v4l2_format *
+ *
+ * @return  status    0 success, EINVAL failed
+ */
+static int mxc_v4l2_g_fmt(cam_data *cam, struct v4l2_format *f)
+{
+       int retval = 0;
+
+       pr_debug("In MVC: mxc_v4l2_g_fmt type=%d\n", f->type);
+
+       switch (f->type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               pr_debug("   type is V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
+               f->fmt.pix = cam->v2f.fmt.pix;
+               break;
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+               pr_debug("   type is V4L2_BUF_TYPE_VIDEO_OVERLAY\n");
+               f->fmt.win = cam->win;
+               break;
+       default:
+               pr_debug("   type is invalid\n");
+               retval = -EINVAL;
+       }
+
+       pr_debug("End of %s: v2f pix widthxheight %d x %d\n",
+                __func__,
+                cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height);
+       pr_debug("End of %s: crop_bounds widthxheight %d x %d\n",
+                __func__,
+                cam->crop_bounds.width, cam->crop_bounds.height);
+       pr_debug("End of %s: crop_defrect widthxheight %d x %d\n",
+                __func__,
+                cam->crop_defrect.width, cam->crop_defrect.height);
+       pr_debug("End of %s: crop_current widthxheight %d x %d\n",
+                __func__,
+                cam->crop_current.width, cam->crop_current.height);
+
+       return retval;
+}
+
+/*!
+ * V4L2 - mxc_v4l2_s_fmt function
+ *
+ * @param cam         structure cam_data *
+ *
+ * @param f           structure v4l2_format *
+ *
+ * @return  status    0 success, EINVAL failed
+ */
+static int mxc_v4l2_s_fmt(cam_data *cam, struct v4l2_format *f)
+{
+       int retval = 0;
+       int size = 0;
+       int bytesperline = 0;
+       int *width, *height;
+
+       pr_debug("In MVC: mxc_v4l2_s_fmt\n");
+
+       switch (f->type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               pr_debug("   type=V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
+               if (!valid_mode(f->fmt.pix.pixelformat)) {
+                       pr_err("ERROR: v4l2 capture: mxc_v4l2_s_fmt: format "
+                              "not supported\n");
+                       return -EINVAL;
+               }
+
+               /*
+                * Force the capture window resolution to be crop bounds
+                * for CSI MEM input mode.
+                */
+               if (strcmp(mxc_capture_inputs[cam->current_input].name,
+                          "CSI MEM") == 0) {
+                       f->fmt.pix.width = cam->crop_current.width;
+                       f->fmt.pix.height = cam->crop_current.height;
+               }
+
+               if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
+                       height = &f->fmt.pix.width;
+                       width = &f->fmt.pix.height;
+               } else {
+                       width = &f->fmt.pix.width;
+                       height = &f->fmt.pix.height;
+               }
+
+               /* stride line limitation */
+               *width -= *width % 8;
+               *height -= *height % 8;
+
+               if (*width == 0 || *height == 0) {
+                       pr_err("ERROR: v4l2 capture: width or height"
+                               " too small.\n");
+                       return -EINVAL;
+               }
+
+               if ((cam->crop_current.width / *width > 8) ||
+                   ((cam->crop_current.width / *width == 8) &&
+                    (cam->crop_current.width % *width))) {
+                       *width = cam->crop_current.width / 8;
+                       if (*width % 8)
+                               *width += 8 - *width % 8;
+                       pr_err("ERROR: v4l2 capture: width exceeds limit "
+                               "resize to %d.\n",
+                              *width);
+               }
+
+               if ((cam->crop_current.height / *height > 8) ||
+                   ((cam->crop_current.height / *height == 8) &&
+                    (cam->crop_current.height % *height))) {
+                       *height = cam->crop_current.height / 8;
+                       if (*height % 8)
+                               *height += 8 - *height % 8;
+                       pr_err("ERROR: v4l2 capture: height exceeds limit "
+                              "resize to %d.\n",
+                              *height);
+               }
+
+               switch (f->fmt.pix.pixelformat) {
+               case V4L2_PIX_FMT_RGB565:
+                       size = f->fmt.pix.width * f->fmt.pix.height * 2;
+                       bytesperline = f->fmt.pix.width * 2;
+                       break;
+               case V4L2_PIX_FMT_BGR24:
+                       size = f->fmt.pix.width * f->fmt.pix.height * 3;
+                       bytesperline = f->fmt.pix.width * 3;
+                       break;
+               case V4L2_PIX_FMT_RGB24:
+                       size = f->fmt.pix.width * f->fmt.pix.height * 3;
+                       bytesperline = f->fmt.pix.width * 3;
+                       break;
+               case V4L2_PIX_FMT_BGR32:
+                       size = f->fmt.pix.width * f->fmt.pix.height * 4;
+                       bytesperline = f->fmt.pix.width * 4;
+                       break;
+               case V4L2_PIX_FMT_RGB32:
+                       size = f->fmt.pix.width * f->fmt.pix.height * 4;
+                       bytesperline = f->fmt.pix.width * 4;
+                       break;
+               case V4L2_PIX_FMT_YUV422P:
+                       size = f->fmt.pix.width * f->fmt.pix.height * 2;
+                       bytesperline = f->fmt.pix.width;
+                       break;
+               case V4L2_PIX_FMT_UYVY:
+               case V4L2_PIX_FMT_YUYV:
+                       size = f->fmt.pix.width * f->fmt.pix.height * 2;
+                       bytesperline = f->fmt.pix.width * 2;
+                       break;
+               case V4L2_PIX_FMT_YUV420:
+                       size = f->fmt.pix.width * f->fmt.pix.height * 3 / 2;
+                       bytesperline = f->fmt.pix.width;
+                       break;
+               case V4L2_PIX_FMT_NV12:
+                       size = f->fmt.pix.width * f->fmt.pix.height * 3 / 2;
+                       bytesperline = f->fmt.pix.width;
+                       break;
+               default:
+                       break;
+               }
+
+               if (f->fmt.pix.bytesperline < bytesperline) {
+                       f->fmt.pix.bytesperline = bytesperline;
+               } else {
+                       bytesperline = f->fmt.pix.bytesperline;
+               }
+
+               if (f->fmt.pix.sizeimage < size) {
+                       f->fmt.pix.sizeimage = size;
+               } else {
+                       size = f->fmt.pix.sizeimage;
+               }
+
+               cam->v2f.fmt.pix = f->fmt.pix;
+
+               if (cam->v2f.fmt.pix.priv != 0) {
+                       if (copy_from_user(&cam->offset,
+                                          (void *)cam->v2f.fmt.pix.priv,
+                                          sizeof(cam->offset))) {
+                               retval = -EFAULT;
+                               break;
+                       }
+               }
+               break;
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+               pr_debug("   type=V4L2_BUF_TYPE_VIDEO_OVERLAY\n");
+               retval = verify_preview(cam, &f->fmt.win);
+               cam->win = f->fmt.win;
+               break;
+       default:
+               retval = -EINVAL;
+       }
+
+       pr_debug("End of %s: v2f pix widthxheight %d x %d\n",
+                __func__,
+                cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height);
+       pr_debug("End of %s: crop_bounds widthxheight %d x %d\n",
+                __func__,
+                cam->crop_bounds.width, cam->crop_bounds.height);
+       pr_debug("End of %s: crop_defrect widthxheight %d x %d\n",
+                __func__,
+                cam->crop_defrect.width, cam->crop_defrect.height);
+       pr_debug("End of %s: crop_current widthxheight %d x %d\n",
+                __func__,
+                cam->crop_current.width, cam->crop_current.height);
+
+       return retval;
+}
+
+/*!
+ * get control param
+ *
+ * @param cam         structure cam_data *
+ *
+ * @param c           structure v4l2_control *
+ *
+ * @return  status    0 success, EINVAL failed
+ */
+static int mxc_v4l2_g_ctrl(cam_data *cam, struct v4l2_control *c)
+{
+       int status = 0;
+
+       pr_debug("In MVC:mxc_v4l2_g_ctrl\n");
+
+       /* probably don't need to store the values that can be retrieved,
+        * locally, but they are for now. */
+       switch (c->id) {
+       case V4L2_CID_HFLIP:
+               /* This is handled in the ipu. */
+               if (cam->rotation == IPU_ROTATE_HORIZ_FLIP)
+                       c->value = 1;
+               break;
+       case V4L2_CID_VFLIP:
+               /* This is handled in the ipu. */
+               if (cam->rotation == IPU_ROTATE_VERT_FLIP)
+                       c->value = 1;
+               break;
+       case V4L2_CID_MXC_ROT:
+               /* This is handled in the ipu. */
+               c->value = cam->rotation;
+               break;
+       case V4L2_CID_BRIGHTNESS:
+               if (cam->sensor) {
+                       c->value = cam->bright;
+                       status = vidioc_int_g_ctrl(cam->sensor, c);
+                       cam->bright = c->value;
+               } else {
+                       pr_err("ERROR: v4l2 capture: slave not found!\n");
+                       status = -ENODEV;
+               }
+               break;
+       case V4L2_CID_HUE:
+               if (cam->sensor) {
+                       c->value = cam->hue;
+                       status = vidioc_int_g_ctrl(cam->sensor, c);
+                       cam->hue = c->value;
+               } else {
+                       pr_err("ERROR: v4l2 capture: slave not found!\n");
+                       status = -ENODEV;
+               }
+               break;
+       case V4L2_CID_CONTRAST:
+               if (cam->sensor) {
+                       c->value = cam->contrast;
+                       status = vidioc_int_g_ctrl(cam->sensor, c);
+                       cam->contrast = c->value;
+               } else {
+                       pr_err("ERROR: v4l2 capture: slave not found!\n");
+                       status = -ENODEV;
+               }
+               break;
+       case V4L2_CID_SATURATION:
+               if (cam->sensor) {
+                       c->value = cam->saturation;
+                       status = vidioc_int_g_ctrl(cam->sensor, c);
+                       cam->saturation = c->value;
+               } else {
+                       pr_err("ERROR: v4l2 capture: slave not found!\n");
+                       status = -ENODEV;
+               }
+               break;
+       case V4L2_CID_RED_BALANCE:
+               if (cam->sensor) {
+                       c->value = cam->red;
+                       status = vidioc_int_g_ctrl(cam->sensor, c);
+                       cam->red = c->value;
+               } else {
+                       pr_err("ERROR: v4l2 capture: slave not found!\n");
+                       status = -ENODEV;
+               }
+               break;
+       case V4L2_CID_BLUE_BALANCE:
+               if (cam->sensor) {
+                       c->value = cam->blue;
+                       status = vidioc_int_g_ctrl(cam->sensor, c);
+                       cam->blue = c->value;
+               } else {
+                       pr_err("ERROR: v4l2 capture: slave not found!\n");
+                       status = -ENODEV;
+               }
+               break;
+       case V4L2_CID_BLACK_LEVEL:
+               if (cam->sensor) {
+                       c->value = cam->ae_mode;
+                       status = vidioc_int_g_ctrl(cam->sensor, c);
+                       cam->ae_mode = c->value;
+               } else {
+                       pr_err("ERROR: v4l2 capture: slave not found!\n");
+                       status = -ENODEV;
+               }
+               break;
+       default:
+               pr_err("ERROR: v4l2 capture: unsupported ioctrl!\n");
+       }
+
+       return status;
+}
+
+/*!
+ * V4L2 - set_control function
+ *          V4L2_CID_PRIVATE_BASE is the extention for IPU preprocessing.
+ *          0 for normal operation
+ *          1 for vertical flip
+ *          2 for horizontal flip
+ *          3 for horizontal and vertical flip
+ *          4 for 90 degree rotation
+ * @param cam         structure cam_data *
+ *
+ * @param c           structure v4l2_control *
+ *
+ * @return  status    0 success, EINVAL failed
+ */
+static int mxc_v4l2_s_ctrl(cam_data *cam, struct v4l2_control *c)
+{
+       int ret = 0;
+       int tmp_rotation = IPU_ROTATE_NONE;
+
+       pr_debug("In MVC:mxc_v4l2_s_ctrl\n");
+
+       switch (c->id) {
+       case V4L2_CID_HFLIP:
+               /* This is done by the IPU */
+               if (c->value == 1) {
+                       if ((cam->rotation != IPU_ROTATE_VERT_FLIP) &&
+                           (cam->rotation != IPU_ROTATE_180))
+                               cam->rotation = IPU_ROTATE_HORIZ_FLIP;
+                       else
+                               cam->rotation = IPU_ROTATE_180;
+               } else {
+                       if (cam->rotation == IPU_ROTATE_HORIZ_FLIP)
+                               cam->rotation = IPU_ROTATE_NONE;
+                       if (cam->rotation == IPU_ROTATE_180)
+                               cam->rotation = IPU_ROTATE_VERT_FLIP;
+               }
+               break;
+       case V4L2_CID_VFLIP:
+               /* This is done by the IPU */
+               if (c->value == 1) {
+                       if ((cam->rotation != IPU_ROTATE_HORIZ_FLIP) &&
+                           (cam->rotation != IPU_ROTATE_180))
+                               cam->rotation = IPU_ROTATE_VERT_FLIP;
+                       else
+                               cam->rotation = IPU_ROTATE_180;
+               } else {
+                       if (cam->rotation == IPU_ROTATE_VERT_FLIP)
+                               cam->rotation = IPU_ROTATE_NONE;
+                       if (cam->rotation == IPU_ROTATE_180)
+                               cam->rotation = IPU_ROTATE_HORIZ_FLIP;
+               }
+               break;
+       case V4L2_CID_MXC_ROT:
+       case V4L2_CID_MXC_VF_ROT:
+               /* This is done by the IPU */
+               switch (c->value) {
+               case V4L2_MXC_ROTATE_NONE:
+                       tmp_rotation = IPU_ROTATE_NONE;
+                       break;
+               case V4L2_MXC_ROTATE_VERT_FLIP:
+                       tmp_rotation = IPU_ROTATE_VERT_FLIP;
+                       break;
+               case V4L2_MXC_ROTATE_HORIZ_FLIP:
+                       tmp_rotation = IPU_ROTATE_HORIZ_FLIP;
+                       break;
+               case V4L2_MXC_ROTATE_180:
+                       tmp_rotation = IPU_ROTATE_180;
+                       break;
+               case V4L2_MXC_ROTATE_90_RIGHT:
+                       tmp_rotation = IPU_ROTATE_90_RIGHT;
+                       break;
+               case V4L2_MXC_ROTATE_90_RIGHT_VFLIP:
+                       tmp_rotation = IPU_ROTATE_90_RIGHT_VFLIP;
+                       break;
+               case V4L2_MXC_ROTATE_90_RIGHT_HFLIP:
+                       tmp_rotation = IPU_ROTATE_90_RIGHT_HFLIP;
+                       break;
+               case V4L2_MXC_ROTATE_90_LEFT:
+                       tmp_rotation = IPU_ROTATE_90_LEFT;
+                       break;
+               default:
+                       ret = -EINVAL;
+               }
+
+               if (c->id == V4L2_CID_MXC_VF_ROT)
+                       cam->vf_rotation = tmp_rotation;
+               else
+                       cam->rotation = tmp_rotation;
+
+               break;
+       case V4L2_CID_HUE:
+               if (cam->sensor) {
+                       cam->hue = c->value;
+                       ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi,
+                                              true, true);
+                       ret = vidioc_int_s_ctrl(cam->sensor, c);
+                       ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi,
+                                              false, false);
+               } else {
+                       pr_err("ERROR: v4l2 capture: slave not found!\n");
+                       ret = -ENODEV;
+               }
+               break;
+       case V4L2_CID_CONTRAST:
+               if (cam->sensor) {
+                       cam->contrast = c->value;
+                       ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi,
+                                              true, true);
+                       ret = vidioc_int_s_ctrl(cam->sensor, c);
+                       ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi,
+                                              false, false);
+               } else {
+                       pr_err("ERROR: v4l2 capture: slave not found!\n");
+                       ret = -ENODEV;
+               }
+               break;
+       case V4L2_CID_BRIGHTNESS:
+               if (cam->sensor) {
+                       cam->bright = c->value;
+                       ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi,
+                                              true, true);
+                       ret = vidioc_int_s_ctrl(cam->sensor, c);
+                       ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi,
+                                              false, false);
+               } else {
+                       pr_err("ERROR: v4l2 capture: slave not found!\n");
+                       ret = -ENODEV;
+               }
+               break;
+       case V4L2_CID_SATURATION:
+               if (cam->sensor) {
+                       cam->saturation = c->value;
+                       ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi,
+                                              true, true);
+                       ret = vidioc_int_s_ctrl(cam->sensor, c);
+                       ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi,
+                                              false, false);
+               } else {
+                       pr_err("ERROR: v4l2 capture: slave not found!\n");
+                       ret = -ENODEV;
+               }
+               break;
+       case V4L2_CID_RED_BALANCE:
+               if (cam->sensor) {
+                       cam->red = c->value;
+                       ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi,
+                                              true, true);
+                       ret = vidioc_int_s_ctrl(cam->sensor, c);
+                       ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi,
+                                              false, false);
+               } else {
+                       pr_err("ERROR: v4l2 capture: slave not found!\n");
+                       ret = -ENODEV;
+               }
+               break;
+       case V4L2_CID_BLUE_BALANCE:
+               if (cam->sensor) {
+                       cam->blue = c->value;
+                       ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi,
+                                              true, true);
+                       ret = vidioc_int_s_ctrl(cam->sensor, c);
+                       ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi,
+                                              false, false);
+               } else {
+                       pr_err("ERROR: v4l2 capture: slave not found!\n");
+                       ret = -ENODEV;
+               }
+               break;
+       case V4L2_CID_EXPOSURE:
+               if (cam->sensor) {
+                       cam->ae_mode = c->value;
+                       ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi,
+                                              true, true);
+                       ret = vidioc_int_s_ctrl(cam->sensor, c);
+                       ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi,
+                                              false, false);
+               } else {
+                       pr_err("ERROR: v4l2 capture: slave not found!\n");
+                       ret = -ENODEV;
+               }
+               break;
+       case V4L2_CID_MXC_FLASH:
+#ifdef CONFIG_MXC_IPU_V1
+               ipu_csi_flash_strobe(true);
+#endif
+               break;
+       default:
+               pr_debug("   default case\n");
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+/*!
+ * V4L2 - mxc_v4l2_s_param function
+ * Allows setting of capturemode and frame rate.
+ *
+ * @param cam         structure cam_data *
+ * @param parm        structure v4l2_streamparm *
+ *
+ * @return  status    0 success, EINVAL failed
+ */
+static int mxc_v4l2_s_param(cam_data *cam, struct v4l2_streamparm *parm)
+{
+       struct v4l2_ifparm ifparm;
+       struct v4l2_format cam_fmt;
+       struct v4l2_streamparm currentparm;
+       ipu_csi_signal_cfg_t csi_param;
+       int err = 0;
+
+       pr_debug("In mxc_v4l2_s_param\n");
+
+       if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+               pr_err(KERN_ERR "mxc_v4l2_s_param invalid type\n");
+               return -EINVAL;
+       }
+
+       /* Stop the viewfinder */
+       if (cam->overlay_on == true) {
+               stop_preview(cam);
+       }
+
+       currentparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+       /* First check that this device can support the changes requested. */
+       err = vidioc_int_g_parm(cam->sensor, &currentparm);
+       if (err) {
+               pr_err("%s: vidioc_int_g_parm returned an error %d\n",
+                       __func__, err);
+               goto exit;
+       }
+
+       pr_debug("   Current capabilities are %x\n",
+                       currentparm.parm.capture.capability);
+       pr_debug("   Current capturemode is %d  change to %d\n",
+                       currentparm.parm.capture.capturemode,
+                       parm->parm.capture.capturemode);
+       pr_debug("   Current framerate is %d  change to %d\n",
+                       currentparm.parm.capture.timeperframe.denominator,
+                       parm->parm.capture.timeperframe.denominator);
+
+       /* This will change any camera settings needed. */
+       ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, true, true);
+       err = vidioc_int_s_parm(cam->sensor, parm);
+       ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, false, false);
+       if (err) {
+               pr_err("%s: vidioc_int_s_parm returned an error %d\n",
+                       __func__, err);
+               goto exit;
+       }
+
+       /* If resolution changed, need to re-program the CSI */
+       /* Get new values. */
+       vidioc_int_g_ifparm(cam->sensor, &ifparm);
+
+       csi_param.data_width = 0;
+       csi_param.clk_mode = 0;
+       csi_param.ext_vsync = 0;
+       csi_param.Vsync_pol = 0;
+       csi_param.Hsync_pol = 0;
+       csi_param.pixclk_pol = 0;
+       csi_param.data_pol = 0;
+       csi_param.sens_clksrc = 0;
+       csi_param.pack_tight = 0;
+       csi_param.force_eof = 0;
+       csi_param.data_en_pol = 0;
+       csi_param.data_fmt = 0;
+       csi_param.csi = 0;
+       csi_param.mclk = 0;
+
+       /* This may not work on other platforms. Check when adding a new one.*/
+       pr_debug("   clock_curr=mclk=%d\n", ifparm.u.bt656.clock_curr);
+       if (ifparm.u.bt656.clock_curr == 0) {
+               csi_param.clk_mode = IPU_CSI_CLK_MODE_CCIR656_INTERLACED;
+       } else {
+               csi_param.clk_mode = IPU_CSI_CLK_MODE_GATED_CLK;
+       }
+
+       csi_param.pixclk_pol = ifparm.u.bt656.latch_clk_inv;
+
+       if (ifparm.u.bt656.mode == V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT) {
+               csi_param.data_width = IPU_CSI_DATA_WIDTH_8;
+       } else if (ifparm.u.bt656.mode
+                               == V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT) {
+               csi_param.data_width = IPU_CSI_DATA_WIDTH_10;
+       } else {
+               csi_param.data_width = IPU_CSI_DATA_WIDTH_8;
+       }
+
+       csi_param.Vsync_pol = ifparm.u.bt656.nobt_vs_inv;
+       csi_param.Hsync_pol = ifparm.u.bt656.nobt_hs_inv;
+       csi_param.ext_vsync = ifparm.u.bt656.bt_sync_correct;
+
+       /* if the capturemode changed, the size bounds will have changed. */
+       cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt);
+       pr_debug("   g_fmt_cap returns widthxheight of input as %d x %d\n",
+                       cam_fmt.fmt.pix.width, cam_fmt.fmt.pix.height);
+
+       csi_param.data_fmt = cam_fmt.fmt.pix.pixelformat;
+
+       cam->crop_bounds.top = cam->crop_bounds.left = 0;
+       cam->crop_bounds.width = cam_fmt.fmt.pix.width;
+       cam->crop_bounds.height = cam_fmt.fmt.pix.height;
+
+       /*
+        * Set the default current cropped resolution to be the same with
+        * the cropping boundary(except for tvin module).
+        */
+       if (cam->device_type != 1) {
+               cam->crop_current.width = cam->crop_bounds.width;
+               cam->crop_current.height = cam->crop_bounds.height;
+       }
+
+       /* This essentially loses the data at the left and bottom of the image
+        * giving a digital zoom image, if crop_current is less than the full
+        * size of the image. */
+       ipu_csi_set_window_size(cam->crop_current.width,
+                               cam->crop_current.height, cam->csi);
+       ipu_csi_set_window_pos(cam->crop_current.left,
+                              cam->crop_current.top,
+                              cam->csi);
+       ipu_csi_init_interface(cam->crop_bounds.width,
+                              cam->crop_bounds.height,
+                              cam_fmt.fmt.pix.pixelformat, csi_param);
+
+
+exit:
+       if (cam->overlay_on == true)
+               start_preview(cam);
+
+       return err;
+}
+
+/*!
+ * V4L2 - mxc_v4l2_s_std function
+ *
+ * Sets the TV standard to be used.
+ *
+ * @param cam        structure cam_data *
+ * @param parm       structure v4l2_streamparm *
+ *
+ * @return  status    0 success, EINVAL failed
+ */
+static int mxc_v4l2_s_std(cam_data *cam, v4l2_std_id e)
+{
+       pr_debug("In mxc_v4l2_s_std %Lx\n", e);
+       if (e == V4L2_STD_PAL) {
+               pr_debug("   Setting standard to PAL %Lx\n", V4L2_STD_PAL);
+               cam->standard.id = V4L2_STD_PAL;
+               video_index = TV_PAL;
+       } else if (e == V4L2_STD_NTSC) {
+               pr_debug("   Setting standard to NTSC %Lx\n",
+                               V4L2_STD_NTSC);
+               /* Get rid of the white dot line in NTSC signal input */
+               cam->standard.id = V4L2_STD_NTSC;
+               video_index = TV_NTSC;
+       } else {
+               cam->standard.id = V4L2_STD_ALL;
+               video_index = TV_NOT_LOCKED;
+               pr_err("ERROR: unrecognized std! %Lx (PAL=%Lx, NTSC=%Lx\n",
+                       e, V4L2_STD_PAL, V4L2_STD_NTSC);
+       }
+
+       cam->standard.index = video_index;
+       strcpy(cam->standard.name, video_fmts[video_index].name);
+       cam->crop_bounds.width = video_fmts[video_index].raw_width;
+       cam->crop_bounds.height = video_fmts[video_index].raw_height;
+       cam->crop_current.width = video_fmts[video_index].active_width;
+       cam->crop_current.height = video_fmts[video_index].active_height;
+       cam->crop_current.top = video_fmts[video_index].active_top;
+       cam->crop_current.left = video_fmts[video_index].active_left;
+
+       return 0;
+}
+
+/*!
+ * V4L2 - mxc_v4l2_g_std function
+ *
+ * Gets the TV standard from the TV input device.
+ *
+ * @param cam        structure cam_data *
+ *
+ * @param e          structure v4l2_streamparm *
+ *
+ * @return  status    0 success, EINVAL failed
+ */
+static int mxc_v4l2_g_std(cam_data *cam, v4l2_std_id *e)
+{
+       struct v4l2_format tv_fmt;
+
+       pr_debug("In mxc_v4l2_g_std\n");
+
+       if (cam->device_type == 1) {
+               /* Use this function to get what the TV-In device detects the
+                * format to be. pixelformat is used to return the std value
+                * since the interface has no vidioc_g_std.*/
+               tv_fmt.type = V4L2_BUF_TYPE_PRIVATE;
+               vidioc_int_g_fmt_cap(cam->sensor, &tv_fmt);
+
+               /* If the TV-in automatically detects the standard, then if it
+                * changes, the settings need to change. */
+               if (cam->standard_autodetect) {
+                       if (cam->standard.id != tv_fmt.fmt.pix.pixelformat) {
+                               pr_debug("MVC: mxc_v4l2_g_std: "
+                                       "Changing standard\n");
+                               mxc_v4l2_s_std(cam, tv_fmt.fmt.pix.pixelformat);
+                       }
+               }
+
+               *e = tv_fmt.fmt.pix.pixelformat;
+       }
+
+       return 0;
+}
+
+/*!
+ * Dequeue one V4L capture buffer
+ *
+ * @param cam         structure cam_data *
+ * @param buf         structure v4l2_buffer *
+ *
+ * @return  status    0 success, EINVAL invalid frame number,
+ *                    ETIME timeout, ERESTARTSYS interrupted by user
+ */
+static int mxc_v4l_dqueue(cam_data *cam, struct v4l2_buffer *buf)
+{
+       int retval = 0;
+       struct mxc_v4l_frame *frame;
+       unsigned long lock_flags;
+
+       pr_debug("In MVC:mxc_v4l_dqueue\n");
+
+       if (!wait_event_interruptible_timeout(cam->enc_queue,
+                                             cam->enc_counter != 0, 10 * HZ)) {
+               pr_err("ERROR: v4l2 capture: mxc_v4l_dqueue timeout "
+                       "enc_counter %x\n",
+                      cam->enc_counter);
+               return -ETIME;
+       } else if (signal_pending(current)) {
+               pr_err("ERROR: v4l2 capture: mxc_v4l_dqueue() "
+                       "interrupt received\n");
+               return -ERESTARTSYS;
+       }
+
+       spin_lock_irqsave(&cam->dqueue_int_lock, lock_flags);
+
+       cam->enc_counter--;
+
+       frame = list_entry(cam->done_q.next, struct mxc_v4l_frame, queue);
+       list_del(cam->done_q.next);
+       if (frame->buffer.flags & V4L2_BUF_FLAG_DONE) {
+               frame->buffer.flags &= ~V4L2_BUF_FLAG_DONE;
+       } else if (frame->buffer.flags & V4L2_BUF_FLAG_QUEUED) {
+               pr_err("ERROR: v4l2 capture: VIDIOC_DQBUF: "
+                       "Buffer not filled.\n");
+               frame->buffer.flags &= ~V4L2_BUF_FLAG_QUEUED;
+               retval = -EINVAL;
+       } else if ((frame->buffer.flags & 0x7) == V4L2_BUF_FLAG_MAPPED) {
+               pr_err("ERROR: v4l2 capture: VIDIOC_DQBUF: "
+                       "Buffer not queued.\n");
+               retval = -EINVAL;
+       }
+
+       buf->bytesused = cam->v2f.fmt.pix.sizeimage;
+       buf->index = frame->index;
+       buf->flags = frame->buffer.flags;
+       buf->m = cam->frame[frame->index].buffer.m;
+       buf->timestamp = cam->frame[frame->index].buffer.timestamp;
+
+       spin_unlock_irqrestore(&cam->dqueue_int_lock, lock_flags);
+       return retval;
+}
+
+/*!
+ * V4L interface - open function
+ *
+ * @param file         structure file *
+ *
+ * @return  status    0 success, ENODEV invalid device instance,
+ *                    ENODEV timeout, ERESTARTSYS interrupted by user
+ */
+static int mxc_v4l_open(struct file *file)
+{
+       struct v4l2_ifparm ifparm;
+       struct v4l2_format cam_fmt;
+       ipu_csi_signal_cfg_t csi_param;
+       struct video_device *dev = video_devdata(file);
+       cam_data *cam = video_get_drvdata(dev);
+       int err = 0;
+
+       pr_debug("\nIn MVC: mxc_v4l_open\n");
+       pr_debug("   device name is %s\n", dev->name);
+
+       if (!cam) {
+               pr_err("ERROR: v4l2 capture: Internal error, "
+                       "cam_data not found!\n");
+               return -EBADF;
+       }
+
+       if (cam->sensor == NULL ||
+           cam->sensor->type != v4l2_int_type_slave) {
+               pr_err("ERROR: v4l2 capture: slave not found!\n");
+               return -EAGAIN;
+       }
+
+       down(&cam->busy_lock);
+       err = 0;
+       if (signal_pending(current))
+               goto oops;
+
+       if (cam->open_count++ == 0) {
+               wait_event_interruptible(cam->power_queue,
+                                        cam->low_power == false);
+
+               if (strcmp(mxc_capture_inputs[cam->current_input].name,
+                          "CSI MEM") == 0) {
+#if defined(CONFIG_MXC_IPU_CSI_ENC) || defined(CONFIG_MXC_IPU_CSI_ENC_MODULE)
+                       err = csi_enc_select(cam);
+#endif
+               } else if (strcmp(mxc_capture_inputs[cam->current_input].name,
+                                 "CSI IC MEM") == 0) {
+#if defined(CONFIG_MXC_IPU_PRP_ENC) || defined(CONFIG_MXC_IPU_PRP_ENC_MODULE)
+                       err = prp_enc_select(cam);
+#endif
+               }
+
+               cam->enc_counter = 0;
+               INIT_LIST_HEAD(&cam->ready_q);
+               INIT_LIST_HEAD(&cam->working_q);
+               INIT_LIST_HEAD(&cam->done_q);
+
+               vidioc_int_g_ifparm(cam->sensor, &ifparm);
+
+               csi_param.sens_clksrc = 0;
+
+               csi_param.clk_mode = 0;
+               csi_param.data_pol = 0;
+               csi_param.ext_vsync = 0;
+
+               csi_param.pack_tight = 0;
+               csi_param.force_eof = 0;
+               csi_param.data_en_pol = 0;
+               csi_param.mclk = ifparm.u.bt656.clock_curr;
+
+               csi_param.pixclk_pol = ifparm.u.bt656.latch_clk_inv;
+
+               /* Once we handle multiple inputs this will need to change. */
+               csi_param.csi = 0;
+
+               if (ifparm.u.bt656.mode
+                               == V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT)
+                       csi_param.data_width = IPU_CSI_DATA_WIDTH_8;
+               else if (ifparm.u.bt656.mode
+                               == V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT)
+                       csi_param.data_width = IPU_CSI_DATA_WIDTH_10;
+               else
+                       csi_param.data_width = IPU_CSI_DATA_WIDTH_8;
+
+
+               csi_param.Vsync_pol = ifparm.u.bt656.nobt_vs_inv;
+               csi_param.Hsync_pol = ifparm.u.bt656.nobt_hs_inv;
+
+               csi_param.csi = cam->csi;
+
+               cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+               vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt);
+
+               /* Reset the sizes.  Needed to prevent carryover of last
+                * operation.*/
+               cam->crop_bounds.top = cam->crop_bounds.left = 0;
+               cam->crop_bounds.width = cam_fmt.fmt.pix.width;
+               cam->crop_bounds.height = cam_fmt.fmt.pix.height;
+
+               /* This also is the max crop size for this device. */
+               cam->crop_defrect.top = cam->crop_defrect.left = 0;
+               cam->crop_defrect.width = cam_fmt.fmt.pix.width;
+               cam->crop_defrect.height = cam_fmt.fmt.pix.height;
+
+               /* At this point, this is also the current image size. */
+               cam->crop_current.top = cam->crop_current.left = 0;
+               cam->crop_current.width = cam_fmt.fmt.pix.width;
+               cam->crop_current.height = cam_fmt.fmt.pix.height;
+
+               pr_debug("End of %s: v2f pix widthxheight %d x %d\n",
+                       __func__,
+                       cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height);
+               pr_debug("End of %s: crop_bounds widthxheight %d x %d\n",
+                       __func__,
+                       cam->crop_bounds.width, cam->crop_bounds.height);
+               pr_debug("End of %s: crop_defrect widthxheight %d x %d\n",
+                       __func__,
+                       cam->crop_defrect.width, cam->crop_defrect.height);
+               pr_debug("End of %s: crop_current widthxheight %d x %d\n",
+                       __func__,
+                       cam->crop_current.width, cam->crop_current.height);
+
+               csi_param.data_fmt = cam_fmt.fmt.pix.pixelformat;
+               pr_debug("On Open: Input to ipu size is %d x %d\n",
+                               cam_fmt.fmt.pix.width, cam_fmt.fmt.pix.height);
+               ipu_csi_set_window_size(cam->crop_current.width,
+                                       cam->crop_current.width,
+                                       cam->csi);
+               ipu_csi_set_window_pos(cam->crop_current.left,
+                                       cam->crop_current.top,
+                                       cam->csi);
+               ipu_csi_init_interface(cam->crop_bounds.width,
+                                       cam->crop_bounds.height,
+                                       cam_fmt.fmt.pix.pixelformat,
+                                       csi_param);
+
+               ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi,
+                                      true, true);
+               vidioc_int_init(cam->sensor);
+
+               ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi,
+                                      false, false);
+}
+
+       file->private_data = dev;
+
+      oops:
+       up(&cam->busy_lock);
+       return err;
+}
+
+/*!
+ * V4L interface - close function
+ *
+ * @param file     struct file *
+ *
+ * @return         0 success
+ */
+static int mxc_v4l_close(struct file *file)
+{
+       struct video_device *dev = video_devdata(file);
+       int err = 0;
+       cam_data *cam = video_get_drvdata(dev);
+
+       pr_debug("In MVC:mxc_v4l_close\n");
+
+       if (!cam) {
+               pr_err("ERROR: v4l2 capture: Internal error, "
+                       "cam_data not found!\n");
+               return -EBADF;
+       }
+
+       /* for the case somebody hit the ctrl C */
+       if (cam->overlay_pid == current->pid) {
+               err = stop_preview(cam);
+               cam->overlay_on = false;
+       }
+       if (cam->capture_pid == current->pid) {
+               err |= mxc_streamoff(cam);
+               wake_up_interruptible(&cam->enc_queue);
+       }
+
+       if (--cam->open_count == 0) {
+               wait_event_interruptible(cam->power_queue,
+                                        cam->low_power == false);
+               pr_info("mxc_v4l_close: release resource\n");
+
+               if (strcmp(mxc_capture_inputs[cam->current_input].name,
+                          "CSI MEM") == 0) {
+#if defined(CONFIG_MXC_IPU_CSI_ENC) || defined(CONFIG_MXC_IPU_CSI_ENC_MODULE)
+                       err |= csi_enc_deselect(cam);
+#endif
+               } else if (strcmp(mxc_capture_inputs[cam->current_input].name,
+                                 "CSI IC MEM") == 0) {
+#if defined(CONFIG_MXC_IPU_PRP_ENC) || defined(CONFIG_MXC_IPU_PRP_ENC_MODULE)
+                       err |= prp_enc_deselect(cam);
+#endif
+               }
+
+               mxc_free_frame_buf(cam);
+               file->private_data = NULL;
+
+               /* capture off */
+               wake_up_interruptible(&cam->enc_queue);
+               mxc_free_frames(cam);
+               cam->enc_counter++;
+       }
+
+       return err;
+}
+
+#if defined(CONFIG_MXC_IPU_PRP_ENC) || defined(CONFIG_MXC_IPU_CSI_ENC) || \
+    defined(CONFIG_MXC_IPU_PRP_ENC_MODULE) || \
+    defined(CONFIG_MXC_IPU_CSI_ENC_MODULE)
+/*
+ * V4L interface - read function
+ *
+ * @param file       struct file *
+ * @param read buf   char *
+ * @param count      size_t
+ * @param ppos       structure loff_t *
+ *
+ * @return           bytes read
+ */
+static ssize_t mxc_v4l_read(struct file *file, char *buf, size_t count,
+                           loff_t *ppos)
+{
+       int err = 0;
+       u8 *v_address[2];
+       struct video_device *dev = video_devdata(file);
+       cam_data *cam = video_get_drvdata(dev);
+
+       if (down_interruptible(&cam->busy_lock))
+               return -EINTR;
+
+       /* Stop the viewfinder */
+       if (cam->overlay_on == true)
+               stop_preview(cam);
+
+       v_address[0] = dma_alloc_coherent(0,
+                                      PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage),
+                                      &cam->still_buf[0],
+                                      GFP_DMA | GFP_KERNEL);
+
+       v_address[1] = dma_alloc_coherent(0,
+                                      PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage),
+                                      &cam->still_buf[1],
+                                      GFP_DMA | GFP_KERNEL);
+
+       if (!v_address[0] || !v_address[1]) {
+               err = -ENOBUFS;
+               goto exit0;
+       }
+
+       err = prp_still_select(cam);
+       if (err != 0) {
+               err = -EIO;
+               goto exit0;
+       }
+
+       cam->still_counter = 0;
+       err = cam->csi_start(cam);
+       if (err != 0) {
+               err = -EIO;
+               goto exit1;
+       }
+
+       if (!wait_event_interruptible_timeout(cam->still_queue,
+                                             cam->still_counter != 0,
+                                             10 * HZ)) {
+               pr_err("ERROR: v4l2 capture: mxc_v4l_read timeout counter %x\n",
+                      cam->still_counter);
+               err = -ETIME;
+               goto exit1;
+       }
+       err = copy_to_user(buf, v_address[1], cam->v2f.fmt.pix.sizeimage);
+
+      exit1:
+       prp_still_deselect(cam);
+
+      exit0:
+       if (v_address[0] != 0)
+               dma_free_coherent(0, cam->v2f.fmt.pix.sizeimage, v_address[0],
+                                 cam->still_buf[0]);
+       if (v_address[1] != 0)
+               dma_free_coherent(0, cam->v2f.fmt.pix.sizeimage, v_address[1],
+                                 cam->still_buf[1]);
+
+       cam->still_buf[0] = cam->still_buf[1] = 0;
+
+       if (cam->overlay_on == true) {
+               start_preview(cam);
+       }
+
+       up(&cam->busy_lock);
+       if (err < 0)
+               return err;
+
+       return cam->v2f.fmt.pix.sizeimage - err;
+}
+#endif
+
+/*!
+ * V4L interface - ioctl function
+ *
+ * @param file       struct file*
+ *
+ * @param ioctlnr    unsigned int
+ *
+ * @param arg        void*
+ *
+ * @return           0 success, ENODEV for invalid device instance,
+ *                   -1 for other errors.
+ */
+static long mxc_v4l_do_ioctl(struct file *file,
+                           unsigned int ioctlnr, void *arg)
+{
+       struct video_device *dev = video_devdata(file);
+       cam_data *cam = video_get_drvdata(dev);
+       int retval = 0;
+       unsigned long lock_flags;
+
+       pr_debug("In MVC: mxc_v4l_do_ioctl %x\n", ioctlnr);
+       wait_event_interruptible(cam->power_queue, cam->low_power == false);
+       /* make this _really_ smp-safe */
+       if (down_interruptible(&cam->busy_lock))
+               return -EBUSY;
+
+       switch (ioctlnr) {
+       /*!
+        * V4l2 VIDIOC_QUERYCAP ioctl
+        */
+       case VIDIOC_QUERYCAP: {
+               struct v4l2_capability *cap = arg;
+               pr_debug("   case VIDIOC_QUERYCAP\n");
+               strcpy(cap->driver, "mxc_v4l2");
+               cap->version = KERNEL_VERSION(0, 1, 11);
+               cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+                                   V4L2_CAP_VIDEO_OVERLAY |
+                                   V4L2_CAP_STREAMING |
+                                   V4L2_CAP_READWRITE;
+               cap->card[0] = '\0';
+               cap->bus_info[0] = '\0';
+               break;
+       }
+
+       /*!
+        * V4l2 VIDIOC_G_FMT ioctl
+        */
+       case VIDIOC_G_FMT: {
+               struct v4l2_format *gf = arg;
+               pr_debug("   case VIDIOC_G_FMT\n");
+               retval = mxc_v4l2_g_fmt(cam, gf);
+               break;
+       }
+
+       /*!
+        * V4l2 VIDIOC_S_FMT ioctl
+        */
+       case VIDIOC_S_FMT: {
+               struct v4l2_format *sf = arg;
+               pr_debug("   case VIDIOC_S_FMT\n");
+               retval = mxc_v4l2_s_fmt(cam, sf);
+               break;
+       }
+
+       /*!
+        * V4l2 VIDIOC_REQBUFS ioctl
+        */
+       case VIDIOC_REQBUFS: {
+               struct v4l2_requestbuffers *req = arg;
+               pr_debug("   case VIDIOC_REQBUFS\n");
+
+               if (req->count > FRAME_NUM) {
+                       pr_err("ERROR: v4l2 capture: VIDIOC_REQBUFS: "
+                              "not enough buffers\n");
+                       req->count = FRAME_NUM;
+               }
+
+               if ((req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
+                   (req->memory != V4L2_MEMORY_MMAP)) {
+                       pr_err("ERROR: v4l2 capture: VIDIOC_REQBUFS: "
+                              "wrong buffer type\n");
+                       retval = -EINVAL;
+                       break;
+               }
+
+               mxc_streamoff(cam);
+               mxc_free_frame_buf(cam);
+               cam->enc_counter = 0;
+               INIT_LIST_HEAD(&cam->ready_q);
+               INIT_LIST_HEAD(&cam->working_q);
+               INIT_LIST_HEAD(&cam->done_q);
+
+               retval = mxc_allocate_frame_buf(cam, req->count);
+               break;
+       }
+
+       /*!
+        * V4l2 VIDIOC_QUERYBUF ioctl
+        */
+       case VIDIOC_QUERYBUF: {
+               struct v4l2_buffer *buf = arg;
+               int index = buf->index;
+               pr_debug("   case VIDIOC_QUERYBUF\n");
+
+               if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+                       pr_err("ERROR: v4l2 capture: "
+                              "VIDIOC_QUERYBUFS: "
+                              "wrong buffer type\n");
+                       retval = -EINVAL;
+                       break;
+               }
+
+               memset(buf, 0, sizeof(buf));
+               buf->index = index;
+
+               down(&cam->param_lock);
+               retval = mxc_v4l2_buffer_status(cam, buf);
+               up(&cam->param_lock);
+               break;
+       }
+
+       /*!
+        * V4l2 VIDIOC_QBUF ioctl
+        */
+       case VIDIOC_QBUF: {
+               struct v4l2_buffer *buf = arg;
+               int index = buf->index;
+               pr_debug("   case VIDIOC_QBUF\n");
+
+               spin_lock_irqsave(&cam->queue_int_lock, lock_flags);
+               if ((cam->frame[index].buffer.flags & 0x7) ==
+                   V4L2_BUF_FLAG_MAPPED) {
+                       cam->frame[index].buffer.flags |=
+                           V4L2_BUF_FLAG_QUEUED;
+                       list_add_tail(&cam->frame[index].queue,
+                                     &cam->ready_q);
+               } else if (cam->frame[index].buffer.
+                          flags & V4L2_BUF_FLAG_QUEUED) {
+                       pr_err("ERROR: v4l2 capture: VIDIOC_QBUF: "
+                              "buffer already queued\n");
+                       retval = -EINVAL;
+               } else if (cam->frame[index].buffer.
+                          flags & V4L2_BUF_FLAG_DONE) {
+                       pr_err("ERROR: v4l2 capture: VIDIOC_QBUF: "
+                              "overwrite done buffer.\n");
+                       cam->frame[index].buffer.flags &=
+                           ~V4L2_BUF_FLAG_DONE;
+                       cam->frame[index].buffer.flags |=
+                           V4L2_BUF_FLAG_QUEUED;
+                       retval = -EINVAL;
+               }
+
+               buf->flags = cam->frame[index].buffer.flags;
+               spin_unlock_irqrestore(&cam->queue_int_lock, lock_flags);
+               break;
+       }
+
+       /*!
+        * V4l2 VIDIOC_DQBUF ioctl
+        */
+       case VIDIOC_DQBUF: {
+               struct v4l2_buffer *buf = arg;
+               pr_debug("   case VIDIOC_DQBUF\n");
+
+               if ((cam->enc_counter == 0) &&
+                       (file->f_flags & O_NONBLOCK)) {
+                       retval = -EAGAIN;
+                       break;
+               }
+
+               retval = mxc_v4l_dqueue(cam, buf);
+
+               break;
+       }
+
+       /*!
+        * V4l2 VIDIOC_STREAMON ioctl
+        */
+       case VIDIOC_STREAMON: {
+               pr_debug("   case VIDIOC_STREAMON\n");
+               retval = mxc_streamon(cam);
+               break;
+       }
+
+       /*!
+        * V4l2 VIDIOC_STREAMOFF ioctl
+        */
+       case VIDIOC_STREAMOFF: {
+               pr_debug("   case VIDIOC_STREAMOFF\n");
+               retval = mxc_streamoff(cam);
+               break;
+       }
+
+       /*!
+        * V4l2 VIDIOC_G_CTRL ioctl
+        */
+       case VIDIOC_G_CTRL: {
+               pr_debug("   case VIDIOC_G_CTRL\n");
+               retval = mxc_v4l2_g_ctrl(cam, arg);
+               break;
+       }
+
+       /*!
+        * V4l2 VIDIOC_S_CTRL ioctl
+        */
+       case VIDIOC_S_CTRL: {
+               pr_debug("   case VIDIOC_S_CTRL\n");
+               retval = mxc_v4l2_s_ctrl(cam, arg);
+               break;
+       }
+
+       /*!
+        * V4l2 VIDIOC_CROPCAP ioctl
+        */
+       case VIDIOC_CROPCAP: {
+               struct v4l2_cropcap *cap = arg;
+               pr_debug("   case VIDIOC_CROPCAP\n");
+               if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+                   cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) {
+                       retval = -EINVAL;
+                       break;
+               }
+               cap->bounds = cam->crop_bounds;
+               cap->defrect = cam->crop_defrect;
+               break;
+       }
+
+       /*!
+        * V4l2 VIDIOC_G_CROP ioctl
+        */
+       case VIDIOC_G_CROP: {
+               struct v4l2_crop *crop = arg;
+               pr_debug("   case VIDIOC_G_CROP\n");
+
+               if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+                   crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) {
+                       retval = -EINVAL;
+                       break;
+               }
+               crop->c = cam->crop_current;
+               break;
+       }
+
+       /*!
+        * V4l2 VIDIOC_S_CROP ioctl
+        */
+       case VIDIOC_S_CROP: {
+               struct v4l2_crop *crop = arg;
+               struct v4l2_rect *b = &cam->crop_bounds;
+               pr_debug("   case VIDIOC_S_CROP\n");
+
+               if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+                   crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) {
+                       retval = -EINVAL;
+                       break;
+               }
+
+               crop->c.top = (crop->c.top < b->top) ? b->top
+                             : crop->c.top;
+               if (crop->c.top > b->top + b->height)
+                       crop->c.top = b->top + b->height - 1;
+               if (crop->c.height > b->top + b->height - crop->c.top)
+                       crop->c.height =
+                               b->top + b->height - crop->c.top;
+
+               crop->c.left = (crop->c.left < b->left) ? b->left
+                   : crop->c.left;
+               if (crop->c.left > b->left + b->width)
+                       crop->c.left = b->left + b->width - 1;
+               if (crop->c.width > b->left - crop->c.left + b->width)
+                       crop->c.width =
+                               b->left - crop->c.left + b->width;
+
+               crop->c.width -= crop->c.width % 8;
+               crop->c.left -= crop->c.left % 4;
+               cam->crop_current = crop->c;
+
+               pr_debug("   Cropping Input to ipu size %d x %d\n",
+                               cam->crop_current.width,
+                               cam->crop_current.height);
+               ipu_csi_set_window_size(cam->crop_current.width,
+                                       cam->crop_current.height,
+                                       cam->csi);
+               ipu_csi_set_window_pos(cam->crop_current.left,
+                                      cam->crop_current.top,
+                                      cam->csi);
+               break;
+       }
+
+       /*!
+        * V4l2 VIDIOC_OVERLAY ioctl
+        */
+       case VIDIOC_OVERLAY: {
+               int *on = arg;
+               pr_debug("   VIDIOC_OVERLAY on=%d\n", *on);
+               if (*on) {
+                       cam->overlay_on = true;
+                       cam->overlay_pid = current->pid;
+                       retval = start_preview(cam);
+               }
+               if (!*on) {
+                       retval = stop_preview(cam);
+                       cam->overlay_on = false;
+               }
+               break;
+       }
+
+       /*!
+        * V4l2 VIDIOC_G_FBUF ioctl
+        */
+       case VIDIOC_G_FBUF: {
+               struct v4l2_framebuffer *fb = arg;
+               pr_debug("   case VIDIOC_G_FBUF\n");
+               *fb = cam->v4l2_fb;
+               fb->capability = V4L2_FBUF_CAP_EXTERNOVERLAY;
+               break;
+       }
+
+       /*!
+        * V4l2 VIDIOC_S_FBUF ioctl
+        */
+       case VIDIOC_S_FBUF: {
+               struct v4l2_framebuffer *fb = arg;
+               pr_debug("   case VIDIOC_S_FBUF\n");
+               cam->v4l2_fb = *fb;
+               break;
+       }
+
+       case VIDIOC_G_PARM: {
+               struct v4l2_streamparm *parm = arg;
+               pr_debug("   case VIDIOC_G_PARM\n");
+               if (cam->sensor)
+                       retval = vidioc_int_g_parm(cam->sensor, parm);
+               else {
+                       pr_err("ERROR: v4l2 capture: slave not found!\n");
+                       retval = -ENODEV;
+               }
+               break;
+       }
+
+       case VIDIOC_S_PARM:  {
+               struct v4l2_streamparm *parm = arg;
+               pr_debug("   case VIDIOC_S_PARM\n");
+               if (cam->sensor)
+                       retval = mxc_v4l2_s_param(cam, parm);
+               else {
+                       pr_err("ERROR: v4l2 capture: slave not found!\n");
+                       retval = -ENODEV;
+               }
+               break;
+       }
+
+       /* linux v4l2 bug, kernel c0485619 user c0405619 */
+       case VIDIOC_ENUMSTD: {
+               struct v4l2_standard *e = arg;
+               pr_debug("   case VIDIOC_ENUMSTD\n");
+               *e = cam->standard;
+               break;
+       }
+
+       case VIDIOC_G_STD: {
+               v4l2_std_id *e = arg;
+               pr_debug("   case VIDIOC_G_STD\n");
+               if (cam->sensor)
+                       retval = mxc_v4l2_g_std(cam, e);
+               else {
+                       pr_err("ERROR: v4l2 capture: slave not found!\n");
+                       retval = -ENODEV;
+               }
+               break;
+       }
+
+       case VIDIOC_S_STD: {
+               v4l2_std_id *e = arg;
+               pr_debug("   case VIDIOC_S_STD\n");
+               retval = mxc_v4l2_s_std(cam, *e);
+
+               break;
+       }
+
+       case VIDIOC_ENUMOUTPUT: {
+               struct v4l2_output *output = arg;
+               pr_debug("   case VIDIOC_ENUMOUTPUT\n");
+               if (output->index >= MXC_V4L2_CAPTURE_NUM_OUTPUTS) {
+                       retval = -EINVAL;
+                       break;
+               }
+               *output = mxc_capture_outputs[output->index];
+
+               break;
+       }
+       case VIDIOC_G_OUTPUT: {
+               int *p_output_num = arg;
+               pr_debug("   case VIDIOC_G_OUTPUT\n");
+               *p_output_num = cam->output;
+               break;
+       }
+
+       case VIDIOC_S_OUTPUT: {
+               int *p_output_num = arg;
+               pr_debug("   case VIDIOC_S_OUTPUT\n");
+               if (*p_output_num >= MXC_V4L2_CAPTURE_NUM_OUTPUTS) {
+                       retval = -EINVAL;
+                       break;
+               }
+               cam->output = *p_output_num;
+               break;
+       }
+
+       case VIDIOC_ENUMINPUT: {
+               struct v4l2_input *input = arg;
+               pr_debug("   case VIDIOC_ENUMINPUT\n");
+               if (input->index >= MXC_V4L2_CAPTURE_NUM_INPUTS) {
+                       retval = -EINVAL;
+                       break;
+               }
+               *input = mxc_capture_inputs[input->index];
+               break;
+       }
+
+       case VIDIOC_G_INPUT: {
+               int *index = arg;
+               pr_debug("   case VIDIOC_G_INPUT\n");
+               *index = cam->current_input;
+               break;
+       }
+
+       case VIDIOC_S_INPUT: {
+               int *index = arg;
+               pr_debug("   case VIDIOC_S_INPUT\n");
+               if (*index >= MXC_V4L2_CAPTURE_NUM_INPUTS) {
+                       retval = -EINVAL;
+                       break;
+               }
+
+               if (*index == cam->current_input)
+                       break;
+
+               if ((mxc_capture_inputs[cam->current_input].status &
+                   V4L2_IN_ST_NO_POWER) == 0) {
+                       retval = mxc_streamoff(cam);
+                       if (retval)
+                               break;
+                       mxc_capture_inputs[cam->current_input].status |=
+                                                       V4L2_IN_ST_NO_POWER;
+               }
+
+               if (strcmp(mxc_capture_inputs[*index].name, "CSI MEM") == 0) {
+#if defined(CONFIG_MXC_IPU_CSI_ENC) || defined(CONFIG_MXC_IPU_CSI_ENC_MODULE)
+                       retval = csi_enc_select(cam);
+                       if (retval)
+                               break;
+#endif
+               } else if (strcmp(mxc_capture_inputs[*index].name,
+                                 "CSI IC MEM") == 0) {
+#if defined(CONFIG_MXC_IPU_PRP_ENC) || defined(CONFIG_MXC_IPU_PRP_ENC_MODULE)
+                       retval = prp_enc_select(cam);
+                       if (retval)
+                               break;
+#endif
+               }
+
+               mxc_capture_inputs[*index].status &= ~V4L2_IN_ST_NO_POWER;
+               cam->current_input = *index;
+               break;
+       }
+       case VIDIOC_ENUM_FMT: {
+               struct v4l2_fmtdesc *f = arg;
+               if (cam->sensor)
+                       retval = vidioc_int_enum_fmt_cap(cam->sensor, f);
+               else {
+                       pr_err("ERROR: v4l2 capture: slave not found!\n");
+                       retval = -ENODEV;
+               }
+               break;
+       }
+       case VIDIOC_ENUM_FRAMESIZES: {
+               struct v4l2_frmsizeenum *fsize = arg;
+               if (cam->sensor)
+                       retval = vidioc_int_enum_framesizes(cam->sensor, fsize);
+               else {
+                       pr_err("ERROR: v4l2 capture: slave not found!\n");
+                       retval = -ENODEV;
+               }
+               break;
+       }
+       case VIDIOC_DBG_G_CHIP_IDENT: {
+               struct v4l2_dbg_chip_ident *p = arg;
+               p->ident = V4L2_IDENT_NONE;
+               p->revision = 0;
+               if (cam->sensor)
+                       retval = vidioc_int_g_chip_ident(cam->sensor, (int *)p);
+               else {
+                       pr_err("ERROR: v4l2 capture: slave not found!\n");
+                       retval = -ENODEV;
+               }
+               break;
+       }
+       case VIDIOC_TRY_FMT:
+       case VIDIOC_QUERYCTRL:
+       case VIDIOC_G_TUNER:
+       case VIDIOC_S_TUNER:
+       case VIDIOC_G_FREQUENCY:
+       case VIDIOC_S_FREQUENCY:
+       default:
+               pr_debug("   case default or not supported\n");
+               retval = -EINVAL;
+               break;
+       }
+
+       up(&cam->busy_lock);
+       return retval;
+}
+
+/*
+ * V4L interface - ioctl function
+ *
+ * @return  None
+ */
+static long mxc_v4l_ioctl(struct file *file, unsigned int cmd,
+                        unsigned long arg)
+{
+       pr_debug("In MVC:mxc_v4l_ioctl\n");
+       return video_usercopy(file, cmd, arg, mxc_v4l_do_ioctl);
+}
+
+/*!
+ * V4L interface - mmap function
+ *
+ * @param file        structure file *
+ *
+ * @param vma         structure vm_area_struct *
+ *
+ * @return status     0 Success, EINTR busy lock error, ENOBUFS remap_page error
+ */
+static int mxc_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       struct video_device *dev = video_devdata(file);
+       unsigned long size;
+       int res = 0;
+       cam_data *cam = video_get_drvdata(dev);
+
+       pr_debug("In MVC:mxc_mmap\n");
+       pr_debug("   pgoff=0x%lx, start=0x%lx, end=0x%lx\n",
+                vma->vm_pgoff, vma->vm_start, vma->vm_end);
+
+       /* make this _really_ smp-safe */
+       if (down_interruptible(&cam->busy_lock))
+               return -EINTR;
+
+       size = vma->vm_end - vma->vm_start;
+       vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+       if (remap_pfn_range(vma, vma->vm_start,
+                           vma->vm_pgoff, size, vma->vm_page_prot)) {
+               pr_err("ERROR: v4l2 capture: mxc_mmap: "
+                       "remap_pfn_range failed\n");
+               res = -ENOBUFS;
+               goto mxc_mmap_exit;
+       }
+
+       vma->vm_flags &= ~VM_IO;        /* using shared anonymous pages */
+
+      mxc_mmap_exit:
+       up(&cam->busy_lock);
+       return res;
+}
+
+/*!
+ * V4L interface - poll function
+ *
+ * @param file       structure file *
+ *
+ * @param wait       structure poll_table_struct *
+ *
+ * @return  status   POLLIN | POLLRDNORM
+ */
+static unsigned int mxc_poll(struct file *file, struct poll_table_struct *wait)
+{
+       struct video_device *dev = video_devdata(file);
+       cam_data *cam = video_get_drvdata(dev);
+       wait_queue_head_t *queue = NULL;
+       int res = POLLIN | POLLRDNORM;
+
+       pr_debug("In MVC:mxc_poll\n");
+
+       if (down_interruptible(&cam->busy_lock))
+               return -EINTR;
+
+       queue = &cam->enc_queue;
+       poll_wait(file, queue, wait);
+
+       up(&cam->busy_lock);
+
+       return res;
+}
+
+/*!
+ * This structure defines the functions to be called in this driver.
+ */
+static struct v4l2_file_operations mxc_v4l_fops = {
+       .owner = THIS_MODULE,
+       .open = mxc_v4l_open,
+       .release = mxc_v4l_close,
+       .read = mxc_v4l_read,
+       .ioctl = mxc_v4l_ioctl,
+       .mmap = mxc_mmap,
+       .poll = mxc_poll,
+};
+
+static struct video_device mxc_v4l_template = {
+       .name = "Mxc Camera",
+       .fops = &mxc_v4l_fops,
+       .release = video_device_release,
+};
+
+/*!
+ * This function can be used to release any platform data on closing.
+ */
+static void camera_platform_release(struct device *device)
+{
+}
+
+/*!
+ * Camera V4l2 callback function.
+ *
+ * @param mask      u32
+ *
+ * @param dev       void device structure
+ *
+ * @return status
+ */
+static void camera_callback(u32 mask, void *dev)
+{
+       struct mxc_v4l_frame *done_frame;
+       struct mxc_v4l_frame *ready_frame;
+       struct timeval cur_time;
+
+       cam_data *cam = (cam_data *) dev;
+       if (cam == NULL)
+               return;
+
+       pr_debug("In MVC:camera_callback\n");
+
+       if (!list_empty(&cam->working_q)) {
+               do_gettimeofday(&cur_time);
+
+               done_frame = list_entry(cam->working_q.next,
+                                       struct mxc_v4l_frame,
+                                       queue);
+
+               if (done_frame->ipu_buf_num != local_buf_num)
+                       goto next;
+
+               /*
+                * Set the current time to done frame buffer's
+                * timestamp. Users can use this information to judge
+                * the frame's usage.
+                */
+               done_frame->buffer.timestamp = cur_time;
+
+               if (done_frame->buffer.flags & V4L2_BUF_FLAG_QUEUED) {
+                       done_frame->buffer.flags |= V4L2_BUF_FLAG_DONE;
+                       done_frame->buffer.flags &= ~V4L2_BUF_FLAG_QUEUED;
+
+                       /* Added to the done queue */
+                       list_del(cam->working_q.next);
+                       list_add_tail(&done_frame->queue, &cam->done_q);
+
+                       /* Wake up the queue */
+                       cam->enc_counter++;
+                       wake_up_interruptible(&cam->enc_queue);
+               } else
+                       pr_err("ERROR: v4l2 capture: camera_callback: "
+                               "buffer not queued\n");
+       }
+
+next:
+       if (!list_empty(&cam->ready_q)) {
+               ready_frame = list_entry(cam->ready_q.next,
+                                        struct mxc_v4l_frame,
+                                        queue);
+               if (cam->enc_update_eba)
+                       if (cam->enc_update_eba(ready_frame->buffer.m.offset,
+                                               &cam->ping_pong_csi) == 0) {
+                               list_del(cam->ready_q.next);
+                               list_add_tail(&ready_frame->queue,
+                                             &cam->working_q);
+                               ready_frame->ipu_buf_num = local_buf_num;
+                       }
+       } else {
+               if (cam->enc_update_eba)
+                       cam->enc_update_eba(
+                               cam->dummy_frame.buffer.m.offset,
+                               &cam->ping_pong_csi);
+       }
+
+       local_buf_num = (local_buf_num == 0) ? 1 : 0;
+
+       return;
+}
+
+/*!
+ * initialize cam_data structure
+ *
+ * @param cam      structure cam_data *
+ *
+ * @return status  0 Success
+ */
+static void init_camera_struct(cam_data *cam, struct platform_device *pdev)
+{
+       pr_debug("In MVC: init_camera_struct\n");
+
+       /* Default everything to 0 */
+       memset(cam, 0, sizeof(cam_data));
+
+       init_MUTEX(&cam->param_lock);
+       init_MUTEX(&cam->busy_lock);
+
+       cam->video_dev = video_device_alloc();
+       if (cam->video_dev == NULL)
+               return;
+
+       *(cam->video_dev) = mxc_v4l_template;
+
+       video_set_drvdata(cam->video_dev, cam);
+       dev_set_drvdata(&pdev->dev, (void *)cam);
+       cam->video_dev->minor = -1;
+
+       init_waitqueue_head(&cam->enc_queue);
+       init_waitqueue_head(&cam->still_queue);
+
+       /* setup cropping */
+       cam->crop_bounds.left = 0;
+       cam->crop_bounds.width = 640;
+       cam->crop_bounds.top = 0;
+       cam->crop_bounds.height = 480;
+       cam->crop_current = cam->crop_defrect = cam->crop_bounds;
+       ipu_csi_set_window_size(cam->crop_current.width,
+                               cam->crop_current.height, cam->csi);
+       ipu_csi_set_window_pos(cam->crop_current.left,
+                               cam->crop_current.top, cam->csi);
+       cam->streamparm.parm.capture.capturemode = 0;
+
+       cam->standard.index = 0;
+       cam->standard.id = V4L2_STD_UNKNOWN;
+       cam->standard.frameperiod.denominator = 30;
+       cam->standard.frameperiod.numerator = 1;
+       cam->standard.framelines = 480;
+       cam->standard_autodetect = true;
+       cam->streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       cam->streamparm.parm.capture.timeperframe = cam->standard.frameperiod;
+       cam->streamparm.parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+       cam->overlay_on = false;
+       cam->capture_on = false;
+       cam->v4l2_fb.flags = V4L2_FBUF_FLAG_OVERLAY;
+
+       cam->v2f.fmt.pix.sizeimage = 352 * 288 * 3 / 2;
+       cam->v2f.fmt.pix.bytesperline = 288 * 3 / 2;
+       cam->v2f.fmt.pix.width = 288;
+       cam->v2f.fmt.pix.height = 352;
+       cam->v2f.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
+       cam->win.w.width = 160;
+       cam->win.w.height = 160;
+       cam->win.w.left = 0;
+       cam->win.w.top = 0;
+
+       cam->csi = 0;  /* Need to determine how to set this correctly with
+                       * multiple video input devices. */
+
+       cam->enc_callback = camera_callback;
+       init_waitqueue_head(&cam->power_queue);
+       spin_lock_init(&cam->queue_int_lock);
+       spin_lock_init(&cam->dqueue_int_lock);
+}
+
+static ssize_t show_streaming(struct device *dev,
+                       struct device_attribute *attr, char *buf)
+{
+       struct video_device *video_dev = container_of(dev,
+                                               struct video_device, dev);
+       cam_data *g_cam = video_get_drvdata(video_dev);
+
+       if (g_cam->capture_on)
+               return sprintf(buf, "stream on\n");
+       else
+               return sprintf(buf, "stream off\n");
+}
+static DEVICE_ATTR(fsl_v4l2_capture_property, S_IRUGO, show_streaming, NULL);
+
+static ssize_t show_overlay(struct device *dev,
+                       struct device_attribute *attr, char *buf)
+{
+       struct video_device *video_dev = container_of(dev,
+                                               struct video_device, dev);
+       cam_data *g_cam = video_get_drvdata(video_dev);
+
+       if (g_cam->overlay_on)
+               return sprintf(buf, "overlay on\n");
+       else
+               return sprintf(buf, "overlay off\n");
+}
+static DEVICE_ATTR(fsl_v4l2_overlay_property, S_IRUGO, show_overlay, NULL);
+
+/*!
+ * This function is called to probe the devices if registered.
+ *
+ * @param   pdev  the device structure used to give information on which device
+ *                to probe
+ *
+ * @return  The function returns 0 on success and -1 on failure.
+ */
+static int mxc_v4l2_probe(struct platform_device *pdev)
+{
+       /* Create g_cam and initialize it. */
+       g_cam = kmalloc(sizeof(cam_data), GFP_KERNEL);
+       if (g_cam == NULL) {
+               pr_err("ERROR: v4l2 capture: failed to register camera\n");
+               return -1;
+       }
+       init_camera_struct(g_cam, pdev);
+       pdev->dev.release = camera_platform_release;
+
+       /* Set up the v4l2 device and register it*/
+       mxc_v4l2_int_device.priv = g_cam;
+       /* This function contains a bug that won't let this be rmmod'd. */
+       v4l2_int_device_register(&mxc_v4l2_int_device);
+
+       /* register v4l video device */
+       if (video_register_device(g_cam->video_dev, VFL_TYPE_GRABBER, video_nr)
+           == -1) {
+               kfree(g_cam);
+               g_cam = NULL;
+               pr_err("ERROR: v4l2 capture: video_register_device failed\n");
+               return -1;
+       }
+       pr_debug("   Video device registered: %s #%d\n",
+                g_cam->video_dev->name, g_cam->video_dev->minor);
+
+       if (device_create_file(&g_cam->video_dev->dev,
+                       &dev_attr_fsl_v4l2_capture_property))
+               dev_err(&pdev->dev, "Error on creating sysfs file"
+                       " for capture\n");
+
+       if (device_create_file(&g_cam->video_dev->dev,
+                       &dev_attr_fsl_v4l2_overlay_property))
+               dev_err(&pdev->dev, "Error on creating sysfs file"
+                       " for overlay\n");
+
+       return 0;
+}
+
+/*!
+ * This function is called to remove the devices when device unregistered.
+ *
+ * @param   pdev  the device structure used to give information on which device
+ *                to remove
+ *
+ * @return  The function returns 0 on success and -1 on failure.
+ */
+static int mxc_v4l2_remove(struct platform_device *pdev)
+{
+
+       if (g_cam->open_count) {
+               pr_err("ERROR: v4l2 capture:camera open "
+                       "-- setting ops to NULL\n");
+               return -EBUSY;
+       } else {
+               device_remove_file(&g_cam->video_dev->dev,
+                       &dev_attr_fsl_v4l2_capture_property);
+               device_remove_file(&g_cam->video_dev->dev,
+                       &dev_attr_fsl_v4l2_overlay_property);
+
+               pr_info("V4L2 freeing image input device\n");
+               v4l2_int_device_unregister(&mxc_v4l2_int_device);
+               video_unregister_device(g_cam->video_dev);
+
+               mxc_free_frame_buf(g_cam);
+               kfree(g_cam);
+               g_cam = NULL;
+       }
+
+       pr_info("V4L2 unregistering video\n");
+       return 0;
+}
+
+/*!
+ * This function is called to put the sensor in a low power state.
+ * Refer to the document driver-model/driver.txt in the kernel source tree
+ * for more information.
+ *
+ * @param   pdev  the device structure used to give information on which I2C
+ *                to suspend
+ * @param   state the power state the device is entering
+ *
+ * @return  The function returns 0 on success and -1 on failure.
+ */
+static int mxc_v4l2_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       cam_data *cam = platform_get_drvdata(pdev);
+
+       pr_debug("In MVC:mxc_v4l2_suspend\n");
+
+       if (cam == NULL) {
+               return -1;
+       }
+
+       cam->low_power = true;
+
+       if (cam->overlay_on == true)
+               stop_preview(cam);
+       if ((cam->capture_on == true) && cam->enc_disable) {
+               cam->enc_disable(cam);
+       }
+
+       if (cam->sensor)
+               vidioc_int_s_power(cam->sensor, 0);
+
+       return 0;
+}
+
+/*!
+ * This function is called to bring the sensor back from a low power state.
+ * Refer to the document driver-model/driver.txt in the kernel source tree
+ * for more information.
+ *
+ * @param   pdev   the device structure
+ *
+ * @return  The function returns 0 on success and -1 on failure
+ */
+static int mxc_v4l2_resume(struct platform_device *pdev)
+{
+       cam_data *cam = platform_get_drvdata(pdev);
+
+       pr_debug("In MVC:mxc_v4l2_resume\n");
+
+       if (cam == NULL) {
+               return -1;
+       }
+
+       cam->low_power = false;
+       wake_up_interruptible(&cam->power_queue);
+
+       if (cam->sensor)
+               vidioc_int_s_power(cam->sensor, 1);
+
+       if (cam->overlay_on == true)
+               start_preview(cam);
+       if (cam->capture_on == true)
+               mxc_streamon(cam);
+
+       return 0;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxc_v4l2_driver = {
+       .driver = {
+                  .name = "mxc_v4l2_capture",
+                  },
+       .probe = mxc_v4l2_probe,
+       .remove = mxc_v4l2_remove,
+       .suspend = mxc_v4l2_suspend,
+       .resume = mxc_v4l2_resume,
+       .shutdown = NULL,
+};
+
+/*!
+ * Initializes the camera driver.
+ */
+static int mxc_v4l2_master_attach(struct v4l2_int_device *slave)
+{
+       cam_data *cam = slave->u.slave->master->priv;
+       struct v4l2_format cam_fmt;
+
+       pr_debug("In MVC: mxc_v4l2_master_attach\n");
+       pr_debug("   slave.name = %s\n", slave->name);
+       pr_debug("   master.name = %s\n", slave->u.slave->master->name);
+
+       cam->sensor = slave;
+       if (slave == NULL) {
+               pr_err("ERROR: v4l2 capture: slave parameter not valid.\n");
+               return -1;
+       }
+
+       ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, true, true);
+       vidioc_int_s_power(cam->sensor, 1);
+       vidioc_int_dev_init(slave);
+       ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, false, false);
+       cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt);
+
+       /* Used to detect TV in (type 1) vs. camera (type 0)*/
+       cam->device_type = cam_fmt.fmt.pix.priv;
+
+       /* Set the input size to the ipu for this device */
+       cam->crop_bounds.top = cam->crop_bounds.left = 0;
+       cam->crop_bounds.width = cam_fmt.fmt.pix.width;
+       cam->crop_bounds.height = cam_fmt.fmt.pix.height;
+
+       /* This also is the max crop size for this device. */
+       cam->crop_defrect.top = cam->crop_defrect.left = 0;
+       cam->crop_defrect.width = cam_fmt.fmt.pix.width;
+       cam->crop_defrect.height = cam_fmt.fmt.pix.height;
+
+       /* At this point, this is also the current image size. */
+       cam->crop_current.top = cam->crop_current.left = 0;
+       cam->crop_current.width = cam_fmt.fmt.pix.width;
+       cam->crop_current.height = cam_fmt.fmt.pix.height;
+
+       pr_debug("End of %s: v2f pix widthxheight %d x %d\n",
+                __func__,
+                cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height);
+       pr_debug("End of %s: crop_bounds widthxheight %d x %d\n",
+                __func__,
+                cam->crop_bounds.width, cam->crop_bounds.height);
+       pr_debug("End of %s: crop_defrect widthxheight %d x %d\n",
+                __func__,
+                cam->crop_defrect.width, cam->crop_defrect.height);
+       pr_debug("End of %s: crop_current widthxheight %d x %d\n",
+                __func__,
+                cam->crop_current.width, cam->crop_current.height);
+
+       return 0;
+}
+
+/*!
+ * Disconnects the camera driver.
+ */
+static void mxc_v4l2_master_detach(struct v4l2_int_device *slave)
+{
+       pr_debug("In MVC:mxc_v4l2_master_detach\n");
+       vidioc_int_dev_exit(slave);
+}
+
+/*!
+ * Entry point for the V4L2
+ *
+ * @return  Error code indicating success or failure
+ */
+static __init int camera_init(void)
+{
+       u8 err = 0;
+
+       pr_debug("In MVC:camera_init\n");
+
+       /* Register the device driver structure. */
+       err = platform_driver_register(&mxc_v4l2_driver);
+       if (err != 0) {
+               pr_err("ERROR: v4l2 capture:camera_init: "
+                       "platform_driver_register failed.\n");
+               return err;
+       }
+
+       return err;
+}
+
+/*!
+ * Exit and cleanup for the V4L2
+ */
+static void __exit camera_exit(void)
+{
+       pr_debug("In MVC: camera_exit\n");
+
+       platform_driver_unregister(&mxc_v4l2_driver);
+}
+
+module_init(camera_init);
+module_exit(camera_exit);
+
+module_param(video_nr, int, 0444);
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("V4L2 capture driver for Mxc based cameras");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("video");
diff --git a/drivers/media/video/mxc/capture/mxc_v4l2_capture.h b/drivers/media/video/mxc/capture/mxc_v4l2_capture.h
new file mode 100644 (file)
index 0000000..3f53d60
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * 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 MXC_V4L2_CAPTURE MXC V4L2 Video Capture Driver
+ */
+/*!
+ * @file mxc_v4l2_capture.h
+ *
+ * @brief mxc V4L2 capture device API  Header file
+ *
+ * It include all the defines for frame operations, also three structure defines
+ * use case ops structure, common v4l2 driver structure and frame structure.
+ *
+ * @ingroup MXC_V4L2_CAPTURE
+ */
+#ifndef __MXC_V4L2_CAPTURE_H__
+#define __MXC_V4L2_CAPTURE_H__
+
+#include <asm/uaccess.h>
+#include <linux/list.h>
+#include <linux/smp_lock.h>
+#include <linux/ipu.h>
+#include <linux/mxc_v4l2.h>
+
+#include <media/v4l2-dev.h>
+
+#define FRAME_NUM 3
+
+/*!
+ * v4l2 frame structure.
+ */
+struct mxc_v4l_frame {
+       u32 paddress;
+       void *vaddress;
+       int count;
+       int width;
+       int height;
+
+       struct v4l2_buffer buffer;
+       struct list_head queue;
+       int index;
+       int ipu_buf_num;
+};
+
+/* Only for old version.  Will go away soon. */
+typedef struct {
+       u8 clk_mode;
+       u8 ext_vsync;
+       u8 Vsync_pol;
+       u8 Hsync_pol;
+       u8 pixclk_pol;
+       u8 data_pol;
+       u8 data_width;
+       u8 pack_tight;
+       u8 force_eof;
+       u8 data_en_pol;
+       u16 width;
+       u16 height;
+       u32 pixel_fmt;
+       u32 mclk;
+       u16 active_width;
+       u16 active_height;
+} sensor_interface;
+
+/* Sensor control function */
+/* Only for old version.  Will go away soon. */
+struct camera_sensor {
+       void (*set_color) (int bright, int saturation, int red, int green,
+                          int blue);
+       void (*get_color) (int *bright, int *saturation, int *red, int *green,
+                          int *blue);
+       void (*set_ae_mode) (int ae_mode);
+       void (*get_ae_mode) (int *ae_mode);
+       sensor_interface *(*config) (int *frame_rate, int high_quality);
+       sensor_interface *(*reset) (void);
+       void (*get_std) (v4l2_std_id *std);
+       void (*set_std) (v4l2_std_id std);
+       unsigned int csi;
+};
+
+/*!
+ * common v4l2 driver structure.
+ */
+typedef struct _cam_data {
+       struct video_device *video_dev;
+       int device_type;
+
+       /* semaphore guard against SMP multithreading */
+       struct semaphore busy_lock;
+
+       int open_count;
+
+       /* params lock for this camera */
+       struct semaphore param_lock;
+
+       /* Encoder */
+       struct list_head ready_q;
+       struct list_head done_q;
+       struct list_head working_q;
+       int ping_pong_csi;
+       spinlock_t queue_int_lock;
+       spinlock_t dqueue_int_lock;
+       struct mxc_v4l_frame frame[FRAME_NUM];
+       struct mxc_v4l_frame dummy_frame;
+       int skip_frame;
+       wait_queue_head_t enc_queue;
+       int enc_counter;
+       dma_addr_t rot_enc_bufs[2];
+       void *rot_enc_bufs_vaddr[2];
+       int rot_enc_buf_size[2];
+       enum v4l2_buf_type type;
+
+       /* still image capture */
+       wait_queue_head_t still_queue;
+       int still_counter;
+       dma_addr_t still_buf[2];
+       void *still_buf_vaddr;
+
+       /* overlay */
+       struct v4l2_window win;
+       struct v4l2_framebuffer v4l2_fb;
+       dma_addr_t vf_bufs[2];
+       void *vf_bufs_vaddr[2];
+       int vf_bufs_size[2];
+       dma_addr_t rot_vf_bufs[2];
+       void *rot_vf_bufs_vaddr[2];
+       int rot_vf_buf_size[2];
+       bool overlay_active;
+       int output;
+       struct fb_info *overlay_fb;
+       int fb_origin_std;
+
+       /* v4l2 format */
+       struct v4l2_format v2f;
+       int rotation;   /* for IPUv1 and IPUv3, this means encoder rotation */
+       int vf_rotation; /* viewfinder rotation only for IPUv1 and IPUv3 */
+       struct v4l2_mxc_offset offset;
+
+       /* V4l2 control bit */
+       int bright;
+       int hue;
+       int contrast;
+       int saturation;
+       int red;
+       int green;
+       int blue;
+       int ae_mode;
+
+       /* standard */
+       struct v4l2_streamparm streamparm;
+       struct v4l2_standard standard;
+       bool standard_autodetect;
+
+       /* crop */
+       struct v4l2_rect crop_bounds;
+       struct v4l2_rect crop_defrect;
+       struct v4l2_rect crop_current;
+
+       int (*enc_update_eba) (dma_addr_t eba, int *bufferNum);
+       int (*enc_enable) (void *private);
+       int (*enc_disable) (void *private);
+       int (*enc_enable_csi) (void *private);
+       int (*enc_disable_csi) (void *private);
+       void (*enc_callback) (u32 mask, void *dev);
+       int (*vf_start_adc) (void *private);
+       int (*vf_stop_adc) (void *private);
+       int (*vf_start_sdc) (void *private);
+       int (*vf_stop_sdc) (void *private);
+       int (*vf_enable_csi) (void *private);
+       int (*vf_disable_csi) (void *private);
+       int (*csi_start) (void *private);
+       int (*csi_stop) (void *private);
+
+       /* misc status flag */
+       bool overlay_on;
+       bool capture_on;
+       int overlay_pid;
+       int capture_pid;
+       bool low_power;
+       wait_queue_head_t power_queue;
+       unsigned int csi;
+       int current_input;
+
+       /* camera sensor interface */
+       struct camera_sensor *cam_sensor;       /* old version */
+       struct v4l2_int_device *sensor;
+} cam_data;
+
+#if defined(CONFIG_MXC_IPU_V1) || defined(CONFIG_VIDEO_MXC_EMMA_CAMERA) \
+                              || defined(CONFIG_VIDEO_MXC_CSI_CAMERA_MODULE) \
+                              || defined(CONFIG_VIDEO_MXC_CSI_CAMERA)
+void set_mclk_rate(uint32_t *p_mclk_freq);
+#else
+void set_mclk_rate(uint32_t *p_mclk_freq, uint32_t csi);
+#endif
+#endif                         /* __MXC_V4L2_CAPTURE_H__ */
diff --git a/drivers/media/video/mxc/capture/ov2640.c b/drivers/media/video/mxc/capture/ov2640.c
new file mode 100644 (file)
index 0000000..c968655
--- /dev/null
@@ -0,0 +1,1081 @@
+/*
+ * Copyright 2005-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 ov2640.c
+ *
+ * @brief ov2640 camera driver functions
+ *
+ * @ingroup Camera
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
+#include <linux/fsl_devices.h>
+
+#include <media/v4l2-int-device.h>
+#include "mxc_v4l2_capture.h"
+
+#define MIN_FPS 5
+#define MAX_FPS 30
+#define DEFAULT_FPS 30
+
+#define OV2640_XCLK_MIN 6000000
+#define OV2640_XCLK_MAX 27000000
+
+/*
+enum ov2640_mode {
+       ov2640_mode_1600_1120,
+       ov2640_mode_800_600
+};
+*/
+
+struct reg_value {
+       u8 reg;
+       u8 value;
+       int delay_ms;
+};
+
+static struct reg_value ov2640_setting_1600_1120[] = {
+#ifdef CONFIG_MACH_MX25_3DS
+       {0xff, 0x01, 0}, {0x12, 0x80, 5}, {0xff, 0x00, 0}, {0x2c, 0xff, 0},
+       {0x2e, 0xdf, 0}, {0xff, 0x01, 0}, {0x3c, 0x32, 0}, {0x11, 0x00, 0},
+       {0x09, 0x02, 0}, {0x04, 0x28, 0}, {0x13, 0xe5, 0}, {0x14, 0x48, 0},
+       {0x2c, 0x0c, 0}, {0x33, 0x78, 0}, {0x3a, 0x33, 0}, {0x3b, 0xfb, 0},
+       {0x3e, 0x00, 0}, {0x43, 0x11, 0}, {0x16, 0x10, 0}, {0x39, 0x02, 0},
+       {0x35, 0x58, 0}, {0x22, 0x0a, 0}, {0x37, 0x40, 0}, {0x23, 0x00, 0},
+       {0x34, 0xa0, 0}, {0x36, 0x1a, 0}, {0x06, 0x02, 0}, {0x07, 0xc0, 0},
+       {0x0d, 0xb7, 0}, {0x0e, 0x01, 0}, {0x4c, 0x00, 0}, {0x4a, 0x81, 0},
+       {0x21, 0x99, 0}, {0x24, 0x40, 0}, {0x25, 0x38, 0}, {0x26, 0x82, 0},
+       {0x5c, 0x00, 0}, {0x63, 0x00, 0}, {0x46, 0x3f, 0}, {0x61, 0x70, 0},
+       {0x62, 0x80, 0}, {0x7c, 0x05, 0}, {0x20, 0x80, 0}, {0x28, 0x30, 0},
+       {0x6c, 0x00, 0}, {0x6d, 0x80, 0}, {0x6e, 0x00, 0}, {0x70, 0x02, 0},
+       {0x71, 0x94, 0}, {0x73, 0xc1, 0}, {0x3d, 0x34, 0}, {0x5a, 0x57, 0},
+       {0x4f, 0xbb, 0}, {0x50, 0x9c, 0}, {0xff, 0x00, 0}, {0xe5, 0x7f, 0},
+       {0xf9, 0xc0, 0}, {0x41, 0x24, 0}, {0xe0, 0x14, 0}, {0x76, 0xff, 0},
+       {0x33, 0xa0, 0}, {0x42, 0x20, 0}, {0x43, 0x18, 0}, {0x4c, 0x00, 0},
+       {0x87, 0xd0, 0}, {0x88, 0x3f, 0}, {0xd7, 0x01, 0}, {0xd9, 0x10, 0},
+       {0xd3, 0x82, 0}, {0xc8, 0x08, 0}, {0xc9, 0x80, 0}, {0x7c, 0x00, 0},
+       {0x7d, 0x00, 0}, {0x7c, 0x03, 0}, {0x7d, 0x48, 0}, {0x7d, 0x48, 0},
+       {0x7c, 0x08, 0}, {0x7d, 0x20, 0}, {0x7d, 0x10, 0}, {0x7d, 0x0e, 0},
+       {0x90, 0x00, 0}, {0x91, 0x0e, 0}, {0x91, 0x1a, 0}, {0x91, 0x31, 0},
+       {0x91, 0x5a, 0}, {0x91, 0x69, 0}, {0x91, 0x75, 0}, {0x91, 0x7e, 0},
+       {0x91, 0x88, 0}, {0x91, 0x8f, 0}, {0x91, 0x96, 0}, {0x91, 0xa3, 0},
+       {0x91, 0xaf, 0}, {0x91, 0xc4, 0}, {0x91, 0xd7, 0}, {0x91, 0xe8, 0},
+       {0x91, 0x20, 0}, {0x92, 0x00, 0}, {0x93, 0x06, 0}, {0x93, 0xe3, 0},
+       {0x93, 0x05, 0}, {0x93, 0x05, 0}, {0x93, 0x00, 0}, {0x93, 0x04, 0},
+       {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0},
+       {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x96, 0x00, 0},
+       {0x97, 0x08, 0}, {0x97, 0x19, 0}, {0x97, 0x02, 0}, {0x97, 0x0c, 0},
+       {0x97, 0x24, 0}, {0x97, 0x30, 0}, {0x97, 0x28, 0}, {0x97, 0x26, 0},
+       {0x97, 0x02, 0}, {0x97, 0x98, 0}, {0x97, 0x80, 0}, {0x97, 0x00, 0},
+       {0x97, 0x00, 0}, {0xc3, 0xed, 0}, {0xa4, 0x00, 0}, {0xa8, 0x00, 0},
+       {0xc5, 0x11, 0}, {0xc6, 0x51, 0}, {0xbf, 0x80, 0}, {0xc7, 0x10, 0},
+       {0xb6, 0x66, 0}, {0xb8, 0xa5, 0}, {0xb7, 0x64, 0}, {0xb9, 0x7c, 0},
+       {0xb3, 0xaf, 0}, {0xb4, 0x97, 0}, {0xb5, 0xff, 0}, {0xb0, 0xc5, 0},
+       {0xb1, 0x94, 0}, {0xb2, 0x0f, 0}, {0xc4, 0x5c, 0}, {0xc0, 0xc8, 0},
+       {0xc1, 0x96, 0}, {0x86, 0x1d, 0}, {0x50, 0x00, 0}, {0x51, 0x90, 0},
+       {0x52, 0x2c, 0}, {0x53, 0x00, 0}, {0x54, 0x00, 0}, {0x55, 0x88, 0},
+       {0x57, 0x00, 0}, {0x5a, 0x90, 0}, {0x5b, 0x2c, 0}, {0x5c, 0x05, 0},
+       {0xc3, 0xed, 0}, {0x7f, 0x00, 0}, {0xda, 0x00, 0}, {0xe5, 0x1f, 0},
+       {0xe1, 0x77, 0}, {0xe0, 0x00, 0}, {0xdd, 0x7f, 0}, {0x05, 0x00, 0},
+       {0xff, 0x00, 0}, {0xe0, 0x04, 0}, {0xc0, 0xc8, 0}, {0xc1, 0x96, 0},
+       {0x86, 0x3d, 0}, {0x50, 0x00, 0}, {0x51, 0x90, 0}, {0x52, 0x2c, 0},
+       {0x53, 0x00, 0}, {0x54, 0x00, 0}, {0x55, 0x88, 0}, {0x57, 0x00, 0},
+       {0x5a, 0x40, 0}, {0x5b, 0xf0, 0}, {0x5c, 0x01, 0}, {0xd3, 0x82, 0},
+       {0xe0, 0x00, 1000}
+#else
+       {0xff, 0x1, 0}, {0x12, 0x80, 1}, {0xff, 0, 0}, {0x2c, 0xff, 0},
+       {0x2e, 0xdf, 0}, {0xff, 0x1, 0}, {0x3c, 0x32, 0}, {0x11, 0x01, 0},
+       {0x09, 0x00, 0}, {0x04, 0x28, 0}, {0x13, 0xe5, 0}, {0x14, 0x48, 0},
+       {0x2c, 0x0c, 0}, {0x33, 0x78, 0}, {0x3a, 0x33, 0}, {0x3b, 0xfb, 0},
+       {0x3e, 0x00, 0}, {0x43, 0x11, 0}, {0x16, 0x10, 0}, {0x39, 0x82, 0},
+       {0x35, 0x88, 0}, {0x22, 0x0a, 0}, {0x37, 0x40, 0}, {0x23, 0x00, 0},
+       {0x34, 0xa0, 0}, {0x36, 0x1a, 0}, {0x06, 0x02, 0}, {0x07, 0xc0, 0},
+       {0x0d, 0xb7, 0}, {0x0e, 0x01, 0}, {0x4c, 0x00, 0}, {0x4a, 0x81, 0},
+       {0x21, 0x99, 0}, {0x24, 0x40, 0}, {0x25, 0x38, 0}, {0x26, 0x82, 0},
+       {0x5c, 0x00, 0}, {0x63, 0x00, 0}, {0x46, 0x3f, 0}, {0x0c, 0x3c, 0},
+       {0x5d, 0x55, 0}, {0x5e, 0x7d, 0}, {0x5f, 0x7d, 0}, {0x60, 0x55, 0},
+       {0x61, 0x70, 0}, {0x62, 0x80, 0}, {0x7c, 0x05, 0}, {0x20, 0x80, 0},
+       {0x28, 0x30, 0}, {0x6c, 0x00, 0}, {0x6d, 0x80, 0}, {0x6e, 00, 0},
+       {0x70, 0x02, 0}, {0x71, 0x94, 0}, {0x73, 0xc1, 0}, {0x3d, 0x34, 0},
+       {0x5a, 0x57, 0}, {0x4f, 0xbb, 0}, {0x50, 0x9c, 0}, {0xff, 0x00, 0},
+       {0xe5, 0x7f, 0}, {0xf9, 0xc0, 0}, {0x41, 0x24, 0}, {0x44, 0x06, 0},
+       {0xe0, 0x14, 0}, {0x76, 0xff, 0}, {0x33, 0xa0, 0}, {0x42, 0x20, 0},
+       {0x43, 0x18, 0}, {0x4c, 0x00, 0}, {0x87, 0xd0, 0}, {0xd7, 0x03, 0},
+       {0xd9, 0x10, 0}, {0xd3, 0x82, 0}, {0xc8, 0x08, 0}, {0xc9, 0x80, 0},
+       {0x7c, 0x00, 0}, {0x7d, 0x00, 0}, {0x7c, 0x03, 0}, {0x7d, 0x48, 0},
+       {0x7d, 0x48, 0}, {0x7c, 0x08, 0}, {0x7d, 0x20, 0}, {0x7d, 0x10, 0},
+       {0x7d, 0x0e, 0}, {0x90, 0x00, 0}, {0x91, 0x0e, 0}, {0x91, 0x1a, 0},
+       {0x91, 0x31, 0}, {0x91, 0x5a, 0}, {0x91, 0x69, 0}, {0x91, 0x75, 0},
+       {0x91, 0x7e, 0}, {0x91, 0x88, 0}, {0x91, 0x8f, 0}, {0x91, 0x96, 0},
+       {0x91, 0xa3, 0}, {0x91, 0xaf, 0}, {0x91, 0xc4, 0}, {0x91, 0xd7, 0},
+       {0x91, 0xe8, 0}, {0x91, 0x20, 0}, {0x92, 0x00, 0}, {0x93, 0x06, 0},
+       {0x93, 0xe3, 0}, {0x93, 0x03, 0}, {0x93, 0x03, 0}, {0x93, 0x00, 0},
+       {0x93, 0x02, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0},
+       {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0},
+       {0x96, 0x00, 0}, {0x97, 0x08, 0}, {0x97, 0x19, 0}, {0x97, 0x02, 0},
+       {0x97, 0x0c, 0}, {0x97, 0x24, 0}, {0x97, 0x30, 0}, {0x97, 0x28, 0},
+       {0x97, 0x26, 0}, {0x97, 0x02, 0}, {0x97, 0x98, 0}, {0x97, 0x80, 0},
+       {0x97, 0x00, 0}, {0x97, 0x00, 0}, {0xa4, 0x00, 0}, {0xa8, 0x00, 0},
+       {0xc5, 0x11, 0}, {0xc6, 0x51, 0}, {0xbf, 0x80, 0}, {0xc7, 0x10, 0},
+       {0xb6, 0x66, 0}, {0xb8, 0xa5, 0}, {0xb7, 0x64, 0}, {0xb9, 0x7c, 0},
+       {0xb3, 0xaf, 0}, {0xb4, 0x97, 0}, {0xb5, 0xff, 0}, {0xb0, 0xc5, 0},
+       {0xb1, 0x94, 0}, {0xb2, 0x0f, 0}, {0xc4, 0x5c, 0}, {0xa6, 0x00, 0},
+       {0xa7, 0x20, 0}, {0xa7, 0xd8, 0}, {0xa7, 0x1b, 0}, {0xa7, 0x31, 0},
+       {0xa7, 0x00, 0}, {0xa7, 0x18, 0}, {0xa7, 0x20, 0}, {0xa7, 0xd8, 0},
+       {0xa7, 0x19, 0}, {0xa7, 0x31, 0}, {0xa7, 0x00, 0}, {0xa7, 0x18, 0},
+       {0xa7, 0x20, 0}, {0xa7, 0xd8, 0}, {0xa7, 0x19, 0}, {0xa7, 0x31, 0},
+       {0xa7, 0x00, 0}, {0xa7, 0x18, 0}, {0xc0, 0xc8, 0}, {0xc1, 0x96, 0},
+       {0x86, 0x3d, 0}, {0x50, 0x00, 0}, {0x51, 0x90, 0}, {0x52, 0x18, 0},
+       {0x53, 0x00, 0}, {0x54, 0x00, 0}, {0x55, 0x88, 0}, {0x57, 0x00, 0},
+       {0x5a, 0x90, 0}, {0x5b, 0x18, 0}, {0x5c, 0x05, 0}, {0xc3, 0xef, 0},
+       {0x7f, 0x00, 0}, {0xda, 0x01, 0}, {0xe5, 0x1f, 0}, {0xe1, 0x67, 0},
+       {0xe0, 0x00, 0}, {0xdd, 0x7f, 0}, {0x05, 0x00, 0}
+#endif
+};
+
+static struct reg_value ov2640_setting_800_600[] = {
+#ifdef CONFIG_MACH_MX25_3DS
+       {0xff, 0x01, 0}, {0x12, 0x80, 5}, {0xff, 0x00, 0}, {0x2c, 0xff, 0},
+       {0x2e, 0xdf, 0}, {0xff, 0x01, 0}, {0x3c, 0x32, 0}, {0x11, 0x00, 0},
+       {0x09, 0x02, 0}, {0x04, 0x28, 0}, {0x13, 0xe5, 0}, {0x14, 0x48, 0},
+       {0x2c, 0x0c, 0}, {0x33, 0x78, 0}, {0x3a, 0x33, 0}, {0x3b, 0xfb, 0},
+       {0x3e, 0x00, 0}, {0x43, 0x11, 0}, {0x16, 0x10, 0}, {0x39, 0x92, 0},
+       {0x35, 0xda, 0}, {0x22, 0x1a, 0}, {0x37, 0xc3, 0}, {0x23, 0x00, 0},
+       {0x34, 0xc0, 0}, {0x36, 0x1a, 0}, {0x06, 0x88, 0}, {0x07, 0xc0, 0},
+       {0x0d, 0x87, 0}, {0x0e, 0x41, 0}, {0x4c, 0x00, 0},
+       {0x48, 0x00, 0}, {0x5b, 0x00, 0}, {0x42, 0x03, 0}, {0x4a, 0x81, 0},
+       {0x21, 0x99, 0}, {0x24, 0x40, 0}, {0x25, 0x38, 0}, {0x26, 0x82, 0},
+       {0x5c, 0x00, 0}, {0x63, 0x00, 0}, {0x46, 0x22, 0}, {0x0c, 0x3c, 0},
+       {0x61, 0x70, 0}, {0x62, 0x80, 0}, {0x7c, 0x05, 0}, {0x20, 0x80, 0},
+       {0x28, 0x30, 0}, {0x6c, 0x00, 0}, {0x6d, 0x80, 0}, {0x6e, 0x00, 0},
+       {0x70, 0x02, 0}, {0x71, 0x94, 0}, {0x73, 0xc1, 0}, {0x12, 0x40, 0},
+       {0x17, 0x11, 0}, {0x18, 0x43, 0}, {0x19, 0x00, 0}, {0x1a, 0x4b, 0},
+       {0x32, 0x09, 0}, {0x37, 0xc0, 0}, {0x4f, 0xca, 0}, {0x50, 0xa8, 0},
+       {0x5a, 0x23, 0}, {0x6d, 0x00, 0}, {0x3d, 0x38, 0}, {0xff, 0x00, 0},
+       {0xe5, 0x7f, 0}, {0xf9, 0xc0, 0}, {0x41, 0x24, 0}, {0xe0, 0x14, 0},
+       {0x76, 0xff, 0}, {0x33, 0xa0, 0}, {0x42, 0x20, 0}, {0x43, 0x18, 0},
+       {0x4c, 0x00, 0}, {0x87, 0xd5, 0}, {0x88, 0x3f, 0}, {0xd7, 0x01, 0},
+       {0xd9, 0x10, 0}, {0xd3, 0x82, 0}, {0xc8, 0x08, 0}, {0xc9, 0x80, 0},
+       {0x7c, 0x00, 0}, {0x7d, 0x00, 0}, {0x7c, 0x03, 0}, {0x7d, 0x48, 0},
+       {0x7d, 0x48, 0}, {0x7c, 0x08, 0}, {0x7d, 0x20, 0}, {0x7d, 0x10, 0},
+       {0x7d, 0x0e, 0}, {0x90, 0x00, 0}, {0x91, 0x0e, 0}, {0x91, 0x1a, 0},
+       {0x91, 0x31, 0}, {0x91, 0x5a, 0}, {0x91, 0x69, 0}, {0x91, 0x75, 0},
+       {0x91, 0x7e, 0}, {0x91, 0x88, 0}, {0x91, 0x8f, 0}, {0x91, 0x96, 0},
+       {0x91, 0xa3, 0}, {0x91, 0xaf, 0}, {0x91, 0xc4, 0}, {0x91, 0xd7, 0},
+       {0x91, 0xe8, 0}, {0x91, 0x20, 0}, {0x92, 0x00, 0}, {0x93, 0x06, 0},
+       {0x93, 0xe3, 0}, {0x93, 0x05, 0}, {0x93, 0x05, 0}, {0x93, 0x00, 0},
+       {0x93, 0x04, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0},
+       {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0},
+       {0x96, 0x00, 0}, {0x97, 0x08, 0}, {0x97, 0x19, 0}, {0x97, 0x02, 0},
+       {0x97, 0x0c, 0}, {0x97, 0x24, 0}, {0x97, 0x30, 0}, {0x97, 0x28, 0},
+       {0x97, 0x26, 0}, {0x97, 0x02, 0}, {0x97, 0x98, 0}, {0x97, 0x80, 0},
+       {0x97, 0x00, 0}, {0x97, 0x00, 0}, {0xc3, 0xed, 0}, {0xa4, 0x00, 0},
+       {0xa8, 0x00, 0}, {0xc5, 0x11, 0}, {0xc6, 0x51, 0}, {0xbf, 0x80, 0},
+       {0xc7, 0x10, 0}, {0xb6, 0x66, 0}, {0xb8, 0xa5, 0}, {0xb7, 0x64, 0},
+       {0xb9, 0x7c, 0}, {0xb3, 0xaf, 0}, {0xb4, 0x97, 0}, {0xb5, 0xff, 0},
+       {0xb0, 0xc5, 0}, {0xb1, 0x94, 0}, {0xb2, 0x0f, 0}, {0xc4, 0x5c, 0},
+       {0xc0, 0x64, 0}, {0xc1, 0x4b, 0}, {0x8c, 0x00, 0}, {0x86, 0x3d, 0},
+       {0x50, 0x00, 0}, {0x51, 0xc8, 0}, {0x52, 0x96, 0}, {0x53, 0x00, 0},
+       {0x54, 0x00, 0}, {0x55, 0x00, 0}, {0x5a, 0xc8, 0}, {0x5b, 0x96, 0},
+       {0x5c, 0x00, 0}, {0xd3, 0x82, 0}, {0xc3, 0xed, 0}, {0x7f, 0x00, 0},
+       {0xda, 0x00, 0}, {0xe5, 0x1f, 0}, {0xe1, 0x67, 0}, {0xe0, 0x00, 0},
+       {0xdd, 0x7f, 0}, {0x05, 0x00, 0}, {0xff, 0x00, 0}, {0xe0, 0x04, 0},
+       {0xc0, 0x64, 0}, {0xc1, 0x4b, 0}, {0x8c, 0x00, 0}, {0x86, 0x3d, 0},
+       {0x50, 0x00, 0}, {0x51, 0xc8, 0}, {0x52, 0x96, 0}, {0x53, 0x00, 0},
+       {0x54, 0x00, 0}, {0x55, 0x00, 0}, {0x5a, 0xa0, 0}, {0x5b, 0x78, 0},
+       {0x5c, 0x00, 0}, {0xd3, 0x82, 0}, {0xe0, 0x00, 1000}
+#else
+       {0xff, 0, 0}, {0xff, 1, 0}, {0x12, 0x80, 1}, {0xff, 00, 0},
+       {0x2c, 0xff, 0}, {0x2e, 0xdf, 0}, {0xff, 0x1, 0}, {0x3c, 0x32, 0},
+       {0x11, 0x01, 0}, {0x09, 0x00, 0}, {0x04, 0x28, 0}, {0x13, 0xe5, 0},
+       {0x14, 0x48, 0}, {0x2c, 0x0c, 0}, {0x33, 0x78, 0}, {0x3a, 0x33, 0},
+       {0x3b, 0xfb, 0}, {0x3e, 0x00, 0}, {0x43, 0x11, 0}, {0x16, 0x10, 0},
+       {0x39, 0x92, 0}, {0x35, 0xda, 0}, {0x22, 0x1a, 0}, {0x37, 0xc3, 0},
+       {0x23, 0x00, 0}, {0x34, 0xc0, 0}, {0x36, 0x1a, 0}, {0x06, 0x88, 0},
+       {0x07, 0xc0, 0}, {0x0d, 0x87, 0}, {0x0e, 0x41, 0}, {0x4c, 0x00, 0},
+       {0x4a, 0x81, 0}, {0x21, 0x99, 0}, {0x24, 0x40, 0}, {0x25, 0x38, 0},
+       {0x26, 0x82, 0}, {0x5c, 0x00, 0}, {0x63, 0x00, 0}, {0x46, 0x22, 0},
+       {0x0c, 0x3c, 0}, {0x5d, 0x55, 0}, {0x5e, 0x7d, 0}, {0x5f, 0x7d, 0},
+       {0x60, 0x55, 0}, {0x61, 0x70, 0}, {0x62, 0x80, 0}, {0x7c, 0x05, 0},
+       {0x20, 0x80, 0}, {0x28, 0x30, 0}, {0x6c, 0x00, 0}, {0x6d, 0x80, 0},
+       {0x6e, 00, 0}, {0x70, 0x02, 0}, {0x71, 0x94, 0}, {0x73, 0xc1, 0},
+       {0x12, 0x40, 0}, {0x17, 0x11, 0}, {0x18, 0x43, 0}, {0x19, 0x00, 0},
+       {0x1a, 0x4b, 0}, {0x32, 0x09, 0}, {0x37, 0xc0, 0}, {0x4f, 0xca, 0},
+       {0x50, 0xa8, 0}, {0x6d, 0x00, 0}, {0x3d, 0x38, 0}, {0xff, 0x00, 0},
+       {0xe5, 0x7f, 0}, {0xf9, 0xc0, 0}, {0x41, 0x24, 0}, {0x44, 0x06, 0},
+       {0xe0, 0x14, 0}, {0x76, 0xff, 0}, {0x33, 0xa0, 0}, {0x42, 0x20, 0},
+       {0x43, 0x18, 0}, {0x4c, 0x00, 0}, {0x87, 0xd0, 0}, {0x88, 0x3f, 0},
+       {0xd7, 0x03, 0}, {0xd9, 0x10, 0}, {0xd3, 0x82, 0}, {0xc8, 0x08, 0},
+       {0xc9, 0x80, 0}, {0x7c, 0x00, 0}, {0x7d, 0x00, 0}, {0x7c, 0x03, 0},
+       {0x7d, 0x48, 0}, {0x7d, 0x48, 0}, {0x7c, 0x08, 0}, {0x7d, 0x20, 0},
+       {0x7d, 0x10, 0}, {0x7d, 0x0e, 0}, {0x90, 0x00, 0}, {0x91, 0x0e, 0},
+       {0x91, 0x1a, 0}, {0x91, 0x31, 0}, {0x91, 0x5a, 0}, {0x91, 0x69, 0},
+       {0x91, 0x75, 0}, {0x91, 0x7e, 0}, {0x91, 0x88, 0}, {0x91, 0x8f, 0},
+       {0x91, 0x96, 0}, {0x91, 0xa3, 0}, {0x91, 0xaf, 0}, {0x91, 0xc4, 0},
+       {0x91, 0xd7, 0}, {0x91, 0xe8, 0}, {0x91, 0x20, 0}, {0x92, 0x00, 0},
+       {0x93, 0x06, 0}, {0x93, 0xe3, 0}, {0x93, 0x03, 0}, {0x93, 0x03, 0},
+       {0x93, 0x00, 0}, {0x93, 0x02, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0},
+       {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0},
+       {0x93, 0x00, 0}, {0x96, 0x00, 0}, {0x97, 0x08, 0}, {0x97, 0x19, 0},
+       {0x97, 0x02, 0}, {0x97, 0x0c, 0}, {0x97, 0x24, 0}, {0x97, 0x30, 0},
+       {0x97, 0x28, 0}, {0x97, 0x26, 0}, {0x97, 0x02, 0}, {0x97, 0x98, 0},
+       {0x97, 0x80, 0}, {0x97, 0x00, 0}, {0x97, 0x00, 0}, {0xa4, 0x00, 0},
+       {0xa8, 0x00, 0}, {0xc5, 0x11, 0}, {0xc6, 0x51, 0}, {0xbf, 0x80, 0},
+       {0xc7, 0x10, 0}, {0xb6, 0x66, 0}, {0xb8, 0xa5, 0}, {0xb7, 0x64, 0},
+       {0xb9, 0x7c, 0}, {0xb3, 0xaf, 0}, {0xb4, 0x97, 0}, {0xb5, 0xff, 0},
+       {0xb0, 0xc5, 0}, {0xb1, 0x94, 0}, {0xb2, 0x0f, 0}, {0xc4, 0x5c, 0},
+       {0xa6, 0x00, 0}, {0xa7, 0x20, 0}, {0xa7, 0xd8, 0}, {0xa7, 0x1b, 0},
+       {0xa7, 0x31, 0}, {0xa7, 0x00, 0}, {0xa7, 0x18, 0}, {0xa7, 0x20, 0},
+       {0xa7, 0xd8, 0}, {0xa7, 0x19, 0}, {0xa7, 0x31, 0}, {0xa7, 0x00, 0},
+       {0xa7, 0x18, 0}, {0xa7, 0x20, 0}, {0xa7, 0xd8, 0}, {0xa7, 0x19, 0},
+       {0xa7, 0x31, 0}, {0xa7, 0x00, 0}, {0xa7, 0x18, 0}, {0xc0, 0x64, 0},
+       {0xc1, 0x4b, 0}, {0x86, 0x1d, 0}, {0x50, 0x00, 0}, {0x51, 0xc8, 0},
+       {0x52, 0x96, 0}, {0x53, 0x00, 0}, {0x54, 0x00, 0}, {0x55, 0x00, 0},
+       {0x57, 0x00, 0}, {0x5a, 0xc8, 0}, {0x5b, 0x96, 0}, {0x5c, 0x00, 0},
+       {0xc3, 0xef, 0}, {0x7f, 0x00, 0}, {0xda, 0x01, 0}, {0xe5, 0x1f, 0},
+       {0xe1, 0x67, 0}, {0xe0, 0x00, 0}, {0xdd, 0x7f, 0}, {0x05, 0x00, 0}
+#endif
+};
+
+/*!
+ * Maintains the information on the current state of the sesor.
+ */
+struct sensor {
+       const struct ov2640_platform_data *platform_data;
+       struct v4l2_int_device *v4l2_int_device;
+       struct i2c_client *i2c_client;
+       struct v4l2_pix_format pix;
+       struct v4l2_captureparm streamcap;
+       bool on;
+
+       /* control settings */
+       int brightness;
+       int hue;
+       int contrast;
+       int saturation;
+       int red;
+       int green;
+       int blue;
+       int ae_mode;
+
+       u32 csi;
+       u32 mclk;
+
+} ov2640_data;
+
+static struct regulator *io_regulator;
+static struct regulator *core_regulator;
+static struct regulator *analog_regulator;
+static struct regulator *gpo_regulator;
+
+extern void gpio_sensor_active(void);
+extern void gpio_sensor_inactive(void);
+
+/* list of image formats supported by this sensor */
+/*
+const static struct v4l2_fmtdesc ov2640_formats[] = {
+       {
+               .description = "YUYV (YUV 4:2:2), packed",
+               .pixelformat = V4L2_PIX_FMT_UYVY,
+       },
+};
+ */
+
+static int ov2640_init_mode(struct sensor *s)
+{
+       int ret = -1;
+       struct reg_value *setting;
+       int i, num;
+
+       pr_debug("In ov2640:ov2640_init_mode capturemode is %d\n",
+               s->streamcap.capturemode);
+
+       if (s->streamcap.capturemode & V4L2_MODE_HIGHQUALITY) {
+               s->pix.width = 1600;
+               s->pix.height = 1120;
+               setting = ov2640_setting_1600_1120;
+               num = ARRAY_SIZE(ov2640_setting_1600_1120);
+       } else {
+               s->pix.width = 800;
+               s->pix.height = 600;
+               setting = ov2640_setting_800_600;
+               num = ARRAY_SIZE(ov2640_setting_800_600);
+       }
+
+       for (i = 0; i < num; i++) {
+               ret = i2c_smbus_write_byte_data(s->i2c_client,
+                                               setting[i].reg,
+                                               setting[i].value);
+               if (ret < 0) {
+                       pr_err("write reg error: reg=%x, val=%x\n",
+                              setting[i].reg, setting[i].value);
+                       return ret;
+               }
+               if (setting[i].delay_ms > 0)
+                       msleep(setting[i].delay_ms);
+       }
+
+       return ret;
+}
+
+/* At present only support change to 15fps(only for SVGA mode) */
+static int ov2640_set_fps(struct sensor *s, int fps)
+{
+       int ret = 0;
+
+       if (i2c_smbus_write_byte_data(s->i2c_client, 0xff, 0x01) < 0) {
+               pr_err("in %s,change to sensor addr failed\n", __func__);
+               ret = -EPERM;
+       }
+
+       /* change the camera framerate to 15fps(only for SVGA mode) */
+       if (i2c_smbus_write_byte_data(s->i2c_client, 0x11, 0x01) < 0) {
+               pr_err("change camera to 15fps failed\n");
+               ret = -EPERM;
+       }
+
+       return ret;
+}
+
+static int ov2640_set_format(struct sensor *s, int format)
+{
+       int ret = 0;
+
+       if (i2c_smbus_write_byte_data(s->i2c_client, 0xff, 0x00) < 0)
+               ret = -EPERM;
+
+       if (format == V4L2_PIX_FMT_RGB565) {
+               /* set RGB565 format */
+               if (i2c_smbus_write_byte_data(s->i2c_client, 0xda, 0x08) < 0)
+                       ret = -EPERM;
+
+               if (i2c_smbus_write_byte_data(s->i2c_client, 0xd7, 0x03) < 0)
+                       ret = -EPERM;
+       } else if (format == V4L2_PIX_FMT_YUV420) {
+               /* set YUV420 format */
+               if (i2c_smbus_write_byte_data(s->i2c_client, 0xda, 0x00) < 0)
+                       ret = -EPERM;
+
+               if (i2c_smbus_write_byte_data(s->i2c_client, 0xd7, 0x1b) < 0)
+                       ret = -EPERM;
+       } else {
+               pr_debug("format not supported\n");
+       }
+
+       return ret;
+}
+
+/* --------------- IOCTL functions from v4l2_int_ioctl_desc --------------- */
+
+/*!
+ * ioctl_g_ifparm - V4L2 sensor interface handler for vidioc_int_g_ifparm_num
+ * s: pointer to standard V4L2 device structure
+ * p: pointer to standard V4L2 vidioc_int_g_ifparm_num ioctl structure
+ *
+ * Gets slave interface parameters.
+ * Calculates the required xclk value to support the requested
+ * clock parameters in p.  This value is returned in the p
+ * parameter.
+ *
+ * vidioc_int_g_ifparm returns platform-specific information about the
+ * interface settings used by the sensor.
+ *
+ * Given the image capture format in pix, the nominal frame period in
+ * timeperframe, calculate the required xclk frequency.
+ *
+ * Called on open.
+ */
+static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p)
+{
+       pr_debug("In ov2640:ioctl_g_ifparm\n");
+
+       if (s == NULL) {
+               pr_err("   ERROR!! no slave device set!\n");
+               return -1;
+       }
+
+       memset(p, 0, sizeof(*p));
+       p->u.bt656.clock_curr = ov2640_data.mclk;
+       p->if_type = V4L2_IF_TYPE_BT656;
+       p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT;
+       p->u.bt656.clock_min = OV2640_XCLK_MIN;
+       p->u.bt656.clock_max = OV2640_XCLK_MAX;
+
+       return 0;
+}
+
+/*!
+ * Sets the camera power.
+ *
+ * s  pointer to the camera device
+ * on if 1, power is to be turned on.  0 means power is to be turned off
+ *
+ * ioctl_s_power - V4L2 sensor interface handler for vidioc_int_s_power_num
+ * @s: pointer to standard V4L2 device structure
+ * @on: power state to which device is to be set
+ *
+ * Sets devices power state to requrested state, if possible.
+ * This is called on open, close, suspend and resume.
+ */
+static int ioctl_s_power(struct v4l2_int_device *s, int on)
+{
+       struct sensor *sensor = s->priv;
+
+       pr_debug("In ov2640:ioctl_s_power\n");
+
+       if (on && !sensor->on) {
+               gpio_sensor_active();
+               if (io_regulator)
+                       if (regulator_enable(io_regulator) != 0)
+                               return -EIO;
+               if (core_regulator)
+                       if (regulator_enable(core_regulator) != 0)
+                               return -EIO;
+               if (gpo_regulator)
+                       if (regulator_enable(gpo_regulator) != 0)
+                               return -EIO;
+               if (analog_regulator)
+                       if (regulator_enable(analog_regulator) != 0)
+                               return -EIO;
+       } else if (!on && sensor->on) {
+               if (analog_regulator)
+                       regulator_disable(analog_regulator);
+               if (core_regulator)
+                       regulator_disable(core_regulator);
+               if (io_regulator)
+                       regulator_disable(io_regulator);
+               if (gpo_regulator)
+                       regulator_disable(gpo_regulator);
+               gpio_sensor_inactive();
+       }
+
+       sensor->on = on;
+
+       return 0;
+}
+
+/*!
+ * ioctl_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure
+ *
+ * Returns the sensor's video CAPTURE parameters.
+ */
+static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+       struct sensor *sensor = s->priv;
+       struct v4l2_captureparm *cparm = &a->parm.capture;
+       int ret = 0;
+
+       pr_debug("In ov2640:ioctl_g_parm\n");
+
+       switch (a->type) {
+       /* This is the only case currently handled. */
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               pr_debug("   type is V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
+               memset(a, 0, sizeof(*a));
+               a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+               cparm->capability = sensor->streamcap.capability;
+               cparm->timeperframe = sensor->streamcap.timeperframe;
+               cparm->capturemode = sensor->streamcap.capturemode;
+               ret = 0;
+               break;
+
+       /* These are all the possible cases. */
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+       case V4L2_BUF_TYPE_VBI_OUTPUT:
+       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+               pr_err("   type is not V4L2_BUF_TYPE_VIDEO_CAPTURE " \
+                       "but %d\n", a->type);
+               ret = -EINVAL;
+               break;
+
+       default:
+               pr_err("   type is unknown - %d\n", a->type);
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+/*!
+ * ioctl_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure
+ *
+ * Configures the sensor to use the input parameters, if possible.  If
+ * not possible, reverts to the old parameters and returns the
+ * appropriate error code.
+ */
+static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+       struct sensor *sensor = s->priv;
+       struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe;
+       u32 tgt_fps;    /* target frames per secound */
+       int ret = 0;
+
+       pr_debug("In ov2640:ioctl_s_parm\n");
+
+       switch (a->type) {
+       /* This is the only case currently handled. */
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               pr_debug("   type is V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
+
+               /* Check that the new frame rate is allowed. */
+               if ((timeperframe->numerator == 0)
+                   || (timeperframe->denominator == 0)) {
+                       timeperframe->denominator = DEFAULT_FPS;
+                       timeperframe->numerator = 1;
+               }
+               tgt_fps = timeperframe->denominator
+                         / timeperframe->numerator;
+
+               if (tgt_fps > MAX_FPS) {
+                       timeperframe->denominator = MAX_FPS;
+                       timeperframe->numerator = 1;
+               } else if (tgt_fps < MIN_FPS) {
+                       timeperframe->denominator = MIN_FPS;
+                       timeperframe->numerator = 1;
+               }
+               sensor->streamcap.timeperframe = *timeperframe;
+               sensor->streamcap.capturemode =
+                               (u32)a->parm.capture.capturemode;
+
+               ret = ov2640_init_mode(sensor);
+               if (tgt_fps == 15)
+                       ov2640_set_fps(sensor, tgt_fps);
+               break;
+
+       /* These are all the possible cases. */
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+       case V4L2_BUF_TYPE_VBI_OUTPUT:
+       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+               pr_err("   type is not V4L2_BUF_TYPE_VIDEO_CAPTURE " \
+                       "but %d\n", a->type);
+               ret = -EINVAL;
+               break;
+
+       default:
+               pr_err("   type is unknown - %d\n", a->type);
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+/*!
+ * ioctl_s_fmt_cap - V4L2 sensor interface handler for ioctl_s_fmt_cap
+ *                  set camera output format and resolution format
+ *
+ * @s: pointer to standard V4L2 device structure
+ * @arg: pointer to parameter, according this to set camera
+ *
+ * Returns 0 if set succeed, else return -1
+ */
+static int ioctl_s_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
+{
+       struct sensor *sensor = s->priv;
+       u32 format = f->fmt.pix.pixelformat;
+       int size = 0, ret = 0;
+
+       size = f->fmt.pix.width * f->fmt.pix.height;
+       switch (format) {
+       case V4L2_PIX_FMT_RGB565:
+               if (size > 640 * 480)
+                       sensor->streamcap.capturemode = V4L2_MODE_HIGHQUALITY;
+               else
+                       sensor->streamcap.capturemode = 0;
+               ret = ov2640_init_mode(sensor);
+
+               ret = ov2640_set_format(sensor, V4L2_PIX_FMT_RGB565);
+               break;
+       case V4L2_PIX_FMT_UYVY:
+               if (size > 640 * 480)
+                       sensor->streamcap.capturemode = V4L2_MODE_HIGHQUALITY;
+               else
+                       sensor->streamcap.capturemode = 0;
+               ret = ov2640_init_mode(sensor);
+               break;
+       case V4L2_PIX_FMT_YUV420:
+               if (size > 640 * 480)
+                       sensor->streamcap.capturemode = V4L2_MODE_HIGHQUALITY;
+               else
+                       sensor->streamcap.capturemode = 0;
+               ret = ov2640_init_mode(sensor);
+
+               /* YUYV: width * 2, YY: width */
+               ret = ov2640_set_format(sensor, V4L2_PIX_FMT_YUV420);
+               break;
+       default:
+               pr_debug("case not supported\n");
+               break;
+       }
+
+       return ret;
+}
+
+/*!
+ * ioctl_g_fmt_cap - V4L2 sensor interface handler for ioctl_g_fmt_cap
+ * @s: pointer to standard V4L2 device structure
+ * @f: pointer to standard V4L2 v4l2_format structure
+ *
+ * Returns the sensor's current pixel format in the v4l2_format
+ * parameter.
+ */
+static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
+{
+       struct sensor *sensor = s->priv;
+
+       pr_debug("In ov2640:ioctl_g_fmt_cap.\n");
+
+       f->fmt.pix = sensor->pix;
+
+       return 0;
+}
+
+/*!
+ * ioctl_g_ctrl - V4L2 sensor interface handler for VIDIOC_G_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @vc: standard V4L2 VIDIOC_G_CTRL ioctl structure
+ *
+ * If the requested control is supported, returns the control's current
+ * value from the video_control[] array.  Otherwise, returns -EINVAL
+ * if the control is not supported.
+ */
+static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc)
+{
+       int ret = 0;
+
+       pr_debug("In ov2640:ioctl_g_ctrl\n");
+
+       switch (vc->id) {
+       case V4L2_CID_BRIGHTNESS:
+               vc->value = ov2640_data.brightness;
+               break;
+       case V4L2_CID_HUE:
+               vc->value = ov2640_data.hue;
+               break;
+       case V4L2_CID_CONTRAST:
+               vc->value = ov2640_data.contrast;
+               break;
+       case V4L2_CID_SATURATION:
+               vc->value = ov2640_data.saturation;
+               break;
+       case V4L2_CID_RED_BALANCE:
+               vc->value = ov2640_data.red;
+               break;
+       case V4L2_CID_BLUE_BALANCE:
+               vc->value = ov2640_data.blue;
+               break;
+       case V4L2_CID_EXPOSURE:
+               vc->value = ov2640_data.ae_mode;
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+/*!
+ * ioctl_s_ctrl - V4L2 sensor interface handler for VIDIOC_S_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure
+ *
+ * If the requested control is supported, sets the control's current
+ * value in HW (and updates the video_control[] array).  Otherwise,
+ * returns -EINVAL if the control is not supported.
+ */
+static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc)
+{
+       int retval = 0;
+
+       pr_debug("In ov2640:ioctl_s_ctrl %d\n", vc->id);
+
+       switch (vc->id) {
+       case V4L2_CID_BRIGHTNESS:
+               pr_debug("   V4L2_CID_BRIGHTNESS\n");
+               break;
+       case V4L2_CID_CONTRAST:
+               pr_debug("   V4L2_CID_CONTRAST\n");
+               break;
+       case V4L2_CID_SATURATION:
+               pr_debug("   V4L2_CID_SATURATION\n");
+               break;
+       case V4L2_CID_HUE:
+               pr_debug("   V4L2_CID_HUE\n");
+               break;
+       case V4L2_CID_AUTO_WHITE_BALANCE:
+               pr_debug(
+                       "   V4L2_CID_AUTO_WHITE_BALANCE\n");
+               break;
+       case V4L2_CID_DO_WHITE_BALANCE:
+               pr_debug(
+                       "   V4L2_CID_DO_WHITE_BALANCE\n");
+               break;
+       case V4L2_CID_RED_BALANCE:
+               pr_debug("   V4L2_CID_RED_BALANCE\n");
+               break;
+       case V4L2_CID_BLUE_BALANCE:
+               pr_debug("   V4L2_CID_BLUE_BALANCE\n");
+               break;
+       case V4L2_CID_GAMMA:
+               pr_debug("   V4L2_CID_GAMMA\n");
+               break;
+       case V4L2_CID_EXPOSURE:
+               pr_debug("   V4L2_CID_EXPOSURE\n");
+               break;
+       case V4L2_CID_AUTOGAIN:
+               pr_debug("   V4L2_CID_AUTOGAIN\n");
+               break;
+       case V4L2_CID_GAIN:
+               pr_debug("   V4L2_CID_GAIN\n");
+               break;
+       case V4L2_CID_HFLIP:
+               pr_debug("   V4L2_CID_HFLIP\n");
+               break;
+       case V4L2_CID_VFLIP:
+               pr_debug("   V4L2_CID_VFLIP\n");
+               break;
+       default:
+               pr_debug("   Default case\n");
+               retval = -EPERM;
+               break;
+       }
+
+       return retval;
+}
+
+/*!
+ * ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT
+ * @s: pointer to standard V4L2 device structure
+ */
+static int ioctl_init(struct v4l2_int_device *s)
+{
+       pr_debug("In ov2640:ioctl_init\n");
+
+       return 0;
+}
+
+/*!
+ * ioctl_dev_init - V4L2 sensor interface handler for vidioc_int_dev_init_num
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Initialise the device when slave attaches to the master.
+ */
+static int ioctl_dev_init(struct v4l2_int_device *s)
+{
+       struct sensor *sensor = s->priv;
+       u32 tgt_xclk;   /* target xclk */
+
+       pr_debug("In ov2640:ioctl_dev_init\n");
+
+       gpio_sensor_active();
+       ov2640_data.on = true;
+
+       tgt_xclk = ov2640_data.mclk;
+       tgt_xclk = min(tgt_xclk, (u32)OV2640_XCLK_MAX);
+       tgt_xclk = max(tgt_xclk, (u32)OV2640_XCLK_MIN);
+       ov2640_data.mclk = tgt_xclk;
+
+       pr_debug("   Setting mclk to %d MHz\n",
+               tgt_xclk / 1000000);
+       set_mclk_rate(&ov2640_data.mclk);
+
+       return ov2640_init_mode(sensor);
+}
+
+/*!
+ * ioctl_dev_exit - V4L2 sensor interface handler for vidioc_int_dev_exit_num
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Delinitialise the device when slave detaches to the master.
+ */
+static int ioctl_dev_exit(struct v4l2_int_device *s)
+{
+       pr_debug("In ov2640:ioctl_dev_exit\n");
+
+       gpio_sensor_inactive();
+
+       return 0;
+}
+
+/*!
+ * This structure defines all the ioctls for this module and links them to the
+ * enumeration.
+ */
+static struct v4l2_int_ioctl_desc ov2640_ioctl_desc[] = {
+       {vidioc_int_dev_init_num, (v4l2_int_ioctl_func *)ioctl_dev_init},
+       {vidioc_int_dev_exit_num, (v4l2_int_ioctl_func *)ioctl_dev_exit},
+       {vidioc_int_s_power_num, (v4l2_int_ioctl_func *)ioctl_s_power},
+       {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func *)ioctl_g_ifparm},
+/*     {vidioc_int_g_needs_reset_num,
+                               (v4l2_int_ioctl_func *)ioctl_g_needs_reset}, */
+/*     {vidioc_int_reset_num, (v4l2_int_ioctl_func *)ioctl_reset}, */
+       {vidioc_int_init_num, (v4l2_int_ioctl_func *)ioctl_init},
+/*     {vidioc_int_enum_fmt_cap_num,
+                               (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap}, */
+/*     {vidioc_int_try_fmt_cap_num,
+                               (v4l2_int_ioctl_func *)ioctl_try_fmt_cap}, */
+       {vidioc_int_g_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_g_fmt_cap},
+       {vidioc_int_s_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_s_fmt_cap},
+       {vidioc_int_g_parm_num, (v4l2_int_ioctl_func *)ioctl_g_parm},
+       {vidioc_int_s_parm_num, (v4l2_int_ioctl_func *)ioctl_s_parm},
+/*     {vidioc_int_queryctrl_num, (v4l2_int_ioctl_func *)ioctl_queryctrl}, */
+       {vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func *)ioctl_g_ctrl},
+       {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *)ioctl_s_ctrl},
+};
+
+static struct v4l2_int_slave ov2640_slave = {
+       .ioctls = ov2640_ioctl_desc,
+       .num_ioctls = ARRAY_SIZE(ov2640_ioctl_desc),
+};
+
+static struct v4l2_int_device ov2640_int_device = {
+       .module = THIS_MODULE,
+       .name = "ov2640",
+       .type = v4l2_int_type_slave,
+       .u = {
+               .slave = &ov2640_slave,
+               },
+};
+
+/*!
+ * ov2640 I2C attach function
+ * Function set in i2c_driver struct.
+ * Called by insmod ov2640_camera.ko.
+ *
+ * @param client            struct i2c_client*
+ * @return  Error code indicating success or failure
+ */
+static int ov2640_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       int retval;
+       struct mxc_camera_platform_data *plat_data = client->dev.platform_data;
+
+       pr_debug("In ov2640_probe (RH_BT565)\n");
+
+       /* Set initial values for the sensor struct. */
+       memset(&ov2640_data, 0, sizeof(ov2640_data));
+       ov2640_data.i2c_client = client;
+       ov2640_data.mclk = 24000000;
+       ov2640_data.mclk = plat_data->mclk;
+       ov2640_data.pix.pixelformat = V4L2_PIX_FMT_UYVY;
+       ov2640_data.pix.width = 800;
+       ov2640_data.pix.height = 600;
+       ov2640_data.streamcap.capability = V4L2_MODE_HIGHQUALITY
+                                          | V4L2_CAP_TIMEPERFRAME;
+       ov2640_data.streamcap.capturemode = 0;
+       ov2640_data.streamcap.timeperframe.denominator = DEFAULT_FPS;
+       ov2640_data.streamcap.timeperframe.numerator = 1;
+
+       if (plat_data->io_regulator) {
+               io_regulator =
+                   regulator_get(&client->dev, plat_data->io_regulator);
+               if (!IS_ERR(io_regulator)) {
+                       regulator_set_voltage(io_regulator, 2800000, 2800000);
+                       if (regulator_enable(io_regulator) != 0) {
+                               pr_err("%s:io set voltage error\n", __func__);
+                               goto err1;
+                       } else {
+                               dev_dbg(&client->dev,
+                                       "%s:io set voltage ok\n", __func__);
+                       }
+               } else
+                       io_regulator = NULL;
+       }
+
+       if (plat_data->core_regulator) {
+               core_regulator =
+                   regulator_get(&client->dev, plat_data->core_regulator);
+               if (!IS_ERR(core_regulator)) {
+                       regulator_set_voltage(core_regulator,
+                                        1300000, 1300000);
+                       if (regulator_enable(core_regulator) != 0) {
+                               pr_err("%s:core set voltage error\n", __func__);
+                               goto err2;
+                       } else {
+                               dev_dbg(&client->dev,
+                                       "%s:core set voltage ok\n", __func__);
+                       }
+               } else
+                       core_regulator = NULL;
+       }
+
+       if (plat_data->analog_regulator) {
+               analog_regulator =
+                   regulator_get(&client->dev, plat_data->analog_regulator);
+               if (!IS_ERR(analog_regulator)) {
+                       regulator_set_voltage(analog_regulator, 2000000, 2000000);
+                       if (regulator_enable(analog_regulator) != 0) {
+                               pr_err("%s:analog set voltage error\n",
+                                        __func__);
+                               goto err3;
+                       } else {
+                               dev_dbg(&client->dev,
+                                       "%s:analog set voltage ok\n", __func__);
+                       }
+               } else
+                       analog_regulator = NULL;
+       }
+
+       if (plat_data->gpo_regulator) {
+               gpo_regulator =
+                   regulator_get(&client->dev, plat_data->gpo_regulator);
+               if (!IS_ERR(gpo_regulator)) {
+                       if (regulator_enable(gpo_regulator) != 0) {
+                               pr_err("%s:gpo3 set voltage error\n", __func__);
+                               goto err4;
+                       } else {
+                               dev_dbg(&client->dev,
+                                       "%s:gpo3 set voltage ok\n", __func__);
+                       }
+               } else
+                       gpo_regulator = NULL;
+       }
+
+       /* This function attaches this structure to the /dev/video0 device.
+        * The pointer in priv points to the ov2640_data structure here.*/
+       ov2640_int_device.priv = &ov2640_data;
+       retval = v4l2_int_device_register(&ov2640_int_device);
+
+       return retval;
+
+err4:
+       if (analog_regulator) {
+               regulator_disable(analog_regulator);
+               regulator_put(analog_regulator);
+       }
+err3:
+       if (core_regulator) {
+               regulator_disable(core_regulator);
+               regulator_put(core_regulator);
+       }
+err2:
+       if (io_regulator) {
+               regulator_disable(io_regulator);
+               regulator_put(io_regulator);
+       }
+err1:
+       return -1;
+}
+
+/*!
+ * ov2640 I2C detach function
+ * Called on rmmod ov2640_camera.ko
+ *
+ * @param client            struct i2c_client*
+ * @return  Error code indicating success or failure
+ */
+static int ov2640_remove(struct i2c_client *client)
+{
+       pr_debug("In ov2640_remove\n");
+
+       v4l2_int_device_unregister(&ov2640_int_device);
+
+       if (gpo_regulator) {
+               regulator_disable(gpo_regulator);
+               regulator_put(gpo_regulator);
+       }
+
+       if (analog_regulator) {
+               regulator_disable(analog_regulator);
+               regulator_put(analog_regulator);
+       }
+
+       if (core_regulator) {
+               regulator_disable(core_regulator);
+               regulator_put(core_regulator);
+       }
+
+       if (io_regulator) {
+               regulator_disable(io_regulator);
+               regulator_put(io_regulator);
+       }
+
+       return 0;
+}
+
+static const struct i2c_device_id ov2640_id[] = {
+       {"ov2640", 0},
+       {},
+};
+
+MODULE_DEVICE_TABLE(i2c, ov2640_id);
+
+static struct i2c_driver ov2640_i2c_driver = {
+       .driver = {
+                  .owner = THIS_MODULE,
+                  .name = "ov2640",
+                 },
+       .probe = ov2640_probe,
+       .remove = ov2640_remove,
+       .id_table = ov2640_id,
+/* To add power management add .suspend and .resume functions */
+};
+
+/*!
+ * ov2640 init function
+ * Called by insmod ov2640_camera.ko.
+ *
+ * @return  Error code indicating success or failure
+ */
+static __init int ov2640_init(void)
+{
+       u8 err;
+
+       pr_debug("In ov2640_init\n");
+
+       err = i2c_add_driver(&ov2640_i2c_driver);
+       if (err != 0)
+               pr_err("%s:driver registration failed, error=%d \n",
+                       __func__, err);
+
+       return err;
+}
+
+/*!
+ * OV2640 cleanup function
+ * Called on rmmod ov2640_camera.ko
+ *
+ * @return  Error code indicating success or failure
+ */
+static void __exit ov2640_clean(void)
+{
+       pr_debug("In ov2640_clean\n");
+       i2c_del_driver(&ov2640_i2c_driver);
+}
+
+module_init(ov2640_init);
+module_exit(ov2640_clean);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("OV2640 Camera Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/ov3640.c b/drivers/media/video/mxc/capture/ov3640.c
new file mode 100644 (file)
index 0000000..dc93b72
--- /dev/null
@@ -0,0 +1,1510 @@
+/*
+ * Copyright 2005-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/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
+#include <linux/fsl_devices.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-int-device.h>
+#include "mxc_v4l2_capture.h"
+
+#define OV3640_VOLTAGE_ANALOG               2800000
+#define OV3640_VOLTAGE_DIGITAL_CORE         1500000
+#define OV3640_VOLTAGE_DIGITAL_IO           1800000
+#define OV3640_VOLTAGE_DIGITAL_GPO         2800000
+
+#define MIN_FPS 15
+#define MAX_FPS 30
+#define DEFAULT_FPS 30
+
+#define OV3640_XCLK_MIN 6000000
+#define OV3640_XCLK_MAX 24000000
+
+enum ov3640_mode {
+       ov3640_mode_MIN = 0,
+       ov3640_mode_VGA_640_480 = 0,
+       ov3640_mode_QVGA_320_240 = 1,
+       ov3640_mode_XGA_1024_768 = 2,
+       ov3640_mode_QXGA_2048_1536 = 3,
+       ov3640_mode_NTSC_720_480 = 4,
+       ov3640_mode_PAL_720_576 = 5,
+       ov3640_mode_MAX = 5
+};
+
+enum ov3640_frame_rate {
+       ov3640_15_fps,
+       ov3640_30_fps
+};
+
+struct reg_value {
+       u16 u16RegAddr;
+       u8 u8Val;
+       u8 u8Mask;
+       u32 u32Delay_ms;
+};
+
+struct ov3640_mode_info {
+       enum ov3640_mode mode;
+       u32 width;
+       u32 height;
+       struct reg_value *init_data_ptr;
+       u32 init_data_size;
+};
+
+/*!
+ * Maintains the information on the current state of the sesor.
+ */
+struct sensor {
+       const struct ov3640_platform_data *platform_data;
+       struct v4l2_int_device *v4l2_int_device;
+       struct i2c_client *i2c_client;
+       struct v4l2_pix_format pix;
+       struct v4l2_captureparm streamcap;
+       bool on;
+
+       /* control settings */
+       int brightness;
+       int hue;
+       int contrast;
+       int saturation;
+       int red;
+       int green;
+       int blue;
+       int ae_mode;
+
+       u32 mclk;
+       int csi;
+} ov3640_data;
+
+static struct reg_value ov3640_setting_15fps_QXGA_2048_1536[] = {
+#if 0
+       /* The true 15fps QXGA setting. */
+       {0x3012, 0x80, 0, 0}, {0x304d, 0x41, 0, 0}, {0x3087, 0x16, 0, 0},
+       {0x30aa, 0x45, 0, 0}, {0x30b0, 0xff, 0, 0}, {0x30b1, 0xff, 0, 0},
+       {0x30b2, 0x13, 0, 0}, {0x30d7, 0x10, 0, 0}, {0x309e, 0x00, 0, 0},
+       {0x3602, 0x26, 0, 0}, {0x3603, 0x4D, 0, 0}, {0x364c, 0x04, 0, 0},
+       {0x360c, 0x12, 0, 0}, {0x361e, 0x00, 0, 0}, {0x361f, 0x11, 0, 0},
+       {0x3633, 0x03, 0, 0}, {0x3629, 0x3c, 0, 0}, {0x300e, 0x33, 0, 0},
+       {0x300f, 0x21, 0, 0}, {0x3010, 0x20, 0, 0}, {0x3011, 0x00, 0, 0},
+       {0x304c, 0x81, 0, 0}, {0x3029, 0x47, 0, 0}, {0x3070, 0x00, 0, 0},
+       {0x3071, 0xEC, 0, 0}, {0x301C, 0x06, 0, 0}, {0x3072, 0x00, 0, 0},
+       {0x3073, 0xC5, 0, 0}, {0x301D, 0x07, 0, 0}, {0x3018, 0x38, 0, 0},
+       {0x3019, 0x30, 0, 0}, {0x301a, 0x61, 0, 0}, {0x307d, 0x00, 0, 0},
+       {0x3087, 0x02, 0, 0}, {0x3082, 0x20, 0, 0}, {0x303c, 0x08, 0, 0},
+       {0x303d, 0x18, 0, 0}, {0x303e, 0x06, 0, 0}, {0x303F, 0x0c, 0, 0},
+       {0x3030, 0x62, 0, 0}, {0x3031, 0x26, 0, 0}, {0x3032, 0xe6, 0, 0},
+       {0x3033, 0x6e, 0, 0}, {0x3034, 0xea, 0, 0}, {0x3035, 0xae, 0, 0},
+       {0x3036, 0xa6, 0, 0}, {0x3037, 0x6a, 0, 0}, {0x3015, 0x12, 0, 0},
+       {0x3014, 0x04, 0, 0}, {0x3013, 0xf7, 0, 0}, {0x3104, 0x02, 0, 0},
+       {0x3105, 0xfd, 0, 0}, {0x3106, 0x00, 0, 0}, {0x3107, 0xff, 0, 0},
+       {0x3308, 0xa5, 0, 0}, {0x3316, 0xff, 0, 0}, {0x3317, 0x00, 0, 0},
+       {0x3087, 0x02, 0, 0}, {0x3082, 0x20, 0, 0}, {0x3300, 0x13, 0, 0},
+       {0x3301, 0xd6, 0, 0}, {0x3302, 0xef, 0, 0}, {0x30b8, 0x20, 0, 0},
+       {0x30b9, 0x17, 0, 0}, {0x30ba, 0x04, 0, 0}, {0x30bb, 0x08, 0, 0},
+       {0x3100, 0x02, 0, 0}, {0x3304, 0x00, 0, 0}, {0x3400, 0x00, 0, 0},
+       {0x3404, 0x02, 0, 0}, {0x3020, 0x01, 0, 0}, {0x3021, 0x1d, 0, 0},
+       {0x3022, 0x00, 0, 0}, {0x3023, 0x0a, 0, 0}, {0x3024, 0x08, 0, 0},
+       {0x3025, 0x18, 0, 0}, {0x3026, 0x06, 0, 0}, {0x3027, 0x0c, 0, 0},
+       {0x335f, 0x68, 0, 0}, {0x3360, 0x18, 0, 0}, {0x3361, 0x0c, 0, 0},
+       {0x3362, 0x68, 0, 0}, {0x3363, 0x08, 0, 0}, {0x3364, 0x04, 0, 0},
+       {0x3403, 0x42, 0, 0}, {0x3088, 0x08, 0, 0}, {0x3089, 0x00, 0, 0},
+       {0x308a, 0x06, 0, 0}, {0x308b, 0x00, 0, 0}, {0x3507, 0x06, 0, 0},
+       {0x350a, 0x4f, 0, 0}, {0x3600, 0xc4, 0, 0},
+#endif
+       /*
+        * Only support 7.5fps for QXGA to workaround screen tearing issue
+        * for 15fps when capturing still image.
+        */
+       {0x3012, 0x80, 0, 0}, {0x304d, 0x45, 0, 0}, {0x30a7, 0x5e, 0, 0},
+       {0x3087, 0x16, 0, 0}, {0x309c, 0x1a, 0, 0}, {0x30a2, 0xe4, 0, 0},
+       {0x30aa, 0x42, 0, 0}, {0x30b0, 0xff, 0, 0}, {0x30b1, 0xff, 0, 0},
+       {0x30b2, 0x10, 0, 0}, {0x300e, 0x32, 0, 0}, {0x300f, 0x21, 0, 0},
+       {0x3010, 0x20, 0, 0}, {0x3011, 0x00, 0, 0}, {0x304c, 0x81, 0, 0},
+       {0x30d7, 0x10, 0, 0}, {0x30d9, 0x0d, 0, 0}, {0x30db, 0x08, 0, 0},
+       {0x3016, 0x82, 0, 0}, {0x3018, 0x38, 0, 0}, {0x3019, 0x30, 0, 0},
+       {0x301a, 0x61, 0, 0}, {0x307d, 0x00, 0, 0}, {0x3087, 0x02, 0, 0},
+       {0x3082, 0x20, 0, 0}, {0x3015, 0x12, 0, 0}, {0x3014, 0x04, 0, 0},
+       {0x3013, 0xf7, 0, 0}, {0x303c, 0x08, 0, 0}, {0x303d, 0x18, 0, 0},
+       {0x303e, 0x06, 0, 0}, {0x303f, 0x0c, 0, 0}, {0x3030, 0x62, 0, 0},
+       {0x3031, 0x26, 0, 0}, {0x3032, 0xe6, 0, 0}, {0x3033, 0x6e, 0, 0},
+       {0x3034, 0xea, 0, 0}, {0x3035, 0xae, 0, 0}, {0x3036, 0xa6, 0, 0},
+       {0x3037, 0x6a, 0, 0}, {0x3104, 0x02, 0, 0}, {0x3105, 0xfd, 0, 0},
+       {0x3106, 0x00, 0, 0}, {0x3107, 0xff, 0, 0}, {0x3300, 0x12, 0, 0},
+       {0x3301, 0xde, 0, 0}, {0x3302, 0xcf, 0, 0}, {0x3312, 0x26, 0, 0},
+       {0x3314, 0x42, 0, 0}, {0x3313, 0x2b, 0, 0}, {0x3315, 0x42, 0, 0},
+       {0x3310, 0xd0, 0, 0}, {0x3311, 0xbd, 0, 0}, {0x330c, 0x18, 0, 0},
+       {0x330d, 0x18, 0, 0}, {0x330e, 0x56, 0, 0}, {0x330f, 0x5c, 0, 0},
+       {0x330b, 0x1c, 0, 0}, {0x3306, 0x5c, 0, 0}, {0x3307, 0x11, 0, 0},
+       {0x336a, 0x52, 0, 0}, {0x3370, 0x46, 0, 0}, {0x3376, 0x38, 0, 0},
+       {0x30b8, 0x20, 0, 0}, {0x30b9, 0x17, 0, 0}, {0x30ba, 0x04, 0, 0},
+       {0x30bb, 0x08, 0, 0}, {0x3507, 0x06, 0, 0}, {0x350a, 0x4f, 0, 0},
+       {0x3100, 0x02, 0, 0}, {0x3301, 0xde, 0, 0}, {0x3304, 0x00, 0, 0},
+       {0x3400, 0x00, 0, 0}, {0x3404, 0x02, 0, 0}, {0x3600, 0xc4, 0, 0},
+       {0x3088, 0x08, 0, 0}, {0x3089, 0x00, 0, 0}, {0x308a, 0x06, 0, 0},
+       {0x308b, 0x00, 0, 0}, {0x308d, 0x04, 0, 0}, {0x3086, 0x03, 0, 0},
+       {0x3086, 0x00, 0, 0}, {0x3011, 0x01, 0, 0},
+};
+
+static struct reg_value ov3640_setting_15fps_XGA_1024_768[] = {
+       {0x3012, 0x80, 0, 0}, {0x304d, 0x45, 0, 0}, {0x30a7, 0x5e, 0, 0},
+       {0x3087, 0x16, 0, 0}, {0x309c, 0x1a, 0, 0}, {0x30a2, 0xe4, 0, 0},
+       {0x30aa, 0x42, 0, 0}, {0x30b0, 0xff, 0, 0}, {0x30b1, 0xff, 0, 0},
+       {0x30b2, 0x10, 0, 0}, {0x300e, 0x32, 0, 0}, {0x300f, 0x21, 0, 0},
+       {0x3010, 0x20, 0, 0}, {0x3011, 0x00, 0, 0}, {0x304c, 0x81, 0, 0},
+       {0x3016, 0x82, 0, 0}, {0x3018, 0x38, 0, 0}, {0x3019, 0x30, 0, 0},
+       {0x301a, 0x61, 0, 0}, {0x307d, 0x00, 0, 0}, {0x3087, 0x02, 0, 0},
+       {0x3082, 0x20, 0, 0}, {0x3015, 0x12, 0, 0}, {0x3014, 0x04, 0, 0},
+       {0x3013, 0xf7, 0, 0}, {0x303c, 0x08, 0, 0}, {0x303d, 0x18, 0, 0},
+       {0x303e, 0x06, 0, 0}, {0x303f, 0x0c, 0, 0}, {0x3030, 0x62, 0, 0},
+       {0x3031, 0x26, 0, 0}, {0x3032, 0xe6, 0, 0}, {0x3033, 0x6e, 0, 0},
+       {0x3034, 0xea, 0, 0}, {0x3035, 0xae, 0, 0}, {0x3036, 0xa6, 0, 0},
+       {0x3037, 0x6a, 0, 0}, {0x3104, 0x02, 0, 0}, {0x3105, 0xfd, 0, 0},
+       {0x3106, 0x00, 0, 0}, {0x3107, 0xff, 0, 0}, {0x3300, 0x12, 0, 0},
+       {0x3301, 0xde, 0, 0}, {0x3302, 0xcf, 0, 0}, {0x3312, 0x26, 0, 0},
+       {0x3314, 0x42, 0, 0}, {0x3313, 0x2b, 0, 0}, {0x3315, 0x42, 0, 0},
+       {0x3310, 0xd0, 0, 0}, {0x3311, 0xbd, 0, 0}, {0x330c, 0x18, 0, 0},
+       {0x330d, 0x18, 0, 0}, {0x330e, 0x56, 0, 0}, {0x330f, 0x5c, 0, 0},
+       {0x330b, 0x1c, 0, 0}, {0x3306, 0x5c, 0, 0}, {0x3307, 0x11, 0, 0},
+       {0x336a, 0x52, 0, 0}, {0x3370, 0x46, 0, 0}, {0x3376, 0x38, 0, 0},
+       {0x30b8, 0x20, 0, 0}, {0x30b9, 0x17, 0, 0}, {0x30ba, 0x04, 0, 0},
+       {0x30bb, 0x08, 0, 0}, {0x3507, 0x06, 0, 0}, {0x350a, 0x4f, 0, 0},
+       {0x3100, 0x02, 0, 0}, {0x3301, 0xde, 0, 0}, {0x3304, 0x00, 0, 0},
+       {0x3400, 0x01, 0, 0}, {0x3404, 0x1d, 0, 0}, {0x3600, 0xc4, 0, 0},
+       {0x3302, 0xef, 0, 0}, {0x3020, 0x01, 0, 0}, {0x3021, 0x1d, 0, 0},
+       {0x3022, 0x00, 0, 0}, {0x3023, 0x0a, 0, 0}, {0x3024, 0x08, 0, 0},
+       {0x3025, 0x00, 0, 0}, {0x3026, 0x06, 0, 0}, {0x3027, 0x00, 0, 0},
+       {0x335f, 0x68, 0, 0}, {0x3360, 0x00, 0, 0}, {0x3361, 0x00, 0, 0},
+       {0x3362, 0x34, 0, 0}, {0x3363, 0x00, 0, 0}, {0x3364, 0x00, 0, 0},
+       {0x3403, 0x00, 0, 0}, {0x3088, 0x04, 0, 0}, {0x3089, 0x00, 0, 0},
+       {0x308a, 0x03, 0, 0}, {0x308b, 0x00, 0, 0}, {0x307c, 0x10, 0, 0},
+       {0x3090, 0xc0, 0, 0}, {0x304c, 0x84, 0, 0}, {0x308d, 0x04, 0, 0},
+       {0x3086, 0x03, 0, 0}, {0x3086, 0x00, 0, 0}, {0x3011, 0x01, 0, 0},
+};
+
+static struct reg_value ov3640_setting_30fps_XGA_1024_768[] = {
+       {0x0, 0x0, 0}
+};
+
+static struct reg_value ov3640_setting_15fps_VGA_640_480[] = {
+       {0x3012, 0x80, 0, 0}, {0x304d, 0x45, 0, 0}, {0x30a7, 0x5e, 0, 0},
+       {0x3087, 0x16, 0, 0}, {0x309c, 0x1a, 0, 0}, {0x30a2, 0xe4, 0, 0},
+       {0x30aa, 0x42, 0, 0}, {0x30b0, 0xff, 0, 0}, {0x30b1, 0xff, 0, 0},
+       {0x30b2, 0x10, 0, 0}, {0x300e, 0x32, 0, 0}, {0x300f, 0x21, 0, 0},
+       {0x3010, 0x20, 0, 0}, {0x3011, 0x00, 0, 0}, {0x304c, 0x81, 0, 0},
+       {0x30d7, 0x10, 0, 0}, {0x30d9, 0x0d, 0, 0}, {0x30db, 0x08, 0, 0},
+       {0x3016, 0x82, 0, 0}, {0x3018, 0x38, 0, 0}, {0x3019, 0x30, 0, 0},
+       {0x301a, 0x61, 0, 0}, {0x307d, 0x00, 0, 0}, {0x3087, 0x02, 0, 0},
+       {0x3082, 0x20, 0, 0}, {0x3015, 0x12, 0, 0}, {0x3014, 0x04, 0, 0},
+       {0x3013, 0xf7, 0, 0}, {0x303c, 0x08, 0, 0}, {0x303d, 0x18, 0, 0},
+       {0x303e, 0x06, 0, 0}, {0x303f, 0x0c, 0, 0}, {0x3030, 0x62, 0, 0},
+       {0x3031, 0x26, 0, 0}, {0x3032, 0xe6, 0, 0}, {0x3033, 0x6e, 0, 0},
+       {0x3034, 0xea, 0, 0}, {0x3035, 0xae, 0, 0}, {0x3036, 0xa6, 0, 0},
+       {0x3037, 0x6a, 0, 0}, {0x3104, 0x02, 0, 0}, {0x3105, 0xfd, 0, 0},
+       {0x3106, 0x00, 0, 0}, {0x3107, 0xff, 0, 0}, {0x3300, 0x12, 0, 0},
+       {0x3301, 0xde, 0, 0}, {0x3302, 0xcf, 0, 0}, {0x3312, 0x26, 0, 0},
+       {0x3314, 0x42, 0, 0}, {0x3313, 0x2b, 0, 0}, {0x3315, 0x42, 0, 0},
+       {0x3310, 0xd0, 0, 0}, {0x3311, 0xbd, 0, 0}, {0x330c, 0x18, 0, 0},
+       {0x330d, 0x18, 0, 0}, {0x330e, 0x56, 0, 0}, {0x330f, 0x5c, 0, 0},
+       {0x330b, 0x1c, 0, 0}, {0x3306, 0x5c, 0, 0}, {0x3307, 0x11, 0, 0},
+       {0x336a, 0x52, 0, 0}, {0x3370, 0x46, 0, 0}, {0x3376, 0x38, 0, 0},
+       {0x30b8, 0x20, 0, 0}, {0x30b9, 0x17, 0, 0}, {0x30ba, 0x04, 0, 0},
+       {0x30bb, 0x08, 0, 0}, {0x3507, 0x06, 0, 0}, {0x350a, 0x4f, 0, 0},
+       {0x3100, 0x02, 0, 0}, {0x3301, 0xde, 0, 0}, {0x3304, 0x00, 0, 0},
+       {0x3400, 0x00, 0, 0}, {0x3404, 0x42, 0, 0}, {0x3600, 0xc4, 0, 0},
+       {0x3302, 0xef, 0, 0}, {0x3020, 0x01, 0, 0}, {0x3021, 0x1d, 0, 0},
+       {0x3022, 0x00, 0, 0}, {0x3023, 0x0a, 0, 0}, {0x3024, 0x08, 0, 0},
+       {0x3025, 0x00, 0, 0}, {0x3026, 0x06, 0, 0}, {0x3027, 0x00, 0, 0},
+       {0x335f, 0x68, 0, 0}, {0x3360, 0x00, 0, 0}, {0x3361, 0x00, 0, 0},
+       {0x3362, 0x12, 0, 0}, {0x3363, 0x80, 0, 0}, {0x3364, 0xe0, 0, 0},
+       {0x3403, 0x00, 0, 0}, {0x3088, 0x02, 0, 0}, {0x3089, 0x80, 0, 0},
+       {0x308a, 0x01, 0, 0}, {0x308b, 0xe0, 0, 0}, {0x307c, 0x10, 0, 0},
+       {0x3090, 0xc0, 0, 0}, {0x304c, 0x84, 0, 0}, {0x308d, 0x04, 0, 0},
+       {0x3086, 0x03, 0, 0}, {0x3086, 0x00, 0, 0}, {0x3011, 0x00, 0, 0},
+};
+
+static struct reg_value ov3640_setting_30fps_VGA_640_480[] = {
+       {0x3012, 0x80, 0, 0}, {0x304d, 0x45, 0, 0}, {0x30a7, 0x5e, 0, 0},
+       {0x3087, 0x16, 0, 0}, {0x309c, 0x1a, 0, 0}, {0x30a2, 0xe4, 0, 0},
+       {0x30aa, 0x42, 0, 0}, {0x30b0, 0xff, 0, 0}, {0x30b1, 0xff, 0, 0},
+       {0x30b2, 0x10, 0, 0}, {0x300e, 0x32, 0, 0}, {0x300f, 0x21, 0, 0},
+       {0x3010, 0x20, 0, 0}, {0x3011, 0x01, 0, 0}, {0x304c, 0x82, 0, 0},
+       {0x30d7, 0x10, 0, 0}, {0x30d9, 0x0d, 0, 0}, {0x30db, 0x08, 0, 0},
+       {0x3016, 0x82, 0, 0}, {0x3018, 0x38, 0, 0}, {0x3019, 0x30, 0, 0},
+       {0x301a, 0x61, 0, 0}, {0x307d, 0x00, 0, 0}, {0x3087, 0x02, 0, 0},
+       {0x3082, 0x20, 0, 0}, {0x3015, 0x12, 0, 0}, {0x3014, 0x0c, 0, 0},
+       {0x3013, 0xf7, 0, 0}, {0x303c, 0x08, 0, 0}, {0x303d, 0x18, 0, 0},
+       {0x303e, 0x06, 0, 0}, {0x303f, 0x0c, 0, 0}, {0x3030, 0x62, 0, 0},
+       {0x3031, 0x26, 0, 0}, {0x3032, 0xe6, 0, 0}, {0x3033, 0x6e, 0, 0},
+       {0x3034, 0xea, 0, 0}, {0x3035, 0xae, 0, 0}, {0x3036, 0xa6, 0, 0},
+       {0x3037, 0x6a, 0, 0}, {0x3104, 0x02, 0, 0}, {0x3105, 0xfd, 0, 0},
+       {0x3106, 0x00, 0, 0}, {0x3107, 0xff, 0, 0}, {0x3300, 0x12, 0, 0},
+       {0x3301, 0xde, 0, 0}, {0x3302, 0xcf, 0, 0}, {0x3312, 0x26, 0, 0},
+       {0x3314, 0x42, 0, 0}, {0x3313, 0x2b, 0, 0}, {0x3315, 0x42, 0, 0},
+       {0x3310, 0xd0, 0, 0}, {0x3311, 0xbd, 0, 0}, {0x330c, 0x18, 0, 0},
+       {0x330d, 0x18, 0, 0}, {0x330e, 0x56, 0, 0}, {0x330f, 0x5c, 0, 0},
+       {0x330b, 0x1c, 0, 0}, {0x3306, 0x5c, 0, 0}, {0x3307, 0x11, 0, 0},
+       {0x336a, 0x52, 0, 0}, {0x3370, 0x46, 0, 0}, {0x3376, 0x38, 0, 0},
+       {0x3300, 0x13, 0, 0}, {0x30b8, 0x20, 0, 0}, {0x30b9, 0x17, 0, 0},
+       {0x30ba, 0x04, 0, 0}, {0x30bb, 0x08, 0, 0}, {0x3100, 0x02, 0, 0},
+       {0x3301, 0x10, 0x30, 0}, {0x3304, 0x00, 0x03, 0}, {0x3400, 0x00, 0, 0},
+       {0x3404, 0x02, 0, 0}, {0x3600, 0xc0, 0, 0}, {0x308d, 0x04, 0, 0},
+       {0x3086, 0x03, 0, 0}, {0x3086, 0x00, 0, 0}, {0x3012, 0x10, 0, 0},
+       {0x3023, 0x06, 0, 0}, {0x3026, 0x03, 0, 0}, {0x3027, 0x04, 0, 0},
+       {0x302a, 0x03, 0, 0}, {0x302b, 0x10, 0, 0}, {0x3075, 0x24, 0, 0},
+       {0x300d, 0x01, 0, 0}, {0x30d7, 0x80, 0x80, 0}, {0x3069, 0x00, 0x40, 0},
+       {0x303e, 0x00, 0, 0}, {0x303f, 0xc0, 0, 0}, {0x3302, 0x20, 0x20, 0},
+       {0x335f, 0x34, 0, 0}, {0x3360, 0x0c, 0, 0}, {0x3361, 0x04, 0, 0},
+       {0x3362, 0x12, 0, 0}, {0x3363, 0x88, 0, 0}, {0x3364, 0xe4, 0, 0},
+       {0x3403, 0x42, 0, 0}, {0x3088, 0x02, 0, 0}, {0x3089, 0x80, 0, 0},
+       {0x308a, 0x01, 0, 0}, {0x308b, 0xe0, 0, 0}, {0x3362, 0x12, 0, 0},
+       {0x3363, 0x88, 0, 0}, {0x3364, 0xe4, 0, 0}, {0x3403, 0x42, 0, 0},
+       {0x3088, 0x02, 0, 0}, {0x3089, 0x80, 0, 0}, {0x308a, 0x01, 0, 0},
+       {0x308b, 0xe0, 0, 0}, {0x300e, 0x37, 0, 0}, {0x300f, 0xe1, 0, 0},
+       {0x3010, 0x22, 0, 0}, {0x3011, 0x01, 0, 0}, {0x304c, 0x84, 0, 0},
+       {0x3014, 0x04, 0, 0}, {0x3015, 0x02, 0, 0}, {0x302e, 0x00, 0, 0},
+       {0x302d, 0x00, 0, 0},
+};
+
+static struct reg_value ov3640_setting_15fps_QVGA_320_240[] = {
+       {0x3012, 0x80, 0, 0}, {0x304d, 0x45, 0, 0}, {0x30a7, 0x5e, 0, 0},
+       {0x3087, 0x16, 0, 0}, {0x309c, 0x1a, 0, 0}, {0x30a2, 0xe4, 0, 0},
+       {0x30aa, 0x42, 0, 0}, {0x30b0, 0xff, 0, 0}, {0x30b1, 0xff, 0, 0},
+       {0x30b2, 0x10, 0, 0}, {0x300e, 0x32, 0, 0}, {0x300f, 0x21, 0, 0},
+       {0x3010, 0x20, 0, 0}, {0x3011, 0x00, 0, 0}, {0x304c, 0x81, 0, 0},
+       {0x30d7, 0x10, 0, 0}, {0x30d9, 0x0d, 0, 0}, {0x30db, 0x08, 0, 0},
+       {0x3016, 0x82, 0, 0}, {0x3018, 0x38, 0, 0}, {0x3019, 0x30, 0, 0},
+       {0x301a, 0x61, 0, 0}, {0x307d, 0x00, 0, 0}, {0x3087, 0x02, 0, 0},
+       {0x3082, 0x20, 0, 0}, {0x3015, 0x12, 0, 0}, {0x3014, 0x04, 0, 0},
+       {0x3013, 0xf7, 0, 0}, {0x303c, 0x08, 0, 0}, {0x303d, 0x18, 0, 0},
+       {0x303e, 0x06, 0, 0}, {0x303f, 0x0c, 0, 0}, {0x3030, 0x62, 0, 0},
+       {0x3031, 0x26, 0, 0}, {0x3032, 0xe6, 0, 0}, {0x3033, 0x6e, 0, 0},
+       {0x3034, 0xea, 0, 0}, {0x3035, 0xae, 0, 0}, {0x3036, 0xa6, 0, 0},
+       {0x3037, 0x6a, 0, 0}, {0x3104, 0x02, 0, 0}, {0x3105, 0xfd, 0, 0},
+       {0x3106, 0x00, 0, 0}, {0x3107, 0xff, 0, 0}, {0x3300, 0x12, 0, 0},
+       {0x3301, 0xde, 0, 0}, {0x3302, 0xcf, 0, 0}, {0x3312, 0x26, 0, 0},
+       {0x3314, 0x42, 0, 0}, {0x3313, 0x2b, 0, 0}, {0x3315, 0x42, 0, 0},
+       {0x3310, 0xd0, 0, 0}, {0x3311, 0xbd, 0, 0}, {0x330c, 0x18, 0, 0},
+       {0x330d, 0x18, 0, 0}, {0x330e, 0x56, 0, 0}, {0x330f, 0x5c, 0, 0},
+       {0x330b, 0x1c, 0, 0}, {0x3306, 0x5c, 0, 0}, {0x3307, 0x11, 0, 0},
+       {0x336a, 0x52, 0, 0}, {0x3370, 0x46, 0, 0}, {0x3376, 0x38, 0, 0},
+       {0x30b8, 0x20, 0, 0}, {0x30b9, 0x17, 0, 0}, {0x30ba, 0x04, 0, 0},
+       {0x30bb, 0x08, 0, 0}, {0x3507, 0x06, 0, 0}, {0x350a, 0x4f, 0, 0},
+       {0x3100, 0x02, 0, 0}, {0x3301, 0xde, 0, 0}, {0x3304, 0x00, 0, 0},
+       {0x3400, 0x00, 0, 0}, {0x3404, 0x42, 0, 0}, {0x3600, 0xc4, 0, 0},
+       {0x3302, 0xef, 0, 0}, {0x3020, 0x01, 0, 0}, {0x3021, 0x1d, 0, 0},
+       {0x3022, 0x00, 0, 0}, {0x3023, 0x0a, 0, 0}, {0x3024, 0x08, 0, 0},
+       {0x3025, 0x00, 0, 0}, {0x3026, 0x06, 0, 0}, {0x3027, 0x00, 0, 0},
+       {0x335f, 0x68, 0, 0}, {0x3360, 0x00, 0, 0}, {0x3361, 0x00, 0, 0},
+       {0x3362, 0x01, 0, 0}, {0x3363, 0x40, 0, 0}, {0x3364, 0xf0, 0, 0},
+       {0x3403, 0x00, 0, 0}, {0x3088, 0x01, 0, 0}, {0x3089, 0x40, 0, 0},
+       {0x308a, 0x00, 0, 0}, {0x308b, 0xf0, 0, 0}, {0x307c, 0x10, 0, 0},
+       {0x3090, 0xc0, 0, 0}, {0x304c, 0x84, 0, 0}, {0x308d, 0x04, 0, 0},
+       {0x3086, 0x03, 0, 0}, {0x3086, 0x00, 0, 0}, {0x3011, 0x01, 0, 0},
+};
+
+static struct reg_value ov3640_setting_30fps_QVGA_320_240[] = {
+       {0x3012, 0x80, 0, 0}, {0x304d, 0x45, 0, 0}, {0x30a7, 0x5e, 0, 0},
+       {0x3087, 0x16, 0, 0}, {0x309c, 0x1a, 0, 0}, {0x30a2, 0xe4, 0, 0},
+       {0x30aa, 0x42, 0, 0}, {0x30b0, 0xff, 0, 0}, {0x30b1, 0xff, 0, 0},
+       {0x30b2, 0x10, 0, 0}, {0x300e, 0x32, 0, 0}, {0x300f, 0x21, 0, 0},
+       {0x3010, 0x20, 0, 0}, {0x3011, 0x01, 0, 0}, {0x304c, 0x82, 0, 0},
+       {0x30d7, 0x10, 0, 0}, {0x30d9, 0x0d, 0, 0}, {0x30db, 0x08, 0, 0},
+       {0x3016, 0x82, 0, 0}, {0x3018, 0x38, 0, 0}, {0x3019, 0x30, 0, 0},
+       {0x301a, 0x61, 0, 0}, {0x307d, 0x00, 0, 0}, {0x3087, 0x02, 0, 0},
+       {0x3082, 0x20, 0, 0}, {0x3015, 0x12, 0, 0}, {0x3014, 0x0c, 0, 0},
+       {0x3013, 0xf7, 0, 0}, {0x303c, 0x08, 0, 0}, {0x303d, 0x18, 0, 0},
+       {0x303e, 0x06, 0, 0}, {0x303f, 0x0c, 0, 0}, {0x3030, 0x62, 0, 0},
+       {0x3031, 0x26, 0, 0}, {0x3032, 0xe6, 0, 0}, {0x3033, 0x6e, 0, 0},
+       {0x3034, 0xea, 0, 0}, {0x3035, 0xae, 0, 0}, {0x3036, 0xa6, 0, 0},
+       {0x3037, 0x6a, 0, 0}, {0x3104, 0x02, 0, 0}, {0x3105, 0xfd, 0, 0},
+       {0x3106, 0x00, 0, 0}, {0x3107, 0xff, 0, 0}, {0x3300, 0x12, 0, 0},
+       {0x3301, 0xde, 0, 0}, {0x3302, 0xcf, 0, 0}, {0x3312, 0x26, 0, 0},
+       {0x3314, 0x42, 0, 0}, {0x3313, 0x2b, 0, 0}, {0x3315, 0x42, 0, 0},
+       {0x3310, 0xd0, 0, 0}, {0x3311, 0xbd, 0, 0}, {0x330c, 0x18, 0, 0},
+       {0x330d, 0x18, 0, 0}, {0x330e, 0x56, 0, 0}, {0x330f, 0x5c, 0, 0},
+       {0x330b, 0x1c, 0, 0}, {0x3306, 0x5c, 0, 0}, {0x3307, 0x11, 0, 0},
+       {0x336a, 0x52, 0, 0}, {0x3370, 0x46, 0, 0}, {0x3376, 0x38, 0, 0},
+       {0x3300, 0x13, 0, 0}, {0x30b8, 0x20, 0, 0}, {0x30b9, 0x17, 0, 0},
+       {0x30ba, 0x04, 0, 0}, {0x30bb, 0x08, 0, 0}, {0x3100, 0x02, 0, 0},
+       {0x3301, 0x10, 0x30, 0}, {0x3304, 0x00, 0x03, 0}, {0x3400, 0x00, 0, 0},
+       {0x3404, 0x02, 0, 0}, {0x3600, 0xc0, 0, 0}, {0x308d, 0x04, 0, 0},
+       {0x3086, 0x03, 0, 0}, {0x3086, 0x00, 0, 0}, {0x3012, 0x10, 0, 0},
+       {0x3023, 0x06, 0, 0}, {0x3026, 0x03, 0, 0}, {0x3027, 0x04, 0, 0},
+       {0x302a, 0x03, 0, 0}, {0x302b, 0x10, 0, 0}, {0x3075, 0x24, 0, 0},
+       {0x300d, 0x01, 0, 0}, {0x30d7, 0x80, 0x80, 0}, {0x3069, 0x00, 0x40, 0},
+       {0x303e, 0x00, 0, 0}, {0x303f, 0xc0, 0, 0}, {0x3302, 0x20, 0x20, 0},
+       {0x335f, 0x34, 0, 0}, {0x3360, 0x0c, 0, 0}, {0x3361, 0x04, 0, 0},
+       {0x3362, 0x34, 0, 0}, {0x3363, 0x08, 0, 0}, {0x3364, 0x04, 0, 0},
+       {0x3403, 0x42, 0, 0}, {0x3088, 0x04, 0, 0}, {0x3089, 0x00, 0, 0},
+       {0x308a, 0x03, 0, 0}, {0x308b, 0x00, 0, 0}, {0x3362, 0x12, 0, 0},
+       {0x3363, 0x88, 0, 0}, {0x3364, 0xe4, 0, 0}, {0x3403, 0x42, 0, 0},
+       {0x3088, 0x02, 0, 0}, {0x3089, 0x80, 0, 0}, {0x308a, 0x01, 0, 0},
+       {0x308b, 0xe0, 0, 0}, {0x300e, 0x37, 0, 0}, {0x300f, 0xe1, 0, 0},
+       {0x3010, 0x22, 0, 0}, {0x3011, 0x01, 0, 0}, {0x304c, 0x84, 0, 0},
+};
+
+static struct reg_value ov3640_setting_15fps_NTSC_720_480[] = {
+       {0x3012, 0x80, 0, 0}, {0x304d, 0x45, 0, 0}, {0x30a7, 0x5e, 0, 0},
+       {0x3087, 0x16, 0, 0}, {0x309C, 0x1a, 0, 0}, {0x30a2, 0xe4, 0, 0},
+       {0x30aa, 0x42, 0, 0}, {0x30b0, 0xff, 0, 0}, {0x30b1, 0xff, 0, 0},
+       {0x30b2, 0x10, 0, 0}, {0x300e, 0x39, 0, 0}, {0x300f, 0x21, 0, 0},
+       {0x3010, 0x20, 0, 0}, {0x304c, 0x81, 0, 0}, {0x30d7, 0x10, 0, 0},
+       {0x30d9, 0x0d, 0, 0}, {0x30db, 0x08, 0, 0}, {0x3016, 0x82, 0, 0},
+       {0x3018, 0x48, 0, 0}, {0x3019, 0x40, 0, 0}, {0x301a, 0x82, 0, 0},
+       {0x307d, 0x00, 0, 0}, {0x3087, 0x02, 0, 0}, {0x3082, 0x20, 0, 0},
+       {0x3015, 0x12, 0, 0}, {0x3014, 0x84, 0, 0}, {0x3013, 0xf7, 0, 0},
+       {0x303c, 0x08, 0, 0}, {0x303d, 0x18, 0, 0}, {0x303e, 0x06, 0, 0},
+       {0x303F, 0x0c, 0, 0}, {0x3030, 0x62, 0, 0}, {0x3031, 0x26, 0, 0},
+       {0x3032, 0xe6, 0, 0}, {0x3033, 0x6e, 0, 0}, {0x3034, 0xea, 0, 0},
+       {0x3035, 0xae, 0, 0}, {0x3036, 0xa6, 0, 0}, {0x3037, 0x6a, 0, 0},
+       {0x3104, 0x02, 0, 0}, {0x3105, 0xfd, 0, 0}, {0x3106, 0x00, 0, 0},
+       {0x3107, 0xff, 0, 0}, {0x3300, 0x13, 0, 0}, {0x3301, 0xde, 0, 0},
+       {0x3302, 0xcf, 0, 0}, {0x3312, 0x26, 0, 0}, {0x3314, 0x42, 0, 0},
+       {0x3313, 0x2b, 0, 0}, {0x3315, 0x42, 0, 0}, {0x3310, 0xd0, 0, 0},
+       {0x3311, 0xbd, 0, 0}, {0x330c, 0x18, 0, 0}, {0x330d, 0x18, 0, 0},
+       {0x330e, 0x56, 0, 0}, {0x330f, 0x5c, 0, 0}, {0x330b, 0x1c, 0, 0},
+       {0x3306, 0x5c, 0, 0}, {0x3307, 0x11, 0, 0}, {0x336a, 0x52, 0, 0},
+       {0x3370, 0x46, 0, 0}, {0x3376, 0x38, 0, 0}, {0x30b8, 0x20, 0, 0},
+       {0x30b9, 0x17, 0, 0}, {0x30ba, 0x04, 0, 0}, {0x30bb, 0x08, 0, 0},
+       {0x3507, 0x06, 0, 0}, {0x350a, 0x4f, 0, 0}, {0x3100, 0x02, 0, 0},
+       {0x3301, 0xde, 0, 0}, {0x3304, 0xfc, 0, 0}, {0x3400, 0x00, 0, 0},
+       {0x3404, 0x00, 0, 0}, {0x3600, 0xc0, 0, 0}, {0x3088, 0x08, 0, 0},
+       {0x3089, 0x00, 0, 0}, {0x308a, 0x06, 0, 0}, {0x308b, 0x00, 0, 0},
+       {0x308d, 0x04, 0, 0}, {0x3086, 0x03, 0, 0}, {0x3086, 0x00, 0, 0},
+       {0x30a9, 0xb5, 0, 0}, {0x3317, 0x04, 0, 0}, {0x3316, 0xf8, 0, 0},
+       {0x3312, 0x17, 0, 0}, {0x3314, 0x30, 0, 0}, {0x3313, 0x23, 0, 0},
+       {0x3315, 0x3e, 0, 0}, {0x3311, 0x9e, 0, 0}, {0x3310, 0xc0, 0, 0},
+       {0x330c, 0x18, 0, 0}, {0x330d, 0x18, 0, 0}, {0x330e, 0x5e, 0, 0},
+       {0x330f, 0x6c, 0, 0}, {0x330b, 0x1c, 0, 0}, {0x3306, 0x5c, 0, 0},
+       {0x3307, 0x11, 0, 0}, {0x3308, 0x25, 0, 0}, {0x3340, 0x20, 0, 0},
+       {0x3341, 0x50, 0, 0}, {0x3342, 0x18, 0, 0}, {0x3343, 0x23, 0, 0},
+       {0x3344, 0xad, 0, 0}, {0x3345, 0xd0, 0, 0}, {0x3346, 0xb8, 0, 0},
+       {0x3347, 0xb4, 0, 0}, {0x3348, 0x04, 0, 0}, {0x3349, 0x98, 0, 0},
+       {0x3355, 0x02, 0, 0}, {0x3358, 0x44, 0, 0}, {0x3359, 0x44, 0, 0},
+       {0x3300, 0x13, 0, 0}, {0x3367, 0x23, 0, 0}, {0x3368, 0xBB, 0, 0},
+       {0x3369, 0xD6, 0, 0}, {0x336A, 0x2A, 0, 0}, {0x336B, 0x07, 0, 0},
+       {0x336C, 0x00, 0, 0}, {0x336D, 0x23, 0, 0}, {0x336E, 0xC3, 0, 0},
+       {0x336F, 0xDE, 0, 0}, {0x3370, 0x2b, 0, 0}, {0x3371, 0x07, 0, 0},
+       {0x3372, 0x00, 0, 0}, {0x3373, 0x23, 0, 0}, {0x3374, 0x9e, 0, 0},
+       {0x3375, 0xD6, 0, 0}, {0x3376, 0x29, 0, 0}, {0x3377, 0x07, 0, 0},
+       {0x3378, 0x00, 0, 0}, {0x332a, 0x1d, 0, 0}, {0x331b, 0x08, 0, 0},
+       {0x331c, 0x16, 0, 0}, {0x331d, 0x2d, 0, 0}, {0x331e, 0x54, 0, 0},
+       {0x331f, 0x66, 0, 0}, {0x3320, 0x73, 0, 0}, {0x3321, 0x80, 0, 0},
+       {0x3322, 0x8c, 0, 0}, {0x3323, 0x95, 0, 0}, {0x3324, 0x9d, 0, 0},
+       {0x3325, 0xac, 0, 0}, {0x3326, 0xb8, 0, 0}, {0x3327, 0xcc, 0, 0},
+       {0x3328, 0xdd, 0, 0}, {0x3329, 0xee, 0, 0}, {0x332e, 0x04, 0, 0},
+       {0x332f, 0x04, 0, 0}, {0x3331, 0x02, 0, 0}, {0x3100, 0x02, 0, 0},
+       {0x3301, 0xde, 0, 0}, {0x3304, 0xfc, 0, 0}, {0x3400, 0x00, 0, 0},
+       {0x3404, 0x00, 0, 0}, {0x3610, 0x40, 0, 0}, {0x304c, 0x81, 0, 0},
+       {0x307C, 0x10, 0, 0}, {0x3012, 0x10, 0, 0}, {0x3023, 0x06, 0, 0},
+       {0x3026, 0x03, 0, 0}, {0x3027, 0x04, 0, 0}, {0x302a, 0x03, 0, 0},
+       {0x302b, 0x10, 0, 0}, {0x3075, 0x24, 0, 0}, {0x300d, 0x01, 0, 0},
+       {0x30d7, 0x90, 0, 0}, {0x3069, 0x04, 0, 0}, {0x303e, 0x00, 0, 0},
+       {0x303f, 0xc0, 0, 0}, {0x3302, 0xef, 0, 0}, {0x335f, 0x34, 0, 0},
+       {0x3360, 0x0c, 0, 0}, {0x3361, 0x04, 0, 0}, {0x3362, 0x34, 0, 0},
+       {0x3363, 0x08, 0, 0}, {0x3364, 0x04, 0, 0}, {0x3403, 0x42, 0, 0},
+       {0x3088, 0x04, 0, 0}, {0x3089, 0x00, 0, 0}, {0x308a, 0x03, 0, 0},
+       {0x308b, 0x00, 0, 0}, {0x300e, 0x32, 0, 0}, {0x300f, 0x21, 0, 0},
+       {0x3010, 0x20, 0, 0}, {0x3011, 0x01, 0, 0}, {0x304c, 0x82, 0, 0},
+       {0x3302, 0xef, 0, 0}, {0x335f, 0x34, 0, 0}, {0x3360, 0x0c, 0, 0},
+       {0x3361, 0x04, 0, 0}, {0x3362, 0x23, 0, 0}, {0x3363, 0x28, 0, 0},
+       {0x3364, 0x5c, 0, 0}, {0x3403, 0x42, 0, 0}, {0x3088, 0x02, 0, 0},
+       {0x3089, 0xD0, 0, 0}, {0x308a, 0x01, 0, 0}, {0x308b, 0xe0, 0, 0},
+       {0x304c, 0x83, 0, 0}, {0x300e, 0x39, 0, 0}, {0x300f, 0x21, 0, 0},
+       {0x3011, 0x00, 0, 0}, {0x3010, 0x81, 0, 0}, {0x302e, 0x00, 0, 0},
+       {0x302d, 0x00, 0, 0}, {0x3071, 0xeb, 0, 0}, {0x301C, 0x02, 0, 0},
+       {0x3404, 0x02, 0, 0},
+};
+
+static struct reg_value ov3640_setting_15fps_PAL_720_576[] = {
+       {0x3012, 0x80, 0, 0}, {0x304d, 0x45, 0, 0}, {0x30a7, 0x5e, 0, 0},
+       {0x3087, 0x16, 0, 0}, {0x309C, 0x1a, 0, 0}, {0x30a2, 0xe4, 0, 0},
+       {0x30aa, 0x42, 0, 0}, {0x30b0, 0xff, 0, 0}, {0x30b1, 0xff, 0, 0},
+       {0x30b2, 0x10, 0, 0}, {0x300e, 0x39, 0, 0}, {0x300f, 0x21, 0, 0},
+       {0x3010, 0x20, 0, 0}, {0x304c, 0x81, 0, 0}, {0x30d7, 0x10, 0, 0},
+       {0x30d9, 0x0d, 0, 0}, {0x30db, 0x08, 0, 0}, {0x3016, 0x82, 0, 0},
+       {0x3018, 0x48, 0, 0}, {0x3019, 0x40, 0, 0}, {0x301a, 0x82, 0, 0},
+       {0x307d, 0x00, 0, 0}, {0x3087, 0x02, 0, 0}, {0x3082, 0x20, 0, 0},
+       {0x3015, 0x12, 0, 0}, {0x3014, 0x84, 0, 0}, {0x3013, 0xf7, 0, 0},
+       {0x303c, 0x08, 0, 0}, {0x303d, 0x18, 0, 0}, {0x303e, 0x06, 0, 0},
+       {0x303F, 0x0c, 0, 0}, {0x3030, 0x62, 0, 0}, {0x3031, 0x26, 0, 0},
+       {0x3032, 0xe6, 0, 0}, {0x3033, 0x6e, 0, 0}, {0x3034, 0xea, 0, 0},
+       {0x3035, 0xae, 0, 0}, {0x3036, 0xa6, 0, 0}, {0x3037, 0x6a, 0, 0},
+       {0x3104, 0x02, 0, 0}, {0x3105, 0xfd, 0, 0}, {0x3106, 0x00, 0, 0},
+       {0x3107, 0xff, 0, 0}, {0x3300, 0x13, 0, 0}, {0x3301, 0xde, 0, 0},
+       {0x3302, 0xcf, 0, 0}, {0x3312, 0x26, 0, 0}, {0x3314, 0x42, 0, 0},
+       {0x3313, 0x2b, 0, 0}, {0x3315, 0x42, 0, 0}, {0x3310, 0xd0, 0, 0},
+       {0x3311, 0xbd, 0, 0}, {0x330c, 0x18, 0, 0}, {0x330d, 0x18, 0, 0},
+       {0x330e, 0x56, 0, 0}, {0x330f, 0x5c, 0, 0}, {0x330b, 0x1c, 0, 0},
+       {0x3306, 0x5c, 0, 0}, {0x3307, 0x11, 0, 0}, {0x336a, 0x52, 0, 0},
+       {0x3370, 0x46, 0, 0}, {0x3376, 0x38, 0, 0}, {0x30b8, 0x20, 0, 0},
+       {0x30b9, 0x17, 0, 0}, {0x30ba, 0x04, 0, 0}, {0x30bb, 0x08, 0, 0},
+       {0x3507, 0x06, 0, 0}, {0x350a, 0x4f, 0, 0}, {0x3100, 0x02, 0, 0},
+       {0x3301, 0xde, 0, 0}, {0x3304, 0xfc, 0, 0}, {0x3400, 0x00, 0, 0},
+       {0x3404, 0x00, 0, 0}, {0x3600, 0xc0, 0, 0}, {0x3088, 0x08, 0, 0},
+       {0x3089, 0x00, 0, 0}, {0x308a, 0x06, 0, 0}, {0x308b, 0x00, 0, 0},
+       {0x308d, 0x04, 0, 0}, {0x3086, 0x03, 0, 0}, {0x3086, 0x00, 0, 0},
+       {0x30a9, 0xb5, 0, 0}, {0x3317, 0x04, 0, 0}, {0x3316, 0xf8, 0, 0},
+       {0x3312, 0x17, 0, 0}, {0x3314, 0x30, 0, 0}, {0x3313, 0x23, 0, 0},
+       {0x3315, 0x3e, 0, 0}, {0x3311, 0x9e, 0, 0}, {0x3310, 0xc0, 0, 0},
+       {0x330c, 0x18, 0, 0}, {0x330d, 0x18, 0, 0}, {0x330e, 0x5e, 0, 0},
+       {0x330f, 0x6c, 0, 0}, {0x330b, 0x1c, 0, 0}, {0x3306, 0x5c, 0, 0},
+       {0x3307, 0x11, 0, 0}, {0x3308, 0x25, 0, 0}, {0x3340, 0x20, 0, 0},
+       {0x3341, 0x50, 0, 0}, {0x3342, 0x18, 0, 0}, {0x3343, 0x23, 0, 0},
+       {0x3344, 0xad, 0, 0}, {0x3345, 0xd0, 0, 0}, {0x3346, 0xb8, 0, 0},
+       {0x3347, 0xb4, 0, 0}, {0x3348, 0x04, 0, 0}, {0x3349, 0x98, 0, 0},
+       {0x3355, 0x02, 0, 0}, {0x3358, 0x44, 0, 0}, {0x3359, 0x44, 0, 0},
+       {0x3300, 0x13, 0, 0}, {0x3367, 0x23, 0, 0}, {0x3368, 0xBB, 0, 0},
+       {0x3369, 0xD6, 0, 0}, {0x336A, 0x2A, 0, 0}, {0x336B, 0x07, 0, 0},
+       {0x336C, 0x00, 0, 0}, {0x336D, 0x23, 0, 0}, {0x336E, 0xC3, 0, 0},
+       {0x336F, 0xDE, 0, 0}, {0x3370, 0x2b, 0, 0}, {0x3371, 0x07, 0, 0},
+       {0x3372, 0x00, 0, 0}, {0x3373, 0x23, 0, 0}, {0x3374, 0x9e, 0, 0},
+       {0x3375, 0xD6, 0, 0}, {0x3376, 0x29, 0, 0}, {0x3377, 0x07, 0, 0},
+       {0x3378, 0x00, 0, 0}, {0x332a, 0x1d, 0, 0}, {0x331b, 0x08, 0, 0},
+       {0x331c, 0x16, 0, 0}, {0x331d, 0x2d, 0, 0}, {0x331e, 0x54, 0, 0},
+       {0x331f, 0x66, 0, 0}, {0x3320, 0x73, 0, 0}, {0x3321, 0x80, 0, 0},
+       {0x3322, 0x8c, 0, 0}, {0x3323, 0x95, 0, 0}, {0x3324, 0x9d, 0, 0},
+       {0x3325, 0xac, 0, 0}, {0x3326, 0xb8, 0, 0}, {0x3327, 0xcc, 0, 0},
+       {0x3328, 0xdd, 0, 0}, {0x3329, 0xee, 0, 0}, {0x332e, 0x04, 0, 0},
+       {0x332f, 0x04, 0, 0}, {0x3331, 0x02, 0, 0}, {0x3100, 0x02, 0, 0},
+       {0x3301, 0xde, 0, 0}, {0x3304, 0xfc, 0, 0}, {0x3400, 0x00, 0, 0},
+       {0x3404, 0x00, 0, 0}, {0x3610, 0x40, 0, 0}, {0x304c, 0x81, 0, 0},
+       {0x307C, 0x10, 0, 0}, {0x3012, 0x10, 0, 0}, {0x3023, 0x06, 0, 0},
+       {0x3026, 0x03, 0, 0}, {0x3027, 0x04, 0, 0}, {0x302a, 0x03, 0, 0},
+       {0x302b, 0x10, 0, 0}, {0x3075, 0x24, 0, 0}, {0x300d, 0x01, 0, 0},
+       {0x30d7, 0x90, 0, 0}, {0x3069, 0x04, 0, 0}, {0x303e, 0x00, 0, 0},
+       {0x303f, 0xc0, 0, 0}, {0x3302, 0xef, 0, 0}, {0x335f, 0x34, 0, 0},
+       {0x3360, 0x0c, 0, 0}, {0x3361, 0x04, 0, 0}, {0x3362, 0x34, 0, 0},
+       {0x3363, 0x08, 0, 0}, {0x3364, 0x04, 0, 0}, {0x3403, 0x42, 0, 0},
+       {0x3088, 0x04, 0, 0}, {0x3089, 0x00, 0, 0}, {0x308a, 0x03, 0, 0},
+       {0x308b, 0x00, 0, 0}, {0x300e, 0x32, 0, 0}, {0x300f, 0x21, 0, 0},
+       {0x3010, 0x20, 0, 0}, {0x3011, 0x01, 0, 0}, {0x304c, 0x82, 0, 0},
+       {0x3302, 0xef, 0, 0}, {0x335f, 0x34, 0, 0}, {0x3360, 0x0c, 0, 0},
+       {0x3361, 0x04, 0, 0}, {0x3362, 0x23, 0, 0}, {0x3363, 0x28, 0, 0},
+       {0x3364, 0x5c, 0, 0}, {0x3403, 0x42, 0, 0}, {0x3088, 0x02, 0, 0},
+       {0x3089, 0xD0, 0, 0}, {0x308a, 0x02, 0, 0}, {0x308b, 0x40, 0, 0},
+       {0x304c, 0x83, 0, 0}, {0x300e, 0x39, 0, 0}, {0x300f, 0x21, 0, 0},
+       {0x3011, 0x00, 0, 0}, {0x3010, 0x81, 0, 0}, {0x302e, 0x00, 0, 0},
+       {0x302d, 0x00, 0, 0}, {0x3071, 0xeb, 0, 0}, {0x301C, 0x02, 0, 0},
+       {0x3404, 0x02, 0, 0},
+};
+
+static struct reg_value ov3640_setting_30fps_NTSC_720_480[] = {
+       {0x3012, 0x80, 0, 0}, {0x304d, 0x45, 0, 0}, {0x30a7, 0x5e, 0, 0},
+       {0x3087, 0x16, 0, 0}, {0x309C, 0x1a, 0, 0}, {0x30a2, 0xe4, 0, 0},
+       {0x30aa, 0x42, 0, 0}, {0x30b0, 0xff, 0, 0}, {0x30b1, 0xff, 0, 0},
+       {0x30b2, 0x10, 0, 0}, {0x300e, 0x39, 0, 0}, {0x300f, 0x21, 0, 0},
+       {0x3010, 0x20, 0, 0}, {0x304c, 0x81, 0, 0}, {0x30d7, 0x10, 0, 0},
+       {0x30d9, 0x0d, 0, 0}, {0x30db, 0x08, 0, 0}, {0x3016, 0x82, 0, 0},
+       {0x3018, 0x48, 0, 0}, {0x3019, 0x40, 0, 0}, {0x301a, 0x82, 0, 0},
+       {0x307d, 0x00, 0, 0}, {0x3087, 0x02, 0, 0}, {0x3082, 0x20, 0, 0},
+       {0x3015, 0x12, 0, 0}, {0x3014, 0x84, 0, 0}, {0x3013, 0xf7, 0, 0},
+       {0x303c, 0x08, 0, 0}, {0x303d, 0x18, 0, 0}, {0x303e, 0x06, 0, 0},
+       {0x303F, 0x0c, 0, 0}, {0x3030, 0x62, 0, 0}, {0x3031, 0x26, 0, 0},
+       {0x3032, 0xe6, 0, 0}, {0x3033, 0x6e, 0, 0}, {0x3034, 0xea, 0, 0},
+       {0x3035, 0xae, 0, 0}, {0x3036, 0xa6, 0, 0}, {0x3037, 0x6a, 0, 0},
+       {0x3104, 0x02, 0, 0}, {0x3105, 0xfd, 0, 0}, {0x3106, 0x00, 0, 0},
+       {0x3107, 0xff, 0, 0}, {0x3300, 0x13, 0, 0}, {0x3301, 0xde, 0, 0},
+       {0x3302, 0xcf, 0, 0}, {0x3312, 0x26, 0, 0}, {0x3314, 0x42, 0, 0},
+       {0x3313, 0x2b, 0, 0}, {0x3315, 0x42, 0, 0}, {0x3310, 0xd0, 0, 0},
+       {0x3311, 0xbd, 0, 0}, {0x330c, 0x18, 0, 0}, {0x330d, 0x18, 0, 0},
+       {0x330e, 0x56, 0, 0}, {0x330f, 0x5c, 0, 0}, {0x330b, 0x1c, 0, 0},
+       {0x3306, 0x5c, 0, 0}, {0x3307, 0x11, 0, 0}, {0x336a, 0x52, 0, 0},
+       {0x3370, 0x46, 0, 0}, {0x3376, 0x38, 0, 0}, {0x30b8, 0x20, 0, 0},
+       {0x30b9, 0x17, 0, 0}, {0x30ba, 0x04, 0, 0}, {0x30bb, 0x08, 0, 0},
+       {0x3507, 0x06, 0, 0}, {0x350a, 0x4f, 0, 0}, {0x3100, 0x02, 0, 0},
+       {0x3301, 0xde, 0, 0}, {0x3304, 0xfc, 0, 0}, {0x3400, 0x00, 0, 0},
+       {0x3404, 0x00, 0, 0}, {0x3600, 0xc0, 0, 0}, {0x3088, 0x08, 0, 0},
+       {0x3089, 0x00, 0, 0}, {0x308a, 0x06, 0, 0}, {0x308b, 0x00, 0, 0},
+       {0x308d, 0x04, 0, 0}, {0x3086, 0x03, 0, 0}, {0x3086, 0x00, 0, 0},
+       {0x30a9, 0xb5, 0, 0}, {0x3317, 0x04, 0, 0}, {0x3316, 0xf8, 0, 0},
+       {0x3312, 0x17, 0, 0}, {0x3314, 0x30, 0, 0}, {0x3313, 0x23, 0, 0},
+       {0x3315, 0x3e, 0, 0}, {0x3311, 0x9e, 0, 0}, {0x3310, 0xc0, 0, 0},
+       {0x330c, 0x18, 0, 0}, {0x330d, 0x18, 0, 0}, {0x330e, 0x5e, 0, 0},
+       {0x330f, 0x6c, 0, 0}, {0x330b, 0x1c, 0, 0}, {0x3306, 0x5c, 0, 0},
+       {0x3307, 0x11, 0, 0}, {0x3308, 0x25, 0, 0}, {0x3340, 0x20, 0, 0},
+       {0x3341, 0x50, 0, 0}, {0x3342, 0x18, 0, 0}, {0x3343, 0x23, 0, 0},
+       {0x3344, 0xad, 0, 0}, {0x3345, 0xd0, 0, 0}, {0x3346, 0xb8, 0, 0},
+       {0x3347, 0xb4, 0, 0}, {0x3348, 0x04, 0, 0}, {0x3349, 0x98, 0, 0},
+       {0x3355, 0x02, 0, 0}, {0x3358, 0x44, 0, 0}, {0x3359, 0x44, 0, 0},
+       {0x3300, 0x13, 0, 0}, {0x3367, 0x23, 0, 0}, {0x3368, 0xBB, 0, 0},
+       {0x3369, 0xD6, 0, 0}, {0x336A, 0x2A, 0, 0}, {0x336B, 0x07, 0, 0},
+       {0x336C, 0x00, 0, 0}, {0x336D, 0x23, 0, 0}, {0x336E, 0xC3, 0, 0},
+       {0x336F, 0xDE, 0, 0}, {0x3370, 0x2b, 0, 0}, {0x3371, 0x07, 0, 0},
+       {0x3372, 0x00, 0, 0}, {0x3373, 0x23, 0, 0}, {0x3374, 0x9e, 0, 0},
+       {0x3375, 0xD6, 0, 0}, {0x3376, 0x29, 0, 0}, {0x3377, 0x07, 0, 0},
+       {0x3378, 0x00, 0, 0}, {0x332a, 0x1d, 0, 0}, {0x331b, 0x08, 0, 0},
+       {0x331c, 0x16, 0, 0}, {0x331d, 0x2d, 0, 0}, {0x331e, 0x54, 0, 0},
+       {0x331f, 0x66, 0, 0}, {0x3320, 0x73, 0, 0}, {0x3321, 0x80, 0, 0},
+       {0x3322, 0x8c, 0, 0}, {0x3323, 0x95, 0, 0}, {0x3324, 0x9d, 0, 0},
+       {0x3325, 0xac, 0, 0}, {0x3326, 0xb8, 0, 0}, {0x3327, 0xcc, 0, 0},
+       {0x3328, 0xdd, 0, 0}, {0x3329, 0xee, 0, 0}, {0x332e, 0x04, 0, 0},
+       {0x332f, 0x04, 0, 0}, {0x3331, 0x02, 0, 0}, {0x3100, 0x02, 0, 0},
+       {0x3301, 0xde, 0, 0}, {0x3304, 0xfc, 0, 0}, {0x3400, 0x00, 0, 0},
+       {0x3404, 0x00, 0, 0}, {0x3610, 0x40, 0, 0}, {0x304c, 0x81, 0, 0},
+       {0x307C, 0x10, 0, 0}, {0x3012, 0x10, 0, 0}, {0x3023, 0x06, 0, 0},
+       {0x3026, 0x03, 0, 0}, {0x3027, 0x04, 0, 0}, {0x302a, 0x03, 0, 0},
+       {0x302b, 0x10, 0, 0}, {0x3075, 0x24, 0, 0}, {0x300d, 0x01, 0, 0},
+       {0x30d7, 0x90, 0, 0}, {0x3069, 0x04, 0, 0}, {0x303e, 0x00, 0, 0},
+       {0x303f, 0xc0, 0, 0}, {0x3302, 0xef, 0, 0}, {0x335f, 0x34, 0, 0},
+       {0x3360, 0x0c, 0, 0}, {0x3361, 0x04, 0, 0}, {0x3362, 0x34, 0, 0},
+       {0x3363, 0x08, 0, 0}, {0x3364, 0x04, 0, 0}, {0x3403, 0x42, 0, 0},
+       {0x3088, 0x04, 0, 0}, {0x3089, 0x00, 0, 0}, {0x308a, 0x03, 0, 0},
+       {0x308b, 0x00, 0, 0}, {0x300e, 0x32, 0, 0}, {0x300f, 0x21, 0, 0},
+       {0x3010, 0x20, 0, 0}, {0x3011, 0x01, 0, 0}, {0x304c, 0x82, 0, 0},
+       {0x3302, 0xef, 0, 0}, {0x335f, 0x34, 0, 0}, {0x3360, 0x0c, 0, 0},
+       {0x3361, 0x04, 0, 0}, {0x3362, 0x23, 0, 0}, {0x3363, 0x28, 0, 0},
+       {0x3364, 0x5c, 0, 0}, {0x3403, 0x42, 0, 0}, {0x3088, 0x02, 0, 0},
+       {0x3089, 0xD0, 0, 0}, {0x308a, 0x01, 0, 0}, {0x308b, 0xe0, 0, 0},
+       {0x304c, 0x83, 0, 0}, {0x300e, 0x39, 0, 0}, {0x300f, 0xA1, 0, 0},
+       {0x3011, 0x00, 0, 0}, {0x3010, 0x81, 0, 0}, {0x3014, 0x84, 0, 0},
+       {0x302e, 0x00, 0, 0}, {0x302d, 0x00, 0, 0}, {0x3071, 0xeb, 0, 0},
+       {0x301C, 0x02, 0, 0}, {0x3404, 0x02, 0, 0},
+};
+
+static struct reg_value ov3640_setting_30fps_PAL_720_576[] = {
+       {0x3012, 0x80, 0, 0}, {0x304d, 0x45, 0, 0}, {0x30a7, 0x5e, 0, 0},
+       {0x3086, 0x16, 0, 0}, {0x309C, 0x1a, 0, 0}, {0x30a2, 0xe4, 0, 0},
+       {0x30aa, 0x42, 0, 0}, {0x30b0, 0xff, 0, 0}, {0x30b1, 0xff, 0, 0},
+       {0x30b2, 0x10, 0, 0}, {0x300e, 0x39, 0, 0}, {0x300f, 0x21, 0, 0},
+       {0x3010, 0x20, 0, 0}, {0x304c, 0x81, 0, 0}, {0x30d7, 0x10, 0, 0},
+       {0x30d9, 0x0d, 0, 0}, {0x30db, 0x08, 0, 0}, {0x3016, 0x82, 0, 0},
+       {0x3018, 0x48, 0, 0}, {0x3019, 0x40, 0, 0}, {0x301a, 0x82, 0, 0},
+       {0x307d, 0x00, 0, 0}, {0x3087, 0x02, 0, 0}, {0x3082, 0x20, 0, 0},
+       {0x3015, 0x12, 0, 0}, {0x3014, 0x84, 0, 0}, {0x3013, 0xf7, 0, 0},
+       {0x303c, 0x08, 0, 0}, {0x303d, 0x18, 0, 0}, {0x303e, 0x06, 0, 0},
+       {0x303F, 0x0c, 0, 0}, {0x3030, 0x62, 0, 0}, {0x3031, 0x26, 0, 0},
+       {0x3032, 0xe6, 0, 0}, {0x3033, 0x6e, 0, 0}, {0x3034, 0xea, 0, 0},
+       {0x3035, 0xae, 0, 0}, {0x3036, 0xa6, 0, 0}, {0x3037, 0x6a, 0, 0},
+       {0x3104, 0x02, 0, 0}, {0x3105, 0xfd, 0, 0}, {0x3106, 0x00, 0, 0},
+       {0x3107, 0xff, 0, 0}, {0x3300, 0x13, 0, 0}, {0x3301, 0xde, 0, 0},
+       {0x3302, 0xcf, 0, 0}, {0x3312, 0x26, 0, 0}, {0x3314, 0x42, 0, 0},
+       {0x3313, 0x2b, 0, 0}, {0x3315, 0x42, 0, 0}, {0x3310, 0xd0, 0, 0},
+       {0x3311, 0xbd, 0, 0}, {0x330c, 0x18, 0, 0}, {0x330d, 0x18, 0, 0},
+       {0x330e, 0x56, 0, 0}, {0x330f, 0x5c, 0, 0}, {0x330b, 0x1c, 0, 0},
+       {0x3306, 0x5c, 0, 0}, {0x3307, 0x11, 0, 0}, {0x336a, 0x52, 0, 0},
+       {0x3370, 0x46, 0, 0}, {0x3376, 0x38, 0, 0}, {0x30b8, 0x20, 0, 0},
+       {0x30b9, 0x17, 0, 0}, {0x30ba, 0x04, 0, 0}, {0x30bb, 0x08, 0, 0},
+       {0x3507, 0x06, 0, 0}, {0x350a, 0x4f, 0, 0}, {0x3100, 0x02, 0, 0},
+       {0x3301, 0xde, 0, 0}, {0x3304, 0xfc, 0, 0}, {0x3400, 0x00, 0, 0},
+       {0x3404, 0x00, 0, 0}, {0x3600, 0xc0, 0, 0}, {0x3088, 0x08, 0, 0},
+       {0x3089, 0x00, 0, 0}, {0x308a, 0x06, 0, 0}, {0x308b, 0x00, 0, 0},
+       {0x308d, 0x04, 0, 0}, {0x3086, 0x03, 0, 0}, {0x3086, 0x00, 0, 0},
+       {0x30a9, 0xb5, 0, 0}, {0x3317, 0x04, 0, 0}, {0x3316, 0xf8, 0, 0},
+       {0x3312, 0x17, 0, 0}, {0x3314, 0x30, 0, 0}, {0x3313, 0x23, 0, 0},
+       {0x3315, 0x3e, 0, 0}, {0x3311, 0x9e, 0, 0}, {0x3310, 0xc0, 0, 0},
+       {0x330c, 0x18, 0, 0}, {0x330d, 0x18, 0, 0}, {0x330e, 0x5e, 0, 0},
+       {0x330f, 0x6c, 0, 0}, {0x330b, 0x1c, 0, 0}, {0x3306, 0x5c, 0, 0},
+       {0x3307, 0x11, 0, 0}, {0x3308, 0x25, 0, 0}, {0x3340, 0x20, 0, 0},
+       {0x3341, 0x50, 0, 0}, {0x3342, 0x18, 0, 0}, {0x3343, 0x23, 0, 0},
+       {0x3344, 0xad, 0, 0}, {0x3345, 0xd0, 0, 0}, {0x3346, 0xb8, 0, 0},
+       {0x3347, 0xb4, 0, 0}, {0x3348, 0x04, 0, 0}, {0x3349, 0x98, 0, 0},
+       {0x3355, 0x02, 0, 0}, {0x3358, 0x44, 0, 0}, {0x3359, 0x44, 0, 0},
+       {0x3300, 0x13, 0, 0}, {0x3367, 0x23, 0, 0}, {0x3368, 0xBB, 0, 0},
+       {0x3369, 0xD6, 0, 0}, {0x336A, 0x2A, 0, 0}, {0x336B, 0x07, 0, 0},
+       {0x336C, 0x00, 0, 0}, {0x336D, 0x23, 0, 0}, {0x336E, 0xC3, 0, 0},
+       {0x336F, 0xDE, 0, 0}, {0x3370, 0x2b, 0, 0}, {0x3371, 0x07, 0, 0},
+       {0x3372, 0x00, 0, 0}, {0x3373, 0x23, 0, 0}, {0x3374, 0x9e, 0, 0},
+       {0x3375, 0xD6, 0, 0}, {0x3376, 0x29, 0, 0}, {0x3377, 0x07, 0, 0},
+       {0x3378, 0x00, 0, 0}, {0x332a, 0x1d, 0, 0}, {0x331b, 0x08, 0, 0},
+       {0x331c, 0x16, 0, 0}, {0x331d, 0x2d, 0, 0}, {0x331e, 0x54, 0, 0},
+       {0x331f, 0x66, 0, 0}, {0x3320, 0x73, 0, 0}, {0x3321, 0x80, 0, 0},
+       {0x3322, 0x8c, 0, 0}, {0x3323, 0x95, 0, 0}, {0x3324, 0x9d, 0, 0},
+       {0x3325, 0xac, 0, 0}, {0x3326, 0xb8, 0, 0}, {0x3327, 0xcc, 0, 0},
+       {0x3328, 0xdd, 0, 0}, {0x3329, 0xee, 0, 0}, {0x332e, 0x04, 0, 0},
+       {0x332f, 0x04, 0, 0}, {0x3331, 0x02, 0, 0}, {0x3100, 0x02, 0, 0},
+       {0x3301, 0xde, 0, 0}, {0x3304, 0xfc, 0, 0}, {0x3400, 0x00, 0, 0},
+       {0x3404, 0x00, 0, 0}, {0x3610, 0x40, 0, 0}, {0x304c, 0x81, 0, 0},
+       {0x307C, 0x10, 0, 0}, {0x3012, 0x10, 0, 0}, {0x3023, 0x06, 0, 0},
+       {0x3026, 0x03, 0, 0}, {0x3027, 0x04, 0, 0}, {0x302a, 0x03, 0, 0},
+       {0x302b, 0x10, 0, 0}, {0x3075, 0x24, 0, 0}, {0x300d, 0x01, 0, 0},
+       {0x30d7, 0x90, 0, 0}, {0x3069, 0x04, 0, 0}, {0x303e, 0x00, 0, 0},
+       {0x303f, 0xc0, 0, 0}, {0x3302, 0xef, 0, 0}, {0x335f, 0x34, 0, 0},
+       {0x3360, 0x0c, 0, 0}, {0x3361, 0x04, 0, 0}, {0x3362, 0x34, 0, 0},
+       {0x3363, 0x08, 0, 0}, {0x3364, 0x04, 0, 0}, {0x3403, 0x42, 0, 0},
+       {0x3088, 0x04, 0, 0}, {0x3089, 0x00, 0, 0}, {0x308a, 0x03, 0, 0},
+       {0x308b, 0x00, 0, 0}, {0x300e, 0x32, 0, 0}, {0x300f, 0x21, 0, 0},
+       {0x3010, 0x20, 0, 0}, {0x3011, 0x01, 0, 0}, {0x304c, 0x82, 0, 0},
+       {0x3302, 0xef, 0, 0}, {0x335f, 0x34, 0, 0}, {0x3360, 0x0c, 0, 0},
+       {0x3361, 0x04, 0, 0}, {0x3362, 0x23, 0, 0}, {0x3363, 0x28, 0, 0},
+       {0x3364, 0x5c, 0, 0}, {0x3403, 0x42, 0, 0}, {0x3088, 0x02, 0, 0},
+       {0x3089, 0xD0, 0, 0}, {0x308a, 0x02, 0, 0}, {0x308b, 0x40, 0, 0},
+       {0x304c, 0x83, 0, 0}, {0x300e, 0x39, 0, 0}, {0x300f, 0xA1, 0, 0},
+       {0x3011, 0x00, 0, 0}, {0x3010, 0x81, 0, 0}, {0x3014, 0x84, 0, 0},
+       {0x302e, 0x00, 0, 0}, {0x302d, 0x00, 0, 0}, {0x3071, 0xeb, 0, 0},
+       {0x301C, 0x02, 0, 0}, {0x3404, 0x02, 0, 0},
+};
+
+static struct ov3640_mode_info ov3640_mode_info_data[2][ov3640_mode_MAX + 1] = {
+       {
+               {ov3640_mode_VGA_640_480,    640,  480,
+               ov3640_setting_15fps_VGA_640_480,
+               ARRAY_SIZE(ov3640_setting_15fps_VGA_640_480)},
+               {ov3640_mode_QVGA_320_240,   320,  240,
+               ov3640_setting_15fps_QVGA_320_240,
+               ARRAY_SIZE(ov3640_setting_15fps_QVGA_320_240)},
+               {ov3640_mode_XGA_1024_768,   1024, 768,
+               ov3640_setting_15fps_XGA_1024_768,
+               ARRAY_SIZE(ov3640_setting_15fps_XGA_1024_768)},
+               {ov3640_mode_QXGA_2048_1536, 2048, 1536,
+               ov3640_setting_15fps_QXGA_2048_1536,
+               ARRAY_SIZE(ov3640_setting_15fps_QXGA_2048_1536)},
+               {ov3640_mode_NTSC_720_480,   720, 480,
+               ov3640_setting_15fps_NTSC_720_480,
+               ARRAY_SIZE(ov3640_setting_15fps_NTSC_720_480)},
+               {ov3640_mode_PAL_720_576,    720, 576,
+               ov3640_setting_15fps_PAL_720_576,
+               ARRAY_SIZE(ov3640_setting_15fps_PAL_720_576)},
+       },
+       {
+               {ov3640_mode_VGA_640_480,    640,  480,
+               ov3640_setting_30fps_VGA_640_480,
+               ARRAY_SIZE(ov3640_setting_30fps_VGA_640_480)},
+               {ov3640_mode_QVGA_320_240,   320,  240,
+               ov3640_setting_30fps_QVGA_320_240,
+               ARRAY_SIZE(ov3640_setting_30fps_QVGA_320_240)},
+               {ov3640_mode_XGA_1024_768,   1024, 768,
+               ov3640_setting_30fps_XGA_1024_768,
+               ARRAY_SIZE(ov3640_setting_30fps_XGA_1024_768)},
+               {ov3640_mode_QXGA_2048_1536, 0, 0, NULL, 0},
+               {ov3640_mode_NTSC_720_480,   720, 480,
+               ov3640_setting_30fps_NTSC_720_480,
+               ARRAY_SIZE(ov3640_setting_30fps_NTSC_720_480)},
+               {ov3640_mode_PAL_720_576,    720, 576,
+               ov3640_setting_30fps_PAL_720_576,
+               ARRAY_SIZE(ov3640_setting_30fps_PAL_720_576)},
+       },
+};
+
+static struct regulator *io_regulator;
+static struct regulator *core_regulator;
+static struct regulator *analog_regulator;
+static struct regulator *gpo_regulator;
+static struct mxc_camera_platform_data *camera_plat;
+
+static int ov3640_probe(struct i2c_client *adapter,
+                               const struct i2c_device_id *device_id);
+static int ov3640_remove(struct i2c_client *client);
+
+static s32 ov3640_read_reg(u16 reg, u8 *val);
+static s32 ov3640_write_reg(u16 reg, u8 val);
+
+static const struct i2c_device_id ov3640_id[] = {
+       {"ov3640", 0},
+       {},
+};
+
+MODULE_DEVICE_TABLE(i2c, ov3640_id);
+
+static struct i2c_driver ov3640_i2c_driver = {
+       .driver = {
+                 .owner = THIS_MODULE,
+                 .name  = "ov3640",
+                 },
+       .probe  = ov3640_probe,
+       .remove = ov3640_remove,
+       .id_table = ov3640_id,
+};
+
+#if 0
+extern void gpio_sensor_active(unsigned int csi_index);
+extern void gpio_sensor_inactive(unsigned int csi);
+#else
+void gpio_sensor_active(unsigned int csi_index)
+{
+       return;
+}
+void gpio_sensor_inactive(unsigned int csi)
+{
+       return;
+}
+#endif
+
+static s32 ov3640_write_reg(u16 reg, u8 val)
+{
+       u8 au8Buf[3] = {0};
+
+       au8Buf[0] = reg >> 8;
+       au8Buf[1] = reg & 0xff;
+       au8Buf[2] = val;
+
+       if (i2c_master_send(ov3640_data.i2c_client, au8Buf, 3) < 0) {
+               pr_err("%s:write reg error:reg=%x,val=%x\n",
+                       __func__, reg, val);
+               return -1;
+       }
+
+       return 0;
+}
+
+static s32 ov3640_read_reg(u16 reg, u8 *val)
+{
+       u8 au8RegBuf[2] = {0};
+       u8 u8RdVal = 0;
+
+       au8RegBuf[0] = reg >> 8;
+       au8RegBuf[1] = reg & 0xff;
+
+       if (2 != i2c_master_send(ov3640_data.i2c_client, au8RegBuf, 2)) {
+               pr_err("%s:write reg error:reg=%x\n",
+                               __func__, reg);
+               return -1;
+       }
+
+       if (1 != i2c_master_recv(ov3640_data.i2c_client, &u8RdVal, 1)) {
+               pr_err("%s:read reg error:reg=%x,val=%x\n",
+                               __func__, reg, u8RdVal);
+               return -1;
+       }
+
+       *val = u8RdVal;
+
+       return u8RdVal;
+}
+
+static int ov3640_init_mode(enum ov3640_frame_rate frame_rate,
+                           enum ov3640_mode mode)
+{
+       struct reg_value *pModeSetting = NULL;
+       s32 i = 0;
+       s32 iModeSettingArySize = 0;
+       register u32 Delay_ms = 0;
+       register u16 RegAddr = 0;
+       register u8 Mask = 0;
+       register u8 Val = 0;
+       u8 RegVal = 0;
+       int retval = 0;
+
+       if (mode > ov3640_mode_MAX || mode < ov3640_mode_MIN) {
+               pr_err("Wrong ov3640 mode detected!\n");
+               return -1;
+       }
+
+       pModeSetting = ov3640_mode_info_data[frame_rate][mode].init_data_ptr;
+       iModeSettingArySize =
+               ov3640_mode_info_data[frame_rate][mode].init_data_size;
+
+       ov3640_data.pix.width = ov3640_mode_info_data[frame_rate][mode].width;
+       ov3640_data.pix.height = ov3640_mode_info_data[frame_rate][mode].height;
+
+       if (ov3640_data.pix.width == 0 || ov3640_data.pix.height == 0 ||
+           pModeSetting == NULL || iModeSettingArySize == 0)
+               return -EINVAL;
+
+       for (i = 0; i < iModeSettingArySize; ++i, ++pModeSetting) {
+               Delay_ms = pModeSetting->u32Delay_ms;
+               RegAddr = pModeSetting->u16RegAddr;
+               Val = pModeSetting->u8Val;
+               Mask = pModeSetting->u8Mask;
+
+               if (Mask) {
+                       retval = ov3640_read_reg(RegAddr, &RegVal);
+                       if (retval < 0)
+                               goto err;
+
+                       RegVal &= ~(u8)Mask;
+                       Val &= Mask;
+                       Val |= RegVal;
+               }
+
+               retval = ov3640_write_reg(RegAddr, Val);
+               if (retval < 0)
+                       goto err;
+
+               if (Delay_ms)
+                       msleep(Delay_ms);
+       }
+err:
+       return retval;
+}
+
+/* --------------- IOCTL functions from v4l2_int_ioctl_desc --------------- */
+
+static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p)
+{
+       if (s == NULL) {
+               pr_err("   ERROR!! no slave device set!\n");
+               return -1;
+       }
+
+       memset(p, 0, sizeof(*p));
+       p->u.bt656.clock_curr = ov3640_data.mclk;
+       pr_debug("   clock_curr=mclk=%d\n", ov3640_data.mclk);
+       p->if_type = V4L2_IF_TYPE_BT656;
+       p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT;
+       p->u.bt656.clock_min = OV3640_XCLK_MIN;
+       p->u.bt656.clock_max = OV3640_XCLK_MAX;
+       p->u.bt656.bt_sync_correct = 1;  /* Indicate external vsync */
+
+       return 0;
+}
+
+/*!
+ * ioctl_s_power - V4L2 sensor interface handler for VIDIOC_S_POWER ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @on: indicates power mode (on or off)
+ *
+ * Turns the power on or off, depending on the value of on and returns the
+ * appropriate error code.
+ */
+static int ioctl_s_power(struct v4l2_int_device *s, int on)
+{
+       struct sensor *sensor = s->priv;
+
+       if (on && !sensor->on) {
+               gpio_sensor_active(ov3640_data.csi);
+               if (io_regulator)
+                       if (regulator_enable(io_regulator) != 0)
+                               return -EIO;
+               if (core_regulator)
+                       if (regulator_enable(core_regulator) != 0)
+                               return -EIO;
+               if (gpo_regulator)
+                       if (regulator_enable(gpo_regulator) != 0)
+                               return -EIO;
+               if (analog_regulator)
+                       if (regulator_enable(analog_regulator) != 0)
+                               return -EIO;
+               /* Make sure power on */
+               if (camera_plat->pwdn)
+                       camera_plat->pwdn(0);
+
+       } else if (!on && sensor->on) {
+               if (analog_regulator)
+                       regulator_disable(analog_regulator);
+               if (core_regulator)
+                       regulator_disable(core_regulator);
+               if (io_regulator)
+                       regulator_disable(io_regulator);
+               if (gpo_regulator)
+                       regulator_disable(gpo_regulator);
+               gpio_sensor_inactive(ov3640_data.csi);
+       }
+
+       sensor->on = on;
+
+       return 0;
+}
+
+/*!
+ * ioctl_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure
+ *
+ * Returns the sensor's video CAPTURE parameters.
+ */
+static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+       struct sensor *sensor = s->priv;
+       struct v4l2_captureparm *cparm = &a->parm.capture;
+       int ret = 0;
+
+       switch (a->type) {
+       /* This is the only case currently handled. */
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               memset(a, 0, sizeof(*a));
+               a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+               cparm->capability = sensor->streamcap.capability;
+               cparm->timeperframe = sensor->streamcap.timeperframe;
+               cparm->capturemode = sensor->streamcap.capturemode;
+               ret = 0;
+               break;
+
+       /* These are all the possible cases. */
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+       case V4L2_BUF_TYPE_VBI_OUTPUT:
+       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+               ret = -EINVAL;
+               break;
+
+       default:
+               pr_debug("   type is unknown - %d\n", a->type);
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+/*!
+ * ioctl_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure
+ *
+ * Configures the sensor to use the input parameters, if possible.  If
+ * not possible, reverts to the old parameters and returns the
+ * appropriate error code.
+ */
+static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+       struct sensor *sensor = s->priv;
+       struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe;
+       u32 tgt_fps;    /* target frames per secound */
+       enum ov3640_frame_rate frame_rate;
+       int ret = 0;
+
+       /* Make sure power on */
+       if (camera_plat->pwdn)
+               camera_plat->pwdn(0);
+
+       switch (a->type) {
+       /* This is the only case currently handled. */
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               /* Check that the new frame rate is allowed. */
+               if ((timeperframe->numerator == 0) ||
+                   (timeperframe->denominator == 0)) {
+                       timeperframe->denominator = DEFAULT_FPS;
+                       timeperframe->numerator = 1;
+               }
+
+               tgt_fps = timeperframe->denominator /
+                         timeperframe->numerator;
+
+               if (tgt_fps > MAX_FPS) {
+                       timeperframe->denominator = MAX_FPS;
+                       timeperframe->numerator = 1;
+               } else if (tgt_fps < MIN_FPS) {
+                       timeperframe->denominator = MIN_FPS;
+                       timeperframe->numerator = 1;
+               }
+
+               /* Actual frame rate we use */
+               tgt_fps = timeperframe->denominator /
+                         timeperframe->numerator;
+
+               if (tgt_fps == 15)
+                       frame_rate = ov3640_15_fps;
+               else if (tgt_fps == 30)
+                       frame_rate = ov3640_30_fps;
+               else {
+                       pr_err(" The camera frame rate is not supported!\n");
+                       return -EINVAL;
+               }
+
+               sensor->streamcap.timeperframe = *timeperframe;
+               sensor->streamcap.capturemode =
+                               (u32)a->parm.capture.capturemode;
+
+               ret = ov3640_init_mode(frame_rate,
+                                      sensor->streamcap.capturemode);
+               break;
+
+       /* These are all the possible cases. */
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+       case V4L2_BUF_TYPE_VBI_OUTPUT:
+       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+               pr_debug("   type is not " \
+                       "V4L2_BUF_TYPE_VIDEO_CAPTURE but %d\n",
+                       a->type);
+               ret = -EINVAL;
+               break;
+
+       default:
+               pr_debug("   type is unknown - %d\n", a->type);
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+/*!
+ * ioctl_g_fmt_cap - V4L2 sensor interface handler for ioctl_g_fmt_cap
+ * @s: pointer to standard V4L2 device structure
+ * @f: pointer to standard V4L2 v4l2_format structure
+ *
+ * Returns the sensor's current pixel format in the v4l2_format
+ * parameter.
+ */
+static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
+{
+       struct sensor *sensor = s->priv;
+
+       f->fmt.pix = sensor->pix;
+
+       return 0;
+}
+
+/*!
+ * ioctl_g_ctrl - V4L2 sensor interface handler for VIDIOC_G_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @vc: standard V4L2 VIDIOC_G_CTRL ioctl structure
+ *
+ * If the requested control is supported, returns the control's current
+ * value from the video_control[] array.  Otherwise, returns -EINVAL
+ * if the control is not supported.
+ */
+static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc)
+{
+       int ret = 0;
+
+       switch (vc->id) {
+       case V4L2_CID_BRIGHTNESS:
+               vc->value = ov3640_data.brightness;
+               break;
+       case V4L2_CID_HUE:
+               vc->value = ov3640_data.hue;
+               break;
+       case V4L2_CID_CONTRAST:
+               vc->value = ov3640_data.contrast;
+               break;
+       case V4L2_CID_SATURATION:
+               vc->value = ov3640_data.saturation;
+               break;
+       case V4L2_CID_RED_BALANCE:
+               vc->value = ov3640_data.red;
+               break;
+       case V4L2_CID_BLUE_BALANCE:
+               vc->value = ov3640_data.blue;
+               break;
+       case V4L2_CID_EXPOSURE:
+               vc->value = ov3640_data.ae_mode;
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+/*!
+ * ioctl_s_ctrl - V4L2 sensor interface handler for VIDIOC_S_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure
+ *
+ * If the requested control is supported, sets the control's current
+ * value in HW (and updates the video_control[] array).  Otherwise,
+ * returns -EINVAL if the control is not supported.
+ */
+static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc)
+{
+       int retval = 0;
+
+       pr_debug("In ov3640:ioctl_s_ctrl %d\n",
+                vc->id);
+
+       switch (vc->id) {
+       case V4L2_CID_BRIGHTNESS:
+               break;
+       case V4L2_CID_CONTRAST:
+               break;
+       case V4L2_CID_SATURATION:
+               break;
+       case V4L2_CID_HUE:
+               break;
+       case V4L2_CID_AUTO_WHITE_BALANCE:
+               break;
+       case V4L2_CID_DO_WHITE_BALANCE:
+               break;
+       case V4L2_CID_RED_BALANCE:
+               break;
+       case V4L2_CID_BLUE_BALANCE:
+               break;
+       case V4L2_CID_GAMMA:
+               break;
+       case V4L2_CID_EXPOSURE:
+               break;
+       case V4L2_CID_AUTOGAIN:
+               break;
+       case V4L2_CID_GAIN:
+               break;
+       case V4L2_CID_HFLIP:
+               break;
+       case V4L2_CID_VFLIP:
+               break;
+       default:
+               retval = -EPERM;
+               break;
+       }
+
+       return retval;
+}
+
+/*!
+ * ioctl_enum_framesizes - V4L2 sensor interface handler for
+ *                        VIDIOC_ENUM_FRAMESIZES ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @fsize: standard V4L2 VIDIOC_ENUM_FRAMESIZES ioctl structure
+ *
+ * Return 0 if successful, otherwise -EINVAL.
+ */
+static int ioctl_enum_framesizes(struct v4l2_int_device *s,
+                                struct v4l2_frmsizeenum *fsize)
+{
+       if (fsize->index > ov3640_mode_MAX)
+               return -EINVAL;
+
+       fsize->pixel_format = ov3640_data.pix.pixelformat;
+       fsize->discrete.width =
+                       max(ov3640_mode_info_data[0][fsize->index].width,
+                           ov3640_mode_info_data[1][fsize->index].width);
+       fsize->discrete.height =
+                       max(ov3640_mode_info_data[0][fsize->index].height,
+                           ov3640_mode_info_data[1][fsize->index].height);
+       return 0;
+}
+
+/*!
+ * ioctl_g_chip_ident - V4L2 sensor interface handler for
+ *                     VIDIOC_DBG_G_CHIP_IDENT ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @id: pointer to int
+ *
+ * Return 0.
+ */
+static int ioctl_g_chip_ident(struct v4l2_int_device *s, int *id)
+{
+       ((struct v4l2_dbg_chip_ident *)id)->match.type =
+                                       V4L2_CHIP_MATCH_I2C_DRIVER;
+       strcpy(((struct v4l2_dbg_chip_ident *)id)->match.name, "ov3640_camera");
+
+       return 0;
+}
+
+/*!
+ * ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT
+ * @s: pointer to standard V4L2 device structure
+ */
+static int ioctl_init(struct v4l2_int_device *s)
+{
+
+       return 0;
+}
+
+/*!
+ * ioctl_enum_fmt_cap - V4L2 sensor interface handler for VIDIOC_ENUM_FMT
+ * @s: pointer to standard V4L2 device structure
+ * @fmt: pointer to standard V4L2 fmt description structure
+ *
+ * Return 0.
+ */
+static int ioctl_enum_fmt_cap(struct v4l2_int_device *s,
+                             struct v4l2_fmtdesc *fmt)
+{
+       if (fmt->index > ov3640_mode_MAX)
+               return -EINVAL;
+
+       fmt->pixelformat = ov3640_data.pix.pixelformat;
+
+       return 0;
+}
+
+/*!
+ * ioctl_dev_init - V4L2 sensor interface handler for vidioc_int_dev_init_num
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Initialise the device when slave attaches to the master.
+ */
+static int ioctl_dev_init(struct v4l2_int_device *s)
+{
+       struct sensor *sensor = s->priv;
+       u32 tgt_xclk;   /* target xclk */
+       u32 tgt_fps;    /* target frames per secound */
+       enum ov3640_frame_rate frame_rate;
+
+       gpio_sensor_active(ov3640_data.csi);
+       ov3640_data.on = true;
+
+       /* mclk */
+       tgt_xclk = ov3640_data.mclk;
+       tgt_xclk = min(tgt_xclk, (u32)OV3640_XCLK_MAX);
+       tgt_xclk = max(tgt_xclk, (u32)OV3640_XCLK_MIN);
+       ov3640_data.mclk = tgt_xclk;
+
+       pr_debug("   Setting mclk to %d MHz\n", tgt_xclk / 1000000);
+       set_mclk_rate(&ov3640_data.mclk, ov3640_data.csi);
+
+       /* Default camera frame rate is set in probe */
+       tgt_fps = sensor->streamcap.timeperframe.denominator /
+                 sensor->streamcap.timeperframe.numerator;
+
+       if (tgt_fps == 15)
+               frame_rate = ov3640_15_fps;
+       else if (tgt_fps == 30)
+               frame_rate = ov3640_30_fps;
+       else
+               return -EINVAL; /* Only support 15fps or 30fps now. */
+
+       return ov3640_init_mode(frame_rate,
+                               sensor->streamcap.capturemode);
+}
+
+/*!
+ * ioctl_dev_exit - V4L2 sensor interface handler for vidioc_int_dev_exit_num
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Delinitialise the device when slave detaches to the master.
+ */
+static int ioctl_dev_exit(struct v4l2_int_device *s)
+{
+       gpio_sensor_inactive(ov3640_data.csi);
+
+       return 0;
+}
+
+/*!
+ * This structure defines all the ioctls for this module and links them to the
+ * enumeration.
+ */
+static struct v4l2_int_ioctl_desc ov3640_ioctl_desc[] = {
+       {vidioc_int_dev_init_num, (v4l2_int_ioctl_func *)ioctl_dev_init},
+       {vidioc_int_dev_exit_num, ioctl_dev_exit},
+       {vidioc_int_s_power_num, (v4l2_int_ioctl_func *)ioctl_s_power},
+       {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func *)ioctl_g_ifparm},
+/*     {vidioc_int_g_needs_reset_num,
+                               (v4l2_int_ioctl_func *)ioctl_g_needs_reset}, */
+/*     {vidioc_int_reset_num, (v4l2_int_ioctl_func *)ioctl_reset}, */
+       {vidioc_int_init_num, (v4l2_int_ioctl_func *)ioctl_init},
+       {vidioc_int_enum_fmt_cap_num,
+                               (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap},
+/*     {vidioc_int_try_fmt_cap_num,
+                               (v4l2_int_ioctl_func *)ioctl_try_fmt_cap}, */
+       {vidioc_int_g_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_g_fmt_cap},
+/*     {vidioc_int_s_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_s_fmt_cap}, */
+       {vidioc_int_g_parm_num, (v4l2_int_ioctl_func *)ioctl_g_parm},
+       {vidioc_int_s_parm_num, (v4l2_int_ioctl_func *)ioctl_s_parm},
+/*     {vidioc_int_queryctrl_num, (v4l2_int_ioctl_func *)ioctl_queryctrl}, */
+       {vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func *)ioctl_g_ctrl},
+       {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *)ioctl_s_ctrl},
+       {vidioc_int_enum_framesizes_num,
+                               (v4l2_int_ioctl_func *)ioctl_enum_framesizes},
+       {vidioc_int_g_chip_ident_num,
+                               (v4l2_int_ioctl_func *)ioctl_g_chip_ident},
+};
+
+static struct v4l2_int_slave ov3640_slave = {
+       .ioctls = ov3640_ioctl_desc,
+       .num_ioctls = ARRAY_SIZE(ov3640_ioctl_desc),
+};
+
+static struct v4l2_int_device ov3640_int_device = {
+       .module = THIS_MODULE,
+       .name = "ov3640",
+       .type = v4l2_int_type_slave,
+       .u = {
+               .slave = &ov3640_slave,
+       },
+};
+
+/*!
+ * ov3640 I2C probe function
+ *
+ * @param adapter            struct i2c_adapter *
+ * @return  Error code indicating success or failure
+ */
+static int ov3640_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       int retval;
+       struct mxc_camera_platform_data *plat_data = client->dev.platform_data;
+
+       /* Set initial values for the sensor struct. */
+       memset(&ov3640_data, 0, sizeof(ov3640_data));
+       ov3640_data.mclk = 24000000; /* 6 - 54 MHz, typical 24MHz */
+       ov3640_data.mclk = plat_data->mclk;
+       ov3640_data.csi = plat_data->csi;
+
+       ov3640_data.i2c_client = client;
+       ov3640_data.pix.pixelformat = V4L2_PIX_FMT_UYVY;
+       ov3640_data.pix.width = 640;
+       ov3640_data.pix.height = 480;
+       ov3640_data.streamcap.capability = V4L2_MODE_HIGHQUALITY |
+                                          V4L2_CAP_TIMEPERFRAME;
+       ov3640_data.streamcap.capturemode = 0;
+       ov3640_data.streamcap.timeperframe.denominator = DEFAULT_FPS;
+       ov3640_data.streamcap.timeperframe.numerator = 1;
+
+       if (plat_data->io_regulator) {
+               io_regulator = regulator_get(&client->dev,
+                                            plat_data->io_regulator);
+               if (!IS_ERR(io_regulator)) {
+                       regulator_set_voltage(io_regulator,
+                                             OV3640_VOLTAGE_DIGITAL_IO,
+                                             OV3640_VOLTAGE_DIGITAL_IO);
+                       if (regulator_enable(io_regulator) != 0) {
+                               pr_err("%s:io set voltage error\n", __func__);
+                               goto err1;
+                       } else {
+                               dev_dbg(&client->dev,
+                                       "%s:io set voltage ok\n", __func__);
+                       }
+               } else
+                       io_regulator = NULL;
+       }
+
+       if (plat_data->core_regulator) {
+               core_regulator = regulator_get(&client->dev,
+                                              plat_data->core_regulator);
+               if (!IS_ERR(core_regulator)) {
+                       regulator_set_voltage(core_regulator,
+                                             OV3640_VOLTAGE_DIGITAL_CORE,
+                                             OV3640_VOLTAGE_DIGITAL_CORE);
+                       if (regulator_enable(core_regulator) != 0) {
+                               pr_err("%s:core set voltage error\n", __func__);
+                               goto err2;
+                       } else {
+                               dev_dbg(&client->dev,
+                                       "%s:core set voltage ok\n", __func__);
+                       }
+               } else
+                       core_regulator = NULL;
+       }
+
+       if (plat_data->analog_regulator) {
+               analog_regulator = regulator_get(&client->dev,
+                                                plat_data->analog_regulator);
+               if (!IS_ERR(analog_regulator)) {
+                       regulator_set_voltage(analog_regulator,
+                                             OV3640_VOLTAGE_ANALOG,
+                                             OV3640_VOLTAGE_ANALOG);
+                       if (regulator_enable(analog_regulator) != 0) {
+                               pr_err("%s:analog set voltage error\n",
+                                       __func__);
+                               goto err3;
+                       } else {
+                               dev_dbg(&client->dev,
+                                       "%s:analog set voltage ok\n", __func__);
+                       }
+               } else
+                       analog_regulator = NULL;
+       }
+
+       if (plat_data->gpo_regulator) {
+               gpo_regulator = regulator_get(&client->dev,
+                                             plat_data->gpo_regulator);
+               if (!IS_ERR(gpo_regulator)) {
+                       regulator_set_voltage(gpo_regulator,
+                                             OV3640_VOLTAGE_DIGITAL_GPO,
+                                             OV3640_VOLTAGE_DIGITAL_GPO);
+                       if (regulator_enable(gpo_regulator) != 0) {
+                               pr_err("%s:gpo enable error\n", __func__);
+                               goto err4;
+                       } else {
+                               dev_dbg(&client->dev,
+                                       "%s:gpo enable ok\n", __func__);
+                       }
+               } else
+                       gpo_regulator = NULL;
+       }
+
+       if (plat_data->pwdn)
+               plat_data->pwdn(0);
+
+       camera_plat = plat_data;
+
+       ov3640_int_device.priv = &ov3640_data;
+       retval = v4l2_int_device_register(&ov3640_int_device);
+
+       return retval;
+
+err4:
+       if (analog_regulator) {
+               regulator_disable(analog_regulator);
+               regulator_put(analog_regulator);
+       }
+err3:
+       if (core_regulator) {
+               regulator_disable(core_regulator);
+               regulator_put(core_regulator);
+       }
+err2:
+       if (io_regulator) {
+               regulator_disable(io_regulator);
+               regulator_put(io_regulator);
+       }
+err1:
+       return -1;
+}
+
+/*!
+ * ov3640 I2C detach function
+ *
+ * @param client            struct i2c_client *
+ * @return  Error code indicating success or failure
+ */
+static int ov3640_remove(struct i2c_client *client)
+{
+       v4l2_int_device_unregister(&ov3640_int_device);
+
+       if (gpo_regulator) {
+               regulator_disable(gpo_regulator);
+               regulator_put(gpo_regulator);
+       }
+
+       if (analog_regulator) {
+               regulator_disable(analog_regulator);
+               regulator_put(analog_regulator);
+       }
+
+       if (core_regulator) {
+               regulator_disable(core_regulator);
+               regulator_put(core_regulator);
+       }
+
+       if (io_regulator) {
+               regulator_disable(io_regulator);
+               regulator_put(io_regulator);
+       }
+
+       return 0;
+}
+
+/*!
+ * ov3640 init function
+ * Called by insmod ov3640_camera.ko.
+ *
+ * @return  Error code indicating success or failure
+ */
+static __init int ov3640_init(void)
+{
+       u8 err;
+
+       err = i2c_add_driver(&ov3640_i2c_driver);
+       if (err != 0)
+               pr_err("%s:driver registration failed, error=%d \n",
+                       __func__, err);
+
+       return err;
+}
+
+/*!
+ * OV3640 cleanup function
+ * Called on rmmod ov3640_camera.ko
+ *
+ * @return  Error code indicating success or failure
+ */
+static void __exit ov3640_clean(void)
+{
+       i2c_del_driver(&ov3640_i2c_driver);
+}
+
+module_init(ov3640_init);
+module_exit(ov3640_clean);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("OV3640 Camera Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("CSI");
diff --git a/drivers/media/video/mxc/capture/ov5640.c b/drivers/media/video/mxc/capture/ov5640.c
new file mode 100644 (file)
index 0000000..cd4b61b
--- /dev/null
@@ -0,0 +1,1527 @@
+/*
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
+#include <linux/fsl_devices.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-int-device.h>
+#include "mxc_v4l2_capture.h"
+
+#define OV5640_VOLTAGE_ANALOG               2800000
+#define OV5640_VOLTAGE_DIGITAL_CORE         1500000
+#define OV5640_VOLTAGE_DIGITAL_IO           1800000
+
+#define MIN_FPS 15
+#define MAX_FPS 30
+#define DEFAULT_FPS 30
+
+#define OV5640_XCLK_MIN 6000000
+#define OV5640_XCLK_MAX 24000000
+
+enum ov5640_mode {
+       ov5640_mode_MIN = 0,
+       ov5640_mode_VGA_640_480 = 0,
+       ov5640_mode_QVGA_320_240 = 1,
+       ov5640_mode_NTSC_720_480 = 2,
+       ov5640_mode_PAL_720_576 = 3,
+       ov5640_mode_720P_1280_720 = 4,
+       ov5640_mode_1080P_1920_1080 = 5,
+       ov5640_mode_QSXGA_2592_1944 = 6,
+       ov5640_mode_MAX = 6
+};
+
+enum ov5640_frame_rate {
+       ov5640_15_fps,
+       ov5640_30_fps
+};
+
+struct reg_value {
+       u16 u16RegAddr;
+       u8 u8Val;
+       u8 u8Mask;
+       u32 u32Delay_ms;
+};
+
+struct ov5640_mode_info {
+       enum ov5640_mode mode;
+       u32 width;
+       u32 height;
+       struct reg_value *init_data_ptr;
+       u32 init_data_size;
+};
+
+/*!
+ * Maintains the information on the current state of the sesor.
+ */
+struct sensor {
+       const struct ov5640_platform_data *platform_data;
+       struct v4l2_int_device *v4l2_int_device;
+       struct i2c_client *i2c_client;
+       struct v4l2_pix_format pix;
+       struct v4l2_captureparm streamcap;
+       bool on;
+
+       /* control settings */
+       int brightness;
+       int hue;
+       int contrast;
+       int saturation;
+       int red;
+       int green;
+       int blue;
+       int ae_mode;
+
+       u32 mclk;
+       int csi;
+} ov5640_data;
+
+static struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = {
+       {0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3008, 0x42, 0, 0},
+       {0x3103, 0x03, 0, 0}, {0x3017, 0xff, 0, 0}, {0x3018, 0xff, 0, 0},
+       {0x3034, 0x1a, 0, 0}, {0x3035, 0x21, 0, 0}, {0x3036, 0x69, 0, 0},
+       {0x3037, 0x13, 0, 0}, {0x3108, 0x01, 0, 0}, {0x3630, 0x2e, 0, 0},
+       {0x3632, 0xe2, 0, 0}, {0x3633, 0x23, 0, 0}, {0x3621, 0xe0, 0, 0},
+       {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0}, {0x3715, 0x78, 0, 0},
+       {0x3717, 0x01, 0, 0}, {0x370b, 0x60, 0, 0}, {0x3705, 0x1a, 0, 0},
+       {0x3905, 0x02, 0, 0}, {0x3906, 0x10, 0, 0}, {0x3901, 0x0a, 0, 0},
+       {0x3731, 0x12, 0, 0}, {0x3600, 0x08, 0, 0}, {0x3601, 0x33, 0, 0},
+       {0x302d, 0x60, 0, 0}, {0x3620, 0x52, 0, 0}, {0x371b, 0x20, 0, 0},
+       {0x471c, 0x50, 0, 0}, {0x3a18, 0x00, 0, 0}, {0x3a19, 0xf8, 0, 0},
+       {0x3635, 0x1c, 0, 0}, {0x3634, 0x40, 0, 0}, {0x3622, 0x01, 0, 0},
+       {0x3c01, 0x34, 0, 0}, {0x3c04, 0x28, 0, 0}, {0x3c05, 0x98, 0, 0},
+       {0x3c06, 0x00, 0, 0}, {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0},
+       {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+       {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, {0x3814, 0x11, 0, 0},
+       {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+       {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
+       {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
+       {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0},
+       {0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0},
+       {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0},
+       {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
+       {0x3618, 0x04, 0, 0}, {0x3612, 0x2b, 0, 0}, {0x3708, 0x21, 0, 0},
+       {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x07, 0, 0},
+       {0x3a03, 0xb0, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+       {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x06, 0, 0},
+       {0x3a0d, 0x08, 0, 0}, {0x3a14, 0x07, 0, 0}, {0x3a15, 0xb0, 0, 0},
+       {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x3000, 0x00, 0, 0},
+       {0x3002, 0x1c, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3006, 0xc3, 0, 0},
+       {0x300e, 0x58, 0, 0}, {0x302e, 0x00, 0, 0}, {0x4300, 0x30, 0, 0},
+       {0x501f, 0x00, 0, 0}, {0x5000, 0xa7, 0, 0}, {0x5001, 0x83, 0, 0},
+       {0x5180, 0xff, 0, 0}, {0x5181, 0xf2, 0, 0}, {0x5182, 0x00, 0, 0},
+       {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0},
+       {0x5186, 0x09, 0, 0}, {0x5187, 0x09, 0, 0}, {0x5188, 0x09, 0, 0},
+       {0x5189, 0x75, 0, 0}, {0x518a, 0x54, 0, 0}, {0x518b, 0xe0, 0, 0},
+       {0x518c, 0xb2, 0, 0}, {0x518d, 0x42, 0, 0}, {0x518e, 0x3d, 0, 0},
+       {0x518f, 0x56, 0, 0}, {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0},
+       {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0},
+       {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0},
+       {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, {0x519a, 0x04, 0, 0},
+       {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, {0x519d, 0x82, 0, 0},
+       {0x519e, 0x38, 0, 0}, {0x5381, 0x1c, 0, 0}, {0x5382, 0x5a, 0, 0},
+       {0x5383, 0x06, 0, 0}, {0x5384, 0x0a, 0, 0}, {0x5385, 0x7e, 0, 0},
+       {0x5386, 0x88, 0, 0}, {0x5387, 0x7c, 0, 0}, {0x5388, 0x6c, 0, 0},
+       {0x5389, 0x10, 0, 0}, {0x538a, 0x01, 0, 0}, {0x538b, 0x98, 0, 0},
+       {0x5300, 0x08, 0, 0}, {0x5301, 0x30, 0, 0}, {0x5302, 0x10, 0, 0},
+       {0x5303, 0x00, 0, 0}, {0x5304, 0x08, 0, 0}, {0x5305, 0x30, 0, 0},
+       {0x5306, 0x08, 0, 0}, {0x5307, 0x16, 0, 0}, {0x5309, 0x08, 0, 0},
+       {0x530a, 0x30, 0, 0}, {0x530b, 0x04, 0, 0}, {0x530c, 0x06, 0, 0},
+       {0x5480, 0x01, 0, 0}, {0x5481, 0x08, 0, 0}, {0x5482, 0x14, 0, 0},
+       {0x5483, 0x28, 0, 0}, {0x5484, 0x51, 0, 0}, {0x5485, 0x65, 0, 0},
+       {0x5486, 0x71, 0, 0}, {0x5487, 0x7d, 0, 0}, {0x5488, 0x87, 0, 0},
+       {0x5489, 0x91, 0, 0}, {0x548a, 0x9a, 0, 0}, {0x548b, 0xaa, 0, 0},
+       {0x548c, 0xb8, 0, 0}, {0x548d, 0xcd, 0, 0}, {0x548e, 0xdd, 0, 0},
+       {0x548f, 0xea, 0, 0}, {0x5490, 0x1d, 0, 0}, {0x5580, 0x02, 0, 0},
+       {0x5583, 0x40, 0, 0}, {0x5584, 0x10, 0, 0}, {0x5589, 0x10, 0, 0},
+       {0x558a, 0x00, 0, 0}, {0x558b, 0xf8, 0, 0}, {0x5800, 0x23, 0, 0},
+       {0x5801, 0x15, 0, 0}, {0x5802, 0x10, 0, 0}, {0x5803, 0x10, 0, 0},
+       {0x5804, 0x15, 0, 0}, {0x5805, 0x23, 0, 0}, {0x5806, 0x0c, 0, 0},
+       {0x5807, 0x08, 0, 0}, {0x5808, 0x05, 0, 0}, {0x5809, 0x05, 0, 0},
+       {0x580a, 0x08, 0, 0}, {0x580b, 0x0c, 0, 0}, {0x580c, 0x07, 0, 0},
+       {0x580d, 0x03, 0, 0}, {0x580e, 0x00, 0, 0}, {0x580f, 0x00, 0, 0},
+       {0x5810, 0x03, 0, 0}, {0x5811, 0x07, 0, 0}, {0x5812, 0x07, 0, 0},
+       {0x5813, 0x03, 0, 0}, {0x5814, 0x00, 0, 0}, {0x5815, 0x00, 0, 0},
+       {0x5816, 0x03, 0, 0}, {0x5817, 0x07, 0, 0}, {0x5818, 0x0b, 0, 0},
+       {0x5819, 0x08, 0, 0}, {0x581a, 0x05, 0, 0}, {0x581b, 0x05, 0, 0},
+       {0x581c, 0x07, 0, 0}, {0x581d, 0x0b, 0, 0}, {0x581e, 0x2a, 0, 0},
+       {0x581f, 0x16, 0, 0}, {0x5820, 0x11, 0, 0}, {0x5821, 0x11, 0, 0},
+       {0x5822, 0x15, 0, 0}, {0x5823, 0x29, 0, 0}, {0x5824, 0xbf, 0, 0},
+       {0x5825, 0xaf, 0, 0}, {0x5826, 0x9f, 0, 0}, {0x5827, 0xaf, 0, 0},
+       {0x5828, 0xdf, 0, 0}, {0x5829, 0x6f, 0, 0}, {0x582a, 0x8e, 0, 0},
+       {0x582b, 0xab, 0, 0}, {0x582c, 0x9e, 0, 0}, {0x582d, 0x7f, 0, 0},
+       {0x582e, 0x4f, 0, 0}, {0x582f, 0x89, 0, 0}, {0x5830, 0x86, 0, 0},
+       {0x5831, 0x98, 0, 0}, {0x5832, 0x6f, 0, 0}, {0x5833, 0x4f, 0, 0},
+       {0x5834, 0x6e, 0, 0}, {0x5835, 0x7b, 0, 0}, {0x5836, 0x7e, 0, 0},
+       {0x5837, 0x6f, 0, 0}, {0x5838, 0xde, 0, 0}, {0x5839, 0xbf, 0, 0},
+       {0x583a, 0x9f, 0, 0}, {0x583b, 0xbf, 0, 0}, {0x583c, 0xec, 0, 0},
+       {0x5025, 0x00, 0, 0}, {0x3a0f, 0x30, 0, 0}, {0x3a10, 0x28, 0, 0},
+       {0x3a1b, 0x30, 0, 0}, {0x3a1e, 0x26, 0, 0}, {0x3a11, 0x60, 0, 0},
+       {0x3a1f, 0x14, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3035, 0x11, 0, 0},
+       {0x3036, 0x46, 0, 0},
+};
+
+static struct reg_value ov5640_setting_30fps_VGA_640_480[] = {
+       {0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3008, 0x42, 0, 0},
+       {0x3103, 0x03, 0, 0}, {0x3017, 0xff, 0, 0}, {0x3018, 0xff, 0, 0},
+       {0x3034, 0x1a, 0, 0}, {0x3035, 0x11, 0, 0}, {0x3036, 0x46, 0, 0},
+       {0x3037, 0x13, 0, 0}, {0x3108, 0x01, 0, 0}, {0x3630, 0x2e, 0, 0},
+       {0x3632, 0xe2, 0, 0}, {0x3633, 0x23, 0, 0}, {0x3621, 0xe0, 0, 0},
+       {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0}, {0x3715, 0x78, 0, 0},
+       {0x3717, 0x01, 0, 0}, {0x370b, 0x60, 0, 0}, {0x3705, 0x1a, 0, 0},
+       {0x3905, 0x02, 0, 0}, {0x3906, 0x10, 0, 0}, {0x3901, 0x0a, 0, 0},
+       {0x3731, 0x12, 0, 0}, {0x3600, 0x08, 0, 0}, {0x3601, 0x33, 0, 0},
+       {0x302d, 0x60, 0, 0}, {0x3620, 0x52, 0, 0}, {0x371b, 0x20, 0, 0},
+       {0x471c, 0x50, 0, 0}, {0x3a18, 0x00, 0, 0}, {0x3a19, 0xf8, 0, 0},
+       {0x3635, 0x1c, 0, 0}, {0x3634, 0x40, 0, 0}, {0x3622, 0x01, 0, 0},
+       {0x3c01, 0x34, 0, 0}, {0x3c04, 0x28, 0, 0}, {0x3c05, 0x98, 0, 0},
+       {0x3c06, 0x00, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c08, 0x00, 0, 0},
+       {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+       {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+       {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+       {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+       {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+       {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
+       {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+       {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+       {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+       {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x62, 0, 0},
+       {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+       {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+       {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+       {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+       {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x3000, 0x00, 0, 0},
+       {0x3002, 0x1c, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3006, 0xc3, 0, 0},
+       {0x300e, 0x58, 0, 0}, {0x302e, 0x00, 0, 0}, {0x4300, 0x30, 0, 0},
+       {0x501f, 0x00, 0, 0}, {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0},
+       {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3824, 0x02, 0, 0},
+       {0x5000, 0xa7, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x5180, 0xff, 0, 0},
+       {0x5181, 0xf2, 0, 0}, {0x5182, 0x00, 0, 0}, {0x5183, 0x14, 0, 0},
+       {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0}, {0x5186, 0x09, 0, 0},
+       {0x5187, 0x09, 0, 0}, {0x5188, 0x09, 0, 0}, {0x5189, 0x75, 0, 0},
+       {0x518a, 0x54, 0, 0}, {0x518b, 0xe0, 0, 0}, {0x518c, 0xb2, 0, 0},
+       {0x518d, 0x42, 0, 0}, {0x518e, 0x3d, 0, 0}, {0x518f, 0x56, 0, 0},
+       {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0},
+       {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0},
+       {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0},
+       {0x5199, 0x12, 0, 0}, {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0},
+       {0x519c, 0x06, 0, 0}, {0x519d, 0x82, 0, 0}, {0x519e, 0x38, 0, 0},
+       {0x5381, 0x1c, 0, 0}, {0x5382, 0x5a, 0, 0}, {0x5383, 0x06, 0, 0},
+       {0x5384, 0x0a, 0, 0}, {0x5385, 0x7e, 0, 0}, {0x5386, 0x88, 0, 0},
+       {0x5387, 0x7c, 0, 0}, {0x5388, 0x6c, 0, 0}, {0x5389, 0x10, 0, 0},
+       {0x538a, 0x01, 0, 0}, {0x538b, 0x98, 0, 0}, {0x5300, 0x08, 0, 0},
+       {0x5301, 0x30, 0, 0}, {0x5302, 0x10, 0, 0}, {0x5303, 0x00, 0, 0},
+       {0x5304, 0x08, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x08, 0, 0},
+       {0x5307, 0x16, 0, 0}, {0x5309, 0x08, 0, 0}, {0x530a, 0x30, 0, 0},
+       {0x530b, 0x04, 0, 0}, {0x530c, 0x06, 0, 0}, {0x5480, 0x01, 0, 0},
+       {0x5481, 0x08, 0, 0}, {0x5482, 0x14, 0, 0}, {0x5483, 0x28, 0, 0},
+       {0x5484, 0x51, 0, 0}, {0x5485, 0x65, 0, 0}, {0x5486, 0x71, 0, 0},
+       {0x5487, 0x7d, 0, 0}, {0x5488, 0x87, 0, 0}, {0x5489, 0x91, 0, 0},
+       {0x548a, 0x9a, 0, 0}, {0x548b, 0xaa, 0, 0}, {0x548c, 0xb8, 0, 0},
+       {0x548d, 0xcd, 0, 0}, {0x548e, 0xdd, 0, 0}, {0x548f, 0xea, 0, 0},
+       {0x5490, 0x1d, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5583, 0x40, 0, 0},
+       {0x5584, 0x10, 0, 0}, {0x5589, 0x10, 0, 0}, {0x558a, 0x00, 0, 0},
+       {0x558b, 0xf8, 0, 0}, {0x5800, 0x23, 0, 0}, {0x5801, 0x15, 0, 0},
+       {0x5802, 0x10, 0, 0}, {0x5803, 0x10, 0, 0}, {0x5804, 0x15, 0, 0},
+       {0x5805, 0x23, 0, 0}, {0x5806, 0x0c, 0, 0}, {0x5807, 0x08, 0, 0},
+       {0x5808, 0x05, 0, 0}, {0x5809, 0x05, 0, 0}, {0x580a, 0x08, 0, 0},
+       {0x580b, 0x0c, 0, 0}, {0x580c, 0x07, 0, 0}, {0x580d, 0x03, 0, 0},
+       {0x580e, 0x00, 0, 0}, {0x580f, 0x00, 0, 0}, {0x5810, 0x03, 0, 0},
+       {0x5811, 0x07, 0, 0}, {0x5812, 0x07, 0, 0}, {0x5813, 0x03, 0, 0},
+       {0x5814, 0x00, 0, 0}, {0x5815, 0x00, 0, 0}, {0x5816, 0x03, 0, 0},
+       {0x5817, 0x07, 0, 0}, {0x5818, 0x0b, 0, 0}, {0x5819, 0x08, 0, 0},
+       {0x581a, 0x05, 0, 0}, {0x581b, 0x05, 0, 0}, {0x581c, 0x07, 0, 0},
+       {0x581d, 0x0b, 0, 0}, {0x581e, 0x2a, 0, 0}, {0x581f, 0x16, 0, 0},
+       {0x5820, 0x11, 0, 0}, {0x5821, 0x11, 0, 0}, {0x5822, 0x15, 0, 0},
+       {0x5823, 0x29, 0, 0}, {0x5824, 0xbf, 0, 0}, {0x5825, 0xaf, 0, 0},
+       {0x5826, 0x9f, 0, 0}, {0x5827, 0xaf, 0, 0}, {0x5828, 0xdf, 0, 0},
+       {0x5829, 0x6f, 0, 0}, {0x582a, 0x8e, 0, 0}, {0x582b, 0xab, 0, 0},
+       {0x582c, 0x9e, 0, 0}, {0x582d, 0x7f, 0, 0}, {0x582e, 0x4f, 0, 0},
+       {0x582f, 0x89, 0, 0}, {0x5830, 0x86, 0, 0}, {0x5831, 0x98, 0, 0},
+       {0x5832, 0x6f, 0, 0}, {0x5833, 0x4f, 0, 0}, {0x5834, 0x6e, 0, 0},
+       {0x5835, 0x7b, 0, 0}, {0x5836, 0x7e, 0, 0}, {0x5837, 0x6f, 0, 0},
+       {0x5838, 0xde, 0, 0}, {0x5839, 0xbf, 0, 0}, {0x583a, 0x9f, 0, 0},
+       {0x583b, 0xbf, 0, 0}, {0x583c, 0xec, 0, 0}, {0x5025, 0x00, 0, 0},
+       {0x3a0f, 0x30, 0, 0}, {0x3a10, 0x28, 0, 0}, {0x3a1b, 0x30, 0, 0},
+       {0x3a1e, 0x26, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x14, 0, 0},
+       {0x3008, 0x02, 0, 0}, {0x3035, 0x11, 0, 0},
+};
+
+static struct reg_value ov5640_setting_30fps_QVGA_320_240[] = {
+       {0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3008, 0x42, 0, 0},
+       {0x3103, 0x03, 0, 0}, {0x3017, 0xff, 0, 0}, {0x3018, 0xff, 0, 0},
+       {0x3034, 0x1a, 0, 0}, {0x3035, 0x11, 0, 0}, {0x3036, 0x46, 0, 0},
+       {0x3037, 0x13, 0, 0}, {0x3108, 0x01, 0, 0}, {0x3630, 0x2e, 0, 0},
+       {0x3632, 0xe2, 0, 0}, {0x3633, 0x23, 0, 0}, {0x3621, 0xe0, 0, 0},
+       {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0}, {0x3715, 0x78, 0, 0},
+       {0x3717, 0x01, 0, 0}, {0x370b, 0x60, 0, 0}, {0x3705, 0x1a, 0, 0},
+       {0x3905, 0x02, 0, 0}, {0x3906, 0x10, 0, 0}, {0x3901, 0x0a, 0, 0},
+       {0x3731, 0x12, 0, 0}, {0x3600, 0x08, 0, 0}, {0x3601, 0x33, 0, 0},
+       {0x302d, 0x60, 0, 0}, {0x3620, 0x52, 0, 0}, {0x371b, 0x20, 0, 0},
+       {0x471c, 0x50, 0, 0}, {0x3a18, 0x00, 0, 0}, {0x3a19, 0xf8, 0, 0},
+       {0x3635, 0x1c, 0, 0}, {0x3634, 0x40, 0, 0}, {0x3622, 0x01, 0, 0},
+       {0x3c01, 0x34, 0, 0}, {0x3c04, 0x28, 0, 0}, {0x3c05, 0x98, 0, 0},
+       {0x3c06, 0x00, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c08, 0x00, 0, 0},
+       {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+       {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+       {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+       {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+       {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+       {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
+       {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+       {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+       {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+       {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x62, 0, 0},
+       {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+       {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+       {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+       {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+       {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x3000, 0x00, 0, 0},
+       {0x3002, 0x1c, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3006, 0xc3, 0, 0},
+       {0x300e, 0x58, 0, 0}, {0x302e, 0x00, 0, 0}, {0x4300, 0x30, 0, 0},
+       {0x501f, 0x00, 0, 0}, {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0},
+       {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3824, 0x02, 0, 0},
+       {0x5000, 0xa7, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x5180, 0xff, 0, 0},
+       {0x5181, 0xf2, 0, 0}, {0x5182, 0x00, 0, 0}, {0x5183, 0x14, 0, 0},
+       {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0}, {0x5186, 0x09, 0, 0},
+       {0x5187, 0x09, 0, 0}, {0x5188, 0x09, 0, 0}, {0x5189, 0x75, 0, 0},
+       {0x518a, 0x54, 0, 0}, {0x518b, 0xe0, 0, 0}, {0x518c, 0xb2, 0, 0},
+       {0x518d, 0x42, 0, 0}, {0x518e, 0x3d, 0, 0}, {0x518f, 0x56, 0, 0},
+       {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0},
+       {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0},
+       {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0},
+       {0x5199, 0x12, 0, 0}, {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0},
+       {0x519c, 0x06, 0, 0}, {0x519d, 0x82, 0, 0}, {0x519e, 0x38, 0, 0},
+       {0x5381, 0x1c, 0, 0}, {0x5382, 0x5a, 0, 0}, {0x5383, 0x06, 0, 0},
+       {0x5384, 0x0a, 0, 0}, {0x5385, 0x7e, 0, 0}, {0x5386, 0x88, 0, 0},
+       {0x5387, 0x7c, 0, 0}, {0x5388, 0x6c, 0, 0}, {0x5389, 0x10, 0, 0},
+       {0x538a, 0x01, 0, 0}, {0x538b, 0x98, 0, 0}, {0x5300, 0x08, 0, 0},
+       {0x5301, 0x30, 0, 0}, {0x5302, 0x10, 0, 0}, {0x5303, 0x00, 0, 0},
+       {0x5304, 0x08, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x08, 0, 0},
+       {0x5307, 0x16, 0, 0}, {0x5309, 0x08, 0, 0}, {0x530a, 0x30, 0, 0},
+       {0x530b, 0x04, 0, 0}, {0x530c, 0x06, 0, 0}, {0x5480, 0x01, 0, 0},
+       {0x5481, 0x08, 0, 0}, {0x5482, 0x14, 0, 0}, {0x5483, 0x28, 0, 0},
+       {0x5484, 0x51, 0, 0}, {0x5485, 0x65, 0, 0}, {0x5486, 0x71, 0, 0},
+       {0x5487, 0x7d, 0, 0}, {0x5488, 0x87, 0, 0}, {0x5489, 0x91, 0, 0},
+       {0x548a, 0x9a, 0, 0}, {0x548b, 0xaa, 0, 0}, {0x548c, 0xb8, 0, 0},
+       {0x548d, 0xcd, 0, 0}, {0x548e, 0xdd, 0, 0}, {0x548f, 0xea, 0, 0},
+       {0x5490, 0x1d, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5583, 0x40, 0, 0},
+       {0x5584, 0x10, 0, 0}, {0x5589, 0x10, 0, 0}, {0x558a, 0x00, 0, 0},
+       {0x558b, 0xf8, 0, 0}, {0x5800, 0x23, 0, 0}, {0x5801, 0x15, 0, 0},
+       {0x5802, 0x10, 0, 0}, {0x5803, 0x10, 0, 0}, {0x5804, 0x15, 0, 0},
+       {0x5805, 0x23, 0, 0}, {0x5806, 0x0c, 0, 0}, {0x5807, 0x08, 0, 0},
+       {0x5808, 0x05, 0, 0}, {0x5809, 0x05, 0, 0}, {0x580a, 0x08, 0, 0},
+       {0x580b, 0x0c, 0, 0}, {0x580c, 0x07, 0, 0}, {0x580d, 0x03, 0, 0},
+       {0x580e, 0x00, 0, 0}, {0x580f, 0x00, 0, 0}, {0x5810, 0x03, 0, 0},
+       {0x5811, 0x07, 0, 0}, {0x5812, 0x07, 0, 0}, {0x5813, 0x03, 0, 0},
+       {0x5814, 0x00, 0, 0}, {0x5815, 0x00, 0, 0}, {0x5816, 0x03, 0, 0},
+       {0x5817, 0x07, 0, 0}, {0x5818, 0x0b, 0, 0}, {0x5819, 0x08, 0, 0},
+       {0x581a, 0x05, 0, 0}, {0x581b, 0x05, 0, 0}, {0x581c, 0x07, 0, 0},
+       {0x581d, 0x0b, 0, 0}, {0x581e, 0x2a, 0, 0}, {0x581f, 0x16, 0, 0},
+       {0x5820, 0x11, 0, 0}, {0x5821, 0x11, 0, 0}, {0x5822, 0x15, 0, 0},
+       {0x5823, 0x29, 0, 0}, {0x5824, 0xbf, 0, 0}, {0x5825, 0xaf, 0, 0},
+       {0x5826, 0x9f, 0, 0}, {0x5827, 0xaf, 0, 0}, {0x5828, 0xdf, 0, 0},
+       {0x5829, 0x6f, 0, 0}, {0x582a, 0x8e, 0, 0}, {0x582b, 0xab, 0, 0},
+       {0x582c, 0x9e, 0, 0}, {0x582d, 0x7f, 0, 0}, {0x582e, 0x4f, 0, 0},
+       {0x582f, 0x89, 0, 0}, {0x5830, 0x86, 0, 0}, {0x5831, 0x98, 0, 0},
+       {0x5832, 0x6f, 0, 0}, {0x5833, 0x4f, 0, 0}, {0x5834, 0x6e, 0, 0},
+       {0x5835, 0x7b, 0, 0}, {0x5836, 0x7e, 0, 0}, {0x5837, 0x6f, 0, 0},
+       {0x5838, 0xde, 0, 0}, {0x5839, 0xbf, 0, 0}, {0x583a, 0x9f, 0, 0},
+       {0x583b, 0xbf, 0, 0}, {0x583c, 0xec, 0, 0}, {0x5025, 0x00, 0, 0},
+       {0x3a0f, 0x30, 0, 0}, {0x3a10, 0x28, 0, 0}, {0x3a1b, 0x30, 0, 0},
+       {0x3a1e, 0x26, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x14, 0, 0},
+       {0x3008, 0x02, 0, 0}, {0x3035, 0x11, 0, 0}, {0x3808, 0x01, 0, 0},
+       {0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0}, {0x380b, 0xf0, 0, 0},
+};
+
+static struct reg_value ov5640_setting_30fps_NTSC_720_480[] = {
+       {0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3008, 0x42, 0, 0},
+       {0x3103, 0x03, 0, 0}, {0x3017, 0xff, 0, 0}, {0x3018, 0xff, 0, 0},
+       {0x3034, 0x1a, 0, 0}, {0x3035, 0x11, 0, 0}, {0x3036, 0x46, 0, 0},
+       {0x3037, 0x13, 0, 0}, {0x3108, 0x01, 0, 0}, {0x3630, 0x2e, 0, 0},
+       {0x3632, 0xe2, 0, 0}, {0x3633, 0x23, 0, 0}, {0x3621, 0xe0, 0, 0},
+       {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0}, {0x3715, 0x78, 0, 0},
+       {0x3717, 0x01, 0, 0}, {0x370b, 0x60, 0, 0}, {0x3705, 0x1a, 0, 0},
+       {0x3905, 0x02, 0, 0}, {0x3906, 0x10, 0, 0}, {0x3901, 0x0a, 0, 0},
+       {0x3731, 0x12, 0, 0}, {0x3600, 0x08, 0, 0}, {0x3601, 0x33, 0, 0},
+       {0x302d, 0x60, 0, 0}, {0x3620, 0x52, 0, 0}, {0x371b, 0x20, 0, 0},
+       {0x471c, 0x50, 0, 0}, {0x3a18, 0x00, 0, 0}, {0x3a19, 0xf8, 0, 0},
+       {0x3635, 0x1c, 0, 0}, {0x3634, 0x40, 0, 0}, {0x3622, 0x01, 0, 0},
+       {0x3c01, 0x34, 0, 0}, {0x3c04, 0x28, 0, 0}, {0x3c05, 0x98, 0, 0},
+       {0x3c06, 0x00, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c08, 0x00, 0, 0},
+       {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+       {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+       {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+       {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+       {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+       {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x01, 0, 0},
+       {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+       {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+       {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x60, 0, 0},
+       {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x62, 0, 0},
+       {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+       {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+       {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+       {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+       {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x3000, 0x00, 0, 0},
+       {0x3002, 0x1c, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3006, 0xc3, 0, 0},
+       {0x300e, 0x58, 0, 0}, {0x302e, 0x00, 0, 0}, {0x4300, 0x30, 0, 0},
+       {0x501f, 0x00, 0, 0}, {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0},
+       {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3824, 0x02, 0, 0},
+       {0x5000, 0xa7, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x5180, 0xff, 0, 0},
+       {0x5181, 0xf2, 0, 0}, {0x5182, 0x00, 0, 0}, {0x5183, 0x14, 0, 0},
+       {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0}, {0x5186, 0x09, 0, 0},
+       {0x5187, 0x09, 0, 0}, {0x5188, 0x09, 0, 0}, {0x5189, 0x75, 0, 0},
+       {0x518a, 0x54, 0, 0}, {0x518b, 0xe0, 0, 0}, {0x518c, 0xb2, 0, 0},
+       {0x518d, 0x42, 0, 0}, {0x518e, 0x3d, 0, 0}, {0x518f, 0x56, 0, 0},
+       {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0},
+       {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0},
+       {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0},
+       {0x5199, 0x12, 0, 0}, {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0},
+       {0x519c, 0x06, 0, 0}, {0x519d, 0x82, 0, 0}, {0x519e, 0x38, 0, 0},
+       {0x5381, 0x1c, 0, 0}, {0x5382, 0x5a, 0, 0}, {0x5383, 0x06, 0, 0},
+       {0x5384, 0x0a, 0, 0}, {0x5385, 0x7e, 0, 0}, {0x5386, 0x88, 0, 0},
+       {0x5387, 0x7c, 0, 0}, {0x5388, 0x6c, 0, 0}, {0x5389, 0x10, 0, 0},
+       {0x538a, 0x01, 0, 0}, {0x538b, 0x98, 0, 0}, {0x5300, 0x08, 0, 0},
+       {0x5301, 0x30, 0, 0}, {0x5302, 0x10, 0, 0}, {0x5303, 0x00, 0, 0},
+       {0x5304, 0x08, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x08, 0, 0},
+       {0x5307, 0x16, 0, 0}, {0x5309, 0x08, 0, 0}, {0x530a, 0x30, 0, 0},
+       {0x530b, 0x04, 0, 0}, {0x530c, 0x06, 0, 0}, {0x5480, 0x01, 0, 0},
+       {0x5481, 0x08, 0, 0}, {0x5482, 0x14, 0, 0}, {0x5483, 0x28, 0, 0},
+       {0x5484, 0x51, 0, 0}, {0x5485, 0x65, 0, 0}, {0x5486, 0x71, 0, 0},
+       {0x5487, 0x7d, 0, 0}, {0x5488, 0x87, 0, 0}, {0x5489, 0x91, 0, 0},
+       {0x548a, 0x9a, 0, 0}, {0x548b, 0xaa, 0, 0}, {0x548c, 0xb8, 0, 0},
+       {0x548d, 0xcd, 0, 0}, {0x548e, 0xdd, 0, 0}, {0x548f, 0xea, 0, 0},
+       {0x5490, 0x1d, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5583, 0x40, 0, 0},
+       {0x5584, 0x10, 0, 0}, {0x5589, 0x10, 0, 0}, {0x558a, 0x00, 0, 0},
+       {0x558b, 0xf8, 0, 0}, {0x5800, 0x23, 0, 0}, {0x5801, 0x15, 0, 0},
+       {0x5802, 0x10, 0, 0}, {0x5803, 0x10, 0, 0}, {0x5804, 0x15, 0, 0},
+       {0x5805, 0x23, 0, 0}, {0x5806, 0x0c, 0, 0}, {0x5807, 0x08, 0, 0},
+       {0x5808, 0x05, 0, 0}, {0x5809, 0x05, 0, 0}, {0x580a, 0x08, 0, 0},
+       {0x580b, 0x0c, 0, 0}, {0x580c, 0x07, 0, 0}, {0x580d, 0x03, 0, 0},
+       {0x580e, 0x00, 0, 0}, {0x580f, 0x00, 0, 0}, {0x5810, 0x03, 0, 0},
+       {0x5811, 0x07, 0, 0}, {0x5812, 0x07, 0, 0}, {0x5813, 0x03, 0, 0},
+       {0x5814, 0x00, 0, 0}, {0x5815, 0x00, 0, 0}, {0x5816, 0x03, 0, 0},
+       {0x5817, 0x07, 0, 0}, {0x5818, 0x0b, 0, 0}, {0x5819, 0x08, 0, 0},
+       {0x581a, 0x05, 0, 0}, {0x581b, 0x05, 0, 0}, {0x581c, 0x07, 0, 0},
+       {0x581d, 0x0b, 0, 0}, {0x581e, 0x2a, 0, 0}, {0x581f, 0x16, 0, 0},
+       {0x5820, 0x11, 0, 0}, {0x5821, 0x11, 0, 0}, {0x5822, 0x15, 0, 0},
+       {0x5823, 0x29, 0, 0}, {0x5824, 0xbf, 0, 0}, {0x5825, 0xaf, 0, 0},
+       {0x5826, 0x9f, 0, 0}, {0x5827, 0xaf, 0, 0}, {0x5828, 0xdf, 0, 0},
+       {0x5829, 0x6f, 0, 0}, {0x582a, 0x8e, 0, 0}, {0x582b, 0xab, 0, 0},
+       {0x582c, 0x9e, 0, 0}, {0x582d, 0x7f, 0, 0}, {0x582e, 0x4f, 0, 0},
+       {0x582f, 0x89, 0, 0}, {0x5830, 0x86, 0, 0}, {0x5831, 0x98, 0, 0},
+       {0x5832, 0x6f, 0, 0}, {0x5833, 0x4f, 0, 0}, {0x5834, 0x6e, 0, 0},
+       {0x5835, 0x7b, 0, 0}, {0x5836, 0x7e, 0, 0}, {0x5837, 0x6f, 0, 0},
+       {0x5838, 0xde, 0, 0}, {0x5839, 0xbf, 0, 0}, {0x583a, 0x9f, 0, 0},
+       {0x583b, 0xbf, 0, 0}, {0x583c, 0xec, 0, 0}, {0x5025, 0x00, 0, 0},
+       {0x3a0f, 0x30, 0, 0}, {0x3a10, 0x28, 0, 0}, {0x3a1b, 0x30, 0, 0},
+       {0x3a1e, 0x26, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x14, 0, 0},
+       {0x3008, 0x02, 0, 0}, {0x3035, 0x11, 0, 0},
+};
+
+static struct reg_value ov5640_setting_30fps_PAL_720_576[] = {
+       {0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3008, 0x42, 0, 0},
+       {0x3103, 0x03, 0, 0}, {0x3017, 0xff, 0, 0}, {0x3018, 0xff, 0, 0},
+       {0x3034, 0x1a, 0, 0}, {0x3035, 0x11, 0, 0}, {0x3036, 0x46, 0, 0},
+       {0x3037, 0x13, 0, 0}, {0x3108, 0x01, 0, 0}, {0x3630, 0x2e, 0, 0},
+       {0x3632, 0xe2, 0, 0}, {0x3633, 0x23, 0, 0}, {0x3621, 0xe0, 0, 0},
+       {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0}, {0x3715, 0x78, 0, 0},
+       {0x3717, 0x01, 0, 0}, {0x370b, 0x60, 0, 0}, {0x3705, 0x1a, 0, 0},
+       {0x3905, 0x02, 0, 0}, {0x3906, 0x10, 0, 0}, {0x3901, 0x0a, 0, 0},
+       {0x3731, 0x12, 0, 0}, {0x3600, 0x08, 0, 0}, {0x3601, 0x33, 0, 0},
+       {0x302d, 0x60, 0, 0}, {0x3620, 0x52, 0, 0}, {0x371b, 0x20, 0, 0},
+       {0x471c, 0x50, 0, 0}, {0x3a18, 0x00, 0, 0}, {0x3a19, 0xf8, 0, 0},
+       {0x3635, 0x1c, 0, 0}, {0x3634, 0x40, 0, 0}, {0x3622, 0x01, 0, 0},
+       {0x3c01, 0x34, 0, 0}, {0x3c04, 0x28, 0, 0}, {0x3c05, 0x98, 0, 0},
+       {0x3c06, 0x00, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c08, 0x00, 0, 0},
+       {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+       {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+       {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+       {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+       {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+       {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x02, 0, 0},
+       {0x380b, 0x40, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+       {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+       {0x3811, 0x62, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+       {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x62, 0, 0},
+       {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+       {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+       {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+       {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+       {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x3000, 0x00, 0, 0},
+       {0x3002, 0x1c, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3006, 0xc3, 0, 0},
+       {0x300e, 0x58, 0, 0}, {0x302e, 0x00, 0, 0}, {0x4300, 0x30, 0, 0},
+       {0x501f, 0x00, 0, 0}, {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0},
+       {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3824, 0x02, 0, 0},
+       {0x5000, 0xa7, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x5180, 0xff, 0, 0},
+       {0x5181, 0xf2, 0, 0}, {0x5182, 0x00, 0, 0}, {0x5183, 0x14, 0, 0},
+       {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0}, {0x5186, 0x09, 0, 0},
+       {0x5187, 0x09, 0, 0}, {0x5188, 0x09, 0, 0}, {0x5189, 0x75, 0, 0},
+       {0x518a, 0x54, 0, 0}, {0x518b, 0xe0, 0, 0}, {0x518c, 0xb2, 0, 0},
+       {0x518d, 0x42, 0, 0}, {0x518e, 0x3d, 0, 0}, {0x518f, 0x56, 0, 0},
+       {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0},
+       {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0},
+       {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0},
+       {0x5199, 0x12, 0, 0}, {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0},
+       {0x519c, 0x06, 0, 0}, {0x519d, 0x82, 0, 0}, {0x519e, 0x38, 0, 0},
+       {0x5381, 0x1c, 0, 0}, {0x5382, 0x5a, 0, 0}, {0x5383, 0x06, 0, 0},
+       {0x5384, 0x0a, 0, 0}, {0x5385, 0x7e, 0, 0}, {0x5386, 0x88, 0, 0},
+       {0x5387, 0x7c, 0, 0}, {0x5388, 0x6c, 0, 0}, {0x5389, 0x10, 0, 0},
+       {0x538a, 0x01, 0, 0}, {0x538b, 0x98, 0, 0}, {0x5300, 0x08, 0, 0},
+       {0x5301, 0x30, 0, 0}, {0x5302, 0x10, 0, 0}, {0x5303, 0x00, 0, 0},
+       {0x5304, 0x08, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x08, 0, 0},
+       {0x5307, 0x16, 0, 0}, {0x5309, 0x08, 0, 0}, {0x530a, 0x30, 0, 0},
+       {0x530b, 0x04, 0, 0}, {0x530c, 0x06, 0, 0}, {0x5480, 0x01, 0, 0},
+       {0x5481, 0x08, 0, 0}, {0x5482, 0x14, 0, 0}, {0x5483, 0x28, 0, 0},
+       {0x5484, 0x51, 0, 0}, {0x5485, 0x65, 0, 0}, {0x5486, 0x71, 0, 0},
+       {0x5487, 0x7d, 0, 0}, {0x5488, 0x87, 0, 0}, {0x5489, 0x91, 0, 0},
+       {0x548a, 0x9a, 0, 0}, {0x548b, 0xaa, 0, 0}, {0x548c, 0xb8, 0, 0},
+       {0x548d, 0xcd, 0, 0}, {0x548e, 0xdd, 0, 0}, {0x548f, 0xea, 0, 0},
+       {0x5490, 0x1d, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5583, 0x40, 0, 0},
+       {0x5584, 0x10, 0, 0}, {0x5589, 0x10, 0, 0}, {0x558a, 0x00, 0, 0},
+       {0x558b, 0xf8, 0, 0}, {0x5800, 0x23, 0, 0}, {0x5801, 0x15, 0, 0},
+       {0x5802, 0x10, 0, 0}, {0x5803, 0x10, 0, 0}, {0x5804, 0x15, 0, 0},
+       {0x5805, 0x23, 0, 0}, {0x5806, 0x0c, 0, 0}, {0x5807, 0x08, 0, 0},
+       {0x5808, 0x05, 0, 0}, {0x5809, 0x05, 0, 0}, {0x580a, 0x08, 0, 0},
+       {0x580b, 0x0c, 0, 0}, {0x580c, 0x07, 0, 0}, {0x580d, 0x03, 0, 0},
+       {0x580e, 0x00, 0, 0}, {0x580f, 0x00, 0, 0}, {0x5810, 0x03, 0, 0},
+       {0x5811, 0x07, 0, 0}, {0x5812, 0x07, 0, 0}, {0x5813, 0x03, 0, 0},
+       {0x5814, 0x00, 0, 0}, {0x5815, 0x00, 0, 0}, {0x5816, 0x03, 0, 0},
+       {0x5817, 0x07, 0, 0}, {0x5818, 0x0b, 0, 0}, {0x5819, 0x08, 0, 0},
+       {0x581a, 0x05, 0, 0}, {0x581b, 0x05, 0, 0}, {0x581c, 0x07, 0, 0},
+       {0x581d, 0x0b, 0, 0}, {0x581e, 0x2a, 0, 0}, {0x581f, 0x16, 0, 0},
+       {0x5820, 0x11, 0, 0}, {0x5821, 0x11, 0, 0}, {0x5822, 0x15, 0, 0},
+       {0x5823, 0x29, 0, 0}, {0x5824, 0xbf, 0, 0}, {0x5825, 0xaf, 0, 0},
+       {0x5826, 0x9f, 0, 0}, {0x5827, 0xaf, 0, 0}, {0x5828, 0xdf, 0, 0},
+       {0x5829, 0x6f, 0, 0}, {0x582a, 0x8e, 0, 0}, {0x582b, 0xab, 0, 0},
+       {0x582c, 0x9e, 0, 0}, {0x582d, 0x7f, 0, 0}, {0x582e, 0x4f, 0, 0},
+       {0x582f, 0x89, 0, 0}, {0x5830, 0x86, 0, 0}, {0x5831, 0x98, 0, 0},
+       {0x5832, 0x6f, 0, 0}, {0x5833, 0x4f, 0, 0}, {0x5834, 0x6e, 0, 0},
+       {0x5835, 0x7b, 0, 0}, {0x5836, 0x7e, 0, 0}, {0x5837, 0x6f, 0, 0},
+       {0x5838, 0xde, 0, 0}, {0x5839, 0xbf, 0, 0}, {0x583a, 0x9f, 0, 0},
+       {0x583b, 0xbf, 0, 0}, {0x583c, 0xec, 0, 0}, {0x5025, 0x00, 0, 0},
+       {0x3a0f, 0x30, 0, 0}, {0x3a10, 0x28, 0, 0}, {0x3a1b, 0x30, 0, 0},
+       {0x3a1e, 0x26, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x14, 0, 0},
+       {0x3008, 0x02, 0, 0}, {0x3035, 0x11, 0, 0},
+};
+
+static struct reg_value ov5640_setting_30fps_720P_1280_720[] = {
+       {0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3008, 0x42, 0, 0},
+       {0x3103, 0x03, 0, 0}, {0x3017, 0xff, 0, 0}, {0x3018, 0xff, 0, 0},
+       {0x3034, 0x1a, 0, 0}, {0x3035, 0x11, 0, 0}, {0x3036, 0x69, 0, 0},
+       {0x3037, 0x13, 0, 0}, {0x3108, 0x01, 0, 0}, {0x3630, 0x2e, 0, 0},
+       {0x3632, 0xe2, 0, 0}, {0x3633, 0x23, 0, 0}, {0x3621, 0xe0, 0, 0},
+       {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0}, {0x3715, 0x78, 0, 0},
+       {0x3717, 0x01, 0, 0}, {0x370b, 0x60, 0, 0}, {0x3705, 0x1a, 0, 0},
+       {0x3905, 0x02, 0, 0}, {0x3906, 0x10, 0, 0}, {0x3901, 0x0a, 0, 0},
+       {0x3731, 0x12, 0, 0}, {0x3600, 0x08, 0, 0}, {0x3601, 0x33, 0, 0},
+       {0x302d, 0x60, 0, 0}, {0x3620, 0x52, 0, 0}, {0x371b, 0x20, 0, 0},
+       {0x471c, 0x50, 0, 0}, {0x3a18, 0x00, 0, 0}, {0x3a19, 0xf8, 0, 0},
+       {0x3635, 0x1c, 0, 0}, {0x3634, 0x40, 0, 0}, {0x3622, 0x01, 0, 0},
+       {0x3c01, 0x34, 0, 0}, {0x3c04, 0x28, 0, 0}, {0x3c05, 0x98, 0, 0},
+       {0x3c06, 0x00, 0, 0}, {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0},
+       {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+       {0x3820, 0x41, 0, 0}, {0x3821, 0x27, 0, 0}, {0x3814, 0x31, 0, 0},
+       {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+       {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0},
+       {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0},
+       {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0},
+       {0x380b, 0xd0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x64, 0, 0},
+       {0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3810, 0x00, 0, 0},
+       {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
+       {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x62, 0, 0},
+       {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0},
+       {0x3a03, 0xe4, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0xbc, 0, 0},
+       {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x72, 0, 0}, {0x3a0e, 0x01, 0, 0},
+       {0x3a0d, 0x02, 0, 0}, {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe4, 0, 0},
+       {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x3000, 0x00, 0, 0},
+       {0x3002, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3006, 0xff, 0, 0},
+       {0x300e, 0x58, 0, 0}, {0x302e, 0x00, 0, 0}, {0x4300, 0x30, 0, 0},
+       {0x501f, 0x00, 0, 0}, {0x4713, 0x02, 0, 0}, {0x4407, 0x04, 0, 0},
+       {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3824, 0x04, 0, 0},
+       {0x5000, 0xa7, 0, 0}, {0x5001, 0x83, 0, 0}, {0x5180, 0xff, 0, 0},
+       {0x5181, 0xf2, 0, 0}, {0x5182, 0x00, 0, 0}, {0x5183, 0x14, 0, 0},
+       {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0}, {0x5186, 0x09, 0, 0},
+       {0x5187, 0x09, 0, 0}, {0x5188, 0x09, 0, 0}, {0x5189, 0x75, 0, 0},
+       {0x518a, 0x54, 0, 0}, {0x518b, 0xe0, 0, 0}, {0x518c, 0xb2, 0, 0},
+       {0x518d, 0x42, 0, 0}, {0x518e, 0x3d, 0, 0}, {0x518f, 0x56, 0, 0},
+       {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0},
+       {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0},
+       {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0},
+       {0x5199, 0x12, 0, 0}, {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0},
+       {0x519c, 0x06, 0, 0}, {0x519d, 0x82, 0, 0}, {0x519e, 0x38, 0, 0},
+       {0x5381, 0x1c, 0, 0}, {0x5382, 0x5a, 0, 0}, {0x5383, 0x06, 0, 0},
+       {0x5384, 0x0a, 0, 0}, {0x5385, 0x7e, 0, 0}, {0x5386, 0x88, 0, 0},
+       {0x5387, 0x7c, 0, 0}, {0x5388, 0x6c, 0, 0}, {0x5389, 0x10, 0, 0},
+       {0x538a, 0x01, 0, 0}, {0x538b, 0x98, 0, 0}, {0x5300, 0x08, 0, 0},
+       {0x5301, 0x30, 0, 0}, {0x5302, 0x10, 0, 0}, {0x5303, 0x00, 0, 0},
+       {0x5304, 0x08, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x08, 0, 0},
+       {0x5307, 0x16, 0, 0}, {0x5309, 0x08, 0, 0}, {0x530a, 0x30, 0, 0},
+       {0x530b, 0x04, 0, 0}, {0x530c, 0x06, 0, 0}, {0x5480, 0x01, 0, 0},
+       {0x5481, 0x08, 0, 0}, {0x5482, 0x14, 0, 0}, {0x5483, 0x28, 0, 0},
+       {0x5484, 0x51, 0, 0}, {0x5485, 0x65, 0, 0}, {0x5486, 0x71, 0, 0},
+       {0x5487, 0x7d, 0, 0}, {0x5488, 0x87, 0, 0}, {0x5489, 0x91, 0, 0},
+       {0x548a, 0x9a, 0, 0}, {0x548b, 0xaa, 0, 0}, {0x548c, 0xb8, 0, 0},
+       {0x548d, 0xcd, 0, 0}, {0x548e, 0xdd, 0, 0}, {0x548f, 0xea, 0, 0},
+       {0x5490, 0x1d, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5583, 0x40, 0, 0},
+       {0x5584, 0x10, 0, 0}, {0x5589, 0x10, 0, 0}, {0x558a, 0x00, 0, 0},
+       {0x558b, 0xf8, 0, 0}, {0x5800, 0x23, 0, 0}, {0x5801, 0x15, 0, 0},
+       {0x5802, 0x10, 0, 0}, {0x5803, 0x10, 0, 0}, {0x5804, 0x15, 0, 0},
+       {0x5805, 0x23, 0, 0}, {0x5806, 0x0c, 0, 0}, {0x5807, 0x08, 0, 0},
+       {0x5808, 0x05, 0, 0}, {0x5809, 0x05, 0, 0}, {0x580a, 0x08, 0, 0},
+       {0x580b, 0x0c, 0, 0}, {0x580c, 0x07, 0, 0}, {0x580d, 0x03, 0, 0},
+       {0x580e, 0x00, 0, 0}, {0x580f, 0x00, 0, 0}, {0x5810, 0x03, 0, 0},
+       {0x5811, 0x07, 0, 0}, {0x5812, 0x07, 0, 0}, {0x5813, 0x03, 0, 0},
+       {0x5814, 0x00, 0, 0}, {0x5815, 0x00, 0, 0}, {0x5816, 0x03, 0, 0},
+       {0x5817, 0x07, 0, 0}, {0x5818, 0x0b, 0, 0}, {0x5819, 0x08, 0, 0},
+       {0x581a, 0x05, 0, 0}, {0x581b, 0x05, 0, 0}, {0x581c, 0x07, 0, 0},
+       {0x581d, 0x0b, 0, 0}, {0x581e, 0x2a, 0, 0}, {0x581f, 0x16, 0, 0},
+       {0x5820, 0x11, 0, 0}, {0x5821, 0x11, 0, 0}, {0x5822, 0x15, 0, 0},
+       {0x5823, 0x29, 0, 0}, {0x5824, 0xbf, 0, 0}, {0x5825, 0xaf, 0, 0},
+       {0x5826, 0x9f, 0, 0}, {0x5827, 0xaf, 0, 0}, {0x5828, 0xdf, 0, 0},
+       {0x5829, 0x6f, 0, 0}, {0x582a, 0x8e, 0, 0}, {0x582b, 0xab, 0, 0},
+       {0x582c, 0x9e, 0, 0}, {0x582d, 0x7f, 0, 0}, {0x582e, 0x4f, 0, 0},
+       {0x582f, 0x89, 0, 0}, {0x5830, 0x86, 0, 0}, {0x5831, 0x98, 0, 0},
+       {0x5832, 0x6f, 0, 0}, {0x5833, 0x4f, 0, 0}, {0x5834, 0x6e, 0, 0},
+       {0x5835, 0x7b, 0, 0}, {0x5836, 0x7e, 0, 0}, {0x5837, 0x6f, 0, 0},
+       {0x5838, 0xde, 0, 0}, {0x5839, 0xbf, 0, 0}, {0x583a, 0x9f, 0, 0},
+       {0x583b, 0xbf, 0, 0}, {0x583c, 0xec, 0, 0}, {0x5025, 0x00, 0, 0},
+       {0x3a0f, 0x30, 0, 0}, {0x3a10, 0x28, 0, 0}, {0x3a1b, 0x30, 0, 0},
+       {0x3a1e, 0x26, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x14, 0, 0},
+       {0x3008, 0x02, 0, 0}, {0x3035, 0x21, 0, 0}, {0x3002, 0x1c, 0, 0},
+       {0x3006, 0xc3, 0, 0}, {0x3821, 0x07, 0, 0}, {0x4300, 0x30, 0, 0},
+       {0x501f, 0x00, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0},
+       {0x460b, 0x37, 0, 0},
+};
+
+static struct reg_value ov5640_setting_30fps_1080P_1920_1080[] = {
+       {0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3008, 0x42, 0, 0},
+       {0x3103, 0x03, 0, 0}, {0x3017, 0xff, 0, 0}, {0x3018, 0xff, 0, 0},
+       {0x3034, 0x1a, 0, 0}, {0x3035, 0x11, 0, 0}, {0x3036, 0x69, 0, 0},
+       {0x3037, 0x13, 0, 0}, {0x3108, 0x01, 0, 0}, {0x3630, 0x2e, 0, 0},
+       {0x3632, 0xe2, 0, 0}, {0x3633, 0x23, 0, 0}, {0x3621, 0xe0, 0, 0},
+       {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0}, {0x3715, 0x78, 0, 0},
+       {0x3717, 0x01, 0, 0}, {0x370b, 0x60, 0, 0}, {0x3705, 0x1a, 0, 0},
+       {0x3905, 0x02, 0, 0}, {0x3906, 0x10, 0, 0}, {0x3901, 0x0a, 0, 0},
+       {0x3731, 0x12, 0, 0}, {0x3600, 0x08, 0, 0}, {0x3601, 0x33, 0, 0},
+       {0x302d, 0x60, 0, 0}, {0x3620, 0x52, 0, 0}, {0x371b, 0x20, 0, 0},
+       {0x471c, 0x50, 0, 0}, {0x3a18, 0x00, 0, 0}, {0x3a19, 0xf8, 0, 0},
+       {0x3635, 0x1c, 0, 0}, {0x3634, 0x40, 0, 0}, {0x3622, 0x01, 0, 0},
+       {0x3c01, 0x34, 0, 0}, {0x3c04, 0x28, 0, 0}, {0x3c05, 0x98, 0, 0},
+       {0x3c06, 0x00, 0, 0}, {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0},
+       {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+       {0x3820, 0x41, 0, 0}, {0x3821, 0x26, 0, 0}, {0x3814, 0x11, 0, 0},
+       {0x3815, 0x11, 0, 0}, {0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0},
+       {0x3802, 0x01, 0, 0}, {0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0},
+       {0x3805, 0xef, 0, 0}, {0x3806, 0x05, 0, 0}, {0x3807, 0xf2, 0, 0},
+       {0x3808, 0x07, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x04, 0, 0},
+       {0x380b, 0x38, 0, 0}, {0x380c, 0x09, 0, 0}, {0x380d, 0xc4, 0, 0},
+       {0x380e, 0x04, 0, 0}, {0x380f, 0x60, 0, 0}, {0x3810, 0x00, 0, 0},
+       {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
+       {0x3618, 0x04, 0, 0}, {0x3612, 0x2b, 0, 0}, {0x3708, 0x62, 0, 0},
+       {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x04, 0, 0},
+       {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x50, 0, 0},
+       {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0}, {0x3a0e, 0x03, 0, 0},
+       {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0}, {0x3a15, 0x60, 0, 0},
+       {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x3000, 0x00, 0, 0},
+       {0x3002, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3006, 0xff, 0, 0},
+       {0x300e, 0x58, 0, 0}, {0x302e, 0x00, 0, 0}, {0x4300, 0x30, 0, 0},
+       {0x501f, 0x00, 0, 0}, {0x4713, 0x02, 0, 0}, {0x4407, 0x04, 0, 0},
+       {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3824, 0x04, 0, 0},
+       {0x5000, 0xa7, 0, 0}, {0x5001, 0x83, 0, 0}, {0x5180, 0xff, 0, 0},
+       {0x5181, 0xf2, 0, 0}, {0x5182, 0x00, 0, 0}, {0x5183, 0x14, 0, 0},
+       {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0}, {0x5186, 0x09, 0, 0},
+       {0x5187, 0x09, 0, 0}, {0x5188, 0x09, 0, 0}, {0x5189, 0x75, 0, 0},
+       {0x518a, 0x54, 0, 0}, {0x518b, 0xe0, 0, 0}, {0x518c, 0xb2, 0, 0},
+       {0x518d, 0x42, 0, 0}, {0x518e, 0x3d, 0, 0}, {0x518f, 0x56, 0, 0},
+       {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0},
+       {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0},
+       {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0},
+       {0x5199, 0x12, 0, 0}, {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0},
+       {0x519c, 0x06, 0, 0}, {0x519d, 0x82, 0, 0}, {0x519e, 0x38, 0, 0},
+       {0x5381, 0x1c, 0, 0}, {0x5382, 0x5a, 0, 0}, {0x5383, 0x06, 0, 0},
+       {0x5384, 0x0a, 0, 0}, {0x5385, 0x7e, 0, 0}, {0x5386, 0x88, 0, 0},
+       {0x5387, 0x7c, 0, 0}, {0x5388, 0x6c, 0, 0}, {0x5389, 0x10, 0, 0},
+       {0x538a, 0x01, 0, 0}, {0x538b, 0x98, 0, 0}, {0x5300, 0x08, 0, 0},
+       {0x5301, 0x30, 0, 0}, {0x5302, 0x10, 0, 0}, {0x5303, 0x00, 0, 0},
+       {0x5304, 0x08, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x08, 0, 0},
+       {0x5307, 0x16, 0, 0}, {0x5309, 0x08, 0, 0}, {0x530a, 0x30, 0, 0},
+       {0x530b, 0x04, 0, 0}, {0x530c, 0x06, 0, 0}, {0x5480, 0x01, 0, 0},
+       {0x5481, 0x08, 0, 0}, {0x5482, 0x14, 0, 0}, {0x5483, 0x28, 0, 0},
+       {0x5484, 0x51, 0, 0}, {0x5485, 0x65, 0, 0}, {0x5486, 0x71, 0, 0},
+       {0x5487, 0x7d, 0, 0}, {0x5488, 0x87, 0, 0}, {0x5489, 0x91, 0, 0},
+       {0x548a, 0x9a, 0, 0}, {0x548b, 0xaa, 0, 0}, {0x548c, 0xb8, 0, 0},
+       {0x548d, 0xcd, 0, 0}, {0x548e, 0xdd, 0, 0}, {0x548f, 0xea, 0, 0},
+       {0x5490, 0x1d, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5583, 0x40, 0, 0},
+       {0x5584, 0x10, 0, 0}, {0x5589, 0x10, 0, 0}, {0x558a, 0x00, 0, 0},
+       {0x558b, 0xf8, 0, 0}, {0x5800, 0x23, 0, 0}, {0x5801, 0x15, 0, 0},
+       {0x5802, 0x10, 0, 0}, {0x5803, 0x10, 0, 0}, {0x5804, 0x15, 0, 0},
+       {0x5805, 0x23, 0, 0}, {0x5806, 0x0c, 0, 0}, {0x5807, 0x08, 0, 0},
+       {0x5808, 0x05, 0, 0}, {0x5809, 0x05, 0, 0}, {0x580a, 0x08, 0, 0},
+       {0x580b, 0x0c, 0, 0}, {0x580c, 0x07, 0, 0}, {0x580d, 0x03, 0, 0},
+       {0x580e, 0x00, 0, 0}, {0x580f, 0x00, 0, 0}, {0x5810, 0x03, 0, 0},
+       {0x5811, 0x07, 0, 0}, {0x5812, 0x07, 0, 0}, {0x5813, 0x03, 0, 0},
+       {0x5814, 0x00, 0, 0}, {0x5815, 0x00, 0, 0}, {0x5816, 0x03, 0, 0},
+       {0x5817, 0x07, 0, 0}, {0x5818, 0x0b, 0, 0}, {0x5819, 0x08, 0, 0},
+       {0x581a, 0x05, 0, 0}, {0x581b, 0x05, 0, 0}, {0x581c, 0x07, 0, 0},
+       {0x581d, 0x0b, 0, 0}, {0x581e, 0x2a, 0, 0}, {0x581f, 0x16, 0, 0},
+       {0x5820, 0x11, 0, 0}, {0x5821, 0x11, 0, 0}, {0x5822, 0x15, 0, 0},
+       {0x5823, 0x29, 0, 0}, {0x5824, 0xbf, 0, 0}, {0x5825, 0xaf, 0, 0},
+       {0x5826, 0x9f, 0, 0}, {0x5827, 0xaf, 0, 0}, {0x5828, 0xdf, 0, 0},
+       {0x5829, 0x6f, 0, 0}, {0x582a, 0x8e, 0, 0}, {0x582b, 0xab, 0, 0},
+       {0x582c, 0x9e, 0, 0}, {0x582d, 0x7f, 0, 0}, {0x582e, 0x4f, 0, 0},
+       {0x582f, 0x89, 0, 0}, {0x5830, 0x86, 0, 0}, {0x5831, 0x98, 0, 0},
+       {0x5832, 0x6f, 0, 0}, {0x5833, 0x4f, 0, 0}, {0x5834, 0x6e, 0, 0},
+       {0x5835, 0x7b, 0, 0}, {0x5836, 0x7e, 0, 0}, {0x5837, 0x6f, 0, 0},
+       {0x5838, 0xde, 0, 0}, {0x5839, 0xbf, 0, 0}, {0x583a, 0x9f, 0, 0},
+       {0x583b, 0xbf, 0, 0}, {0x583c, 0xec, 0, 0}, {0x5025, 0x00, 0, 0},
+       {0x3a0f, 0x30, 0, 0}, {0x3a10, 0x28, 0, 0}, {0x3a1b, 0x30, 0, 0},
+       {0x3a1e, 0x26, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x14, 0, 0},
+       {0x3008, 0x02, 0, 0}, {0x3035, 0x21, 0, 0}, {0x3002, 0x1c, 0, 0},
+       {0x3006, 0xc3, 0, 0}, {0x3821, 0x06, 0, 0}, {0x4300, 0x30, 0, 0},
+       {0x501f, 0x00, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0},
+       {0x460b, 0x37, 0, 0},
+};
+
+static struct ov5640_mode_info ov5640_mode_info_data[2][ov5640_mode_MAX + 1] = {
+       {
+               {ov5640_mode_VGA_640_480, 0, 0, NULL, 0},
+               {ov5640_mode_QVGA_320_240, 0, 0, NULL, 0},
+               {ov5640_mode_NTSC_720_480, 0, 0, NULL, 0},
+               {ov5640_mode_PAL_720_576, 0, 0, NULL, 0},
+               {ov5640_mode_720P_1280_720, 0, 0, NULL, 0},
+               {ov5640_mode_1080P_1920_1080, 0, 0, NULL, 0},
+               {ov5640_mode_QSXGA_2592_1944, 2592, 1944,
+               ov5640_setting_15fps_QSXGA_2592_1944,
+               ARRAY_SIZE(ov5640_setting_15fps_QSXGA_2592_1944)},
+       },
+       {
+               {ov5640_mode_VGA_640_480,    640,  480,
+               ov5640_setting_30fps_VGA_640_480,
+               ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480)},
+               {ov5640_mode_QVGA_320_240,   320,  240,
+               ov5640_setting_30fps_QVGA_320_240,
+               ARRAY_SIZE(ov5640_setting_30fps_QVGA_320_240)},
+               {ov5640_mode_NTSC_720_480,   720, 480,
+               ov5640_setting_30fps_NTSC_720_480,
+               ARRAY_SIZE(ov5640_setting_30fps_NTSC_720_480)},
+               {ov5640_mode_PAL_720_576,    720, 576,
+               ov5640_setting_30fps_PAL_720_576,
+               ARRAY_SIZE(ov5640_setting_30fps_PAL_720_576)},
+               {ov5640_mode_720P_1280_720,  1280, 720,
+               ov5640_setting_30fps_720P_1280_720,
+               ARRAY_SIZE(ov5640_setting_30fps_720P_1280_720)},
+               {ov5640_mode_1080P_1920_1080,  1920, 1080,
+               ov5640_setting_30fps_1080P_1920_1080,
+               ARRAY_SIZE(ov5640_setting_30fps_1080P_1920_1080)},
+               {ov5640_mode_QSXGA_2592_1944, 0, 0, NULL, 0},
+       },
+};
+
+static struct regulator *io_regulator;
+static struct regulator *core_regulator;
+static struct regulator *analog_regulator;
+static struct regulator *gpo_regulator;
+static struct mxc_camera_platform_data *camera_plat;
+
+static int ov5640_probe(struct i2c_client *adapter,
+                               const struct i2c_device_id *device_id);
+static int ov5640_remove(struct i2c_client *client);
+
+static s32 ov5640_read_reg(u16 reg, u8 *val);
+static s32 ov5640_write_reg(u16 reg, u8 val);
+
+static const struct i2c_device_id ov5640_id[] = {
+       {"ov5640", 0},
+       {},
+};
+
+MODULE_DEVICE_TABLE(i2c, ov5640_id);
+
+static struct i2c_driver ov5640_i2c_driver = {
+       .driver = {
+                 .owner = THIS_MODULE,
+                 .name  = "ov5640",
+                 },
+       .probe  = ov5640_probe,
+       .remove = ov5640_remove,
+       .id_table = ov5640_id,
+};
+
+extern void gpio_sensor_active(unsigned int csi_index);
+extern void gpio_sensor_inactive(unsigned int csi);
+
+static s32 ov5640_write_reg(u16 reg, u8 val)
+{
+       u8 au8Buf[3] = {0};
+
+       au8Buf[0] = reg >> 8;
+       au8Buf[1] = reg & 0xff;
+       au8Buf[2] = val;
+
+       if (i2c_master_send(ov5640_data.i2c_client, au8Buf, 3) < 0) {
+               pr_err("%s:write reg error:reg=%x,val=%x\n",
+                       __func__, reg, val);
+               return -1;
+       }
+
+       return 0;
+}
+
+static s32 ov5640_read_reg(u16 reg, u8 *val)
+{
+       u8 au8RegBuf[2] = {0};
+       u8 u8RdVal = 0;
+
+       au8RegBuf[0] = reg >> 8;
+       au8RegBuf[1] = reg & 0xff;
+
+       if (2 != i2c_master_send(ov5640_data.i2c_client, au8RegBuf, 2)) {
+               pr_err("%s:write reg error:reg=%x\n",
+                               __func__, reg);
+               return -1;
+       }
+
+       if (1 != i2c_master_recv(ov5640_data.i2c_client, &u8RdVal, 1)) {
+               pr_err("%s:read reg error:reg=%x,val=%x\n",
+                               __func__, reg, u8RdVal);
+               return -1;
+       }
+
+       *val = u8RdVal;
+
+       return u8RdVal;
+}
+
+static int ov5640_init_mode(enum ov5640_frame_rate frame_rate,
+                           enum ov5640_mode mode)
+{
+       struct reg_value *pModeSetting = NULL;
+       s32 i = 0;
+       s32 iModeSettingArySize = 0;
+       register u32 Delay_ms = 0;
+       register u16 RegAddr = 0;
+       register u8 Mask = 0;
+       register u8 Val = 0;
+       u8 RegVal = 0;
+       int retval = 0;
+
+       if (mode > ov5640_mode_MAX || mode < ov5640_mode_MIN) {
+               pr_err("Wrong ov5640 mode detected!\n");
+               return -1;
+       }
+
+       pModeSetting = ov5640_mode_info_data[frame_rate][mode].init_data_ptr;
+       iModeSettingArySize =
+               ov5640_mode_info_data[frame_rate][mode].init_data_size;
+
+       ov5640_data.pix.width = ov5640_mode_info_data[frame_rate][mode].width;
+       ov5640_data.pix.height = ov5640_mode_info_data[frame_rate][mode].height;
+
+       if (ov5640_data.pix.width == 0 || ov5640_data.pix.height == 0 ||
+           pModeSetting == NULL || iModeSettingArySize == 0)
+               return -EINVAL;
+
+       for (i = 0; i < iModeSettingArySize; ++i, ++pModeSetting) {
+               Delay_ms = pModeSetting->u32Delay_ms;
+               RegAddr = pModeSetting->u16RegAddr;
+               Val = pModeSetting->u8Val;
+               Mask = pModeSetting->u8Mask;
+
+               if (Mask) {
+                       retval = ov5640_read_reg(RegAddr, &RegVal);
+                       if (retval < 0)
+                               goto err;
+
+                       RegVal &= ~(u8)Mask;
+                       Val &= Mask;
+                       Val |= RegVal;
+               }
+
+               retval = ov5640_write_reg(RegAddr, Val);
+               if (retval < 0)
+                       goto err;
+
+               if (Delay_ms)
+                       msleep(Delay_ms);
+       }
+err:
+       return retval;
+}
+
+/* --------------- IOCTL functions from v4l2_int_ioctl_desc --------------- */
+
+static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p)
+{
+       if (s == NULL) {
+               pr_err("   ERROR!! no slave device set!\n");
+               return -1;
+       }
+
+       memset(p, 0, sizeof(*p));
+       p->u.bt656.clock_curr = ov5640_data.mclk;
+       pr_debug("   clock_curr=mclk=%d\n", ov5640_data.mclk);
+       p->if_type = V4L2_IF_TYPE_BT656;
+       p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT;
+       p->u.bt656.clock_min = OV5640_XCLK_MIN;
+       p->u.bt656.clock_max = OV5640_XCLK_MAX;
+       p->u.bt656.bt_sync_correct = 1;  /* Indicate external vsync */
+
+       return 0;
+}
+
+/*!
+ * ioctl_s_power - V4L2 sensor interface handler for VIDIOC_S_POWER ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @on: indicates power mode (on or off)
+ *
+ * Turns the power on or off, depending on the value of on and returns the
+ * appropriate error code.
+ */
+static int ioctl_s_power(struct v4l2_int_device *s, int on)
+{
+       struct sensor *sensor = s->priv;
+
+       if (on && !sensor->on) {
+               gpio_sensor_active(ov5640_data.csi);
+               if (io_regulator)
+                       if (regulator_enable(io_regulator) != 0)
+                               return -EIO;
+               if (core_regulator)
+                       if (regulator_enable(core_regulator) != 0)
+                               return -EIO;
+               if (gpo_regulator)
+                       if (regulator_enable(gpo_regulator) != 0)
+                               return -EIO;
+               if (analog_regulator)
+                       if (regulator_enable(analog_regulator) != 0)
+                               return -EIO;
+               /* Make sure power on */
+               if (camera_plat->pwdn)
+                       camera_plat->pwdn(0);
+
+       } else if (!on && sensor->on) {
+               if (analog_regulator)
+                       regulator_disable(analog_regulator);
+               if (core_regulator)
+                       regulator_disable(core_regulator);
+               if (io_regulator)
+                       regulator_disable(io_regulator);
+               if (gpo_regulator)
+                       regulator_disable(gpo_regulator);
+               gpio_sensor_inactive(ov5640_data.csi);
+       }
+
+       sensor->on = on;
+
+       return 0;
+}
+
+/*!
+ * ioctl_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure
+ *
+ * Returns the sensor's video CAPTURE parameters.
+ */
+static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+       struct sensor *sensor = s->priv;
+       struct v4l2_captureparm *cparm = &a->parm.capture;
+       int ret = 0;
+
+       switch (a->type) {
+       /* This is the only case currently handled. */
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               memset(a, 0, sizeof(*a));
+               a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+               cparm->capability = sensor->streamcap.capability;
+               cparm->timeperframe = sensor->streamcap.timeperframe;
+               cparm->capturemode = sensor->streamcap.capturemode;
+               ret = 0;
+               break;
+
+       /* These are all the possible cases. */
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+       case V4L2_BUF_TYPE_VBI_OUTPUT:
+       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+               ret = -EINVAL;
+               break;
+
+       default:
+               pr_debug("   type is unknown - %d\n", a->type);
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+/*!
+ * ioctl_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure
+ *
+ * Configures the sensor to use the input parameters, if possible.  If
+ * not possible, reverts to the old parameters and returns the
+ * appropriate error code.
+ */
+static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+       struct sensor *sensor = s->priv;
+       struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe;
+       u32 tgt_fps;    /* target frames per secound */
+       enum ov5640_frame_rate frame_rate;
+       int ret = 0;
+
+       /* Make sure power on */
+       if (camera_plat->pwdn)
+               camera_plat->pwdn(0);
+
+       switch (a->type) {
+       /* This is the only case currently handled. */
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               /* Check that the new frame rate is allowed. */
+               if ((timeperframe->numerator == 0) ||
+                   (timeperframe->denominator == 0)) {
+                       timeperframe->denominator = DEFAULT_FPS;
+                       timeperframe->numerator = 1;
+               }
+
+               tgt_fps = timeperframe->denominator /
+                         timeperframe->numerator;
+
+               if (tgt_fps > MAX_FPS) {
+                       timeperframe->denominator = MAX_FPS;
+                       timeperframe->numerator = 1;
+               } else if (tgt_fps < MIN_FPS) {
+                       timeperframe->denominator = MIN_FPS;
+                       timeperframe->numerator = 1;
+               }
+
+               /* Actual frame rate we use */
+               tgt_fps = timeperframe->denominator /
+                         timeperframe->numerator;
+
+               if (tgt_fps == 15)
+                       frame_rate = ov5640_15_fps;
+               else if (tgt_fps == 30)
+                       frame_rate = ov5640_30_fps;
+               else {
+                       pr_err(" The camera frame rate is not supported!\n");
+                       return -EINVAL;
+               }
+
+               sensor->streamcap.timeperframe = *timeperframe;
+               sensor->streamcap.capturemode =
+                               (u32)a->parm.capture.capturemode;
+
+               ret = ov5640_init_mode(frame_rate,
+                                      sensor->streamcap.capturemode);
+               break;
+
+       /* These are all the possible cases. */
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+       case V4L2_BUF_TYPE_VBI_OUTPUT:
+       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+               pr_debug("   type is not " \
+                       "V4L2_BUF_TYPE_VIDEO_CAPTURE but %d\n",
+                       a->type);
+               ret = -EINVAL;
+               break;
+
+       default:
+               pr_debug("   type is unknown - %d\n", a->type);
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+/*!
+ * ioctl_g_fmt_cap - V4L2 sensor interface handler for ioctl_g_fmt_cap
+ * @s: pointer to standard V4L2 device structure
+ * @f: pointer to standard V4L2 v4l2_format structure
+ *
+ * Returns the sensor's current pixel format in the v4l2_format
+ * parameter.
+ */
+static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
+{
+       struct sensor *sensor = s->priv;
+
+       f->fmt.pix = sensor->pix;
+
+       return 0;
+}
+
+/*!
+ * ioctl_g_ctrl - V4L2 sensor interface handler for VIDIOC_G_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @vc: standard V4L2 VIDIOC_G_CTRL ioctl structure
+ *
+ * If the requested control is supported, returns the control's current
+ * value from the video_control[] array.  Otherwise, returns -EINVAL
+ * if the control is not supported.
+ */
+static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc)
+{
+       int ret = 0;
+
+       switch (vc->id) {
+       case V4L2_CID_BRIGHTNESS:
+               vc->value = ov5640_data.brightness;
+               break;
+       case V4L2_CID_HUE:
+               vc->value = ov5640_data.hue;
+               break;
+       case V4L2_CID_CONTRAST:
+               vc->value = ov5640_data.contrast;
+               break;
+       case V4L2_CID_SATURATION:
+               vc->value = ov5640_data.saturation;
+               break;
+       case V4L2_CID_RED_BALANCE:
+               vc->value = ov5640_data.red;
+               break;
+       case V4L2_CID_BLUE_BALANCE:
+               vc->value = ov5640_data.blue;
+               break;
+       case V4L2_CID_EXPOSURE:
+               vc->value = ov5640_data.ae_mode;
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+/*!
+ * ioctl_s_ctrl - V4L2 sensor interface handler for VIDIOC_S_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure
+ *
+ * If the requested control is supported, sets the control's current
+ * value in HW (and updates the video_control[] array).  Otherwise,
+ * returns -EINVAL if the control is not supported.
+ */
+static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc)
+{
+       int retval = 0;
+
+       pr_debug("In ov5640:ioctl_s_ctrl %d\n",
+                vc->id);
+
+       switch (vc->id) {
+       case V4L2_CID_BRIGHTNESS:
+               break;
+       case V4L2_CID_CONTRAST:
+               break;
+       case V4L2_CID_SATURATION:
+               break;
+       case V4L2_CID_HUE:
+               break;
+       case V4L2_CID_AUTO_WHITE_BALANCE:
+               break;
+       case V4L2_CID_DO_WHITE_BALANCE:
+               break;
+       case V4L2_CID_RED_BALANCE:
+               break;
+       case V4L2_CID_BLUE_BALANCE:
+               break;
+       case V4L2_CID_GAMMA:
+               break;
+       case V4L2_CID_EXPOSURE:
+               break;
+       case V4L2_CID_AUTOGAIN:
+               break;
+       case V4L2_CID_GAIN:
+               break;
+       case V4L2_CID_HFLIP:
+               break;
+       case V4L2_CID_VFLIP:
+               break;
+       default:
+               retval = -EPERM;
+               break;
+       }
+
+       return retval;
+}
+
+/*!
+ * ioctl_enum_framesizes - V4L2 sensor interface handler for
+ *                        VIDIOC_ENUM_FRAMESIZES ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @fsize: standard V4L2 VIDIOC_ENUM_FRAMESIZES ioctl structure
+ *
+ * Return 0 if successful, otherwise -EINVAL.
+ */
+static int ioctl_enum_framesizes(struct v4l2_int_device *s,
+                                struct v4l2_frmsizeenum *fsize)
+{
+       if (fsize->index > ov5640_mode_MAX)
+               return -EINVAL;
+
+       fsize->pixel_format = ov5640_data.pix.pixelformat;
+       fsize->discrete.width =
+                       max(ov5640_mode_info_data[0][fsize->index].width,
+                           ov5640_mode_info_data[1][fsize->index].width);
+       fsize->discrete.height =
+                       max(ov5640_mode_info_data[0][fsize->index].height,
+                           ov5640_mode_info_data[1][fsize->index].height);
+       return 0;
+}
+
+/*!
+ * ioctl_g_chip_ident - V4L2 sensor interface handler for
+ *                     VIDIOC_DBG_G_CHIP_IDENT ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @id: pointer to int
+ *
+ * Return 0.
+ */
+static int ioctl_g_chip_ident(struct v4l2_int_device *s, int *id)
+{
+       ((struct v4l2_dbg_chip_ident *)id)->match.type =
+                                       V4L2_CHIP_MATCH_I2C_DRIVER;
+       strcpy(((struct v4l2_dbg_chip_ident *)id)->match.name, "ov5640_camera");
+
+       return 0;
+}
+
+/*!
+ * ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT
+ * @s: pointer to standard V4L2 device structure
+ */
+static int ioctl_init(struct v4l2_int_device *s)
+{
+
+       return 0;
+}
+
+/*!
+ * ioctl_enum_fmt_cap - V4L2 sensor interface handler for VIDIOC_ENUM_FMT
+ * @s: pointer to standard V4L2 device structure
+ * @fmt: pointer to standard V4L2 fmt description structure
+ *
+ * Return 0.
+ */
+static int ioctl_enum_fmt_cap(struct v4l2_int_device *s,
+                             struct v4l2_fmtdesc *fmt)
+{
+       if (fmt->index > ov5640_mode_MAX)
+               return -EINVAL;
+
+       fmt->pixelformat = ov5640_data.pix.pixelformat;
+
+       return 0;
+}
+
+/*!
+ * ioctl_dev_init - V4L2 sensor interface handler for vidioc_int_dev_init_num
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Initialise the device when slave attaches to the master.
+ */
+static int ioctl_dev_init(struct v4l2_int_device *s)
+{
+       struct sensor *sensor = s->priv;
+       u32 tgt_xclk;   /* target xclk */
+       u32 tgt_fps;    /* target frames per secound */
+       enum ov5640_frame_rate frame_rate;
+
+       gpio_sensor_active(ov5640_data.csi);
+       ov5640_data.on = true;
+
+       /* mclk */
+       tgt_xclk = ov5640_data.mclk;
+       tgt_xclk = min(tgt_xclk, (u32)OV5640_XCLK_MAX);
+       tgt_xclk = max(tgt_xclk, (u32)OV5640_XCLK_MIN);
+       ov5640_data.mclk = tgt_xclk;
+
+       pr_debug("   Setting mclk to %d MHz\n", tgt_xclk / 1000000);
+       set_mclk_rate(&ov5640_data.mclk, ov5640_data.csi);
+
+       /* Default camera frame rate is set in probe */
+       tgt_fps = sensor->streamcap.timeperframe.denominator /
+                 sensor->streamcap.timeperframe.numerator;
+
+       if (tgt_fps == 15)
+               frame_rate = ov5640_15_fps;
+       else if (tgt_fps == 30)
+               frame_rate = ov5640_30_fps;
+       else
+               return -EINVAL; /* Only support 15fps or 30fps now. */
+
+       return ov5640_init_mode(frame_rate,
+                               sensor->streamcap.capturemode);
+}
+
+/*!
+ * ioctl_dev_exit - V4L2 sensor interface handler for vidioc_int_dev_exit_num
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Delinitialise the device when slave detaches to the master.
+ */
+static int ioctl_dev_exit(struct v4l2_int_device *s)
+{
+       gpio_sensor_inactive(ov5640_data.csi);
+
+       return 0;
+}
+
+/*!
+ * This structure defines all the ioctls for this module and links them to the
+ * enumeration.
+ */
+static struct v4l2_int_ioctl_desc ov5640_ioctl_desc[] = {
+       {vidioc_int_dev_init_num, (v4l2_int_ioctl_func *)ioctl_dev_init},
+       {vidioc_int_dev_exit_num, ioctl_dev_exit},
+       {vidioc_int_s_power_num, (v4l2_int_ioctl_func *)ioctl_s_power},
+       {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func *)ioctl_g_ifparm},
+/*     {vidioc_int_g_needs_reset_num,
+                               (v4l2_int_ioctl_func *)ioctl_g_needs_reset}, */
+/*     {vidioc_int_reset_num, (v4l2_int_ioctl_func *)ioctl_reset}, */
+       {vidioc_int_init_num, (v4l2_int_ioctl_func *)ioctl_init},
+       {vidioc_int_enum_fmt_cap_num,
+                               (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap},
+/*     {vidioc_int_try_fmt_cap_num,
+                               (v4l2_int_ioctl_func *)ioctl_try_fmt_cap}, */
+       {vidioc_int_g_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_g_fmt_cap},
+/*     {vidioc_int_s_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_s_fmt_cap}, */
+       {vidioc_int_g_parm_num, (v4l2_int_ioctl_func *)ioctl_g_parm},
+       {vidioc_int_s_parm_num, (v4l2_int_ioctl_func *)ioctl_s_parm},
+/*     {vidioc_int_queryctrl_num, (v4l2_int_ioctl_func *)ioctl_queryctrl}, */
+       {vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func *)ioctl_g_ctrl},
+       {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *)ioctl_s_ctrl},
+       {vidioc_int_enum_framesizes_num,
+                               (v4l2_int_ioctl_func *)ioctl_enum_framesizes},
+       {vidioc_int_g_chip_ident_num,
+                               (v4l2_int_ioctl_func *)ioctl_g_chip_ident},
+};
+
+static struct v4l2_int_slave ov5640_slave = {
+       .ioctls = ov5640_ioctl_desc,
+       .num_ioctls = ARRAY_SIZE(ov5640_ioctl_desc),
+};
+
+static struct v4l2_int_device ov5640_int_device = {
+       .module = THIS_MODULE,
+       .name = "ov5640",
+       .type = v4l2_int_type_slave,
+       .u = {
+               .slave = &ov5640_slave,
+       },
+};
+
+/*!
+ * ov5640 I2C probe function
+ *
+ * @param adapter            struct i2c_adapter *
+ * @return  Error code indicating success or failure
+ */
+static int ov5640_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       int retval;
+       struct mxc_camera_platform_data *plat_data = client->dev.platform_data;
+
+       /* Set initial values for the sensor struct. */
+       memset(&ov5640_data, 0, sizeof(ov5640_data));
+       ov5640_data.mclk = 24000000; /* 6 - 54 MHz, typical 24MHz */
+       ov5640_data.mclk = plat_data->mclk;
+       ov5640_data.csi = plat_data->csi;
+
+       ov5640_data.i2c_client = client;
+       ov5640_data.pix.pixelformat = V4L2_PIX_FMT_YUYV;
+       ov5640_data.pix.width = 640;
+       ov5640_data.pix.height = 480;
+       ov5640_data.streamcap.capability = V4L2_MODE_HIGHQUALITY |
+                                          V4L2_CAP_TIMEPERFRAME;
+       ov5640_data.streamcap.capturemode = 0;
+       ov5640_data.streamcap.timeperframe.denominator = DEFAULT_FPS;
+       ov5640_data.streamcap.timeperframe.numerator = 1;
+
+       if (plat_data->io_regulator) {
+               io_regulator = regulator_get(&client->dev,
+                                            plat_data->io_regulator);
+               if (!IS_ERR(io_regulator)) {
+                       regulator_set_voltage(io_regulator,
+                                             OV5640_VOLTAGE_DIGITAL_IO,
+                                             OV5640_VOLTAGE_DIGITAL_IO);
+                       if (regulator_enable(io_regulator) != 0) {
+                               pr_err("%s:io set voltage error\n", __func__);
+                               goto err1;
+                       } else {
+                               dev_dbg(&client->dev,
+                                       "%s:io set voltage ok\n", __func__);
+                       }
+               } else
+                       io_regulator = NULL;
+       }
+
+       if (plat_data->core_regulator) {
+               core_regulator = regulator_get(&client->dev,
+                                              plat_data->core_regulator);
+               if (!IS_ERR(core_regulator)) {
+                       regulator_set_voltage(core_regulator,
+                                             OV5640_VOLTAGE_DIGITAL_CORE,
+                                             OV5640_VOLTAGE_DIGITAL_CORE);
+                       if (regulator_enable(core_regulator) != 0) {
+                               pr_err("%s:core set voltage error\n", __func__);
+                               goto err2;
+                       } else {
+                               dev_dbg(&client->dev,
+                                       "%s:core set voltage ok\n", __func__);
+                       }
+               } else
+                       core_regulator = NULL;
+       }
+
+       if (plat_data->analog_regulator) {
+               analog_regulator = regulator_get(&client->dev,
+                                                plat_data->analog_regulator);
+               if (!IS_ERR(analog_regulator)) {
+                       regulator_set_voltage(analog_regulator,
+                                             OV5640_VOLTAGE_ANALOG,
+                                             OV5640_VOLTAGE_ANALOG);
+                       if (regulator_enable(analog_regulator) != 0) {
+                               pr_err("%s:analog set voltage error\n",
+                                       __func__);
+                               goto err3;
+                       } else {
+                               dev_dbg(&client->dev,
+                                       "%s:analog set voltage ok\n", __func__);
+                       }
+               } else
+                       analog_regulator = NULL;
+       }
+
+       if (plat_data->pwdn)
+               plat_data->pwdn(0);
+
+       camera_plat = plat_data;
+
+       ov5640_int_device.priv = &ov5640_data;
+       retval = v4l2_int_device_register(&ov5640_int_device);
+
+       return retval;
+
+err3:
+       if (core_regulator) {
+               regulator_disable(core_regulator);
+               regulator_put(core_regulator);
+       }
+err2:
+       if (io_regulator) {
+               regulator_disable(io_regulator);
+               regulator_put(io_regulator);
+       }
+err1:
+       return -1;
+}
+
+/*!
+ * ov5640 I2C detach function
+ *
+ * @param client            struct i2c_client *
+ * @return  Error code indicating success or failure
+ */
+static int ov5640_remove(struct i2c_client *client)
+{
+       v4l2_int_device_unregister(&ov5640_int_device);
+
+       if (gpo_regulator) {
+               regulator_disable(gpo_regulator);
+               regulator_put(gpo_regulator);
+       }
+
+       if (analog_regulator) {
+               regulator_disable(analog_regulator);
+               regulator_put(analog_regulator);
+       }
+
+       if (core_regulator) {
+               regulator_disable(core_regulator);
+               regulator_put(core_regulator);
+       }
+
+       if (io_regulator) {
+               regulator_disable(io_regulator);
+               regulator_put(io_regulator);
+       }
+
+       return 0;
+}
+
+/*!
+ * ov5640 init function
+ * Called by insmod ov5640_camera.ko.
+ *
+ * @return  Error code indicating success or failure
+ */
+static __init int ov5640_init(void)
+{
+       u8 err;
+
+       err = i2c_add_driver(&ov5640_i2c_driver);
+       if (err != 0)
+               pr_err("%s:driver registration failed, error=%d \n",
+                       __func__, err);
+
+       return err;
+}
+
+/*!
+ * OV5640 cleanup function
+ * Called on rmmod ov5640_camera.ko
+ *
+ * @return  Error code indicating success or failure
+ */
+static void __exit ov5640_clean(void)
+{
+       i2c_del_driver(&ov5640_i2c_driver);
+}
+
+module_init(ov5640_init);
+module_exit(ov5640_clean);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("OV5640 Camera Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("CSI");
diff --git a/drivers/media/video/mxc/capture/ov5642.c b/drivers/media/video/mxc/capture/ov5642.c
new file mode 100644 (file)
index 0000000..63d85c6
--- /dev/null
@@ -0,0 +1,2105 @@
+/*
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
+#include <linux/fsl_devices.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-int-device.h>
+#include "mxc_v4l2_capture.h"
+
+#define OV5642_VOLTAGE_ANALOG               2800000
+#define OV5642_VOLTAGE_DIGITAL_CORE         1500000
+#define OV5642_VOLTAGE_DIGITAL_IO           1800000
+
+#define MIN_FPS 15
+#define MAX_FPS 30
+#define DEFAULT_FPS 30
+
+#define OV5642_XCLK_MIN 6000000
+#define OV5642_XCLK_MAX 24000000
+
+enum ov5642_mode {
+       ov5642_mode_MIN = 0,
+       ov5642_mode_VGA_640_480 = 0,
+       ov5642_mode_QVGA_320_240 = 1,
+       ov5642_mode_NTSC_720_480 = 2,
+       ov5642_mode_PAL_720_576 = 3,
+       ov5642_mode_720P_1280_720 = 4,
+       ov5642_mode_1080P_1920_1080 = 5,
+       ov5642_mode_QSXGA_2592_1944 = 6,
+       ov5642_mode_MAX = 6
+};
+
+enum ov5642_frame_rate {
+       ov5642_15_fps,
+       ov5642_30_fps
+};
+
+struct reg_value {
+       u16 u16RegAddr;
+       u8 u8Val;
+       u8 u8Mask;
+       u32 u32Delay_ms;
+};
+
+struct ov5642_mode_info {
+       enum ov5642_mode mode;
+       u32 width;
+       u32 height;
+       struct reg_value *init_data_ptr;
+       u32 init_data_size;
+};
+
+/*!
+ * Maintains the information on the current state of the sesor.
+ */
+struct sensor {
+       const struct ov5642_platform_data *platform_data;
+       struct v4l2_int_device *v4l2_int_device;
+       struct i2c_client *i2c_client;
+       struct v4l2_pix_format pix;
+       struct v4l2_captureparm streamcap;
+       bool on;
+
+       /* control settings */
+       int brightness;
+       int hue;
+       int contrast;
+       int saturation;
+       int red;
+       int green;
+       int blue;
+       int ae_mode;
+
+       u32 mclk;
+       int csi;
+} ov5642_data;
+
+static struct reg_value ov5642_setting_15fps_QSXGA_2592_1944[] = {
+       {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+       {0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0},
+       {0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x00, 0, 0},
+       {0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3030, 0x2b, 0, 0},
+       {0x3011, 0x08, 0, 0}, {0x3010, 0x00, 0, 0}, {0x3604, 0x60, 0, 0},
+       {0x3622, 0x60, 0, 0}, {0x3621, 0x09, 0, 0}, {0x3709, 0x00, 0, 0},
+       {0x4000, 0x21, 0, 0}, {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0},
+       {0x3605, 0x04, 0, 0}, {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0},
+       {0x300d, 0x22, 0, 0}, {0x3623, 0x22, 0, 0}, {0x5000, 0x4f, 0, 0},
+       {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+       {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5500, 0x0a, 0, 0},
+       {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, {0x5080, 0x08, 0, 0},
+       {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, {0x471d, 0x05, 0, 0},
+       {0x4708, 0x06, 0, 0}, {0x370c, 0xa0, 0, 0}, {0x3808, 0x0a, 0, 0},
+       {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, {0x380b, 0x98, 0, 0},
+       {0x380c, 0x0c, 0, 0}, {0x380d, 0x80, 0, 0}, {0x380e, 0x07, 0, 0},
+       {0x380f, 0xd0, 0, 0}, {0x5687, 0x94, 0, 0}, {0x501f, 0x00, 0, 0},
+       {0x5000, 0x4f, 0, 0}, {0x5001, 0xcf, 0, 0}, {0x4300, 0x30, 0, 0},
+       {0x4300, 0x30, 0, 0}, {0x460b, 0x35, 0, 0}, {0x471d, 0x00, 0, 0},
+       {0x3002, 0x0c, 0, 0}, {0x3002, 0x00, 0, 0}, {0x4713, 0x03, 0, 0},
+       {0x471c, 0x50, 0, 0}, {0x4721, 0x02, 0, 0}, {0x4402, 0x90, 0, 0},
+       {0x460c, 0x22, 0, 0}, {0x3815, 0x44, 0, 0}, {0x3503, 0x07, 0, 0},
+       {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+       {0x3818, 0xc8, 0, 0}, {0x3801, 0x88, 0, 0}, {0x3824, 0x11, 0, 0},
+       {0x3a00, 0x78, 0, 0}, {0x3a1a, 0x04, 0, 0}, {0x3a13, 0x30, 0, 0},
+       {0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0},
+       {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0},
+       {0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0}, {0x3a0d, 0x08, 0, 0},
+       {0x3a0e, 0x06, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0},
+       {0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0},
+       {0x3503, 0x00, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x32, 0, 0},
+       {0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x32, 0, 0}, {0x3a11, 0x80, 0, 0},
+       {0x3a1f, 0x20, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0},
+       {0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0},
+       {0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0},
+       {0x3a08, 0x09, 0, 0}, {0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0},
+       {0x3a0b, 0xd0, 0, 0}, {0x3a0d, 0x10, 0, 0}, {0x3a0e, 0x0d, 0, 0},
+       {0x4407, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x589b, 0x00, 0, 0},
+       {0x589a, 0xc0, 0, 0}, {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0},
+       {0x401c, 0x06, 0, 0}, {0x3825, 0xac, 0, 0}, {0x3827, 0x0c, 0, 0},
+       {0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+       {0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0},
+       {0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0},
+       {0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0},
+       {0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0},
+       {0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0},
+       {0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0},
+       {0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0},
+       {0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0},
+       {0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0},
+       {0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0},
+       {0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0},
+       {0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0},
+       {0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0},
+       {0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0},
+       {0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0},
+       {0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0},
+       {0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0},
+       {0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0},
+       {0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0},
+       {0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0},
+       {0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0},
+       {0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0},
+       {0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0},
+       {0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0},
+       {0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0},
+       {0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0},
+       {0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0},
+       {0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0},
+       {0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0},
+       {0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0},
+       {0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0},
+       {0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0},
+       {0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0},
+       {0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0},
+       {0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0},
+       {0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0},
+       {0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0},
+       {0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0},
+       {0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0},
+       {0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0},
+       {0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0},
+       {0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0},
+       {0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0},
+       {0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0},
+       {0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0},
+       {0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0},
+       {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0},
+       {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0},
+       {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0},
+       {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0},
+       {0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0},
+       {0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0},
+       {0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0},
+       {0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0},
+       {0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0},
+       {0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0},
+       {0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0},
+       {0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0},
+       {0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0},
+       {0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0},
+       {0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0},
+       {0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0},
+       {0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0},
+       {0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0},
+       {0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0},
+       {0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0},
+       {0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0},
+       {0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0},
+       {0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0},
+       {0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0},
+       {0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0},
+       {0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0},
+       {0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0},
+       {0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0},
+       {0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0},
+       {0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0},
+       {0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0},
+       {0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0},
+       {0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0},
+       {0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0},
+       {0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0},
+       {0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0},
+       {0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0},
+       {0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0},
+       {0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0},
+       {0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0},
+       {0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0},
+       {0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0},
+       {0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0},
+       {0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0},
+       {0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0},
+       {0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0},
+       {0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0},
+       {0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0},
+       {0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0},
+       {0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0},
+       {0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0},
+       {0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0},
+       {0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0},
+       {0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0},
+       {0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0},
+       {0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0},
+       {0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0},
+       {0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0},
+       {0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0},
+       {0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0},
+       {0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0},
+       {0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0},
+       {0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0},
+       {0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0},
+       {0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0},
+       {0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0},
+       {0x302b, 0x00, 0, 0}, {0x460c, 0x20, 0, 0}, {0x460b, 0x37, 0, 0},
+       {0x471c, 0xd0, 0, 0}, {0x471d, 0x05, 0, 0}, {0x3815, 0x01, 0, 0},
+       {0x3818, 0xc0, 0, 0}, {0x501f, 0x00, 0, 0}, {0x4300, 0x30, 0, 0},
+       {0x3002, 0x1c, 0, 0}, {0x3819, 0x80, 0, 0}, {0x5002, 0xe0, 0, 0},
+       {0x3012, 0x01, 0, 0},
+};
+
+static struct reg_value ov5642_setting_30fps_VGA_640_480[] = {
+       {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+       {0x3018, 0xfc, 0, 0}, {0x3615, 0xf0, 0, 0}, {0x3000, 0x00, 0, 0},
+       {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0},
+       {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0},
+       {0x3007, 0x37, 0, 0}, {0x3011, 0x09, 0, 0}, {0x3012, 0x02, 0, 0},
+       {0x3010, 0x00, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3815, 0x04, 0, 0},
+       {0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0},
+       {0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0},
+       {0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0},
+       {0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0},
+       {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0},
+       {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0},
+       {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+       {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0},
+       {0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0},
+       {0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0},
+       {0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0},
+       {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0},
+       {0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0},
+       {0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0},
+       {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+       {0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3825, 0xb0, 0, 0},
+       {0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0},
+       {0x380c, 0x07, 0, 0}, {0x380d, 0x2a, 0, 0}, {0x380e, 0x03, 0, 0},
+       {0x380f, 0xe8, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+       {0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0},
+       {0x3801, 0x80, 0, 0}, {0x3621, 0xc7, 0, 0}, {0x3801, 0x50, 0, 0},
+       {0x3803, 0x08, 0, 0}, {0x3827, 0x08, 0, 0}, {0x3810, 0x80, 0, 0},
+       {0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0},
+       {0x5683, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0},
+       {0x5686, 0x03, 0, 0}, {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0},
+       {0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0},
+       {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0},
+       {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0},
+       {0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0},
+       {0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0},
+       {0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0},
+       {0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0},
+       {0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0},
+       {0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0},
+       {0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0},
+       {0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0},
+       {0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0},
+       {0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0},
+       {0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0},
+       {0x3a1f, 0x10, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0},
+       {0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0},
+       {0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0},
+       {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0},
+       {0x3a0b, 0xa0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+       {0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0},
+       {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0},
+       {0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+       {0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0},
+       {0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0},
+       {0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0},
+       {0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0},
+       {0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0},
+       {0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0},
+       {0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0},
+       {0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0},
+       {0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0},
+       {0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0},
+       {0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0},
+       {0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0},
+       {0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0},
+       {0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0},
+       {0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0},
+       {0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0},
+       {0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0},
+       {0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0},
+       {0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0},
+       {0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0},
+       {0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0},
+       {0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0},
+       {0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0},
+       {0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0},
+       {0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0},
+       {0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0},
+       {0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0},
+       {0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0},
+       {0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0},
+       {0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0},
+       {0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0},
+       {0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0},
+       {0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0},
+       {0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0},
+       {0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0},
+       {0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0},
+       {0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0},
+       {0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0},
+       {0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0},
+       {0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0},
+       {0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0},
+       {0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0},
+       {0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0},
+       {0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0},
+       {0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0},
+       {0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0},
+       {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0},
+       {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0},
+       {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0},
+       {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0},
+       {0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0},
+       {0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0},
+       {0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0},
+       {0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0},
+       {0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0},
+       {0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0},
+       {0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0},
+       {0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0},
+       {0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0},
+       {0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0},
+       {0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0},
+       {0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0},
+       {0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0},
+       {0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0},
+       {0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0},
+       {0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0},
+       {0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0},
+       {0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0},
+       {0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0},
+       {0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0},
+       {0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0},
+       {0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0},
+       {0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0},
+       {0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0},
+       {0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0},
+       {0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0},
+       {0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0},
+       {0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0},
+       {0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0},
+       {0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0},
+       {0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0},
+       {0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0},
+       {0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0},
+       {0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0},
+       {0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0},
+       {0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0},
+       {0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0},
+       {0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0},
+       {0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0},
+       {0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0},
+       {0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0},
+       {0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0},
+       {0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0},
+       {0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0},
+       {0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0},
+       {0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0},
+       {0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0},
+       {0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0},
+       {0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0},
+       {0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0},
+       {0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0},
+       {0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0},
+       {0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0},
+       {0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0},
+       {0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0},
+       {0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0},
+       {0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0},
+       {0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0},
+       {0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0},
+       {0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0},
+       {0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0},
+       {0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0},
+       {0x302b, 0x00, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3a00, 0x78, 0, 0},
+};
+
+static struct reg_value ov5642_setting_30fps_QVGA_320_240[] = {
+       {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+       {0x3018, 0xfc, 0, 0}, {0x3615, 0xf0, 0, 0}, {0x3000, 0x00, 0, 0},
+       {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0},
+       {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0},
+       {0x3007, 0x37, 0, 0}, {0x3011, 0x09, 0, 0}, {0x3012, 0x02, 0, 0},
+       {0x3010, 0x00, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3815, 0x04, 0, 0},
+       {0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0},
+       {0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0},
+       {0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0},
+       {0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0},
+       {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0},
+       {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0},
+       {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+       {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0},
+       {0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0},
+       {0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0},
+       {0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0},
+       {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0},
+       {0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0},
+       {0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0},
+       {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+       {0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3825, 0xb0, 0, 0},
+       {0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0},
+       {0x380c, 0x07, 0, 0}, {0x380d, 0x2a, 0, 0}, {0x380e, 0x03, 0, 0},
+       {0x380f, 0xe8, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+       {0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0},
+       {0x3801, 0x80, 0, 0}, {0x3621, 0xc7, 0, 0}, {0x3801, 0x50, 0, 0},
+       {0x3803, 0x08, 0, 0}, {0x3827, 0x08, 0, 0}, {0x3810, 0x80, 0, 0},
+       {0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0},
+       {0x5683, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0},
+       {0x5686, 0x03, 0, 0}, {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0},
+       {0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0},
+       {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0},
+       {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0},
+       {0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0},
+       {0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0},
+       {0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0},
+       {0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0},
+       {0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0},
+       {0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0},
+       {0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0},
+       {0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0},
+       {0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0},
+       {0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0},
+       {0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0},
+       {0x3a1f, 0x10, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0},
+       {0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0},
+       {0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0},
+       {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0},
+       {0x3a0b, 0xa0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+       {0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0},
+       {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0},
+       {0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+       {0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0},
+       {0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0},
+       {0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0},
+       {0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0},
+       {0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0},
+       {0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0},
+       {0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0},
+       {0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0},
+       {0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0},
+       {0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0},
+       {0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0},
+       {0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0},
+       {0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0},
+       {0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0},
+       {0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0},
+       {0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0},
+       {0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0},
+       {0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0},
+       {0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0},
+       {0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0},
+       {0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0},
+       {0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0},
+       {0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0},
+       {0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0},
+       {0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0},
+       {0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0},
+       {0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0},
+       {0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0},
+       {0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0},
+       {0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0},
+       {0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0},
+       {0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0},
+       {0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0},
+       {0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0},
+       {0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0},
+       {0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0},
+       {0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0},
+       {0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0},
+       {0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0},
+       {0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0},
+       {0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0},
+       {0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0},
+       {0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0},
+       {0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0},
+       {0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0},
+       {0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0},
+       {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0},
+       {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0},
+       {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0},
+       {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0},
+       {0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0},
+       {0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0},
+       {0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0},
+       {0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0},
+       {0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0},
+       {0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0},
+       {0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0},
+       {0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0},
+       {0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0},
+       {0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0},
+       {0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0},
+       {0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0},
+       {0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0},
+       {0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0},
+       {0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0},
+       {0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0},
+       {0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0},
+       {0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0},
+       {0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0},
+       {0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0},
+       {0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0},
+       {0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0},
+       {0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0},
+       {0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0},
+       {0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0},
+       {0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0},
+       {0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0},
+       {0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0},
+       {0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0},
+       {0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0},
+       {0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0},
+       {0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0},
+       {0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0},
+       {0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0},
+       {0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0},
+       {0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0},
+       {0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0},
+       {0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0},
+       {0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0},
+       {0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0},
+       {0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0},
+       {0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0},
+       {0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0},
+       {0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0},
+       {0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0},
+       {0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0},
+       {0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0},
+       {0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0},
+       {0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0},
+       {0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0},
+       {0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0},
+       {0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0},
+       {0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0},
+       {0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0},
+       {0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0},
+       {0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0},
+       {0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0},
+       {0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0},
+       {0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0},
+       {0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0},
+       {0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0},
+       {0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0},
+       {0x302b, 0x00, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3808, 0x01, 0, 0},
+       {0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0}, {0x380b, 0xf0, 0, 0},
+};
+
+static struct reg_value ov5642_setting_30fps_NTSC_720_480[] = {
+       {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+       {0x3018, 0xfc, 0, 0}, {0x3615, 0xf0, 0, 0}, {0x3000, 0x00, 0, 0},
+       {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0},
+       {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0},
+       {0x3007, 0x37, 0, 0}, {0x3011, 0x09, 0, 0}, {0x3012, 0x02, 0, 0},
+       {0x3010, 0x00, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3815, 0x04, 0, 0},
+       {0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0},
+       {0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0},
+       {0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0},
+       {0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0},
+       {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0},
+       {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0},
+       {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+       {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0},
+       {0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0},
+       {0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0},
+       {0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0},
+       {0x3809, 0xd0, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0},
+       {0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0},
+       {0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0},
+       {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+       {0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3825, 0xb0, 0, 0},
+       {0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0},
+       {0x380c, 0x07, 0, 0}, {0x380d, 0x2a, 0, 0}, {0x380e, 0x03, 0, 0},
+       {0x380f, 0xe8, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+       {0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0},
+       {0x3801, 0x80, 0, 0}, {0x3621, 0xc7, 0, 0}, {0x3801, 0x50, 0, 0},
+       {0x3803, 0x08, 0, 0}, {0x3827, 0x3c, 0, 0}, {0x3810, 0x80, 0, 0},
+       {0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0},
+       {0x5683, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0x58, 0, 0},
+       {0x5686, 0x03, 0, 0}, {0x5687, 0x58, 0, 0}, {0x3a00, 0x78, 0, 0},
+       {0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0},
+       {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0},
+       {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0},
+       {0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0},
+       {0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0},
+       {0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0},
+       {0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0},
+       {0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0},
+       {0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0},
+       {0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0},
+       {0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0},
+       {0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0},
+       {0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0},
+       {0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0},
+       {0x3a1f, 0x10, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0},
+       {0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0},
+       {0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0},
+       {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0},
+       {0x3a0b, 0xa0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+       {0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0},
+       {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0},
+       {0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+       {0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0},
+       {0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0},
+       {0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0},
+       {0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0},
+       {0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0},
+       {0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0},
+       {0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0},
+       {0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0},
+       {0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0},
+       {0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0},
+       {0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0},
+       {0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0},
+       {0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0},
+       {0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0},
+       {0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0},
+       {0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0},
+       {0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0},
+       {0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0},
+       {0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0},
+       {0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0},
+       {0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0},
+       {0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0},
+       {0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0},
+       {0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0},
+       {0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0},
+       {0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0},
+       {0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0},
+       {0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0},
+       {0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0},
+       {0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0},
+       {0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0},
+       {0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0},
+       {0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0},
+       {0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0},
+       {0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0},
+       {0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0},
+       {0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0},
+       {0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0},
+       {0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0},
+       {0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0},
+       {0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0},
+       {0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0},
+       {0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0},
+       {0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0},
+       {0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0},
+       {0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0},
+       {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0},
+       {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0},
+       {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0},
+       {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0},
+       {0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0},
+       {0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0},
+       {0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0},
+       {0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0},
+       {0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0},
+       {0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0},
+       {0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0},
+       {0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0},
+       {0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0},
+       {0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0},
+       {0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0},
+       {0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0},
+       {0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0},
+       {0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0},
+       {0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0},
+       {0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0},
+       {0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0},
+       {0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0},
+       {0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0},
+       {0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0},
+       {0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0},
+       {0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0},
+       {0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0},
+       {0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0},
+       {0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0},
+       {0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0},
+       {0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0},
+       {0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0},
+       {0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0},
+       {0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0},
+       {0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0},
+       {0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0},
+       {0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0},
+       {0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0},
+       {0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0},
+       {0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0},
+       {0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0},
+       {0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0},
+       {0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0},
+       {0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0},
+       {0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0},
+       {0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0},
+       {0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0},
+       {0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0},
+       {0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0},
+       {0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0},
+       {0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0},
+       {0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0},
+       {0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0},
+       {0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0},
+       {0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0},
+       {0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0},
+       {0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0},
+       {0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0},
+       {0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0},
+       {0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0},
+       {0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0},
+       {0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0},
+       {0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0},
+       {0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0},
+       {0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0},
+       {0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0},
+       {0x302b, 0x00, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3a00, 0x78, 0, 0},
+};
+
+static struct reg_value ov5642_setting_30fps_PAL_720_576[] = {
+       {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+       {0x3018, 0xfc, 0, 0}, {0x3615, 0xf0, 0, 0}, {0x3000, 0x00, 0, 0},
+       {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0},
+       {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0},
+       {0x3007, 0x37, 0, 0}, {0x3011, 0x09, 0, 0}, {0x3012, 0x02, 0, 0},
+       {0x3010, 0x00, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3815, 0x04, 0, 0},
+       {0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0},
+       {0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0},
+       {0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0},
+       {0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0},
+       {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0},
+       {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0},
+       {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+       {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0},
+       {0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0},
+       {0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0},
+       {0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0},
+       {0x3809, 0xd0, 0, 0}, {0x380a, 0x02, 0, 0}, {0x380b, 0x40, 0, 0},
+       {0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0},
+       {0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0},
+       {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+       {0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3825, 0xd8, 0, 0},
+       {0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0},
+       {0x380c, 0x07, 0, 0}, {0x380d, 0x2a, 0, 0}, {0x380e, 0x03, 0, 0},
+       {0x380f, 0xe8, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+       {0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0},
+       {0x3801, 0x80, 0, 0}, {0x3621, 0xc7, 0, 0}, {0x3801, 0x50, 0, 0},
+       {0x3803, 0x08, 0, 0}, {0x3827, 0x3c, 0, 0}, {0x3810, 0x80, 0, 0},
+       {0x3804, 0x04, 0, 0}, {0x3805, 0xb0, 0, 0}, {0x5682, 0x05, 0, 0},
+       {0x5683, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0x58, 0, 0},
+       {0x5686, 0x03, 0, 0}, {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0},
+       {0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0},
+       {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0},
+       {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0},
+       {0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0},
+       {0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0},
+       {0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0},
+       {0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0},
+       {0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0},
+       {0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0},
+       {0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0},
+       {0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0},
+       {0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0},
+       {0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0},
+       {0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0},
+       {0x3a1f, 0x10, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0},
+       {0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0},
+       {0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0},
+       {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0},
+       {0x3a0b, 0xa0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+       {0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0},
+       {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0},
+       {0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+       {0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0},
+       {0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0},
+       {0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0},
+       {0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0},
+       {0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0},
+       {0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0},
+       {0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0},
+       {0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0},
+       {0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0},
+       {0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0},
+       {0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0},
+       {0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0},
+       {0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0},
+       {0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0},
+       {0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0},
+       {0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0},
+       {0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0},
+       {0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0},
+       {0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0},
+       {0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0},
+       {0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0},
+       {0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0},
+       {0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0},
+       {0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0},
+       {0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0},
+       {0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0},
+       {0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0},
+       {0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0},
+       {0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0},
+       {0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0},
+       {0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0},
+       {0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0},
+       {0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0},
+       {0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0},
+       {0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0},
+       {0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0},
+       {0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0},
+       {0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0},
+       {0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0},
+       {0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0},
+       {0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0},
+       {0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0},
+       {0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0},
+       {0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0},
+       {0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0},
+       {0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0},
+       {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0},
+       {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0},
+       {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0},
+       {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0},
+       {0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0},
+       {0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0},
+       {0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0},
+       {0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0},
+       {0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0},
+       {0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0},
+       {0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0},
+       {0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0},
+       {0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0},
+       {0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0},
+       {0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0},
+       {0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0},
+       {0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0},
+       {0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0},
+       {0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0},
+       {0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0},
+       {0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0},
+       {0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0},
+       {0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0},
+       {0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0},
+       {0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0},
+       {0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0},
+       {0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0},
+       {0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0},
+       {0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0},
+       {0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0},
+       {0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0},
+       {0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0},
+       {0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0},
+       {0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0},
+       {0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0},
+       {0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0},
+       {0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0},
+       {0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0},
+       {0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0},
+       {0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0},
+       {0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0},
+       {0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0},
+       {0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0},
+       {0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0},
+       {0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0},
+       {0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0},
+       {0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0},
+       {0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0},
+       {0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0},
+       {0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0},
+       {0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0},
+       {0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0},
+       {0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0},
+       {0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0},
+       {0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0},
+       {0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0},
+       {0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0},
+       {0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0},
+       {0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0},
+       {0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0},
+       {0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0},
+       {0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0},
+       {0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0},
+       {0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0},
+       {0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0},
+       {0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0},
+       {0x302b, 0x00, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3a00, 0x78, 0, 0},
+};
+
+static struct reg_value ov5642_setting_30fps_720P_1280_720[] = {
+       {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+       {0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0},
+       {0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x00, 0, 0},
+       {0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3030, 0x2b, 0, 0},
+       {0x3011, 0x08, 0, 0}, {0x3010, 0x10, 0, 0}, {0x3604, 0x60, 0, 0},
+       {0x3622, 0x60, 0, 0}, {0x3621, 0x09, 0, 0}, {0x3709, 0x00, 0, 0},
+       {0x4000, 0x21, 0, 0}, {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0},
+       {0x3605, 0x04, 0, 0}, {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0},
+       {0x300d, 0x22, 0, 0}, {0x3623, 0x22, 0, 0}, {0x5000, 0x4f, 0, 0},
+       {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+       {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5500, 0x0a, 0, 0},
+       {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, {0x5080, 0x08, 0, 0},
+       {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, {0x471d, 0x05, 0, 0},
+       {0x4708, 0x06, 0, 0}, {0x370c, 0xa0, 0, 0}, {0x3808, 0x0a, 0, 0},
+       {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, {0x380b, 0x98, 0, 0},
+       {0x380c, 0x0c, 0, 0}, {0x380d, 0x80, 0, 0}, {0x380e, 0x07, 0, 0},
+       {0x380f, 0xd0, 0, 0}, {0x5687, 0x94, 0, 0}, {0x501f, 0x00, 0, 0},
+       {0x5000, 0x4f, 0, 0}, {0x5001, 0xcf, 0, 0}, {0x4300, 0x30, 0, 0},
+       {0x4300, 0x30, 0, 0}, {0x460b, 0x35, 0, 0}, {0x471d, 0x00, 0, 0},
+       {0x3002, 0x0c, 0, 0}, {0x3002, 0x00, 0, 0}, {0x4713, 0x03, 0, 0},
+       {0x471c, 0x50, 0, 0}, {0x4721, 0x02, 0, 0}, {0x4402, 0x90, 0, 0},
+       {0x460c, 0x22, 0, 0}, {0x3815, 0x44, 0, 0}, {0x3503, 0x07, 0, 0},
+       {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+       {0x3818, 0xc8, 0, 0}, {0x3801, 0x88, 0, 0}, {0x3824, 0x11, 0, 0},
+       {0x3a00, 0x78, 0, 0}, {0x3a1a, 0x04, 0, 0}, {0x3a13, 0x30, 0, 0},
+       {0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0},
+       {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0},
+       {0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0}, {0x3a0d, 0x08, 0, 0},
+       {0x3a0e, 0x06, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0},
+       {0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0},
+       {0x3503, 0x00, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x32, 0, 0},
+       {0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x32, 0, 0}, {0x3a11, 0x80, 0, 0},
+       {0x3a1f, 0x20, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0},
+       {0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0},
+       {0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0},
+       {0x3a08, 0x09, 0, 0}, {0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0},
+       {0x3a0b, 0xd0, 0, 0}, {0x3a0d, 0x10, 0, 0}, {0x3a0e, 0x0d, 0, 0},
+       {0x4407, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x589b, 0x00, 0, 0},
+       {0x589a, 0xc0, 0, 0}, {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0},
+       {0x401c, 0x06, 0, 0}, {0x3825, 0xac, 0, 0}, {0x3827, 0x0c, 0, 0},
+       {0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+       {0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0},
+       {0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0},
+       {0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0},
+       {0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0},
+       {0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0},
+       {0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0},
+       {0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0},
+       {0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0},
+       {0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0},
+       {0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0},
+       {0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0},
+       {0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0},
+       {0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0},
+       {0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0},
+       {0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0},
+       {0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0},
+       {0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0},
+       {0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0},
+       {0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0},
+       {0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0},
+       {0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0},
+       {0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0},
+       {0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0},
+       {0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0},
+       {0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0},
+       {0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0},
+       {0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0},
+       {0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0},
+       {0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0},
+       {0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0},
+       {0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0},
+       {0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0},
+       {0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0},
+       {0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0},
+       {0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0},
+       {0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0},
+       {0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0},
+       {0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0},
+       {0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0},
+       {0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0},
+       {0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0},
+       {0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0},
+       {0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0},
+       {0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0},
+       {0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0},
+       {0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0},
+       {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0},
+       {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0},
+       {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0},
+       {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0},
+       {0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0},
+       {0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0},
+       {0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0},
+       {0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0},
+       {0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0},
+       {0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0},
+       {0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0},
+       {0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0},
+       {0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0},
+       {0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0},
+       {0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0},
+       {0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0},
+       {0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0},
+       {0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0},
+       {0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0},
+       {0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0},
+       {0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0},
+       {0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0},
+       {0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0},
+       {0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0},
+       {0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0},
+       {0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0},
+       {0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0},
+       {0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0},
+       {0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0},
+       {0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0},
+       {0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0},
+       {0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0},
+       {0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0},
+       {0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0},
+       {0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0},
+       {0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0},
+       {0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0},
+       {0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0},
+       {0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0},
+       {0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0},
+       {0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0},
+       {0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0},
+       {0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0},
+       {0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0},
+       {0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0},
+       {0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0},
+       {0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0},
+       {0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0},
+       {0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0},
+       {0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0},
+       {0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0},
+       {0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0},
+       {0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0},
+       {0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0},
+       {0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0},
+       {0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0},
+       {0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0},
+       {0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0},
+       {0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0},
+       {0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0},
+       {0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0},
+       {0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0},
+       {0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0},
+       {0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0},
+       {0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0},
+       {0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0},
+       {0x302b, 0x00, 0, 0}, {0x3503, 0x07, 0, 0}, {0x3011, 0x08, 0, 0},
+       {0x350c, 0x02, 0, 0}, {0x350d, 0xe4, 0, 0}, {0x3621, 0xc9, 0, 0},
+       {0x370a, 0x81, 0, 0}, {0x3803, 0x08, 0, 0}, {0x3804, 0x05, 0, 0},
+       {0x3805, 0x00, 0, 0}, {0x3806, 0x02, 0, 0}, {0x3807, 0xd0, 0, 0},
+       {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0},
+       {0x380b, 0xd0, 0, 0}, {0x380c, 0x08, 0, 0}, {0x380d, 0x72, 0, 0},
+       {0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3810, 0xc0, 0, 0},
+       {0x3818, 0xc9, 0, 0}, {0x381c, 0x10, 0, 0}, {0x381d, 0xa0, 0, 0},
+       {0x381e, 0x05, 0, 0}, {0x381f, 0xb0, 0, 0}, {0x3820, 0x00, 0, 0},
+       {0x3821, 0x00, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3a08, 0x1b, 0, 0},
+       {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x17, 0, 0}, {0x3a0b, 0x20, 0, 0},
+       {0x3a0d, 0x02, 0, 0}, {0x3a0e, 0x01, 0, 0}, {0x401c, 0x04, 0, 0},
+       {0x5682, 0x05, 0, 0}, {0x5683, 0x00, 0, 0}, {0x5686, 0x02, 0, 0},
+       {0x5687, 0xcc, 0, 0}, {0x5001, 0x7f, 0, 0}, {0x589b, 0x06, 0, 0},
+       {0x589a, 0xc5, 0, 0}, {0x3503, 0x00, 0, 0}, {0x3010, 0x10, 0, 0},
+       {0x460c, 0x20, 0, 0}, {0x460b, 0x37, 0, 0}, {0x471c, 0xd0, 0, 0},
+       {0x471d, 0x05, 0, 0}, {0x3815, 0x01, 0, 0}, {0x3818, 0x00, 0x08, 0},
+       {0x501f, 0x00, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3002, 0x1c, 0, 0},
+       {0x3819, 0x80, 0, 0}, {0x5002, 0xe0, 0, 0},
+};
+
+static struct reg_value ov5642_setting_30fps_1080P_1920_1080[] = {
+       {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+       {0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0},
+       {0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x00, 0, 0},
+       {0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3030, 0x2b, 0, 0},
+       {0x3011, 0x08, 0, 0}, {0x3010, 0x10, 0, 0}, {0x3604, 0x60, 0, 0},
+       {0x3622, 0x60, 0, 0}, {0x3621, 0x09, 0, 0}, {0x3709, 0x00, 0, 0},
+       {0x4000, 0x21, 0, 0}, {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0},
+       {0x3605, 0x04, 0, 0}, {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0},
+       {0x300d, 0x22, 0, 0}, {0x3623, 0x22, 0, 0}, {0x5000, 0x4f, 0, 0},
+       {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+       {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5500, 0x0a, 0, 0},
+       {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, {0x5080, 0x08, 0, 0},
+       {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, {0x471d, 0x05, 0, 0},
+       {0x4708, 0x06, 0, 0}, {0x370c, 0xa0, 0, 0}, {0x3808, 0x0a, 0, 0},
+       {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, {0x380b, 0x98, 0, 0},
+       {0x380c, 0x0c, 0, 0}, {0x380d, 0x80, 0, 0}, {0x380e, 0x07, 0, 0},
+       {0x380f, 0xd0, 0, 0}, {0x5687, 0x94, 0, 0}, {0x501f, 0x00, 0, 0},
+       {0x5000, 0x4f, 0, 0}, {0x5001, 0xcf, 0, 0}, {0x4300, 0x30, 0, 0},
+       {0x4300, 0x30, 0, 0}, {0x460b, 0x35, 0, 0}, {0x471d, 0x00, 0, 0},
+       {0x3002, 0x0c, 0, 0}, {0x3002, 0x00, 0, 0}, {0x4713, 0x03, 0, 0},
+       {0x471c, 0x50, 0, 0}, {0x4721, 0x02, 0, 0}, {0x4402, 0x90, 0, 0},
+       {0x460c, 0x22, 0, 0}, {0x3815, 0x44, 0, 0}, {0x3503, 0x07, 0, 0},
+       {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+       {0x3818, 0xc8, 0, 0}, {0x3801, 0x88, 0, 0}, {0x3824, 0x11, 0, 0},
+       {0x3a00, 0x78, 0, 0}, {0x3a1a, 0x04, 0, 0}, {0x3a13, 0x30, 0, 0},
+       {0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0},
+       {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0},
+       {0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0}, {0x3a0d, 0x08, 0, 0},
+       {0x3a0e, 0x06, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0},
+       {0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0},
+       {0x3503, 0x00, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x32, 0, 0},
+       {0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x32, 0, 0}, {0x3a11, 0x80, 0, 0},
+       {0x3a1f, 0x20, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0},
+       {0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0},
+       {0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0},
+       {0x3a08, 0x09, 0, 0}, {0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0},
+       {0x3a0b, 0xd0, 0, 0}, {0x3a0d, 0x10, 0, 0}, {0x3a0e, 0x0d, 0, 0},
+       {0x4407, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x589b, 0x00, 0, 0},
+       {0x589a, 0xc0, 0, 0}, {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0},
+       {0x401c, 0x06, 0, 0}, {0x3825, 0xac, 0, 0}, {0x3827, 0x0c, 0, 0},
+       {0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+       {0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0},
+       {0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0},
+       {0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0},
+       {0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0},
+       {0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0},
+       {0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0},
+       {0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0},
+       {0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0},
+       {0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0},
+       {0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0},
+       {0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0},
+       {0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0},
+       {0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0},
+       {0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0},
+       {0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0},
+       {0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0},
+       {0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0},
+       {0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0},
+       {0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0},
+       {0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0},
+       {0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0},
+       {0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0},
+       {0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0},
+       {0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0},
+       {0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0},
+       {0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0},
+       {0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0},
+       {0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0},
+       {0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0},
+       {0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0},
+       {0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0},
+       {0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0},
+       {0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0},
+       {0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0},
+       {0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0},
+       {0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0},
+       {0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0},
+       {0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0},
+       {0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0},
+       {0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0},
+       {0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0},
+       {0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0},
+       {0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0},
+       {0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0},
+       {0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0},
+       {0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0},
+       {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0},
+       {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0},
+       {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0},
+       {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0},
+       {0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0},
+       {0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0},
+       {0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0},
+       {0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0},
+       {0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0},
+       {0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0},
+       {0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0},
+       {0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0},
+       {0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0},
+       {0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0},
+       {0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0},
+       {0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0},
+       {0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0},
+       {0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0},
+       {0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0},
+       {0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0},
+       {0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0},
+       {0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0},
+       {0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0},
+       {0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0},
+       {0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0},
+       {0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0},
+       {0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0},
+       {0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0},
+       {0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0},
+       {0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0},
+       {0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0},
+       {0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0},
+       {0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0},
+       {0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0},
+       {0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0},
+       {0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0},
+       {0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0},
+       {0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0},
+       {0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0},
+       {0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0},
+       {0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0},
+       {0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0},
+       {0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0},
+       {0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0},
+       {0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0},
+       {0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0},
+       {0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0},
+       {0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0},
+       {0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0},
+       {0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0},
+       {0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0},
+       {0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0},
+       {0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0},
+       {0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0},
+       {0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0},
+       {0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0},
+       {0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0},
+       {0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0},
+       {0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0},
+       {0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0},
+       {0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0},
+       {0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0},
+       {0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0},
+       {0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0},
+       {0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0},
+       {0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0},
+       {0x302b, 0x00, 0, 0}, {0x3503, 0x07, 0, 0}, {0x3011, 0x07, 0, 0},
+       {0x350c, 0x04, 0, 0}, {0x350d, 0x58, 0, 0}, {0x3801, 0x8a, 0, 0},
+       {0x3803, 0x0a, 0, 0}, {0x3804, 0x07, 0, 0}, {0x3805, 0x80, 0, 0},
+       {0x3806, 0x04, 0, 0}, {0x3807, 0x38, 0, 0}, {0x3808, 0x07, 0, 0},
+       {0x3809, 0x80, 0, 0}, {0x380a, 0x04, 0, 0}, {0x380b, 0x38, 0, 0},
+       {0x380c, 0x09, 0, 0}, {0x380d, 0xd6, 0, 0}, {0x380e, 0x04, 0, 0},
+       {0x380f, 0x58, 0, 0}, {0x381c, 0x11, 0, 0}, {0x381d, 0xba, 0, 0},
+       {0x381e, 0x04, 0, 0}, {0x381f, 0x48, 0, 0}, {0x3820, 0x04, 0, 0},
+       {0x3821, 0x18, 0, 0}, {0x3a08, 0x14, 0, 0}, {0x3a09, 0xe0, 0, 0},
+       {0x3a0a, 0x11, 0, 0}, {0x3a0b, 0x60, 0, 0}, {0x3a0d, 0x04, 0, 0},
+       {0x3a0e, 0x03, 0, 0}, {0x5682, 0x07, 0, 0}, {0x5683, 0x60, 0, 0},
+       {0x5686, 0x04, 0, 0}, {0x5687, 0x1c, 0, 0}, {0x5001, 0x7f, 0, 0},
+       {0x3503, 0x00, 0, 0}, {0x3010, 0x00, 0, 0}, {0x460c, 0x20, 0, 0},
+       {0x460b, 0x37, 0, 0}, {0x471c, 0xd0, 0, 0}, {0x471d, 0x05, 0, 0},
+       {0x3815, 0x01, 0, 0}, {0x3818, 0x00, 0x08, 0}, {0x501f, 0x00, 0, 0},
+       {0x4300, 0x30, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3819, 0x80, 0, 0},
+       {0x5002, 0xe0, 0, 0},
+};
+
+static struct ov5642_mode_info ov5642_mode_info_data[2][ov5642_mode_MAX + 1] = {
+       {
+               {ov5642_mode_VGA_640_480, 0, 0, NULL, 0},
+               {ov5642_mode_QVGA_320_240, 0, 0, NULL, 0},
+               {ov5642_mode_NTSC_720_480, 0, 0, NULL, 0},
+               {ov5642_mode_PAL_720_576, 0, 0, NULL, 0},
+               {ov5642_mode_720P_1280_720, 0, 0, NULL, 0},
+               {ov5642_mode_1080P_1920_1080, 0, 0, NULL, 0},
+               {ov5642_mode_QSXGA_2592_1944, 2592, 1944,
+               ov5642_setting_15fps_QSXGA_2592_1944,
+               ARRAY_SIZE(ov5642_setting_15fps_QSXGA_2592_1944)},
+       },
+       {
+               {ov5642_mode_VGA_640_480,    640,  480,
+               ov5642_setting_30fps_VGA_640_480,
+               ARRAY_SIZE(ov5642_setting_30fps_VGA_640_480)},
+               {ov5642_mode_QVGA_320_240,   320,  240,
+               ov5642_setting_30fps_QVGA_320_240,
+               ARRAY_SIZE(ov5642_setting_30fps_QVGA_320_240)},
+               {ov5642_mode_NTSC_720_480,   720, 480,
+               ov5642_setting_30fps_NTSC_720_480,
+               ARRAY_SIZE(ov5642_setting_30fps_NTSC_720_480)},
+               {ov5642_mode_PAL_720_576,    720, 576,
+               ov5642_setting_30fps_PAL_720_576,
+               ARRAY_SIZE(ov5642_setting_30fps_PAL_720_576)},
+               {ov5642_mode_720P_1280_720,  1280, 720,
+               ov5642_setting_30fps_720P_1280_720,
+               ARRAY_SIZE(ov5642_setting_30fps_720P_1280_720)},
+               {ov5642_mode_1080P_1920_1080,  1920, 1080,
+               ov5642_setting_30fps_1080P_1920_1080,
+               ARRAY_SIZE(ov5642_setting_30fps_1080P_1920_1080)},
+               {ov5642_mode_QSXGA_2592_1944, 0, 0, NULL, 0},
+       },
+};
+
+static struct regulator *io_regulator;
+static struct regulator *core_regulator;
+static struct regulator *analog_regulator;
+static struct regulator *gpo_regulator;
+static struct mxc_camera_platform_data *camera_plat;
+
+static int ov5642_probe(struct i2c_client *adapter,
+                               const struct i2c_device_id *device_id);
+static int ov5642_remove(struct i2c_client *client);
+
+static s32 ov5642_read_reg(u16 reg, u8 *val);
+static s32 ov5642_write_reg(u16 reg, u8 val);
+
+static const struct i2c_device_id ov5642_id[] = {
+       {"ov5642", 0},
+       {},
+};
+
+MODULE_DEVICE_TABLE(i2c, ov5642_id);
+
+static struct i2c_driver ov5642_i2c_driver = {
+       .driver = {
+                 .owner = THIS_MODULE,
+                 .name  = "ov5642",
+                 },
+       .probe  = ov5642_probe,
+       .remove = ov5642_remove,
+       .id_table = ov5642_id,
+};
+
+extern void gpio_sensor_active(unsigned int csi_index);
+extern void gpio_sensor_inactive(unsigned int csi);
+
+static s32 ov5642_write_reg(u16 reg, u8 val)
+{
+       u8 au8Buf[3] = {0};
+
+       au8Buf[0] = reg >> 8;
+       au8Buf[1] = reg & 0xff;
+       au8Buf[2] = val;
+
+       if (i2c_master_send(ov5642_data.i2c_client, au8Buf, 3) < 0) {
+               pr_err("%s:write reg error:reg=%x,val=%x\n",
+                       __func__, reg, val);
+               return -1;
+       }
+
+       return 0;
+}
+
+static s32 ov5642_read_reg(u16 reg, u8 *val)
+{
+       u8 au8RegBuf[2] = {0};
+       u8 u8RdVal = 0;
+
+       au8RegBuf[0] = reg >> 8;
+       au8RegBuf[1] = reg & 0xff;
+
+       if (2 != i2c_master_send(ov5642_data.i2c_client, au8RegBuf, 2)) {
+               pr_err("%s:write reg error:reg=%x\n",
+                               __func__, reg);
+               return -1;
+       }
+
+       if (1 != i2c_master_recv(ov5642_data.i2c_client, &u8RdVal, 1)) {
+               pr_err("%s:read reg error:reg=%x,val=%x\n",
+                               __func__, reg, u8RdVal);
+               return -1;
+       }
+
+       *val = u8RdVal;
+
+       return u8RdVal;
+}
+
+static int ov5642_init_mode(enum ov5642_frame_rate frame_rate,
+                           enum ov5642_mode mode)
+{
+       struct reg_value *pModeSetting = NULL;
+       s32 i = 0;
+       s32 iModeSettingArySize = 0;
+       register u32 Delay_ms = 0;
+       register u16 RegAddr = 0;
+       register u8 Mask = 0;
+       register u8 Val = 0;
+       u8 RegVal = 0;
+       int retval = 0;
+
+       if (mode > ov5642_mode_MAX || mode < ov5642_mode_MIN) {
+               pr_err("Wrong ov5642 mode detected!\n");
+               return -1;
+       }
+
+       pModeSetting = ov5642_mode_info_data[frame_rate][mode].init_data_ptr;
+       iModeSettingArySize =
+               ov5642_mode_info_data[frame_rate][mode].init_data_size;
+
+       ov5642_data.pix.width = ov5642_mode_info_data[frame_rate][mode].width;
+       ov5642_data.pix.height = ov5642_mode_info_data[frame_rate][mode].height;
+
+       if (ov5642_data.pix.width == 0 || ov5642_data.pix.height == 0 ||
+           pModeSetting == NULL || iModeSettingArySize == 0)
+               return -EINVAL;
+
+       for (i = 0; i < iModeSettingArySize; ++i, ++pModeSetting) {
+               Delay_ms = pModeSetting->u32Delay_ms;
+               RegAddr = pModeSetting->u16RegAddr;
+               Val = pModeSetting->u8Val;
+               Mask = pModeSetting->u8Mask;
+
+               if (Mask) {
+                       retval = ov5642_read_reg(RegAddr, &RegVal);
+                       if (retval < 0)
+                               goto err;
+
+                       RegVal &= ~(u8)Mask;
+                       Val &= Mask;
+                       Val |= RegVal;
+               }
+
+               retval = ov5642_write_reg(RegAddr, Val);
+               if (retval < 0)
+                       goto err;
+
+               if (Delay_ms)
+                       msleep(Delay_ms);
+       }
+err:
+       return retval;
+}
+
+/* --------------- IOCTL functions from v4l2_int_ioctl_desc --------------- */
+
+static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p)
+{
+       if (s == NULL) {
+               pr_err("   ERROR!! no slave device set!\n");
+               return -1;
+       }
+
+       memset(p, 0, sizeof(*p));
+       p->u.bt656.clock_curr = ov5642_data.mclk;
+       pr_debug("   clock_curr=mclk=%d\n", ov5642_data.mclk);
+       p->if_type = V4L2_IF_TYPE_BT656;
+       p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT;
+       p->u.bt656.clock_min = OV5642_XCLK_MIN;
+       p->u.bt656.clock_max = OV5642_XCLK_MAX;
+       p->u.bt656.bt_sync_correct = 1;  /* Indicate external vsync */
+
+       return 0;
+}
+
+/*!
+ * ioctl_s_power - V4L2 sensor interface handler for VIDIOC_S_POWER ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @on: indicates power mode (on or off)
+ *
+ * Turns the power on or off, depending on the value of on and returns the
+ * appropriate error code.
+ */
+static int ioctl_s_power(struct v4l2_int_device *s, int on)
+{
+       struct sensor *sensor = s->priv;
+
+       if (on && !sensor->on) {
+               gpio_sensor_active(ov5642_data.csi);
+               if (io_regulator)
+                       if (regulator_enable(io_regulator) != 0)
+                               return -EIO;
+               if (core_regulator)
+                       if (regulator_enable(core_regulator) != 0)
+                               return -EIO;
+               if (gpo_regulator)
+                       if (regulator_enable(gpo_regulator) != 0)
+                               return -EIO;
+               if (analog_regulator)
+                       if (regulator_enable(analog_regulator) != 0)
+                               return -EIO;
+               /* Make sure power on */
+               if (camera_plat->pwdn)
+                       camera_plat->pwdn(0);
+
+       } else if (!on && sensor->on) {
+               if (analog_regulator)
+                       regulator_disable(analog_regulator);
+               if (core_regulator)
+                       regulator_disable(core_regulator);
+               if (io_regulator)
+                       regulator_disable(io_regulator);
+               if (gpo_regulator)
+                       regulator_disable(gpo_regulator);
+               gpio_sensor_inactive(ov5642_data.csi);
+       }
+
+       sensor->on = on;
+
+       return 0;
+}
+
+/*!
+ * ioctl_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure
+ *
+ * Returns the sensor's video CAPTURE parameters.
+ */
+static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+       struct sensor *sensor = s->priv;
+       struct v4l2_captureparm *cparm = &a->parm.capture;
+       int ret = 0;
+
+       switch (a->type) {
+       /* This is the only case currently handled. */
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               memset(a, 0, sizeof(*a));
+               a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+               cparm->capability = sensor->streamcap.capability;
+               cparm->timeperframe = sensor->streamcap.timeperframe;
+               cparm->capturemode = sensor->streamcap.capturemode;
+               ret = 0;
+               break;
+
+       /* These are all the possible cases. */
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+       case V4L2_BUF_TYPE_VBI_OUTPUT:
+       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+               ret = -EINVAL;
+               break;
+
+       default:
+               pr_debug("   type is unknown - %d\n", a->type);
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+/*!
+ * ioctl_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure
+ *
+ * Configures the sensor to use the input parameters, if possible.  If
+ * not possible, reverts to the old parameters and returns the
+ * appropriate error code.
+ */
+static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+       struct sensor *sensor = s->priv;
+       struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe;
+       u32 tgt_fps;    /* target frames per secound */
+       enum ov5642_frame_rate frame_rate;
+       int ret = 0;
+
+       /* Make sure power on */
+       if (camera_plat->pwdn)
+               camera_plat->pwdn(0);
+
+       switch (a->type) {
+       /* This is the only case currently handled. */
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               /* Check that the new frame rate is allowed. */
+               if ((timeperframe->numerator == 0) ||
+                   (timeperframe->denominator == 0)) {
+                       timeperframe->denominator = DEFAULT_FPS;
+                       timeperframe->numerator = 1;
+               }
+
+               tgt_fps = timeperframe->denominator /
+                         timeperframe->numerator;
+
+               if (tgt_fps > MAX_FPS) {
+                       timeperframe->denominator = MAX_FPS;
+                       timeperframe->numerator = 1;
+               } else if (tgt_fps < MIN_FPS) {
+                       timeperframe->denominator = MIN_FPS;
+                       timeperframe->numerator = 1;
+               }
+
+               /* Actual frame rate we use */
+               tgt_fps = timeperframe->denominator /
+                         timeperframe->numerator;
+
+               if (tgt_fps == 15)
+                       frame_rate = ov5642_15_fps;
+               else if (tgt_fps == 30)
+                       frame_rate = ov5642_30_fps;
+               else {
+                       pr_err(" The camera frame rate is not supported!\n");
+                       return -EINVAL;
+               }
+
+               sensor->streamcap.timeperframe = *timeperframe;
+               sensor->streamcap.capturemode =
+                               (u32)a->parm.capture.capturemode;
+
+               ret = ov5642_init_mode(frame_rate,
+                                      sensor->streamcap.capturemode);
+               break;
+
+       /* These are all the possible cases. */
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+       case V4L2_BUF_TYPE_VBI_OUTPUT:
+       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+               pr_debug("   type is not " \
+                       "V4L2_BUF_TYPE_VIDEO_CAPTURE but %d\n",
+                       a->type);
+               ret = -EINVAL;
+               break;
+
+       default:
+               pr_debug("   type is unknown - %d\n", a->type);
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+/*!
+ * ioctl_g_fmt_cap - V4L2 sensor interface handler for ioctl_g_fmt_cap
+ * @s: pointer to standard V4L2 device structure
+ * @f: pointer to standard V4L2 v4l2_format structure
+ *
+ * Returns the sensor's current pixel format in the v4l2_format
+ * parameter.
+ */
+static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
+{
+       struct sensor *sensor = s->priv;
+
+       f->fmt.pix = sensor->pix;
+
+       return 0;
+}
+
+/*!
+ * ioctl_g_ctrl - V4L2 sensor interface handler for VIDIOC_G_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @vc: standard V4L2 VIDIOC_G_CTRL ioctl structure
+ *
+ * If the requested control is supported, returns the control's current
+ * value from the video_control[] array.  Otherwise, returns -EINVAL
+ * if the control is not supported.
+ */
+static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc)
+{
+       int ret = 0;
+
+       switch (vc->id) {
+       case V4L2_CID_BRIGHTNESS:
+               vc->value = ov5642_data.brightness;
+               break;
+       case V4L2_CID_HUE:
+               vc->value = ov5642_data.hue;
+               break;
+       case V4L2_CID_CONTRAST:
+               vc->value = ov5642_data.contrast;
+               break;
+       case V4L2_CID_SATURATION:
+               vc->value = ov5642_data.saturation;
+               break;
+       case V4L2_CID_RED_BALANCE:
+               vc->value = ov5642_data.red;
+               break;
+       case V4L2_CID_BLUE_BALANCE:
+               vc->value = ov5642_data.blue;
+               break;
+       case V4L2_CID_EXPOSURE:
+               vc->value = ov5642_data.ae_mode;
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+/*!
+ * ioctl_s_ctrl - V4L2 sensor interface handler for VIDIOC_S_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure
+ *
+ * If the requested control is supported, sets the control's current
+ * value in HW (and updates the video_control[] array).  Otherwise,
+ * returns -EINVAL if the control is not supported.
+ */
+static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc)
+{
+       int retval = 0;
+
+       pr_debug("In ov5642:ioctl_s_ctrl %d\n",
+                vc->id);
+
+       switch (vc->id) {
+       case V4L2_CID_BRIGHTNESS:
+               break;
+       case V4L2_CID_CONTRAST:
+               break;
+       case V4L2_CID_SATURATION:
+               break;
+       case V4L2_CID_HUE:
+               break;
+       case V4L2_CID_AUTO_WHITE_BALANCE:
+               break;
+       case V4L2_CID_DO_WHITE_BALANCE:
+               break;
+       case V4L2_CID_RED_BALANCE:
+               break;
+       case V4L2_CID_BLUE_BALANCE:
+               break;
+       case V4L2_CID_GAMMA:
+               break;
+       case V4L2_CID_EXPOSURE:
+               break;
+       case V4L2_CID_AUTOGAIN:
+               break;
+       case V4L2_CID_GAIN:
+               break;
+       case V4L2_CID_HFLIP:
+               break;
+       case V4L2_CID_VFLIP:
+               break;
+       default:
+               retval = -EPERM;
+               break;
+       }
+
+       return retval;
+}
+
+/*!
+ * ioctl_enum_framesizes - V4L2 sensor interface handler for
+ *                        VIDIOC_ENUM_FRAMESIZES ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @fsize: standard V4L2 VIDIOC_ENUM_FRAMESIZES ioctl structure
+ *
+ * Return 0 if successful, otherwise -EINVAL.
+ */
+static int ioctl_enum_framesizes(struct v4l2_int_device *s,
+                                struct v4l2_frmsizeenum *fsize)
+{
+       if (fsize->index > ov5642_mode_MAX)
+               return -EINVAL;
+
+       fsize->pixel_format = ov5642_data.pix.pixelformat;
+       fsize->discrete.width =
+                       max(ov5642_mode_info_data[0][fsize->index].width,
+                           ov5642_mode_info_data[1][fsize->index].width);
+       fsize->discrete.height =
+                       max(ov5642_mode_info_data[0][fsize->index].height,
+                           ov5642_mode_info_data[1][fsize->index].height);
+       return 0;
+}
+
+/*!
+ * ioctl_g_chip_ident - V4L2 sensor interface handler for
+ *                     VIDIOC_DBG_G_CHIP_IDENT ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @id: pointer to int
+ *
+ * Return 0.
+ */
+static int ioctl_g_chip_ident(struct v4l2_int_device *s, int *id)
+{
+       ((struct v4l2_dbg_chip_ident *)id)->match.type =
+                                       V4L2_CHIP_MATCH_I2C_DRIVER;
+       strcpy(((struct v4l2_dbg_chip_ident *)id)->match.name, "ov5642_camera");
+
+       return 0;
+}
+
+/*!
+ * ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT
+ * @s: pointer to standard V4L2 device structure
+ */
+static int ioctl_init(struct v4l2_int_device *s)
+{
+
+       return 0;
+}
+
+/*!
+ * ioctl_enum_fmt_cap - V4L2 sensor interface handler for VIDIOC_ENUM_FMT
+ * @s: pointer to standard V4L2 device structure
+ * @fmt: pointer to standard V4L2 fmt description structure
+ *
+ * Return 0.
+ */
+static int ioctl_enum_fmt_cap(struct v4l2_int_device *s,
+                             struct v4l2_fmtdesc *fmt)
+{
+       if (fmt->index > ov5642_mode_MAX)
+               return -EINVAL;
+
+       fmt->pixelformat = ov5642_data.pix.pixelformat;
+
+       return 0;
+}
+
+/*!
+ * ioctl_dev_init - V4L2 sensor interface handler for vidioc_int_dev_init_num
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Initialise the device when slave attaches to the master.
+ */
+static int ioctl_dev_init(struct v4l2_int_device *s)
+{
+       struct sensor *sensor = s->priv;
+       u32 tgt_xclk;   /* target xclk */
+       u32 tgt_fps;    /* target frames per secound */
+       enum ov5642_frame_rate frame_rate;
+
+       gpio_sensor_active(ov5642_data.csi);
+       ov5642_data.on = true;
+
+       /* mclk */
+       tgt_xclk = ov5642_data.mclk;
+       tgt_xclk = min(tgt_xclk, (u32)OV5642_XCLK_MAX);
+       tgt_xclk = max(tgt_xclk, (u32)OV5642_XCLK_MIN);
+       ov5642_data.mclk = tgt_xclk;
+
+       pr_debug("   Setting mclk to %d MHz\n", tgt_xclk / 1000000);
+       set_mclk_rate(&ov5642_data.mclk, ov5642_data.csi);
+
+       /* Default camera frame rate is set in probe */
+       tgt_fps = sensor->streamcap.timeperframe.denominator /
+                 sensor->streamcap.timeperframe.numerator;
+
+       if (tgt_fps == 15)
+               frame_rate = ov5642_15_fps;
+       else if (tgt_fps == 30)
+               frame_rate = ov5642_30_fps;
+       else
+               return -EINVAL; /* Only support 15fps or 30fps now. */
+
+       return ov5642_init_mode(frame_rate,
+                               sensor->streamcap.capturemode);
+}
+
+/*!
+ * ioctl_dev_exit - V4L2 sensor interface handler for vidioc_int_dev_exit_num
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Delinitialise the device when slave detaches to the master.
+ */
+static int ioctl_dev_exit(struct v4l2_int_device *s)
+{
+       gpio_sensor_inactive(ov5642_data.csi);
+
+       return 0;
+}
+
+/*!
+ * This structure defines all the ioctls for this module and links them to the
+ * enumeration.
+ */
+static struct v4l2_int_ioctl_desc ov5642_ioctl_desc[] = {
+       {vidioc_int_dev_init_num, (v4l2_int_ioctl_func *)ioctl_dev_init},
+       {vidioc_int_dev_exit_num, ioctl_dev_exit},
+       {vidioc_int_s_power_num, (v4l2_int_ioctl_func *)ioctl_s_power},
+       {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func *)ioctl_g_ifparm},
+/*     {vidioc_int_g_needs_reset_num,
+                               (v4l2_int_ioctl_func *)ioctl_g_needs_reset}, */
+/*     {vidioc_int_reset_num, (v4l2_int_ioctl_func *)ioctl_reset}, */
+       {vidioc_int_init_num, (v4l2_int_ioctl_func *)ioctl_init},
+       {vidioc_int_enum_fmt_cap_num,
+                               (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap},
+/*     {vidioc_int_try_fmt_cap_num,
+                               (v4l2_int_ioctl_func *)ioctl_try_fmt_cap}, */
+       {vidioc_int_g_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_g_fmt_cap},
+/*     {vidioc_int_s_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_s_fmt_cap}, */
+       {vidioc_int_g_parm_num, (v4l2_int_ioctl_func *)ioctl_g_parm},
+       {vidioc_int_s_parm_num, (v4l2_int_ioctl_func *)ioctl_s_parm},
+/*     {vidioc_int_queryctrl_num, (v4l2_int_ioctl_func *)ioctl_queryctrl}, */
+       {vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func *)ioctl_g_ctrl},
+       {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *)ioctl_s_ctrl},
+       {vidioc_int_enum_framesizes_num,
+                               (v4l2_int_ioctl_func *)ioctl_enum_framesizes},
+       {vidioc_int_g_chip_ident_num,
+                               (v4l2_int_ioctl_func *)ioctl_g_chip_ident},
+};
+
+static struct v4l2_int_slave ov5642_slave = {
+       .ioctls = ov5642_ioctl_desc,
+       .num_ioctls = ARRAY_SIZE(ov5642_ioctl_desc),
+};
+
+static struct v4l2_int_device ov5642_int_device = {
+       .module = THIS_MODULE,
+       .name = "ov5642",
+       .type = v4l2_int_type_slave,
+       .u = {
+               .slave = &ov5642_slave,
+       },
+};
+
+/*!
+ * ov5642 I2C probe function
+ *
+ * @param adapter            struct i2c_adapter *
+ * @return  Error code indicating success or failure
+ */
+static int ov5642_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       int retval;
+       struct mxc_camera_platform_data *plat_data = client->dev.platform_data;
+
+       /* Set initial values for the sensor struct. */
+       memset(&ov5642_data, 0, sizeof(ov5642_data));
+       ov5642_data.mclk = 24000000; /* 6 - 54 MHz, typical 24MHz */
+       ov5642_data.mclk = plat_data->mclk;
+       ov5642_data.csi = plat_data->csi;
+
+       ov5642_data.i2c_client = client;
+       ov5642_data.pix.pixelformat = V4L2_PIX_FMT_YUYV;
+       ov5642_data.pix.width = 640;
+       ov5642_data.pix.height = 480;
+       ov5642_data.streamcap.capability = V4L2_MODE_HIGHQUALITY |
+                                          V4L2_CAP_TIMEPERFRAME;
+       ov5642_data.streamcap.capturemode = 0;
+       ov5642_data.streamcap.timeperframe.denominator = DEFAULT_FPS;
+       ov5642_data.streamcap.timeperframe.numerator = 1;
+
+       if (plat_data->io_regulator) {
+               io_regulator = regulator_get(&client->dev,
+                                            plat_data->io_regulator);
+               if (!IS_ERR(io_regulator)) {
+                       regulator_set_voltage(io_regulator,
+                                             OV5642_VOLTAGE_DIGITAL_IO,
+                                             OV5642_VOLTAGE_DIGITAL_IO);
+                       if (regulator_enable(io_regulator) != 0) {
+                               pr_err("%s:io set voltage error\n", __func__);
+                               goto err1;
+                       } else {
+                               dev_dbg(&client->dev,
+                                       "%s:io set voltage ok\n", __func__);
+                       }
+               } else
+                       io_regulator = NULL;
+       }
+
+       if (plat_data->core_regulator) {
+               core_regulator = regulator_get(&client->dev,
+                                              plat_data->core_regulator);
+               if (!IS_ERR(core_regulator)) {
+                       regulator_set_voltage(core_regulator,
+                                             OV5642_VOLTAGE_DIGITAL_CORE,
+                                             OV5642_VOLTAGE_DIGITAL_CORE);
+                       if (regulator_enable(core_regulator) != 0) {
+                               pr_err("%s:core set voltage error\n", __func__);
+                               goto err2;
+                       } else {
+                               dev_dbg(&client->dev,
+                                       "%s:core set voltage ok\n", __func__);
+                       }
+               } else
+                       core_regulator = NULL;
+       }
+
+       if (plat_data->analog_regulator) {
+               analog_regulator = regulator_get(&client->dev,
+                                                plat_data->analog_regulator);
+               if (!IS_ERR(analog_regulator)) {
+                       regulator_set_voltage(analog_regulator,
+                                             OV5642_VOLTAGE_ANALOG,
+                                             OV5642_VOLTAGE_ANALOG);
+                       if (regulator_enable(analog_regulator) != 0) {
+                               pr_err("%s:analog set voltage error\n",
+                                       __func__);
+                               goto err3;
+                       } else {
+                               dev_dbg(&client->dev,
+                                       "%s:analog set voltage ok\n", __func__);
+                       }
+               } else
+                       analog_regulator = NULL;
+       }
+
+       if (plat_data->pwdn)
+               plat_data->pwdn(0);
+
+       camera_plat = plat_data;
+
+       ov5642_int_device.priv = &ov5642_data;
+       retval = v4l2_int_device_register(&ov5642_int_device);
+
+       return retval;
+
+err3:
+       if (core_regulator) {
+               regulator_disable(core_regulator);
+               regulator_put(core_regulator);
+       }
+err2:
+       if (io_regulator) {
+               regulator_disable(io_regulator);
+               regulator_put(io_regulator);
+       }
+err1:
+       return -1;
+}
+
+/*!
+ * ov5642 I2C detach function
+ *
+ * @param client            struct i2c_client *
+ * @return  Error code indicating success or failure
+ */
+static int ov5642_remove(struct i2c_client *client)
+{
+       v4l2_int_device_unregister(&ov5642_int_device);
+
+       if (gpo_regulator) {
+               regulator_disable(gpo_regulator);
+               regulator_put(gpo_regulator);
+       }
+
+       if (analog_regulator) {
+               regulator_disable(analog_regulator);
+               regulator_put(analog_regulator);
+       }
+
+       if (core_regulator) {
+               regulator_disable(core_regulator);
+               regulator_put(core_regulator);
+       }
+
+       if (io_regulator) {
+               regulator_disable(io_regulator);
+               regulator_put(io_regulator);
+       }
+
+       return 0;
+}
+
+/*!
+ * ov5642 init function
+ * Called by insmod ov5642_camera.ko.
+ *
+ * @return  Error code indicating success or failure
+ */
+static __init int ov5642_init(void)
+{
+       u8 err;
+
+       err = i2c_add_driver(&ov5642_i2c_driver);
+       if (err != 0)
+               pr_err("%s:driver registration failed, error=%d \n",
+                       __func__, err);
+
+       return err;
+}
+
+/*!
+ * OV5642 cleanup function
+ * Called on rmmod ov5642_camera.ko
+ *
+ * @return  Error code indicating success or failure
+ */
+static void __exit ov5642_clean(void)
+{
+       i2c_del_driver(&ov5642_i2c_driver);
+}
+
+module_init(ov5642_init);
+module_exit(ov5642_clean);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("OV5642 Camera Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("CSI");
diff --git a/drivers/media/video/mxc/capture/sensor_clock.c b/drivers/media/video/mxc/capture/sensor_clock.c
new file mode 100644 (file)
index 0000000..31560b5
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * 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 sensor_clock.c
+ *
+ * @brief camera clock function
+ *
+ * @ingroup Camera
+ */
+#include <linux/init.h>
+#include <linux/ctype.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/clk.h>
+#include <mach/hardware.h>
+
+#if defined(CONFIG_MXC_IPU_V1) || defined(CONFIG_VIDEO_MXC_EMMA_CAMERA) \
+                              || defined(CONFIG_VIDEO_MXC_CSI_CAMERA_MODULE) \
+                              || defined(CONFIG_VIDEO_MXC_CSI_CAMERA)
+/*
+ * set_mclk_rate
+ *
+ * @param       p_mclk_freq  mclk frequence
+ *
+ */
+void set_mclk_rate(uint32_t *p_mclk_freq)
+{
+       struct clk *clk;
+       uint32_t freq = 0;
+
+       clk = clk_get(NULL, "csi_clk");
+
+       freq = clk_round_rate(clk, *p_mclk_freq);
+       clk_set_rate(clk, freq);
+
+       *p_mclk_freq = freq;
+
+       clk_put(clk);
+       pr_debug("mclk frequency = %d\n", *p_mclk_freq);
+}
+#else
+/*
+ * set_mclk_rate
+ *
+ * @param       p_mclk_freq  mclk frequence
+ * @param      csi         csi 0 or csi 1
+ *
+ */
+void set_mclk_rate(uint32_t *p_mclk_freq, uint32_t csi)
+{
+       struct clk *clk;
+       uint32_t freq = 0;
+       char *mclk;
+
+       if (cpu_is_mx53()) {
+               if (csi == 0)
+                       mclk = "ssi_ext1_clk";
+               else {
+                       pr_err("invalid csi num %d\n", csi);
+                       return;
+               }
+       } else {
+               if (csi == 0) {
+                       mclk = "csi_mclk1";
+               } else if (csi == 1) {
+                       mclk = "csi_mclk2";
+               } else {
+                       pr_err("invalid csi num %d\n", csi);
+                       return;
+               }
+       }
+
+       clk = clk_get(NULL, mclk);
+
+       freq = clk_round_rate(clk, *p_mclk_freq);
+       clk_set_rate(clk, freq);
+
+       *p_mclk_freq = freq;
+
+       clk_put(clk);
+       pr_debug("%s frequency = %d\n", mclk, *p_mclk_freq);
+}
+#endif
+
+/* Exported symbols for modules. */
+EXPORT_SYMBOL(set_mclk_rate);
diff --git a/drivers/media/video/mxc/opl/Makefile b/drivers/media/video/mxc/opl/Makefile
new file mode 100644 (file)
index 0000000..092a62c
--- /dev/null
@@ -0,0 +1,5 @@
+opl-objs       := opl_mod.o rotate90_u16.o rotate270_u16.o     \
+                  rotate90_u16_qcif.o rotate270_u16_qcif.o     \
+                  vmirror_u16.o hmirror_rotate180_u16.o
+
+obj-$(CONFIG_VIDEO_MXC_OPL)    += opl.o
diff --git a/drivers/media/video/mxc/opl/hmirror_rotate180_u16.c b/drivers/media/video/mxc/opl/hmirror_rotate180_u16.c
new file mode 100644 (file)
index 0000000..b03d7d9
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include "opl.h"
+
+static inline u32 rot_left_u16(u16 x, unsigned int n)
+{
+       return (x << n) | (x >> (16 - n));
+}
+
+static inline u32 rot_left_u32(u32 x, unsigned int n)
+{
+       return (x << n) | (x >> (32 - n));
+}
+
+static inline u32 byte_swap_u32(u32 x)
+{
+       u32 t1, t2, t3;
+
+       t1 = x ^ ((x << 16) | x >> 16);
+       t2 = t1 & 0xff00ffff;
+       t3 = (x >> 8) | (x << 24);
+       return t3 ^ (t2 >> 8);
+}
+
+static int opl_hmirror_u16_by1(const u8 *src, int src_line_stride, int width,
+                              int height, u8 *dst, int dst_line_stride,
+                              int vmirror);
+static int opl_hmirror_u16_by2(const u8 *src, int src_line_stride, int width,
+                              int height, u8 *dst, int dst_line_stride,
+                              int vmirror);
+static int opl_hmirror_u16_by4(const u8 *src, int src_line_stride, int width,
+                              int height, u8 *dst, int dst_line_stride,
+                              int vmirror);
+static int opl_hmirror_u16_by8(const u8 *src, int src_line_stride, int width,
+                              int height, u8 *dst, int dst_line_stride,
+                              int vmirror);
+
+int opl_hmirror_u16(const u8 *src, int src_line_stride, int width, int height,
+                   u8 *dst, int dst_line_stride)
+{
+       if (!src || !dst)
+               return OPLERR_NULL_PTR;
+
+       if (width == 0 || height == 0 || src_line_stride == 0
+           || dst_line_stride == 0)
+               return OPLERR_BAD_ARG;
+
+       if (width % 8 == 0)
+               return opl_hmirror_u16_by8(src, src_line_stride, width, height,
+                                          dst, dst_line_stride, 0);
+       else if (width % 4 == 0)
+               return opl_hmirror_u16_by4(src, src_line_stride, width, height,
+                                          dst, dst_line_stride, 0);
+       else if (width % 2 == 0)
+               return opl_hmirror_u16_by2(src, src_line_stride, width, height,
+                                          dst, dst_line_stride, 0);
+       else                    /* (width % 1) */
+               return opl_hmirror_u16_by1(src, src_line_stride, width, height,
+                                          dst, dst_line_stride, 0);
+}
+
+int opl_rotate180_u16(const u8 *src, int src_line_stride, int width,
+                     int height, u8 *dst, int dst_line_stride)
+{
+       if (!src || !dst)
+               return OPLERR_NULL_PTR;
+
+       if (width == 0 || height == 0 || src_line_stride == 0
+           || dst_line_stride == 0)
+               return OPLERR_BAD_ARG;
+
+       if (width % 8 == 0)
+               return opl_hmirror_u16_by8(src, src_line_stride, width, height,
+                                          dst, dst_line_stride, 1);
+       else if (width % 4 == 0)
+               return opl_hmirror_u16_by4(src, src_line_stride, width, height,
+                                          dst, dst_line_stride, 1);
+       else if (width % 2 == 0)
+               return opl_hmirror_u16_by2(src, src_line_stride, width, height,
+                                          dst, dst_line_stride, 1);
+       else                    /* (width % 1) */
+               return opl_hmirror_u16_by1(src, src_line_stride, width, height,
+                                          dst, dst_line_stride, 1);
+}
+
+static int opl_hmirror_u16_by1(const u8 *src, int src_line_stride, int width,
+                              int height, u8 *dst, int dst_line_stride,
+                              int vmirror)
+{
+       const u8 *src_row_addr;
+       const u8 *psrc;
+       u8 *dst_row_addr, *pdst;
+       int i, j;
+       u16 pixel;
+
+       src_row_addr = src;
+       if (vmirror) {
+               dst_row_addr = dst + dst_line_stride * (height - 1);
+               dst_line_stride = -dst_line_stride;
+       } else
+               dst_row_addr = dst;
+
+       /* Loop over all rows */
+       for (i = 0; i < height; i++) {
+               /* Loop over each pixel */
+               psrc = src_row_addr;
+               pdst = dst_row_addr + (width - 1) * BYTES_PER_PIXEL
+                   - (BYTES_PER_PIXEL - BYTES_PER_PIXEL);
+               for (j = 0; j < width; j++) {
+                       pixel = *(u16 *) psrc;
+                       *(u16 *) pdst = pixel;
+                       psrc += BYTES_PER_PIXEL;
+                       pdst -= BYTES_PER_PIXEL;
+               }
+               src_row_addr += src_line_stride;
+               dst_row_addr += dst_line_stride;
+       }
+
+       return OPLERR_SUCCESS;
+}
+
+static int opl_hmirror_u16_by2(const u8 *src, int src_line_stride, int width,
+                              int height, u8 *dst, int dst_line_stride,
+                              int vmirror)
+{
+       const u8 *src_row_addr;
+       const u8 *psrc;
+       u8 *dst_row_addr, *pdst;
+       int i, j;
+       u32 pixelsin, pixelsout;
+
+       src_row_addr = src;
+       if (vmirror) {
+               dst_row_addr = dst + dst_line_stride * (height - 1);
+               dst_line_stride = -dst_line_stride;
+       } else
+               dst_row_addr = dst;
+
+       /* Loop over all rows */
+       for (i = 0; i < height; i++) {
+               /* Loop over each pixel */
+               psrc = src_row_addr;
+               pdst = dst_row_addr + (width - 2) * BYTES_PER_PIXEL;
+               for (j = 0; j < (width >> 1); j++) {
+                       pixelsin = *(u32 *) psrc;
+                       pixelsout = rot_left_u32(pixelsin, 16);
+                       *(u32 *) pdst = pixelsout;
+                       psrc += BYTES_PER_2PIXEL;
+                       pdst -= BYTES_PER_2PIXEL;
+               }
+               src_row_addr += src_line_stride;
+               dst_row_addr += dst_line_stride;
+       }
+
+       return OPLERR_SUCCESS;
+}
+
+static int opl_hmirror_u16_by4(const u8 *src, int src_line_stride, int width,
+                              int height, u8 *dst, int dst_line_stride,
+                              int vmirror)
+{
+       const u8 *src_row_addr;
+       const u8 *psrc;
+       u8 *dst_row_addr, *pdst;
+       int i, j;
+
+       union doubleword {
+               u64 dw;
+               u32 w[2];
+       };
+
+       union doubleword inbuf;
+       union doubleword outbuf;
+
+       src_row_addr = src;
+       if (vmirror) {
+               dst_row_addr = dst + dst_line_stride * (height - 1);
+               dst_line_stride = -dst_line_stride;
+       } else
+               dst_row_addr = dst;
+
+       /* Loop over all rows */
+       for (i = 0; i < height; i++) {
+               /* Loop over each pixel */
+               psrc = src_row_addr;
+               pdst = dst_row_addr + (width - 4) * BYTES_PER_PIXEL;
+               for (j = 0; j < (width >> 2); j++) {
+                       inbuf.dw = *(u64 *) psrc;
+                       outbuf.w[0] = rot_left_u32(inbuf.w[1], 16);
+                       outbuf.w[1] = rot_left_u32(inbuf.w[0], 16);
+                       *(u64 *) pdst = outbuf.dw;
+                       psrc += BYTES_PER_4PIXEL;
+                       pdst -= BYTES_PER_4PIXEL;
+               }
+               src_row_addr += src_line_stride;
+               dst_row_addr += dst_line_stride;
+       }
+       return OPLERR_SUCCESS;
+}
+
+static int opl_hmirror_u16_by8(const u8 *src, int src_line_stride, int width,
+                              int height, u8 *dst, int dst_line_stride,
+                              int vmirror)
+{
+       const u8 *src_row_addr;
+       const u8 *psrc;
+       u8 *dst_row_addr, *pdst;
+       int i, j;
+
+       src_row_addr = src;
+       if (vmirror) {
+               dst_row_addr = dst + dst_line_stride * (height - 1);
+               dst_line_stride = -dst_line_stride;
+       } else
+               dst_row_addr = dst;
+
+       /* Loop over all rows */
+       for (i = 0; i < height; i++) {
+               /* Loop over each pixel */
+               psrc = src_row_addr;
+               pdst = dst_row_addr + (width - 1) * BYTES_PER_PIXEL - 2;
+               for (j = (width >> 3); j > 0; j--) {
+                       __asm__ volatile (
+                               "ldmia  %0!,{r2-r5}\n\t"
+                               "mov    r6, r2\n\t"
+                               "mov    r7, r3\n\t"
+                               "mov    r2, r5, ROR #16\n\t"
+                               "mov    r3, r4, ROR #16\n\t"
+                               "mov    r4, r7, ROR #16\n\t"
+                               "mov    r5, r6, ROR #16\n\t"
+                               "stmda  %1!,{r2-r5}\n\t"
+
+                               : "+r"(psrc), "+r"(pdst)
+                               : "0"(psrc), "1"(pdst)
+                               : "r2", "r3", "r4", "r5", "r6", "r7",
+                               "memory"
+                       );
+               }
+               src_row_addr += src_line_stride;
+               dst_row_addr += dst_line_stride;
+       }
+
+       return OPLERR_SUCCESS;
+}
+
+EXPORT_SYMBOL(opl_hmirror_u16);
+EXPORT_SYMBOL(opl_rotate180_u16);
diff --git a/drivers/media/video/mxc/opl/opl.h b/drivers/media/video/mxc/opl/opl.h
new file mode 100644 (file)
index 0000000..f1158a8
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * 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 OPLIP OPL Image Processing
+ */
+/*!
+ * @file opl.h
+ *
+ * @brief The OPL (Open Primitives Library) Image Processing library defines
+ * efficient functions for rotation and mirroring.
+ *
+ * It includes ARM9-optimized rotation and mirroring functions. It is derived
+ * from the original OPL project which is found at sourceforge.freescale.net.
+ *
+ * @ingroup OPLIP
+ */
+#ifndef __OPL_H__
+#define __OPL_H__
+
+#include <linux/types.h>
+
+#define BYTES_PER_PIXEL                        2
+#define CACHE_LINE_WORDS               8
+#define BYTES_PER_WORD                 4
+
+#define BYTES_PER_2PIXEL               (BYTES_PER_PIXEL * 2)
+#define BYTES_PER_4PIXEL               (BYTES_PER_PIXEL * 4)
+#define BYTES_PER_8PIXEL               (BYTES_PER_PIXEL * 8)
+
+#define QCIF_Y_WIDTH                   176
+#define QCIF_Y_HEIGHT                  144
+
+/*! Enumerations of opl error code */
+enum opl_error {
+       OPLERR_SUCCESS = 0,
+       OPLERR_NULL_PTR,
+       OPLERR_BAD_ARG,
+       OPLERR_DIV_BY_ZERO,
+       OPLERR_OVER_FLOW,
+       OPLERR_UNDER_FLOW,
+       OPLERR_MISALIGNED,
+};
+
+/*!
+ * @brief Rotate a 16bbp buffer 90 degrees clockwise.
+ *
+ * @param src             Pointer to the input buffer
+ * @param src_line_stride Length in bytes of a raster line of the input buffer
+ * @param width           Width in pixels of the region in the input buffer
+ * @param height          Height in pixels of the region in the input buffer
+ * @param dst             Pointer to the output buffer
+ * @param dst_line_stride Length in bytes of a raster line of the output buffer
+ *
+ * @return Standard OPL error code. See enumeration for possible result codes.
+ */
+int opl_rotate90_u16(const u8 *src, int src_line_stride, int width, int height,
+                    u8 *dst, int dst_line_stride);
+
+/*!
+ * @brief Rotate a 16bbp buffer 180 degrees clockwise.
+ *
+ * @param src             Pointer to the input buffer
+ * @param src_line_stride Length in bytes of a raster line of the input buffer
+ * @param width           Width in pixels of the region in the input buffer
+ * @param height          Height in pixels of the region in the input buffer
+ * @param dst             Pointer to the output buffer
+ * @param dst_line_stride Length in bytes of a raster line of the output buffer
+ *
+ * @return Standard OPL error code. See enumeration for possible result codes.
+ */
+int opl_rotate180_u16(const u8 *src, int src_line_stride, int width,
+                     int height, u8 *dst, int dst_line_stride);
+
+/*!
+ * @brief Rotate a 16bbp buffer 270 degrees clockwise
+ *
+ * @param src             Pointer to the input buffer
+ * @param src_line_stride Length in bytes of a raster line of the input buffer
+ * @param width           Width in pixels of the region in the input buffer
+ * @param height          Height in pixels of the region in the input buffer
+ * @param dst             Pointer to the output buffer
+ * @param dst_line_stride Length in bytes of a raster line of the output buffer
+ *
+ * @return Standard OPL error code. See enumeration for possible result codes.
+ */
+int opl_rotate270_u16(const u8 *src, int src_line_stride, int width,
+                     int height, u8 *dst, int dst_line_stride);
+
+/*!
+ * @brief Mirror a 16bpp buffer horizontally
+ *
+ * @param src             Pointer to the input buffer
+ * @param src_line_stride Length in bytes of a raster line of the input buffer
+ * @param width           Width in pixels of the region in the input buffer
+ * @param height          Height in pixels of the region in the input buffer
+ * @param dst             Pointer to the output buffer
+ * @param dst_line_stride Length in bytes of a raster line of the output buffer
+ *
+ * @return Standard OPL error code. See enumeration for possible result codes.
+ */
+int opl_hmirror_u16(const u8 *src, int src_line_stride, int width, int height,
+                   u8 *dst, int dst_line_stride);
+
+/*!
+ * @brief Mirror a 16bpp buffer vertically
+ *
+ * @param src             Pointer to the input buffer
+ * @param src_line_stride Length in bytes of a raster line of the input buffer
+ * @param width           Width in pixels of the region in the input buffer
+ * @param height          Height in pixels of the region in the input buffer
+ * @param dst             Pointer to the output buffer
+ * @param dst_line_stride Length in bytes of a raster line of the output buffer
+ *
+ * @return Standard OPL error code. See enumeration for possible result codes.
+ */
+int opl_vmirror_u16(const u8 *src, int src_line_stride, int width, int height,
+                   u8 *dst, int dst_line_stride);
+
+/*!
+ * @brief Rotate a 16bbp buffer 90 degrees clockwise and mirror vertically
+ *       It is equivalent to rotate 270 degree and mirror horizontally
+ *
+ * @param src             Pointer to the input buffer
+ * @param src_line_stride Length in bytes of a raster line of the input buffer
+ * @param width           Width in pixels of the region in the input buffer
+ * @param height          Height in pixels of the region in the input buffer
+ * @param dst             Pointer to the output buffer
+ * @param dst_line_stride Length in bytes of a raster line of the output buffer
+ *
+ * @return Standard OPL error code. See enumeration for possible result codes.
+ */
+int opl_rotate90_vmirror_u16(const u8 *src, int src_line_stride, int width,
+                            int height, u8 *dst, int dst_line_stride);
+
+/*!
+ * @brief Rotate a 16bbp buffer 270 degrees clockwise and mirror vertically
+ *       It is equivalent to rotate 90 degree and mirror horizontally
+ *
+ * @param src             Pointer to the input buffer
+ * @param src_line_stride Length in bytes of a raster line of the input buffer
+ * @param width           Width in pixels of the region in the input buffer
+ * @param height          Height in pixels of the region in the input buffer
+ * @param dst             Pointer to the output buffer
+ * @param dst_line_stride Length in bytes of a raster line of the output buffer
+ *
+ * @return Standard OPL error code. See enumeration for possible result codes.
+ */
+int opl_rotate270_vmirror_u16(const u8 *src, int src_line_stride, int width,
+                             int height, u8 *dst, int dst_line_stride);
+
+#endif                         /* __OPL_H__ */
diff --git a/drivers/media/video/mxc/opl/opl_mod.c b/drivers/media/video/mxc/opl/opl_mod.c
new file mode 100644 (file)
index 0000000..7df5063
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+
+static __init int opl_init(void)
+{
+       return 0;
+}
+
+static void __exit opl_exit(void)
+{
+}
+
+module_init(opl_init);
+module_exit(opl_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("OPL Software Rotation/Mirroring");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/opl/rotate270_u16.c b/drivers/media/video/mxc/opl/rotate270_u16.c
new file mode 100644 (file)
index 0000000..9a5c7e0
--- /dev/null
@@ -0,0 +1,285 @@
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include "opl.h"
+
+static int opl_rotate270_u16_by16(const u8 *src, int src_line_stride,
+                                 int width, int height, u8 *dst,
+                                 int dst_line_stride, int vmirror);
+static int opl_rotate270_u16_by4(const u8 *src, int src_line_stride, int width,
+                                int height, u8 *dst, int dst_line_stride,
+                                int vmirror);
+static int opl_rotate270_vmirror_u16_both(const u8 *src, int src_line_stride,
+                                         int width, int height, u8 *dst,
+                                         int dst_line_stride, int vmirror);
+int opl_rotate270_u16_qcif(const u8 *src, u8 *dst);
+
+int opl_rotate270_u16(const u8 *src, int src_line_stride, int width,
+                     int height, u8 *dst, int dst_line_stride)
+{
+       return opl_rotate270_vmirror_u16_both(src, src_line_stride, width,
+                                             height, dst, dst_line_stride, 0);
+}
+
+int opl_rotate270_vmirror_u16(const u8 *src, int src_line_stride, int width,
+                             int height, u8 *dst, int dst_line_stride)
+{
+       return opl_rotate270_vmirror_u16_both(src, src_line_stride, width,
+                                             height, dst, dst_line_stride, 1);
+}
+
+static int opl_rotate270_vmirror_u16_both(const u8 *src, int src_line_stride,
+                                         int width, int height, u8 *dst,
+                                         int dst_line_stride, int vmirror)
+{
+       const int BLOCK_SIZE_PIXELS = CACHE_LINE_WORDS * BYTES_PER_WORD
+           / BYTES_PER_PIXEL;
+       const int BLOCK_SIZE_PIXELS_BY4 = CACHE_LINE_WORDS * BYTES_PER_WORD
+           / BYTES_PER_PIXEL / 4;
+
+       if (!src || !dst)
+               return OPLERR_NULL_PTR;
+
+       if (width == 0 || height == 0 || src_line_stride == 0
+           || dst_line_stride == 0)
+               return OPLERR_BAD_ARG;
+
+       /* The QCIF algorithm doesn't support vertical mirroring */
+       if (vmirror == 0 && width == QCIF_Y_WIDTH && height == QCIF_Y_HEIGHT
+           && src_line_stride == QCIF_Y_WIDTH * 2
+           && src_line_stride == QCIF_Y_HEIGHT * 2)
+               return opl_rotate270_u16_qcif(src, dst);
+       else if (width % BLOCK_SIZE_PIXELS == 0
+                && height % BLOCK_SIZE_PIXELS == 0)
+               return opl_rotate270_u16_by16(src, src_line_stride, width,
+                                             height, dst, dst_line_stride,
+                                             vmirror);
+       else if (width % BLOCK_SIZE_PIXELS_BY4 == 0
+                && height % BLOCK_SIZE_PIXELS_BY4 == 0)
+               return opl_rotate270_u16_by4(src, src_line_stride, width,
+                                            height, dst, dst_line_stride,
+                                            vmirror);
+       else
+               return OPLERR_BAD_ARG;
+}
+
+/*
+ * Rotate Counter Clockwise, divide RGB component into 16 row strips, read
+ * non sequentially and write sequentially. This is done in 16 line strips
+ * so that the cache is used better. Cachelines are 8 words = 32 bytes. Pixels
+ * are 2 bytes. The 16 reads will be cache misses, but the next 240 should
+ * be from cache. The writes to the output buffer will be sequential for 16
+ * writes.
+ *
+ * Example:
+ * Input data matrix:      output matrix
+ *
+ * 0 | 1 | 2 | 3 | 4 |      4 | 0 | 0 | 3 |
+ * 4 | 3 | 2 | 1 | 0 |      3 | 1 | 9 | 6 |
+ * 6 | 7 | 8 | 9 | 0 |      2 | 2 | 8 | 2 |
+ * 5 | 3 | 2 | 6 | 3 |      1 | 3 | 7 | 3 |
+ * ^                       0 | 4 | 6 | 5 | < Write the input data sequentially
+ * Read first column
+ * Start at the bottom
+ * Move to next column and repeat
+ *
+ * Loop over k decreasing (blocks)
+ * in_block_ptr = src + (((RGB_HEIGHT_PIXELS / BLOCK_SIZE_PIXELS) - k)
+ *                * BLOCK_SIZE_PIXELS) * (RGB_WIDTH_BYTES)
+ * out_block_ptr = dst + (((RGB_HEIGHT_PIXELS / BLOCK_SIZE_PIXELS) - k)
+ *                * BLOCK_SIZE_BYTES) + (RGB_WIDTH_PIXELS - 1)
+ *                * RGB_HEIGHT_PIXELS * BYTES_PER_PIXEL
+ *
+ * Loop over i decreasing (width)
+ * Each pix:
+ * in_block_ptr += RGB_WIDTH_BYTES
+ * out_block_ptr += 4
+ *
+ * Each row of block:
+ * in_block_ptr -= RGB_WIDTH_BYTES * BLOCK_SIZE_PIXELS - 2
+ * out_block_ptr -= RGB_HEIGHT_PIXELS * BYTES_PER_PIXEL + 2 * BLOCK_SIZE_PIXELS;
+ *
+ * It may perform vertical mirroring too depending on the vmirror flag.
+ */
+static int opl_rotate270_u16_by16(const u8 *src, int src_line_stride,
+                                 int width, int height, u8 *dst,
+                                 int dst_line_stride, int vmirror)
+{
+       const int BLOCK_SIZE_PIXELS = CACHE_LINE_WORDS * BYTES_PER_WORD
+           / BYTES_PER_PIXEL;
+       const int IN_INDEX = src_line_stride * BLOCK_SIZE_PIXELS
+           - BYTES_PER_PIXEL;
+       const int OUT_INDEX = vmirror ?
+           -dst_line_stride + BYTES_PER_PIXEL * BLOCK_SIZE_PIXELS
+           : dst_line_stride + BYTES_PER_PIXEL * BLOCK_SIZE_PIXELS;
+       const u8 *in_block_ptr;
+       u8 *out_block_ptr;
+       int i, k;
+
+       for (k = height / BLOCK_SIZE_PIXELS; k > 0; k--) {
+               in_block_ptr = src + (((height / BLOCK_SIZE_PIXELS) - k)
+                                     * BLOCK_SIZE_PIXELS) * src_line_stride;
+               out_block_ptr = dst + (((height / BLOCK_SIZE_PIXELS) - k)
+                                      * BLOCK_SIZE_PIXELS * BYTES_PER_PIXEL) +
+                   (width - 1) * dst_line_stride;
+
+               /*
+               * For vertical mirroring the writing starts from the
+               * first line
+               */
+               if (vmirror)
+                       out_block_ptr -= dst_line_stride * (width - 1);
+
+               for (i = width; i > 0; i--) {
+                       __asm__ volatile (
+                               "ldrh   r2, [%0], %4\n\t"
+                               "ldrh   r3, [%0], %4\n\t"
+                               "ldrh   r4, [%0], %4\n\t"
+                               "ldrh   r5, [%0], %4\n\t"
+                               "orr    r2, r2, r3, lsl #16\n\t"
+                               "orr    r4, r4, r5, lsl #16\n\t"
+                               "str    r2, [%1], #4\n\t"
+                               "str    r4, [%1], #4\n\t"
+
+                               "ldrh   r2, [%0], %4\n\t"
+                               "ldrh   r3, [%0], %4\n\t"
+                               "ldrh   r4, [%0], %4\n\t"
+                               "ldrh   r5, [%0], %4\n\t"
+                               "orr    r2, r2, r3, lsl #16\n\t"
+                               "orr    r4, r4, r5, lsl #16\n\t"
+                               "str    r2, [%1], #4\n\t"
+                               "str    r4, [%1], #4\n\t"
+
+                               "ldrh   r2, [%0], %4\n\t"
+                               "ldrh   r3, [%0], %4\n\t"
+                               "ldrh   r4, [%0], %4\n\t"
+                               "ldrh   r5, [%0], %4\n\t"
+                               "orr    r2, r2, r3, lsl #16\n\t"
+                               "orr    r4, r4, r5, lsl #16\n\t"
+                               "str    r2, [%1], #4\n\t"
+                               "str    r4, [%1], #4\n\t"
+
+                               "ldrh   r2, [%0], %4\n\t"
+                               "ldrh   r3, [%0], %4\n\t"
+                               "ldrh   r4, [%0], %4\n\t"
+                               "ldrh   r5, [%0], %4\n\t"
+                               "orr    r2, r2, r3, lsl #16\n\t"
+                               "orr    r4, r4, r5, lsl #16\n\t"
+                               "str    r2, [%1], #4\n\t"
+                               "str    r4, [%1], #4\n\t"
+
+                               : "+r" (in_block_ptr), "+r"(out_block_ptr)      /* output */
+                               : "0"(in_block_ptr), "1"(out_block_ptr), "r"(src_line_stride)   /* input  */
+                               : "r2", "r3", "r4", "r5", "memory"      /* modify */
+                       );
+                       in_block_ptr -= IN_INDEX;
+                       out_block_ptr -= OUT_INDEX;
+               }
+       }
+
+       return OPLERR_SUCCESS;
+}
+
+/*
+ * Rotate Counter Clockwise, divide RGB component into 4 row strips, read
+ * non sequentially and write sequentially. This is done in 4 line strips
+ * so that the cache is used better. Cachelines are 8 words = 32 bytes. Pixels
+ * are 2 bytes. The 4 reads will be cache misses, but the next 60 should
+ * be from cache. The writes to the output buffer will be sequential for 4
+ * writes.
+ *
+ * Example:
+ * Input data matrix:      output matrix
+ *
+ * 0 | 1 | 2 | 3 | 4 |      4 | 0 | 0 | 3 |
+ * 4 | 3 | 2 | 1 | 0 |      3 | 1 | 9 | 6 |
+ * 6 | 7 | 8 | 9 | 0 |      2 | 2 | 8 | 2 |
+ * 5 | 3 | 2 | 6 | 3 |      1 | 3 | 7 | 3 |
+ * ^                       0 | 4 | 6 | 5 | < Write the input data sequentially
+ * Read first column
+ * Start at the bottom
+ * Move to next column and repeat
+ *
+ * Loop over k decreasing (blocks)
+ * in_block_ptr = src + (((RGB_HEIGHT_PIXELS / BLOCK_SIZE_PIXELS) - k)
+ *                * BLOCK_SIZE_PIXELS) * (RGB_WIDTH_BYTES)
+ * out_block_ptr = dst + (((RGB_HEIGHT_PIXELS / BLOCK_SIZE_PIXELS) - k)
+ *                * BLOCK_SIZE_BYTES) + (RGB_WIDTH_PIXELS - 1)
+ *                * RGB_HEIGHT_PIXELS * BYTES_PER_PIXEL
+ *
+ * Loop over i decreasing (width)
+ * Each pix:
+ * in_block_ptr += RGB_WIDTH_BYTES
+ * out_block_ptr += 4
+ *
+ * Each row of block:
+ * in_block_ptr -= RGB_WIDTH_BYTES * BLOCK_SIZE_PIXELS - 2
+ * out_block_ptr -= RGB_HEIGHT_PIXELS * BYTES_PER_PIXEL + 2 * BLOCK_SIZE_PIXELS;
+ *
+ * It may perform vertical mirroring too depending on the vmirror flag.
+ */
+static int opl_rotate270_u16_by4(const u8 *src, int src_line_stride, int width,
+                                int height, u8 *dst, int dst_line_stride,
+                                int vmirror)
+{
+       const int BLOCK_SIZE_PIXELS = CACHE_LINE_WORDS * BYTES_PER_WORD
+           / BYTES_PER_PIXEL / 4;
+       const int IN_INDEX = src_line_stride * BLOCK_SIZE_PIXELS
+           - BYTES_PER_PIXEL;
+       const int OUT_INDEX = vmirror ?
+           -dst_line_stride + BYTES_PER_PIXEL * BLOCK_SIZE_PIXELS
+           : dst_line_stride + BYTES_PER_PIXEL * BLOCK_SIZE_PIXELS;
+       const u8 *in_block_ptr;
+       u8 *out_block_ptr;
+       int i, k;
+
+       for (k = height / BLOCK_SIZE_PIXELS; k > 0; k--) {
+               in_block_ptr = src + (((height / BLOCK_SIZE_PIXELS) - k)
+                                     * BLOCK_SIZE_PIXELS) * src_line_stride;
+               out_block_ptr = dst + (((height / BLOCK_SIZE_PIXELS) - k)
+                                      * BLOCK_SIZE_PIXELS * BYTES_PER_PIXEL)
+                   + (width - 1) * dst_line_stride;
+
+               /*
+               * For vertical mirroring the writing starts from the
+               * first line
+               */
+               if (vmirror)
+                       out_block_ptr -= dst_line_stride * (width - 1);
+
+               for (i = width; i > 0; i--) {
+                       __asm__ volatile (
+                               "ldrh   r2, [%0], %4\n\t"
+                               "ldrh   r3, [%0], %4\n\t"
+                               "ldrh   r4, [%0], %4\n\t"
+                               "ldrh   r5, [%0], %4\n\t"
+                               "orr    r2, r2, r3, lsl #16\n\t"
+                               "orr    r4, r4, r5, lsl #16\n\t"
+                               "str    r2, [%1], #4\n\t"
+                               "str    r4, [%1], #4\n\t"
+
+                               : "+r" (in_block_ptr), "+r"(out_block_ptr)      /* output */
+                               : "0"(in_block_ptr), "1"(out_block_ptr), "r"(src_line_stride)   /* input  */
+                               : "r2", "r3", "r4", "r5", "memory"      /* modify */
+                       );
+                       in_block_ptr -= IN_INDEX;
+                       out_block_ptr -= OUT_INDEX;
+               }
+       }
+
+       return OPLERR_SUCCESS;
+}
+
+EXPORT_SYMBOL(opl_rotate270_u16);
+EXPORT_SYMBOL(opl_rotate270_vmirror_u16);
diff --git a/drivers/media/video/mxc/opl/rotate270_u16_qcif.S b/drivers/media/video/mxc/opl/rotate270_u16_qcif.S
new file mode 100644 (file)
index 0000000..6a28af0
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * 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
+ */
+#include <linux/linkage.h>
+
+       .text
+       .align  2
+ENTRY(opl_rotate270_u16_qcif)
+       STMFD    sp!,{r4-r10}
+        MOV      r12,#0x160
+        MOV      r10,#0x90
+        MOV      r3,r10,LSR #4
+.L1.16:
+        RSB      r2,r3,r10,LSR #4
+        MOV      r5,r2,LSL #5
+        MOV      r4,r12,LSR #1
+        SMULBB   r4,r5,r4
+        ADD      r2,r1,r2,LSL #5
+        ADD      r5,r2,#0xc000
+        ADD      r5,r5,#0x4e0
+        MOV      r2,r12,LSR #1
+        ADD      r4,r0,r4
+.L1.52:
+        LDRH     r6,[r4],r12
+        LDRH     r7,[r4],r12
+        LDRH     r8,[r4],r12
+        LDRH     r9,[r4],r12
+        ORR      r6,r6,r7,LSL #16
+        ORR      r7,r8,r9,LSL #16
+        STMIA    r5!,{r6,r7}
+        SUBS     r2,r2,#1
+        LDRH     r6,[r4],r12
+        LDRH     r7,[r4],r12
+        LDRH     r8,[r4],r12
+        LDRH     r9,[r4],r12
+        ORR      r6,r6,r7,LSL #16
+        ORR      r7,r8,r9,LSL #16
+        STMIA    r5!,{r6,r7}
+        LDRH     r6,[r4],r12
+        LDRH     r7,[r4],r12
+        LDRH     r8,[r4],r12
+        LDRH     r9,[r4],r12
+        ORR      r6,r6,r7,LSL #16
+        ORR      r7,r8,r9,LSL #16
+        STMIA    r5!,{r6,r7}
+        LDRH     r6,[r4],r12
+        LDRH     r7,[r4],r12
+        LDRH     r8,[r4],r12
+        LDRH     r9,[r4],r12
+        ORR      r6,r6,r7,LSL #16
+        ORR      r7,r8,r9,LSL #16
+        SUB      r4,r4,#0x1500
+        STMIA    r5,{r6,r7}
+        SUB      r5,r5,#0x138
+        SUB      r4,r4,#0xfe
+        BGT      .L1.52
+        SUBS     r3,r3,#1
+        BGT      .L1.16
+        LDMFD    sp!,{r4-r10}
+        BX       lr
+       .size   opl_rotate270_u16_qcif, . - opl_rotate270_u16_qcif
diff --git a/drivers/media/video/mxc/opl/rotate90_u16.c b/drivers/media/video/mxc/opl/rotate90_u16.c
new file mode 100644 (file)
index 0000000..553d41b
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include "opl.h"
+
+static int opl_rotate90_u16_by16(const u8 *src, int src_line_stride, int width,
+                                int height, u8 *dst, int dst_line_stride,
+                                int vmirror);
+static int opl_rotate90_u16_by4(const u8 *src, int src_line_stride, int width,
+                               int height, u8 *dst, int dst_line_stride,
+                               int vmirror);
+static int opl_rotate90_vmirror_u16_both(const u8 *src, int src_line_stride,
+                                        int width, int height, u8 *dst,
+                                        int dst_line_stride, int vmirror);
+int opl_rotate90_u16_qcif(const u8 *src, u8 *dst);
+
+int opl_rotate90_u16(const u8 *src, int src_line_stride, int width, int height,
+                    u8 *dst, int dst_line_stride)
+{
+       return opl_rotate90_vmirror_u16_both(src, src_line_stride, width,
+                                           height, dst, dst_line_stride, 0);
+}
+
+int opl_rotate90_vmirror_u16(const u8 *src, int src_line_stride, int width,
+                           int height, u8 *dst, int dst_line_stride)
+{
+       return opl_rotate90_vmirror_u16_both(src, src_line_stride, width,
+                                           height, dst, dst_line_stride, 1);
+}
+
+static int opl_rotate90_vmirror_u16_both(const u8 *src, int src_line_stride,
+                                        int width, int height, u8 *dst,
+                                        int dst_line_stride, int vmirror)
+{
+       const int BLOCK_SIZE_PIXELS = CACHE_LINE_WORDS * BYTES_PER_WORD
+           / BYTES_PER_PIXEL;
+       const int BLOCK_SIZE_PIXELS_BY4 = CACHE_LINE_WORDS * BYTES_PER_WORD
+           / BYTES_PER_PIXEL / 4;
+
+       if (!src || !dst)
+               return OPLERR_NULL_PTR;
+
+       if (width == 0 || height == 0 || src_line_stride == 0
+           || dst_line_stride == 0)
+               return OPLERR_BAD_ARG;
+
+       /* The QCIF algorithm doesn't support vertical mirroring */
+       if (vmirror == 0 && width == QCIF_Y_WIDTH && height == QCIF_Y_HEIGHT
+           && src_line_stride == QCIF_Y_WIDTH * 2
+           && src_line_stride == QCIF_Y_HEIGHT * 2)
+               return opl_rotate90_u16_qcif(src, dst);
+       else if (width % BLOCK_SIZE_PIXELS == 0
+                && height % BLOCK_SIZE_PIXELS == 0)
+               return opl_rotate90_u16_by16(src, src_line_stride, width,
+                                            height, dst, dst_line_stride,
+                                            vmirror);
+       else if (width % BLOCK_SIZE_PIXELS_BY4 == 0
+                && height % BLOCK_SIZE_PIXELS_BY4 == 0)
+               return opl_rotate90_u16_by4(src, src_line_stride, width, height,
+                                           dst, dst_line_stride, vmirror);
+       else
+               return OPLERR_BAD_ARG;
+}
+
+/*
+ * Performs clockwise rotation (and possibly vertical mirroring depending
+ * on the vmirror flag) using block sizes of 16x16
+ * The algorithm is similar to 270 degree clockwise rotation algorithm
+ */
+static int opl_rotate90_u16_by16(const u8 *src, int src_line_stride, int width,
+                                int height, u8 *dst, int dst_line_stride,
+                                int vmirror)
+{
+       const int BLOCK_SIZE_PIXELS = CACHE_LINE_WORDS * BYTES_PER_WORD
+           / BYTES_PER_PIXEL;
+       const int BLOCK_SIZE_BYTES = BYTES_PER_PIXEL * BLOCK_SIZE_PIXELS;
+       const int IN_INDEX = src_line_stride * BLOCK_SIZE_PIXELS
+           + BYTES_PER_PIXEL;
+       const int OUT_INDEX = vmirror ?
+           -dst_line_stride - BLOCK_SIZE_BYTES
+           : dst_line_stride - BLOCK_SIZE_BYTES;
+       const u8 *in_block_ptr;
+       u8 *out_block_ptr;
+       int i, k;
+
+       for (k = height / BLOCK_SIZE_PIXELS; k > 0; k--) {
+               in_block_ptr = src + src_line_stride * (height - 1)
+                   - (src_line_stride * BLOCK_SIZE_PIXELS *
+                      (height / BLOCK_SIZE_PIXELS - k));
+               out_block_ptr = dst + BYTES_PER_PIXEL * BLOCK_SIZE_PIXELS *
+                   ((height / BLOCK_SIZE_PIXELS) - k);
+
+               /*
+                * For vertical mirroring the writing starts from the
+                * bottom line
+                */
+               if (vmirror)
+                       out_block_ptr += dst_line_stride * (width - 1);
+
+               for (i = width; i > 0; i--) {
+                       __asm__ volatile (
+                               "ldrh   r2, [%0], -%4\n\t"
+                               "ldrh   r3, [%0], -%4\n\t"
+                               "ldrh   r4, [%0], -%4\n\t"
+                               "ldrh   r5, [%0], -%4\n\t"
+                               "orr    r2, r2, r3, lsl #16\n\t"
+                               "orr    r4, r4, r5, lsl #16\n\t"
+                               "str    r2, [%1], #4\n\t"
+                               "str    r4, [%1], #4\n\t"
+
+                               "ldrh   r2, [%0], -%4\n\t"
+                               "ldrh   r3, [%0], -%4\n\t"
+                               "ldrh   r4, [%0], -%4\n\t"
+                               "ldrh   r5, [%0], -%4\n\t"
+                               "orr    r2, r2, r3, lsl #16\n\t"
+                               "orr    r4, r4, r5, lsl #16\n\t"
+                               "str    r2, [%1], #4\n\t"
+                               "str    r4, [%1], #4\n\t"
+
+                               "ldrh   r2, [%0], -%4\n\t"
+                               "ldrh   r3, [%0], -%4\n\t"
+                               "ldrh   r4, [%0], -%4\n\t"
+                               "ldrh   r5, [%0], -%4\n\t"
+                               "orr    r2, r2, r3, lsl #16\n\t"
+                               "orr    r4, r4, r5, lsl #16\n\t"
+                               "str    r2, [%1], #4\n\t"
+                               "str    r4, [%1], #4\n\t"
+
+                               "ldrh   r2, [%0], -%4\n\t"
+                               "ldrh   r3, [%0], -%4\n\t"
+                               "ldrh   r4, [%0], -%4\n\t"
+                               "ldrh   r5, [%0], -%4\n\t"
+                               "orr    r2, r2, r3, lsl #16\n\t"
+                               "orr    r4, r4, r5, lsl #16\n\t"
+                               "str    r2, [%1], #4\n\t"
+                               "str    r4, [%1], #4\n\t"
+
+                               : "+r" (in_block_ptr), "+r"(out_block_ptr)      /* output */
+                               : "0"(in_block_ptr), "1"(out_block_ptr), "r"(src_line_stride)   /* input  */
+                               : "r2", "r3", "r4", "r5", "memory"      /* modify */
+                       );
+                       in_block_ptr += IN_INDEX;
+                       out_block_ptr += OUT_INDEX;
+               }
+       }
+
+       return OPLERR_SUCCESS;
+}
+
+/*
+ * Performs clockwise rotation (and possibly vertical mirroring depending
+ * on the vmirror flag) using block sizes of 4x4
+ * The algorithm is similar to 270 degree clockwise rotation algorithm
+ */
+static int opl_rotate90_u16_by4(const u8 *src, int src_line_stride, int width,
+                               int height, u8 *dst, int dst_line_stride,
+                               int vmirror)
+{
+       const int BLOCK_SIZE_PIXELS = CACHE_LINE_WORDS * BYTES_PER_WORD
+           / BYTES_PER_PIXEL / 4;
+       const int BLOCK_SIZE_BYTES = BYTES_PER_PIXEL * BLOCK_SIZE_PIXELS;
+       const int IN_INDEX = src_line_stride * BLOCK_SIZE_PIXELS
+           + BYTES_PER_PIXEL;
+       const int OUT_INDEX = vmirror ?
+           -dst_line_stride - BLOCK_SIZE_BYTES
+           : dst_line_stride - BLOCK_SIZE_BYTES;
+       const u8 *in_block_ptr;
+       u8 *out_block_ptr;
+       int i, k;
+
+       for (k = height / BLOCK_SIZE_PIXELS; k > 0; k--) {
+               in_block_ptr = src + src_line_stride * (height - 1)
+                   - (src_line_stride * BLOCK_SIZE_PIXELS *
+                      (height / BLOCK_SIZE_PIXELS - k));
+               out_block_ptr = dst + BYTES_PER_PIXEL * BLOCK_SIZE_PIXELS
+                   * ((height / BLOCK_SIZE_PIXELS) - k);
+
+               /*
+                * For horizontal mirroring the writing starts from the
+                * bottom line
+                */
+               if (vmirror)
+                       out_block_ptr += dst_line_stride * (width - 1);
+
+               for (i = width; i > 0; i--) {
+                       __asm__ volatile (
+                               "ldrh   r2, [%0], -%4\n\t"
+                               "ldrh   r3, [%0], -%4\n\t"
+                               "ldrh   r4, [%0], -%4\n\t"
+                               "ldrh   r5, [%0], -%4\n\t"
+                               "orr    r2, r2, r3, lsl #16\n\t"
+                               "orr    r4, r4, r5, lsl #16\n\t"
+                               "str    r2, [%1], #4\n\t"
+                               "str    r4, [%1], #4\n\t"
+
+                               : "+r" (in_block_ptr), "+r"(out_block_ptr)      /* output */
+                               : "0"(in_block_ptr), "1"(out_block_ptr), "r"(src_line_stride)   /* input  */
+                               : "r2", "r3", "r4", "r5", "memory"      /* modify */
+                       );
+                       in_block_ptr += IN_INDEX;
+                       out_block_ptr += OUT_INDEX;
+               }
+       }
+
+       return OPLERR_SUCCESS;
+}
+
+EXPORT_SYMBOL(opl_rotate90_u16);
+EXPORT_SYMBOL(opl_rotate90_vmirror_u16);
diff --git a/drivers/media/video/mxc/opl/rotate90_u16_qcif.S b/drivers/media/video/mxc/opl/rotate90_u16_qcif.S
new file mode 100644 (file)
index 0000000..f76e0e6
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * 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
+ */
+#include <linux/linkage.h>
+
+       .text
+       .align  2
+ENTRY(opl_rotate90_u16_qcif)
+        STMFD    sp!,{r4-r10}
+        MOV      r12,#0x160
+        MOV      r10,#0x90
+        MOV      r3,r10,LSR #4
+.L1.216:
+        RSB      r2,r3,r10,LSR #4
+        MOV      r4,#0x20
+        SMULBB   r5,r4,r2
+        MOV      r4,#0x1600
+        SMULBB   r2,r4,r2
+        ADD      r4,r0,#0xc000
+        ADD      r4,r4,#0x4a0
+        SUB      r4,r4,r2
+        MOV      r2,r12,LSR #1
+        ADD      r5,r1,r5
+.L1.256:
+        LDRH     r6,[r4],-r12
+        LDRH     r7,[r4],-r12
+        LDRH     r8,[r4],-r12
+        LDRH     r9,[r4],-r12
+        ORR      r6,r6,r7,LSL #16
+        ORR      r7,r8,r9,LSL #16
+        STMIA    r5!,{r6,r7}
+        SUBS     r2,r2,#1
+        LDRH     r6,[r4],-r12
+        LDRH     r7,[r4],-r12
+        LDRH     r8,[r4],-r12
+        LDRH     r9,[r4],-r12
+        ORR      r6,r6,r7,LSL #16
+        ORR      r7,r8,r9,LSL #16
+        STMIA    r5!,{r6,r7}
+        LDRH     r6,[r4],-r12
+        LDRH     r7,[r4],-r12
+        LDRH     r8,[r4],-r12
+        LDRH     r9,[r4],-r12
+        ORR      r6,r6,r7,LSL #16
+        ORR      r7,r8,r9,LSL #16
+        STMIA    r5!,{r6,r7}
+        LDRH     r6,[r4],-r12
+        LDRH     r7,[r4],-r12
+        LDRH     r8,[r4],-r12
+        LDRH     r9,[r4],-r12
+        ORR      r6,r6,r7,LSL #16
+        ORR      r7,r8,r9,LSL #16
+        ADD      r4,r4,#0x1600
+        STMIA    r5!,{r6,r7}
+        ADD      r5,r5,#0x100
+        ADD      r4,r4,#2
+        BGT      .L1.256
+        SUBS     r3,r3,#1
+        BGT      .L1.216
+        LDMFD    sp!,{r4-r10}
+        BX       lr
+       .size   opl_rotate90_u16_qcif, . - opl_rotate90_u16_qcif
diff --git a/drivers/media/video/mxc/opl/vmirror_u16.c b/drivers/media/video/mxc/opl/vmirror_u16.c
new file mode 100644 (file)
index 0000000..05b6bbc
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/string.h>
+#include "opl.h"
+
+int opl_vmirror_u16(const u8 *src, int src_line_stride, int width, int height,
+                   u8 *dst, int dst_line_stride)
+{
+       const u8 *src_row_addr;
+       u8 *dst_row_addr;
+       int i;
+
+       if (!src || !dst)
+               return OPLERR_NULL_PTR;
+
+       if (width == 0 || height == 0 || src_line_stride == 0
+           || dst_line_stride == 0)
+               return OPLERR_BAD_ARG;
+
+       src_row_addr = src;
+       dst_row_addr = dst + (height - 1) * dst_line_stride;
+
+       /* Loop over all rows */
+       for (i = 0; i < height; i++) {
+               /* memcpy each row */
+               memcpy(dst_row_addr, src_row_addr, BYTES_PER_PIXEL * width);
+               src_row_addr += src_line_stride;
+               dst_row_addr -= dst_line_stride;
+       }
+
+       return OPLERR_SUCCESS;
+}
+
+EXPORT_SYMBOL(opl_vmirror_u16);
diff --git a/drivers/media/video/mxc/output/Kconfig b/drivers/media/video/mxc/output/Kconfig
new file mode 100644 (file)
index 0000000..2153ad2
--- /dev/null
@@ -0,0 +1,28 @@
+config VIDEO_MXC_IPU_OUTPUT
+       bool "IPU v4l2 support"
+       depends on VIDEO_MXC_OUTPUT && MXC_IPU
+       default y
+       ---help---
+         This is the video4linux2 driver for IPU post processing video output.
+
+config VIDEO_MXC_IPUV1_WVGA_OUTPUT
+       bool "IPUv1 WVGA v4l2 display support"
+       depends on VIDEO_MXC_OUTPUT && MXC_IPU
+       default n
+       ---help---
+         This is the video4linux2 driver for IPUv1 WVGA post processing video output.
+
+config VIDEO_MXC_EMMA_OUTPUT
+       bool
+       depends on VIDEO_MXC_OUTPUT && MXC_EMMA && FB_MXC_SYNC_PANEL
+       default y
+       ---help---
+         This is the video4linux2 driver for EMMA post processing video output.
+
+config VIDEO_MXC_OUTPUT_FBSYNC
+       bool "Synchronize the output with LCDC refresh"
+       depends on VIDEO_MXC_EMMA_OUTPUT
+       default y
+       ---help---
+         Synchronize the post-processing with LCDC EOF (End of Frame) to
+         prevent tearing issue. If unsure, say Y.
diff --git a/drivers/media/video/mxc/output/Makefile b/drivers/media/video/mxc/output/Makefile
new file mode 100644 (file)
index 0000000..5004425
--- /dev/null
@@ -0,0 +1,14 @@
+ifeq ($(CONFIG_VIDEO_MXC_EMMA_OUTPUT),y)
+       mx27_output-objs := mx27_v4l2_output.o mx27_pp.o
+       obj-$(CONFIG_VIDEO_MXC_OUTPUT) += mx27_output.o
+endif
+
+ifeq ($(CONFIG_VIDEO_MXC_IPU_OUTPUT),y)
+       obj-$(CONFIG_VIDEO_MXC_OUTPUT)  += mxc_v4l2_output.o
+endif
+ifeq ($(CONFIG_VIDEO_MXC_PXP_V4L2),y)
+       obj-$(CONFIG_VIDEO_MXC_PXP_V4L2)        += mxc_pxp_v4l2.o
+endif
+ifeq ($(CONFIG_VIDEO_MXC_IPUV1_WVGA_OUTPUT),y)
+       obj-$(CONFIG_VIDEO_MXC_OUTPUT)  += mx31_v4l2_wvga_output.o
+endif
diff --git a/drivers/media/video/mxc/output/mx31_v4l2_wvga_output.c b/drivers/media/video/mxc/output/mx31_v4l2_wvga_output.c
new file mode 100644 (file)
index 0000000..a165c61
--- /dev/null
@@ -0,0 +1,1928 @@
+/*
+ * Copyright 2005-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 drivers/media/video/mxc/output/mxc_v4l2_output.c
+ *
+ * @brief MXC V4L2 Video Output Driver
+ *
+ * Video4Linux2 Output Device using MXC IPU Post-processing functionality.
+ *
+ * @ingroup MXC_V4L2_OUTPUT
+ */
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <asm/cacheflush.h>
+#include <asm/io.h>
+#include <asm/semaphore.h>
+#include <linux/dma-mapping.h>
+
+#include <mach/mxcfb.h>
+#include <mach/ipu.h>
+
+#include "mxc_v4l2_output.h"
+
+vout_data *g_vout;
+#define SDC_FG_FB_FORMAT        IPU_PIX_FMT_RGB565
+
+struct v4l2_output mxc_outputs[2] = {
+       {
+        .index = MXC_V4L2_OUT_2_SDC,
+        .name = "DISP3 Video Out",
+        .type = V4L2_OUTPUT_TYPE_ANALOG,       /* not really correct,
+                                                  but no other choice */
+        .audioset = 0,
+        .modulator = 0,
+        .std = V4L2_STD_UNKNOWN},
+       {
+        .index = MXC_V4L2_OUT_2_ADC,
+        .name = "DISPx Video Out",
+        .type = V4L2_OUTPUT_TYPE_ANALOG,       /* not really correct,
+                                                  but no other choice */
+        .audioset = 0,
+        .modulator = 0,
+        .std = V4L2_STD_UNKNOWN}
+};
+
+static int video_nr = 16;
+static DEFINE_SPINLOCK(g_lock);
+static unsigned int g_pp_out_number;
+static unsigned int g_pp_in_number;
+
+/* debug counters */
+uint32_t g_irq_cnt;
+uint32_t g_buf_output_cnt;
+uint32_t g_buf_q_cnt;
+uint32_t g_buf_dq_cnt;
+
+static inline uint32_t channel_2_dma(ipu_channel_t ch, ipu_buffer_t type)
+{
+       return ((type == IPU_INPUT_BUFFER) ? ((uint32_t) ch & 0xFF) :
+               ((type == IPU_OUTPUT_BUFFER) ? (((uint32_t) ch >> 8) & 0xFF)
+                : (((uint32_t) ch >> 16) & 0xFF)));
+};
+
+static inline uint32_t DMAParamAddr(uint32_t dma_ch)
+{
+       return 0x10000 | (dma_ch << 4);
+};
+
+#define QUEUE_SIZE (MAX_FRAME_NUM + 1)
+static inline int queue_size(v4l_queue *q)
+{
+       if (q->tail >= q->head)
+               return q->tail - q->head;
+       else
+               return (q->tail + QUEUE_SIZE) - q->head;
+}
+
+static inline int queue_buf(v4l_queue *q, int idx)
+{
+       if (((q->tail + 1) % QUEUE_SIZE) == q->head)
+               return -1;      /* queue full */
+       q->list[q->tail] = idx;
+       q->tail = (q->tail + 1) % QUEUE_SIZE;
+       return 0;
+}
+
+static inline int dequeue_buf(v4l_queue *q)
+{
+       int ret;
+       if (q->tail == q->head)
+               return -1;      /* queue empty */
+       ret = q->list[q->head];
+       q->head = (q->head + 1) % QUEUE_SIZE;
+       return ret;
+}
+
+static inline int peek_next_buf(v4l_queue *q)
+{
+       if (q->tail == q->head)
+               return -1;      /* queue empty */
+       return q->list[q->head];
+}
+
+static inline unsigned long get_jiffies(struct timeval *t)
+{
+       struct timeval cur;
+
+       if (t->tv_usec >= 1000000) {
+               t->tv_sec += t->tv_usec / 1000000;
+               t->tv_usec = t->tv_usec % 1000000;
+       }
+
+       do_gettimeofday(&cur);
+       if ((t->tv_sec < cur.tv_sec)
+           || ((t->tv_sec == cur.tv_sec) && (t->tv_usec < cur.tv_usec)))
+               return jiffies;
+
+       if (t->tv_usec < cur.tv_usec) {
+               cur.tv_sec = t->tv_sec - cur.tv_sec - 1;
+               cur.tv_usec = t->tv_usec + 1000000 - cur.tv_usec;
+       } else {
+               cur.tv_sec = t->tv_sec - cur.tv_sec;
+               cur.tv_usec = t->tv_usec - cur.tv_usec;
+       }
+
+       return jiffies + timeval_to_jiffies(&cur);
+}
+
+/*!
+ * Private function to free buffers
+ *
+ * @param bufs_paddr   Array of physical address of buffers to be freed
+ *
+ * @param bufs_vaddr   Array of virtual address of buffers to be freed
+ *
+ * @param num_buf      Number of buffers to be freed
+ *
+ * @param size         Size for each buffer to be free
+ *
+ * @return status  0 success.
+ */
+static int mxc_free_buffers(dma_addr_t bufs_paddr[], void *bufs_vaddr[],
+                           int num_buf, int size)
+{
+       int i;
+
+       for (i = 0; i < num_buf; i++) {
+               if (bufs_vaddr[i] != 0) {
+                       dma_free_coherent(0, size, bufs_vaddr[i],
+                                         bufs_paddr[i]);
+                       pr_debug("freed @ paddr=0x%08X\n", (u32) bufs_paddr[i]);
+                       bufs_paddr[i] = 0;
+                       bufs_vaddr[i] = NULL;
+               }
+       }
+       return 0;
+}
+
+/*!
+ * Private function to allocate buffers
+ *
+ * @param bufs_paddr   Output array of physical address of buffers allocated
+ *
+ * @param bufs_vaddr   Output array of virtual address of buffers allocated
+ *
+ * @param num_buf      Input number of buffers to allocate
+ *
+ * @param size         Input size for each buffer to allocate
+ *
+ * @return status      -0 Successfully allocated a buffer, -ENOBUFS failed.
+ */
+static int mxc_allocate_buffers(dma_addr_t bufs_paddr[], void *bufs_vaddr[],
+                               int num_buf, int size)
+{
+       int i;
+
+       for (i = 0; i < num_buf; i++) {
+               bufs_vaddr[i] = dma_alloc_coherent(0, size,
+                                                  &bufs_paddr[i],
+                                                  GFP_DMA | GFP_KERNEL);
+
+               if (bufs_vaddr[i] == 0) {
+                       mxc_free_buffers(bufs_paddr, bufs_vaddr, i, size);
+                       printk(KERN_ERR "dma_alloc_coherent failed.\n");
+                       return -ENOBUFS;
+               }
+               pr_debug("allocated @ paddr=0x%08X, size=%d.\n",
+                        (u32) bufs_paddr[i], size);
+       }
+
+       return 0;
+}
+
+/*
+ * Returns bits per pixel for given pixel format
+ *
+ * @param pixelformat  V4L2_PIX_FMT_RGB565,
+ *                    V4L2_PIX_FMT_BGR24 or V4L2_PIX_FMT_BGR32
+ *
+ * @return bits per pixel of pixelformat
+ */
+static u32 fmt_to_bpp(u32 pixelformat)
+{
+       u32 bpp;
+
+       switch (pixelformat) {
+       case V4L2_PIX_FMT_RGB565:
+               bpp = 16;
+               break;
+       case V4L2_PIX_FMT_BGR24:
+       case V4L2_PIX_FMT_RGB24:
+               bpp = 24;
+               break;
+       case V4L2_PIX_FMT_BGR32:
+       case V4L2_PIX_FMT_RGB32:
+               bpp = 32;
+               break;
+       default:
+               bpp = 8;
+               break;
+       }
+       return bpp;
+}
+
+static u32 bpp_to_fmt(struct fb_info *fbi)
+{
+       if (fbi->var.nonstd)
+               return fbi->var.nonstd;
+
+       if (fbi->var.bits_per_pixel == 24)
+               return V4L2_PIX_FMT_BGR24;
+       else if (fbi->var.bits_per_pixel == 32)
+               return V4L2_PIX_FMT_BGR32;
+       else if (fbi->var.bits_per_pixel == 16)
+               return V4L2_PIX_FMT_RGB565;
+
+       return 0;
+}
+
+static void mxc_v4l2out_timer_handler(unsigned long arg)
+{
+       int index;
+       unsigned long timeout;
+       unsigned long lock_flags = 0;
+       vout_data *vout = (vout_data *) arg;
+
+       dev_dbg(vout->video_dev->dev, "timer handler: %lu\n", jiffies);
+
+       spin_lock_irqsave(&g_lock, lock_flags);
+
+       if ((vout->state == STATE_STREAM_STOPPING)
+           || (vout->state == STATE_STREAM_OFF))
+               goto exit0;
+       /*
+        * If timer occurs before IPU h/w is ready, then set the state to
+        * paused and the timer will be set again when next buffer is queued
+        * or PP comletes
+        */
+       if (vout->ipu_buf[0] != -1) {
+               dev_dbg(vout->video_dev->dev, "IPU buffer busy\n");
+               vout->state = STATE_STREAM_PAUSED;
+               goto exit0;
+       }
+
+       /* One frame buffer should be ready here */
+       if (vout->frame_count % 2 == 1) {
+               /* set BUF0 rdy */
+               if (ipu_select_buffer(vout->display_ch, IPU_INPUT_BUFFER, 0) <
+                   0)
+                       pr_debug("error selecting display buf 0");
+       } else {
+               if (ipu_select_buffer(vout->display_ch, IPU_INPUT_BUFFER, 1) <
+                   0)
+                       pr_debug("error selecting display buf 1");
+       }
+
+       /* Dequeue buffer and pass to IPU */
+       index = dequeue_buf(&vout->ready_q);
+       if (index == -1) {      /* no buffers ready, should never occur */
+               dev_err(vout->video_dev->dev,
+                       "mxc_v4l2out: timer - no queued buffers ready\n");
+               goto exit0;
+       }
+
+       g_buf_dq_cnt++;
+       vout->frame_count++;
+       vout->ipu_buf[1] = vout->ipu_buf[0] = index;
+
+       if (ipu_update_channel_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER,
+                                     0,
+                                     vout->v4l2_bufs[vout->ipu_buf[0]].m.
+                                     offset) < 0)
+               goto exit0;
+
+       if (ipu_update_channel_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER,
+                                     1,
+                                     vout->v4l2_bufs[vout->ipu_buf[0]].m.
+                                     offset + vout->v2f.fmt.pix.width / 2) < 0)
+               goto exit0;
+
+       /* All buffer should now ready in IPU out, tranfer to display buf */
+       if (ipu_update_channel_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER,
+                                     0,
+                                     vout->
+                                     display_bufs[(vout->frame_count -
+                                                   1) % 2]) < 0) {
+               dev_err(vout->video_dev->dev,
+                       "unable to update buffer %d address\n",
+                       vout->next_rdy_ipu_buf);
+               goto exit0;
+       }
+       if (ipu_update_channel_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER,
+                                     1,
+                                     vout->
+                                     display_bufs[(vout->frame_count -
+                                                   1) % 2] +
+                                     vout->crop_current.width / 2 *
+                                     bytes_per_pixel(SDC_FG_FB_FORMAT)) < 0) {
+               dev_err(vout->video_dev->dev,
+                       "unable to update buffer %d address\n",
+                       vout->next_rdy_ipu_buf);
+               goto exit0;
+       }
+
+       if (ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 0) < 0) {
+               dev_err(vout->video_dev->dev,
+                       "unable to set IPU buffer ready\n");
+               goto exit0;
+       }
+
+       if (ipu_select_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, 0) < 0) {
+               dev_err(vout->video_dev->dev,
+                       "unable to set IPU buffer ready\n");
+               goto exit0;
+       }
+
+       /* Setup timer for next buffer */
+       index = peek_next_buf(&vout->ready_q);
+       if (index != -1) {
+               /* if timestamp is 0, then default to 30fps */
+               if ((vout->v4l2_bufs[index].timestamp.tv_sec == 0)
+                   && (vout->v4l2_bufs[index].timestamp.tv_usec == 0))
+                       timeout =
+                           vout->start_jiffies + vout->frame_count * HZ / 30;
+               else
+                       timeout =
+                           get_jiffies(&vout->v4l2_bufs[index].timestamp);
+
+               if (jiffies >= timeout) {
+                       dev_dbg(vout->video_dev->dev,
+                               "warning: timer timeout already expired.\n");
+               }
+               if (mod_timer(&vout->output_timer, timeout))
+                       dev_dbg(vout->video_dev->dev,
+                               "warning: timer was already set\n");
+
+               dev_dbg(vout->video_dev->dev,
+                       "timer handler next schedule: %lu\n", timeout);
+       } else {
+               vout->state = STATE_STREAM_PAUSED;
+       }
+
+exit0:
+       spin_unlock_irqrestore(&g_lock, lock_flags);
+}
+
+extern void _ipu_write_param_mem(uint32_t addr, uint32_t *data,
+                                uint32_t numWords);
+
+static irqreturn_t mxc_v4l2out_pp_in_irq_handler(int irq, void *dev_id)
+{
+       unsigned long lock_flags = 0;
+       vout_data *vout = dev_id;
+       uint32_t u_offset;
+       uint32_t v_offset;
+       uint32_t local_params[4];
+       uint32_t width, height;
+       uint32_t dma_chan;
+
+       spin_lock_irqsave(&g_lock, lock_flags);
+       g_irq_cnt++;
+
+       dma_chan = channel_2_dma(vout->post_proc_ch, IPU_INPUT_BUFFER);
+       memset(&local_params, 0, sizeof(local_params));
+
+       if (g_pp_in_number % 2 == 1) {
+               u_offset = vout->offset.u_offset - vout->v2f.fmt.pix.width / 4;
+               v_offset = vout->offset.v_offset - vout->v2f.fmt.pix.width / 4;
+               width = vout->v2f.fmt.pix.width / 2;
+               height = vout->v2f.fmt.pix.height;
+               local_params[3] =
+                   (uint32_t) ((width - 1) << 12) | ((uint32_t) (height -
+                                                                 1) << 24);
+               local_params[1] = (1UL << (46 - 32)) | (u_offset << (53 - 32));
+               local_params[2] = u_offset >> (64 - 53);
+               local_params[2] |= v_offset << (79 - 64);
+               local_params[3] |= v_offset >> (96 - 79);
+               _ipu_write_param_mem(DMAParamAddr(dma_chan), local_params, 4);
+
+               if (ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 1) <
+                   0) {
+                       dev_err(vout->video_dev->dev,
+                               "unable to set IPU buffer ready\n");
+               }
+       } else {
+               u_offset = vout->offset.u_offset;
+               v_offset = vout->offset.v_offset;
+               width = vout->v2f.fmt.pix.width / 2;
+               height = vout->v2f.fmt.pix.height;
+               local_params[3] =
+                   (uint32_t) ((width - 1) << 12) | ((uint32_t) (height -
+                                                                 1) << 24);
+               local_params[1] = (1UL << (46 - 32)) | (u_offset << (53 - 32));
+               local_params[2] = u_offset >> (64 - 53);
+               local_params[2] |= v_offset << (79 - 64);
+               local_params[3] |= v_offset >> (96 - 79);
+               _ipu_write_param_mem(DMAParamAddr(dma_chan), local_params, 4);
+       }
+       g_pp_in_number++;
+
+       spin_unlock_irqrestore(&g_lock, lock_flags);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t mxc_v4l2out_pp_out_irq_handler(int irq, void *dev_id)
+{
+       vout_data *vout = dev_id;
+       int index;
+       unsigned long timeout;
+       u32 lock_flags = 0;
+
+       spin_lock_irqsave(&g_lock, lock_flags);
+
+       if (g_pp_out_number % 2 == 1) {
+               if (ipu_select_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, 1)
+                   < 0) {
+                       dev_err(vout->video_dev->dev,
+                               "unable to set IPU buffer ready\n");
+               }
+       } else {
+               if (vout->ipu_buf[0] != -1) {
+                       vout->v4l2_bufs[vout->ipu_buf[0]].flags =
+                           V4L2_BUF_FLAG_DONE;
+                       queue_buf(&vout->done_q, vout->ipu_buf[0]);
+                       wake_up_interruptible(&vout->v4l_bufq);
+                       vout->ipu_buf[0] = -1;
+               }
+               index = peek_next_buf(&vout->ready_q);
+               if (vout->state == STATE_STREAM_STOPPING) {
+                       if ((vout->ipu_buf[0] == -1)
+                           && (vout->ipu_buf[1] == -1))
+                               vout->state = STATE_STREAM_OFF;
+               } else if ((vout->state == STATE_STREAM_PAUSED)
+                          && (index != -1)) {
+                       /*!
+                        * Setup timer for next buffer,
+                        * when stream has been paused
+                        */
+                       pr_debug("next index %d\n", index);
+
+                       /* if timestamp is 0, then default to 30fps */
+                       if ((vout->v4l2_bufs[index].timestamp.tv_sec == 0)
+                           && (vout->v4l2_bufs[index].timestamp.tv_usec == 0))
+                               timeout =
+                                   vout->start_jiffies +
+                                   vout->frame_count * HZ / 30;
+                       else
+                               timeout =
+                                   get_jiffies(&vout->v4l2_bufs[index].
+                                               timestamp);
+
+                       if (jiffies >= timeout) {
+                               pr_debug
+                                   ("warning: timer timeout"
+                                    "already expired.\n");
+                       }
+
+                       vout->state = STATE_STREAM_ON;
+
+                       if (mod_timer(&vout->output_timer, timeout))
+                               pr_debug("warning: timer was already set\n");
+
+                       pr_debug("timer handler next schedule: %lu\n", timeout);
+               }
+       }
+       g_pp_out_number++;
+
+       spin_unlock_irqrestore(&g_lock, lock_flags);
+       return IRQ_HANDLED;
+}
+
+/*!
+ * Start the output stream
+ *
+ * @param vout      structure vout_data *
+ *
+ * @return status  0 Success
+ */
+static int mxc_v4l2out_streamon(vout_data *vout)
+{
+       struct device *dev = vout->video_dev->dev;
+       ipu_channel_params_t params;
+       struct mxcfb_pos fb_pos;
+       struct fb_var_screeninfo fbvar;
+       struct fb_info *fbi =
+           registered_fb[vout->output_fb_num[vout->cur_disp_output]];
+       int pp_in_buf[2];
+       u16 out_width;
+       u16 out_height;
+       ipu_channel_t display_input_ch = MEM_PP_MEM;
+       bool use_direct_adc = false;
+       mm_segment_t old_fs;
+
+       if (!vout)
+               return -EINVAL;
+
+       if (vout->state != STATE_STREAM_OFF)
+               return -EBUSY;
+
+       if (queue_size(&vout->ready_q) < 2) {
+               dev_err(dev, "2 buffers not been queued yet!\n");
+               return -EINVAL;
+       }
+
+       out_width = vout->crop_current.width;
+       out_height = vout->crop_current.height;
+
+       vout->next_done_ipu_buf = vout->next_rdy_ipu_buf = 0;
+       vout->ipu_buf[0] = pp_in_buf[0] = dequeue_buf(&vout->ready_q);
+       vout->ipu_buf[1] = pp_in_buf[1] = vout->ipu_buf[0];
+       vout->frame_count = 1;
+       g_pp_out_number = 1;
+       g_pp_in_number = 1;
+
+       ipu_enable_irq(IPU_IRQ_PP_IN_EOF);
+       ipu_enable_irq(IPU_IRQ_PP_OUT_EOF);
+
+       /* Init Display Channel */
+#ifdef CONFIG_FB_MXC_ASYNC_PANEL
+       if (vout->cur_disp_output < DISP3) {
+               mxcfb_set_refresh_mode(fbi, MXCFB_REFRESH_OFF, 0);
+               fbi = NULL;
+               if (ipu_can_rotate_in_place(vout->rotate)) {
+                       dev_dbg(dev, "Using PP direct to ADC channel\n");
+                       use_direct_adc = true;
+                       vout->display_ch = MEM_PP_ADC;
+                       vout->post_proc_ch = MEM_PP_ADC;
+
+                       memset(&params, 0, sizeof(params));
+                       params.mem_pp_adc.in_width = vout->v2f.fmt.pix.width;
+                       params.mem_pp_adc.in_height = vout->v2f.fmt.pix.height;
+                       params.mem_pp_adc.in_pixel_fmt =
+                           vout->v2f.fmt.pix.pixelformat;
+                       params.mem_pp_adc.out_width = out_width;
+                       params.mem_pp_adc.out_height = out_height;
+                       params.mem_pp_adc.out_pixel_fmt = SDC_FG_FB_FORMAT;
+#ifdef CONFIG_FB_MXC_EPSON_PANEL
+                       params.mem_pp_adc.out_left =
+                           2 + vout->crop_current.left;
+#else
+                       params.mem_pp_adc.out_left =
+                           12 + vout->crop_current.left;
+#endif
+                       params.mem_pp_adc.out_top = vout->crop_current.top;
+                       if (ipu_init_channel(
+                               vout->post_proc_ch, &params) != 0) {
+                               dev_err(dev, "Error initializing PP chan\n");
+                               return -EINVAL;
+                       }
+
+                       if (ipu_init_channel_buffer(vout->post_proc_ch,
+                                                   IPU_INPUT_BUFFER,
+                                                   params.mem_pp_adc.
+                                                   in_pixel_fmt,
+                                                   params.mem_pp_adc.in_width,
+                                                   params.mem_pp_adc.in_height,
+                                                   vout->v2f.fmt.pix.
+                                                   bytesperline /
+                                                   bytes_per_pixel(params.
+                                                       mem_pp_adc.
+                                                       in_pixel_fmt),
+                                                   vout->rotate,
+                                                   vout->
+                                                   v4l2_bufs[pp_in_buf[0]].m.
+                                                   offset,
+                                                   vout->
+                                                   v4l2_bufs[pp_in_buf[1]].m.
+                                                   offset,
+                                                   vout->offset.u_offset,
+                                                   vout->offset.v_offset) !=
+                           0) {
+                               dev_err(dev, "Error initializing PP in buf\n");
+                               return -EINVAL;
+                       }
+
+                       if (ipu_init_channel_buffer(vout->post_proc_ch,
+                                                   IPU_OUTPUT_BUFFER,
+                                                   params.mem_pp_adc.
+                                                   out_pixel_fmt, out_width,
+                                                   out_height, out_width,
+                                                   vout->rotate, 0, 0, 0,
+                                                   0) != 0) {
+                               dev_err(dev,
+                                       "Error initializing PP"
+                                       "output buffer\n");
+                               return -EINVAL;
+                       }
+
+               } else {
+                       dev_dbg(dev, "Using ADC SYS2 channel\n");
+                       vout->display_ch = ADC_SYS2;
+                       vout->post_proc_ch = MEM_PP_MEM;
+
+                       if (vout->display_bufs[0]) {
+                               mxc_free_buffers(vout->display_bufs,
+                                                vout->display_bufs_vaddr,
+                                                2, vout->display_buf_size);
+                       }
+
+                       vout->display_buf_size = vout->crop_current.width *
+                           vout->crop_current.height *
+                           fmt_to_bpp(SDC_FG_FB_FORMAT) / 8;
+                       mxc_allocate_buffers(vout->display_bufs,
+                                            vout->display_bufs_vaddr,
+                                            2, vout->display_buf_size);
+
+                       memset(&params, 0, sizeof(params));
+                       params.adc_sys2.disp = vout->cur_disp_output;
+                       params.adc_sys2.ch_mode = WriteTemplateNonSeq;
+#ifdef CONFIG_FB_MXC_EPSON_PANEL
+                       params.adc_sys2.out_left = 2 + vout->crop_current.left;
+#else
+                       params.adc_sys2.out_left = 12 + vout->crop_current.left;
+#endif
+                       params.adc_sys2.out_top = vout->crop_current.top;
+                       if (ipu_init_channel(ADC_SYS2, &params) < 0)
+                               return -EINVAL;
+
+                       if (ipu_init_channel_buffer(vout->display_ch,
+                                                   IPU_INPUT_BUFFER,
+                                                   SDC_FG_FB_FORMAT,
+                                                   out_width, out_height,
+                                                   out_width, IPU_ROTATE_NONE,
+                                                   vout->display_bufs[0],
+                                                   vout->display_bufs[1], 0,
+                                                   0) != 0) {
+                               dev_err(dev,
+                                       "Error initializing SDC FG buffer\n");
+                               return -EINVAL;
+                       }
+               }
+       } else
+#endif
+       {                       /* Use SDC */
+               dev_dbg(dev, "Using SDC channel\n");
+
+               fbvar = fbi->var;
+               if (vout->cur_disp_output == 3) {
+                       vout->display_ch = MEM_FG_SYNC;
+                       fbvar.bits_per_pixel = 16;
+                       fbvar.nonstd = IPU_PIX_FMT_UYVY;
+
+                       fbvar.xres = fbvar.xres_virtual = out_width;
+                       fbvar.yres = out_height;
+                       fbvar.yres_virtual = out_height * 2;
+               } else if (vout->cur_disp_output == 5) {
+                       vout->display_ch = MEM_DC_SYNC;
+                       fbvar.bits_per_pixel = 16;
+                       fbvar.nonstd = IPU_PIX_FMT_UYVY;
+
+                       fbvar.xres = fbvar.xres_virtual = out_width;
+                       fbvar.yres = out_height;
+                       fbvar.yres_virtual = out_height * 2;
+               } else {
+                       vout->display_ch = MEM_BG_SYNC;
+               }
+
+               fbvar.activate |= FB_ACTIVATE_FORCE;
+               fb_set_var(fbi, &fbvar);
+
+               fb_pos.x = vout->crop_current.left;
+               fb_pos.y = vout->crop_current.top;
+               if (fbi->fbops->fb_ioctl) {
+                       old_fs = get_fs();
+                       set_fs(KERNEL_DS);
+                       fbi->fbops->fb_ioctl(fbi, MXCFB_SET_OVERLAY_POS,
+                                            (unsigned long)&fb_pos);
+                       set_fs(old_fs);
+               }
+
+               vout->display_bufs[1] = fbi->fix.smem_start;
+               vout->display_bufs[0] = fbi->fix.smem_start +
+                   (fbi->fix.line_length * fbi->var.yres);
+               vout->display_buf_size = vout->crop_current.width *
+                   vout->crop_current.height * fbi->var.bits_per_pixel / 8;
+
+               vout->post_proc_ch = MEM_PP_MEM;
+       }
+
+       /* Init PP */
+       if (use_direct_adc == false) {
+               if (vout->rotate >= IPU_ROTATE_90_RIGHT) {
+                       out_width = vout->crop_current.height;
+                       out_height = vout->crop_current.width;
+               }
+               memset(&params, 0, sizeof(params));
+               params.mem_pp_mem.in_width = vout->v2f.fmt.pix.width / 2;
+               params.mem_pp_mem.in_height = vout->v2f.fmt.pix.height;
+               params.mem_pp_mem.in_pixel_fmt = vout->v2f.fmt.pix.pixelformat;
+               params.mem_pp_mem.out_width = out_width / 2;
+               params.mem_pp_mem.out_height = out_height;
+               if (vout->display_ch == ADC_SYS2)
+                       params.mem_pp_mem.out_pixel_fmt = SDC_FG_FB_FORMAT;
+               else
+                       params.mem_pp_mem.out_pixel_fmt = bpp_to_fmt(fbi);
+               if (ipu_init_channel(vout->post_proc_ch, &params) != 0) {
+                       dev_err(dev, "Error initializing PP channel\n");
+                       return -EINVAL;
+               }
+
+               if (ipu_init_channel_buffer(vout->post_proc_ch,
+                                           IPU_INPUT_BUFFER,
+                                           params.mem_pp_mem.in_pixel_fmt,
+                                           params.mem_pp_mem.in_width,
+                                           params.mem_pp_mem.in_height,
+                                           vout->v2f.fmt.pix.bytesperline /
+                                           bytes_per_pixel(params.mem_pp_mem.
+                                                           in_pixel_fmt),
+                                           IPU_ROTATE_NONE,
+                                           vout->v4l2_bufs[pp_in_buf[0]].m.
+                                           offset,
+                                           vout->v4l2_bufs[pp_in_buf[0]].m.
+                                           offset + params.mem_pp_mem.in_width,
+                                           vout->offset.u_offset,
+                                           vout->offset.v_offset) != 0) {
+                       dev_err(dev, "Error initializing PP input buffer\n");
+                       return -EINVAL;
+               }
+
+               if (!ipu_can_rotate_in_place(vout->rotate)) {
+                       if (vout->rot_pp_bufs[0]) {
+                               mxc_free_buffers(vout->rot_pp_bufs,
+                                                vout->rot_pp_bufs_vaddr, 2,
+                                                vout->display_buf_size);
+                       }
+                       if (mxc_allocate_buffers
+                           (vout->rot_pp_bufs, vout->rot_pp_bufs_vaddr, 2,
+                            vout->display_buf_size) < 0)
+                               return -ENOBUFS;
+
+                       if (ipu_init_channel_buffer(vout->post_proc_ch,
+                                                   IPU_OUTPUT_BUFFER,
+                                                   params.mem_pp_mem.
+                                                   out_pixel_fmt, out_width,
+                                                   out_height, out_width,
+                                                   IPU_ROTATE_NONE,
+                                                   vout->rot_pp_bufs[0],
+                                                   vout->rot_pp_bufs[1], 0,
+                                                   0) != 0) {
+                               dev_err(dev,
+                                       "Error initializing"
+                                       "PP output buffer\n");
+                               return -EINVAL;
+                       }
+
+                       if (ipu_init_channel(MEM_ROT_PP_MEM, NULL) != 0) {
+                               dev_err(dev,
+                                       "Error initializing PP ROT channel\n");
+                               return -EINVAL;
+                       }
+
+                       if (ipu_init_channel_buffer(MEM_ROT_PP_MEM,
+                                                   IPU_INPUT_BUFFER,
+                                                   params.mem_pp_mem.
+                                                   out_pixel_fmt, out_width,
+                                                   out_height, out_width,
+                                                   vout->rotate,
+                                                   vout->rot_pp_bufs[0],
+                                                   vout->rot_pp_bufs[1], 0,
+                                                   0) != 0) {
+                               dev_err(dev,
+                                       "Error initializing PP ROT"
+                                       "input buffer\n");
+                               return -EINVAL;
+                       }
+
+                       /* swap width and height */
+                       if (vout->rotate >= IPU_ROTATE_90_RIGHT) {
+                               out_width = vout->crop_current.width;
+                               out_height = vout->crop_current.height;
+                       }
+
+                       if (ipu_init_channel_buffer(MEM_ROT_PP_MEM,
+                                                   IPU_OUTPUT_BUFFER,
+                                                   params.mem_pp_mem.
+                                                   out_pixel_fmt, out_width,
+                                                   out_height, out_width,
+                                                   IPU_ROTATE_NONE,
+                                                   vout->display_bufs[0],
+                                                   vout->display_bufs[1], 0,
+                                                   0) != 0) {
+                               dev_err(dev,
+                                       "Error initializing PP"
+                                       "output buffer\n");
+                               return -EINVAL;
+                       }
+
+                       if (ipu_link_channels(vout->post_proc_ch,
+                                             MEM_ROT_PP_MEM) < 0)
+                               return -EINVAL;
+
+                       ipu_select_buffer(MEM_ROT_PP_MEM, IPU_OUTPUT_BUFFER, 0);
+                       ipu_select_buffer(MEM_ROT_PP_MEM, IPU_OUTPUT_BUFFER, 1);
+
+                       ipu_enable_channel(MEM_ROT_PP_MEM);
+
+                       display_input_ch = MEM_ROT_PP_MEM;
+               } else {
+                       if (ipu_init_channel_buffer(vout->post_proc_ch,
+                                                   IPU_OUTPUT_BUFFER,
+                                                   params.mem_pp_mem.
+                                                   out_pixel_fmt,
+                                                   out_width / 2,
+                                                   out_height,
+                                                   out_width,
+                                                   vout->rotate,
+                                                   vout->display_bufs[0],
+                                                   vout->display_bufs[0]
+                                                   +
+                                                   out_width / 2 *
+                                                   bytes_per_pixel
+                                                   (SDC_FG_FB_FORMAT), 0,
+                                                   0) != 0) {
+                               dev_err(dev,
+                                       "Error initializing PP"
+                                       "output buffer\n");
+                               return -EINVAL;
+                       }
+               }
+               if (ipu_unlink_channels(
+                       display_input_ch, vout->display_ch) < 0) {
+                       dev_err(dev, "Error linking ipu channels\n");
+                       return -EINVAL;
+               }
+       }
+
+       vout->state = STATE_STREAM_PAUSED;
+
+       ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 0);
+
+       if (use_direct_adc == false) {
+               ipu_select_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, 0);
+               ipu_enable_channel(vout->post_proc_ch);
+
+               if (fbi) {
+                       acquire_console_sem();
+                       fb_blank(fbi, FB_BLANK_UNBLANK);
+                       release_console_sem();
+               } else {
+                       ipu_enable_channel(vout->display_ch);
+               }
+       } else {
+               ipu_enable_channel(vout->post_proc_ch);
+       }
+
+       vout->start_jiffies = jiffies;
+       dev_dbg(dev,
+               "streamon: start time = %lu jiffies\n", vout->start_jiffies);
+
+       return 0;
+}
+
+/*!
+ * Shut down the voutera
+ *
+ * @param vout      structure vout_data *
+ *
+ * @return status  0 Success
+ */
+static int mxc_v4l2out_streamoff(vout_data *vout)
+{
+       struct fb_info *fbi =
+           registered_fb[vout->output_fb_num[vout->cur_disp_output]];
+       int i, retval = 0;
+       unsigned long lockflag = 0;
+
+       if (!vout)
+               return -EINVAL;
+
+       if (vout->state == STATE_STREAM_OFF)
+               return 0;
+
+       spin_lock_irqsave(&g_lock, lockflag);
+
+       del_timer(&vout->output_timer);
+
+       if (vout->state == STATE_STREAM_ON)
+               vout->state = STATE_STREAM_STOPPING;
+
+       ipu_disable_irq(IPU_IRQ_PP_IN_EOF);
+       ipu_disable_irq(IPU_IRQ_PP_OUT_EOF);
+
+       spin_unlock_irqrestore(&g_lock, lockflag);
+
+       if (vout->post_proc_ch == MEM_PP_MEM) { /* SDC or ADC with Rotation */
+               if (!ipu_can_rotate_in_place(vout->rotate)) {
+                       ipu_unlink_channels(MEM_PP_MEM, MEM_ROT_PP_MEM);
+                       ipu_unlink_channels(MEM_ROT_PP_MEM, vout->display_ch);
+                       ipu_disable_channel(MEM_ROT_PP_MEM, true);
+
+                       if (vout->rot_pp_bufs[0]) {
+                               mxc_free_buffers(vout->rot_pp_bufs,
+                                                vout->rot_pp_bufs_vaddr, 2,
+                                                vout->display_buf_size);
+                       }
+               } else {
+                       ipu_unlink_channels(MEM_PP_MEM, vout->display_ch);
+               }
+               ipu_disable_channel(MEM_PP_MEM, true);
+
+               if (vout->display_ch == ADC_SYS2) {
+                       ipu_disable_channel(vout->display_ch, true);
+                       ipu_uninit_channel(vout->display_ch);
+               } else {
+                       fbi->var.activate |= FB_ACTIVATE_FORCE;
+                       fb_set_var(fbi, &fbi->var);
+
+                       if (vout->display_ch == MEM_FG_SYNC) {
+                               acquire_console_sem();
+                               fb_blank(fbi, FB_BLANK_POWERDOWN);
+                               release_console_sem();
+                       }
+
+                       vout->display_bufs[0] = 0;
+                       vout->display_bufs[1] = 0;
+               }
+
+               ipu_uninit_channel(MEM_PP_MEM);
+               if (!ipu_can_rotate_in_place(vout->rotate))
+                       ipu_uninit_channel(MEM_ROT_PP_MEM);
+       } else {                /* ADC Direct */
+               ipu_disable_channel(MEM_PP_ADC, true);
+               ipu_uninit_channel(MEM_PP_ADC);
+       }
+       vout->ready_q.head = vout->ready_q.tail = 0;
+       vout->done_q.head = vout->done_q.tail = 0;
+       for (i = 0; i < vout->buffer_cnt; i++) {
+               vout->v4l2_bufs[i].flags = 0;
+               vout->v4l2_bufs[i].timestamp.tv_sec = 0;
+               vout->v4l2_bufs[i].timestamp.tv_usec = 0;
+       }
+
+       vout->state = STATE_STREAM_OFF;
+
+#ifdef CONFIG_FB_MXC_ASYNC_PANEL
+       if (vout->cur_disp_output < DISP3) {
+               if (vout->display_bufs[0] != 0) {
+                       mxc_free_buffers(vout->display_bufs,
+                                        vout->display_bufs_vaddr, 2,
+                                        vout->display_buf_size);
+               }
+
+               mxcfb_set_refresh_mode(registered_fb
+                                      [vout->
+                                       output_fb_num[vout->cur_disp_output]],
+                                      MXCFB_REFRESH_PARTIAL, 0);
+       }
+#endif
+
+       return retval;
+}
+
+/*
+ * Valid whether the palette is supported
+ *
+ * @param palette  V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_BGR24 or V4L2_PIX_FMT_BGR32
+ *
+ * @return 1 if supported, 0 if failed
+ */
+static inline int valid_mode(u32 palette)
+{
+       return ((palette == V4L2_PIX_FMT_RGB565) ||
+               (palette == V4L2_PIX_FMT_BGR24) ||
+               (palette == V4L2_PIX_FMT_RGB24) ||
+               (palette == V4L2_PIX_FMT_BGR32) ||
+               (palette == V4L2_PIX_FMT_RGB32) ||
+               (palette == V4L2_PIX_FMT_NV12) ||
+               (palette == V4L2_PIX_FMT_YUV422P) ||
+               (palette == V4L2_PIX_FMT_YUV420));
+}
+
+/*
+ * V4L2 - Handles VIDIOC_G_FMT Ioctl
+ *
+ * @param vout         structure vout_data *
+ *
+ * @param v4l2_format structure v4l2_format *
+ *
+ * @return  status    0 success, EINVAL failed
+ */
+static int mxc_v4l2out_g_fmt(vout_data *vout, struct v4l2_format *f)
+{
+       if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+               return -EINVAL;
+       *f = vout->v2f;
+       return 0;
+}
+
+/*
+ * V4L2 - Handles VIDIOC_S_FMT Ioctl
+ *
+ * @param vout         structure vout_data *
+ *
+ * @param v4l2_format structure v4l2_format *
+ *
+ * @return  status    0 success, EINVAL failed
+ */
+static int mxc_v4l2out_s_fmt(vout_data *vout, struct v4l2_format *f)
+{
+       int retval = 0;
+       u32 size = 0;
+       u32 bytesperline;
+
+       if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+               retval = -EINVAL;
+               goto err0;
+       }
+       if (!valid_mode(f->fmt.pix.pixelformat)) {
+               dev_err(vout->video_dev->dev, "pixel format not supported\n");
+               retval = -EINVAL;
+               goto err0;
+       }
+
+       bytesperline = (f->fmt.pix.width * fmt_to_bpp(f->fmt.pix.pixelformat)) /
+           8;
+       if (f->fmt.pix.bytesperline < bytesperline) {
+               f->fmt.pix.bytesperline = bytesperline;
+       } else {
+               bytesperline = f->fmt.pix.bytesperline;
+       }
+
+       switch (f->fmt.pix.pixelformat) {
+       case V4L2_PIX_FMT_YUV422P:
+               /* byteperline for YUV planar formats is for
+                  Y plane only */
+               size = bytesperline * f->fmt.pix.height * 2;
+               break;
+       case V4L2_PIX_FMT_YUV420:
+       case V4L2_PIX_FMT_NV12:
+               size = (bytesperline * f->fmt.pix.height * 3) / 2;
+               break;
+       default:
+               size = bytesperline * f->fmt.pix.height;
+               break;
+       }
+
+       /* Return the actual size of the image to the app */
+       if (f->fmt.pix.sizeimage < size)
+               f->fmt.pix.sizeimage = size;
+       else
+               size = f->fmt.pix.sizeimage;
+
+       vout->v2f.fmt.pix = f->fmt.pix;
+       if (vout->v2f.fmt.pix.priv != 0) {
+               if (copy_from_user(&vout->offset,
+                                  (void *)vout->v2f.fmt.pix.priv,
+                                  sizeof(vout->offset))) {
+                       retval = -EFAULT;
+                       goto err0;
+               }
+       } else {
+               vout->offset.u_offset = 0;
+               vout->offset.v_offset = 0;
+       }
+
+       retval = 0;
+err0:
+       return retval;
+}
+
+/*
+ * V4L2 - Handles VIDIOC_G_CTRL Ioctl
+ *
+ * @param vout         structure vout_data *
+ *
+ * @param c           structure v4l2_control *
+ *
+ * @return  status    0 success, EINVAL failed
+ */
+static int mxc_get_v42lout_control(vout_data *vout, struct v4l2_control *c)
+{
+       switch (c->id) {
+       case V4L2_CID_HFLIP:
+               return (vout->rotate & IPU_ROTATE_HORIZ_FLIP) ? 1 : 0;
+       case V4L2_CID_VFLIP:
+               return (vout->rotate & IPU_ROTATE_VERT_FLIP) ? 1 : 0;
+       case (V4L2_CID_PRIVATE_BASE + 1):
+               return vout->rotate;
+       default:
+               return -EINVAL;
+       }
+}
+
+/*
+ * V4L2 - Handles VIDIOC_S_CTRL Ioctl
+ *
+ * @param vout         structure vout_data *
+ *
+ * @param c           structure v4l2_control *
+ *
+ * @return  status    0 success, EINVAL failed
+ */
+static int mxc_set_v42lout_control(vout_data *vout, struct v4l2_control *c)
+{
+       switch (c->id) {
+       case V4L2_CID_HFLIP:
+               vout->rotate |= c->value ? IPU_ROTATE_HORIZ_FLIP :
+                   IPU_ROTATE_NONE;
+               break;
+       case V4L2_CID_VFLIP:
+               vout->rotate |= c->value ? IPU_ROTATE_VERT_FLIP :
+                   IPU_ROTATE_NONE;
+               break;
+       case V4L2_CID_MXC_ROT:
+               vout->rotate = c->value;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+/*!
+ * V4L2 interface - open function
+ *
+ * @param inode        structure inode *
+ *
+ * @param file         structure file *
+ *
+ * @return  status    0 success, ENODEV invalid device instance,
+ *                    ENODEV timeout, ERESTARTSYS interrupted by user
+ */
+static int mxc_v4l2out_open(struct inode *inode, struct file *file)
+{
+       struct video_device *dev = video_devdata(file);
+       vout_data *vout = video_get_drvdata(dev);
+       int err;
+
+       if (!vout)
+               return -ENODEV;
+
+       down(&vout->busy_lock);
+
+       err = -EINTR;
+       if (signal_pending(current))
+               goto oops;
+
+       if (vout->open_count++ == 0) {
+               ipu_request_irq(IPU_IRQ_PP_IN_EOF,
+                               mxc_v4l2out_pp_in_irq_handler,
+                               0, dev->name, vout);
+               ipu_request_irq(IPU_IRQ_PP_OUT_EOF,
+                               mxc_v4l2out_pp_out_irq_handler,
+                               0, dev->name, vout);
+
+               init_waitqueue_head(&vout->v4l_bufq);
+
+               init_timer(&vout->output_timer);
+               vout->output_timer.function = mxc_v4l2out_timer_handler;
+               vout->output_timer.data = (unsigned long)vout;
+
+               vout->state = STATE_STREAM_OFF;
+               vout->rotate = IPU_ROTATE_NONE;
+               g_irq_cnt = g_buf_output_cnt = g_buf_q_cnt = g_buf_dq_cnt = 0;
+
+       }
+
+       file->private_data = dev;
+
+       up(&vout->busy_lock);
+
+       return 0;
+
+oops:
+       up(&vout->busy_lock);
+       return err;
+}
+
+/*!
+ * V4L2 interface - close function
+ *
+ * @param inode    struct inode *
+ *
+ * @param file     struct file *
+ *
+ * @return         0 success
+ */
+static int mxc_v4l2out_close(struct inode *inode, struct file *file)
+{
+       struct video_device *dev = video_devdata(file);
+       vout_data *vout = video_get_drvdata(dev);
+
+       if (--vout->open_count == 0) {
+               if (vout->state != STATE_STREAM_OFF)
+                       mxc_v4l2out_streamoff(vout);
+
+               ipu_free_irq(IPU_IRQ_PP_IN_EOF, vout);
+               ipu_free_irq(IPU_IRQ_PP_OUT_EOF, vout);
+
+               file->private_data = NULL;
+
+               mxc_free_buffers(vout->queue_buf_paddr, vout->queue_buf_vaddr,
+                                vout->buffer_cnt, vout->queue_buf_size);
+               vout->buffer_cnt = 0;
+               mxc_free_buffers(vout->rot_pp_bufs, vout->rot_pp_bufs_vaddr, 2,
+                                vout->display_buf_size);
+
+               /* capture off */
+               wake_up_interruptible(&vout->v4l_bufq);
+       }
+
+       return 0;
+}
+
+/*!
+ * V4L2 interface - ioctl function
+ *
+ * @param inode      struct inode *
+ *
+ * @param file       struct file *
+ *
+ * @param ioctlnr    unsigned int
+ *
+ * @param arg        void *
+ *
+ * @return           0 success, ENODEV for invalid device instance,
+ *                   -1 for other errors.
+ */
+static int
+mxc_v4l2out_do_ioctl(struct inode *inode, struct file *file,
+                    unsigned int ioctlnr, void *arg)
+{
+       struct video_device *vdev = file->private_data;
+       vout_data *vout = video_get_drvdata(vdev);
+       int retval = 0;
+       int i = 0;
+
+       if (!vout)
+               return -EBADF;
+
+       /* make this _really_ smp-safe */
+       if (down_interruptible(&vout->busy_lock))
+               return -EBUSY;
+
+       switch (ioctlnr) {
+       case VIDIOC_QUERYCAP:
+               {
+                       struct v4l2_capability *cap = arg;
+                       strcpy(cap->driver, "mxc_v4l2_output");
+                       cap->version = 0;
+                       cap->capabilities =
+                           V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+                       cap->card[0] = '\0';
+                       cap->bus_info[0] = '\0';
+                       retval = 0;
+                       break;
+               }
+       case VIDIOC_G_FMT:
+               {
+                       struct v4l2_format *gf = arg;
+                       retval = mxc_v4l2out_g_fmt(vout, gf);
+                       break;
+               }
+       case VIDIOC_S_FMT:
+               {
+                       struct v4l2_format *sf = arg;
+                       if (vout->state != STATE_STREAM_OFF) {
+                               retval = -EBUSY;
+                               break;
+                       }
+                       retval = mxc_v4l2out_s_fmt(vout, sf);
+                       break;
+               }
+       case VIDIOC_REQBUFS:
+               {
+                       struct v4l2_requestbuffers *req = arg;
+                       if ((req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) ||
+                           (req->memory != V4L2_MEMORY_MMAP)) {
+                               dev_dbg(vdev->dev,
+                                       "VIDIOC_REQBUFS: incorrect"
+                                       "buffer type\n");
+                               retval = -EINVAL;
+                               break;
+                       }
+
+                       if (req->count == 0)
+                               mxc_v4l2out_streamoff(vout);
+
+                       if (vout->state == STATE_STREAM_OFF) {
+                               if (vout->queue_buf_paddr[0] != 0) {
+                                       mxc_free_buffers(vout->queue_buf_paddr,
+                                                        vout->queue_buf_vaddr,
+                                                        vout->buffer_cnt,
+                                                        vout->queue_buf_size);
+                                       dev_dbg(vdev->dev,
+                                               "VIDIOC_REQBUFS:"
+                                               "freed buffers\n");
+                               }
+                               vout->buffer_cnt = 0;
+                       } else {
+                               dev_dbg(vdev->dev,
+                                       "VIDIOC_REQBUFS: Buffer is in use\n");
+                               retval = -EBUSY;
+                               break;
+                       }
+
+                       if (req->count == 0)
+                               break;
+
+                       if (req->count < MIN_FRAME_NUM)
+                               req->count = MIN_FRAME_NUM;
+                       else if (req->count > MAX_FRAME_NUM)
+                               req->count = MAX_FRAME_NUM;
+                       vout->buffer_cnt = req->count;
+                       vout->queue_buf_size =
+                           PAGE_ALIGN(vout->v2f.fmt.pix.sizeimage);
+
+                       retval = mxc_allocate_buffers(vout->queue_buf_paddr,
+                                                     vout->queue_buf_vaddr,
+                                                     vout->buffer_cnt,
+                                                     vout->queue_buf_size);
+                       if (retval < 0)
+                               break;
+
+                       /* Init buffer queues */
+                       vout->done_q.head = 0;
+                       vout->done_q.tail = 0;
+                       vout->ready_q.head = 0;
+                       vout->ready_q.tail = 0;
+
+                       for (i = 0; i < vout->buffer_cnt; i++) {
+                               memset(&(vout->v4l2_bufs[i]), 0,
+                                      sizeof(vout->v4l2_bufs[i]));
+                               vout->v4l2_bufs[i].flags = 0;
+                               vout->v4l2_bufs[i].memory = V4L2_MEMORY_MMAP;
+                               vout->v4l2_bufs[i].index = i;
+                               vout->v4l2_bufs[i].type =
+                                   V4L2_BUF_TYPE_VIDEO_OUTPUT;
+                               vout->v4l2_bufs[i].length =
+                                   PAGE_ALIGN(vout->v2f.fmt.pix.sizeimage);
+                               vout->v4l2_bufs[i].m.offset =
+                                   (unsigned long)vout->queue_buf_paddr[i];
+                               vout->v4l2_bufs[i].timestamp.tv_sec = 0;
+                               vout->v4l2_bufs[i].timestamp.tv_usec = 0;
+                       }
+                       break;
+               }
+       case VIDIOC_QUERYBUF:
+               {
+                       struct v4l2_buffer *buf = arg;
+                       u32 type = buf->type;
+                       int index = buf->index;
+
+                       if ((type != V4L2_BUF_TYPE_VIDEO_OUTPUT) ||
+                           (index >= vout->buffer_cnt)) {
+                               dev_dbg(vdev->dev,
+                                       "VIDIOC_QUERYBUFS: incorrect"
+                                       "buffer type\n");
+                               retval = -EINVAL;
+                               break;
+                       }
+                       down(&vout->param_lock);
+                       memcpy(buf, &(vout->v4l2_bufs[index]), sizeof(*buf));
+                       up(&vout->param_lock);
+                       break;
+               }
+       case VIDIOC_QBUF:
+               {
+                       struct v4l2_buffer *buf = arg;
+                       int index = buf->index;
+                       unsigned long lock_flags;
+
+                       if ((buf->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) ||
+                           (index >= vout->buffer_cnt)) {
+                               retval = -EINVAL;
+                               break;
+                       }
+
+                       dev_dbg(vdev->dev, "VIDIOC_QBUF: %d\n", buf->index);
+
+                       /* mmapped buffers are L1 WB cached,
+                        * so we need to clean them */
+                       if (buf->flags & V4L2_BUF_FLAG_MAPPED)
+                               flush_cache_all();
+
+                       spin_lock_irqsave(&g_lock, lock_flags);
+
+                       memcpy(&(vout->v4l2_bufs[index]), buf, sizeof(*buf));
+                       vout->v4l2_bufs[index].flags |= V4L2_BUF_FLAG_QUEUED;
+
+                       g_buf_q_cnt++;
+                       queue_buf(&vout->ready_q, index);
+                       if (vout->state == STATE_STREAM_PAUSED) {
+                               unsigned long timeout;
+
+                               index = peek_next_buf(&vout->ready_q);
+
+                               /* if timestamp is 0, then default to 30fps */
+                               if ((vout->v4l2_bufs[index].timestamp.tv_sec ==
+                                    0)
+                                   && (vout->v4l2_bufs[index].timestamp.
+                                       tv_usec == 0))
+                                       timeout =
+                                           vout->start_jiffies +
+                                           vout->frame_count * HZ / 30;
+                               else
+                                       timeout =
+                                           get_jiffies(&vout->v4l2_bufs[index].
+                                                       timestamp);
+
+                               if (jiffies >= timeout) {
+                                       dev_dbg(vout->video_dev->dev,
+                                               "warning: timer timeout"
+                                               "already expired.\n");
+                               }
+                               vout->output_timer.expires = timeout;
+                               dev_dbg(vdev->dev,
+                                       "QBUF: frame #%u timeout @"
+                                       " %lu jiffies, current = %lu\n",
+                                       vout->frame_count, timeout, jiffies);
+                               add_timer(&vout->output_timer);
+                               vout->state = STATE_STREAM_ON;
+                       }
+
+                       spin_unlock_irqrestore(&g_lock, lock_flags);
+                       break;
+               }
+       case VIDIOC_DQBUF:
+               {
+                       struct v4l2_buffer *buf = arg;
+                       int idx;
+
+                       if ((queue_size(&vout->done_q) == 0) &&
+                           (file->f_flags & O_NONBLOCK)) {
+                               retval = -EAGAIN;
+                               break;
+                       }
+
+                       if (!wait_event_interruptible_timeout(vout->v4l_bufq,
+                                                             queue_size(&vout->
+                                                                        done_q)
+                                                             != 0, 10 * HZ)) {
+                               dev_dbg(vdev->dev, "VIDIOC_DQBUF: timeout\n");
+                               retval = -ETIME;
+                               break;
+                       } else if (signal_pending(current)) {
+                               dev_dbg(vdev->dev,
+                                       "VIDIOC_DQBUF: interrupt received\n");
+                               retval = -ERESTARTSYS;
+                               break;
+                       }
+                       idx = dequeue_buf(&vout->done_q);
+                       if (idx == -1) {        /* No frame free */
+                               dev_dbg(vdev->dev,
+                                       "VIDIOC_DQBUF: no free buffers\n");
+                               retval = -EAGAIN;
+                               break;
+                       }
+                       if ((vout->v4l2_bufs[idx].flags & V4L2_BUF_FLAG_DONE) ==
+                           0)
+                               dev_dbg(vdev->dev,
+                                       "VIDIOC_DQBUF: buffer in done q, "
+                                       "but not flagged as done\n");
+
+                       vout->v4l2_bufs[idx].flags = 0;
+                       memcpy(buf, &(vout->v4l2_bufs[idx]), sizeof(*buf));
+                       dev_dbg(vdev->dev, "VIDIOC_DQBUF: %d\n", buf->index);
+                       break;
+               }
+       case VIDIOC_STREAMON:
+               {
+                       retval = mxc_v4l2out_streamon(vout);
+                       break;
+               }
+       case VIDIOC_STREAMOFF:
+               {
+                       retval = mxc_v4l2out_streamoff(vout);
+                       break;
+               }
+       case VIDIOC_G_CTRL:
+               {
+                       retval = mxc_get_v42lout_control(vout, arg);
+                       break;
+               }
+       case VIDIOC_S_CTRL:
+               {
+                       retval = mxc_set_v42lout_control(vout, arg);
+                       break;
+               }
+       case VIDIOC_CROPCAP:
+               {
+                       struct v4l2_cropcap *cap = arg;
+
+                       if (cap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+                               retval = -EINVAL;
+                               break;
+                       }
+
+                       cap->bounds = vout->crop_bounds[vout->cur_disp_output];
+                       cap->defrect = vout->crop_bounds[vout->cur_disp_output];
+                       retval = 0;
+                       break;
+               }
+       case VIDIOC_G_CROP:
+               {
+                       struct v4l2_crop *crop = arg;
+
+                       if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+                               retval = -EINVAL;
+                               break;
+                       }
+                       crop->c = vout->crop_current;
+                       break;
+               }
+       case VIDIOC_S_CROP:
+               {
+                       struct v4l2_crop *crop = arg;
+                       struct v4l2_rect *b =
+                           &(vout->crop_bounds[vout->cur_disp_output]);
+
+                       if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+                               retval = -EINVAL;
+                               break;
+                       }
+                       if (crop->c.height < 0) {
+                               retval = -EINVAL;
+                               break;
+                       }
+                       if (crop->c.width < 0) {
+                               retval = -EINVAL;
+                               break;
+                       }
+
+                       /* only full screen supported for SDC BG */
+                       if (vout->cur_disp_output == 4) {
+                               crop->c = vout->crop_current;
+                               break;
+                       }
+
+                       if (crop->c.top < b->top)
+                               crop->c.top = b->top;
+                       if (crop->c.top >= b->top + b->height)
+                               crop->c.top = b->top + b->height - 1;
+                       if (crop->c.height > b->top - crop->c.top + b->height)
+                               crop->c.height =
+                                   b->top - crop->c.top + b->height;
+
+                       if (crop->c.left < b->left)
+                               crop->c.left = b->left;
+                       if (crop->c.left >= b->left + b->width)
+                               crop->c.left = b->left + b->width - 1;
+                       if (crop->c.width > b->left - crop->c.left + b->width)
+                               crop->c.width =
+                                   b->left - crop->c.left + b->width;
+
+                       /* stride line limitation */
+                       crop->c.height -= crop->c.height % 8;
+                       crop->c.width -= crop->c.width % 8;
+
+                       vout->crop_current = crop->c;
+                       break;
+               }
+       case VIDIOC_ENUMOUTPUT:
+               {
+                       struct v4l2_output *output = arg;
+
+                       if ((output->index >= 5) ||
+                           (vout->output_enabled[output->index] == false)) {
+                               retval = -EINVAL;
+                               break;
+                       }
+
+                       if (output->index < 3) {
+                               *output = mxc_outputs[MXC_V4L2_OUT_2_ADC];
+                               output->name[4] = '0' + output->index;
+                       } else {
+                               *output = mxc_outputs[MXC_V4L2_OUT_2_SDC];
+                       }
+                       break;
+               }
+       case VIDIOC_G_OUTPUT:
+               {
+                       int *p_output_num = arg;
+
+                       *p_output_num = vout->cur_disp_output;
+                       break;
+               }
+       case VIDIOC_S_OUTPUT:
+               {
+                       int *p_output_num = arg;
+                       int fbnum;
+                       struct v4l2_rect *b;
+
+                       if ((*p_output_num >= MXC_V4L2_OUT_NUM_OUTPUTS) ||
+                           (vout->output_enabled[*p_output_num] == false)) {
+                               retval = -EINVAL;
+                               break;
+                       }
+
+                       if (vout->state != STATE_STREAM_OFF) {
+                               retval = -EBUSY;
+                               break;
+                       }
+
+                       vout->cur_disp_output = *p_output_num;
+
+                       /* Update bounds in case they have changed */
+                       b = &vout->crop_bounds[vout->cur_disp_output];
+
+                       fbnum = vout->output_fb_num[vout->cur_disp_output];
+                       if (vout->cur_disp_output == 3)
+                               fbnum = vout->output_fb_num[4];
+
+                       b->width = registered_fb[fbnum]->var.xres;
+                       b->height = registered_fb[fbnum]->var.yres;
+
+                       vout->crop_current = *b;
+                       break;
+               }
+       case VIDIOC_ENUM_FMT:
+       case VIDIOC_TRY_FMT:
+       case VIDIOC_QUERYCTRL:
+       case VIDIOC_G_PARM:
+       case VIDIOC_ENUMSTD:
+       case VIDIOC_G_STD:
+       case VIDIOC_S_STD:
+       case VIDIOC_G_TUNER:
+       case VIDIOC_S_TUNER:
+       case VIDIOC_G_FREQUENCY:
+       case VIDIOC_S_FREQUENCY:
+       default:
+               retval = -EINVAL;
+               break;
+       }
+
+       up(&vout->busy_lock);
+       return retval;
+}
+
+/*
+ * V4L2 interface - ioctl function
+ *
+ * @return  None
+ */
+static int
+mxc_v4l2out_ioctl(struct inode *inode, struct file *file,
+                 unsigned int cmd, unsigned long arg)
+{
+       return video_usercopy(inode, file, cmd, arg, mxc_v4l2out_do_ioctl);
+}
+
+/*!
+ * V4L2 interface - mmap function
+ *
+ * @param file          structure file *
+ *
+ * @param vma           structure vm_area_struct *
+ *
+ * @return status       0 Success, EINTR busy lock error,
+ *                      ENOBUFS remap_page error
+ */
+static int mxc_v4l2out_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       struct video_device *vdev = video_devdata(file);
+       unsigned long size = vma->vm_end - vma->vm_start;
+       int res = 0;
+       int i;
+       vout_data *vout = video_get_drvdata(vdev);
+
+       dev_dbg(vdev->dev, "pgoff=0x%lx, start=0x%lx, end=0x%lx\n",
+               vma->vm_pgoff, vma->vm_start, vma->vm_end);
+
+       /* make this _really_ smp-safe */
+       if (down_interruptible(&vout->busy_lock))
+               return -EINTR;
+
+       for (i = 0; i < vout->buffer_cnt; i++) {
+               if ((vout->v4l2_bufs[i].m.offset ==
+                    (vma->vm_pgoff << PAGE_SHIFT)) &&
+                   (vout->v4l2_bufs[i].length >= size)) {
+                       vout->v4l2_bufs[i].flags |= V4L2_BUF_FLAG_MAPPED;
+                       break;
+               }
+       }
+       if (i == vout->buffer_cnt) {
+               res = -ENOBUFS;
+               goto mxc_mmap_exit;
+       }
+
+       /* make buffers inner write-back, outer write-thru cacheable */
+       vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+       if (remap_pfn_range(vma, vma->vm_start,
+                           vma->vm_pgoff, size, vma->vm_page_prot)) {
+               dev_dbg(vdev->dev, "mmap remap_pfn_range failed\n");
+               res = -ENOBUFS;
+               goto mxc_mmap_exit;
+       }
+
+       vma->vm_flags &= ~VM_IO;        /* using shared anonymous pages */
+
+mxc_mmap_exit:
+       up(&vout->busy_lock);
+       return res;
+}
+
+/*!
+ * V4L2 interface - poll function
+ *
+ * @param file       structure file *
+ *
+ * @param wait       structure poll_table *
+ *
+ * @return  status   POLLIN | POLLRDNORM
+ */
+static unsigned int mxc_v4l2out_poll(struct file *file, poll_table * wait)
+{
+       struct video_device *dev = video_devdata(file);
+       vout_data *vout = video_get_drvdata(dev);
+
+       wait_queue_head_t *queue = NULL;
+       int res = POLLIN | POLLRDNORM;
+
+       if (down_interruptible(&vout->busy_lock))
+               return -EINTR;
+
+       queue = &vout->v4l_bufq;
+       poll_wait(file, queue, wait);
+
+       up(&vout->busy_lock);
+       return res;
+}
+
+static struct
+file_operations mxc_v4l2out_fops = {
+       .owner = THIS_MODULE,
+       .open = mxc_v4l2out_open,
+       .release = mxc_v4l2out_close,
+       .ioctl = mxc_v4l2out_ioctl,
+       .mmap = mxc_v4l2out_mmap,
+       .poll = mxc_v4l2out_poll,
+};
+
+static struct video_device mxc_v4l2out_template = {
+       .owner = THIS_MODULE,
+       .name = "MXC Video Output",
+       .type = 0,
+       .type2 = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING,
+       .fops = &mxc_v4l2out_fops,
+       .release = video_device_release,
+};
+
+/*!
+ * 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 mxc_v4l2out_probe(struct platform_device *pdev)
+{
+       int i;
+       vout_data *vout;
+
+       /*
+        * Allocate sufficient memory for the fb structure
+        */
+       g_vout = vout = kmalloc(sizeof(vout_data), GFP_KERNEL);
+
+       if (!vout)
+               return 0;
+
+       memset(vout, 0, sizeof(vout_data));
+
+       vout->video_dev = video_device_alloc();
+       if (vout->video_dev == NULL)
+               return -1;
+       vout->video_dev->dev = &pdev->dev;
+       vout->video_dev->minor = -1;
+
+       *(vout->video_dev) = mxc_v4l2out_template;
+
+       /* register v4l device */
+       if (video_register_device(vout->video_dev,
+                                 VFL_TYPE_GRABBER, video_nr) == -1) {
+               dev_dbg(&pdev->dev, "video_register_device failed\n");
+               return 0;
+       }
+       dev_info(&pdev->dev, "Registered device video%d\n",
+                vout->video_dev->minor & 0x1f);
+       vout->video_dev->dev = &pdev->dev;
+
+       video_set_drvdata(vout->video_dev, vout);
+
+       init_MUTEX(&vout->param_lock);
+       init_MUTEX(&vout->busy_lock);
+
+       /* setup outputs and cropping */
+       vout->cur_disp_output = -1;
+       for (i = 0; i < num_registered_fb; i++) {
+               char *idstr = registered_fb[i]->fix.id;
+               if (strncmp(idstr, "DISP", 4) == 0) {
+                       int disp_num = idstr[4] - '0';
+                       if (disp_num == 3) {
+                               if (strcmp(idstr, "DISP3 BG - DI1") == 0)
+                                       disp_num = 5;
+                               else if (strncmp(idstr, "DISP3 BG", 8) == 0)
+                                       disp_num = 4;
+                       }
+                       vout->crop_bounds[disp_num].left = 0;
+                       vout->crop_bounds[disp_num].top = 0;
+                       vout->crop_bounds[disp_num].width =
+                           registered_fb[i]->var.xres;
+                       vout->crop_bounds[disp_num].height =
+                           registered_fb[i]->var.yres;
+                       vout->output_enabled[disp_num] = true;
+                       vout->output_fb_num[disp_num] = i;
+                       if (vout->cur_disp_output == -1)
+                               vout->cur_disp_output = disp_num;
+               }
+
+       }
+       vout->crop_current = vout->crop_bounds[vout->cur_disp_output];
+
+       platform_set_drvdata(pdev, vout);
+
+       return 0;
+}
+
+static int mxc_v4l2out_remove(struct platform_device *pdev)
+{
+       vout_data *vout = platform_get_drvdata(pdev);
+
+       if (vout->video_dev) {
+               if (-1 != vout->video_dev->minor)
+                       video_unregister_device(vout->video_dev);
+               else
+                       video_device_release(vout->video_dev);
+               vout->video_dev = NULL;
+       }
+
+       platform_set_drvdata(pdev, NULL);
+
+       kfree(vout);
+
+       return 0;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxc_v4l2out_driver = {
+       .driver = {
+                  .name = "MXC Video Output",
+                  },
+       .probe = mxc_v4l2out_probe,
+       .remove = mxc_v4l2out_remove,
+};
+
+static struct platform_device mxc_v4l2out_device = {
+       .name = "MXC Video Output",
+       .id = 0,
+};
+
+/*!
+ * mxc v4l2 init function
+ *
+ */
+static int mxc_v4l2out_init(void)
+{
+       u8 err = 0;
+
+       err = platform_driver_register(&mxc_v4l2out_driver);
+       if (err == 0)
+               platform_device_register(&mxc_v4l2out_device);
+       return err;
+}
+
+/*!
+ * mxc v4l2 cleanup function
+ *
+ */
+static void mxc_v4l2out_clean(void)
+{
+       video_unregister_device(g_vout->video_dev);
+
+       platform_driver_unregister(&mxc_v4l2out_driver);
+       platform_device_unregister(&mxc_v4l2out_device);
+       kfree(g_vout);
+       g_vout = NULL;
+}
+
+module_init(mxc_v4l2out_init);
+module_exit(mxc_v4l2out_clean);
+
+module_param(video_nr, int, 0444);
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("V4L2-driver for MXC video output");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("video");
diff --git a/drivers/media/video/mxc/output/mxc_pxp_v4l2.c b/drivers/media/video/mxc/output/mxc_pxp_v4l2.c
new file mode 100644 (file)
index 0000000..a26b5d9
--- /dev/null
@@ -0,0 +1,1237 @@
+/*
+ * Copyright (C) 2010-2011 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 PxP driver
+ * Copyright 2008-2009 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/vmalloc.h>
+#include <linux/videodev2.h>
+#include <linux/dmaengine.h>
+#include <linux/pxp_dma.h>
+#include <linux/delay.h>
+#include <linux/console.h>
+#include <linux/mxcfb.h>
+
+#include <media/videobuf-dma-contig.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+
+#include "mxc_pxp_v4l2.h"
+
+#define PXP_DRIVER_NAME                        "pxp-v4l2"
+#define PXP_DRIVER_MAJOR               2
+#define PXP_DRIVER_MINOR               0
+
+#define PXP_DEF_BUFS                   2
+#define PXP_MIN_PIX                    8
+
+#define V4L2_OUTPUT_TYPE_INTERNAL      4
+
+static struct pxp_data_format pxp_s0_formats[] = {
+       {
+               .name = "24-bit RGB",
+               .bpp = 4,
+               .fourcc = V4L2_PIX_FMT_RGB24,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+       }, {
+               .name = "16-bit RGB 5:6:5",
+               .bpp = 2,
+               .fourcc = V4L2_PIX_FMT_RGB565,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+       }, {
+               .name = "16-bit RGB 5:5:5",
+               .bpp = 2,
+               .fourcc = V4L2_PIX_FMT_RGB555,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+       }, {
+               .name = "YUV 4:2:0 Planar",
+               .bpp = 2,
+               .fourcc = V4L2_PIX_FMT_YUV420,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+       }, {
+               .name = "YUV 4:2:2 Planar",
+               .bpp = 2,
+               .fourcc = V4L2_PIX_FMT_YUV422P,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+       },
+};
+
+static unsigned int v4l2_fmt_to_pxp_fmt(u32 v4l2_pix_fmt)
+{
+       u32 pxp_fmt = 0;
+
+       if (v4l2_pix_fmt == V4L2_PIX_FMT_RGB24)
+               pxp_fmt = PXP_PIX_FMT_RGB24;
+       else if (v4l2_pix_fmt == V4L2_PIX_FMT_RGB565)
+               pxp_fmt = PXP_PIX_FMT_RGB565;
+       else if (v4l2_pix_fmt == V4L2_PIX_FMT_RGB555)
+               pxp_fmt = PXP_PIX_FMT_RGB555;
+       else if (v4l2_pix_fmt == V4L2_PIX_FMT_RGB555)
+               pxp_fmt = PXP_PIX_FMT_RGB555;
+       else if (v4l2_pix_fmt == V4L2_PIX_FMT_YUV420)
+               pxp_fmt = PXP_PIX_FMT_YUV420P;
+       else if (v4l2_pix_fmt == V4L2_PIX_FMT_YUV422P)
+               pxp_fmt = PXP_PIX_FMT_YUV422P;
+
+       return pxp_fmt;
+}
+struct v4l2_queryctrl pxp_controls[] = {
+       {
+               .id             = V4L2_CID_HFLIP,
+               .type           = V4L2_CTRL_TYPE_BOOLEAN,
+               .name           = "Horizontal Flip",
+               .minimum        = 0,
+               .maximum        = 1,
+               .step           = 1,
+               .default_value  = 0,
+               .flags          = 0,
+       }, {
+               .id             = V4L2_CID_VFLIP,
+               .type           = V4L2_CTRL_TYPE_BOOLEAN,
+               .name           = "Vertical Flip",
+               .minimum        = 0,
+               .maximum        = 1,
+               .step           = 1,
+               .default_value  = 0,
+               .flags          = 0,
+       }, {
+               .id             = V4L2_CID_PRIVATE_BASE,
+               .type           = V4L2_CTRL_TYPE_INTEGER,
+               .name           = "Rotation",
+               .minimum        = 0,
+               .maximum        = 270,
+               .step           = 90,
+               .default_value  = 0,
+               .flags          = 0,
+       }, {
+               .id             = V4L2_CID_PRIVATE_BASE + 1,
+               .name           = "Background Color",
+               .minimum        = 0,
+               .maximum        = 0xFFFFFF,
+               .step           = 1,
+               .default_value  = 0,
+               .flags          = 0,
+               .type           = V4L2_CTRL_TYPE_INTEGER,
+       }, {
+               .id             = V4L2_CID_PRIVATE_BASE + 2,
+               .name           = "Set S0 Chromakey",
+               .minimum        = -1,
+               .maximum        = 0xFFFFFF,
+               .step           = 1,
+               .default_value  = -1,
+               .flags          = 0,
+               .type           = V4L2_CTRL_TYPE_INTEGER,
+       }, {
+               .id             = V4L2_CID_PRIVATE_BASE + 3,
+               .name           = "YUV Colorspace",
+               .minimum        = 0,
+               .maximum        = 1,
+               .step           = 1,
+               .default_value  = 0,
+               .flags          = 0,
+               .type           = V4L2_CTRL_TYPE_BOOLEAN,
+       },
+};
+
+/* callback function */
+static void video_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 pxps *pxp = pxp_chan->client;
+       struct videobuf_buffer *vb;
+
+       dev_dbg(chan->device->dev, "callback cookie %d, active DMA 0x%08x\n",
+                       tx_desc->txd.cookie,
+                       pxp->active ? sg_dma_address(&pxp->active->sg[0]) : 0);
+
+       spin_lock(&pxp->lock);
+       if (pxp->active) {
+               vb = &pxp->active->vb;
+
+               list_del_init(&vb->queue);
+               vb->state = VIDEOBUF_DONE;
+               do_gettimeofday(&vb->ts);
+               vb->field_count++;
+               wake_up(&vb->done);
+       }
+
+       if (list_empty(&pxp->outq)) {
+               pxp->active = NULL;
+               spin_unlock(&pxp->lock);
+
+               return;
+       }
+
+       pxp->active = list_entry(pxp->outq.next,
+                                    struct pxp_buffer, vb.queue);
+       pxp->active->vb.state = VIDEOBUF_ACTIVE;
+       spin_unlock(&pxp->lock);
+}
+
+static int acquire_dma_channel(struct pxps *pxp)
+{
+       dma_cap_mask_t mask;
+       struct dma_chan *chan;
+       struct pxp_channel **pchan = &pxp->pxp_channel[0];
+
+       if (*pchan) {
+               struct videobuf_buffer *vb, *_vb;
+               dma_release_channel(&(*pchan)->dma_chan);
+               *pchan = NULL;
+               pxp->active = NULL;
+               list_for_each_entry_safe(vb, _vb, &pxp->outq, queue) {
+                       list_del_init(&vb->queue);
+                       vb->state = VIDEOBUF_ERROR;
+                       wake_up(&vb->done);
+               }
+       }
+
+       dma_cap_zero(mask);
+       dma_cap_set(DMA_SLAVE, mask);
+       dma_cap_set(DMA_PRIVATE, mask);
+       chan = dma_request_channel(mask, NULL, NULL);
+       if (!chan)
+               return -EBUSY;
+
+       *pchan = to_pxp_channel(chan);
+       (*pchan)->client = pxp;
+
+       return 0;
+}
+
+static int _get_fbinfo(struct fb_info **fbi)
+{
+       int i;
+       for (i = 0; i < num_registered_fb; i++) {
+               char *idstr = registered_fb[i]->fix.id;
+               if (strcmp(idstr, "mxc_elcdif_fb") == 0) {
+                       *fbi = registered_fb[i];
+                       return 0;
+               }
+       }
+
+       return -ENODEV;
+}
+
+static int pxp_set_fbinfo(struct pxps *pxp)
+{
+       struct fb_info *fbi;
+       struct v4l2_framebuffer *fb = &pxp->fb;
+       int err;
+
+       err = _get_fbinfo(&fbi);
+       if (err)
+               return err;
+
+       fb->fmt.width = fbi->var.xres;
+       fb->fmt.height = fbi->var.yres;
+       if (fbi->var.bits_per_pixel == 16)
+               fb->fmt.pixelformat = V4L2_PIX_FMT_RGB565;
+       else
+               fb->fmt.pixelformat = V4L2_PIX_FMT_RGB24;
+       fb->base = (void *)fbi->fix.smem_start;
+
+       return 0;
+}
+
+static int _get_cur_fb_blank(struct pxps *pxp)
+{
+       struct fb_info *fbi;
+       mm_segment_t old_fs;
+       int err = 0;
+
+       err = _get_fbinfo(&fbi);
+       if (err)
+               return err;
+
+       if (fbi->fbops->fb_ioctl) {
+               old_fs = get_fs();
+               set_fs(KERNEL_DS);
+               err = fbi->fbops->fb_ioctl(fbi, MXCFB_GET_FB_BLANK,
+                               (unsigned int)(&pxp->fb_blank));
+               set_fs(old_fs);
+       }
+
+       return err;
+}
+
+static int set_fb_blank(int blank)
+{
+       struct fb_info *fbi;
+       int err = 0;
+
+       err = _get_fbinfo(&fbi);
+       if (err)
+               return err;
+
+       acquire_console_sem();
+       fb_blank(fbi, blank);
+       release_console_sem();
+
+       return err;
+}
+
+static int pxp_set_cstate(struct pxps *pxp, struct v4l2_control *vc)
+{
+
+       if (vc->id == V4L2_CID_HFLIP) {
+               pxp->pxp_conf.proc_data.hflip = vc->value;
+       } else if (vc->id == V4L2_CID_VFLIP) {
+               pxp->pxp_conf.proc_data.vflip = vc->value;
+       } else if (vc->id == V4L2_CID_PRIVATE_BASE) {
+               if (vc->value % 90)
+                       return -ERANGE;
+               pxp->pxp_conf.proc_data.rotate = vc->value;
+       } else if (vc->id == V4L2_CID_PRIVATE_BASE + 1) {
+               pxp->pxp_conf.proc_data.bgcolor = vc->value;
+       } else if (vc->id == V4L2_CID_PRIVATE_BASE + 2) {
+               pxp->pxp_conf.s0_param.color_key = vc->value;
+       } else if (vc->id == V4L2_CID_PRIVATE_BASE + 3) {
+               pxp->pxp_conf.proc_data.yuv = vc->value;
+       }
+
+       return 0;
+}
+
+static int pxp_get_cstate(struct pxps *pxp, struct v4l2_control *vc)
+{
+       if (vc->id == V4L2_CID_HFLIP)
+               vc->value = pxp->pxp_conf.proc_data.hflip;
+       else if (vc->id == V4L2_CID_VFLIP)
+               vc->value = pxp->pxp_conf.proc_data.vflip;
+       else if (vc->id == V4L2_CID_PRIVATE_BASE)
+               vc->value = pxp->pxp_conf.proc_data.rotate;
+       else if (vc->id == V4L2_CID_PRIVATE_BASE + 1)
+               vc->value = pxp->pxp_conf.proc_data.bgcolor;
+       else if (vc->id == V4L2_CID_PRIVATE_BASE + 2)
+               vc->value = pxp->pxp_conf.s0_param.color_key;
+       else if (vc->id == V4L2_CID_PRIVATE_BASE + 3)
+               vc->value = pxp->pxp_conf.proc_data.yuv;
+
+       return 0;
+}
+
+static int pxp_enumoutput(struct file *file, void *fh,
+                       struct v4l2_output *o)
+{
+       struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+       if ((o->index < 0) || (o->index > 1))
+               return -EINVAL;
+
+       memset(o, 0, sizeof(struct v4l2_output));
+       if (o->index == 0) {
+               strcpy(o->name, "PxP Display Output");
+               pxp->output = 0;
+       } else {
+               strcpy(o->name, "PxP Virtual Output");
+               pxp->output = 1;
+       }
+       o->type = V4L2_OUTPUT_TYPE_INTERNAL;
+       o->std = 0;
+       o->reserved[0] = pxp->outb_phys;
+
+       return 0;
+}
+
+static int pxp_g_output(struct file *file, void *fh,
+                       unsigned int *i)
+{
+       struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+       *i = pxp->output;
+
+       return 0;
+}
+
+static int pxp_s_output(struct file *file, void *fh,
+                       unsigned int i)
+{
+       struct pxps *pxp = video_get_drvdata(video_devdata(file));
+       struct v4l2_pix_format *fmt = &pxp->fb.fmt;
+       int bpp;
+
+       if ((i < 0) || (i > 1))
+               return -EINVAL;
+
+       if (pxp->outb)
+               return 0;
+
+       /* Output buffer is same format as fbdev */
+       if (fmt->pixelformat == V4L2_PIX_FMT_RGB24)
+               bpp = 4;
+       else
+               bpp = 2;
+
+       pxp->outb_size = fmt->width * fmt->height * bpp;
+       pxp->outb = kmalloc(fmt->width * fmt->height * bpp, GFP_KERNEL);
+       pxp->outb_phys = virt_to_phys(pxp->outb);
+       dma_map_single(NULL, pxp->outb,
+                       fmt->width * fmt->height * bpp, DMA_TO_DEVICE);
+
+       pxp->pxp_conf.out_param.width = fmt->width;
+       pxp->pxp_conf.out_param.height = fmt->height;
+       if (fmt->pixelformat == V4L2_PIX_FMT_RGB24)
+               pxp->pxp_conf.out_param.pixel_fmt = PXP_PIX_FMT_RGB24;
+       else
+               pxp->pxp_conf.out_param.pixel_fmt = PXP_PIX_FMT_RGB565;
+
+       return 0;
+}
+
+static int pxp_enum_fmt_video_output(struct file *file, void *fh,
+                               struct v4l2_fmtdesc *fmt)
+{
+       enum v4l2_buf_type type = fmt->type;
+       int index = fmt->index;
+
+       if ((fmt->index < 0) || (fmt->index >= ARRAY_SIZE(pxp_s0_formats)))
+               return -EINVAL;
+
+       memset(fmt, 0, sizeof(struct v4l2_fmtdesc));
+       fmt->index = index;
+       fmt->type = type;
+       fmt->pixelformat = pxp_s0_formats[index].fourcc;
+       strcpy(fmt->description, pxp_s0_formats[index].name);
+
+       return 0;
+}
+
+static int pxp_g_fmt_video_output(struct file *file, void *fh,
+                               struct v4l2_format *f)
+{
+       struct v4l2_pix_format *pf = &f->fmt.pix;
+       struct pxps *pxp = video_get_drvdata(video_devdata(file));
+       struct pxp_data_format *fmt = pxp->s0_fmt;
+
+       pf->width = pxp->pxp_conf.s0_param.width;
+       pf->height = pxp->pxp_conf.s0_param.height;
+       pf->pixelformat = fmt->fourcc;
+       pf->field = V4L2_FIELD_NONE;
+       pf->bytesperline = fmt->bpp * pf->width;
+       pf->sizeimage = pf->bytesperline * pf->height;
+       pf->colorspace = fmt->colorspace;
+       pf->priv = 0;
+
+       return 0;
+}
+
+static struct pxp_data_format *pxp_get_format(struct v4l2_format *f)
+{
+       struct pxp_data_format *fmt;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(pxp_s0_formats); i++) {
+               fmt = &pxp_s0_formats[i];
+               if (fmt->fourcc == f->fmt.pix.pixelformat)
+                       break;
+       }
+
+       if (i == ARRAY_SIZE(pxp_s0_formats))
+               return NULL;
+
+       return &pxp_s0_formats[i];
+}
+
+static int pxp_try_fmt_video_output(struct file *file, void *fh,
+                               struct v4l2_format *f)
+{
+       int w = f->fmt.pix.width;
+       int h = f->fmt.pix.height;
+       struct pxp_data_format *fmt = pxp_get_format(f);
+
+       if (!fmt)
+               return -EINVAL;
+
+       w = min(w, 2040);
+       w = max(w, 8);
+       h = min(h, 2040);
+       h = max(h, 8);
+       f->fmt.pix.field = V4L2_FIELD_NONE;
+       f->fmt.pix.width = w;
+       f->fmt.pix.height = h;
+       f->fmt.pix.pixelformat = fmt->fourcc;
+
+       return 0;
+}
+
+static int pxp_s_fmt_video_output(struct file *file, void *fh,
+                               struct v4l2_format *f)
+{
+       struct pxps *pxp = video_get_drvdata(video_devdata(file));
+       struct v4l2_pix_format *pf = &f->fmt.pix;
+       int ret;
+
+       ret = acquire_dma_channel(pxp);
+       if (ret < 0)
+               return ret;
+
+       ret = pxp_try_fmt_video_output(file, fh, f);
+       if (ret == 0) {
+               pxp->s0_fmt = pxp_get_format(f);
+               pxp->pxp_conf.s0_param.pixel_fmt =
+                       v4l2_fmt_to_pxp_fmt(pxp->s0_fmt->fourcc);
+               pxp->pxp_conf.s0_param.width = pf->width;
+               pxp->pxp_conf.s0_param.height = pf->height;
+       }
+
+
+       return ret;
+}
+
+static int pxp_g_fmt_output_overlay(struct file *file, void *fh,
+                               struct v4l2_format *f)
+{
+       struct pxps *pxp = video_get_drvdata(video_devdata(file));
+       struct v4l2_window *wf = &f->fmt.win;
+
+       memset(wf, 0, sizeof(struct v4l2_window));
+       wf->chromakey = pxp->s1_chromakey;
+       wf->global_alpha = pxp->global_alpha;
+       wf->field = V4L2_FIELD_NONE;
+       wf->clips = NULL;
+       wf->clipcount = 0;
+       wf->bitmap = NULL;
+       wf->w.left = pxp->pxp_conf.proc_data.srect.left;
+       wf->w.top = pxp->pxp_conf.proc_data.srect.top;
+       wf->w.width = pxp->pxp_conf.proc_data.srect.width;
+       wf->w.height = pxp->pxp_conf.proc_data.srect.height;
+
+       return 0;
+}
+
+static int pxp_try_fmt_output_overlay(struct file *file, void *fh,
+                               struct v4l2_format *f)
+{
+       struct pxps *pxp = video_get_drvdata(video_devdata(file));
+       struct v4l2_window *wf = &f->fmt.win;
+       struct v4l2_rect srect;
+       u32 s1_chromakey = wf->chromakey;
+       u8 global_alpha = wf->global_alpha;
+
+       memcpy(&srect, &(wf->w), sizeof(struct v4l2_rect));
+
+       pxp_g_fmt_output_overlay(file, fh, f);
+
+       wf->chromakey = s1_chromakey;
+       wf->global_alpha = global_alpha;
+
+       /* Constrain parameters to the input buffer */
+       wf->w.left = srect.left;
+       wf->w.top = srect.top;
+       wf->w.width = min(srect.width,
+                       ((__s32)pxp->pxp_conf.s0_param.width - wf->w.left));
+       wf->w.height = min(srect.height,
+                       ((__s32)pxp->pxp_conf.s0_param.height - wf->w.top));
+
+       return 0;
+}
+
+static int pxp_s_fmt_output_overlay(struct file *file, void *fh,
+                                       struct v4l2_format *f)
+{
+       struct pxps *pxp = video_get_drvdata(video_devdata(file));
+       struct v4l2_window *wf = &f->fmt.win;
+       int ret = pxp_try_fmt_output_overlay(file, fh, f);
+
+       if (ret == 0) {
+               pxp->global_alpha = wf->global_alpha;
+               pxp->s1_chromakey = wf->chromakey;
+               pxp->pxp_conf.proc_data.srect.left = wf->w.left;
+               pxp->pxp_conf.proc_data.srect.top = wf->w.top;
+               pxp->pxp_conf.proc_data.srect.width = wf->w.width;
+               pxp->pxp_conf.proc_data.srect.height = wf->w.height;
+               pxp->pxp_conf.ol_param[0].global_alpha = pxp->global_alpha;
+               pxp->pxp_conf.ol_param[0].color_key = pxp->s1_chromakey;
+               pxp->pxp_conf.ol_param[0].color_key_enable =
+                                       pxp->s1_chromakey_state;
+       }
+
+       return ret;
+}
+
+static int pxp_reqbufs(struct file *file, void *priv,
+                       struct v4l2_requestbuffers *r)
+{
+       struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+       return videobuf_reqbufs(&pxp->s0_vbq, r);
+}
+
+static int pxp_querybuf(struct file *file, void *priv,
+                       struct v4l2_buffer *b)
+{
+       struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+       return videobuf_querybuf(&pxp->s0_vbq, b);
+}
+
+static int pxp_qbuf(struct file *file, void *priv,
+                       struct v4l2_buffer *b)
+{
+       struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+       return videobuf_qbuf(&pxp->s0_vbq, b);
+}
+
+static int pxp_dqbuf(struct file *file, void *priv,
+                       struct v4l2_buffer *b)
+{
+       struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+       return videobuf_dqbuf(&pxp->s0_vbq, b, file->f_flags & O_NONBLOCK);
+}
+
+static int pxp_streamon(struct file *file, void *priv,
+                       enum v4l2_buf_type t)
+{
+       struct pxps *pxp = video_get_drvdata(video_devdata(file));
+       int ret = 0;
+
+       if (t != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+               return -EINVAL;
+
+       _get_cur_fb_blank(pxp);
+       set_fb_blank(FB_BLANK_UNBLANK);
+
+       ret = videobuf_streamon(&pxp->s0_vbq);
+
+       if (!ret && (pxp->output == 0))
+               mxc_elcdif_frame_addr_setup(pxp->outb_phys);
+
+       return ret;
+}
+
+static int pxp_streamoff(struct file *file, void *priv,
+                       enum v4l2_buf_type t)
+{
+       struct pxps *pxp = video_get_drvdata(video_devdata(file));
+       int ret = 0;
+
+       if ((t != V4L2_BUF_TYPE_VIDEO_OUTPUT))
+               return -EINVAL;
+
+       ret = videobuf_streamoff(&pxp->s0_vbq);
+
+       if (!ret)
+               mxc_elcdif_frame_addr_setup((dma_addr_t)pxp->fb.base);
+
+       if (pxp->fb_blank)
+               set_fb_blank(FB_BLANK_POWERDOWN);
+
+       return ret;
+}
+
+static int pxp_buf_setup(struct videobuf_queue *q,
+                       unsigned int *count, unsigned *size)
+{
+       struct pxps *pxp = q->priv_data;
+
+       *size = pxp->pxp_conf.s0_param.width *
+               pxp->pxp_conf.s0_param.height * pxp->s0_fmt->bpp;
+
+       if (0 == *count)
+               *count = PXP_DEF_BUFS;
+
+       return 0;
+}
+
+static void pxp_buf_free(struct videobuf_queue *q, struct pxp_buffer *buf)
+{
+       struct videobuf_buffer *vb = &buf->vb;
+       struct dma_async_tx_descriptor *txd = buf->txd;
+
+       BUG_ON(in_interrupt());
+
+       pr_debug("%s (vb=0x%p) 0x%08lx %d\n", __func__,
+               vb, vb->baddr, vb->bsize);
+
+       /*
+        * This waits until this buffer is out of danger, i.e., until it is no
+        * longer in STATE_QUEUED or STATE_ACTIVE
+        */
+       videobuf_waiton(vb, 0, 0);
+       if (txd)
+               async_tx_ack(txd);
+
+       videobuf_dma_contig_free(q, vb);
+       buf->txd = NULL;
+
+       vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int pxp_buf_prepare(struct videobuf_queue *q,
+                       struct videobuf_buffer *vb,
+                       enum v4l2_field field)
+{
+       struct pxps *pxp = q->priv_data;
+       struct pxp_config_data *pxp_conf = &pxp->pxp_conf;
+       struct pxp_proc_data *proc_data = &pxp_conf->proc_data;
+       struct pxp_buffer *buf = container_of(vb, struct pxp_buffer, vb);
+       struct pxp_tx_desc *desc;
+       int ret = 0;
+       int i, length;
+
+       vb->width = pxp->pxp_conf.s0_param.width;
+       vb->height = pxp->pxp_conf.s0_param.height;
+       vb->size = vb->width * vb->height * pxp->s0_fmt->bpp;
+       vb->field = V4L2_FIELD_NONE;
+       if (vb->state != VIDEOBUF_NEEDS_INIT)
+               pxp_buf_free(q, buf);
+
+       if (vb->state == VIDEOBUF_NEEDS_INIT) {
+               struct pxp_channel *pchan = pxp->pxp_channel[0];
+               struct scatterlist *sg = &buf->sg;
+
+               /* This actually (allocates and) maps buffers */
+               ret = videobuf_iolock(q, vb, NULL);
+               if (ret) {
+                       pr_err("fail to call videobuf_iolock, ret = %d\n", ret);
+                       goto fail;
+               }
+
+               /*
+                * sg[0] for input(S0)
+                * Sg[1] for output
+                */
+               sg_init_table(sg, 3);
+
+               buf->txd = pchan->dma_chan.device->device_prep_slave_sg(
+                       &pchan->dma_chan, sg, 3, DMA_FROM_DEVICE,
+                       DMA_PREP_INTERRUPT);
+               if (!buf->txd) {
+                       ret = -EIO;
+                       goto fail;
+               }
+
+               buf->txd->callback_param        = buf->txd;
+               buf->txd->callback              = video_dma_done;
+
+               desc = to_tx_desc(buf->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 =
+                                               videobuf_to_dma_contig(vb);
+                               memcpy(&desc->layer_param.s0_param,
+                                       &pxp_conf->s0_param,
+                                       sizeof(struct pxp_layer_param));
+                       } else if (i == 1) { /* Output */
+                               if (proc_data->rotate % 180) {
+                                       pxp_conf->out_param.width =
+                                               pxp->fb.fmt.height;
+                                       pxp_conf->out_param.height =
+                                               pxp->fb.fmt.width;
+                               } else {
+                                       pxp_conf->out_param.width =
+                                               pxp->fb.fmt.width;
+                                       pxp_conf->out_param.height =
+                                               pxp->fb.fmt.height;
+                               }
+
+                               pxp_conf->out_param.paddr = pxp->outb_phys;
+                               memcpy(&desc->layer_param.out_param,
+                                       &pxp_conf->out_param,
+                                       sizeof(struct pxp_layer_param));
+                       } else if (pxp_conf->ol_param[0].combine_enable) {
+                               /* Overlay */
+                               pxp_conf->ol_param[0].paddr =
+                                               (dma_addr_t)pxp->fb.base;
+                               pxp_conf->ol_param[0].width = pxp->fb.fmt.width;
+                               pxp_conf->ol_param[0].height =
+                                               pxp->fb.fmt.height;
+                               pxp_conf->ol_param[0].pixel_fmt =
+                                               pxp_conf->out_param.pixel_fmt;
+                               memcpy(&desc->layer_param.ol_param,
+                                      &pxp_conf->ol_param[0],
+                                      sizeof(struct pxp_layer_param));
+                       }
+
+                       desc = desc->next;
+               }
+
+               vb->state = VIDEOBUF_PREPARED;
+       }
+
+       return 0;
+
+fail:
+       pxp_buf_free(q, buf);
+       return ret;
+}
+
+
+static void pxp_buf_queue(struct videobuf_queue *q,
+                       struct videobuf_buffer *vb)
+{
+       struct pxps *pxp = q->priv_data;
+       struct pxp_buffer *buf = container_of(vb, struct pxp_buffer, vb);
+       struct dma_async_tx_descriptor *txd = buf->txd;
+       struct pxp_channel *pchan = pxp->pxp_channel[0];
+       dma_cookie_t cookie;
+
+       BUG_ON(!irqs_disabled());
+
+       list_add_tail(&vb->queue, &pxp->outq);
+
+       if (!pxp->active) {
+               pxp->active = buf;
+               vb->state = VIDEOBUF_ACTIVE;
+       } else {
+               vb->state = VIDEOBUF_QUEUED;
+       }
+
+       spin_unlock_irq(&pxp->lock);
+
+       cookie = txd->tx_submit(txd);
+       dev_dbg(&pxp->pdev->dev, "Submitted cookie %d DMA 0x%08x\n",
+                               cookie, sg_dma_address(&buf->sg[0]));
+       mdelay(5);
+       /* trigger ePxP */
+       dma_async_issue_pending(&pchan->dma_chan);
+
+       spin_lock_irq(&pxp->lock);
+
+       if (cookie >= 0)
+               return;
+
+       /* Submit error */
+       pr_err("%s: Submit error\n", __func__);
+       vb->state = VIDEOBUF_PREPARED;
+
+       list_del_init(&vb->queue);
+
+       if (pxp->active == buf)
+               pxp->active = NULL;
+}
+
+static void pxp_buf_release(struct videobuf_queue *q,
+                       struct videobuf_buffer *vb)
+{
+       struct pxps *pxp = q->priv_data;
+       struct pxp_buffer *buf = container_of(vb, struct pxp_buffer, vb);
+       unsigned long flags;
+
+       spin_lock_irqsave(&pxp->lock, flags);
+       if ((vb->state == VIDEOBUF_ACTIVE || vb->state == VIDEOBUF_QUEUED) &&
+           !list_empty(&vb->queue)) {
+               vb->state = VIDEOBUF_ERROR;
+
+               list_del_init(&vb->queue);
+               if (pxp->active == buf)
+                       pxp->active = NULL;
+       }
+       spin_unlock_irqrestore(&pxp->lock, flags);
+
+       pxp_buf_free(q, buf);
+}
+
+static struct videobuf_queue_ops pxp_vbq_ops = {
+       .buf_setup      = pxp_buf_setup,
+       .buf_prepare    = pxp_buf_prepare,
+       .buf_queue      = pxp_buf_queue,
+       .buf_release    = pxp_buf_release,
+};
+
+static int pxp_querycap(struct file *file, void *fh,
+                       struct v4l2_capability *cap)
+{
+       struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+       memset(cap, 0, sizeof(*cap));
+       strcpy(cap->driver, "pxp");
+       strcpy(cap->card, "pxp");
+       strlcpy(cap->bus_info, dev_name(&pxp->pdev->dev),
+               sizeof(cap->bus_info));
+
+       cap->version = (PXP_DRIVER_MAJOR << 8) + PXP_DRIVER_MINOR;
+
+       cap->capabilities = V4L2_CAP_VIDEO_OUTPUT |
+                               V4L2_CAP_VIDEO_OUTPUT_OVERLAY |
+                               V4L2_CAP_STREAMING;
+
+       return 0;
+}
+
+static int pxp_g_fbuf(struct file *file, void *priv,
+                       struct v4l2_framebuffer *fb)
+{
+       struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+       memset(fb, 0, sizeof(*fb));
+
+       fb->capability = V4L2_FBUF_CAP_EXTERNOVERLAY |
+                        V4L2_FBUF_CAP_CHROMAKEY |
+                        V4L2_FBUF_CAP_LOCAL_ALPHA |
+                        V4L2_FBUF_CAP_GLOBAL_ALPHA;
+
+       if (pxp->global_alpha_state)
+               fb->flags |= V4L2_FBUF_FLAG_GLOBAL_ALPHA;
+       if (pxp->s1_chromakey_state)
+               fb->flags |= V4L2_FBUF_FLAG_CHROMAKEY;
+
+       return 0;
+}
+
+static int pxp_s_fbuf(struct file *file, void *priv,
+                       struct v4l2_framebuffer *fb)
+{
+       struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+       pxp->overlay_state =
+               (fb->flags & V4L2_FBUF_FLAG_OVERLAY) != 0;
+       pxp->global_alpha_state =
+               (fb->flags & V4L2_FBUF_FLAG_GLOBAL_ALPHA) != 0;
+       pxp->s1_chromakey_state =
+               (fb->flags & V4L2_FBUF_FLAG_CHROMAKEY) != 0;
+
+       pxp->pxp_conf.ol_param[0].combine_enable = pxp->overlay_state;
+       pxp->pxp_conf.ol_param[0].global_alpha_enable = pxp->global_alpha_state;
+
+       return 0;
+}
+
+static int pxp_g_crop(struct file *file, void *fh,
+                       struct v4l2_crop *c)
+{
+       struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+       if (c->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY)
+               return -EINVAL;
+
+       c->c.left = pxp->pxp_conf.proc_data.drect.left;
+       c->c.top = pxp->pxp_conf.proc_data.drect.top;
+       c->c.width = pxp->pxp_conf.proc_data.drect.width;
+       c->c.height = pxp->pxp_conf.proc_data.drect.height;
+
+       return 0;
+}
+
+static int pxp_s_crop(struct file *file, void *fh,
+                       struct v4l2_crop *c)
+{
+       struct pxps *pxp = video_get_drvdata(video_devdata(file));
+       int l = c->c.left;
+       int t = c->c.top;
+       int w = c->c.width;
+       int h = c->c.height;
+       int fbw = pxp->fb.fmt.width;
+       int fbh = pxp->fb.fmt.height;
+
+       if (c->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY)
+               return -EINVAL;
+
+       /* Constrain parameters to FB limits */
+       w = min(w, fbw);
+       w = max(w, PXP_MIN_PIX);
+       h = min(h, fbh);
+       h = max(h, PXP_MIN_PIX);
+       if ((l + w) > fbw)
+               l = 0;
+       if ((t + h) > fbh)
+               t = 0;
+
+       /* Round up values to PxP pixel block */
+       l = roundup(l, PXP_MIN_PIX);
+       t = roundup(t, PXP_MIN_PIX);
+       w = roundup(w, PXP_MIN_PIX);
+       h = roundup(h, PXP_MIN_PIX);
+
+       pxp->pxp_conf.proc_data.drect.left = l;
+       pxp->pxp_conf.proc_data.drect.top = t;
+       pxp->pxp_conf.proc_data.drect.width = w;
+       pxp->pxp_conf.proc_data.drect.height = h;
+
+       return 0;
+}
+
+static int pxp_queryctrl(struct file *file, void *priv,
+                        struct v4l2_queryctrl *qc)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(pxp_controls); i++)
+               if (qc->id && qc->id == pxp_controls[i].id) {
+                       memcpy(qc, &(pxp_controls[i]), sizeof(*qc));
+                       return 0;
+               }
+
+       return -EINVAL;
+}
+
+static int pxp_g_ctrl(struct file *file, void *priv,
+                        struct v4l2_control *vc)
+{
+       int i;
+
+       struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+       for (i = 0; i < ARRAY_SIZE(pxp_controls); i++)
+               if (vc->id == pxp_controls[i].id)
+                       return pxp_get_cstate(pxp, vc);
+
+       return -EINVAL;
+}
+
+static int pxp_s_ctrl(struct file *file, void *priv,
+                        struct v4l2_control *vc)
+{
+       int i;
+       struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+       for (i = 0; i < ARRAY_SIZE(pxp_controls); i++)
+               if (vc->id == pxp_controls[i].id) {
+                       if (vc->value < pxp_controls[i].minimum ||
+                           vc->value > pxp_controls[i].maximum)
+                               return -ERANGE;
+                       return pxp_set_cstate(pxp, vc);
+               }
+
+       return -EINVAL;
+}
+
+void pxp_release(struct video_device *vfd)
+{
+       struct pxps *pxp = video_get_drvdata(vfd);
+
+       spin_lock(&pxp->lock);
+       video_device_release(vfd);
+       spin_unlock(&pxp->lock);
+}
+
+static int pxp_open(struct file *file)
+{
+       struct pxps *pxp = video_get_drvdata(video_devdata(file));
+       int ret = 0;
+
+       mutex_lock(&pxp->mutex);
+       pxp->users++;
+
+       if (pxp->users > 1) {
+               pxp->users--;
+               ret = -EBUSY;
+               goto out;
+       }
+out:
+       mutex_unlock(&pxp->mutex);
+       if (ret)
+               return ret;
+
+       videobuf_queue_dma_contig_init(&pxp->s0_vbq,
+                               &pxp_vbq_ops,
+                               &pxp->pdev->dev,
+                               &pxp->lock,
+                               V4L2_BUF_TYPE_VIDEO_OUTPUT,
+                               V4L2_FIELD_NONE,
+                               sizeof(struct pxp_buffer),
+                               pxp);
+       dev_dbg(&pxp->pdev->dev, "call pxp_open\n");
+
+       return 0;
+}
+
+static int pxp_close(struct file *file)
+{
+       struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+       videobuf_stop(&pxp->s0_vbq);
+       videobuf_mmap_free(&pxp->s0_vbq);
+       pxp->active = NULL;
+       kfree(pxp->outb);
+       pxp->outb = NULL;
+
+       mutex_lock(&pxp->mutex);
+       pxp->users--;
+       mutex_unlock(&pxp->mutex);
+
+       return 0;
+}
+
+static int pxp_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       struct pxps *pxp = video_get_drvdata(video_devdata(file));
+       int ret;
+
+       ret = videobuf_mmap_mapper(&pxp->s0_vbq, vma);
+
+       return ret;
+}
+
+static const struct v4l2_file_operations pxp_fops = {
+       .owner          = THIS_MODULE,
+       .open           = pxp_open,
+       .release        = pxp_close,
+       .ioctl          = video_ioctl2,
+       .mmap           = pxp_mmap,
+};
+
+static const struct v4l2_ioctl_ops pxp_ioctl_ops = {
+       .vidioc_querycap                = pxp_querycap,
+
+       .vidioc_reqbufs                 = pxp_reqbufs,
+       .vidioc_querybuf                = pxp_querybuf,
+       .vidioc_qbuf                    = pxp_qbuf,
+       .vidioc_dqbuf                   = pxp_dqbuf,
+
+       .vidioc_streamon                = pxp_streamon,
+       .vidioc_streamoff               = pxp_streamoff,
+
+       .vidioc_enum_output             = pxp_enumoutput,
+       .vidioc_g_output                = pxp_g_output,
+       .vidioc_s_output                = pxp_s_output,
+
+       .vidioc_enum_fmt_vid_out        = pxp_enum_fmt_video_output,
+       .vidioc_try_fmt_vid_out         = pxp_try_fmt_video_output,
+       .vidioc_g_fmt_vid_out           = pxp_g_fmt_video_output,
+       .vidioc_s_fmt_vid_out           = pxp_s_fmt_video_output,
+
+       .vidioc_try_fmt_vid_out_overlay = pxp_try_fmt_output_overlay,
+       .vidioc_g_fmt_vid_out_overlay   = pxp_g_fmt_output_overlay,
+       .vidioc_s_fmt_vid_out_overlay   = pxp_s_fmt_output_overlay,
+
+       .vidioc_g_fbuf                  = pxp_g_fbuf,
+       .vidioc_s_fbuf                  = pxp_s_fbuf,
+
+       .vidioc_g_crop                  = pxp_g_crop,
+       .vidioc_s_crop                  = pxp_s_crop,
+
+       .vidioc_queryctrl               = pxp_queryctrl,
+       .vidioc_g_ctrl                  = pxp_g_ctrl,
+       .vidioc_s_ctrl                  = pxp_s_ctrl,
+};
+
+static const struct video_device pxp_template = {
+       .name                           = "PxP",
+       .vfl_type                       = V4L2_CAP_VIDEO_OUTPUT |
+                                               V4L2_CAP_VIDEO_OVERLAY |
+                                               V4L2_CAP_STREAMING,
+       .fops                           = &pxp_fops,
+       .release                        = pxp_release,
+       .minor                          = -1,
+       .ioctl_ops                      = &pxp_ioctl_ops,
+};
+
+static int pxp_probe(struct platform_device *pdev)
+{
+       struct pxps *pxp;
+       int err = 0;
+
+       pxp = kzalloc(sizeof(*pxp), GFP_KERNEL);
+       if (!pxp) {
+               dev_err(&pdev->dev, "failed to allocate control object\n");
+               err = -ENOMEM;
+               goto exit;
+       }
+
+       dev_set_drvdata(&pdev->dev, pxp);
+
+       INIT_LIST_HEAD(&pxp->outq);
+       spin_lock_init(&pxp->lock);
+       mutex_init(&pxp->mutex);
+
+       pxp->pdev = pdev;
+
+       pxp->vdev = video_device_alloc();
+       if (!pxp->vdev) {
+               dev_err(&pdev->dev, "video_device_alloc() failed\n");
+               err = -ENOMEM;
+               goto freeirq;
+       }
+
+       memcpy(pxp->vdev, &pxp_template, sizeof(pxp_template));
+       video_set_drvdata(pxp->vdev, pxp);
+
+       err = video_register_device(pxp->vdev, VFL_TYPE_GRABBER, 0);
+       if (err) {
+               dev_err(&pdev->dev, "failed to register video device\n");
+               goto freevdev;
+       }
+
+       err = pxp_set_fbinfo(pxp);
+       if (err) {
+               dev_err(&pdev->dev, "failed to call pxp_set_fbinfo\n");
+               goto freevdev;
+       }
+
+       dev_info(&pdev->dev, "initialized\n");
+
+exit:
+       return err;
+
+freevdev:
+       video_device_release(pxp->vdev);
+
+freeirq:
+       kfree(pxp);
+
+       return err;
+}
+
+static int __devexit pxp_remove(struct platform_device *pdev)
+{
+       struct pxps *pxp = platform_get_drvdata(pdev);
+
+       video_unregister_device(pxp->vdev);
+       video_device_release(pxp->vdev);
+
+       kfree(pxp);
+
+       return 0;
+}
+
+static struct platform_driver pxp_driver = {
+       .driver         = {
+               .name   = PXP_DRIVER_NAME,
+       },
+       .probe          = pxp_probe,
+       .remove         = __exit_p(pxp_remove),
+};
+
+
+static int __devinit pxp_init(void)
+{
+       return platform_driver_register(&pxp_driver);
+}
+
+static void __exit pxp_exit(void)
+{
+       platform_driver_unregister(&pxp_driver);
+}
+
+module_init(pxp_init);
+module_exit(pxp_exit);
+
+MODULE_DESCRIPTION("MXC PxP V4L2 driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/output/mxc_pxp_v4l2.h b/drivers/media/video/mxc/output/mxc_pxp_v4l2.h
new file mode 100644 (file)
index 0000000..9a531dd
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2010-2011 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 PxP driver
+ * Copyright 2008-2009 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+#ifndef        _MXC_PXP_V4L2_H
+#define        _MXC_PXP_V4L2_H
+
+#include <linux/dmaengine.h>
+#include <linux/pxp_dma.h>
+
+struct pxp_buffer {
+       /* Must be first! */
+       struct videobuf_buffer vb;
+
+       /* One descriptor per scatterlist (per frame) */
+       struct dma_async_tx_descriptor          *txd;
+
+       struct scatterlist                      sg[3];
+};
+
+struct pxps {
+       struct platform_device *pdev;
+
+       spinlock_t lock;
+       struct mutex mutex;
+       int users;
+
+       struct video_device *vdev;
+
+       struct videobuf_queue s0_vbq;
+       struct pxp_buffer *active;
+       struct list_head outq;
+       struct pxp_channel      *pxp_channel[1];        /* We need 1 channel */
+       struct pxp_config_data pxp_conf;
+
+       int output;
+       u32 *outb;
+       dma_addr_t outb_phys;
+       u32 outb_size;
+
+       /* Current S0 configuration */
+       struct pxp_data_format *s0_fmt;
+
+       struct v4l2_framebuffer fb;
+
+       /* Output overlay support */
+       int overlay_state;
+       int global_alpha_state;
+       u8  global_alpha;
+       int s1_chromakey_state;
+       u32 s1_chromakey;
+
+       int fb_blank;
+};
+
+struct pxp_data_format {
+       char *name;
+       unsigned int bpp;
+       u32 fourcc;
+       enum v4l2_colorspace colorspace;
+};
+
+#endif
diff --git a/drivers/media/video/mxc/output/mxc_v4l2_output.c b/drivers/media/video/mxc/output/mxc_v4l2_output.c
new file mode 100644 (file)
index 0000000..e558571
--- /dev/null
@@ -0,0 +1,2795 @@
+/*
+ * Copyright 2005-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 drivers/media/video/mxc/output/mxc_v4l2_output.c
+ *
+ * @brief MXC V4L2 Video Output Driver
+ *
+ * Video4Linux2 Output Device using MXC IPU Post-processing functionality.
+ *
+ * @ingroup MXC_V4L2_OUTPUT
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/semaphore.h>
+#include <linux/slab.h>
+#include <linux/console.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/mxcfb.h>
+#include <media/v4l2-ioctl.h>
+#include <asm/cacheflush.h>
+#include <mach/hardware.h>
+
+#include "mxc_v4l2_output.h"
+
+#define init_MUTEX(sem)         sema_init(sem, 1)
+
+#define INTERLACED_CONTENT(vout) ((cpu_is_mx51() || \
+                                  cpu_is_mx53()) &&                    \
+                                 (((vout)->field_fmt == V4L2_FIELD_INTERLACED_TB) || \
+                                  ((vout)->field_fmt == V4L2_FIELD_INTERLACED_BT)))
+#define LOAD_3FIELDS(vout) ((INTERLACED_CONTENT(vout)) && \
+                           ((vout)->motion_sel != HIGH_MOTION))
+
+struct v4l2_output mxc_outputs[1] = {
+       {
+        .index = MXC_V4L2_OUT_2_SDC,
+        .name = "DISP3 Video Out",
+        .type = V4L2_OUTPUT_TYPE_ANALOG,       /* not really correct,
+                                                  but no other choice */
+        .audioset = 0,
+        .modulator = 0,
+        .std = V4L2_STD_UNKNOWN}
+};
+
+static int video_nr = 16;
+static DEFINE_SPINLOCK(g_lock);
+static int last_index_n;
+static unsigned int ipu_ic_out_max_width_size;
+static unsigned int ipu_ic_out_max_height_size;
+/* debug counters */
+uint32_t g_irq_cnt;
+uint32_t g_buf_output_cnt;
+uint32_t g_buf_q_cnt;
+uint32_t g_buf_dq_cnt;
+
+#define QUEUE_SIZE (MAX_FRAME_NUM + 1)
+static __inline int queue_size(v4l_queue *q)
+{
+       if (q->tail >= q->head)
+               return q->tail - q->head;
+       else
+               return (q->tail + QUEUE_SIZE) - q->head;
+}
+
+static __inline int queue_buf(v4l_queue *q, int idx)
+{
+       if (((q->tail + 1) % QUEUE_SIZE) == q->head)
+               return -1;      /* queue full */
+       q->list[q->tail] = idx;
+       q->tail = (q->tail + 1) % QUEUE_SIZE;
+       return 0;
+}
+
+static __inline int dequeue_buf(v4l_queue *q)
+{
+       int ret;
+       if (q->tail == q->head)
+               return -1;      /* queue empty */
+       ret = q->list[q->head];
+       q->head = (q->head + 1) % QUEUE_SIZE;
+       return ret;
+}
+
+static __inline int peek_next_buf(v4l_queue *q)
+{
+       if (q->tail == q->head)
+               return -1;      /* queue empty */
+       return q->list[q->head];
+}
+
+static __inline unsigned long get_jiffies(struct timeval *t)
+{
+       struct timeval cur;
+
+       if (t->tv_usec >= 1000000) {
+               t->tv_sec += t->tv_usec / 1000000;
+               t->tv_usec = t->tv_usec % 1000000;
+       }
+
+       do_gettimeofday(&cur);
+       if ((t->tv_sec < cur.tv_sec)
+           || ((t->tv_sec == cur.tv_sec) && (t->tv_usec < cur.tv_usec)))
+               return jiffies;
+
+       if (t->tv_usec < cur.tv_usec) {
+               cur.tv_sec = t->tv_sec - cur.tv_sec - 1;
+               cur.tv_usec = t->tv_usec + 1000000 - cur.tv_usec;
+       } else {
+               cur.tv_sec = t->tv_sec - cur.tv_sec;
+               cur.tv_usec = t->tv_usec - cur.tv_usec;
+       }
+
+       return jiffies + timeval_to_jiffies(&cur);
+}
+
+/*!
+ * Private function to free buffers
+ *
+ * @param bufs_paddr   Array of physical address of buffers to be freed
+ *
+ * @param bufs_vaddr   Array of virtual address of buffers to be freed
+ *
+ * @param num_buf      Number of buffers to be freed
+ *
+ * @param size         Size for each buffer to be free
+ *
+ * @return status  0 success.
+ */
+static int mxc_free_buffers(dma_addr_t bufs_paddr[], void *bufs_vaddr[],
+                           int num_buf, int size)
+{
+       int i;
+
+       for (i = 0; i < num_buf; i++) {
+               if (bufs_vaddr[i] != 0) {
+                       dma_free_coherent(0, size, bufs_vaddr[i],
+                                         bufs_paddr[i]);
+                       pr_debug("freed @ paddr=0x%08X\n", (u32) bufs_paddr[i]);
+                       bufs_paddr[i] = 0;
+                       bufs_vaddr[i] = NULL;
+               }
+       }
+       return 0;
+}
+
+/*!
+ * Private function to allocate buffers
+ *
+ * @param bufs_paddr   Output array of physical address of buffers allocated
+ *
+ * @param bufs_vaddr   Output array of virtual address of buffers allocated
+ *
+ * @param num_buf      Input number of buffers to allocate
+ *
+ * @param size         Input size for each buffer to allocate
+ *
+ * @return status      -0 Successfully allocated a buffer, -ENOBUFS failed.
+ */
+static int mxc_allocate_buffers(dma_addr_t bufs_paddr[], void *bufs_vaddr[],
+                               int num_buf, int size)
+{
+       int i;
+
+       for (i = 0; i < num_buf; i++) {
+               bufs_vaddr[i] = dma_alloc_coherent(0, size,
+                                                  &bufs_paddr[i],
+                                                  GFP_DMA | GFP_KERNEL);
+
+               if (bufs_vaddr[i] == 0) {
+                       mxc_free_buffers(bufs_paddr, bufs_vaddr, i, size);
+                       printk(KERN_ERR "dma_alloc_coherent failed.\n");
+                       return -ENOBUFS;
+               }
+               pr_debug("allocated @ paddr=0x%08X, size=%d.\n",
+                        (u32) bufs_paddr[i], size);
+       }
+
+       return 0;
+}
+
+/*
+ * Returns bits per pixel for given pixel format
+ *
+ * @param pixelformat  V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_BGR24 or V4L2_PIX_FMT_BGR32
+ *
+ * @return bits per pixel of pixelformat
+ */
+static u32 fmt_to_bpp(u32 pixelformat)
+{
+       u32 bpp;
+
+       bpp = 8 * bytes_per_pixel(pixelformat);
+       return bpp;
+}
+
+static bool format_is_yuv(u32 pixelformat)
+{
+       switch (pixelformat) {
+       case V4L2_PIX_FMT_YUV420:
+       case V4L2_PIX_FMT_UYVY:
+       case V4L2_PIX_FMT_YUYV:
+       case V4L2_PIX_FMT_YUV422P:
+       case V4L2_PIX_FMT_YVU420:
+       case V4L2_PIX_FMT_YUV444:
+       case V4L2_PIX_FMT_NV12:
+               return true;
+               break;
+       }
+       return false;
+}
+
+static u32 bpp_to_fmt(struct fb_info *fbi)
+{
+       if (fbi->var.nonstd)
+               return fbi->var.nonstd;
+
+       if (fbi->var.bits_per_pixel == 24)
+               return V4L2_PIX_FMT_BGR24;
+       else if (fbi->var.bits_per_pixel == 32)
+               return V4L2_PIX_FMT_BGR32;
+       else if (fbi->var.bits_per_pixel == 16)
+               return V4L2_PIX_FMT_RGB565;
+
+       return 0;
+}
+
+/*
+ * we are using double buffer for video playback, ipu need make
+ * sure current buffer should not be the same buffer of next display
+ * one.
+ */
+static int select_display_buffer(vout_data *vout, int next_buf)
+{
+       int ret = 0;
+
+       vout->disp_buf_num = next_buf;
+       if (ipu_get_cur_buffer_idx(vout->display_ch, IPU_INPUT_BUFFER)
+                       != next_buf)
+               ret = ipu_select_buffer(vout->display_ch, IPU_INPUT_BUFFER,
+                               next_buf);
+       else
+               dev_dbg(&vout->video_dev->dev,
+                       "display buffer not ready for select\n");
+       return ret;
+}
+
+static void setup_next_buf_timer(vout_data *vout, int index)
+{
+       unsigned long timeout;
+
+       /* Setup timer for next buffer */
+       /* if timestamp is 0, then default to 30fps */
+       if ((vout->v4l2_bufs[index].timestamp.tv_sec == 0)
+                       && (vout->v4l2_bufs[index].timestamp.tv_usec == 0)
+                       && vout->start_jiffies)
+               timeout =
+                       vout->start_jiffies + vout->frame_count * HZ / 30;
+       else
+               timeout =
+                       get_jiffies(&vout->v4l2_bufs[index].timestamp);
+
+       if (jiffies >= timeout) {
+               dev_dbg(&vout->video_dev->dev,
+                               "warning: timer timeout already expired.\n");
+       }
+       if (mod_timer(&vout->output_timer, timeout))
+               dev_dbg(&vout->video_dev->dev,
+                               "warning: timer was already set\n");
+
+       dev_dbg(&vout->video_dev->dev,
+                       "timer handler next schedule: %lu\n", timeout);
+}
+
+static int finish_previous_frame(vout_data *vout)
+{
+       struct fb_info *fbi =
+               registered_fb[vout->output_fb_num[vout->cur_disp_output]];
+       mm_segment_t old_fs;
+       int ret = 0, try = 0;
+
+       /* make sure buf[vout->disp_buf_num] in showing */
+       while (ipu_check_buffer_ready(vout->display_ch,
+                       IPU_INPUT_BUFFER, vout->disp_buf_num)) {
+               if (fbi->fbops->fb_ioctl) {
+                       old_fs = get_fs();
+                       set_fs(KERNEL_DS);
+                       ret = fbi->fbops->fb_ioctl(fbi, MXCFB_WAIT_FOR_VSYNC,
+                                       (unsigned int)NULL);
+                       set_fs(old_fs);
+
+                       if ((ret < 0) || (try == 1)) {
+                               /*
+                                * ic_bypass need clear display buffer ready for next update.
+                                * when fb doing blank and unblank, it has chance to go into
+                                * dead loop: fb unblank just after buffer 1 ready selected.
+                                */
+                               ipu_clear_buffer_ready(vout->display_ch, IPU_INPUT_BUFFER,
+                                               vout->disp_buf_num);
+                       }
+               }
+               try++;
+       }
+
+       return ret;
+}
+
+static int show_current_frame(vout_data *vout)
+{
+       struct fb_info *fbi =
+               registered_fb[vout->output_fb_num[vout->cur_disp_output]];
+       mm_segment_t old_fs;
+       int ret = 0;
+
+       /* make sure buf[vout->disp_buf_num] begin to show */
+       if (ipu_get_cur_buffer_idx(vout->display_ch, IPU_INPUT_BUFFER)
+               != vout->disp_buf_num) {
+               /* wait for display frame finish */
+               if (fbi->fbops->fb_ioctl) {
+                       old_fs = get_fs();
+                       set_fs(KERNEL_DS);
+                       ret = fbi->fbops->fb_ioctl(fbi, MXCFB_WAIT_FOR_VSYNC,
+                                       (unsigned int)NULL);
+                       set_fs(old_fs);
+               }
+       }
+
+       return ret;
+}
+
+static void icbypass_work_func(struct work_struct *work)
+{
+       vout_data *vout =
+               container_of(work, vout_data, icbypass_work);
+       int index, ret;
+       int last_buf;
+       unsigned long lock_flags = 0;
+
+       finish_previous_frame(vout);
+
+       spin_lock_irqsave(&g_lock, lock_flags);
+
+       index = dequeue_buf(&vout->ready_q);
+       if (index == -1) {      /* no buffers ready, should never occur */
+               dev_err(&vout->video_dev->dev,
+                               "mxc_v4l2out: timer - no queued buffers ready\n");
+               goto exit;
+       }
+       g_buf_dq_cnt++;
+       vout->frame_count++;
+
+       vout->ipu_buf[vout->next_rdy_ipu_buf] = index;
+       ret = ipu_update_channel_buffer(vout->display_ch, IPU_INPUT_BUFFER,
+                       vout->next_rdy_ipu_buf,
+                       vout->v4l2_bufs[index].m.offset);
+       ret += select_display_buffer(vout, vout->next_rdy_ipu_buf);
+       if (ret < 0) {
+               dev_err(&vout->video_dev->dev,
+                               "unable to update buffer %d address rc=%d\n",
+                               vout->next_rdy_ipu_buf, ret);
+               goto exit;
+       }
+       spin_unlock_irqrestore(&g_lock, lock_flags);
+       show_current_frame(vout);
+       spin_lock_irqsave(&g_lock, lock_flags);
+       vout->next_rdy_ipu_buf = !vout->next_rdy_ipu_buf;
+
+       last_buf = vout->ipu_buf[vout->next_done_ipu_buf];
+       if (last_buf != -1) {
+               g_buf_output_cnt++;
+               vout->v4l2_bufs[last_buf].flags = V4L2_BUF_FLAG_DONE;
+               queue_buf(&vout->done_q, last_buf);
+               wake_up_interruptible(&vout->v4l_bufq);
+               vout->ipu_buf[vout->next_done_ipu_buf] = -1;
+               vout->next_done_ipu_buf = !vout->next_done_ipu_buf;
+       }
+
+       if (g_buf_output_cnt > 0) {
+               /* Setup timer for next buffer */
+               index = peek_next_buf(&vout->ready_q);
+               if (index != -1)
+                       setup_next_buf_timer(vout, index);
+               else
+                       vout->state = STATE_STREAM_PAUSED;
+
+               if (vout->state == STATE_STREAM_STOPPING) {
+                       if ((vout->ipu_buf[0] == -1) && (vout->ipu_buf[1] == -1)) {
+                               vout->state = STATE_STREAM_OFF;
+                       }
+               }
+       }
+exit:
+       spin_unlock_irqrestore(&g_lock, lock_flags);
+}
+
+static int get_cur_fb_blank(vout_data *vout)
+{
+       struct fb_info *fbi =
+               registered_fb[vout->output_fb_num[vout->cur_disp_output]];
+       mm_segment_t old_fs;
+       int ret = 0;
+
+       /* Check BG blank first, if BG is blank, FG should be blank too */
+       if (vout->display_ch == MEM_FG_SYNC) {
+               int i, bg_found = 0;
+               for (i = 0; i < num_registered_fb; i++) {
+                       struct fb_info *bg_fbi;
+                       char *idstr = registered_fb[i]->fix.id;
+                       if (strncmp(idstr, "DISP3 BG", 8) == 0) {
+                               bg_found = 1;
+                               bg_fbi = registered_fb[i];
+                               if (bg_fbi->fbops->fb_ioctl) {
+                                       old_fs = get_fs();
+                                       set_fs(KERNEL_DS);
+                                       ret = bg_fbi->fbops->fb_ioctl(bg_fbi,
+                                                       MXCFB_GET_FB_BLANK,
+                                                       (unsigned int)(&vout->fb_blank));
+                                       set_fs(old_fs);
+                               }
+                       }
+                       if (bg_found) {
+                               if (vout->fb_blank == FB_BLANK_UNBLANK)
+                                       break;
+                               else
+                                       return ret;
+                       }
+               }
+       }
+
+       if (fbi->fbops->fb_ioctl) {
+               old_fs = get_fs();
+               set_fs(KERNEL_DS);
+               ret = fbi->fbops->fb_ioctl(fbi, MXCFB_GET_FB_BLANK,
+                               (unsigned int)(&vout->fb_blank));
+               set_fs(old_fs);
+       }
+
+       return ret;
+}
+
+static void mxc_v4l2out_timer_handler(unsigned long arg)
+{
+       int index, ret;
+       unsigned long lock_flags = 0;
+       vout_data *vout = (vout_data *) arg;
+       static int old_fb_blank = FB_BLANK_UNBLANK;
+
+       spin_lock_irqsave(&g_lock, lock_flags);
+
+       if ((vout->state == STATE_STREAM_STOPPING)
+           || (vout->state == STATE_STREAM_OFF))
+               goto exit0;
+
+       /*
+        * If timer occurs before IPU h/w is ready, then set the state to
+        * paused and the timer will be set again when next buffer is queued
+        * or PP comletes
+        */
+       if (vout->ipu_buf[vout->next_rdy_ipu_buf] != -1) {
+               dev_dbg(&vout->video_dev->dev, "IPU buffer busy\n");
+               vout->state = STATE_STREAM_PAUSED;
+               goto exit0;
+       }
+
+       /* VDI need both buffer done before update buffer? */
+       if (INTERLACED_CONTENT(vout) &&
+               (vout->ipu_buf[!vout->next_rdy_ipu_buf] != -1)) {
+               dev_dbg(&vout->video_dev->dev, "IPU buffer busy\n");
+               vout->state = STATE_STREAM_PAUSED;
+               goto exit0;
+       }
+
+       /* Handle ic bypass mode in work queue */
+       if (vout->ic_bypass) {
+               if (queue_work(vout->v4l_wq, &vout->icbypass_work) == 0) {
+                       dev_err(&vout->video_dev->dev,
+                               "ic bypass work was in queue already!\n ");
+                       vout->state = STATE_STREAM_PAUSED;
+               }
+               goto exit0;
+       }
+
+       get_cur_fb_blank(vout);
+       if (vout->fb_blank == FB_BLANK_UNBLANK) {
+               /* if first come back from fb blank, recover correct stack */
+               if (old_fb_blank != FB_BLANK_UNBLANK) {
+                       if (vout->next_disp_ipu_buf == 1)
+                               ipu_select_buffer(vout->display_ch, IPU_INPUT_BUFFER, 0);
+                       else
+                               ipu_select_buffer(vout->display_ch, IPU_INPUT_BUFFER, 1);
+               }
+               if (ipu_get_cur_buffer_idx(vout->display_ch, IPU_INPUT_BUFFER)
+                               == vout->next_disp_ipu_buf) {
+                       dev_dbg(&vout->video_dev->dev, "IPU disp busy\n");
+                       index = peek_next_buf(&vout->ready_q);
+                       setup_next_buf_timer(vout, index);
+                       old_fb_blank = vout->fb_blank;
+                       goto exit0;
+               }
+       }
+       old_fb_blank = vout->fb_blank;
+
+       /* Dequeue buffer and pass to IPU */
+       index = dequeue_buf(&vout->ready_q);
+       if (index == -1) {      /* no buffers ready, should never occur */
+               dev_err(&vout->video_dev->dev,
+                       "mxc_v4l2out: timer - no queued buffers ready\n");
+               goto exit0;
+       }
+       g_buf_dq_cnt++;
+       vout->frame_count++;
+
+       /* update next buffer */
+       if (LOAD_3FIELDS(vout)) {
+               int index_n = index;
+               int index_p = last_index_n;
+               vout->ipu_buf_p[vout->next_rdy_ipu_buf] = last_index_n;
+               vout->ipu_buf[vout->next_rdy_ipu_buf] = index;
+               vout->ipu_buf_n[vout->next_rdy_ipu_buf] = index;
+               ret = ipu_update_channel_buffer(vout->post_proc_ch,
+                               IPU_INPUT_BUFFER,
+                               vout->next_rdy_ipu_buf,
+                               vout->v4l2_bufs[index].m.offset);
+               ret += ipu_update_channel_buffer(MEM_VDI_PRP_VF_MEM_P,
+                               IPU_INPUT_BUFFER,
+                               vout->next_rdy_ipu_buf,
+                               vout->v4l2_bufs[index_p].m.offset + vout->bytesperline);
+               ret += ipu_update_channel_buffer(MEM_VDI_PRP_VF_MEM_N,
+                               IPU_INPUT_BUFFER,
+                               vout->next_rdy_ipu_buf,
+                               vout->v4l2_bufs[index_n].m.offset + vout->bytesperline);
+               last_index_n = index;
+       } else {
+               vout->ipu_buf[vout->next_rdy_ipu_buf] = index;
+               if (vout->pp_split) {
+                       vout->ipu_buf[!vout->next_rdy_ipu_buf] = index;
+                       /* always left stripe */
+                       ret = ipu_update_channel_buffer(vout->post_proc_ch,
+                                       IPU_INPUT_BUFFER,
+                                       0,/* vout->next_rdy_ipu_buf,*/
+                                       (vout->v4l2_bufs[index].m.offset) +
+                                       vout->pp_left_stripe.input_column +
+                                       vout->pp_up_stripe.input_column * vout->bytesperline);
+
+                       /* the U/V offset has to be updated inside of IDMAC */
+                       /* according to stripe offset */
+                       ret += ipu_update_channel_offset(vout->post_proc_ch,
+                                       IPU_INPUT_BUFFER,
+                                       vout->v2f.fmt.pix.pixelformat,
+                                       vout->v2f.fmt.pix.width,
+                                       vout->v2f.fmt.pix.height,
+                                       vout->bytesperline,
+                                       vout->offset.u_offset,
+                                       vout->offset.v_offset,
+                                       vout->pp_up_stripe.input_column,
+                                       vout->pp_left_stripe.input_column);
+               } else
+                       ret = ipu_update_channel_buffer(vout->post_proc_ch,
+                                       IPU_INPUT_BUFFER,
+                                       vout->next_rdy_ipu_buf,
+                                       vout->v4l2_bufs[index].m.offset);
+       }
+
+       if (ret < 0) {
+               dev_err(&vout->video_dev->dev,
+                       "unable to update buffer %d address rc=%d\n",
+                       vout->next_rdy_ipu_buf, ret);
+               goto exit0;
+       }
+
+       /* set next buffer ready */
+       if (LOAD_3FIELDS(vout))
+               ret = ipu_select_multi_vdi_buffer(vout->next_rdy_ipu_buf);
+       else
+               ret = ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER,
+                               vout->next_rdy_ipu_buf);
+       if (ret < 0) {
+               dev_err(&vout->video_dev->dev,
+                               "unable to set IPU buffer ready\n");
+               goto exit0;
+       }
+
+       /* Split mode use buf 0 only, no need swith buf */
+       if (!vout->pp_split)
+               vout->next_rdy_ipu_buf = !vout->next_rdy_ipu_buf;
+
+       /* Always assume display in double buffers */
+       vout->next_disp_ipu_buf = !vout->next_disp_ipu_buf;
+
+       /* Setup timer for next buffer */
+       index = peek_next_buf(&vout->ready_q);
+       if (index != -1)
+               setup_next_buf_timer(vout, index);
+       else
+               vout->state = STATE_STREAM_PAUSED;
+
+       if (vout->state == STATE_STREAM_STOPPING) {
+               if ((vout->ipu_buf[0] == -1) && (vout->ipu_buf[1] == -1)) {
+                       vout->state = STATE_STREAM_OFF;
+               }
+       }
+
+       spin_unlock_irqrestore(&g_lock, lock_flags);
+
+       return;
+
+exit0:
+       spin_unlock_irqrestore(&g_lock, lock_flags);
+}
+
+static irqreturn_t mxc_v4l2out_work_irq_handler(int irq, void *dev_id)
+{
+       int last_buf;
+       int index;
+       unsigned long lock_flags = 0;
+       vout_data *vout = dev_id;
+       int pp_out_buf_left_right = 0;
+       int disp_buf_num = 0;
+       int disp_buf_num_next = 1;
+       int local_buffer = 0;
+       int pp_out_buf_offset = 0;
+       int pp_out_buf_up_down = 0;
+       int release_buffer = 0;
+       u32 eba_offset = 0;
+       u32 vertical_offset = 0;
+       u16 x_pos;
+       u16 y_pos;
+       int ret = -1;
+
+       spin_lock_irqsave(&g_lock, lock_flags);
+
+       g_irq_cnt++;
+
+       /* Process previous buffer */
+       if (LOAD_3FIELDS(vout))
+               last_buf = vout->ipu_buf_p[vout->next_done_ipu_buf];
+       else
+               last_buf = vout->ipu_buf[vout->next_done_ipu_buf];
+
+       if (last_buf != -1) {
+               /* If IC split mode on, update output buffer number */
+               if (vout->pp_split) {
+                       pp_out_buf_up_down = vout->pp_split_buf_num & 1;/* left/right stripe */
+                       pp_out_buf_left_right = (vout->pp_split_buf_num >> 1) & 1; /* up/down */
+                       local_buffer = (vout->pp_split == 1) ? pp_out_buf_up_down :
+                                       pp_out_buf_left_right;
+                       disp_buf_num = vout->pp_split_buf_num >> 2;
+                       disp_buf_num_next =
+                                       ((vout->pp_split_buf_num + (vout->pp_split << 0x1)) & 7) >> 2;
+                       if ((!pp_out_buf_left_right) ||
+                               ((!pp_out_buf_up_down) && (vout->pp_split == 1))) {
+                               if (vout->pp_split == 1) {
+                                               eba_offset = ((pp_out_buf_left_right + pp_out_buf_up_down) & 1) ?
+                                                                       vout->pp_right_stripe.input_column :
+                                                                       vout->pp_left_stripe.input_column;
+                                               vertical_offset = pp_out_buf_up_down ?
+                                                                       vout->pp_up_stripe.input_column :
+                                                                       vout->pp_down_stripe.input_column;
+
+                               } else {
+                                               eba_offset = pp_out_buf_left_right ?
+                                                                       vout->pp_left_stripe.input_column :
+                                                                       vout->pp_right_stripe.input_column;
+                                               vertical_offset = pp_out_buf_left_right ?
+                                                                       vout->pp_up_stripe.input_column :
+                                                                       vout->pp_down_stripe.input_column;
+                               }
+
+                               ret = ipu_update_channel_buffer(vout->post_proc_ch,
+                                               IPU_INPUT_BUFFER,
+                                               (1 - local_buffer),
+                                               (vout->v4l2_bufs[vout->ipu_buf[disp_buf_num]].m.offset)
+                                               + eba_offset + vertical_offset * vout->bytesperline);
+                               ret += ipu_update_channel_offset(vout->post_proc_ch,
+                                                               IPU_INPUT_BUFFER,
+                                                               vout->v2f.fmt.pix.pixelformat,
+                                                               vout->v2f.fmt.pix.width,
+                                                               vout->v2f.fmt.pix.height,
+                                                               vout->bytesperline,
+                                                               vout->offset.u_offset,
+                                                               vout->offset.v_offset,
+                                                               vertical_offset,
+                                                               eba_offset);
+
+                               /* select right stripe */
+                               ret += ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER,
+                                                       (1 - local_buffer));
+                               if (ret < 0)
+                                       dev_err(&vout->video_dev->dev,
+                                       "unable to set IPU buffer ready\n");
+                       }
+
+                       /* offset for next buffer's EBA */
+                       eba_offset = 0;
+                       if (vout->pp_split == 1) {
+                               pp_out_buf_offset = ((vout->pp_split_buf_num >> 1) & 1) ?
+                                                               vout->pp_left_stripe.output_column :
+                                                               vout->pp_right_stripe.output_column;
+
+                               eba_offset = ((vout->pp_split_buf_num & 1) ?
+                                                               vout->pp_down_stripe.output_column :
+                                                               vout->pp_up_stripe.output_column);
+
+                       } else {
+                               pp_out_buf_offset = ((vout->pp_split_buf_num >> 1) & 1) ?
+                                                                       vout->pp_right_stripe.output_column :
+                                                                       vout->pp_left_stripe.output_column;
+                               eba_offset = ((vout->pp_split_buf_num >> 1) & 1) ?
+                                                                       vout->pp_down_stripe.output_column :
+                                                                       vout->pp_up_stripe.output_column;
+                       }
+
+                       if (vout->cur_disp_output == 5) {
+                               x_pos = (vout->crop_current.left / 8) * 8;
+                               y_pos = vout->crop_current.top;
+                               eba_offset += (vout->xres * y_pos + x_pos) * vout->bpp / 8;
+                       }
+
+
+                       /* next buffer update */
+                       eba_offset = vout->display_bufs[disp_buf_num_next] +
+                                               pp_out_buf_offset + eba_offset;
+
+                       ipu_update_channel_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER,
+                                               local_buffer, eba_offset);
+
+                       /* next buffer ready */
+                       ret = ipu_select_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, local_buffer);
+
+                       /* next stripe_buffer index 0..7 */
+                       vout->pp_split_buf_num = (vout->pp_split_buf_num + vout->pp_split) & 0x7;
+               } else {
+                       disp_buf_num = vout->next_done_ipu_buf;
+                       ret += ipu_select_buffer(vout->display_input_ch, IPU_OUTPUT_BUFFER,
+                                       vout->next_done_ipu_buf);
+               }
+
+               /* release buffer. For split mode: if second stripe is done */
+               release_buffer = vout->pp_split ? (!(vout->pp_split_buf_num & 0x3)) : 1;
+               if (release_buffer) {
+                       if (vout->fb_blank == FB_BLANK_UNBLANK)
+                               select_display_buffer(vout, disp_buf_num);
+                       g_buf_output_cnt++;
+                       vout->v4l2_bufs[last_buf].flags = V4L2_BUF_FLAG_DONE;
+                       queue_buf(&vout->done_q, last_buf);
+                       wake_up_interruptible(&vout->v4l_bufq);
+                       vout->ipu_buf[vout->next_done_ipu_buf] = -1;
+                       if (LOAD_3FIELDS(vout)) {
+                               vout->ipu_buf_p[vout->next_done_ipu_buf] = -1;
+                               vout->ipu_buf_n[vout->next_done_ipu_buf] = -1;
+                       }
+                       /* split mode use buf 0 only, no need switch buf */
+                       if (!vout->pp_split)
+                               vout->next_done_ipu_buf = !vout->next_done_ipu_buf;
+               }
+       } /* end of last_buf != -1 */
+
+       index = peek_next_buf(&vout->ready_q);
+       if (vout->state == STATE_STREAM_STOPPING) {
+               if ((vout->ipu_buf[0] == -1) && (vout->ipu_buf[1] == -1)) {
+                       vout->state = STATE_STREAM_OFF;
+               }
+       } else if ((vout->state == STATE_STREAM_PAUSED)
+                  && (index != -1)) {
+               /* Setup timer for next buffer, when stream has been paused */
+               pr_debug("next index %d\n", index);
+               setup_next_buf_timer(vout, index);
+               vout->state = STATE_STREAM_ON;
+       }
+
+       spin_unlock_irqrestore(&g_lock, lock_flags);
+
+       return IRQ_HANDLED;
+}
+
+/*!
+ *  Initialize VDI channels
+ *
+ * @param vout      structure vout_data *
+ *
+ * @return status  0 Success
+ */
+static int init_VDI_channel(vout_data *vout, ipu_channel_params_t params)
+{
+       struct device *dev = &vout->video_dev->dev;
+
+       if (ipu_init_channel(MEM_VDI_PRP_VF_MEM, &params) != 0) {
+               dev_dbg(dev, "Error initializing VDI current channel\n");
+               return -EINVAL;
+       }
+       if (LOAD_3FIELDS(vout)) {
+               if (ipu_init_channel(MEM_VDI_PRP_VF_MEM_P, &params) != 0) {
+                       dev_err(dev, "Error initializing VDI previous channel\n");
+                       return -EINVAL;
+               }
+               if (ipu_init_channel(MEM_VDI_PRP_VF_MEM_N, &params) != 0) {
+                       dev_err(dev, "Error initializing VDI next channel\n");
+                       return -EINVAL;
+               }
+       }
+       return 0;
+}
+
+/*!
+ * Initialize VDI channel buffers
+ *
+ * @param vout      structure vout_data *
+ *
+ * @return status  0 Success
+ */
+static int init_VDI_in_channel_buffer(vout_data *vout, uint32_t in_pixel_fmt,
+                                  uint16_t in_width, uint16_t in_height,
+                                  uint32_t stride,
+                                  uint32_t u_offset, uint32_t v_offset)
+{
+       struct device *dev = &vout->video_dev->dev;
+
+       if (ipu_init_channel_buffer(MEM_VDI_PRP_VF_MEM, IPU_INPUT_BUFFER,
+                                   in_pixel_fmt, in_width, in_height, stride,
+                                   IPU_ROTATE_NONE,
+                                   vout->v4l2_bufs[vout->ipu_buf[0]].m.offset,
+                                   vout->v4l2_bufs[vout->ipu_buf[0]].m.offset,
+                                   u_offset, v_offset) != 0) {
+               dev_err(dev, "Error initializing VDI current input buffer\n");
+               return -EINVAL;
+       }
+       if (LOAD_3FIELDS(vout)) {
+               if (ipu_init_channel_buffer(MEM_VDI_PRP_VF_MEM_P,
+                                           IPU_INPUT_BUFFER,
+                                           in_pixel_fmt, in_width, in_height,
+                                           stride, IPU_ROTATE_NONE,
+                                           vout->v4l2_bufs[vout->ipu_buf_p[0]].m.offset+vout->bytesperline,
+                                           vout->v4l2_bufs[vout->ipu_buf_p[0]].m.offset+vout->bytesperline,
+                                           u_offset, v_offset) != 0) {
+                       dev_err(dev, "Error initializing VDI previous input buffer\n");
+                       return -EINVAL;
+               }
+               if (ipu_init_channel_buffer(MEM_VDI_PRP_VF_MEM_N,
+                                           IPU_INPUT_BUFFER,
+                                           in_pixel_fmt, in_width, in_height,
+                                           stride, IPU_ROTATE_NONE,
+                                           vout->v4l2_bufs[vout->ipu_buf_n[0]].m.offset+vout->bytesperline,
+                                           vout->v4l2_bufs[vout->ipu_buf_n[0]].m.offset+vout->bytesperline,
+                                           u_offset, v_offset) != 0) {
+                       dev_err(dev, "Error initializing VDI next input buffer\n");
+                       return -EINVAL;
+               }
+       }
+       return 0;
+}
+
+/*!
+ * Initialize VDI path
+ *
+ * @param vout      structure vout_data *
+ *
+ * @return status  0 Success
+ */
+static int init_VDI(ipu_channel_params_t params, vout_data *vout,
+                       struct device *dev, struct fb_info *fbi,
+                       u16 out_width, u16 out_height)
+{
+       params.mem_prp_vf_mem.in_width = vout->v2f.fmt.pix.width;
+       params.mem_prp_vf_mem.in_height = vout->v2f.fmt.pix.height;
+       params.mem_prp_vf_mem.motion_sel = vout->motion_sel;
+       params.mem_prp_vf_mem.field_fmt = vout->field_fmt;
+       params.mem_prp_vf_mem.in_pixel_fmt = vout->v2f.fmt.pix.pixelformat;
+       params.mem_prp_vf_mem.out_width = out_width;
+       params.mem_prp_vf_mem.out_height = out_height;
+       params.mem_prp_vf_mem.out_pixel_fmt = bpp_to_fmt(fbi);
+
+       if (init_VDI_channel(vout, params) != 0) {
+               dev_err(dev, "Error init_VDI_channel channel\n");
+               return -EINVAL;
+       }
+
+       if (init_VDI_in_channel_buffer(vout,
+                                      params.mem_prp_vf_mem.in_pixel_fmt,
+                                      params.mem_prp_vf_mem.in_width,
+                                      params.mem_prp_vf_mem.in_height,
+                                      bytes_per_pixel(params.mem_prp_vf_mem.
+                                                      in_pixel_fmt),
+                                      vout->offset.u_offset,
+                                      vout->offset.v_offset) != 0) {
+               return -EINVAL;
+       }
+
+       if (!ipu_can_rotate_in_place(vout->rotate)) {
+               if (vout->rot_pp_bufs[0]) {
+                       mxc_free_buffers(vout->rot_pp_bufs,
+                                        vout->rot_pp_bufs_vaddr, 2,
+                                        vout->display_buf_size);
+               }
+               if (mxc_allocate_buffers
+                   (vout->rot_pp_bufs, vout->rot_pp_bufs_vaddr, 2,
+                    vout->display_buf_size) < 0) {
+                       return -ENOBUFS;
+               }
+
+               if (ipu_init_channel_buffer(vout->post_proc_ch,
+                                           IPU_OUTPUT_BUFFER,
+                                           params.mem_prp_vf_mem.
+                                           out_pixel_fmt, out_width,
+                                           out_height, out_width,
+                                           IPU_ROTATE_NONE,
+                                           vout->rot_pp_bufs[0],
+                                           vout->rot_pp_bufs[1], 0, 0) != 0) {
+                       dev_err(dev, "Error initializing PRP output buffer\n");
+                       return -EINVAL;
+               }
+
+               if (ipu_init_channel(MEM_ROT_VF_MEM, NULL) != 0) {
+                       dev_err(dev, "Error initializing PP ROT channel\n");
+                       return -EINVAL;
+               }
+               if (ipu_init_channel_buffer(MEM_ROT_VF_MEM,
+                                           IPU_INPUT_BUFFER,
+                                           params.mem_prp_vf_mem.
+                                           out_pixel_fmt, out_width,
+                                           out_height, out_width,
+                                           vout->rotate,
+                                           vout->rot_pp_bufs[0],
+                                           vout->rot_pp_bufs[1], 0, 0) != 0) {
+                       dev_err(dev,
+                               "Error initializing PP ROT input buffer\n");
+                       return -EINVAL;
+               }
+
+               /* swap width and height */
+               if (vout->rotate >= IPU_ROTATE_90_RIGHT) {
+                       out_width = vout->crop_current.width;
+                       out_height = vout->crop_current.height;
+               }
+
+               if (ipu_init_channel_buffer(MEM_ROT_VF_MEM,
+                                           IPU_OUTPUT_BUFFER,
+                                           params.mem_prp_vf_mem.
+                                           out_pixel_fmt, out_width,
+                                           out_height, out_width,
+                                           IPU_ROTATE_NONE,
+                                           vout->display_bufs[0],
+                                           vout->display_bufs[1], 0, 0) != 0) {
+                       dev_err(dev,
+                               "Error initializing PP-VDI output buffer\n");
+                       return -EINVAL;
+               }
+
+               if (ipu_link_channels(vout->post_proc_ch, MEM_ROT_VF_MEM) < 0)
+                       return -EINVAL;
+
+               vout->display_input_ch = MEM_ROT_VF_MEM;
+               ipu_enable_channel(MEM_ROT_VF_MEM);
+               ipu_select_buffer(MEM_ROT_VF_MEM, IPU_OUTPUT_BUFFER, 0);
+               ipu_select_buffer(MEM_ROT_VF_MEM, IPU_OUTPUT_BUFFER, 1);
+       } else {
+               if (ipu_init_channel_buffer(vout->post_proc_ch,
+                                           IPU_OUTPUT_BUFFER,
+                                           params.mem_prp_vf_mem.
+                                           out_pixel_fmt, out_width,
+                                           out_height, out_width,
+                                           vout->rotate,
+                                           vout->display_bufs[0],
+                                           vout->display_bufs[1], 0, 0) != 0) {
+                       dev_err(dev,
+                               "Error initializing PP-VDI output buffer\n");
+                       return -EINVAL;
+               }
+       }
+       return 0;
+}
+
+/*!
+ * Initialize PP path
+ *
+ * @param params    structure ipu_channel_params_t
+ *
+ * @param vout      structure vout_data *
+ *
+ * @return status  0 Success
+ */
+static int init_PP(ipu_channel_params_t *params, vout_data *vout,
+                  struct device *dev, struct fb_info *fbi,
+                  u16 out_width, u16 out_height)
+{
+       u16 in_width, out_stride; /* stride of output channel */
+       u32 eba_offset;
+       u16 x_pos;
+       u16 y_pos;
+       dma_addr_t phy_addr0;
+       dma_addr_t phy_addr1;
+
+       eba_offset = 0;
+       x_pos = 0;
+       y_pos = 0;
+
+       params->mem_pp_mem.out_pixel_fmt = bpp_to_fmt(fbi);
+
+       if (vout->cur_disp_output == 5) {
+               x_pos = (vout->crop_current.left / 8) * 8;
+               y_pos = vout->crop_current.top;
+               eba_offset = (vout->xres*y_pos + x_pos) *
+                               bytes_per_pixel(params->mem_pp_mem.out_pixel_fmt);
+       }
+
+       vout->bpp = fmt_to_bpp(params->mem_pp_mem.out_pixel_fmt);
+       out_stride = vout->xres *
+                               bytes_per_pixel(params->mem_pp_mem.out_pixel_fmt);
+       in_width = params->mem_pp_mem.in_width = vout->v2f.fmt.pix.width;
+       params->mem_pp_mem.in_height = vout->v2f.fmt.pix.height;
+       params->mem_pp_mem.in_pixel_fmt = vout->v2f.fmt.pix.pixelformat;
+       params->mem_pp_mem.out_width = out_width;
+       params->mem_pp_mem.out_height = out_height;
+       params->mem_pp_mem.outh_resize_ratio = 0; /* 0 means unused */
+       params->mem_pp_mem.outv_resize_ratio = 0; /* 0 means unused */
+       /* split IC by two stripes, the by pass is impossible*/
+       if (vout->pp_split) {
+               vout->pp_left_stripe.input_column = 0;
+               vout->pp_left_stripe.output_column = 0;
+               vout->pp_right_stripe.input_column = 0;
+               vout->pp_right_stripe.output_column = 0;
+               vout->pp_up_stripe.input_column = 0;
+               vout->pp_up_stripe.output_column = 0;
+               vout->pp_down_stripe.input_column = 0;
+               vout->pp_down_stripe.output_column = 0;
+               if (vout->pp_split != 3) {
+                       ipu_calc_stripes_sizes(
+                               params->mem_pp_mem.in_width, /* input frame width;>1 */
+                               params->mem_pp_mem.out_width, /* output frame width; >1 */
+                               ipu_ic_out_max_width_size,
+                               (((unsigned long long)1) << 32), /* 32bit for fractional*/
+                               1,      /* equal stripes */
+                               params->mem_pp_mem.in_pixel_fmt,
+                               params->mem_pp_mem.out_pixel_fmt,
+                               &(vout->pp_left_stripe),
+                               &(vout->pp_right_stripe));
+
+                       vout->pp_left_stripe.input_column = vout->pp_left_stripe.input_column *
+                                                               fmt_to_bpp(vout->v2f.fmt.pix.pixelformat) / 8;
+                       vout->pp_left_stripe.output_column = vout->pp_left_stripe.output_column *
+                                                               fmt_to_bpp(params->mem_pp_mem.out_pixel_fmt) / 8;
+                       vout->pp_right_stripe.input_column = vout->pp_right_stripe.input_column *
+                                                               fmt_to_bpp(vout->v2f.fmt.pix.pixelformat) / 8;
+                       vout->pp_right_stripe.output_column = vout->pp_right_stripe.output_column *
+                                                               fmt_to_bpp(params->mem_pp_mem.out_pixel_fmt) / 8;
+
+
+               /* updare parameters */
+               params->mem_pp_mem.in_width = vout->pp_left_stripe.input_width;
+               params->mem_pp_mem.out_width = vout->pp_left_stripe.output_width;
+               out_width = vout->pp_left_stripe.output_width;
+               /* for using in ic_init*/
+               params->mem_pp_mem.outh_resize_ratio = vout->pp_left_stripe.irr;
+               }
+               if (vout->pp_split != 2) {
+                       ipu_calc_stripes_sizes(
+                               params->mem_pp_mem.in_height, /* input frame width;>1 */
+                               params->mem_pp_mem.out_height, /* output frame width; >1 */
+                               ipu_ic_out_max_height_size,
+                               (((unsigned long long)1) << 32),/* 32bit for fractional */
+                               1,      /* equal stripes */
+                               params->mem_pp_mem.in_pixel_fmt,
+                               params->mem_pp_mem.out_pixel_fmt,
+                               &(vout->pp_up_stripe),
+                               &(vout->pp_down_stripe));
+                       vout->pp_down_stripe.output_column = vout->pp_down_stripe.output_column * out_stride;
+                       vout->pp_up_stripe.output_column = vout->pp_up_stripe.output_column * out_stride;
+                       params->mem_pp_mem.outv_resize_ratio = vout->pp_up_stripe.irr;
+                       params->mem_pp_mem.in_height = vout->pp_up_stripe.input_width;/*height*/
+                       out_height = vout->pp_up_stripe.output_width;/*height*/
+                       if (vout->pp_split == 3)
+                               vout->pp_split = 2;/*2 vertical stripe as two horizontal stripes */
+               }
+               vout->pp_split_buf_num = 0;
+       }
+
+       if (ipu_init_channel(vout->post_proc_ch, params) != 0) {
+               dev_err(dev, "Error initializing PP channel\n");
+               return -EINVAL;
+       }
+
+       /* always enable double buffer */
+       phy_addr0 = vout->v4l2_bufs[vout->ipu_buf[0]].m.offset;
+       if (vout->ipu_buf[1] == -1)
+               phy_addr1 = phy_addr0;
+       else
+               phy_addr1 = vout->v4l2_bufs[vout->ipu_buf[1]].m.offset;
+       if (ipu_init_channel_buffer(vout->post_proc_ch,
+                                   IPU_INPUT_BUFFER,
+                                   params->mem_pp_mem.in_pixel_fmt,
+                                   params->mem_pp_mem.in_width,
+                                   params->mem_pp_mem.in_height,
+                                   vout->v2f.fmt.pix.bytesperline /
+                                   bytes_per_pixel(params->mem_pp_mem.
+                                           in_pixel_fmt),
+                                   IPU_ROTATE_NONE,
+                                   phy_addr0,
+                                   phy_addr1,
+                                   vout->offset.u_offset,
+                                   vout->offset.v_offset) != 0) {
+               dev_err(dev, "Error initializing PP input buffer\n");
+               return -EINVAL;
+       }
+
+       if (!ipu_can_rotate_in_place(vout->rotate)) {
+               if (vout->rot_pp_bufs[0]) {
+                       mxc_free_buffers(vout->rot_pp_bufs,
+                                        vout->rot_pp_bufs_vaddr, 2,
+                                        vout->display_buf_size);
+               }
+               if (mxc_allocate_buffers
+                   (vout->rot_pp_bufs, vout->rot_pp_bufs_vaddr, 2,
+                    vout->display_buf_size) < 0) {
+                       return -ENOBUFS;
+               }
+
+               if (ipu_init_channel_buffer(vout->post_proc_ch,
+                                           IPU_OUTPUT_BUFFER,
+                                           params->mem_pp_mem.
+                                           out_pixel_fmt, out_width,
+                                           out_height, out_stride,
+                                           IPU_ROTATE_NONE,
+                                           vout->rot_pp_bufs[0] + eba_offset,
+                                           vout->rot_pp_bufs[1] + eba_offset, 0, 0) != 0) {
+                       dev_err(dev, "Error initializing PP output buffer\n");
+                       return -EINVAL;
+               }
+
+               if (ipu_init_channel(MEM_ROT_PP_MEM, NULL) != 0) {
+                       dev_err(dev, "Error initializing PP ROT channel\n");
+                       return -EINVAL;
+               }
+               if (ipu_init_channel_buffer(MEM_ROT_PP_MEM,
+                                           IPU_INPUT_BUFFER,
+                                           params->mem_pp_mem.
+                                           out_pixel_fmt, out_width,
+                                           out_height, out_stride,
+                                           vout->rotate,
+                                           vout->rot_pp_bufs[0],
+                                           vout->rot_pp_bufs[1], 0, 0) != 0) {
+                       dev_err(dev,
+                               "Error initializing PP ROT input buffer\n");
+                       return -EINVAL;
+               }
+
+               /* swap width and height */
+               if (vout->rotate >= IPU_ROTATE_90_RIGHT) {
+                       out_width = vout->crop_current.width;
+                       out_height = vout->crop_current.height;
+               }
+
+               if (ipu_init_channel_buffer(MEM_ROT_PP_MEM,
+                                           IPU_OUTPUT_BUFFER,
+                                           params->mem_pp_mem.
+                                           out_pixel_fmt, out_width,
+                                           out_height, out_stride,
+                                           IPU_ROTATE_NONE,
+                                           vout->display_bufs[0] + eba_offset,
+                                           vout->display_bufs[1] + eba_offset, 0, 0) != 0) {
+                       dev_err(dev, "Error initializing PP output buffer\n");
+                       return -EINVAL;
+               }
+
+               if (ipu_link_channels(vout->post_proc_ch, MEM_ROT_PP_MEM) < 0)
+                       return -EINVAL;
+
+               vout->display_input_ch = MEM_ROT_PP_MEM;
+               ipu_enable_channel(MEM_ROT_PP_MEM);
+               ipu_select_buffer(MEM_ROT_PP_MEM, IPU_OUTPUT_BUFFER, 0);
+               ipu_select_buffer(MEM_ROT_PP_MEM, IPU_OUTPUT_BUFFER, 1);
+       } else {
+               if (ipu_init_channel_buffer(vout->post_proc_ch,
+                                           IPU_OUTPUT_BUFFER,
+                                           params->mem_pp_mem.
+                                           out_pixel_fmt, out_width,
+                                           out_height, out_stride,
+                                           vout->rotate,
+                                           vout->display_bufs[0] + eba_offset,
+                                           vout->display_bufs[1] + eba_offset, 0, 0) != 0) {
+                       dev_err(dev, "Error initializing PP output buffer\n");
+                       return -EINVAL;
+               }
+       }
+
+       /* fix EBAs for IDMAC channels */
+       if (vout->pp_split) {
+               ipu_update_channel_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER,
+                                                                       0,
+                                                                       vout->v4l2_bufs[vout->ipu_buf[0]].m.offset +
+                                                                       vout->pp_left_stripe.input_column +
+                                                                       vout->pp_up_stripe.input_column * vout->bytesperline);
+
+
+               ipu_update_channel_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER,
+                                                                       1,
+                                                                       vout->v4l2_bufs[vout->ipu_buf[0]].m.offset +
+                                                                       vout->pp_right_stripe.input_column +
+                                                                       vout->pp_up_stripe.input_column * vout->bytesperline);
+
+               ipu_update_channel_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER,
+                                                                       0,
+                                                                       vout->display_bufs[0] + eba_offset +
+                                                                       vout->pp_left_stripe.output_column +
+                                                                       vout->pp_up_stripe.output_column);
+
+               ipu_update_channel_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER,
+                                                                       1,
+                                                                       vout->display_bufs[0] + eba_offset +
+                                                                       vout->pp_right_stripe.output_column +
+                                                                       vout->pp_up_stripe.output_column);
+       }
+
+       return 0;
+}
+
+/*!
+ * Start the output stream
+ *
+ * @param vout      structure vout_data *
+ *
+ * @return status  0 Success
+ */
+static int mxc_v4l2out_streamon(vout_data *vout)
+{
+       struct device *dev = &vout->video_dev->dev;
+       ipu_channel_params_t params;
+       struct mxcfb_pos fb_pos;
+       struct fb_var_screeninfo fbvar;
+       struct fb_info *fbi =
+           registered_fb[vout->output_fb_num[vout->cur_disp_output]];
+       u16 out_width;
+       u16 out_height;
+       mm_segment_t old_fs;
+       unsigned int ipu_ch = CHAN_NONE;
+       unsigned int fb_fmt;
+       int rc = 0;
+
+       dev_dbg(dev, "mxc_v4l2out_streamon: field format=%d\n",
+               vout->field_fmt);
+
+       if (!vout)
+               return -EINVAL;
+
+       if (vout->state != STATE_STREAM_OFF)
+               return -EBUSY;
+
+       if (queue_size(&vout->ready_q) < 2) {
+               dev_err(dev, "2 buffers not been queued yet!\n");
+               return -EINVAL;
+       }
+
+       if ((vout->field_fmt == V4L2_FIELD_BOTTOM) || (vout->field_fmt == V4L2_FIELD_TOP)) {
+               dev_err(dev, "4 queued buffers need, not supported yet!\n");
+               return -EINVAL;
+       }
+
+       /*
+        * params init, check whether operation exceed the IC limitation:
+        * whether split mode used ( ipu version >= ipuv3 only)
+        */
+       g_irq_cnt = g_buf_output_cnt = g_buf_q_cnt = g_buf_dq_cnt = 0;
+       out_width = vout->crop_current.width;
+       out_height = vout->crop_current.height;
+       vout->disp_buf_num = 0;
+       vout->next_done_ipu_buf = 0;
+       vout->next_rdy_ipu_buf = vout->next_disp_ipu_buf = 1;
+       vout->pp_split = 0;
+       ipu_ic_out_max_height_size = 1024;
+#ifdef CONFIG_MXC_IPU_V1
+       if (cpu_is_mx35())
+               ipu_ic_out_max_width_size = 800;
+       else
+               ipu_ic_out_max_width_size = 720;
+#else
+       ipu_ic_out_max_width_size = 1024;
+#endif
+       if ((out_width > ipu_ic_out_max_width_size) ||
+               (out_height > ipu_ic_out_max_height_size))
+               vout->pp_split = 4;
+       if (!INTERLACED_CONTENT(vout)) {
+               vout->ipu_buf[0] = dequeue_buf(&vout->ready_q);
+               /* split IC by two stripes, the by pass is impossible*/
+               if ((out_width != vout->v2f.fmt.pix.width ||
+                       out_height != vout->v2f.fmt.pix.height) &&
+                       vout->pp_split) {
+                       vout->next_done_ipu_buf = vout->next_rdy_ipu_buf = 0;
+                       vout->ipu_buf[1] = vout->ipu_buf[0];
+                       vout->frame_count = 1;
+                       if ((out_width > ipu_ic_out_max_width_size) &&
+                               (out_height > ipu_ic_out_max_height_size))
+                               vout->pp_split = 1; /*4 stripes*/
+                       else if (!(out_height > ipu_ic_out_max_height_size))
+                               vout->pp_split = 2; /*two horizontal stripes */
+                       else
+                               vout->pp_split = 3; /*2 vertical stripes*/
+               } else {
+                       vout->ipu_buf[1] = -1;
+                       vout->frame_count = 1;
+               }
+       } else if (!LOAD_3FIELDS(vout)) {
+               vout->ipu_buf[0] = dequeue_buf(&vout->ready_q);
+               vout->ipu_buf[1] = -1;
+               vout->frame_count = 1;
+       } else {
+               vout->ipu_buf_p[0] = dequeue_buf(&vout->ready_q);
+               vout->ipu_buf[0] = dequeue_buf(&vout->ready_q);
+               vout->ipu_buf_n[0] = vout->ipu_buf[0];
+               vout->ipu_buf_p[1] = -1;
+               vout->ipu_buf[1] = -1;
+               vout->ipu_buf_n[1] = -1;
+               last_index_n = vout->ipu_buf_n[0];
+               vout->frame_count = 2;
+       }
+
+       /*
+        * Bypass IC if resizing and rotation are not needed
+        * Meanwhile, apply IC bypass to SDC only
+        */
+       fbvar = fbi->var;
+       vout->xres = fbvar.xres;
+       vout->yres = fbvar.yres;
+
+       if (vout->cur_disp_output == 3 || vout->cur_disp_output == 5) {
+               fb_fmt = vout->v2f.fmt.pix.pixelformat;
+
+               /* DC channel can not use CSC */
+               if (vout->cur_disp_output == 5) {
+                       if (fbi->fbops->fb_ioctl) {
+                               old_fs = get_fs();
+                               set_fs(KERNEL_DS);
+                               fbi->fbops->fb_ioctl(fbi, MXCFB_GET_DIFMT,
+                                               (unsigned long)&fb_fmt);
+                               set_fs(old_fs);
+                       }
+               }
+
+               fbvar.bits_per_pixel = 16;
+               if (format_is_yuv(fb_fmt))
+                       fbvar.nonstd = IPU_PIX_FMT_UYVY;
+               else
+                       fbvar.nonstd = 0;
+               if (vout->cur_disp_output == 3) {
+                       fbvar.xres = out_width;
+                       fbvar.yres = out_height;
+                       vout->xres = fbvar.xres;
+                       vout->yres = fbvar.yres;
+               }
+
+               fbvar.xres_virtual = fbvar.xres;
+               fbvar.yres_virtual = fbvar.yres * 2;
+       }
+
+       if (out_width == vout->v2f.fmt.pix.width &&
+           out_height == vout->v2f.fmt.pix.height &&
+           vout->xres == out_width &&
+           vout->yres == out_height &&
+           ipu_can_rotate_in_place(vout->rotate) &&
+           (vout->bytesperline ==
+            bytes_per_pixel(vout->v2f.fmt.pix.pixelformat) * out_width) &&
+           !INTERLACED_CONTENT(vout)) {
+               vout->ic_bypass = 1;
+       } else {
+               vout->ic_bypass = 0;
+       }
+
+#ifdef CONFIG_MXC_IPU_V1
+       /* IPUv1 needs IC to do CSC */
+       if (format_is_yuv(vout->v2f.fmt.pix.pixelformat) !=
+           format_is_yuv(bpp_to_fmt(fbi)))
+#else
+       /* DC channel needs IC to do CSC */
+       if ((format_is_yuv(vout->v2f.fmt.pix.pixelformat) !=
+           format_is_yuv(fb_fmt)) &&
+               (vout->cur_disp_output == 5))
+               vout->ic_bypass = 0;
+#endif
+
+       if (fbi->fbops->fb_ioctl) {
+               old_fs = get_fs();
+               set_fs(KERNEL_DS);
+               fbi->fbops->fb_ioctl(fbi, MXCFB_GET_FB_IPU_CHAN,
+                               (unsigned long)&ipu_ch);
+               set_fs(old_fs);
+       }
+
+       if (ipu_ch == CHAN_NONE) {
+               dev_err(dev, "Can not get display ipu channel\n");
+               return -EINVAL;
+       }
+
+       vout->display_ch = ipu_ch;
+
+       if (vout->ic_bypass) {
+               dev_info(dev, "Bypassing IC\n");
+               vout->pp_split = 0;
+               switch (vout->v2f.fmt.pix.pixelformat) {
+               case V4L2_PIX_FMT_YUV420:
+               case V4L2_PIX_FMT_YVU420:
+               case V4L2_PIX_FMT_NV12:
+                       fbvar.bits_per_pixel = 12;
+                       break;
+               case V4L2_PIX_FMT_YUV422P:
+                       fbvar.bits_per_pixel = 16;
+                       break;
+               default:
+                       fbvar.bits_per_pixel = 8*
+                       bytes_per_pixel(vout->v2f.fmt.pix.pixelformat);
+               }
+               fbvar.nonstd = vout->v2f.fmt.pix.pixelformat;
+       }
+
+       /* Init display channel through fb API */
+       fbvar.yoffset = 0;
+       fbvar.activate |= FB_ACTIVATE_FORCE;
+       console_lock();
+       fbi->flags |= FBINFO_MISC_USEREVENT;
+       fb_set_var(fbi, &fbvar);
+       fbi->flags &= ~FBINFO_MISC_USEREVENT;
+       console_unlock();
+
+       if (fbi->fbops->fb_ioctl && vout->display_ch == MEM_FG_SYNC) {
+               fb_pos.x = vout->crop_current.left;
+               fb_pos.y = vout->crop_current.top;
+               old_fs = get_fs();
+               set_fs(KERNEL_DS);
+               fbi->fbops->fb_ioctl(fbi, MXCFB_SET_OVERLAY_POS,
+                               (unsigned long)&fb_pos);
+               set_fs(old_fs);
+       }
+
+       vout->display_bufs[1] = fbi->fix.smem_start;
+       vout->display_bufs[0] = fbi->fix.smem_start +
+               (fbi->fix.line_length * vout->yres);
+       vout->display_buf_size = vout->xres *
+               vout->yres * fbi->var.bits_per_pixel / 8;
+
+       /* fill black color for init fb, we assume fb has double buffer*/
+       if (format_is_yuv(vout->v2f.fmt.pix.pixelformat)) {
+               int i;
+
+               if ((vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY) ||
+                       (vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) ||
+                       (!vout->ic_bypass)) {
+                       short * tmp = (short *) fbi->screen_base;
+                       short color;
+                       if (vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
+                               color = 0x8000;
+                       else
+                               color = 0x80;
+                       for (i = 0; i < (fbi->fix.line_length * fbi->var.yres_virtual)/2;
+                                       i++, tmp++)
+                               *tmp = color;
+               } else if ((vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) ||
+                               (vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YVU420) ||
+                               (vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_NV12)) {
+                       char * base = (char *)fbi->screen_base;
+                       int j, screen_size = fbi->var.xres * fbi->var.yres;
+
+                       for (j = 0; j < 2; j++) {
+                               memset(base, 0, screen_size);
+                               base += screen_size;
+                               for (i = 0; i < screen_size/2; i++, base++)
+                                       *base = 0x80;
+                       }
+               } else if (vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P) {
+                       char * base = (char *)fbi->screen_base;
+                       int j, screen_size = fbi->var.xres * fbi->var.yres;
+
+                       for (j = 0; j < 2; j++) {
+                               memset(base, 0, screen_size);
+                               base += screen_size;
+                               for (i = 0; i < screen_size; i++, base++)
+                                       *base = 0x80;
+                       }
+               }
+       } else
+               memset(fbi->screen_base, 0x0,
+                               fbi->fix.line_length * fbi->var.yres_virtual);
+
+       if (INTERLACED_CONTENT(vout))
+               vout->post_proc_ch = MEM_VDI_PRP_VF_MEM;
+       else if (!vout->ic_bypass)
+               vout->post_proc_ch = MEM_PP_MEM;
+
+       /* Init IC channel */
+       if (!vout->ic_bypass) {
+               if (vout->rotate >= IPU_ROTATE_90_RIGHT) {
+                       out_width = vout->crop_current.height;
+                       out_height = vout->crop_current.width;
+               }
+               vout->display_input_ch = vout->post_proc_ch;
+               memset(&params, 0, sizeof(params));
+               if (INTERLACED_CONTENT(vout)) {
+                       if (vout->pp_split) {
+                               dev_err(&vout->video_dev->dev, "VDI split has not supported yet.\n");
+                               return -1;
+                       } else
+                               rc = init_VDI(params, vout, dev, fbi, out_width, out_height);
+               } else {
+                       rc = init_PP(&params, vout, dev, fbi, out_width, out_height);
+               }
+               if (rc < 0)
+                       return rc;
+       }
+
+       if (!vout->ic_bypass) {
+               switch (vout->display_input_ch) {
+               case MEM_PP_MEM:
+                       vout->work_irq = IPU_IRQ_PP_OUT_EOF;
+                       break;
+               case MEM_VDI_PRP_VF_MEM:
+                       vout->work_irq = IPU_IRQ_PRP_VF_OUT_EOF;
+                       break;
+               case MEM_ROT_VF_MEM:
+                       vout->work_irq = IPU_IRQ_PRP_VF_ROT_OUT_EOF;
+                       break;
+               case MEM_ROT_PP_MEM:
+                       vout->work_irq = IPU_IRQ_PP_ROT_OUT_EOF;
+                       break;
+               default:
+                       dev_err(&vout->video_dev->dev,
+                               "not support channel, should not be here\n");
+               }
+       } else
+               vout->work_irq = -1;
+
+       if (!vout->ic_bypass && (vout->work_irq > 0)) {
+               ipu_clear_irq(vout->work_irq);
+               ipu_request_irq(vout->work_irq,
+                               mxc_v4l2out_work_irq_handler,
+                               0, vout->video_dev->name, vout);
+       }
+
+       vout->state = STATE_STREAM_PAUSED;
+
+       /* Enable display and IC channels */
+       if (fbi) {
+               console_lock();
+               fb_blank(fbi, FB_BLANK_UNBLANK);
+               console_unlock();
+               vout->fb_blank = FB_BLANK_UNBLANK;
+       } else {
+               ipu_enable_channel(vout->display_ch);
+       }
+
+       /* correct display ch buffer address */
+       ipu_update_channel_buffer(vout->display_ch, IPU_INPUT_BUFFER,
+                                       0, vout->display_bufs[0]);
+       ipu_update_channel_buffer(vout->display_ch, IPU_INPUT_BUFFER,
+                                       1, vout->display_bufs[1]);
+
+       if (!vout->ic_bypass) {
+#ifndef CONFIG_MXC_IPU_V1
+               ipu_enable_channel(vout->post_proc_ch);
+#endif
+               if (LOAD_3FIELDS(vout)) {
+                       ipu_enable_channel(MEM_VDI_PRP_VF_MEM_P);
+                       ipu_enable_channel(MEM_VDI_PRP_VF_MEM_N);
+                       ipu_select_multi_vdi_buffer(0);
+               } else
+                       ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 0);
+               ipu_select_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, 0);
+               ipu_select_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, 1);
+#ifdef CONFIG_MXC_IPU_V1
+               ipu_enable_channel(vout->post_proc_ch);
+#endif
+       } else {
+               ipu_update_channel_buffer(vout->display_ch,
+                               IPU_INPUT_BUFFER,
+                               0, vout->v4l2_bufs[vout->ipu_buf[0]].m.offset);
+               if (vout->offset.u_offset || vout->offset.v_offset)
+                       /* only update u/v offset */
+                       ipu_update_channel_offset(vout->display_ch,
+                                       IPU_INPUT_BUFFER,
+                                       vout->v2f.fmt.pix.pixelformat,
+                                       vout->v2f.fmt.pix.width,
+                                       vout->v2f.fmt.pix.height,
+                                       vout->bytesperline,
+                                       vout->offset.u_offset,
+                                       vout->offset.v_offset,
+                                       0,
+                                       0);
+               ipu_select_buffer(vout->display_ch, IPU_INPUT_BUFFER, 0);
+               queue_work(vout->v4l_wq, &vout->icbypass_work);
+       }
+
+       vout->start_jiffies = jiffies;
+
+       msleep(1);
+
+       dev_dbg(dev,
+               "streamon: start time = %lu jiffies\n", vout->start_jiffies);
+
+       return 0;
+}
+
+/*!
+ * Shut down the voutera
+ *
+ * @param vout      structure vout_data *
+ *
+ * @return status  0 Success
+ */
+static int mxc_v4l2out_streamoff(vout_data *vout)
+{
+       struct fb_info *fbi =
+           registered_fb[vout->output_fb_num[vout->cur_disp_output]];
+       int i, retval = 0;
+       unsigned long lockflag = 0;
+
+       if (!vout)
+               return -EINVAL;
+
+       if (vout->state == STATE_STREAM_OFF) {
+               return 0;
+       }
+
+       if (!vout->ic_bypass)
+               ipu_free_irq(vout->work_irq, vout);
+
+       if (vout->ic_bypass)
+               cancel_work_sync(&vout->icbypass_work);
+
+       /* fill black color for fb, we assume fb has double buffer*/
+       if (format_is_yuv(vout->v2f.fmt.pix.pixelformat)) {
+               int i;
+
+               if ((vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY) ||
+                       (vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) ||
+                       (!vout->ic_bypass)) {
+                       short * tmp = (short *) fbi->screen_base;
+                       short color;
+                       if (vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
+                               color = 0x8000;
+                       else
+                               color = 0x80;
+                       for (i = 0; i < (fbi->fix.line_length * fbi->var.yres_virtual)/2;
+                                       i++, tmp++)
+                               *tmp = color;
+               } else if ((vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) ||
+                               (vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YVU420) ||
+                               (vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_NV12)) {
+                       char * base = (char *)fbi->screen_base;
+                       int j, screen_size = fbi->var.xres * fbi->var.yres;
+
+                       for (j = 0; j < 2; j++) {
+                               memset(base, 0, screen_size);
+                               base += screen_size;
+                               for (i = 0; i < screen_size/2; i++, base++)
+                                       *base = 0x80;
+                       }
+               } else if (vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P) {
+                       char * base = (char *)fbi->screen_base;
+                       int j, screen_size = fbi->var.xres * fbi->var.yres;
+
+                       for (j = 0; j < 2; j++) {
+                               memset(base, 0, screen_size);
+                               base += screen_size;
+                               for (i = 0; i < screen_size; i++, base++)
+                                       *base = 0x80;
+                       }
+               }
+       } else
+               memset(fbi->screen_base, 0x0,
+                               fbi->fix.line_length * fbi->var.yres_virtual);
+
+       spin_lock_irqsave(&g_lock, lockflag);
+
+       del_timer(&vout->output_timer);
+
+       if (vout->state == STATE_STREAM_ON) {
+               vout->state = STATE_STREAM_STOPPING;
+       }
+
+       spin_unlock_irqrestore(&g_lock, lockflag);
+
+       if (vout->display_ch == MEM_FG_SYNC) {
+               struct mxcfb_pos fb_pos;
+               mm_segment_t old_fs;
+
+               fb_pos.x = 0;
+               fb_pos.y = 0;
+               if (fbi->fbops->fb_ioctl) {
+                       old_fs = get_fs();
+                       set_fs(KERNEL_DS);
+                       fbi->fbops->fb_ioctl(fbi, MXCFB_SET_OVERLAY_POS,
+                                       (unsigned long)&fb_pos);
+                       set_fs(old_fs);
+               }
+       }
+
+       if (vout->ic_bypass) {
+               fbi->var.activate |= FB_ACTIVATE_FORCE;
+               console_lock();
+               fbi->flags |= FBINFO_MISC_USEREVENT;
+               fb_set_var(fbi, &fbi->var);
+               fbi->flags &= ~FBINFO_MISC_USEREVENT;
+               console_unlock();
+
+               if (vout->display_ch == MEM_FG_SYNC) {
+                       console_lock();
+                       fb_blank(fbi, FB_BLANK_POWERDOWN);
+                       console_unlock();
+               }
+
+               vout->display_bufs[0] = 0;
+               vout->display_bufs[1] = 0;
+       } else if (vout->post_proc_ch == MEM_PP_MEM ||
+           vout->post_proc_ch == MEM_PRP_VF_MEM) {
+               /* SDC with Rotation */
+               if (!ipu_can_rotate_in_place(vout->rotate)) {
+                       ipu_unlink_channels(MEM_PP_MEM, MEM_ROT_PP_MEM);
+                       ipu_disable_channel(MEM_ROT_PP_MEM, true);
+
+                       if (vout->rot_pp_bufs[0]) {
+                               mxc_free_buffers(vout->rot_pp_bufs,
+                                                vout->rot_pp_bufs_vaddr, 2,
+                                                vout->display_buf_size);
+                       }
+               }
+               ipu_disable_channel(MEM_PP_MEM, true);
+
+               fbi->var.activate |= FB_ACTIVATE_FORCE;
+               console_lock();
+               fbi->flags |= FBINFO_MISC_USEREVENT;
+               fb_set_var(fbi, &fbi->var);
+               fbi->flags &= ~FBINFO_MISC_USEREVENT;
+               console_unlock();
+
+               if (vout->display_ch == MEM_FG_SYNC) {
+                       console_lock();
+                       fb_blank(fbi, FB_BLANK_POWERDOWN);
+                       console_unlock();
+               }
+
+               vout->display_bufs[0] = 0;
+               vout->display_bufs[1] = 0;
+
+               ipu_uninit_channel(MEM_PP_MEM);
+               if (!ipu_can_rotate_in_place(vout->rotate))
+                       ipu_uninit_channel(MEM_ROT_PP_MEM);
+       } else if (INTERLACED_CONTENT(vout) &&
+               (vout->post_proc_ch == MEM_VDI_PRP_VF_MEM)) {
+               if (!ipu_can_rotate_in_place(vout->rotate)) {
+                       ipu_unlink_channels(MEM_VDI_PRP_VF_MEM,
+                                           MEM_ROT_VF_MEM);
+                       ipu_disable_channel(MEM_ROT_VF_MEM, true);
+
+                       if (vout->rot_pp_bufs[0]) {
+                               mxc_free_buffers(vout->rot_pp_bufs,
+                                                vout->rot_pp_bufs_vaddr, 2,
+                                                vout->display_buf_size);
+                       }
+               }
+
+               ipu_disable_channel(MEM_VDI_PRP_VF_MEM, true);
+               if (LOAD_3FIELDS(vout)) {
+                       ipu_disable_channel(MEM_VDI_PRP_VF_MEM_P, true);
+                       ipu_disable_channel(MEM_VDI_PRP_VF_MEM_N, true);
+               }
+
+               fbi->var.activate |= FB_ACTIVATE_FORCE;
+               console_lock();
+               fbi->flags |= FBINFO_MISC_USEREVENT;
+               fb_set_var(fbi, &fbi->var);
+               fbi->flags &= ~FBINFO_MISC_USEREVENT;
+               console_unlock();
+
+               if (vout->display_ch == MEM_FG_SYNC) {
+                       console_lock();
+                       fb_blank(fbi, FB_BLANK_POWERDOWN);
+                       console_unlock();
+               }
+
+               vout->display_bufs[0] = 0;
+               vout->display_bufs[1] = 0;
+
+               ipu_uninit_channel(MEM_VDI_PRP_VF_MEM);
+               if (LOAD_3FIELDS(vout)) {
+                       ipu_uninit_channel(MEM_VDI_PRP_VF_MEM_P);
+                       ipu_uninit_channel(MEM_VDI_PRP_VF_MEM_N);
+               }
+               if (!ipu_can_rotate_in_place(vout->rotate))
+                       ipu_uninit_channel(MEM_ROT_VF_MEM);
+       }
+
+       vout->ready_q.head = vout->ready_q.tail = 0;
+       vout->done_q.head = vout->done_q.tail = 0;
+       for (i = 0; i < vout->buffer_cnt; i++) {
+               vout->v4l2_bufs[i].flags = 0;
+               vout->v4l2_bufs[i].timestamp.tv_sec = 0;
+               vout->v4l2_bufs[i].timestamp.tv_usec = 0;
+       }
+
+       vout->post_proc_ch = CHAN_NONE;
+       vout->state = STATE_STREAM_OFF;
+
+       return retval;
+}
+
+/*
+ * Valid whether the palette is supported
+ *
+ * @param palette  V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_BGR24 or V4L2_PIX_FMT_BGR32
+ *
+ * @return 1 if supported, 0 if failed
+ */
+static inline int valid_mode(u32 palette)
+{
+       return ((palette == V4L2_PIX_FMT_RGB565) ||
+               (palette == V4L2_PIX_FMT_BGR24) ||
+               (palette == V4L2_PIX_FMT_RGB24) ||
+               (palette == V4L2_PIX_FMT_BGR32) ||
+               (palette == V4L2_PIX_FMT_RGB32) ||
+               (palette == V4L2_PIX_FMT_NV12) ||
+               (palette == V4L2_PIX_FMT_UYVY) ||
+               (palette == V4L2_PIX_FMT_YUYV) ||
+               (palette == V4L2_PIX_FMT_YUV422P) ||
+               (palette == V4L2_PIX_FMT_YUV444) ||
+               (palette == V4L2_PIX_FMT_YUV420));
+}
+
+/*
+ * V4L2 - Handles VIDIOC_G_FMT Ioctl
+ *
+ * @param vout         structure vout_data *
+ *
+ * @param v4l2_format structure v4l2_format *
+ *
+ * @return  status    0 success, EINVAL failed
+ */
+static int mxc_v4l2out_g_fmt(vout_data *vout, struct v4l2_format *f)
+{
+       if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+               return -EINVAL;
+       }
+       *f = vout->v2f;
+       return 0;
+}
+
+/*
+ * V4L2 - Handles VIDIOC_S_FMT Ioctl
+ *
+ * @param vout         structure vout_data *
+ *
+ * @param v4l2_format structure v4l2_format *
+ *
+ * @return  status    0 success, EINVAL failed
+ */
+static int mxc_v4l2out_s_fmt(vout_data *vout, struct v4l2_format *f)
+{
+       int retval = 0;
+       u32 size = 0;
+       u32 bytesperline;
+
+       if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+               retval = -EINVAL;
+               goto err0;
+       }
+       if (!valid_mode(f->fmt.pix.pixelformat)) {
+               dev_err(&vout->video_dev->dev, "pixel format not supported\n");
+               retval = -EINVAL;
+               goto err0;
+       }
+
+       bytesperline = (f->fmt.pix.width * fmt_to_bpp(f->fmt.pix.pixelformat)) /
+           8;
+       if (f->fmt.pix.bytesperline < bytesperline) {
+               f->fmt.pix.bytesperline = bytesperline;
+       } else {
+               bytesperline = f->fmt.pix.bytesperline;
+       }
+       vout->bytesperline = bytesperline;
+
+       /* Based on http://v4l2spec.bytesex.org/spec/x6386.htm#V4L2-FIELD */
+       vout->field_fmt = f->fmt.pix.field;
+       switch (vout->field_fmt) {
+               /* Images are in progressive format, not interlaced */
+       case V4L2_FIELD_NONE:
+               break;
+               /* The two fields of a frame are passed in separate buffers,
+                  in temporal order, i. e. the older one first. */
+       case V4L2_FIELD_ALTERNATE:
+               dev_err(&vout->video_dev->dev,
+                       "V4L2_FIELD_ALTERNATE field format not supported yet!\n");
+               break;
+       case V4L2_FIELD_INTERLACED_TB:
+       case V4L2_FIELD_INTERLACED_BT:
+               if (cpu_is_mx51() || cpu_is_mx53())
+                       break;
+               dev_err(&vout->video_dev->dev,
+                       "De-interlacing not supported in this device!\n");
+               vout->field_fmt = V4L2_FIELD_NONE;
+               break;
+       default:
+               vout->field_fmt = V4L2_FIELD_NONE;
+               break;
+       }
+
+       switch (f->fmt.pix.pixelformat) {
+       case V4L2_PIX_FMT_YUV422P:
+               /* byteperline for YUV planar formats is for
+                  Y plane only */
+               size = bytesperline * f->fmt.pix.height * 2;
+               break;
+       case V4L2_PIX_FMT_YUV420:
+       case V4L2_PIX_FMT_NV12:
+               size = (bytesperline * f->fmt.pix.height * 3) / 2;
+               break;
+       default:
+               size = bytesperline * f->fmt.pix.height;
+               break;
+       }
+
+       /* Return the actual size of the image to the app */
+       if (f->fmt.pix.sizeimage < size) {
+               f->fmt.pix.sizeimage = size;
+       } else {
+               size = f->fmt.pix.sizeimage;
+       }
+
+       vout->v2f.fmt.pix = f->fmt.pix;
+       if (vout->v2f.fmt.pix.priv != 0) {
+               if (copy_from_user(&vout->offset,
+                                  (void *)vout->v2f.fmt.pix.priv,
+                                  sizeof(vout->offset))) {
+                       retval = -EFAULT;
+                       goto err0;
+               }
+       } else {
+               vout->offset.u_offset = 0;
+               vout->offset.v_offset = 0;
+       }
+
+       retval = 0;
+      err0:
+       return retval;
+}
+
+/*
+ * V4L2 - Handles VIDIOC_G_CTRL Ioctl
+ *
+ * @param vout         structure vout_data *
+ *
+ * @param c           structure v4l2_control *
+ *
+ * @return  status    0 success, EINVAL failed
+ */
+static int mxc_get_v42lout_control(vout_data *vout, struct v4l2_control *c)
+{
+       switch (c->id) {
+       case V4L2_CID_HFLIP:
+               return (vout->rotate & IPU_ROTATE_HORIZ_FLIP) ? 1 : 0;
+       case V4L2_CID_VFLIP:
+               return (vout->rotate & IPU_ROTATE_VERT_FLIP) ? 1 : 0;
+       case (V4L2_CID_PRIVATE_BASE + 1):
+               return vout->rotate;
+       default:
+               return -EINVAL;
+       }
+}
+
+/*
+ * V4L2 - Handles VIDIOC_S_CTRL Ioctl
+ *
+ * @param vout         structure vout_data *
+ *
+ * @param c           structure v4l2_control *
+ *
+ * @return  status    0 success, EINVAL failed
+ */
+static int mxc_set_v42lout_control(vout_data *vout, struct v4l2_control *c)
+{
+       switch (c->id) {
+       case V4L2_CID_HFLIP:
+               vout->rotate |= c->value ? IPU_ROTATE_HORIZ_FLIP :
+                   IPU_ROTATE_NONE;
+               break;
+       case V4L2_CID_VFLIP:
+               vout->rotate |= c->value ? IPU_ROTATE_VERT_FLIP :
+                   IPU_ROTATE_NONE;
+               break;
+       case V4L2_CID_MXC_ROT:
+               vout->rotate = c->value;
+               break;
+       case V4L2_CID_MXC_MOTION:
+               vout->motion_sel = c->value;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+/*!
+ * V4L2 interface - open function
+ *
+ * @param file         structure file *
+ *
+ * @return  status    0 success, ENODEV invalid device instance,
+ *                    ENODEV timeout, ERESTARTSYS interrupted by user
+ */
+static int mxc_v4l2out_open(struct file *file)
+{
+       struct video_device *dev = video_devdata(file);
+       vout_data *vout = video_get_drvdata(dev);
+       int err;
+
+       if (!vout) {
+               return -ENODEV;
+       }
+
+       down(&vout->busy_lock);
+
+       err = -EINTR;
+       if (signal_pending(current))
+               goto oops;
+
+
+       if (vout->open_count++ == 0) {
+               init_waitqueue_head(&vout->v4l_bufq);
+
+               init_timer(&vout->output_timer);
+               vout->output_timer.function = mxc_v4l2out_timer_handler;
+               vout->output_timer.data = (unsigned long)vout;
+
+               vout->state = STATE_STREAM_OFF;
+               vout->rotate = IPU_ROTATE_NONE;
+
+               vout->v4l_wq = create_singlethread_workqueue("v4l2q");
+               if (!vout->v4l_wq) {
+                       dev_dbg(&dev->dev,
+                                       "Could not create work queue\n");
+                       err = -ENOMEM;
+                       goto oops;
+               }
+
+               INIT_WORK(&vout->icbypass_work, icbypass_work_func);
+       }
+
+       file->private_data = dev;
+
+       up(&vout->busy_lock);
+
+       return 0;
+
+      oops:
+       up(&vout->busy_lock);
+       return err;
+}
+
+/*!
+ * V4L2 interface - close function
+ *
+ * @param file     struct file *
+ *
+ * @return         0 success
+ */
+static int mxc_v4l2out_close(struct file *file)
+{
+       struct video_device *dev = video_devdata(file);
+       vout_data *vout = video_get_drvdata(dev);
+
+       if (--vout->open_count == 0) {
+               if (vout->state != STATE_STREAM_OFF)
+                       mxc_v4l2out_streamoff(vout);
+
+               file->private_data = NULL;
+
+               mxc_free_buffers(vout->queue_buf_paddr, vout->queue_buf_vaddr,
+                                vout->buffer_cnt, vout->queue_buf_size);
+               vout->buffer_cnt = 0;
+               mxc_free_buffers(vout->rot_pp_bufs, vout->rot_pp_bufs_vaddr, 2,
+                                vout->display_buf_size);
+
+               /* capture off */
+               wake_up_interruptible(&vout->v4l_bufq);
+
+               flush_workqueue(vout->v4l_wq);
+               destroy_workqueue(vout->v4l_wq);
+       }
+
+       return 0;
+}
+
+/*!
+ * V4L2 interface - ioctl function
+ *
+ * @param file       struct file *
+ *
+ * @param ioctlnr    unsigned int
+ *
+ * @param arg        void *
+ *
+ * @return           0 success, ENODEV for invalid device instance,
+ *                   -1 for other errors.
+ */
+static long
+mxc_v4l2out_do_ioctl(struct file *file,
+                    unsigned int ioctlnr, void *arg)
+{
+       struct video_device *vdev = file->private_data;
+       vout_data *vout = video_get_drvdata(vdev);
+       int retval = 0;
+       int i = 0;
+
+       if (!vout)
+               return -EBADF;
+
+       /* make this _really_ smp-safe */
+       if (down_interruptible(&vout->busy_lock))
+               return -EBUSY;
+
+       switch (ioctlnr) {
+       case VIDIOC_QUERYCAP:
+               {
+                       struct v4l2_capability *cap = arg;
+                       strcpy(cap->driver, "mxc_v4l2_output");
+                       cap->version = 0;
+                       cap->capabilities =
+                           V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+                       cap->card[0] = '\0';
+                       cap->bus_info[0] = '\0';
+                       retval = 0;
+                       break;
+               }
+       case VIDIOC_G_FMT:
+               {
+                       struct v4l2_format *gf = arg;
+                       retval = mxc_v4l2out_g_fmt(vout, gf);
+                       break;
+               }
+       case VIDIOC_S_FMT:
+               {
+                       struct v4l2_format *sf = arg;
+                       if (vout->state != STATE_STREAM_OFF) {
+                               retval = -EBUSY;
+                               break;
+                       }
+                       retval = mxc_v4l2out_s_fmt(vout, sf);
+                       break;
+               }
+       case VIDIOC_REQBUFS:
+               {
+                       struct v4l2_requestbuffers *req = arg;
+                       if ((req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) ||
+                           (req->memory != V4L2_MEMORY_MMAP)) {
+                               dev_dbg(&vdev->dev,
+                                       "VIDIOC_REQBUFS: incorrect buffer type\n");
+                               retval = -EINVAL;
+                               break;
+                       }
+
+                       if (req->count == 0)
+                               mxc_v4l2out_streamoff(vout);
+
+                       if (vout->state == STATE_STREAM_OFF) {
+                               if (vout->queue_buf_paddr[0] != 0) {
+                                       mxc_free_buffers(vout->queue_buf_paddr,
+                                                        vout->queue_buf_vaddr,
+                                                        vout->buffer_cnt,
+                                                        vout->queue_buf_size);
+                                       dev_dbg(&vdev->dev,
+                                               "VIDIOC_REQBUFS: freed buffers\n");
+                               }
+                               vout->buffer_cnt = 0;
+                       } else {
+                               dev_dbg(&vdev->dev,
+                                       "VIDIOC_REQBUFS: Buffer is in use\n");
+                               retval = -EBUSY;
+                               break;
+                       }
+
+                       if (req->count == 0)
+                               break;
+
+                       if (req->count < MIN_FRAME_NUM) {
+                               req->count = MIN_FRAME_NUM;
+                       } else if (req->count > MAX_FRAME_NUM) {
+                               req->count = MAX_FRAME_NUM;
+                       }
+                       vout->buffer_cnt = req->count;
+                       vout->queue_buf_size =
+                           PAGE_ALIGN(vout->v2f.fmt.pix.sizeimage);
+
+                       retval = mxc_allocate_buffers(vout->queue_buf_paddr,
+                                                     vout->queue_buf_vaddr,
+                                                     vout->buffer_cnt,
+                                                     vout->queue_buf_size);
+                       if (retval < 0)
+                               break;
+
+                       /* Init buffer queues */
+                       vout->done_q.head = 0;
+                       vout->done_q.tail = 0;
+                       vout->ready_q.head = 0;
+                       vout->ready_q.tail = 0;
+
+                       for (i = 0; i < vout->buffer_cnt; i++) {
+                               memset(&(vout->v4l2_bufs[i]), 0,
+                                      sizeof(vout->v4l2_bufs[i]));
+                               vout->v4l2_bufs[i].flags = 0;
+                               vout->v4l2_bufs[i].memory = V4L2_MEMORY_MMAP;
+                               vout->v4l2_bufs[i].index = i;
+                               vout->v4l2_bufs[i].type =
+                                   V4L2_BUF_TYPE_VIDEO_OUTPUT;
+                               vout->v4l2_bufs[i].length =
+                                   PAGE_ALIGN(vout->v2f.fmt.pix.sizeimage);
+                               vout->v4l2_bufs[i].m.offset =
+                                   (unsigned long)vout->queue_buf_paddr[i];
+                               vout->v4l2_bufs[i].timestamp.tv_sec = 0;
+                               vout->v4l2_bufs[i].timestamp.tv_usec = 0;
+                       }
+                       break;
+               }
+       case VIDIOC_QUERYBUF:
+               {
+                       struct v4l2_buffer *buf = arg;
+                       u32 type = buf->type;
+                       int index = buf->index;
+
+                       if ((type != V4L2_BUF_TYPE_VIDEO_OUTPUT) ||
+                           (index >= vout->buffer_cnt)) {
+                               dev_dbg(&vdev->dev,
+                                       "VIDIOC_QUERYBUFS: incorrect buffer type\n");
+                               retval = -EINVAL;
+                               break;
+                       }
+                       down(&vout->param_lock);
+                       memcpy(buf, &(vout->v4l2_bufs[index]), sizeof(*buf));
+                       up(&vout->param_lock);
+                       break;
+               }
+       case VIDIOC_QBUF:
+               {
+                       struct v4l2_buffer *buf = arg;
+                       int index = buf->index;
+                       unsigned long lock_flags;
+                       int param[5][3];
+
+                       if ((buf->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) ||
+                           (index >= vout->buffer_cnt)) {
+                               retval = -EINVAL;
+                               break;
+                       }
+
+                       dev_dbg(&vdev->dev, "VIDIOC_QBUF: %d field = %d\n", buf->index, buf->field);
+
+                       /* mmapped buffers are L1 WB cached,
+                        * so we need to clean them */
+                       if (buf->memory & V4L2_MEMORY_MMAP) {
+                               flush_cache_all();
+                       }
+
+                       spin_lock_irqsave(&g_lock, lock_flags);
+
+                       memcpy(&(vout->v4l2_bufs[index]), buf, sizeof(*buf));
+                       vout->v4l2_bufs[index].flags |= V4L2_BUF_FLAG_QUEUED;
+
+                       g_buf_q_cnt++;
+                       if (vout->v4l2_bufs[index].reserved)
+                               if (!copy_from_user(&param[0][0],
+                                                   (void *)vout->
+                                                   v4l2_bufs[index]
+                                                   .reserved, sizeof(param)))
+                                       ipu_set_csc_coefficients(vout->
+                                                                display_ch,
+                                                                param);
+                       queue_buf(&vout->ready_q, index);
+                       if (vout->state == STATE_STREAM_PAUSED) {
+                               index = peek_next_buf(&vout->ready_q);
+                               setup_next_buf_timer(vout, index);
+                               vout->state = STATE_STREAM_ON;
+                       }
+
+                       spin_unlock_irqrestore(&g_lock, lock_flags);
+                       break;
+               }
+       case VIDIOC_DQBUF:
+               {
+                       struct v4l2_buffer *buf = arg;
+                       int idx;
+
+                       if ((queue_size(&vout->done_q) == 0) &&
+                           (file->f_flags & O_NONBLOCK)) {
+                               retval = -EAGAIN;
+                               break;
+                       }
+
+                       if (!wait_event_interruptible_timeout(vout->v4l_bufq,
+                                                             queue_size(&vout->
+                                                                        done_q)
+                                                             != 0, 10 * HZ)) {
+                               dev_dbg(&vdev->dev, "VIDIOC_DQBUF: timeout\n");
+                               retval = -ETIME;
+                               break;
+                       } else if (signal_pending(current)) {
+                               dev_dbg(&vdev->dev,
+                                       "VIDIOC_DQBUF: interrupt received\n");
+                               retval = -ERESTARTSYS;
+                               break;
+                       }
+                       idx = dequeue_buf(&vout->done_q);
+                       if (idx == -1) {        /* No frame free */
+                               dev_dbg(&vdev->dev,
+                                       "VIDIOC_DQBUF: no free buffers, returning\n");
+                               retval = -EAGAIN;
+                               break;
+                       }
+                       if ((vout->v4l2_bufs[idx].flags & V4L2_BUF_FLAG_DONE) ==
+                           0)
+                               dev_dbg(&vdev->dev,
+                                       "VIDIOC_DQBUF: buffer in done q, but not "
+                                       "flagged as done\n");
+
+                       vout->v4l2_bufs[idx].flags = 0;
+                       memcpy(buf, &(vout->v4l2_bufs[idx]), sizeof(*buf));
+                       dev_dbg(&vdev->dev, "VIDIOC_DQBUF: %d\n", buf->index);
+                       break;
+               }
+       case VIDIOC_STREAMON:
+               {
+                       retval = mxc_v4l2out_streamon(vout);
+                       break;
+               }
+       case VIDIOC_STREAMOFF:
+               {
+                       retval = mxc_v4l2out_streamoff(vout);
+                       break;
+               }
+       case VIDIOC_G_CTRL:
+               {
+                       retval = mxc_get_v42lout_control(vout, arg);
+                       break;
+               }
+       case VIDIOC_S_CTRL:
+               {
+                       retval = mxc_set_v42lout_control(vout, arg);
+                       break;
+               }
+       case VIDIOC_CROPCAP:
+               {
+                       struct v4l2_cropcap *cap = arg;
+
+                       if (cap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+                               retval = -EINVAL;
+                               break;
+                       }
+
+                       cap->bounds = vout->crop_bounds[vout->cur_disp_output];
+                       cap->defrect = vout->crop_bounds[vout->cur_disp_output];
+                       retval = 0;
+                       break;
+               }
+       case VIDIOC_G_CROP:
+               {
+                       struct v4l2_crop *crop = arg;
+
+                       if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+                               retval = -EINVAL;
+                               break;
+                       }
+                       crop->c = vout->crop_current;
+                       break;
+               }
+       case VIDIOC_S_CROP:
+               {
+                       struct v4l2_crop *crop = arg;
+                       struct v4l2_rect *b =
+                           &(vout->crop_bounds[vout->cur_disp_output]);
+
+                       if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+                               retval = -EINVAL;
+                               break;
+                       }
+                       if (crop->c.height < 0) {
+                               retval = -EINVAL;
+                               break;
+                       }
+                       if (crop->c.width < 0) {
+                               retval = -EINVAL;
+                               break;
+                       }
+
+                       /* only full screen supported for SDC BG and SDC DC */
+                       if (vout->cur_disp_output == 4) {
+                               crop->c = vout->crop_current;
+                               break;
+                       }
+
+                       if (crop->c.top < b->top)
+                               crop->c.top = b->top;
+                       if (crop->c.top >= b->top + b->height)
+                               crop->c.top = b->top + b->height - 1;
+                       if (crop->c.height > b->top - crop->c.top + b->height)
+                               crop->c.height =
+                                   b->top - crop->c.top + b->height;
+
+                       if (crop->c.left < b->left)
+                               crop->c.left = b->left;
+                       if (crop->c.left >= b->left + b->width)
+                               crop->c.left = b->left + b->width - 1;
+                       if (crop->c.width > b->left - crop->c.left + b->width)
+                               crop->c.width =
+                                   b->left - crop->c.left + b->width;
+
+                       /* stride line limitation */
+                       crop->c.height -= crop->c.height % 8;
+                       crop->c.width -= crop->c.width % 8;
+
+                       vout->crop_current = crop->c;
+                       break;
+               }
+       case VIDIOC_ENUMOUTPUT:
+               {
+                       struct v4l2_output *output = arg;
+
+                       if ((output->index >= 5) ||
+                           (vout->output_enabled[output->index] == false)) {
+                               retval = -EINVAL;
+                               break;
+                       }
+
+                       if (output->index >= 3)
+                               *output = mxc_outputs[MXC_V4L2_OUT_2_SDC];
+                       break;
+               }
+       case VIDIOC_G_OUTPUT:
+               {
+                       int *p_output_num = arg;
+
+                       *p_output_num = vout->cur_disp_output;
+                       break;
+               }
+       case VIDIOC_S_OUTPUT:
+               {
+                       int *p_output_num = arg;
+                       int fbnum;
+                       struct v4l2_rect *b;
+
+                       if ((*p_output_num >= MXC_V4L2_OUT_NUM_OUTPUTS) ||
+                           (vout->output_enabled[*p_output_num] == false)) {
+                               retval = -EINVAL;
+                               break;
+                       }
+
+                       if (vout->state != STATE_STREAM_OFF) {
+                               retval = -EBUSY;
+                               break;
+                       }
+
+                       vout->cur_disp_output = *p_output_num;
+
+                       /* Update bounds in case they have changed */
+                       b = &vout->crop_bounds[vout->cur_disp_output];
+
+                       fbnum = vout->output_fb_num[vout->cur_disp_output];
+
+                       /*
+                        * For FG overlay, it uses BG window parameter as
+                        * limitation reference; and BG must be enabled to
+                        * support FG.
+                        */
+                       if (vout->cur_disp_output == 3) {
+                               unsigned int i, ipu_ch = CHAN_NONE;
+                               struct fb_info *fbi;
+                               mm_segment_t old_fs;
+
+                               for (i = 0; i < num_registered_fb; i++) {
+                                       fbi = registered_fb[i];
+                                       if (fbi->fbops->fb_ioctl) {
+                                               old_fs = get_fs();
+                                               set_fs(KERNEL_DS);
+                                               fbi->fbops->fb_ioctl(fbi,
+                                                       MXCFB_GET_FB_IPU_CHAN,
+                                                       (unsigned long)&ipu_ch);
+                                               set_fs(old_fs);
+                                       }
+                                       if (ipu_ch == CHAN_NONE) {
+                                               dev_err(&vdev->dev,
+                                               "Can't get disp ipu channel\n");
+                                               retval = -EINVAL;
+                                               break;
+                                       }
+
+                                       if (ipu_ch == MEM_BG_SYNC) {
+                                               fbnum = i;
+                                               break;
+                                       }
+                               }
+                       }
+
+                       b->width = registered_fb[fbnum]->var.xres;
+                       b->height = registered_fb[fbnum]->var.yres;
+
+                       vout->crop_current = *b;
+                       break;
+               }
+       case VIDIOC_ENUM_FMT:
+       case VIDIOC_TRY_FMT:
+       case VIDIOC_QUERYCTRL:
+       case VIDIOC_G_PARM:
+       case VIDIOC_ENUMSTD:
+       case VIDIOC_G_STD:
+       case VIDIOC_S_STD:
+       case VIDIOC_G_TUNER:
+       case VIDIOC_S_TUNER:
+       case VIDIOC_G_FREQUENCY:
+       case VIDIOC_S_FREQUENCY:
+       default:
+               retval = -EINVAL;
+               break;
+       }
+
+       up(&vout->busy_lock);
+       return retval;
+}
+
+/*
+ * V4L2 interface - ioctl function
+ *
+ * @return  None
+ */
+static long
+mxc_v4l2out_ioctl(struct file *file,
+                 unsigned int cmd, unsigned long arg)
+{
+       return video_usercopy(file, cmd, arg, mxc_v4l2out_do_ioctl);
+}
+
+/*!
+ * V4L2 interface - mmap function
+ *
+ * @param file          structure file *
+ *
+ * @param vma           structure vm_area_struct *
+ *
+ * @return status       0 Success, EINTR busy lock error,
+ *                      ENOBUFS remap_page error
+ */
+static int mxc_v4l2out_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       struct video_device *vdev = video_devdata(file);
+       unsigned long size = vma->vm_end - vma->vm_start;
+       int res = 0;
+       int i;
+       vout_data *vout = video_get_drvdata(vdev);
+
+       dev_dbg(&vdev->dev, "pgoff=0x%lx, start=0x%lx, end=0x%lx\n",
+               vma->vm_pgoff, vma->vm_start, vma->vm_end);
+
+       /* make this _really_ smp-safe */
+       if (down_interruptible(&vout->busy_lock))
+               return -EINTR;
+
+       for (i = 0; i < vout->buffer_cnt; i++) {
+               if ((vout->v4l2_bufs[i].m.offset ==
+                    (vma->vm_pgoff << PAGE_SHIFT)) &&
+                   (vout->v4l2_bufs[i].length >= size)) {
+                       vout->v4l2_bufs[i].flags |= V4L2_BUF_FLAG_MAPPED;
+                       break;
+               }
+       }
+       if (i == vout->buffer_cnt) {
+               res = -ENOBUFS;
+               goto mxc_mmap_exit;
+       }
+
+       /* make buffers inner write-back, outer write-thru cacheable */
+       /* vma->vm_page_prot = pgprot_outer_wrthru(vma->vm_page_prot);*/
+
+       if (remap_pfn_range(vma, vma->vm_start,
+                           vma->vm_pgoff, size, vma->vm_page_prot)) {
+               dev_dbg(&vdev->dev, "mmap remap_pfn_range failed\n");
+               res = -ENOBUFS;
+               goto mxc_mmap_exit;
+       }
+
+       vma->vm_flags &= ~VM_IO;        /* using shared anonymous pages */
+
+      mxc_mmap_exit:
+       up(&vout->busy_lock);
+       return res;
+}
+
+/*!
+ * V4L2 interface - poll function
+ *
+ * @param file       structure file *
+ *
+ * @param wait       structure poll_table_struct *
+ *
+ * @return  status   POLLIN | POLLRDNORM
+ */
+static unsigned int mxc_v4l2out_poll(struct file *file, struct poll_table_struct * wait)
+{
+       struct video_device *dev = video_devdata(file);
+       vout_data *vout = video_get_drvdata(dev);
+
+       wait_queue_head_t *queue = NULL;
+       int res = POLLIN | POLLRDNORM;
+
+       if (down_interruptible(&vout->busy_lock))
+               return -EINTR;
+
+       queue = &vout->v4l_bufq;
+       poll_wait(file, queue, wait);
+
+       up(&vout->busy_lock);
+       return res;
+}
+
+static struct
+v4l2_file_operations mxc_v4l2out_fops = {
+       .owner = THIS_MODULE,
+       .open = mxc_v4l2out_open,
+       .release = mxc_v4l2out_close,
+       .ioctl = mxc_v4l2out_ioctl,
+       .mmap = mxc_v4l2out_mmap,
+       .poll = mxc_v4l2out_poll,
+};
+
+static struct video_device mxc_v4l2out_template = {
+       .name = "MXC Video Output",
+       .vfl_type = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING,
+       .fops = &mxc_v4l2out_fops,
+       .release = video_device_release,
+};
+
+static ssize_t show_streaming(struct device *dev,
+                       struct device_attribute *attr, char *buf)
+{
+       struct video_device *video_dev = container_of(dev,
+                                               struct video_device, dev);
+       vout_data *vout = video_get_drvdata(video_dev);
+
+       if (vout->state == STATE_STREAM_OFF)
+               return sprintf(buf, "stream off\n");
+       else if (vout->state == STATE_STREAM_PAUSED)
+               return sprintf(buf, "stream paused\n");
+       else
+               return sprintf(buf, "stream on\n");
+}
+static DEVICE_ATTR(fsl_v4l2_output_property, S_IRUGO, show_streaming, NULL);
+
+/*!
+ * 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 mxc_v4l2out_probe(struct platform_device *pdev)
+{
+       int i;
+       vout_data *vout;
+
+       /*
+        * Allocate sufficient memory for the fb structure
+        */
+       vout = kmalloc(sizeof(vout_data), GFP_KERNEL);
+
+       if (!vout)
+               return 0;
+
+       memset(vout, 0, sizeof(vout_data));
+
+       vout->video_dev = video_device_alloc();
+       if (vout->video_dev == NULL)
+               return -1;
+       vout->video_dev->minor = -1;
+
+       *(vout->video_dev) = mxc_v4l2out_template;
+
+       /* register v4l device */
+       if (video_register_device(vout->video_dev,
+                                 VFL_TYPE_GRABBER, video_nr) == -1) {
+               dev_dbg(&pdev->dev, "video_register_device failed\n");
+               return 0;
+       }
+       dev_info(&pdev->dev, "Registered device video%d\n",
+                vout->video_dev->minor & 0x1f);
+       /*vout->video_dev->dev = &pdev->dev;*/
+
+       video_set_drvdata(vout->video_dev, vout);
+
+       init_MUTEX(&vout->param_lock);
+       init_MUTEX(&vout->busy_lock);
+
+       /* setup outputs and cropping */
+       vout->cur_disp_output = -1;
+       for (i = 0; i < num_registered_fb; i++) {
+               char *idstr = registered_fb[i]->fix.id;
+               if (strncmp(idstr, "DISP", 4) == 0) {
+                       int disp_num = idstr[4] - '0';
+                       if (disp_num == 3) {
+                               if (strcmp(idstr, "DISP3 BG - DI1") == 0)
+                                       disp_num = 5;
+                               else if (strncmp(idstr, "DISP3 BG", 8) == 0)
+                                       disp_num = 4;
+                       }
+                       vout->crop_bounds[disp_num].left = 0;
+                       vout->crop_bounds[disp_num].top = 0;
+                       vout->crop_bounds[disp_num].width =
+                           registered_fb[i]->var.xres;
+                       vout->crop_bounds[disp_num].height =
+                           registered_fb[i]->var.yres;
+                       vout->output_enabled[disp_num] = true;
+                       vout->output_fb_num[disp_num] = i;
+                       if (vout->cur_disp_output == -1) {
+                               vout->cur_disp_output = disp_num;
+                       }
+               }
+
+       }
+       vout->crop_current = vout->crop_bounds[vout->cur_disp_output];
+
+       platform_set_drvdata(pdev, vout);
+
+       if (device_create_file(&vout->video_dev->dev,
+                       &dev_attr_fsl_v4l2_output_property))
+               dev_err(&pdev->dev, "Error on creating file\n");
+
+       return 0;
+}
+
+static int mxc_v4l2out_remove(struct platform_device *pdev)
+{
+       vout_data *vout = platform_get_drvdata(pdev);
+
+       if (vout->video_dev) {
+               device_remove_file(&vout->video_dev->dev,
+                       &dev_attr_fsl_v4l2_output_property);
+               video_unregister_device(vout->video_dev);
+               vout->video_dev = NULL;
+       }
+
+       platform_set_drvdata(pdev, NULL);
+
+       kfree(vout);
+
+       return 0;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxc_v4l2out_driver = {
+       .driver = {
+                  .name = "mxc_v4l2_output",
+                  },
+       .probe = mxc_v4l2out_probe,
+       .remove = mxc_v4l2out_remove,
+};
+
+/*!
+ * mxc v4l2 init function
+ *
+ */
+static int mxc_v4l2out_init(void)
+{
+       return platform_driver_register(&mxc_v4l2out_driver);
+}
+
+/*!
+ * mxc v4l2 cleanup function
+ *
+ */
+static void mxc_v4l2out_clean(void)
+{
+       platform_driver_unregister(&mxc_v4l2out_driver);
+}
+
+module_init(mxc_v4l2out_init);
+module_exit(mxc_v4l2out_clean);
+
+module_param(video_nr, int, 0444);
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("V4L2-driver for MXC video output");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("video");
diff --git a/drivers/media/video/mxc/output/mxc_v4l2_output.h b/drivers/media/video/mxc/output/mxc_v4l2_output.h
new file mode 100644 (file)
index 0000000..c38bfdd
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2005-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 MXC_V4L2_OUTPUT MXC V4L2 Video Output Driver
+ */
+/*!
+ * @file mxc_v4l2_output.h
+ *
+ * @brief MXC V4L2 Video Output Driver Header file
+ *
+ * Video4Linux2 Output Device using MXC IPU Post-processing functionality.
+ *
+ * @ingroup MXC_V4L2_OUTPUT
+ */
+#ifndef __MXC_V4L2_OUTPUT_H__
+#define __MXC_V4L2_OUTPUT_H__
+
+#include <media/v4l2-dev.h>
+
+#ifdef __KERNEL__
+
+#include <linux/ipu.h>
+#include <linux/mxc_v4l2.h>
+#include <linux/videodev2.h>
+
+#define MIN_FRAME_NUM 2
+#define MAX_FRAME_NUM 30
+
+#define MXC_V4L2_OUT_NUM_OUTPUTS        6
+#define MXC_V4L2_OUT_2_SDC              0
+
+
+typedef struct {
+       int list[MAX_FRAME_NUM + 1];
+       int head;
+       int tail;
+} v4l_queue;
+
+/*!
+ * States for the video stream
+ */
+typedef enum {
+       STATE_STREAM_OFF,
+       STATE_STREAM_ON,
+       STATE_STREAM_PAUSED,
+       STATE_STREAM_STOPPING,
+} v4lout_state;
+
+/*!
+ * common v4l2 driver structure.
+ */
+typedef struct _vout_data {
+       struct video_device *video_dev;
+       /*!
+        * semaphore guard against SMP multithreading
+        */
+       struct semaphore busy_lock;
+
+       /*!
+        * number of process that have device open
+        */
+       int open_count;
+
+       /*!
+        * params lock for this camera
+        */
+       struct semaphore param_lock;
+
+       struct timer_list output_timer;
+       struct workqueue_struct *v4l_wq;
+       struct work_struct icbypass_work;
+       int disp_buf_num;
+       int fb_blank;
+       unsigned long start_jiffies;
+       u32 frame_count;
+
+       v4l_queue ready_q;
+       v4l_queue done_q;
+
+       s8 next_rdy_ipu_buf;
+       s8 next_done_ipu_buf;
+       s8 next_disp_ipu_buf;
+       s8 ipu_buf[2];
+       s8 ipu_buf_p[2];
+       s8 ipu_buf_n[2];
+       volatile v4lout_state state;
+
+       int cur_disp_output;
+       int output_fb_num[MXC_V4L2_OUT_NUM_OUTPUTS];
+       int output_enabled[MXC_V4L2_OUT_NUM_OUTPUTS];
+       struct v4l2_framebuffer v4l2_fb;
+       int ic_bypass;
+       u32 work_irq;
+       ipu_channel_t display_ch;
+       ipu_channel_t post_proc_ch;
+       ipu_channel_t display_input_ch;
+
+       /*!
+        * FRAME_NUM-buffering, so we need a array
+        */
+       int buffer_cnt;
+       dma_addr_t queue_buf_paddr[MAX_FRAME_NUM];
+       void *queue_buf_vaddr[MAX_FRAME_NUM];
+       u32 queue_buf_size;
+       struct v4l2_buffer v4l2_bufs[MAX_FRAME_NUM];
+       u32 display_buf_size;
+       dma_addr_t display_bufs[2];
+       void *display_bufs_vaddr[2];
+       dma_addr_t rot_pp_bufs[2];
+       void *rot_pp_bufs_vaddr[2];
+
+       /*!
+        * Poll wait queue
+        */
+       wait_queue_head_t v4l_bufq;
+
+       /*!
+        * v4l2 format
+        */
+       struct v4l2_format v2f;
+       struct v4l2_mxc_offset offset;
+       ipu_rotate_mode_t rotate;
+
+       /* crop */
+       struct v4l2_rect crop_bounds[MXC_V4L2_OUT_NUM_OUTPUTS];
+       struct v4l2_rect crop_current;
+       u32 bytesperline;
+       enum v4l2_field field_fmt;
+       ipu_motion_sel motion_sel;
+
+       /* PP split fot two stripes*/
+       int pp_split; /* 0,1 */
+       struct stripe_param pp_left_stripe;
+       struct stripe_param pp_right_stripe; /* struct for split parameters */
+       struct stripe_param pp_up_stripe;
+       struct stripe_param pp_down_stripe;
+       /* IC ouput buffer number. Counting from 0 to 7 */
+       int pp_split_buf_num; /*  0..7 */
+       u16 bpp ; /* bit per pixel */
+       u16 xres; /* width of physical frame (BGs) */
+       u16 yres; /* heigth of physical frame (BGs)*/
+
+} vout_data;
+
+#endif
+#endif                         /* __MXC_V4L2_OUTPUT_H__ */
diff --git a/drivers/mxc/Kconfig b/drivers/mxc/Kconfig
new file mode 100644 (file)
index 0000000..6e67087
--- /dev/null
@@ -0,0 +1,39 @@
+# drivers/video/mxc/Kconfig
+
+if ARCH_MXC
+
+menu "MXC support drivers"
+
+config MXC_IPU
+       bool "Image Processing Unit Driver"
+       depends on !ARCH_MX21
+       depends on !ARCH_MX27
+       depends on !ARCH_MX25
+       select MXC_IPU_V1 if !ARCH_MX37 && !ARCH_MX5
+       select MXC_IPU_V3 if ARCH_MX37 || ARCH_MX5
+       select MXC_IPU_V3D if ARCH_MX37
+       select MXC_IPU_V3EX if ARCH_MX5
+       help
+         If you plan to use the Image Processing unit, say
+         Y here. IPU is needed by Framebuffer and V4L2 drivers.
+
+source "drivers/mxc/ipu/Kconfig"
+source "drivers/mxc/ipu3/Kconfig"
+
+source "drivers/mxc/ssi/Kconfig"
+source "drivers/mxc/dam/Kconfig"
+source "drivers/mxc/pmic/Kconfig"
+source "drivers/mxc/mcu_pmic/Kconfig"
+source "drivers/mxc/security/Kconfig"
+source "drivers/mxc/hmp4e/Kconfig"
+source "drivers/mxc/hw_event/Kconfig"
+source "drivers/mxc/vpu/Kconfig"
+source "drivers/mxc/asrc/Kconfig"
+source "drivers/mxc/bt/Kconfig"
+source "drivers/mxc/gps_ioctrl/Kconfig"
+source "drivers/mxc/mlb/Kconfig"
+source "drivers/mxc/adc/Kconfig"
+
+endmenu
+
+endif
diff --git a/drivers/mxc/Makefile b/drivers/mxc/Makefile
new file mode 100644 (file)
index 0000000..6416bc4
--- /dev/null
@@ -0,0 +1,17 @@
+obj-$(CONFIG_MXC_IPU_V1)                       += ipu/
+obj-$(CONFIG_MXC_IPU_V3)                       += ipu3/
+obj-$(CONFIG_MXC_SSI)                  += ssi/
+obj-$(CONFIG_MXC_DAM)                  += dam/
+
+obj-$(CONFIG_MXC_PMIC_MC9SDZ60)                += mcu_pmic/
+obj-$(CONFIG_MXC_PMIC)                 += pmic/
+
+obj-$(CONFIG_MXC_HMP4E)                += hmp4e/
+obj-y                                   += security/
+obj-$(CONFIG_MXC_VPU)                   += vpu/
+obj-$(CONFIG_MXC_HWEVENT)               += hw_event/
+obj-$(CONFIG_MXC_ASRC)                 += asrc/
+obj-$(CONFIG_MXC_BLUETOOTH)            += bt/
+obj-$(CONFIG_GPS_IOCTRL)               += gps_ioctrl/
+obj-$(CONFIG_MXC_MLB)                   += mlb/
+obj-$(CONFIG_IMX_ADC)                   += adc/
diff --git a/drivers/mxc/adc/Kconfig b/drivers/mxc/adc/Kconfig
new file mode 100644 (file)
index 0000000..91ad23b
--- /dev/null
@@ -0,0 +1,14 @@
+#
+# i.MX ADC devices
+#
+
+menu "i.MX ADC support"
+
+config IMX_ADC
+       tristate "i.MX ADC"
+       depends on ARCH_MXC
+       default n
+       help
+          This selects the Freescale i.MX on-chip ADC driver.
+
+endmenu
diff --git a/drivers/mxc/adc/Makefile b/drivers/mxc/adc/Makefile
new file mode 100644 (file)
index 0000000..e21e48e
--- /dev/null
@@ -0,0 +1,4 @@
+#
+# Makefile for i.MX adc devices.
+#
+obj-$(CONFIG_IMX_ADC)          += imx_adc.o
diff --git a/drivers/mxc/adc/imx_adc.c b/drivers/mxc/adc/imx_adc.c
new file mode 100644 (file)
index 0000000..07011b3
--- /dev/null
@@ -0,0 +1,1134 @@
+/*
+ * 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
+ */
+
+/*!
+ * @file adc/imx_adc.c
+ * @brief This is the main file of i.MX ADC driver.
+ *
+ * @ingroup IMX_ADC
+ */
+
+/*
+ * Includes
+ */
+
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/imx_adc.h>
+#include "imx_adc_reg.h"
+
+static int imx_adc_major;
+
+/*!
+ * Number of users waiting in suspendq
+ */
+static int swait;
+
+/*!
+ * To indicate whether any of the adc devices are suspending
+ */
+static int suspend_flag;
+
+/*!
+ * The suspendq is used by blocking application calls
+ */
+static wait_queue_head_t suspendq;
+static wait_queue_head_t tsq;
+
+static bool imx_adc_ready;
+static bool ts_data_ready;
+static int tsi_data = TSI_DATA;
+static unsigned short ts_data_buf[16];
+
+static struct class *imx_adc_class;
+static struct imx_adc_data *adc_data;
+
+static DECLARE_MUTEX(general_convert_mutex);
+static DECLARE_MUTEX(ts_convert_mutex);
+
+unsigned long tsc_base;
+
+int is_imx_adc_ready(void)
+{
+       return imx_adc_ready;
+}
+EXPORT_SYMBOL(is_imx_adc_ready);
+
+void tsc_clk_enable(void)
+{
+       unsigned long reg;
+
+       clk_enable(adc_data->adc_clk);
+
+       reg = __raw_readl(tsc_base + TGCR);
+       reg |= TGCR_IPG_CLK_EN;
+       __raw_writel(reg, tsc_base + TGCR);
+}
+
+void tsc_clk_disable(void)
+{
+       unsigned long reg;
+
+       clk_disable(adc_data->adc_clk);
+
+       reg = __raw_readl(tsc_base + TGCR);
+       reg &= ~TGCR_IPG_CLK_EN;
+       __raw_writel(reg, tsc_base + TGCR);
+}
+
+void tsc_self_reset(void)
+{
+       unsigned long reg;
+
+       reg = __raw_readl(tsc_base + TGCR);
+       reg |= TGCR_TSC_RST;
+       __raw_writel(reg, tsc_base + TGCR);
+
+       while (__raw_readl(tsc_base + TGCR) & TGCR_TSC_RST)
+               continue;
+}
+
+/* Internal reference */
+void tsc_intref_enable(void)
+{
+       unsigned long reg;
+
+       reg = __raw_readl(tsc_base + TGCR);
+       reg |= TGCR_INTREFEN;
+       __raw_writel(reg, tsc_base + TGCR);
+}
+
+/* initialize touchscreen */
+void imx_tsc_init(void)
+{
+       unsigned long reg;
+       int lastitemid;
+
+       /* Level sense */
+       reg = __raw_readl(tsc_base + TCQCR);
+       reg &= ~CQCR_PD_CFG;  /* edge sensitive */
+       reg |= (0xf << CQCR_FIFOWATERMARK_SHIFT);  /* watermark */
+       __raw_writel(reg, tsc_base + TCQCR);
+
+       /* Configure 4-wire */
+       reg = TSC_4WIRE_PRECHARGE;
+       reg |= CC_IGS;
+       __raw_writel(reg, tsc_base + TCC0);
+
+       reg = TSC_4WIRE_TOUCH_DETECT;
+       reg |= 3 << CC_NOS_SHIFT;       /* 4 samples */
+       reg |= 32 << CC_SETTLING_TIME_SHIFT;    /* it's important! */
+       __raw_writel(reg, tsc_base + TCC1);
+
+       reg = TSC_4WIRE_X_MEASUMENT;
+       reg |= 3 << CC_NOS_SHIFT;       /* 4 samples */
+       reg |= 16 << CC_SETTLING_TIME_SHIFT;    /* settling time */
+       __raw_writel(reg, tsc_base + TCC2);
+
+       reg = TSC_4WIRE_Y_MEASUMENT;
+       reg |= 3 << CC_NOS_SHIFT;       /* 4 samples */
+       reg |= 16 << CC_SETTLING_TIME_SHIFT;    /* settling time */
+       __raw_writel(reg, tsc_base + TCC3);
+
+       reg = (TCQ_ITEM_TCC0 << TCQ_ITEM7_SHIFT) |
+             (TCQ_ITEM_TCC0 << TCQ_ITEM6_SHIFT) |
+             (TCQ_ITEM_TCC1 << TCQ_ITEM5_SHIFT) |
+             (TCQ_ITEM_TCC0 << TCQ_ITEM4_SHIFT) |
+             (TCQ_ITEM_TCC3 << TCQ_ITEM3_SHIFT) |
+             (TCQ_ITEM_TCC2 << TCQ_ITEM2_SHIFT) |
+             (TCQ_ITEM_TCC1 << TCQ_ITEM1_SHIFT) |
+             (TCQ_ITEM_TCC0 << TCQ_ITEM0_SHIFT);
+       __raw_writel(reg, tsc_base + TCQ_ITEM_7_0);
+
+       lastitemid = 5;
+       reg = __raw_readl(tsc_base + TCQCR);
+       reg = (reg & ~CQCR_LAST_ITEM_ID_MASK) |
+             (lastitemid << CQCR_LAST_ITEM_ID_SHIFT);
+       __raw_writel(reg, tsc_base + TCQCR);
+
+       /* Config idle for 4-wire */
+       reg = TSC_4WIRE_PRECHARGE;
+       __raw_writel(reg, tsc_base + TICR);
+
+       reg = TSC_4WIRE_TOUCH_DETECT;
+       __raw_writel(reg, tsc_base + TICR);
+
+       /* pen down mask */
+       reg = __raw_readl(tsc_base + TCQCR);
+       reg &= ~CQCR_PD_MSK;
+       __raw_writel(reg, tsc_base + TCQCR);
+       reg = __raw_readl(tsc_base + TCQMR);
+       reg &= ~TCQMR_PD_IRQ_MSK;
+       __raw_writel(reg, tsc_base + TCQMR);
+
+       /* Debounce time = dbtime*8 adc clock cycles */
+       reg = __raw_readl(tsc_base + TGCR);
+       reg &= ~TGCR_PDBTIME_MASK;
+       reg |= TGCR_PDBTIME128 | TGCR_HSYNC_EN;
+       __raw_writel(reg, tsc_base + TGCR);
+
+       /* pen down enable */
+       reg = __raw_readl(tsc_base + TGCR);
+       reg |= TGCR_PDB_EN;
+       __raw_writel(reg, tsc_base + TGCR);
+       reg |= TGCR_PD_EN;
+       __raw_writel(reg, tsc_base + TGCR);
+}
+
+static irqreturn_t imx_adc_interrupt(int irq, void *dev_id)
+{
+       unsigned long reg;
+
+       if (__raw_readl(tsc_base + TGSR) & 0x4) {
+               /* deep sleep wakeup interrupt */
+               /* clear tgsr */
+               __raw_writel(0,  tsc_base + TGSR);
+               /* clear deep sleep wakeup irq */
+               reg = __raw_readl(tsc_base + TGCR);
+               reg &= ~TGCR_SLPC;
+               __raw_writel(reg, tsc_base + TGCR);
+               /* un-mask pen down and pen down irq */
+               reg = __raw_readl(tsc_base + TCQCR);
+               reg &= ~CQCR_PD_MSK;
+               __raw_writel(reg, tsc_base + TCQCR);
+               reg = __raw_readl(tsc_base + TCQMR);
+               reg &= ~TCQMR_PD_IRQ_MSK;
+               __raw_writel(reg, tsc_base + TCQMR);
+       } else if ((__raw_readl(tsc_base + TGSR) & 0x1) &&
+                  (__raw_readl(tsc_base + TCQSR) & 0x1)) {
+
+               /* mask pen down detect irq */
+               reg = __raw_readl(tsc_base + TCQMR);
+               reg |= TCQMR_PD_IRQ_MSK;
+               __raw_writel(reg, tsc_base + TCQMR);
+
+               ts_data_ready = 1;
+               wake_up_interruptible(&tsq);
+       }
+       return IRQ_HANDLED;
+}
+
+enum IMX_ADC_STATUS imx_adc_read_general(unsigned short *result)
+{
+       unsigned long reg;
+       unsigned int data_num = 0;
+
+       reg = __raw_readl(tsc_base + GCQCR);
+       reg |= CQCR_FQS;
+       __raw_writel(reg, tsc_base + GCQCR);
+
+       while (!(__raw_readl(tsc_base + GCQSR) & CQSR_EOQ))
+               continue;
+       reg = __raw_readl(tsc_base + GCQCR);
+       reg &= ~CQCR_FQS;
+       __raw_writel(reg, tsc_base + GCQCR);
+       reg = __raw_readl(tsc_base + GCQSR);
+       reg |= CQSR_EOQ;
+       __raw_writel(reg, tsc_base + GCQSR);
+
+       while (!(__raw_readl(tsc_base + GCQSR) & CQSR_EMPT)) {
+               result[data_num] = __raw_readl(tsc_base + GCQFIFO) >>
+                                GCQFIFO_ADCOUT_SHIFT;
+               data_num++;
+       }
+       return IMX_ADC_SUCCESS;
+}
+
+/*!
+ * This function will get raw (X,Y) value by converting the voltage
+ * @param        touch_sample Pointer to touch sample
+ *
+ * return        This funciton returns 0 if successful.
+ *
+ *
+ */
+enum IMX_ADC_STATUS imx_adc_read_ts(struct t_touch_screen *touch_sample,
+                                   int wait_tsi)
+{
+       unsigned long reg;
+       int data_num = 0;
+       int detect_sample1, detect_sample2;
+
+       memset(ts_data_buf, 0, sizeof ts_data_buf);
+       touch_sample->valid_flag = 1;
+
+       if (wait_tsi) {
+               /* Config idle for 4-wire */
+               reg = TSC_4WIRE_TOUCH_DETECT;
+               __raw_writel(reg, tsc_base + TICR);
+
+               /* Pen interrupt starts new conversion queue */
+               reg = __raw_readl(tsc_base + TCQCR);
+               reg &= ~CQCR_QSM_MASK;
+               reg |= CQCR_QSM_PEN;
+               __raw_writel(reg, tsc_base + TCQCR);
+
+               /* unmask pen down detect irq */
+               reg = __raw_readl(tsc_base + TCQMR);
+               reg &= ~TCQMR_PD_IRQ_MSK;
+               __raw_writel(reg, tsc_base + TCQMR);
+
+               wait_event_interruptible(tsq, ts_data_ready);
+               while (!(__raw_readl(tsc_base + TCQSR) & CQSR_EOQ))
+                       continue;
+
+               /* stop the conversion */
+               reg = __raw_readl(tsc_base + TCQCR);
+               reg &= ~CQCR_QSM_MASK;
+               __raw_writel(reg, tsc_base + TCQCR);
+               reg = CQSR_PD | CQSR_EOQ;
+               __raw_writel(reg, tsc_base + TCQSR);
+
+               /* change configuration for FQS mode */
+               tsi_data = TSI_DATA;
+               reg = (0x1 << CC_YPLLSW_SHIFT) | (0x1 << CC_XNURSW_SHIFT) |
+                     CC_XPULSW;
+               __raw_writel(reg, tsc_base + TICR);
+       } else {
+               /* FQS semaphore */
+               down(&ts_convert_mutex);
+
+               reg = (0x1 << CC_YPLLSW_SHIFT) | (0x1 << CC_XNURSW_SHIFT) |
+                     CC_XPULSW;
+               __raw_writel(reg, tsc_base + TICR);
+
+               /* FQS */
+               reg = __raw_readl(tsc_base + TCQCR);
+               reg &= ~CQCR_QSM_MASK;
+               reg |= CQCR_QSM_FQS;
+               __raw_writel(reg, tsc_base + TCQCR);
+               reg = __raw_readl(tsc_base + TCQCR);
+               reg |= CQCR_FQS;
+               __raw_writel(reg, tsc_base + TCQCR);
+               while (!(__raw_readl(tsc_base + TCQSR) & CQSR_EOQ))
+                       continue;
+
+               /* stop FQS */
+               reg = __raw_readl(tsc_base + TCQCR);
+               reg &= ~CQCR_QSM_MASK;
+               __raw_writel(reg, tsc_base + TCQCR);
+               reg = __raw_readl(tsc_base + TCQCR);
+               reg &= ~CQCR_FQS;
+               __raw_writel(reg, tsc_base + TCQCR);
+
+               /* clear status bit */
+               reg = __raw_readl(tsc_base + TCQSR);
+               reg |= CQSR_EOQ;
+               __raw_writel(reg, tsc_base + TCQSR);
+               tsi_data = FQS_DATA;
+
+               /* Config idle for 4-wire */
+               reg = TSC_4WIRE_PRECHARGE;
+               __raw_writel(reg, tsc_base + TICR);
+
+               reg = TSC_4WIRE_TOUCH_DETECT;
+               __raw_writel(reg, tsc_base + TICR);
+
+       }
+
+       while (!(__raw_readl(tsc_base + TCQSR) & CQSR_EMPT)) {
+               reg = __raw_readl(tsc_base + TCQFIFO);
+               ts_data_buf[data_num] = reg;
+               data_num++;
+       }
+
+       touch_sample->x_position1 = ts_data_buf[4] >> 4;
+       touch_sample->x_position2 = ts_data_buf[5] >> 4;
+       touch_sample->x_position3 = ts_data_buf[6] >> 4;
+       touch_sample->y_position1 = ts_data_buf[9] >> 4;
+       touch_sample->y_position2 = ts_data_buf[10] >> 4;
+       touch_sample->y_position3 = ts_data_buf[11] >> 4;
+
+       detect_sample1 = ts_data_buf[0];
+       detect_sample2 = ts_data_buf[12];
+
+       if ((detect_sample1 > 0x6000) || (detect_sample2 > 0x6000))
+               touch_sample->valid_flag = 0;
+
+       ts_data_ready = 0;
+
+       if (!(touch_sample->x_position1 ||
+             touch_sample->x_position2 || touch_sample->x_position3))
+               touch_sample->contact_resistance = 0;
+       else
+               touch_sample->contact_resistance = 1;
+
+       if (tsi_data == FQS_DATA)
+               up(&ts_convert_mutex);
+       return IMX_ADC_SUCCESS;
+}
+
+/*!
+ * This function performs filtering and rejection of excessive noise prone
+ * sampl.
+ *
+ * @param        ts_curr     Touch screen value
+ *
+ * @return       This function returns 0 on success, -1 otherwise.
+ */
+static int imx_adc_filter(struct t_touch_screen *ts_curr)
+{
+
+       unsigned int ydiff1, ydiff2, ydiff3, xdiff1, xdiff2, xdiff3;
+       unsigned int sample_sumx, sample_sumy;
+       static unsigned int prev_x[FILTLEN], prev_y[FILTLEN];
+       int index = 0;
+       unsigned int y_curr, x_curr;
+       static int filt_count;
+       /* Added a variable filt_type to decide filtering at run-time */
+       unsigned int filt_type = 0;
+
+       /* ignore the data converted when pen down and up */
+       if ((ts_curr->contact_resistance == 0) || tsi_data == TSI_DATA) {
+               ts_curr->x_position = 0;
+               ts_curr->y_position = 0;
+               filt_count = 0;
+               return 0;
+       }
+       /* ignore the data valid */
+       if (ts_curr->valid_flag == 0)
+               return -1;
+
+       ydiff1 = abs(ts_curr->y_position1 - ts_curr->y_position2);
+       ydiff2 = abs(ts_curr->y_position2 - ts_curr->y_position3);
+       ydiff3 = abs(ts_curr->y_position1 - ts_curr->y_position3);
+       if ((ydiff1 > DELTA_Y_MAX) ||
+           (ydiff2 > DELTA_Y_MAX) || (ydiff3 > DELTA_Y_MAX)) {
+               pr_debug("imx_adc_filter: Ret pos 1\n");
+               return -1;
+       }
+
+       xdiff1 = abs(ts_curr->x_position1 - ts_curr->x_position2);
+       xdiff2 = abs(ts_curr->x_position2 - ts_curr->x_position3);
+       xdiff3 = abs(ts_curr->x_position1 - ts_curr->x_position3);
+
+       if ((xdiff1 > DELTA_X_MAX) ||
+           (xdiff2 > DELTA_X_MAX) || (xdiff3 > DELTA_X_MAX)) {
+               pr_debug("imx_adc_filter: Ret pos 2\n");
+               return -1;
+       }
+       /* Compute two closer values among the three available Y readouts */
+
+       if (ydiff1 < ydiff2) {
+               if (ydiff1 < ydiff3) {
+                       /* Sample 0 & 1 closest together */
+                       sample_sumy = ts_curr->y_position1 +
+                           ts_curr->y_position2;
+               } else {
+                       /* Sample 0 & 2 closest together */
+                       sample_sumy = ts_curr->y_position1 +
+                           ts_curr->y_position3;
+               }
+       } else {
+               if (ydiff2 < ydiff3) {
+                       /* Sample 1 & 2 closest together */
+                       sample_sumy = ts_curr->y_position2 +
+                           ts_curr->y_position3;
+               } else {
+                       /* Sample 0 & 2 closest together */
+                       sample_sumy = ts_curr->y_position1 +
+                           ts_curr->y_position3;
+               }
+       }
+
+       /*
+        * Compute two closer values among the three available X
+        * readouts
+        */
+       if (xdiff1 < xdiff2) {
+               if (xdiff1 < xdiff3) {
+                       /* Sample 0 & 1 closest together */
+                       sample_sumx = ts_curr->x_position1 +
+                           ts_curr->x_position2;
+               } else {
+                       /* Sample 0 & 2 closest together */
+                       sample_sumx = ts_curr->x_position1 +
+                           ts_curr->x_position3;
+               }
+       } else {
+               if (xdiff2 < xdiff3) {
+                       /* Sample 1 & 2 closest together */
+                       sample_sumx = ts_curr->x_position2 +
+                           ts_curr->x_position3;
+               } else {
+                       /* Sample 0 & 2 closest together */
+                       sample_sumx = ts_curr->x_position1 +
+                           ts_curr->x_position3;
+               }
+       }
+
+       /*
+        * Wait FILTER_MIN_DELAY number of samples to restart
+        * filtering
+        */
+       if (filt_count < FILTER_MIN_DELAY) {
+               /*
+                * Current output is the average of the two closer
+                * values and no filtering is used
+                */
+               y_curr = (sample_sumy / 2);
+               x_curr = (sample_sumx / 2);
+               ts_curr->y_position = y_curr;
+               ts_curr->x_position = x_curr;
+               filt_count++;
+
+       } else {
+               if (abs(sample_sumx - (prev_x[0] + prev_x[1])) >
+                   (DELTA_X_MAX * 16)) {
+                       pr_debug("imx_adc_filter: : Ret pos 3\n");
+                       return -1;
+               }
+               if (abs(sample_sumy - (prev_y[0] + prev_y[1])) >
+                   (DELTA_Y_MAX * 16)) {
+                       pr_debug("imx_adc_filter: : Ret pos 4\n");
+                       return -1;
+               }
+               sample_sumy /= 2;
+               sample_sumx /= 2;
+               /* Use hard filtering if the sample difference < 10 */
+               if ((abs(sample_sumy - prev_y[0]) > 10) ||
+                   (abs(sample_sumx - prev_x[0]) > 10))
+                       filt_type = 1;
+
+               /*
+                * Current outputs are the average of three previous
+                * values and the present readout
+                */
+               y_curr = sample_sumy;
+               for (index = 0; index < FILTLEN; index++) {
+                       if (filt_type == 0)
+                               y_curr = y_curr + (prev_y[index]);
+                       else
+                               y_curr = y_curr + (prev_y[index] / 3);
+               }
+               if (filt_type == 0)
+                       y_curr = y_curr >> 2;
+               else
+                       y_curr = y_curr >> 1;
+               ts_curr->y_position = y_curr;
+
+               x_curr = sample_sumx;
+               for (index = 0; index < FILTLEN; index++) {
+                       if (filt_type == 0)
+                               x_curr = x_curr + (prev_x[index]);
+                       else
+                               x_curr = x_curr + (prev_x[index] / 3);
+               }
+               if (filt_type == 0)
+                       x_curr = x_curr >> 2;
+               else
+                       x_curr = x_curr >> 1;
+               ts_curr->x_position = x_curr;
+
+       }
+
+       /* Update previous X and Y values */
+       for (index = (FILTLEN - 1); index > 0; index--) {
+               prev_x[index] = prev_x[index - 1];
+               prev_y[index] = prev_y[index - 1];
+       }
+
+       /*
+        * Current output will be the most recent past for the
+        * next sample
+        */
+       prev_y[0] = y_curr;
+       prev_x[0] = x_curr;
+
+       return 0;
+
+}
+
+/*!
+ * This function retrieves the current touch screen (X,Y) coordinates.
+ *
+ * @param        touch_sample Pointer to touch sample.
+ *
+ * @return       This function returns IMX_ADC_SUCCESS if successful.
+ */
+enum IMX_ADC_STATUS imx_adc_get_touch_sample(struct t_touch_screen
+                                            *touch_sample, int wait_tsi)
+{
+       if (imx_adc_read_ts(touch_sample, wait_tsi))
+               return IMX_ADC_ERROR;
+       if (!imx_adc_filter(touch_sample))
+               return IMX_ADC_SUCCESS;
+       else
+               return IMX_ADC_ERROR;
+}
+EXPORT_SYMBOL(imx_adc_get_touch_sample);
+
+void imx_adc_set_hsync(int on)
+{
+       unsigned long reg;
+       if (imx_adc_ready) {
+               reg = __raw_readl(tsc_base + TGCR);
+               if (on)
+                       reg |= TGCR_HSYNC_EN;
+               else
+                       reg &= ~TGCR_HSYNC_EN;
+               __raw_writel(reg, tsc_base + TGCR);
+       }
+}
+EXPORT_SYMBOL(imx_adc_set_hsync);
+
+/*!
+ * This is the suspend of power management for the i.MX ADC API.
+ * It supports SAVE and POWER_DOWN state.
+ *
+ * @param        pdev           the device
+ * @param        state          the state
+ *
+ * @return       This function returns 0 if successful.
+ */
+static int imx_adc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       unsigned long reg;
+
+       /* Config idle for 4-wire */
+       reg = TSC_4WIRE_PRECHARGE;
+       __raw_writel(reg, tsc_base + TICR);
+
+       reg = TSC_4WIRE_TOUCH_DETECT;
+       __raw_writel(reg, tsc_base + TICR);
+
+       /* enable deep sleep wake up */
+       reg = __raw_readl(tsc_base + TGCR);
+       reg |= TGCR_SLPC;
+       __raw_writel(reg, tsc_base + TGCR);
+
+       /* mask pen down and pen down irq */
+       reg = __raw_readl(tsc_base + TCQCR);
+       reg |= CQCR_PD_MSK;
+       __raw_writel(reg, tsc_base + TCQCR);
+       reg = __raw_readl(tsc_base + TCQMR);
+       reg |= TCQMR_PD_IRQ_MSK;
+       __raw_writel(reg, tsc_base + TCQMR);
+
+       /* Set power mode to off */
+       reg = __raw_readl(tsc_base + TGCR) & ~TGCR_POWER_MASK;
+       reg |= TGCR_POWER_OFF;
+       __raw_writel(reg, tsc_base + TGCR);
+
+       if (device_may_wakeup(&pdev->dev)) {
+               enable_irq_wake(adc_data->irq);
+       } else {
+               suspend_flag = 1;
+               tsc_clk_disable();
+       }
+       return 0;
+};
+
+/*!
+ * This is the resume of power management for the i.MX adc API.
+ * It supports RESTORE state.
+ *
+ * @param        pdev           the device
+ *
+ * @return       This function returns 0 if successful.
+ */
+static int imx_adc_resume(struct platform_device *pdev)
+{
+       unsigned long reg;
+
+       if (device_may_wakeup(&pdev->dev)) {
+               disable_irq_wake(adc_data->irq);
+       } else {
+               suspend_flag = 0;
+               tsc_clk_enable();
+               while (swait > 0) {
+                       swait--;
+                       wake_up_interruptible(&suspendq);
+               }
+       }
+
+       /* recover power mode */
+       reg = __raw_readl(tsc_base + TGCR) & ~TGCR_POWER_MASK;
+       reg |= TGCR_POWER_SAVE;
+       __raw_writel(reg, tsc_base + TGCR);
+
+       return 0;
+}
+
+/*!
+ * This function implements the open method on an i.MX ADC device.
+ *
+ * @param        inode       pointer on the node
+ * @param        file        pointer on the file
+ * @return       This function returns 0.
+ */
+static int imx_adc_open(struct inode *inode, struct file *file)
+{
+       while (suspend_flag) {
+               swait++;
+               /* Block if the device is suspended */
+               if (wait_event_interruptible(suspendq, !suspend_flag))
+                       return -ERESTARTSYS;
+       }
+       pr_debug("imx_adc : imx_adc_open()\n");
+       return 0;
+}
+
+/*!
+ * This function implements the release method on an i.MX ADC device.
+ *
+ * @param        inode       pointer on the node
+ * @param        file        pointer on the file
+ * @return       This function returns 0.
+ */
+static int imx_adc_free(struct inode *inode, struct file *file)
+{
+       pr_debug("imx_adc : imx_adc_free()\n");
+       return 0;
+}
+
+/*!
+ * This function initializes all ADC registers with default values. This
+ * function also registers the interrupt events.
+ *
+ * @return       This function returns IMX_ADC_SUCCESS if successful.
+ */
+int imx_adc_init(void)
+{
+       unsigned long reg;
+
+       pr_debug("imx_adc_init()\n");
+
+       if (suspend_flag)
+               return -EBUSY;
+
+       tsc_clk_enable();
+
+       /* Reset */
+       tsc_self_reset();
+
+       /* Internal reference */
+       tsc_intref_enable();
+
+       /* Set power mode */
+       reg = __raw_readl(tsc_base + TGCR) & ~TGCR_POWER_MASK;
+       reg |= TGCR_POWER_SAVE;
+       __raw_writel(reg, tsc_base + TGCR);
+
+       imx_tsc_init();
+
+       return IMX_ADC_SUCCESS;
+}
+EXPORT_SYMBOL(imx_adc_init);
+
+/*!
+ * This function disables the ADC, de-registers the interrupt events.
+ *
+ * @return       This function returns IMX_ADC_SUCCESS if successful.
+ */
+enum IMX_ADC_STATUS imx_adc_deinit(void)
+{
+       pr_debug("imx_adc_deinit()\n");
+
+       return IMX_ADC_SUCCESS;
+}
+EXPORT_SYMBOL(imx_adc_deinit);
+
+/*!
+ * This function triggers a conversion and returns one sampling result of one
+ * channel.
+ *
+ * @param        channel   The channel to be sampled
+ * @param        result    The pointer to the conversion result. The memory
+ *                         should be allocated by the caller of this function.
+ *
+ * @return       This function returns IMX_ADC_SUCCESS if successful.
+ */
+enum IMX_ADC_STATUS imx_adc_convert(enum t_channel channel,
+                                   unsigned short *result)
+{
+       unsigned long reg;
+       int lastitemid;
+       struct t_touch_screen touch_sample;
+
+       switch (channel) {
+
+       case TS_X_POS:
+               imx_adc_get_touch_sample(&touch_sample, 0);
+               result[0] = touch_sample.x_position;
+
+               /* if no pen down ,recover the register configuration */
+               if (touch_sample.contact_resistance == 0) {
+                       reg = __raw_readl(tsc_base + TCQCR);
+                       reg &= ~CQCR_QSM_MASK;
+                       reg |= CQCR_QSM_PEN;
+                       __raw_writel(reg, tsc_base + TCQCR);
+
+                       reg = __raw_readl(tsc_base + TCQMR);
+                       reg &= ~TCQMR_PD_IRQ_MSK;
+                       __raw_writel(reg, tsc_base + TCQMR);
+               }
+               break;
+
+       case TS_Y_POS:
+               imx_adc_get_touch_sample(&touch_sample, 0);
+               result[1] = touch_sample.y_position;
+
+               /* if no pen down ,recover the register configuration */
+               if (touch_sample.contact_resistance == 0) {
+                       reg = __raw_readl(tsc_base + TCQCR);
+                       reg &= ~CQCR_QSM_MASK;
+                       reg |= CQCR_QSM_PEN;
+                       __raw_writel(reg, tsc_base + TCQCR);
+
+                       reg = __raw_readl(tsc_base + TCQMR);
+                       reg &= ~TCQMR_PD_IRQ_MSK;
+                       __raw_writel(reg, tsc_base + TCQMR);
+               }
+               break;
+
+       case GER_PURPOSE_ADC0:
+               down(&general_convert_mutex);
+
+               lastitemid = 0;
+               reg = (0xf << CQCR_FIFOWATERMARK_SHIFT) |
+                     (lastitemid << CQCR_LAST_ITEM_ID_SHIFT) | CQCR_QSM_FQS;
+               __raw_writel(reg, tsc_base + GCQCR);
+
+               reg = TSC_GENERAL_ADC_GCC0;
+               reg |= (3 << CC_NOS_SHIFT) | (16 << CC_SETTLING_TIME_SHIFT);
+               __raw_writel(reg, tsc_base + GCC0);
+
+               imx_adc_read_general(result);
+               up(&general_convert_mutex);
+               break;
+
+       case GER_PURPOSE_ADC1:
+               down(&general_convert_mutex);
+
+               lastitemid = 0;
+               reg = (0xf << CQCR_FIFOWATERMARK_SHIFT) |
+                     (lastitemid << CQCR_LAST_ITEM_ID_SHIFT) | CQCR_QSM_FQS;
+               __raw_writel(reg, tsc_base + GCQCR);
+
+               reg = TSC_GENERAL_ADC_GCC1;
+               reg |= (3 << CC_NOS_SHIFT) | (16 << CC_SETTLING_TIME_SHIFT);
+               __raw_writel(reg, tsc_base + GCC0);
+
+               imx_adc_read_general(result);
+               up(&general_convert_mutex);
+               break;
+
+       case GER_PURPOSE_ADC2:
+               down(&general_convert_mutex);
+
+               lastitemid = 0;
+               reg = (0xf << CQCR_FIFOWATERMARK_SHIFT) |
+                     (lastitemid << CQCR_LAST_ITEM_ID_SHIFT) | CQCR_QSM_FQS;
+               __raw_writel(reg, tsc_base + GCQCR);
+
+               reg = TSC_GENERAL_ADC_GCC2;
+               reg |= (3 << CC_NOS_SHIFT) | (16 << CC_SETTLING_TIME_SHIFT);
+               __raw_writel(reg, tsc_base + GCC0);
+
+               imx_adc_read_general(result);
+               up(&general_convert_mutex);
+               break;
+
+       case GER_PURPOSE_MULTICHNNEL:
+               down(&general_convert_mutex);
+
+               reg = TSC_GENERAL_ADC_GCC0;
+               reg |= (3 << CC_NOS_SHIFT) | (16 << CC_SETTLING_TIME_SHIFT);
+               __raw_writel(reg, tsc_base + GCC0);
+
+               reg = TSC_GENERAL_ADC_GCC1;
+               reg |= (3 << CC_NOS_SHIFT) | (16 << CC_SETTLING_TIME_SHIFT);
+               __raw_writel(reg, tsc_base + GCC1);
+
+               reg = TSC_GENERAL_ADC_GCC2;
+               reg |= (3 << CC_NOS_SHIFT) | (16 << CC_SETTLING_TIME_SHIFT);
+               __raw_writel(reg, tsc_base + GCC2);
+
+               reg = (GCQ_ITEM_GCC2 << GCQ_ITEM2_SHIFT) |
+                     (GCQ_ITEM_GCC1 << GCQ_ITEM1_SHIFT) |
+                     (GCQ_ITEM_GCC0 << GCQ_ITEM0_SHIFT);
+               __raw_writel(reg, tsc_base + GCQ_ITEM_7_0);
+
+               lastitemid = 2;
+               reg = (0xf << CQCR_FIFOWATERMARK_SHIFT) |
+                     (lastitemid << CQCR_LAST_ITEM_ID_SHIFT) | CQCR_QSM_FQS;
+               __raw_writel(reg, tsc_base + GCQCR);
+
+               imx_adc_read_general(result);
+               up(&general_convert_mutex);
+               break;
+       default:
+               pr_debug("%s: bad channel number\n", __func__);
+               return IMX_ADC_ERROR;
+       }
+
+       return IMX_ADC_SUCCESS;
+}
+EXPORT_SYMBOL(imx_adc_convert);
+
+/*!
+ * This function triggers a conversion and returns sampling results of each
+ * specified channel.
+ *
+ * @param        channels  This input parameter is bitmap to specify channels
+ *                         to be sampled.
+ * @param        result    The pointer to array to store sampling results.
+ *                         The memory should be allocated by the caller of this
+ *                         function.
+ *
+ * @return       This function returns IMX_ADC_SUCCESS if successful.
+ */
+enum IMX_ADC_STATUS imx_adc_convert_multichnnel(enum t_channel channels,
+                                               unsigned short *result)
+{
+       imx_adc_convert(GER_PURPOSE_MULTICHNNEL, result);
+       return IMX_ADC_SUCCESS;
+}
+EXPORT_SYMBOL(imx_adc_convert_multichnnel);
+
+/*!
+ * This function implements IOCTL controls on an i.MX ADC device.
+ *
+ * @param        inode       pointer on the node
+ * @param        file        pointer on the file
+ * @param        cmd         the command
+ * @param        arg         the parameter
+ * @return       This function returns 0 if successful.
+ */
+static int imx_adc_ioctl(struct inode *inode, struct file *file,
+                        unsigned int cmd, unsigned long arg)
+{
+       struct t_adc_convert_param *convert_param;
+
+       if ((_IOC_TYPE(cmd) != 'p') && (_IOC_TYPE(cmd) != 'D'))
+               return -ENOTTY;
+
+       while (suspend_flag) {
+               swait++;
+               /* Block if the device is suspended */
+               if (wait_event_interruptible(suspendq, !suspend_flag))
+                       return -ERESTARTSYS;
+       }
+
+       switch (cmd) {
+       case IMX_ADC_INIT:
+               pr_debug("init adc\n");
+               CHECK_ERROR(imx_adc_init());
+               break;
+
+       case IMX_ADC_DEINIT:
+               pr_debug("deinit adc\n");
+               CHECK_ERROR(imx_adc_deinit());
+               break;
+
+       case IMX_ADC_CONVERT:
+               convert_param = kmalloc(sizeof(*convert_param), GFP_KERNEL);
+               if (convert_param == NULL)
+                       return -ENOMEM;
+               if (copy_from_user(convert_param,
+                                  (struct t_adc_convert_param *)arg,
+                                  sizeof(*convert_param))) {
+                       kfree(convert_param);
+                       return -EFAULT;
+               }
+               CHECK_ERROR_KFREE(imx_adc_convert(convert_param->channel,
+                                                 convert_param->result),
+                                 (kfree(convert_param)));
+
+               if (copy_to_user((struct t_adc_convert_param *)arg,
+                                convert_param, sizeof(*convert_param))) {
+                       kfree(convert_param);
+                       return -EFAULT;
+               }
+               kfree(convert_param);
+               break;
+
+       case IMX_ADC_CONVERT_MULTICHANNEL:
+               convert_param = kmalloc(sizeof(*convert_param), GFP_KERNEL);
+               if (convert_param == NULL)
+                       return -ENOMEM;
+               if (copy_from_user(convert_param,
+                                  (struct t_adc_convert_param *)arg,
+                                  sizeof(*convert_param))) {
+                       kfree(convert_param);
+                       return -EFAULT;
+               }
+               CHECK_ERROR_KFREE(imx_adc_convert_multichnnel
+                                 (convert_param->channel,
+                                  convert_param->result),
+                                 (kfree(convert_param)));
+
+               if (copy_to_user((struct t_adc_convert_param *)arg,
+                                convert_param, sizeof(*convert_param))) {
+                       kfree(convert_param);
+                       return -EFAULT;
+               }
+               kfree(convert_param);
+               break;
+
+       default:
+               pr_debug("imx_adc_ioctl: unsupported ioctl command 0x%x\n",
+                        cmd);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static struct file_operations imx_adc_fops = {
+       .owner = THIS_MODULE,
+       .ioctl = imx_adc_ioctl,
+       .open = imx_adc_open,
+       .release = imx_adc_free,
+};
+
+static int imx_adc_module_probe(struct platform_device *pdev)
+{
+       int ret = 0;
+       int retval;
+       struct device *temp_class;
+       struct resource *res;
+       void __iomem *base;
+
+       /* ioremap the base address */
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (res == NULL) {
+               dev_err(&pdev->dev, "No TSC base address provided\n");
+               goto err_out0;
+       }
+       base = ioremap(res->start, res->end - res->start);
+       if (base == NULL) {
+               dev_err(&pdev->dev, "failed to rebase TSC base address\n");
+               goto err_out0;
+       }
+       tsc_base = (unsigned long)base;
+
+       /* create the chrdev */
+       imx_adc_major = register_chrdev(0, "imx_adc", &imx_adc_fops);
+
+       if (imx_adc_major < 0) {
+               dev_err(&pdev->dev, "Unable to get a major for imx_adc\n");
+               return imx_adc_major;
+       }
+       init_waitqueue_head(&suspendq);
+       init_waitqueue_head(&tsq);
+
+       imx_adc_class = class_create(THIS_MODULE, "imx_adc");
+       if (IS_ERR(imx_adc_class)) {
+               dev_err(&pdev->dev, "Error creating imx_adc class.\n");
+               ret = PTR_ERR(imx_adc_class);
+               goto err_out1;
+       }
+
+       temp_class = device_create(imx_adc_class, NULL,
+                                  MKDEV(imx_adc_major, 0), NULL, "imx_adc");
+       if (IS_ERR(temp_class)) {
+               dev_err(&pdev->dev, "Error creating imx_adc class device.\n");
+               ret = PTR_ERR(temp_class);
+               goto err_out2;
+       }
+
+       adc_data = kmalloc(sizeof(struct imx_adc_data), GFP_KERNEL);
+       if (adc_data == NULL)
+               return -ENOMEM;
+       adc_data->irq = platform_get_irq(pdev, 0);
+       retval = request_irq(adc_data->irq, imx_adc_interrupt,
+                            0, MOD_NAME, MOD_NAME);
+       if (retval) {
+               return retval;
+       }
+       adc_data->adc_clk = clk_get(&pdev->dev, "tchscrn_clk");
+
+       ret = imx_adc_init();
+
+       if (ret != IMX_ADC_SUCCESS) {
+               dev_err(&pdev->dev, "Error in imx_adc_init.\n");
+               goto err_out4;
+       }
+       imx_adc_ready = 1;
+
+       /* By default, devices should wakeup if they can */
+       /* So TouchScreen is set as "should wakeup" as it can */
+       device_init_wakeup(&pdev->dev, 1);
+
+       pr_info("i.MX ADC at 0x%x irq %d\n", (unsigned int)res->start,
+               adc_data->irq);
+       return ret;
+
+err_out4:
+       device_destroy(imx_adc_class, MKDEV(imx_adc_major, 0));
+err_out2:
+       class_destroy(imx_adc_class);
+err_out1:
+       unregister_chrdev(imx_adc_major, "imx_adc");
+err_out0:
+       return ret;
+}
+
+static int imx_adc_module_remove(struct platform_device *pdev)
+{
+       imx_adc_ready = 0;
+       imx_adc_deinit();
+       device_destroy(imx_adc_class, MKDEV(imx_adc_major, 0));
+       class_destroy(imx_adc_class);
+       unregister_chrdev(imx_adc_major, "imx_adc");
+       free_irq(adc_data->irq, MOD_NAME);
+       kfree(adc_data);
+       pr_debug("i.MX ADC successfully removed\n");
+       return 0;
+}
+
+static struct platform_driver imx_adc_driver = {
+       .driver = {
+                  .name = "imx_adc",
+                  },
+       .suspend = imx_adc_suspend,
+       .resume = imx_adc_resume,
+       .probe = imx_adc_module_probe,
+       .remove = imx_adc_module_remove,
+};
+
+/*
+ * Initialization and Exit
+ */
+static int __init imx_adc_module_init(void)
+{
+       pr_debug("i.MX ADC driver loading...\n");
+       return platform_driver_register(&imx_adc_driver);
+}
+
+static void __exit imx_adc_module_exit(void)
+{
+       platform_driver_unregister(&imx_adc_driver);
+       pr_debug("i.MX ADC driver successfully unloaded\n");
+}
+
+/*
+ * Module entry points
+ */
+
+module_init(imx_adc_module_init);
+module_exit(imx_adc_module_exit);
+
+MODULE_DESCRIPTION("i.MX ADC device driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/adc/imx_adc_reg.h b/drivers/mxc/adc/imx_adc_reg.h
new file mode 100644 (file)
index 0000000..05c0a19
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * Copyright 2009-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU Lesser General
+ * Public License.  You may obtain a copy of the GNU Lesser General
+ * Public License Version 2.1 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/lgpl-license.html
+ * http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#ifndef __IMX_ADC_H__
+#define __IMX_ADC_H__
+
+/* TSC General Config Register */
+#define TGCR                  0x000
+#define TGCR_IPG_CLK_EN       (1 << 0)
+#define TGCR_TSC_RST          (1 << 1)
+#define TGCR_FUNC_RST         (1 << 2)
+#define TGCR_SLPC             (1 << 4)
+#define TGCR_STLC             (1 << 5)
+#define TGCR_HSYNC_EN         (1 << 6)
+#define TGCR_HSYNC_POL        (1 << 7)
+#define TGCR_POWERMODE_SHIFT  8
+#define TGCR_POWER_OFF        (0x0 << TGCR_POWERMODE_SHIFT)
+#define TGCR_POWER_SAVE       (0x1 << TGCR_POWERMODE_SHIFT)
+#define TGCR_POWER_ON         (0x3 << TGCR_POWERMODE_SHIFT)
+#define TGCR_POWER_MASK       (0x3 << TGCR_POWERMODE_SHIFT)
+#define TGCR_INTREFEN         (1 << 10)
+#define TGCR_ADCCLKCFG_SHIFT  16
+#define TGCR_PD_EN            (1 << 23)
+#define TGCR_PDB_EN           (1 << 24)
+#define TGCR_PDBTIME_SHIFT    25
+#define TGCR_PDBTIME128       (0x3f << TGCR_PDBTIME_SHIFT)
+#define TGCR_PDBTIME_MASK     (0x7f << TGCR_PDBTIME_SHIFT)
+
+/* TSC General Status Register */
+#define TGSR                  0x004
+#define TCQ_INT               (1 << 0)
+#define GCQ_INT               (1 << 1)
+#define SLP_INT               (1 << 2)
+#define TCQ_DMA               (1 << 16)
+#define GCQ_DMA               (1 << 17)
+
+/* TSC IDLE Config Register */
+#define TICR                  0x008
+
+/* TouchScreen Convert Queue FIFO Register */
+#define TCQFIFO               0x400
+/* TouchScreen Convert Queue Control Register */
+#define TCQCR                 0x404
+#define CQCR_QSM_SHIFT        0
+#define CQCR_QSM_STOP         (0x0 << CQCR_QSM_SHIFT)
+#define CQCR_QSM_PEN          (0x1 << CQCR_QSM_SHIFT)
+#define CQCR_QSM_FQS          (0x2 << CQCR_QSM_SHIFT)
+#define CQCR_QSM_FQS_PEN      (0x3 << CQCR_QSM_SHIFT)
+#define CQCR_QSM_MASK         (0x3 << CQCR_QSM_SHIFT)
+#define CQCR_FQS              (1 << 2)
+#define CQCR_RPT              (1 << 3)
+#define CQCR_LAST_ITEM_ID_SHIFT   4
+#define CQCR_LAST_ITEM_ID_MASK    (0xf << CQCR_LAST_ITEM_ID_SHIFT)
+#define CQCR_FIFOWATERMARK_SHIFT  8
+#define CQCR_FIFOWATERMARK_MASK   (0xf << CQCR_FIFOWATERMARK_SHIFT)
+#define CQCR_REPEATWAIT_SHIFT     12
+#define CQCR_REPEATWAIT_MASK      (0xf << CQCR_REPEATWAIT_SHIFT)
+#define CQCR_QRST             (1 << 16)
+#define CQCR_FRST             (1 << 17)
+#define CQCR_PD_MSK           (1 << 18)
+#define CQCR_PD_CFG           (1 << 19)
+
+/* TouchScreen Convert Queue Status Register */
+#define TCQSR                 0x408
+#define CQSR_PD               (1 << 0)
+#define CQSR_EOQ              (1 << 1)
+#define CQSR_FOR              (1 << 4)
+#define CQSR_FUR              (1 << 5)
+#define CQSR_FER              (1 << 6)
+#define CQSR_EMPT             (1 << 13)
+#define CQSR_FULL             (1 << 14)
+#define CQSR_FDRY             (1 << 15)
+
+/* TouchScreen Convert Queue Mask Register */
+#define TCQMR                 0x40c
+#define TCQMR_PD_IRQ_MSK      (1 << 0)
+#define TCQMR_EOQ_IRQ_MSK     (1 << 1)
+#define TCQMR_FOR_IRQ_MSK     (1 << 4)
+#define TCQMR_FUR_IRQ_MSK     (1 << 5)
+#define TCQMR_FER_IRQ_MSK     (1 << 6)
+#define TCQMR_PD_DMA_MSK      (1 << 16)
+#define TCQMR_EOQ_DMA_MSK     (1 << 17)
+#define TCQMR_FOR_DMA_MSK     (1 << 20)
+#define TCQMR_FUR_DMA_MSK     (1 << 21)
+#define TCQMR_FER_DMA_MSK     (1 << 22)
+#define TCQMR_FDRY_DMA_MSK    (1 << 31)
+
+/* TouchScreen Convert Queue ITEM 7~0 */
+#define TCQ_ITEM_7_0          0x420
+
+/* TouchScreen Convert Queue ITEM 15~8 */
+#define TCQ_ITEM_15_8         0x424
+
+#define TCQ_ITEM7_SHIFT       28
+#define TCQ_ITEM6_SHIFT       24
+#define TCQ_ITEM5_SHIFT       20
+#define TCQ_ITEM4_SHIFT       16
+#define TCQ_ITEM3_SHIFT       12
+#define TCQ_ITEM2_SHIFT       8
+#define TCQ_ITEM1_SHIFT       4
+#define TCQ_ITEM0_SHIFT       0
+
+#define TCQ_ITEM_TCC0         0x0
+#define TCQ_ITEM_TCC1         0x1
+#define TCQ_ITEM_TCC2         0x2
+#define TCQ_ITEM_TCC3         0x3
+#define TCQ_ITEM_TCC4         0x4
+#define TCQ_ITEM_TCC5         0x5
+#define TCQ_ITEM_TCC6         0x6
+#define TCQ_ITEM_TCC7         0x7
+#define TCQ_ITEM_GCC7         0x8
+#define TCQ_ITEM_GCC6         0x9
+#define TCQ_ITEM_GCC5         0xa
+#define TCQ_ITEM_GCC4         0xb
+#define TCQ_ITEM_GCC3         0xc
+#define TCQ_ITEM_GCC2         0xd
+#define TCQ_ITEM_GCC1         0xe
+#define TCQ_ITEM_GCC0         0xf
+
+/* TouchScreen Convert Config 0-7 */
+#define TCC0                  0x440
+#define TCC1                  0x444
+#define TCC2                  0x448
+#define TCC3                  0x44c
+#define TCC4                  0x450
+#define TCC5                  0x454
+#define TCC6                  0x458
+#define TCC7                  0x45c
+#define CC_PEN_IACK           (1 << 1)
+#define CC_SEL_REFN_SHIFT     2
+#define CC_SEL_REFN_YNLR      (0x1 << CC_SEL_REFN_SHIFT)
+#define CC_SEL_REFN_AGND      (0x2 << CC_SEL_REFN_SHIFT)
+#define CC_SEL_REFN_MASK      (0x3 << CC_SEL_REFN_SHIFT)
+#define CC_SELIN_SHIFT        4
+#define CC_SELIN_XPUL         (0x0 << CC_SELIN_SHIFT)
+#define CC_SELIN_YPLL         (0x1 << CC_SELIN_SHIFT)
+#define CC_SELIN_XNUR         (0x2 << CC_SELIN_SHIFT)
+#define CC_SELIN_YNLR         (0x3 << CC_SELIN_SHIFT)
+#define CC_SELIN_WIPER        (0x4 << CC_SELIN_SHIFT)
+#define CC_SELIN_INAUX0       (0x5 << CC_SELIN_SHIFT)
+#define CC_SELIN_INAUX1       (0x6 << CC_SELIN_SHIFT)
+#define CC_SELIN_INAUX2       (0x7 << CC_SELIN_SHIFT)
+#define CC_SELIN_MASK         (0x7 << CC_SELIN_SHIFT)
+#define CC_SELREFP_SHIFT      7
+#define CC_SELREFP_YPLL       (0x0 << CC_SELREFP_SHIFT)
+#define CC_SELREFP_XPUL       (0x1 << CC_SELREFP_SHIFT)
+#define CC_SELREFP_EXT        (0x2 << CC_SELREFP_SHIFT)
+#define CC_SELREFP_INT        (0x3 << CC_SELREFP_SHIFT)
+#define CC_SELREFP_MASK       (0x3 << CC_SELREFP_SHIFT)
+#define CC_XPULSW             (1 << 9)
+#define CC_XNURSW_SHIFT       10
+#define CC_XNURSW_HIGH        (0x0 << CC_XNURSW_SHIFT)
+#define CC_XNURSW_OFF         (0x1 << CC_XNURSW_SHIFT)
+#define CC_XNURSW_LOW         (0x3 << CC_XNURSW_SHIFT)
+#define CC_XNURSW_MASK        (0x3 << CC_XNURSW_SHIFT)
+#define CC_YPLLSW_SHIFT       12
+#define CC_YPLLSW_MASK        (0x3 << CC_YPLLSW_SHIFT)
+#define CC_YNLRSW             (1 << 14)
+#define CC_WIPERSW            (1 << 15)
+#define CC_NOS_SHIFT          16
+#define CC_YPLLSW_HIGH        (0x0 << CC_NOS_SHIFT)
+#define CC_YPLLSW_OFF         (0x1 << CC_NOS_SHIFT)
+#define CC_YPLLSW_LOW         (0x3 << CC_NOS_SHIFT)
+#define CC_NOS_MASK           (0xf << CC_NOS_SHIFT)
+#define CC_IGS                (1 << 20)
+#define CC_SETTLING_TIME_SHIFT 24
+#define CC_SETTLING_TIME_MASK (0xff << CC_SETTLING_TIME_SHIFT)
+
+#define TSC_4WIRE_PRECHARGE    0x158c
+#define TSC_4WIRE_TOUCH_DETECT 0x578e
+
+#define TSC_4WIRE_X_MEASUMENT  0x1c90
+#define TSC_4WIRE_Y_MEASUMENT  0x4604
+
+#define TSC_GENERAL_ADC_GCC0   0x17dc
+#define TSC_GENERAL_ADC_GCC1   0x17ec
+#define TSC_GENERAL_ADC_GCC2   0x17fc
+
+/* GeneralADC Convert Queue FIFO Register */
+#define GCQFIFO                0x800
+#define GCQFIFO_ADCOUT_SHIFT   4
+#define GCQFIFO_ADCOUT_MASK    (0xfff << GCQFIFO_ADCOUT_SHIFT)
+/* GeneralADC Convert Queue Control Register */
+#define GCQCR                  0x804
+/* GeneralADC Convert Queue Status Register */
+#define GCQSR                  0x808
+/* GeneralADC Convert Queue Mask Register */
+#define GCQMR                  0x80c
+
+/* GeneralADC Convert Queue ITEM 7~0 */
+#define GCQ_ITEM_7_0           0x820
+/* GeneralADC Convert Queue ITEM 15~8 */
+#define GCQ_ITEM_15_8          0x824
+
+#define GCQ_ITEM7_SHIFT        28
+#define GCQ_ITEM6_SHIFT        24
+#define GCQ_ITEM5_SHIFT        20
+#define GCQ_ITEM4_SHIFT        16
+#define GCQ_ITEM3_SHIFT        12
+#define GCQ_ITEM2_SHIFT        8
+#define GCQ_ITEM1_SHIFT        4
+#define GCQ_ITEM0_SHIFT        0
+
+#define GCQ_ITEM_GCC0          0x0
+#define GCQ_ITEM_GCC1          0x1
+#define GCQ_ITEM_GCC2          0x2
+#define GCQ_ITEM_GCC3          0x3
+
+/* GeneralADC Convert Config 0-7 */
+#define GCC0                   0x840
+#define GCC1                   0x844
+#define GCC2                   0x848
+#define GCC3                   0x84c
+#define GCC4                   0x850
+#define GCC5                   0x854
+#define GCC6                   0x858
+#define GCC7                   0x85c
+
+/* TSC Test Register R/W */
+#define TTR                    0xc00
+/* TSC Monitor Register 1, 2 */
+#define MNT1                   0xc04
+#define MNT2                   0xc04
+
+#define DETECT_ITEM_ID_1       1
+#define DETECT_ITEM_ID_2       5
+#define TS_X_ITEM_ID           2
+#define TS_Y_ITEM_ID           3
+#define TSI_DATA               1
+#define FQS_DATA               0
+
+#endif                         /* __IMX_ADC_H__ */
diff --git a/drivers/mxc/asrc/Kconfig b/drivers/mxc/asrc/Kconfig
new file mode 100644 (file)
index 0000000..91c6570
--- /dev/null
@@ -0,0 +1,13 @@
+#
+# ASRC configuration
+#
+
+menu "MXC Asynchronous Sample Rate Converter support"
+
+config MXC_ASRC
+       tristate "ASRC support"
+       depends on ARCH_MX35 || ARCH_MX53
+       ---help---
+         Say Y to get the ASRC service.
+
+endmenu
diff --git a/drivers/mxc/asrc/Makefile b/drivers/mxc/asrc/Makefile
new file mode 100644 (file)
index 0000000..7e9aba3
--- /dev/null
@@ -0,0 +1,4 @@
+#
+# Makefile for the kernel Asynchronous Sample Rate Converter driver
+#
+obj-$(CONFIG_MXC_ASRC) += mxc_asrc.o
diff --git a/drivers/mxc/asrc/mxc_asrc.c b/drivers/mxc/asrc/mxc_asrc.c
new file mode 100644 (file)
index 0000000..932e7bb
--- /dev/null
@@ -0,0 +1,1728 @@
+/*
+ * 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 mxc_asrc.c
+ *
+ * @brief MXC Asynchronous Sample Rate Converter
+ *
+ * @ingroup SOUND
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/pagemap.h>
+#include <linux/vmalloc.h>
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/dma-mapping.h>
+#include <linux/mxc_asrc.h>
+#include <linux/fsl_devices.h>
+#include <asm/irq.h>
+#include <asm/memory.h>
+#include <mach/dma.h>
+
+static int asrc_major;
+static struct class *asrc_class;
+#define ASRC_PROC_PATH        "driver/asrc"
+
+#define ASRC_RATIO_DECIMAL_DEPTH 26
+
+DEFINE_SPINLOCK(data_lock);
+DEFINE_SPINLOCK(input_int_lock);
+DEFINE_SPINLOCK(output_int_lock);
+
+#define AICPA          0       /* Input Clock Divider A Offset */
+#define AICDA          3       /* Input Clock Prescaler A Offset */
+#define AICPB           6      /* Input Clock Divider B Offset */
+#define AICDB           9      /* Input Clock Prescaler B Offset */
+#define AOCPA           12     /* Output Clock Divider A Offset */
+#define AOCDA           15     /* Output Clock Prescaler A Offset */
+#define AOCPB           18     /* Output Clock Divider B Offset */
+#define AOCDB           21     /* Output Clock Prescaler B Offset */
+#define AICPC           0      /* Input Clock Divider C Offset */
+#define AICDC           3      /* Input Clock Prescaler C Offset */
+#define AOCDC           6      /* Output Clock Prescaler C Offset */
+#define AOCPC           9      /* Output Clock Divider C Offset */
+
+char *asrc_pair_id[] = {
+       [0] = "ASRC RX PAIR A",
+       [1] = "ASRC TX PAIR A",
+       [2] = "ASRC RX PAIR B",
+       [3] = "ASRC TX PAIR B",
+       [4] = "ASRC RX PAIR C",
+       [5] = "ASRC TX PAIR C",
+};
+
+enum asrc_status {
+       ASRC_ASRSTR_AIDEA = 0x01,
+       ASRC_ASRSTR_AIDEB = 0x02,
+       ASRC_ASRSTR_AIDEC = 0x04,
+       ASRC_ASRSTR_AODFA = 0x08,
+       ASRC_ASRSTR_AODFB = 0x10,
+       ASRC_ASRSTR_AODFC = 0x20,
+       ASRC_ASRSTR_AOLE = 0x40,
+       ASRC_ASRSTR_FPWT = 0x80,
+       ASRC_ASRSTR_AIDUA = 0x100,
+       ASRC_ASRSTR_AIDUB = 0x200,
+       ASRC_ASRSTR_AIDUC = 0x400,
+       ASRC_ASRSTR_AODOA = 0x800,
+       ASRC_ASRSTR_AODOB = 0x1000,
+       ASRC_ASRSTR_AODOC = 0x2000,
+       ASRC_ASRSTR_AIOLA = 0x4000,
+       ASRC_ASRSTR_AIOLB = 0x8000,
+       ASRC_ASRSTR_AIOLC = 0x10000,
+       ASRC_ASRSTR_AOOLA = 0x20000,
+       ASRC_ASRSTR_AOOLB = 0x40000,
+       ASRC_ASRSTR_AOOLC = 0x80000,
+       ASRC_ASRSTR_ATQOL = 0x100000,
+       ASRC_ASRSTR_DSLCNT = 0x200000,
+};
+
+/* Sample rates are aligned with that defined in pcm.h file */
+static const unsigned char asrc_process_table[][8][2] = {
+       /* 32kHz 44.1kHz 48kHz   64kHz   88.2kHz 96kHz  176kHz   192kHz */
+/*5512Hz*/
+       {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},},
+/*8kHz*/
+       {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},},
+/*11025Hz*/
+       {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},},
+/*16kHz*/
+       {{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},},
+/*22050Hz*/
+       {{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},},
+/*32kHz*/
+       {{0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0},},
+/*44.1kHz*/
+       {{0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},},
+/*48kHz*/
+       {{0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},},
+/*64kHz*/
+       {{1, 2}, {0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0},},
+/*88.2kHz*/
+       {{1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},},
+/*96kHz*/
+       {{1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},},
+/*176kHz*/
+       {{2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},},
+/*192kHz*/
+       {{2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},},
+};
+
+static const unsigned char asrc_divider_table[] = {
+/*5500Hz 8kHz 11025Hz 16kHz 22050kHz 32kHz 44.1kHz 48kHz 64kHz 88.2kHz 96kHz 176400Hz 192kHz*/
+       0x07, 0x15, 0x06, 0x14, 0x05, 0x13, 0x04, 0x04, 0x12, 0x03, 0x03, 0x02,
+       0x02,
+};
+
+static struct asrc_data *g_asrc_data;
+static struct proc_dir_entry *proc_asrc;
+static unsigned long asrc_vrt_base_addr;
+static struct mxc_asrc_platform_data *mxc_asrc_data;
+
+/* The following tables map the relationship between asrc_inclk/asrc_outclk in
+ * mxc_asrc.h and the registers of ASRCSR
+ */
+static unsigned char input_clk_map_v1[] = {
+       0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
+};
+
+static unsigned char output_clk_map_v1[] = {
+       0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
+};
+
+static unsigned char input_clk_map_v2[] = {
+       0, 1, 2, 3, 4, 5, 0xf, 0xf, 0xf, 8, 9, 0xa, 0xb, 0xc, 0xf, 0xd,
+};
+
+static unsigned char output_clk_map_v2[] = {
+       8, 9, 0xa, 0, 0xc, 0x5, 0xf, 0xf, 0, 1, 2, 0xf, 0xf, 4, 0xf, 0xd,
+};
+
+static unsigned char *input_clk_map, *output_clk_map;
+
+static int asrc_set_clock_ratio(enum asrc_pair_index index,
+                               int input_sample_rate, int output_sample_rate)
+{
+       int i;
+       int integ = 0;
+       unsigned long reg_val = 0;
+
+       if (output_sample_rate == 0)
+               return -1;
+       while (input_sample_rate >= output_sample_rate) {
+               input_sample_rate -= output_sample_rate;
+               integ++;
+       }
+       reg_val |= (integ << 26);
+
+       for (i = 1; i <= ASRC_RATIO_DECIMAL_DEPTH; i++) {
+               if ((input_sample_rate * 2) >= output_sample_rate) {
+                       reg_val |= (1 << (ASRC_RATIO_DECIMAL_DEPTH - i));
+                       input_sample_rate =
+                           input_sample_rate * 2 - output_sample_rate;
+               } else
+                       input_sample_rate = input_sample_rate << 1;
+
+               if (input_sample_rate == 0)
+                       break;
+       }
+
+       __raw_writel(reg_val,
+                    (asrc_vrt_base_addr + ASRC_ASRIDRLA_REG + (index << 3)));
+       __raw_writel((reg_val >> 24),
+                    (asrc_vrt_base_addr + ASRC_ASRIDRHA_REG + (index << 3)));
+       return 0;
+}
+
+static int asrc_set_process_configuration(enum asrc_pair_index index,
+                                         int input_sample_rate,
+                                         int output_sample_rate)
+{
+       int i = 0, j = 0;
+       unsigned long reg;
+       switch (input_sample_rate) {
+       case 5512:
+               i = 0;
+       case 8000:
+               i = 1;
+               break;
+       case 11025:
+               i = 2;
+               break;
+       case 16000:
+               i = 3;
+               break;
+       case 22050:
+               i = 4;
+               break;
+       case 32000:
+               i = 5;
+               break;
+       case 44100:
+               i = 6;
+               break;
+       case 48000:
+               i = 7;
+               break;
+       case 64000:
+               i = 8;
+               break;
+       case 88200:
+               i = 9;
+               break;
+       case 96000:
+               i = 10;
+               break;
+       case 176400:
+               i = 11;
+               break;
+       case 192000:
+               i = 12;
+               break;
+       default:
+               return -1;
+       }
+
+       switch (output_sample_rate) {
+       case 32000:
+               j = 0;
+               break;
+       case 44100:
+               j = 1;
+               break;
+       case 48000:
+               j = 2;
+               break;
+       case 64000:
+               j = 3;
+               break;
+       case 88200:
+               j = 4;
+               break;
+       case 96000:
+               j = 5;
+               break;
+       case 176400:
+               j = 6;
+               break;
+       case 192000:
+               j = 7;
+               break;
+       default:
+               return -1;
+       }
+
+       reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCFG_REG);
+       reg &= ~(0x0f << (6 + (index << 2)));
+       reg |=
+           ((asrc_process_table[i][j][0] << (6 + (index << 2))) |
+            (asrc_process_table[i][j][1] << (8 + (index << 2))));
+       __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCFG_REG);
+
+       return 0;
+}
+
+static int asrc_get_asrck_clock_divider(int sample_rate)
+{
+       int i = 0;
+       switch (sample_rate) {
+       case 5500:
+               i = 0;
+               break;
+       case 8000:
+               i = 1;
+               break;
+       case 11025:
+               i = 2;
+               break;
+       case 16000:
+               i = 3;
+               break;
+       case 22050:
+               i = 4;
+               break;
+       case 32000:
+               i = 5;
+               break;
+       case 44100:
+               i = 6;
+               break;
+       case 48000:
+               i = 7;
+               break;
+       case 64000:
+               i = 8;
+               break;
+       case 88200:
+               i = 9;
+               break;
+       case 96000:
+               i = 10;
+               break;
+       case 176400:
+               i = 11;
+               break;
+       case 192000:
+               i = 12;
+               break;
+       default:
+               return -1;
+       }
+
+       return asrc_divider_table[i];
+}
+
+int asrc_req_pair(int chn_num, enum asrc_pair_index *index)
+{
+       int err = 0;
+       unsigned long lock_flags;
+       spin_lock_irqsave(&data_lock, lock_flags);
+
+       if (chn_num > 2) {
+               if (g_asrc_data->asrc_pair[ASRC_PAIR_C].active
+                   || (chn_num > g_asrc_data->asrc_pair[ASRC_PAIR_C].chn_max))
+                       err = -EBUSY;
+               else {
+                       *index = ASRC_PAIR_C;
+                       g_asrc_data->asrc_pair[ASRC_PAIR_C].chn_num = chn_num;
+                       g_asrc_data->asrc_pair[ASRC_PAIR_C].active = 1;
+               }
+       } else {
+               if (g_asrc_data->asrc_pair[ASRC_PAIR_A].active ||
+                   (g_asrc_data->asrc_pair[ASRC_PAIR_A].chn_max == 0)) {
+                       if (g_asrc_data->asrc_pair[ASRC_PAIR_B].
+                           active
+                           || (g_asrc_data->asrc_pair[ASRC_PAIR_B].
+                               chn_max == 0))
+                               err = -EBUSY;
+                       else {
+                               *index = ASRC_PAIR_B;
+                               g_asrc_data->asrc_pair[ASRC_PAIR_B].chn_num = 2;
+                               g_asrc_data->asrc_pair[ASRC_PAIR_B].active = 1;
+                       }
+               } else {
+                       *index = ASRC_PAIR_A;
+                       g_asrc_data->asrc_pair[ASRC_PAIR_A].chn_num = 2;
+                       g_asrc_data->asrc_pair[ASRC_PAIR_A].active = 1;
+               }
+       }
+       spin_unlock_irqrestore(&data_lock, lock_flags);
+       return err;
+}
+
+EXPORT_SYMBOL(asrc_req_pair);
+
+void asrc_release_pair(enum asrc_pair_index index)
+{
+       unsigned long reg;
+       unsigned long lock_flags;
+
+       spin_lock_irqsave(&data_lock, lock_flags);
+       g_asrc_data->asrc_pair[index].active = 0;
+       g_asrc_data->asrc_pair[index].overload_error = 0;
+       /********Disable PAIR*************/
+       reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+       reg &= ~(1 << (index + 1));
+       __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+       spin_unlock_irqrestore(&data_lock, lock_flags);
+}
+
+EXPORT_SYMBOL(asrc_release_pair);
+
+int asrc_config_pair(struct asrc_config *config)
+{
+       int err = 0;
+       int reg, tmp, channel_num;
+       unsigned long lock_flags;
+       /* Set the channel number */
+       reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCNCR_REG);
+       spin_lock_irqsave(&data_lock, lock_flags);
+       g_asrc_data->asrc_pair[config->pair].chn_num = config->channel_num;
+       spin_unlock_irqrestore(&data_lock, lock_flags);
+       reg &=
+           ~((0xFFFFFFFF >> (32 - mxc_asrc_data->channel_bits)) <<
+             (mxc_asrc_data->channel_bits * config->pair));
+       if (mxc_asrc_data->channel_bits > 3)
+               channel_num = config->channel_num;
+       else
+               channel_num = (config->channel_num + 1) / 2;
+       tmp = channel_num << (mxc_asrc_data->channel_bits * config->pair);
+       reg |= tmp;
+       __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCNCR_REG);
+
+       /* Set the clock source */
+       reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCSR_REG);
+       tmp = ~(0x0f << (config->pair << 2));
+       reg &= tmp;
+       tmp = ~(0x0f << (12 + (config->pair << 2)));
+       reg &= tmp;
+       reg |=
+           ((input_clk_map[config->inclk] << (config->pair << 2)) | (output_clk_map[config->
+                                                      outclk] << (12 +
+                                                                 (config->
+                                                                  pair <<
+                                                                  2))));
+
+       __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCSR_REG);
+
+       /* default setting */
+       /* automatic selection for processing mode */
+       reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+       reg |= (1 << (20 + config->pair));
+       reg &= ~(1 << (14 + (config->pair << 1)));
+
+       __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+
+       reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRRA_REG);
+       reg &= 0xffbfffff;
+       __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRRA_REG);
+
+       reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+       reg = reg & (~(1 << 23));
+       __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+
+       /* Default Clock Divider Setting */
+       reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCDR1_REG);
+       if (config->pair == ASRC_PAIR_A) {
+               reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCDR1_REG);
+               reg &= 0xfc0fc0;
+               /* Input Part */
+               if ((config->inclk & 0x0f) == INCLK_SPDIF_RX)
+                       reg |= 7 << AICPA;
+               else if ((config->inclk & 0x0f) == INCLK_SPDIF_TX)
+                       reg |= 6 << AICPA;
+               else if ((config->inclk & 0x0f) == INCLK_ASRCK1_CLK) {
+                       tmp =
+                           asrc_get_asrck_clock_divider(config->
+                                                        input_sample_rate);
+                       reg |= tmp << AICPA;
+               } else {
+                       if (config->word_width == 16 || config->word_width == 8)
+                               reg |= 5 << AICPA;
+                       else if (config->word_width == 32
+                                || config->word_width == 24)
+                               reg |= 6 << AICPA;
+                       else
+                               err = -EFAULT;
+               }
+               /* Output Part */
+               if ((config->outclk & 0x0f) == OUTCLK_SPDIF_RX)
+                       reg |= 7 << AOCPA;
+               else if ((config->outclk & 0x0f) == OUTCLK_SPDIF_TX)
+                       reg |= 6 << AOCPA;
+               else if ((config->outclk & 0x0f) == OUTCLK_ASRCK1_CLK) {
+                       tmp =
+                           asrc_get_asrck_clock_divider(config->
+                                                        output_sample_rate);
+                       reg |= tmp << AOCPA;
+               } else {
+                       if (config->word_width == 16 || config->word_width == 8)
+                               reg |= 5 << AOCPA;
+                       else if (config->word_width == 32
+                                || config->word_width == 24)
+                               reg |= 6 << AOCPA;
+                       else
+                               err = -EFAULT;
+               }
+
+               __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCDR1_REG);
+
+       } else if (config->pair == ASRC_PAIR_B) {
+               reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCDR1_REG);
+               reg &= 0x03f03f;
+               /* Input Part */
+               if ((config->inclk & 0x0f) == INCLK_SPDIF_RX)
+                       reg |= 7 << AICPB;
+               else if ((config->inclk & 0x0f) == INCLK_SPDIF_TX)
+                       reg |= 6 << AICPB;
+               else if ((config->inclk & 0x0f) == INCLK_ASRCK1_CLK) {
+                       tmp =
+                           asrc_get_asrck_clock_divider(config->
+                                                        input_sample_rate);
+                       reg |= tmp << AICPB;
+               } else {
+                       if (config->word_width == 16 || config->word_width == 8)
+                               reg |= 5 << AICPB;
+                       else if (config->word_width == 32
+                                || config->word_width == 24)
+                               reg |= 6 << AICPB;
+                       else
+                               err = -EFAULT;
+               }
+               /* Output Part */
+               if ((config->outclk & 0x0f) == OUTCLK_SPDIF_RX)
+                       reg |= 7 << AOCPB;
+               else if ((config->outclk & 0x0f) == OUTCLK_SPDIF_TX)
+                       reg |= 6 << AOCPB;
+               else if ((config->outclk & 0x0f) == OUTCLK_ASRCK1_CLK) {
+                       tmp =
+                           asrc_get_asrck_clock_divider(config->
+                                                        output_sample_rate);
+                       reg |= tmp << AOCPB;
+               } else {
+                       if (config->word_width == 16 || config->word_width == 8)
+                               reg |= 5 << AOCPB;
+                       else if (config->word_width == 32
+                                || config->word_width == 24)
+                               reg |= 6 << AOCPB;
+                       else
+                               err = -EFAULT;
+               }
+
+               __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCDR1_REG);
+
+       } else {
+               reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCDR2_REG);
+               reg &= 0;
+               /* Input Part */
+               if ((config->inclk & 0x0f) == INCLK_SPDIF_RX)
+                       reg |= 7 << AICPC;
+               else if ((config->inclk & 0x0f) == INCLK_SPDIF_TX)
+                       reg |= 6 << AICPC;
+               else if ((config->inclk & 0x0f) == INCLK_ASRCK1_CLK) {
+                       tmp =
+                           asrc_get_asrck_clock_divider(config->
+                                                        input_sample_rate);
+                       reg |= tmp << AICPC;
+               } else {
+                       if (config->word_width == 16 || config->word_width == 8)
+                               reg |= 5 << AICPC;
+                       else if (config->word_width == 32
+                                || config->word_width == 24)
+                               reg |= 6 << AICPC;
+                       else
+                               err = -EFAULT;
+               }
+               /* Output Part */
+               if ((config->outclk & 0x0f) == OUTCLK_SPDIF_RX)
+                       reg |= 7 << AOCPC;
+               else if ((config->outclk & 0x0f) == OUTCLK_SPDIF_TX)
+                       reg |= 6 << AOCPC;
+               else if ((config->outclk & 0x0f) == OUTCLK_ASRCK1_CLK) {
+                       tmp =
+                           asrc_get_asrck_clock_divider(config->
+                                                        output_sample_rate);
+                       reg |= tmp << AOCPC;
+               } else {
+                       if (config->word_width == 16 || config->word_width == 8)
+                               reg |= 5 << AOCPC;
+                       else if (config->word_width == 32
+                                || config->word_width == 24)
+                               reg |= 6 << AOCPC;
+                       else
+                               err = -EFAULT;
+               }
+               __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCDR2_REG);
+
+       }
+
+       /* check whether ideal ratio is a must */
+       if ((config->inclk & 0x0f) == INCLK_NONE) {
+               reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+               reg &= ~(1 << (20 + config->pair));
+               reg |= (0x03 << (13 + (config->pair << 1)));
+               __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+               err = asrc_set_clock_ratio(config->pair,
+                                          config->input_sample_rate,
+                                          config->output_sample_rate);
+               if (err < 0)
+                       return err;
+
+               err = asrc_set_process_configuration(config->pair,
+                                                    config->input_sample_rate,
+                                                    config->
+                                                    output_sample_rate);
+               if (err < 0)
+                       return err;
+       } else if ((config->inclk & 0x0f) == INCLK_ASRCK1_CLK) {
+               if (config->input_sample_rate == 44100
+                   || config->input_sample_rate == 88200) {
+                       pr_info
+                           ("ASRC core clock cann't support sample rate %d\n",
+                            config->input_sample_rate);
+                       err = -EFAULT;
+               }
+       } else if ((config->outclk & 0x0f) == OUTCLK_ASRCK1_CLK) {
+               if (config->output_sample_rate == 44100
+                   || config->output_sample_rate == 88200) {
+                       pr_info
+                           ("ASRC core clock cann't support sample rate %d\n",
+                            config->input_sample_rate);
+                       err = -EFAULT;
+               }
+       }
+
+       return err;
+}
+
+EXPORT_SYMBOL(asrc_config_pair);
+
+void asrc_start_conv(enum asrc_pair_index index)
+{
+       int reg, reg_1;
+       unsigned long lock_flags;
+       int i;
+
+       spin_lock_irqsave(&data_lock, lock_flags);
+
+       reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+       if ((reg & 0x0E) == 0)
+               clk_enable(mxc_asrc_data->asrc_audio_clk);
+       reg |= (1 << (1 + index));
+       __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+
+       reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCFG_REG);
+       while (!(reg & (1 << (index + 21))))
+               reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCFG_REG);
+       reg_1 = __raw_readl(asrc_vrt_base_addr + ASRC_ASRSTR_REG);
+
+       reg = 0;
+       for (i = 0; i < 20; i++) {
+               __raw_writel(reg,
+                            asrc_vrt_base_addr + ASRC_ASRDIA_REG +
+                            (index << 3));
+               __raw_writel(reg,
+                            asrc_vrt_base_addr + ASRC_ASRDIA_REG +
+                            (index << 3));
+               __raw_writel(reg,
+                            asrc_vrt_base_addr + ASRC_ASRDIA_REG +
+                            (index << 3));
+               __raw_writel(reg,
+                            asrc_vrt_base_addr + ASRC_ASRDIA_REG +
+                            (index << 3));
+               __raw_writel(reg,
+                            asrc_vrt_base_addr + ASRC_ASRDIA_REG +
+                            (index << 3));
+               __raw_writel(reg,
+                            asrc_vrt_base_addr + ASRC_ASRDIA_REG +
+                            (index << 3));
+               __raw_writel(reg,
+                            asrc_vrt_base_addr + ASRC_ASRDIA_REG +
+                            (index << 3));
+               __raw_writel(reg,
+                            asrc_vrt_base_addr + ASRC_ASRDIA_REG +
+                            (index << 3));
+       }
+
+       __raw_writel(0x40, asrc_vrt_base_addr + ASRC_ASRIER_REG);
+       spin_unlock_irqrestore(&data_lock, lock_flags);
+       return;
+}
+
+EXPORT_SYMBOL(asrc_start_conv);
+
+void asrc_stop_conv(enum asrc_pair_index index)
+{
+       int reg;
+       unsigned long lock_flags;
+       spin_lock_irqsave(&data_lock, lock_flags);
+       reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+       reg &= ~(1 << (1 + index));
+       __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+       if ((reg & 0x0E) == 0)
+               clk_disable(mxc_asrc_data->asrc_audio_clk);
+       spin_unlock_irqrestore(&data_lock, lock_flags);
+       return;
+}
+
+EXPORT_SYMBOL(asrc_stop_conv);
+
+/*!
+ * @brief asrc interrupt handler
+ */
+static irqreturn_t asrc_isr(int irq, void *dev_id)
+{
+       unsigned long status;
+       int reg = 0x40;
+
+       status = __raw_readl(asrc_vrt_base_addr + ASRC_ASRSTR_REG);
+       if (g_asrc_data->asrc_pair[ASRC_PAIR_A].active == 1) {
+               if (status & ASRC_ASRSTR_ATQOL)
+                       g_asrc_data->asrc_pair[ASRC_PAIR_A].overload_error |=
+                           ASRC_TASK_Q_OVERLOAD;
+               if (status & ASRC_ASRSTR_AOOLA)
+                       g_asrc_data->asrc_pair[ASRC_PAIR_A].overload_error |=
+                           ASRC_OUTPUT_TASK_OVERLOAD;
+               if (status & ASRC_ASRSTR_AIOLA)
+                       g_asrc_data->asrc_pair[ASRC_PAIR_A].overload_error |=
+                           ASRC_INPUT_TASK_OVERLOAD;
+               if (status & ASRC_ASRSTR_AODOA)
+                       g_asrc_data->asrc_pair[ASRC_PAIR_A].overload_error |=
+                           ASRC_OUTPUT_BUFFER_OVERFLOW;
+               if (status & ASRC_ASRSTR_AIDUA)
+                       g_asrc_data->asrc_pair[ASRC_PAIR_A].overload_error |=
+                           ASRC_INPUT_BUFFER_UNDERRUN;
+       } else if (g_asrc_data->asrc_pair[ASRC_PAIR_B].active == 1) {
+               if (status & ASRC_ASRSTR_ATQOL)
+                       g_asrc_data->asrc_pair[ASRC_PAIR_B].overload_error |=
+                           ASRC_TASK_Q_OVERLOAD;
+               if (status & ASRC_ASRSTR_AOOLB)
+                       g_asrc_data->asrc_pair[ASRC_PAIR_B].overload_error |=
+                           ASRC_OUTPUT_TASK_OVERLOAD;
+               if (status & ASRC_ASRSTR_AIOLB)
+                       g_asrc_data->asrc_pair[ASRC_PAIR_B].overload_error |=
+                           ASRC_INPUT_TASK_OVERLOAD;
+               if (status & ASRC_ASRSTR_AODOB)
+                       g_asrc_data->asrc_pair[ASRC_PAIR_B].overload_error |=
+                           ASRC_OUTPUT_BUFFER_OVERFLOW;
+               if (status & ASRC_ASRSTR_AIDUB)
+                       g_asrc_data->asrc_pair[ASRC_PAIR_B].overload_error |=
+                           ASRC_INPUT_BUFFER_UNDERRUN;
+       } else if (g_asrc_data->asrc_pair[ASRC_PAIR_C].active == 1) {
+               if (status & ASRC_ASRSTR_ATQOL)
+                       g_asrc_data->asrc_pair[ASRC_PAIR_C].overload_error |=
+                           ASRC_TASK_Q_OVERLOAD;
+               if (status & ASRC_ASRSTR_AOOLC)
+                       g_asrc_data->asrc_pair[ASRC_PAIR_C].overload_error |=
+                           ASRC_OUTPUT_TASK_OVERLOAD;
+               if (status & ASRC_ASRSTR_AIOLC)
+                       g_asrc_data->asrc_pair[ASRC_PAIR_C].overload_error |=
+                           ASRC_INPUT_TASK_OVERLOAD;
+               if (status & ASRC_ASRSTR_AODOC)
+                       g_asrc_data->asrc_pair[ASRC_PAIR_C].overload_error |=
+                           ASRC_OUTPUT_BUFFER_OVERFLOW;
+               if (status & ASRC_ASRSTR_AIDUC)
+                       g_asrc_data->asrc_pair[ASRC_PAIR_C].overload_error |=
+                           ASRC_INPUT_BUFFER_UNDERRUN;
+       }
+
+       /* try to clean the overload error  */
+       __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRSTR_REG);
+
+       return IRQ_HANDLED;
+}
+
+void asrc_get_status(struct asrc_status_flags *flags)
+{
+       unsigned long lock_flags;
+       enum asrc_pair_index index;
+
+       spin_lock_irqsave(&data_lock, lock_flags);
+       index = flags->index;
+       flags->overload_error = g_asrc_data->asrc_pair[index].overload_error;
+
+       spin_unlock_irqrestore(&data_lock, lock_flags);
+       return;
+}
+
+EXPORT_SYMBOL(asrc_get_status);
+
+static int mxc_init_asrc(void)
+{
+       /* Halt ASRC internal FP when input FIFO needs data for pair A, B, C */
+       __raw_writel(0x0001, asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+
+       /* Enable overflow interrupt */
+       __raw_writel(0x00, asrc_vrt_base_addr + ASRC_ASRIER_REG);
+
+       /* Default 6: 2: 2 channel assignment */
+       __raw_writel((0x06 << mxc_asrc_data->channel_bits *
+                     2) | (0x02 << mxc_asrc_data->channel_bits) | 0x02,
+                    asrc_vrt_base_addr + ASRC_ASRCNCR_REG);
+
+       /* Parameter Registers recommended settings */
+       __raw_writel(0x7fffff, asrc_vrt_base_addr + ASRC_ASRPM1_REG);
+       __raw_writel(0x255555, asrc_vrt_base_addr + ASRC_ASRPM2_REG);
+       __raw_writel(0xff7280, asrc_vrt_base_addr + ASRC_ASRPM3_REG);
+       __raw_writel(0xff7280, asrc_vrt_base_addr + ASRC_ASRPM4_REG);
+       __raw_writel(0xff7280, asrc_vrt_base_addr + ASRC_ASRPM5_REG);
+
+       __raw_writel(0x001f00, asrc_vrt_base_addr + ASRC_ASRTFR1);
+
+       /* Set the processing clock for 76KHz, 133M  */
+       __raw_writel(0x30E, asrc_vrt_base_addr + ASRC_ASR76K_REG);
+
+       /* Set the processing clock for 56KHz, 133M */
+       __raw_writel(0x0426, asrc_vrt_base_addr + ASRC_ASR56K_REG);
+
+       return 0;
+}
+
+static int asrc_get_output_buffer_size(int input_buffer_size,
+                                      int input_sample_rate,
+                                      int output_sample_rate)
+{
+       int i = 0;
+       int outbuffer_size = 0;
+       int outsample = output_sample_rate;
+       while (outsample >= input_sample_rate) {
+               ++i;
+               outsample -= input_sample_rate;
+       }
+       outbuffer_size = i * input_buffer_size;
+       i = 1;
+       while (((input_buffer_size >> i) > 2) && (outsample != 0)) {
+               if (((outsample << 1) - input_sample_rate) >= 0) {
+                       outsample = (outsample << 1) - input_sample_rate;
+                       outbuffer_size += (input_buffer_size >> i);
+               } else {
+                       outsample = outsample << 1;
+               }
+               i++;
+       }
+       outbuffer_size = (outbuffer_size >> 3) << 3;
+       return outbuffer_size;
+}
+
+static void asrc_input_dma_callback(void *data, int error, unsigned int count)
+{
+       struct asrc_pair_params *params;
+       struct dma_block *block;
+       mxc_dma_requestbuf_t dma_request;
+       unsigned long lock_flags;
+
+       params = data;
+
+       spin_lock_irqsave(&input_int_lock, lock_flags);
+       params->input_queue_empty--;
+       if (!list_empty(&params->input_queue)) {
+               block =
+                   list_entry(params->input_queue.next,
+                              struct dma_block, queue);
+               dma_request.src_addr = (dma_addr_t) block->dma_paddr;
+               dma_request.dst_addr =
+                   (ASRC_BASE_ADDR + ASRC_ASRDIA_REG + (params->index << 3));
+               dma_request.num_of_bytes = block->length;
+               mxc_dma_config(params->input_dma_channel, &dma_request,
+                              1, MXC_DMA_MODE_WRITE);
+               list_del(params->input_queue.next);
+               list_add_tail(&block->queue, &params->input_done_queue);
+               params->input_queue_empty++;
+       }
+       params->input_counter++;
+       wake_up_interruptible(&params->input_wait_queue);
+       spin_unlock_irqrestore(&input_int_lock, lock_flags);
+       return;
+}
+
+static void asrc_output_dma_callback(void *data, int error, unsigned int count)
+{
+       struct asrc_pair_params *params;
+       struct dma_block *block;
+       mxc_dma_requestbuf_t dma_request;
+       unsigned long lock_flags;
+
+       params = data;
+
+       spin_lock_irqsave(&output_int_lock, lock_flags);
+       params->output_queue_empty--;
+
+       if (!list_empty(&params->output_queue)) {
+               block =
+                   list_entry(params->output_queue.next,
+                              struct dma_block, queue);
+               dma_request.src_addr =
+                   (ASRC_BASE_ADDR + ASRC_ASRDOA_REG + (params->index << 3));
+               dma_request.dst_addr = (dma_addr_t) block->dma_paddr;
+               dma_request.num_of_bytes = block->length;
+               mxc_dma_config(params->output_dma_channel, &dma_request,
+                              1, MXC_DMA_MODE_READ);
+               list_del(params->output_queue.next);
+               list_add_tail(&block->queue, &params->output_done_queue);
+               params->output_queue_empty++;
+       }
+       params->output_counter++;
+       wake_up_interruptible(&params->output_wait_queue);
+       spin_unlock_irqrestore(&output_int_lock, lock_flags);
+       return;
+}
+
+static void mxc_free_dma_buf(struct asrc_pair_params *params)
+{
+       int i;
+       for (i = 0; i < ASRC_DMA_BUFFER_NUM; i++) {
+               if (params->input_dma[i].dma_vaddr != NULL) {
+                       dma_free_coherent(0,
+                                         params->input_buffer_size,
+                                         params->input_dma[i].
+                                         dma_vaddr,
+                                         params->input_dma[i].dma_paddr);
+                       params->input_dma[i].dma_vaddr = NULL;
+               }
+               if (params->output_dma[i].dma_vaddr != NULL) {
+                       dma_free_coherent(0,
+                                         params->output_buffer_size,
+                                         params->output_dma[i].
+                                         dma_vaddr,
+                                         params->output_dma[i].dma_paddr);
+                       params->output_dma[i].dma_vaddr = NULL;
+               }
+       }
+
+       return;
+}
+
+static int mxc_allocate_dma_buf(struct asrc_pair_params *params)
+{
+       int i;
+       for (i = 0; i < ASRC_DMA_BUFFER_NUM; i++) {
+               params->input_dma[i].dma_vaddr =
+                   dma_alloc_coherent(0, params->input_buffer_size,
+                                      &params->input_dma[i].dma_paddr,
+                                      GFP_DMA | GFP_KERNEL);
+               if (params->input_dma[i].dma_vaddr == NULL) {
+                       mxc_free_dma_buf(params);
+                       pr_info("can't allocate buff\n");
+                       return -ENOBUFS;
+               }
+       }
+       for (i = 0; i < ASRC_DMA_BUFFER_NUM; i++) {
+               params->output_dma[i].dma_vaddr =
+                   dma_alloc_coherent(0,
+                                      params->output_buffer_size,
+                                      &params->output_dma[i].dma_paddr,
+                                      GFP_DMA | GFP_KERNEL);
+               if (params->output_dma[i].dma_vaddr == NULL) {
+                       mxc_free_dma_buf(params);
+                       return -ENOBUFS;
+               }
+       }
+
+       return 0;
+}
+
+/*!
+ * asrc interface - ioctl function
+ *
+ * @param inode      struct inode *
+ *
+ * @param file       struct file *
+ *
+ * @param cmd    unsigned int
+ *
+ * @param arg        unsigned long
+ *
+ * @return           0 success, ENODEV for invalid device instance,
+ *                   -1 for other errors.
+ */
+static int asrc_ioctl(struct inode *inode, struct file *file,
+                     unsigned int cmd, unsigned long arg)
+{
+       int err = 0;
+       struct asrc_pair_params *params;
+       params = file->private_data;
+
+       if (down_interruptible(&params->busy_lock))
+               return -EBUSY;
+       switch (cmd) {
+       case ASRC_REQ_PAIR:
+               {
+                       struct asrc_req req;
+                       if (copy_from_user(&req, (void __user *)arg,
+                                          sizeof(struct asrc_req))) {
+                               err = -EFAULT;
+                               break;
+                       }
+                       err = asrc_req_pair(req.chn_num, &req.index);
+                       if (err < 0)
+                               break;
+                       params->pair_hold = 1;
+                       params->index = req.index;
+                       if (copy_to_user
+                           ((void __user *)arg, &req, sizeof(struct asrc_req)))
+                               err = -EFAULT;
+
+                       break;
+               }
+       case ASRC_CONFIG_PAIR:
+               {
+                       struct asrc_config config;
+                       mxc_dma_device_t rx_id, tx_id;
+                       char *rx_name, *tx_name;
+                       int channel = -1;
+                       if (copy_from_user
+                           (&config, (void __user *)arg,
+                            sizeof(struct asrc_config))) {
+                               err = -EFAULT;
+                               break;
+                       }
+                       err = asrc_config_pair(&config);
+                       if (err < 0)
+                               break;
+                       params->output_buffer_size =
+                           asrc_get_output_buffer_size(config.
+                                                       dma_buffer_size,
+                                                       config.
+                                                       input_sample_rate,
+                                                       config.
+                                                       output_sample_rate);
+                       params->input_buffer_size = config.dma_buffer_size;
+                       if (config.buffer_num > ASRC_DMA_BUFFER_NUM)
+                               params->buffer_num = ASRC_DMA_BUFFER_NUM;
+                       else
+                               params->buffer_num = config.buffer_num;
+                       err = mxc_allocate_dma_buf(params);
+                       if (err < 0)
+                               break;
+
+                       /* TBD - need to update when new SDMA interface ready */
+                       if (config.pair == ASRC_PAIR_A) {
+                               rx_id = MXC_DMA_ASRC_A_RX;
+                               tx_id = MXC_DMA_ASRC_A_TX;
+                               rx_name = asrc_pair_id[0];
+                               tx_name = asrc_pair_id[1];
+                       } else if (config.pair == ASRC_PAIR_B) {
+                               rx_id = MXC_DMA_ASRC_B_RX;
+                               tx_id = MXC_DMA_ASRC_B_TX;
+                               rx_name = asrc_pair_id[2];
+                               tx_name = asrc_pair_id[3];
+                       } else {
+                               rx_id = MXC_DMA_ASRC_C_RX;
+                               tx_id = MXC_DMA_ASRC_C_TX;
+                               rx_name = asrc_pair_id[4];
+                               tx_name = asrc_pair_id[5];
+                       }
+                       channel = mxc_dma_request(rx_id, rx_name);
+                       params->input_dma_channel = channel;
+                       err = mxc_dma_callback_set(channel, (mxc_dma_callback_t)
+                                                  asrc_input_dma_callback,
+                                                  (void *)params);
+                       channel = mxc_dma_request(tx_id, tx_name);
+                       params->output_dma_channel = channel;
+                       err = mxc_dma_callback_set(channel, (mxc_dma_callback_t)
+                                                  asrc_output_dma_callback,
+                                                  (void *)params);
+                       /* TBD - need to update when new SDMA interface ready */
+                       params->input_queue_empty = 0;
+                       params->output_queue_empty = 0;
+                       INIT_LIST_HEAD(&params->input_queue);
+                       INIT_LIST_HEAD(&params->input_done_queue);
+                       INIT_LIST_HEAD(&params->output_queue);
+                       INIT_LIST_HEAD(&params->output_done_queue);
+                       init_waitqueue_head(&params->input_wait_queue);
+                       init_waitqueue_head(&params->output_wait_queue);
+
+                       if (copy_to_user
+                           ((void __user *)arg, &config,
+                            sizeof(struct asrc_config)))
+                               err = -EFAULT;
+                       break;
+               }
+       case ASRC_QUERYBUF:
+               {
+                       struct asrc_querybuf buffer;
+                       if (copy_from_user
+                           (&buffer, (void __user *)arg,
+                            sizeof(struct asrc_querybuf))) {
+                               err = -EFAULT;
+                               break;
+                       }
+                       buffer.input_offset =
+                           (unsigned long)params->input_dma[buffer.
+                                                            buffer_index].
+                           dma_paddr;
+                       buffer.input_length = params->input_buffer_size;
+                       buffer.output_offset =
+                           (unsigned long)params->output_dma[buffer.
+                                                             buffer_index].
+                           dma_paddr;
+                       buffer.output_length = params->output_buffer_size;
+                       if (copy_to_user
+                           ((void __user *)arg, &buffer,
+                            sizeof(struct asrc_querybuf)))
+                               err = -EFAULT;
+                       break;
+               }
+       case ASRC_RELEASE_PAIR:
+               {
+                       enum asrc_pair_index index;
+                       if (copy_from_user
+                           (&index, (void __user *)arg,
+                            sizeof(enum asrc_pair_index))) {
+                               err = -EFAULT;
+                               break;
+                       }
+
+                       mxc_dma_free(params->input_dma_channel);
+                       mxc_dma_free(params->output_dma_channel);
+                       mxc_free_dma_buf(params);
+                       asrc_release_pair(index);
+                       params->pair_hold = 0;
+                       break;
+               }
+       case ASRC_Q_INBUF:
+               {
+                       struct asrc_buffer buf;
+                       struct dma_block *block;
+                       mxc_dma_requestbuf_t dma_request;
+                       unsigned long lock_flags;
+                       if (copy_from_user
+                           (&buf, (void __user *)arg,
+                            sizeof(struct asrc_buffer))) {
+                               err = -EFAULT;
+                               break;
+                       }
+                       spin_lock_irqsave(&input_int_lock, lock_flags);
+                       params->input_dma[buf.index].index = buf.index;
+                       params->input_dma[buf.index].length = buf.length;
+                       list_add_tail(&params->input_dma[buf.index].
+                                     queue, &params->input_queue);
+                       if (params->asrc_active == 0
+                           || params->input_queue_empty == 0) {
+                               block =
+                                   list_entry(params->input_queue.next,
+                                              struct dma_block, queue);
+                               dma_request.src_addr =
+                                   (dma_addr_t) block->dma_paddr;
+                               dma_request.dst_addr =
+                                   (ASRC_BASE_ADDR + ASRC_ASRDIA_REG +
+                                    (params->index << 3));
+                               dma_request.num_of_bytes = block->length;
+                               mxc_dma_config(params->
+                                              input_dma_channel,
+                                              &dma_request, 1,
+                                              MXC_DMA_MODE_WRITE);
+                               params->input_queue_empty++;
+                               list_del(params->input_queue.next);
+                               list_add_tail(&block->queue,
+                                             &params->input_done_queue);
+                       }
+
+                       spin_unlock_irqrestore(&input_int_lock, lock_flags);
+                       break;
+               }
+       case ASRC_DQ_INBUF:{
+                       struct asrc_buffer buf;
+                       struct dma_block *block;
+                       unsigned long lock_flags;
+                       if (copy_from_user
+                           (&buf, (void __user *)arg,
+                            sizeof(struct asrc_buffer))) {
+                               err = -EFAULT;
+                               break;
+                       }
+                       /* if ASRC is inactive, nonsense to DQ buffer */
+                       if (params->asrc_active == 0) {
+                               err = -EFAULT;
+                               buf.buf_valid = ASRC_BUF_NA;
+                               if (copy_to_user
+                                   ((void __user *)arg, &buf,
+                                    sizeof(struct asrc_buffer)))
+                                       err = -EFAULT;
+                               break;
+                       }
+
+                       if (!wait_event_interruptible_timeout
+                           (params->input_wait_queue,
+                            params->input_counter != 0, 10 * HZ)) {
+                               pr_info
+                                   ("ASRC_DQ_INBUF timeout counter %x\n",
+                                    params->input_counter);
+                               err = -ETIME;
+                               break;
+                       } else if (signal_pending(current)) {
+                               pr_info("ASRC_DQ_INBUF interrupt received\n");
+                               err = -ERESTARTSYS;
+                               break;
+                       }
+                       spin_lock_irqsave(&input_int_lock, lock_flags);
+                       params->input_counter--;
+                       block =
+                           list_entry(params->input_done_queue.next,
+                                      struct dma_block, queue);
+                       list_del(params->input_done_queue.next);
+                       spin_unlock_irqrestore(&input_int_lock, lock_flags);
+                       buf.index = block->index;
+                       buf.length = block->length;
+                       buf.buf_valid = ASRC_BUF_AV;
+                       if (copy_to_user
+                           ((void __user *)arg, &buf,
+                            sizeof(struct asrc_buffer)))
+                               err = -EFAULT;
+
+                       break;
+               }
+       case ASRC_Q_OUTBUF:{
+                       struct asrc_buffer buf;
+                       struct dma_block *block;
+                       mxc_dma_requestbuf_t dma_request;
+                       unsigned long lock_flags;
+                       if (copy_from_user
+                           (&buf, (void __user *)arg,
+                            sizeof(struct asrc_buffer))) {
+                               err = -EFAULT;
+                               break;
+                       }
+                       spin_lock_irqsave(&output_int_lock, lock_flags);
+                       params->output_dma[buf.index].index = buf.index;
+                       params->output_dma[buf.index].length = buf.length;
+                       list_add_tail(&params->output_dma[buf.index].
+                                     queue, &params->output_queue);
+                       if (params->asrc_active == 0
+                           || params->output_queue_empty == 0) {
+                               block =
+                                   list_entry(params->output_queue.
+                                              next, struct dma_block, queue);
+                               dma_request.src_addr =
+                                   (ASRC_BASE_ADDR + ASRC_ASRDOA_REG +
+                                    (params->index << 3));
+                               dma_request.dst_addr =
+                                   (dma_addr_t) block->dma_paddr;
+                               dma_request.num_of_bytes = block->length;
+                               mxc_dma_config(params->
+                                              output_dma_channel,
+                                              &dma_request, 1,
+                                              MXC_DMA_MODE_READ);
+                               list_del(params->output_queue.next);
+                               list_add_tail(&block->queue,
+                                             &params->output_done_queue);
+                               params->output_queue_empty++;
+                       }
+
+                       spin_unlock_irqrestore(&output_int_lock, lock_flags);
+                       break;
+               }
+       case ASRC_DQ_OUTBUF:{
+                       struct asrc_buffer buf;
+                       struct dma_block *block;
+                       unsigned long lock_flags;
+                       if (copy_from_user
+                           (&buf, (void __user *)arg,
+                            sizeof(struct asrc_buffer))) {
+                               err = -EFAULT;
+                               break;
+                       }
+                       /* if ASRC is inactive, nonsense to DQ buffer */
+                       if (params->asrc_active == 0) {
+                               buf.buf_valid = ASRC_BUF_NA;
+                               err = -EFAULT;
+                               if (copy_to_user
+                                   ((void __user *)arg, &buf,
+                                    sizeof(struct asrc_buffer)))
+                                       err = -EFAULT;
+                               break;
+                       }
+
+                       if (!wait_event_interruptible_timeout
+                           (params->output_wait_queue,
+                            params->output_counter != 0, 10 * HZ)) {
+                               pr_info
+                                   ("ASRC_DQ_OUTBUF timeout counter %x\n",
+                                    params->output_counter);
+                               err = -ETIME;
+                               break;
+                       } else if (signal_pending(current)) {
+                               pr_info("ASRC_DQ_INBUF interrupt received\n");
+                               err = -ERESTARTSYS;
+                               break;
+                       }
+                       spin_lock_irqsave(&output_int_lock, lock_flags);
+                       params->output_counter--;
+                       block =
+                           list_entry(params->output_done_queue.next,
+                                      struct dma_block, queue);
+                       list_del(params->output_done_queue.next);
+                       spin_unlock_irqrestore(&output_int_lock, lock_flags);
+                       buf.index = block->index;
+                       buf.length = block->length;
+                       buf.buf_valid = ASRC_BUF_AV;
+                       if (copy_to_user
+                           ((void __user *)arg, &buf,
+                            sizeof(struct asrc_buffer)))
+                               err = -EFAULT;
+
+                       break;
+               }
+       case ASRC_START_CONV:{
+                       enum asrc_pair_index index;
+                       unsigned long lock_flags;
+                       if (copy_from_user
+                           (&index, (void __user *)arg,
+                            sizeof(enum asrc_pair_index))) {
+                               err = -EFAULT;
+                               break;
+                       }
+
+                       spin_lock_irqsave(&input_int_lock, lock_flags);
+                       if (params->input_queue_empty == 0) {
+                               err = -EFAULT;
+                               pr_info
+                                   ("ASRC_START_CONV - no block available\n");
+                               break;
+                       }
+                       spin_unlock_irqrestore(&input_int_lock, lock_flags);
+                       params->asrc_active = 1;
+
+                       asrc_start_conv(index);
+                       mxc_dma_enable(params->input_dma_channel);
+                       mxc_dma_enable(params->output_dma_channel);
+                       break;
+               }
+       case ASRC_STOP_CONV:{
+                       enum asrc_pair_index index;
+                       if (copy_from_user
+                           (&index, (void __user *)arg,
+                            sizeof(enum asrc_pair_index))) {
+                               err = -EFAULT;
+                               break;
+                       }
+                       mxc_dma_disable(params->input_dma_channel);
+                       mxc_dma_disable(params->output_dma_channel);
+                       asrc_stop_conv(index);
+                       params->asrc_active = 0;
+                       break;
+               }
+       case ASRC_STATUS:{
+                       struct asrc_status_flags flags;
+                       if (copy_from_user
+                           (&flags, (void __user *)arg,
+                            sizeof(struct asrc_status_flags))) {
+                               err = -EFAULT;
+                               break;
+                       }
+                       asrc_get_status(&flags);
+                       if (copy_to_user
+                           ((void __user *)arg, &flags,
+                            sizeof(struct asrc_status_flags)))
+                               err = -EFAULT;
+                       break;
+               }
+       case ASRC_FLUSH:{
+                       /* flush input dma buffer */
+                       unsigned long lock_flags;
+                       mxc_dma_device_t rx_id, tx_id;
+                       char *rx_name, *tx_name;
+                       int channel = -1;
+                       spin_lock_irqsave(&input_int_lock, lock_flags);
+                       while (!list_empty(&params->input_queue))
+                               list_del(params->input_queue.next);
+                       while (!list_empty(&params->input_done_queue))
+                               list_del(params->input_done_queue.next);
+                       params->input_counter = 0;
+                       params->input_queue_empty = 0;
+                       spin_unlock_irqrestore(&input_int_lock, lock_flags);
+
+                       /* flush output dma buffer */
+                       spin_lock_irqsave(&output_int_lock, lock_flags);
+                       while (!list_empty(&params->output_queue))
+                               list_del(params->output_queue.next);
+                       while (!list_empty(&params->output_done_queue))
+                               list_del(params->output_done_queue.next);
+                       params->output_counter = 0;
+                       params->output_queue_empty = 0;
+                       spin_unlock_irqrestore(&output_int_lock, lock_flags);
+
+                       /* release DMA and request again */
+                       mxc_dma_free(params->input_dma_channel);
+                       mxc_dma_free(params->output_dma_channel);
+                       if (params->index == ASRC_PAIR_A) {
+                               rx_id = MXC_DMA_ASRC_A_RX;
+                               tx_id = MXC_DMA_ASRC_A_TX;
+                               rx_name = asrc_pair_id[0];
+                               tx_name = asrc_pair_id[1];
+                       } else if (params->index == ASRC_PAIR_B) {
+                               rx_id = MXC_DMA_ASRC_B_RX;
+                               tx_id = MXC_DMA_ASRC_B_TX;
+                               rx_name = asrc_pair_id[2];
+                               tx_name = asrc_pair_id[3];
+                       } else {
+                               rx_id = MXC_DMA_ASRC_C_RX;
+                               tx_id = MXC_DMA_ASRC_C_TX;
+                               rx_name = asrc_pair_id[4];
+                               tx_name = asrc_pair_id[5];
+                       }
+                       channel = mxc_dma_request(rx_id, rx_name);
+                       params->input_dma_channel = channel;
+                       err = mxc_dma_callback_set(channel, (mxc_dma_callback_t)
+                                                  asrc_input_dma_callback,
+                                                  (void *)params);
+                       channel = mxc_dma_request(tx_id, tx_name);
+                       params->output_dma_channel = channel;
+                       err = mxc_dma_callback_set(channel, (mxc_dma_callback_t)
+                                                  asrc_output_dma_callback,
+                                                  (void *)params);
+
+                       break;
+               }
+       default:
+               break;
+       }
+
+       up(&params->busy_lock);
+       return err;
+}
+
+/*!
+ * asrc interface - open function
+ *
+ * @param inode        structure inode *
+ *
+ * @param file         structure file *
+ *
+ * @return  status    0 success, ENODEV invalid device instance,
+ *      ENOBUFS failed to allocate buffer, ERESTARTSYS interrupted by user
+ */
+static int mxc_asrc_open(struct inode *inode, struct file *file)
+{
+       int err = 0;
+       struct asrc_pair_params *pair_params;
+       if (signal_pending(current))
+               return -EINTR;
+       pair_params = kzalloc(sizeof(struct asrc_pair_params), GFP_KERNEL);
+       if (pair_params == NULL) {
+               pr_debug("Failed to allocate pair_params\n");
+               err = -ENOBUFS;
+       }
+
+       init_MUTEX(&pair_params->busy_lock);
+       file->private_data = pair_params;
+       return err;
+}
+
+/*!
+ * asrc interface - close function
+ *
+ * @param inode    struct inode *
+ * @param file        structure file *
+ *
+ * @return status     0 Success, EINTR busy lock error, ENOBUFS remap_page error
+ */
+static int mxc_asrc_close(struct inode *inode, struct file *file)
+{
+       struct asrc_pair_params *pair_params;
+       pair_params = file->private_data;
+       if (pair_params->asrc_active == 1) {
+               mxc_dma_disable(pair_params->input_dma_channel);
+               mxc_dma_disable(pair_params->output_dma_channel);
+               asrc_stop_conv(pair_params->index);
+               wake_up_interruptible(&pair_params->input_wait_queue);
+               wake_up_interruptible(&pair_params->output_wait_queue);
+       }
+       if (pair_params->pair_hold == 1) {
+               mxc_dma_free(pair_params->input_dma_channel);
+               mxc_dma_free(pair_params->output_dma_channel);
+               mxc_free_dma_buf(pair_params);
+               asrc_release_pair(pair_params->index);
+       }
+       kfree(pair_params);
+       file->private_data = NULL;
+       return 0;
+}
+
+/*!
+ * asrc interface - mmap function
+ *
+ * @param file        structure file *
+ *
+ * @param vma         structure vm_area_struct *
+ *
+ * @return status     0 Success, EINTR busy lock error, ENOBUFS remap_page error
+ */
+static int mxc_asrc_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       unsigned long size;
+       int res = 0;
+       size = vma->vm_end - vma->vm_start;
+       vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+       if (remap_pfn_range(vma, vma->vm_start,
+                           vma->vm_pgoff, size, vma->vm_page_prot))
+               return -ENOBUFS;
+
+       vma->vm_flags &= ~VM_IO;
+       return res;
+}
+
+static struct file_operations asrc_fops = {
+       .owner = THIS_MODULE,
+       .ioctl = asrc_ioctl,
+       .mmap = mxc_asrc_mmap,
+       .open = mxc_asrc_open,
+       .release = mxc_asrc_close,
+};
+
+static int asrc_read_proc_attr(char *page, char **start, off_t off,
+                              int count, int *eof, void *data)
+{
+       unsigned long reg;
+       int len = 0;
+       reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCNCR_REG);
+
+       len += sprintf(page, "ANCA: %d\n",
+                      (int)(reg &
+                            (0xFFFFFFFF >>
+                             (32 - mxc_asrc_data->channel_bits))));
+       len +=
+           sprintf(page + len, "ANCB: %d\n",
+                   (int)((reg >> mxc_asrc_data->
+                          channel_bits) & (0xFFFFFFFF >> (32 -
+                                                          mxc_asrc_data->
+                                                          channel_bits))));
+       len +=
+           sprintf(page + len, "ANCC: %d\n",
+                   (int)((reg >> (mxc_asrc_data->channel_bits * 2)) &
+                         (0xFFFFFFFF >> (32 - mxc_asrc_data->channel_bits))));
+
+       if (off > len)
+               return 0;
+
+       *eof = (len <= count) ? 1 : 0;
+       *start = page + off;
+
+       return min(count, len - (int)off);
+}
+
+static int asrc_write_proc_attr(struct file *file, const char *buffer,
+                               unsigned long count, void *data)
+{
+       char buf[50];
+       unsigned long reg;
+       int na, nb, nc;
+       int total;
+       if (count > 48)
+               return -EINVAL;
+       if (copy_from_user(buf, buffer, count)) {
+               pr_debug("Attr proc write, Failed to copy buffer from user\n");
+               return -EFAULT;
+       }
+
+       reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCNCR_REG);
+       sscanf(buf, "ANCA: %d\nANCB: %d\nANCC: %d", &na, &nb, &nc);
+       if (mxc_asrc_data->channel_bits > 3)
+               total = 10;
+       else
+               total = 5;
+       if ((na + nb + nc) != total) {
+               pr_info("Wrong ASRCNR settings\n");
+               return -EFAULT;
+       }
+       reg = na | (nb << mxc_asrc_data->
+                   channel_bits) | (nc << (mxc_asrc_data->channel_bits * 2));
+
+       __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCNCR_REG);
+
+       return count;
+}
+
+static void asrc_proc_create(void)
+{
+       struct proc_dir_entry *proc_attr;
+       proc_asrc = proc_mkdir(ASRC_PROC_PATH, NULL);
+       if (proc_asrc) {
+               proc_attr = create_proc_entry("ChSettings",
+                                             S_IFREG | S_IRUGO |
+                                             S_IWUSR, proc_asrc);
+               if (proc_attr) {
+                       proc_attr->read_proc = asrc_read_proc_attr;
+                       proc_attr->write_proc = asrc_write_proc_attr;
+                       proc_attr->size = 48;
+                       proc_attr->uid = proc_attr->gid = 0;
+               } else {
+                       remove_proc_entry(ASRC_PROC_PATH, NULL);
+                       pr_info("Failed to create proc attribute entry \n");
+               }
+       } else {
+               pr_info("ASRC: Failed to create proc entry %s\n",
+                       ASRC_PROC_PATH);
+       }
+}
+
+/*!
+ * Entry point for the asrc device
+ *
+ * @param      pdev Pionter to the registered platform device
+ * @return  Error code indicating success or failure
+ */
+static int mxc_asrc_probe(struct platform_device *pdev)
+{
+       int err = 0;
+       struct resource *res;
+       struct device *temp_class;
+       int irq;
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res)
+               return -ENOENT;
+
+       g_asrc_data = kzalloc(sizeof(struct asrc_data), GFP_KERNEL);
+
+       if (g_asrc_data == NULL) {
+               pr_info("Failed to allocate g_asrc_data\n");
+               return -ENOMEM;
+       }
+
+       g_asrc_data->asrc_pair[0].chn_max = 2;
+       g_asrc_data->asrc_pair[1].chn_max = 2;
+       g_asrc_data->asrc_pair[2].chn_max = 6;
+       g_asrc_data->asrc_pair[0].overload_error = 0;
+       g_asrc_data->asrc_pair[1].overload_error = 0;
+       g_asrc_data->asrc_pair[2].overload_error = 0;
+
+       asrc_major = register_chrdev(asrc_major, "mxc_asrc", &asrc_fops);
+       if (asrc_major < 0) {
+               pr_info("Unable to register asrc device\n");
+               err = -EBUSY;
+               goto error;
+       }
+
+       asrc_class = class_create(THIS_MODULE, "mxc_asrc");
+       if (IS_ERR(asrc_class)) {
+               err = PTR_ERR(asrc_class);
+               goto err_out_chrdev;
+       }
+
+       temp_class = device_create(asrc_class, NULL, MKDEV(asrc_major, 0),
+                                  NULL, "mxc_asrc");
+       if (IS_ERR(temp_class)) {
+               err = PTR_ERR(temp_class);
+               goto err_out_class;
+       }
+
+       asrc_vrt_base_addr =
+           (unsigned long)ioremap(res->start, res->end - res->start + 1);
+
+       mxc_asrc_data =
+           (struct mxc_asrc_platform_data *)pdev->dev.platform_data;
+       clk_enable(mxc_asrc_data->asrc_core_clk);
+
+       switch (mxc_asrc_data->clk_map_ver) {
+       case 1:
+               input_clk_map = &input_clk_map_v1[0];
+               output_clk_map = &output_clk_map_v1[0];
+               break;
+       case 2:
+       default:
+               input_clk_map = &input_clk_map_v2[0];
+               output_clk_map = &output_clk_map_v2[0];
+               break;
+       }
+       irq = platform_get_irq(pdev, 0);
+       if (request_irq(irq, asrc_isr, 0, "asrc", NULL))
+               return -1;
+
+       asrc_proc_create();
+       err = mxc_init_asrc();
+       if (err < 0)
+               goto err_out_class;
+
+       goto out;
+
+      err_out_class:
+       clk_disable(mxc_asrc_data->asrc_core_clk);
+       device_destroy(asrc_class, MKDEV(asrc_major, 0));
+       class_destroy(asrc_class);
+      err_out_chrdev:
+       unregister_chrdev(asrc_major, "mxc_asrc");
+      error:
+       kfree(g_asrc_data);
+      out:
+       pr_info("mxc_asrc registered\n");
+       return err;
+}
+
+/*!
+ * Exit asrc device
+ *
+ * @param      pdev Pionter to the registered platform device
+ * @return  Error code indicating success or failure
+ */
+static int mxc_asrc_remove(struct platform_device *pdev)
+{
+       int irq = platform_get_irq(pdev, 0);
+       free_irq(irq, NULL);
+       kfree(g_asrc_data);
+       clk_disable(mxc_asrc_data->asrc_core_clk);
+       mxc_asrc_data = NULL;
+       iounmap((unsigned long __iomem *)asrc_vrt_base_addr);
+       remove_proc_entry("ChSettings", proc_asrc);
+       remove_proc_entry(ASRC_PROC_PATH, NULL);
+       device_destroy(asrc_class, MKDEV(asrc_major, 0));
+       class_destroy(asrc_class);
+       unregister_chrdev(asrc_major, "mxc_asrc");
+       return 0;
+}
+
+/*! mxc asrc driver definition
+ *
+ */
+static struct platform_driver mxc_asrc_driver = {
+       .driver = {
+                  .name = "mxc_asrc",
+                  },
+       .probe = mxc_asrc_probe,
+       .remove = mxc_asrc_remove,
+};
+
+/*!
+ * Register asrc driver
+ *
+ */
+static __init int asrc_init(void)
+{
+       int ret;
+       ret = platform_driver_register(&mxc_asrc_driver);
+       return ret;
+}
+
+/*!
+ * Exit and free the asrc data
+ *
+ */ static void __exit asrc_exit(void)
+{
+       platform_driver_unregister(&mxc_asrc_driver);
+       return;
+}
+
+module_init(asrc_init);
+module_exit(asrc_exit);
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Asynchronous Sample Rate Converter");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/bt/Kconfig b/drivers/mxc/bt/Kconfig
new file mode 100644 (file)
index 0000000..9dbfbe5
--- /dev/null
@@ -0,0 +1,13 @@
+#
+# Bluetooth configuration
+#
+
+menu "MXC Bluetooth support"
+
+config MXC_BLUETOOTH
+       tristate "MXC Bluetooth support"
+       depends on MACH_MX31_3DS || MACH_MX35_3DS || MACH_MX37_3DS || MACH_MX51_3DS
+       ---help---
+         Say Y to get the third party Bluetooth service.
+
+endmenu
diff --git a/drivers/mxc/bt/Makefile b/drivers/mxc/bt/Makefile
new file mode 100644 (file)
index 0000000..91bc4cf
--- /dev/null
@@ -0,0 +1,4 @@
+#
+# Makefile for the kernel Bluetooth power-on/reset
+#
+obj-$(CONFIG_MXC_BLUETOOTH) += mxc_bt.o
diff --git a/drivers/mxc/bt/mxc_bt.c b/drivers/mxc/bt/mxc_bt.c
new file mode 100644 (file)
index 0000000..ba92318
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * 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 mxc_bt.c
+ *
+ * @brief MXC Thirty party Bluetooth
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/regulator/consumer.h>
+#include <linux/fsl_devices.h>
+
+static struct regulator *bt_vdd;
+static struct regulator *bt_vdd_parent;
+static struct regulator *bt_vusb;
+static struct regulator *bt_vusb_parent;
+
+/*!
+  * This function poweron the bluetooth hardware module
+  *
+  * @param      pdev    Pointer to the platform device
+  * @return              0 on success, -1 otherwise.
+  */
+static int mxc_bt_probe(struct platform_device *pdev)
+{
+       struct mxc_bt_platform_data *platform_data;
+       platform_data = (struct mxc_bt_platform_data *)pdev->dev.platform_data;
+       if (platform_data->bt_vdd) {
+               bt_vdd = regulator_get(&pdev->dev, platform_data->bt_vdd);
+               regulator_enable(bt_vdd);
+       }
+       if (platform_data->bt_vdd_parent) {
+               bt_vdd_parent =
+                   regulator_get(&pdev->dev, platform_data->bt_vdd_parent);
+               regulator_enable(bt_vdd_parent);
+       }
+       if (platform_data->bt_vusb) {
+               bt_vusb = regulator_get(&pdev->dev, platform_data->bt_vusb);
+               regulator_enable(bt_vusb);
+       }
+       if (platform_data->bt_vusb_parent) {
+               bt_vusb_parent =
+                   regulator_get(&pdev->dev, platform_data->bt_vusb_parent);
+               regulator_enable(bt_vusb_parent);
+       }
+
+       if (platform_data->bt_reset != NULL)
+               platform_data->bt_reset();
+       return 0;
+
+}
+
+/*!
+  * This function poweroff the bluetooth hardware module
+  *
+  * @param      pdev    Pointer to the platform device
+  * @return              0 on success, -1 otherwise.
+  */
+static int mxc_bt_remove(struct platform_device *pdev)
+{
+       struct mxc_bt_platform_data *platform_data;
+       platform_data = (struct mxc_bt_platform_data *)pdev->dev.platform_data;
+       if (bt_vdd) {
+               regulator_disable(bt_vdd);
+               regulator_put(bt_vdd);
+       }
+       if (bt_vdd_parent) {
+               regulator_disable(bt_vdd_parent);
+               regulator_put(bt_vdd_parent);
+       }
+       if (bt_vusb) {
+               regulator_disable(bt_vusb);
+               regulator_put(bt_vusb);
+       }
+       if (bt_vusb_parent) {
+               regulator_disable(bt_vusb_parent);
+               regulator_put(bt_vusb_parent);
+       }
+       return 0;
+
+}
+
+static struct platform_driver bluetooth_driver = {
+       .driver = {
+                  .name = "mxc_bt",
+                  },
+       .probe = mxc_bt_probe,
+       .remove = mxc_bt_remove,
+};
+
+/*!
+ * Register bluetooth driver module
+ *
+ */
+static __init int bluetooth_init(void)
+{
+       return platform_driver_register(&bluetooth_driver);
+}
+
+/*!
+ * Exit and free the bluetooth module
+ *
+ */
+static void __exit bluetooth_exit(void)
+{
+       platform_driver_unregister(&bluetooth_driver);
+}
+
+module_init(bluetooth_init);
+module_exit(bluetooth_exit);
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC Thirty party Bluetooth");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/dam/Kconfig b/drivers/mxc/dam/Kconfig
new file mode 100644 (file)
index 0000000..7b4bee9
--- /dev/null
@@ -0,0 +1,13 @@
+#
+# DAM API configuration
+#
+
+menu "MXC Digital Audio Multiplexer support"
+
+config MXC_DAM
+       tristate "DAM support"
+       depends on ARCH_MXC
+       ---help---
+         Say Y to get the Digital Audio Multiplexer services API available on MXC platform.
+
+endmenu
diff --git a/drivers/mxc/dam/Makefile b/drivers/mxc/dam/Makefile
new file mode 100644 (file)
index 0000000..b5afdc1
--- /dev/null
@@ -0,0 +1,9 @@
+#
+# Makefile for the kernel Digital Audio MUX (DAM) device driver.
+#
+
+ifeq ($(CONFIG_ARCH_MX27),y)
+       obj-$(CONFIG_MXC_DAM) += dam_v1.o
+else
+       obj-$(CONFIG_MXC_DAM) += dam.o
+endif
diff --git a/drivers/mxc/dam/dam.c b/drivers/mxc/dam/dam.c
new file mode 100644 (file)
index 0000000..9e57e1a
--- /dev/null
@@ -0,0 +1,427 @@
+/*
+ * 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 dam.c
+ * @brief This is the brief documentation for this dam.c file.
+ *
+ * This file contains the implementation of the DAM driver main services
+ *
+ * @ingroup DAM
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include "dam.h"
+
+/*!
+ * This include to define bool type, false and true definitions.
+ */
+#include <mach/hardware.h>
+
+#define ModifyRegister32(a, b, c)      (c = (((c)&(~(a))) | (b)))
+
+#define DAM_VIRT_BASE_ADDR     IO_ADDRESS(AUDMUX_BASE_ADDR)
+
+#ifndef _reg_DAM_PTCR1
+#define    _reg_DAM_PTCR1   (*((volatile unsigned long *) \
+               (DAM_VIRT_BASE_ADDR + 0x00)))
+#endif
+
+#ifndef _reg_DAM_PDCR1
+#define    _reg_DAM_PDCR1  (*((volatile unsigned long *) \
+               (DAM_VIRT_BASE_ADDR + 0x04)))
+#endif
+
+#ifndef _reg_DAM_PTCR2
+#define    _reg_DAM_PTCR2   (*((volatile unsigned long *) \
+               (DAM_VIRT_BASE_ADDR + 0x08)))
+#endif
+
+#ifndef _reg_DAM_PDCR2
+#define    _reg_DAM_PDCR2  (*((volatile unsigned long *) \
+               (DAM_VIRT_BASE_ADDR + 0x0C)))
+#endif
+
+#ifndef _reg_DAM_PTCR3
+#define    _reg_DAM_PTCR3   (*((volatile unsigned long *) \
+               (DAM_VIRT_BASE_ADDR + 0x10)))
+#endif
+
+#ifndef _reg_DAM_PDCR3
+#define    _reg_DAM_PDCR3  (*((volatile unsigned long *) \
+               (DAM_VIRT_BASE_ADDR + 0x14)))
+#endif
+
+#ifndef _reg_DAM_PTCR4
+#define    _reg_DAM_PTCR4   (*((volatile unsigned long *) \
+               (DAM_VIRT_BASE_ADDR + 0x18)))
+#endif
+
+#ifndef _reg_DAM_PDCR4
+#define    _reg_DAM_PDCR4  (*((volatile unsigned long *) \
+               (DAM_VIRT_BASE_ADDR + 0x1C)))
+#endif
+
+#ifndef _reg_DAM_PTCR5
+#define    _reg_DAM_PTCR5   (*((volatile unsigned long *) \
+               (DAM_VIRT_BASE_ADDR + 0x20)))
+#endif
+
+#ifndef _reg_DAM_PDCR5
+#define    _reg_DAM_PDCR5  (*((volatile unsigned long *) \
+               (DAM_VIRT_BASE_ADDR + 0x24)))
+#endif
+
+#ifndef _reg_DAM_PTCR6
+#define    _reg_DAM_PTCR6   (*((volatile unsigned long *) \
+               (DAM_VIRT_BASE_ADDR + 0x28)))
+#endif
+
+#ifndef _reg_DAM_PDCR6
+#define    _reg_DAM_PDCR6  (*((volatile unsigned long *) \
+               (DAM_VIRT_BASE_ADDR + 0x2C)))
+#endif
+
+#ifndef _reg_DAM_PTCR7
+#define    _reg_DAM_PTCR7   (*((volatile unsigned long *) \
+               (DAM_VIRT_BASE_ADDR + 0x30)))
+#endif
+
+#ifndef _reg_DAM_PDCR7
+#define    _reg_DAM_PDCR7  (*((volatile unsigned long *) \
+               (DAM_VIRT_BASE_ADDR + 0x34)))
+#endif
+
+#ifndef _reg_DAM_CNMCR
+#define    _reg_DAM_CNMCR   (*((volatile unsigned long *) \
+               (DAM_VIRT_BASE_ADDR + 0x38)))
+#endif
+
+#ifndef _reg_DAM_PTCR
+#define    _reg_DAM_PTCR(a)   (*((volatile unsigned long *) \
+               (DAM_VIRT_BASE_ADDR + a*8)))
+#endif
+
+#ifndef _reg_DAM_PDCR
+#define    _reg_DAM_PDCR(a)   (*((volatile unsigned long *) \
+               (DAM_VIRT_BASE_ADDR + 4 + a*8)))
+#endif
+
+/*!
+ * PTCR Registers bit shift definitions
+ */
+#define dam_synchronous_mode_shift               11
+#define dam_receive_clock_select_shift           12
+#define dam_receive_clock_direction_shift        16
+#define dam_receive_frame_sync_select_shift      17
+#define dam_receive_frame_sync_direction_shift   21
+#define dam_transmit_clock_select_shift          22
+#define dam_transmit_clock_direction_shift       26
+#define dam_transmit_frame_sync_select_shift     27
+#define dam_transmit_frame_sync_direction_shift  31
+#define dam_selection_mask                      0xF
+
+/*!
+ * HPDCR Register bit shift definitions
+ */
+#define dam_internal_network_mode_shift           0
+#define dam_mode_shift                            8
+#define dam_transmit_receive_switch_shift        12
+#define dam_receive_data_select_shift            13
+
+/*!
+ * HPDCR Register bit masq definitions
+ */
+#define dam_mode_masq                          0x03
+#define dam_internal_network_mode_mask         0xFF
+
+/*!
+ * CNMCR Register bit shift definitions
+ */
+#define dam_ce_bus_port_cntlow_shift              0
+#define dam_ce_bus_port_cnthigh_shift             8
+#define dam_ce_bus_port_clkpol_shift             16
+#define dam_ce_bus_port_fspol_shift              17
+#define dam_ce_bus_port_enable_shift             18
+
+#define DAM_NAME   "dam"
+
+EXPORT_SYMBOL(dam_select_mode);
+EXPORT_SYMBOL(dam_select_RxClk_direction);
+EXPORT_SYMBOL(dam_select_RxClk_source);
+EXPORT_SYMBOL(dam_select_RxD_source);
+EXPORT_SYMBOL(dam_select_RxFS_direction);
+EXPORT_SYMBOL(dam_select_RxFS_source);
+EXPORT_SYMBOL(dam_select_TxClk_direction);
+EXPORT_SYMBOL(dam_select_TxClk_source);
+EXPORT_SYMBOL(dam_select_TxFS_direction);
+EXPORT_SYMBOL(dam_select_TxFS_source);
+EXPORT_SYMBOL(dam_set_internal_network_mode_mask);
+EXPORT_SYMBOL(dam_set_synchronous);
+EXPORT_SYMBOL(dam_switch_Tx_Rx);
+EXPORT_SYMBOL(dam_reset_register);
+
+/*!
+ * This function selects the operation mode of the port.
+ *
+ * @param        port              the DAM port to configure
+ * @param        the_mode          the operation mode of the port
+ *
+ * @return       This function returns the result of the operation
+ *               (0 if successful, -1 otherwise).
+ */
+int dam_select_mode(dam_port port, dam_mode the_mode)
+{
+       int result;
+       result = 0;
+
+       ModifyRegister32(dam_mode_masq << dam_mode_shift,
+                        the_mode << dam_mode_shift, _reg_DAM_PDCR(port));
+
+       return result;
+}
+
+/*!
+ * This function controls Receive clock signal direction for the port.
+ *
+ * @param        port              the DAM port to configure
+ * @param        direction         the Rx clock signal direction
+ */
+void dam_select_RxClk_direction(dam_port port, signal_direction direction)
+{
+       ModifyRegister32(1 << dam_receive_clock_direction_shift,
+                        direction << dam_receive_clock_direction_shift,
+                        _reg_DAM_PTCR(port));
+}
+
+/*!
+ * This function controls Receive clock signal source for the port.
+ *
+ * @param        p_config          the DAM port to configure
+ * @param        from_RxClk        the signal comes from RxClk or TxClk of
+ *                                 the source port
+ * @param        p_source          the source port
+ */
+void dam_select_RxClk_source(dam_port p_config,
+                            bool from_RxClk, dam_port p_source)
+{
+       ModifyRegister32(dam_selection_mask << dam_receive_clock_select_shift,
+                        ((from_RxClk << 3) | p_source) <<
+                        dam_receive_clock_select_shift,
+                        _reg_DAM_PTCR(p_config));
+}
+
+/*!
+ * This function selects the source port for the RxD data.
+ *
+ * @param        p_config          the DAM port to configure
+ * @param        p_source          the source port
+ */
+void dam_select_RxD_source(dam_port p_config, dam_port p_source)
+{
+       ModifyRegister32(dam_selection_mask << dam_receive_data_select_shift,
+                        p_source << dam_receive_data_select_shift,
+                        _reg_DAM_PDCR(p_config));
+}
+
+/*!
+ * This function controls Receive Frame Sync signal direction for the port.
+ *
+ * @param        port              the DAM port to configure
+ * @param        direction         the Rx Frame Sync signal direction
+ */
+void dam_select_RxFS_direction(dam_port port, signal_direction direction)
+{
+       ModifyRegister32(1 << dam_receive_frame_sync_direction_shift,
+                        direction << dam_receive_frame_sync_direction_shift,
+                        _reg_DAM_PTCR(port));
+}
+
+/*!
+ * This function controls Receive Frame Sync signal source for the port.
+ *
+ * @param        p_config          the DAM port to configure
+ * @param        from_RxFS         the signal comes from RxFS or TxFS of
+ *                                 the source port
+ * @param        p_source          the source port
+ */
+void dam_select_RxFS_source(dam_port p_config,
+                           bool from_RxFS, dam_port p_source)
+{
+       ModifyRegister32(dam_selection_mask <<
+                        dam_receive_frame_sync_select_shift,
+                        ((from_RxFS << 3) | p_source) <<
+                        dam_receive_frame_sync_select_shift,
+                        _reg_DAM_PTCR(p_config));
+}
+
+/*!
+ * This function controls Transmit clock signal direction for the port.
+ *
+ * @param        port              the DAM port to configure
+ * @param        direction         the Tx clock signal direction
+ */
+void dam_select_TxClk_direction(dam_port port, signal_direction direction)
+{
+       ModifyRegister32(1 << dam_transmit_clock_direction_shift,
+                        direction << dam_transmit_clock_direction_shift,
+                        _reg_DAM_PTCR(port));
+}
+
+/*!
+ * This function controls Transmit clock signal source for the port.
+ *
+ * @param        p_config          the DAM port to configure
+ * @param        from_RxClk        the signal comes from RxClk or TxClk of
+ *                                 the source port
+ * @param        p_source          the source port
+ */
+void dam_select_TxClk_source(dam_port p_config,
+                            bool from_RxClk, dam_port p_source)
+{
+       ModifyRegister32(dam_selection_mask << dam_transmit_clock_select_shift,
+                        ((from_RxClk << 3) | p_source) <<
+                        dam_transmit_clock_select_shift,
+                        _reg_DAM_PTCR(p_config));
+}
+
+/*!
+ * This function controls Transmit Frame Sync signal direction for the port.
+ *
+ * @param        port              the DAM port to configure
+ * @param        direction         the Tx Frame Sync signal direction
+ */
+void dam_select_TxFS_direction(dam_port port, signal_direction direction)
+{
+       ModifyRegister32(1 << dam_transmit_frame_sync_direction_shift,
+                        direction << dam_transmit_frame_sync_direction_shift,
+                        _reg_DAM_PTCR(port));
+}
+
+/*!
+ * This function controls Transmit Frame Sync signal source for the port.
+ *
+ * @param        p_config          the DAM port to configure
+ * @param        from_RxFS         the signal comes from RxFS or TxFS of
+ *                                 the source port
+ * @param        p_source          the source port
+ */
+void dam_select_TxFS_source(dam_port p_config,
+                           bool from_RxFS, dam_port p_source)
+{
+       ModifyRegister32(dam_selection_mask <<
+                        dam_transmit_frame_sync_select_shift,
+                        ((from_RxFS << 3) | p_source) <<
+                        dam_transmit_frame_sync_select_shift,
+                        _reg_DAM_PTCR(p_config));
+}
+
+/*!
+ * This function sets a bit mask that selects the port from which of the RxD
+ * signals are to be ANDed together for internal network mode.
+ * Bit 6 represents RxD from Port7 and bit0 represents RxD from Port1.
+ * 1 excludes RxDn from ANDing. 0 includes RxDn for ANDing.
+ *
+ * @param        port              the DAM port to configure
+ * @param        bit_mask          the bit mask
+ *
+ * @return       This function returns the result of the operation
+ *               (0 if successful, -1 otherwise).
+ */
+int dam_set_internal_network_mode_mask(dam_port port, unsigned char bit_mask)
+{
+       int result;
+       result = 0;
+
+       ModifyRegister32(dam_internal_network_mode_mask <<
+                        dam_internal_network_mode_shift,
+                        bit_mask << dam_internal_network_mode_shift,
+                        _reg_DAM_PDCR(port));
+
+       return result;
+}
+
+/*!
+ * This function controls whether or not the port is in synchronous mode.
+ * When the synchronous mode is selected, the receive and the transmit sections
+ * use common clock and frame sync signals.
+ * When the synchronous mode is not selected, separate clock and frame sync
+ * signals are used for the transmit and the receive sections.
+ * The defaut value is the synchronous mode selected.
+ *
+ * @param        port              the DAM port to configure
+ * @param        synchronous       the state to assign
+ */
+void dam_set_synchronous(dam_port port, bool synchronous)
+{
+       ModifyRegister32(1 << dam_synchronous_mode_shift,
+                        synchronous << dam_synchronous_mode_shift,
+                        _reg_DAM_PTCR(port));
+}
+
+/*!
+ * This function swaps the transmit and receive signals from (Da-TxD, Db-RxD)
+ * to (Da-RxD, Db-TxD).
+ * This default signal configuration is Da-TxD, Db-RxD.
+ *
+ * @param        port              the DAM port to configure
+ * @param        value             the switch state
+ */
+void dam_switch_Tx_Rx(dam_port port, bool value)
+{
+       ModifyRegister32(1 << dam_transmit_receive_switch_shift,
+                        value << dam_transmit_receive_switch_shift,
+                        _reg_DAM_PDCR(port));
+}
+
+/*!
+ * This function resets the two registers of the selected port.
+ *
+ * @param        port              the DAM port to reset
+ */
+void dam_reset_register(dam_port port)
+{
+       ModifyRegister32(0xFFFFFFFF, 0x00000000, _reg_DAM_PTCR(port));
+       ModifyRegister32(0xFFFFFFFF, 0x00000000, _reg_DAM_PDCR(port));
+}
+
+/*!
+ * This function implements the init function of the DAM device.
+ * This function is called when the module is loaded.
+ *
+ * @return       This function returns 0.
+ */
+static int __init dam_init(void)
+{
+       return 0;
+}
+
+/*!
+ * This function implements the exit function of the SPI device.
+ * This function is called when the module is unloaded.
+ *
+ */
+static void __exit dam_exit(void)
+{
+}
+
+module_init(dam_init);
+module_exit(dam_exit);
+
+MODULE_DESCRIPTION("DAM char device driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/dam/dam.h b/drivers/mxc/dam/dam.h
new file mode 100644 (file)
index 0000000..56c6d46
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ * 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 DAM Digital Audio Multiplexer (AUDMUX) Driver
+  */
+
+ /*!
+  * @file dam.h
+  * @brief This is the brief documentation for this dam.h file.
+  *
+  * This header file contains DAM driver functions prototypes.
+  *
+  * @ingroup DAM
+  */
+
+#ifndef __MXC_DAM_H__
+#define __MXC_DAM_H__
+
+/*!
+ * This enumeration describes the Digital Audio Multiplexer mode.
+ */
+typedef enum {
+
+       /*!
+        * Normal mode
+        */
+       normal_mode = 0,
+
+       /*!
+        * Internal network mode
+        */
+       internal_network_mode = 1,
+
+       /*!
+        * CE bus network mode
+        */
+       CE_bus_network_mode = 2
+} dam_mode;
+
+/*!
+ * This enumeration describes the port.
+ */
+typedef enum {
+
+       /*!
+        * The port 1
+        */
+       port_1 = 0,
+
+       /*!
+        * The port 2
+        */
+       port_2 = 1,
+
+       /*!
+        * The port 3
+        */
+       port_3 = 2,
+
+       /*!
+        * The port 4
+        */
+       port_4 = 3,
+
+       /*!
+        * The port 5
+        */
+       port_5 = 4,
+
+       /*!
+        * The port 6
+        */
+       port_6 = 5,
+
+       /*!
+        * The port 7
+        */
+       port_7 = 6
+} dam_port;
+
+/*!
+ * This enumeration describes the signal direction.
+ */
+typedef enum {
+
+       /*!
+        * Signal In
+        */
+       signal_in = 0,
+
+       /*!
+        * Signal Out
+        */
+       signal_out = 1
+} signal_direction;
+
+/*!
+ * Test purpose definition
+ */
+#define TEST_DAM 1
+
+#ifdef TEST_DAM
+
+#define DAM_IOCTL 0x55
+#define DAM_CONFIG_SSI1_MC13783 _IOWR(DAM_IOCTL, 1, int)
+#define DAM_CONFIG_SSI2_MC13783 _IOWR(DAM_IOCTL, 2, int)
+#define DAM_CONFIG_SSI_NETWORK_MODE_MC13783 _IOWR(DAM_IOCTL, 3, int)
+#endif
+
+/*!
+ * This function selects the operation mode of the port.
+ *
+ * @param        port              the DAM port to configure
+ * @param        the_mode          the operation mode of the port
+ * @return       This function returns the result of the operation
+ *               (0 if successful, -1 otherwise).
+ */
+int dam_select_mode(dam_port port, dam_mode the_mode);
+
+/*!
+ * This function controls Receive clock signal direction for the port.
+ *
+ * @param        port              the DAM port to configure
+ * @param        direction         the Rx clock signal direction
+ */
+void dam_select_RxClk_direction(dam_port port, signal_direction direction);
+
+/*!
+ * This function controls Receive clock signal source for the port.
+ *
+ * @param        p_config          the DAM port to configure
+ * @param        from_RxClk        the signal comes from RxClk or TxClk of
+ *                                 the source port
+ * @param        p_source          the source port
+ */
+void dam_select_RxClk_source(dam_port p_config, bool from_RxClk,
+                            dam_port p_source);
+
+/*!
+ * This function selects the source port for the RxD data.
+ *
+ * @param        p_config          the DAM port to configure
+ * @param        p_source          the source port
+ */
+void dam_select_RxD_source(dam_port p_config, dam_port p_source);
+
+/*!
+ * This function controls Receive Frame Sync signal direction for the port.
+ *
+ * @param        port              the DAM port to configure
+ * @param        direction         the Rx Frame Sync signal direction
+ */
+void dam_select_RxFS_direction(dam_port port, signal_direction direction);
+
+/*!
+ * This function controls Receive Frame Sync signal source for the port.
+ *
+ * @param        p_config          the DAM port to configure
+ * @param        from_RxFS         the signal comes from RxFS or TxFS of
+ *                                 the source port
+ * @param        p_source          the source port
+ */
+void dam_select_RxFS_source(dam_port p_config, bool from_RxFS,
+                           dam_port p_source);
+
+/*!
+ * This function controls Transmit clock signal direction for the port.
+ *
+ * @param        port              the DAM port to configure
+ * @param        direction         the Tx clock signal direction
+ */
+void dam_select_TxClk_direction(dam_port port, signal_direction direction);
+
+/*!
+ * This function controls Transmit clock signal source for the port.
+ *
+ * @param        p_config          the DAM port to configure
+ * @param        from_RxClk        the signal comes from RxClk or TxClk of
+ *                                 the source port
+ * @param        p_source          the source port
+ */
+void dam_select_TxClk_source(dam_port p_config, bool from_RxClk,
+                            dam_port p_source);
+
+/*!
+ * This function controls Transmit Frame Sync signal direction for the port.
+ *
+ * @param        port              the DAM port to configure
+ * @param        direction         the Tx Frame Sync signal direction
+ */
+void dam_select_TxFS_direction(dam_port port, signal_direction direction);
+
+/*!
+ * This function controls Transmit Frame Sync signal source for the port.
+ *
+ * @param        p_config          the DAM port to configure
+ * @param        from_RxFS         the signal comes from RxFS or TxFS of
+ *                                 the source port
+ * @param        p_source          the source port
+ */
+void dam_select_TxFS_source(dam_port p_config, bool from_RxFS,
+                           dam_port p_source);
+
+/*!
+ * This function sets a bit mask that selects the port from which of
+ * the RxD signals are to be ANDed together for internal network mode.
+ * Bit 6 represents RxD from Port7 and bit0 represents RxD from Port1.
+ * 1 excludes RxDn from ANDing. 0 includes RxDn for ANDing.
+ *
+ * @param        port              the DAM port to configure
+ * @param        bit_mask          the bit mask
+ * @return       This function returns the result of the operation
+ *               (0 if successful, -1 otherwise).
+ */
+int dam_set_internal_network_mode_mask(dam_port port, unsigned char bit_mask);
+
+/*!
+ * This function controls whether or not the port is in synchronous mode.
+ * When the synchronous mode is selected, the receive and the transmit sections
+ * use common clock and frame sync signals.
+ * When the synchronous mode is not selected, separate clock and frame sync
+ * signals are used for the transmit and the receive sections.
+ * The defaut value is the synchronous mode selected.
+ *
+ * @param        port              the DAM port to configure
+ * @param        synchronous       the state to assign
+ */
+void dam_set_synchronous(dam_port port, bool synchronous);
+
+/*!
+ * This function swaps the transmit and receive signals from (Da-TxD, Db-RxD) to
+ * (Da-RxD, Db-TxD).
+ * This default signal configuration is Da-TxD, Db-RxD.
+ *
+ * @param        port              the DAM port to configure
+ * @param        value             the switch state
+ */
+void dam_switch_Tx_Rx(dam_port port, bool value);
+
+/*!
+ * This function resets the two registers of the selected port.
+ *
+ * @param        port              the DAM port to reset
+ */
+void dam_reset_register(dam_port port);
+
+#endif
diff --git a/drivers/mxc/dam/dam_v1.c b/drivers/mxc/dam/dam_v1.c
new file mode 100644 (file)
index 0000000..5e3cdf8
--- /dev/null
@@ -0,0 +1,617 @@
+/*
+ * 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 dam_v1.c
+ * @brief This is the brief documentation for this dam_v1.c file.
+ *
+ * This file contains the implementation of the DAM driver main services
+ *
+ * @ingroup DAM
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <asm/uaccess.h>
+#include "dam.h"
+
+/*!
+ * This include to define bool type, false and true definitions.
+ */
+#include <mach/hardware.h>
+
+#define DAM_VIRT_BASE_ADDR     IO_ADDRESS(AUDMUX_BASE_ADDR)
+
+#define ModifyRegister32(a, b, c)      do {\
+       __raw_writel(((__raw_readl(c)) & (~(a))) | (b), (c));\
+} while (0)
+
+#ifndef _reg_DAM_HPCR1
+#define    _reg_DAM_HPCR1   (*((volatile unsigned long *) \
+               (DAM_VIRT_BASE_ADDR + 0x00)))
+#endif
+
+#ifndef _reg_DAM_HPCR2
+#define    _reg_DAM_HPCR2   (*((volatile unsigned long *) \
+               (DAM_VIRT_BASE_ADDR + 0x04)))
+#endif
+
+#ifndef _reg_DAM_HPCR3
+#define    _reg_DAM_HPCR3   (*((volatile unsigned long *) \
+               (DAM_VIRT_BASE_ADDR + 0x08)))
+#endif
+
+#ifndef _reg_DAM_PPCR1
+#define    _reg_DAM_PPCR1   (*((volatile unsigned long *) \
+               (DAM_VIRT_BASE_ADDR + 0x10)))
+#endif
+
+#ifndef _reg_DAM_PPCR2
+#define    _reg_DAM_PPCR2  (*((volatile unsigned long *) \
+               (DAM_VIRT_BASE_ADDR + 0x14)))
+#endif
+
+#ifndef _reg_DAM_PPCR3
+#define    _reg_DAM_PPCR3   (*((volatile unsigned long *) \
+               (DAM_VIRT_BASE_ADDR + 0x1c)))
+#endif
+
+#ifndef _reg_DAM_HPCR
+#define    _reg_DAM_HPCR(a)   ((volatile unsigned long *) \
+               (DAM_VIRT_BASE_ADDR + (a)*4))
+#endif
+
+#ifndef _reg_DAM_PPCR
+#define    _reg_DAM_PPCR(a)   ((volatile unsigned long *) \
+               (DAM_VIRT_BASE_ADDR + 0x0c + (0x04 << (a-3))))
+#endif
+
+/*!
+ * HPCR/PPCR Registers bit shift definitions
+ */
+#define dam_transmit_frame_sync_direction_shift  31
+#define dam_transmit_clock_direction_shift       30
+#define dam_transmit_frame_sync_select_shift     26
+#define dam_transmit_clock_select_shift          26
+#define dam_receive_frame_sync_direction_shift   25
+#define dam_receive_clock_direction_shift        24
+#define dam_receive_clock_select_shift           20
+#define dam_receive_frame_sync_select_shift      20
+
+#define dam_receive_data_select_shift            13
+#define dam_synchronous_mode_shift               12
+
+#define dam_transmit_receive_switch_shift        10
+
+#define dam_mode_shift                            8
+#define dam_internal_network_mode_shift           0
+
+/*!
+ * HPCR/PPCR Register bit masq definitions
+ */
+/*#define dam_selection_mask              0xF*/
+#define dam_fs_selection_mask             0xF
+#define dam_clk_selection_mask            0xF
+#define dam_dat_selection_mask                 0x7
+/*#define dam_mode_masq                   0x03*/
+#define dam_internal_network_mode_mask    0xFF
+
+/*!
+ * HPCR/PPCR Register reset value definitions
+ */
+#define dam_hpcr_default_value 0x00001000
+#define dam_ppcr_default_value 0x00001000
+
+#define DAM_NAME   "dam"
+static struct class *mxc_dam_class;
+
+EXPORT_SYMBOL(dam_select_mode);
+EXPORT_SYMBOL(dam_select_RxClk_direction);
+EXPORT_SYMBOL(dam_select_RxClk_source);
+EXPORT_SYMBOL(dam_select_RxD_source);
+EXPORT_SYMBOL(dam_select_RxFS_direction);
+EXPORT_SYMBOL(dam_select_RxFS_source);
+EXPORT_SYMBOL(dam_select_TxClk_direction);
+EXPORT_SYMBOL(dam_select_TxClk_source);
+EXPORT_SYMBOL(dam_select_TxFS_direction);
+EXPORT_SYMBOL(dam_select_TxFS_source);
+EXPORT_SYMBOL(dam_set_internal_network_mode_mask);
+EXPORT_SYMBOL(dam_set_synchronous);
+EXPORT_SYMBOL(dam_switch_Tx_Rx);
+EXPORT_SYMBOL(dam_reset_register);
+
+/*!
+ * DAM major
+ */
+#ifdef TEST_DAM
+static int major_dam;
+
+typedef struct _mxc_cfg {
+       int reg;
+       int val;
+} mxc_cfg;
+
+#endif
+
+/*!
+ * This function selects the operation mode of the port.
+ *
+ * @param        port              the DAM port to configure
+ * @param        the_mode          the operation mode of the port
+ *
+ * @return       This function returns the result of the operation
+ *               (0 if successful, -1 otherwise).
+ */
+int dam_select_mode(dam_port port, dam_mode the_mode)
+{
+       int result;
+       result = 0;
+
+       if (port >= 3)
+               the_mode = normal_mode;
+       ModifyRegister32(1 << dam_mode_shift,
+                        the_mode << dam_mode_shift, _reg_DAM_HPCR(port));
+
+       return result;
+}
+
+/*!
+ * This function controls Receive clock signal direction for the port.
+ *
+ * @param        port              the DAM port to configure
+ * @param        direction         the Rx clock signal direction
+ */
+void dam_select_RxClk_direction(dam_port port, signal_direction direction)
+{
+       if (port < 3) {
+               ModifyRegister32(1 << dam_receive_clock_direction_shift,
+                                direction << dam_receive_clock_direction_shift,
+                                _reg_DAM_HPCR(port));
+       } else {
+               ModifyRegister32(1 << dam_receive_clock_direction_shift,
+                                direction << dam_receive_clock_direction_shift,
+                                _reg_DAM_PPCR(port));
+       }
+       return;
+}
+
+/*!
+ * This function controls Receive clock signal source for the port.
+ *
+ * @param        p_config          the DAM port to configure
+ * @param        from_RxClk        the signal comes from RxClk or TxClk of
+ *                                 the source port
+ * @param        p_source          the source port
+ */
+void dam_select_RxClk_source(dam_port p_config,
+                            bool from_RxClk, dam_port p_source)
+{
+       if (p_config < 3) {
+               ModifyRegister32(dam_clk_selection_mask <<
+                                dam_receive_clock_select_shift,
+                                ((from_RxClk << 3) | p_source) <<
+                                dam_receive_clock_select_shift,
+                                _reg_DAM_HPCR(p_config));
+       } else {
+               ModifyRegister32(dam_clk_selection_mask <<
+                                dam_receive_clock_select_shift,
+                                ((from_RxClk << 3) | p_source) <<
+                                dam_receive_clock_select_shift,
+                                _reg_DAM_PPCR(p_config));
+       }
+       return;
+}
+
+/*!
+ * This function selects the source port for the RxD data.
+ *
+ * @param        p_config          the DAM port to configure
+ * @param        p_source          the source port
+ */
+void dam_select_RxD_source(dam_port p_config, dam_port p_source)
+{
+       if (p_config < 3) {
+               ModifyRegister32(dam_dat_selection_mask <<
+                                dam_receive_data_select_shift,
+                                p_source << dam_receive_data_select_shift,
+                                _reg_DAM_HPCR(p_config));
+       } else {
+               ModifyRegister32(dam_dat_selection_mask <<
+                                dam_receive_data_select_shift,
+                                p_source << dam_receive_data_select_shift,
+                                _reg_DAM_PPCR(p_config));
+       }
+       return;
+}
+
+/*!
+ * This function controls Receive Frame Sync signal direction for the port.
+ *
+ * @param        port              the DAM port to configure
+ * @param        direction         the Rx Frame Sync signal direction
+ */
+void dam_select_RxFS_direction(dam_port port, signal_direction direction)
+{
+       if (port < 3) {
+               ModifyRegister32(1 << dam_receive_frame_sync_direction_shift,
+                                direction <<
+                                dam_receive_frame_sync_direction_shift,
+                                _reg_DAM_HPCR(port));
+       } else {
+               ModifyRegister32(1 << dam_receive_frame_sync_direction_shift,
+                                direction <<
+                                dam_receive_frame_sync_direction_shift,
+                                _reg_DAM_PPCR(port));
+       }
+       return;
+}
+
+/*!
+ * This function controls Receive Frame Sync signal source for the port.
+ *
+ * @param        p_config          the DAM port to configure
+ * @param        from_RxFS         the signal comes from RxFS or TxFS of
+ *                                 the source port
+ * @param        p_source          the source port
+ */
+void dam_select_RxFS_source(dam_port p_config,
+                           bool from_RxFS, dam_port p_source)
+{
+       if (p_config < 3) {
+               ModifyRegister32(dam_fs_selection_mask <<
+                                dam_receive_frame_sync_select_shift,
+                                ((from_RxFS << 3) | p_source) <<
+                                dam_receive_frame_sync_select_shift,
+                                _reg_DAM_HPCR(p_config));
+       } else {
+               ModifyRegister32(dam_fs_selection_mask <<
+                                dam_receive_frame_sync_select_shift,
+                                ((from_RxFS << 3) | p_source) <<
+                                dam_receive_frame_sync_select_shift,
+                                _reg_DAM_PPCR(p_config));
+       }
+       return;
+}
+
+/*!
+ * This function controls Transmit clock signal direction for the port.
+ *
+ * @param        port              the DAM port to configure
+ * @param        direction         the Tx clock signal direction
+ */
+void dam_select_TxClk_direction(dam_port port, signal_direction direction)
+{
+       if (port < 3) {
+               ModifyRegister32(1 << dam_transmit_clock_direction_shift,
+                                direction <<
+                                dam_transmit_clock_direction_shift,
+                                _reg_DAM_HPCR(port));
+       } else {
+               ModifyRegister32(1 << dam_transmit_clock_direction_shift,
+                                direction <<
+                                dam_transmit_clock_direction_shift,
+                                _reg_DAM_PPCR(port));
+       }
+       return;
+}
+
+/*!
+ * This function controls Transmit clock signal source for the port.
+ *
+ * @param        p_config          the DAM port to configure
+ * @param        from_RxClk        the signal comes from RxClk or TxClk of
+ *                                 the source port
+ * @param        p_source          the source port
+ */
+void dam_select_TxClk_source(dam_port p_config,
+                            bool from_RxClk, dam_port p_source)
+{
+       if (p_config < 3) {
+               ModifyRegister32(dam_clk_selection_mask <<
+                                dam_transmit_clock_select_shift,
+                                ((from_RxClk << 3) | p_source) <<
+                                dam_transmit_clock_select_shift,
+                                _reg_DAM_HPCR(p_config));
+       } else {
+               ModifyRegister32(dam_clk_selection_mask <<
+                                dam_transmit_clock_select_shift,
+                                ((from_RxClk << 3) | p_source) <<
+                                dam_transmit_clock_select_shift,
+                                _reg_DAM_PPCR(p_config));
+       }
+       return;
+}
+
+/*!
+ * This function controls Transmit Frame Sync signal direction for the port.
+ *
+ * @param        port              the DAM port to configure
+ * @param        direction         the Tx Frame Sync signal direction
+ */
+void dam_select_TxFS_direction(dam_port port, signal_direction direction)
+{
+       if (port < 3) {
+               ModifyRegister32(1 << dam_transmit_frame_sync_direction_shift,
+                                direction <<
+                                dam_transmit_frame_sync_direction_shift,
+                                _reg_DAM_HPCR(port));
+       } else {
+               ModifyRegister32(1 << dam_transmit_frame_sync_direction_shift,
+                                direction <<
+                                dam_transmit_frame_sync_direction_shift,
+                                _reg_DAM_HPCR(port));
+       }
+       return;
+}
+
+/*!
+ * This function controls Transmit Frame Sync signal source for the port.
+ *
+ * @param        p_config          the DAM port to configure
+ * @param        from_RxFS         the signal comes from RxFS or TxFS of
+ *                                 the source port
+ * @param        p_source          the source port
+ */
+void dam_select_TxFS_source(dam_port p_config,
+                           bool from_RxFS, dam_port p_source)
+{
+       if (p_config < 3) {
+               ModifyRegister32(dam_fs_selection_mask <<
+                                dam_transmit_frame_sync_select_shift,
+                                ((from_RxFS << 3) | p_source) <<
+                                dam_transmit_frame_sync_select_shift,
+                                _reg_DAM_HPCR(p_config));
+       } else {
+               ModifyRegister32(dam_fs_selection_mask <<
+                                dam_transmit_frame_sync_select_shift,
+                                ((from_RxFS << 3) | p_source) <<
+                                dam_transmit_frame_sync_select_shift,
+                                _reg_DAM_PPCR(p_config));
+       }
+       return;
+}
+
+/*!
+ * This function sets a bit mask that selects the port from which of the RxD
+ * signals are to be ANDed together for internal network mode.
+ * Bit 6 represents RxD from Port7 and bit0 represents RxD from Port1.
+ * 1 excludes RxDn from ANDing. 0 includes RxDn for ANDing.
+ *
+ * @param        port              the DAM port to configure
+ * @param        bit_mask          the bit mask
+ *
+ * @return       This function returns the result of the operation
+ *               (0 if successful, -1 otherwise).
+ */
+int dam_set_internal_network_mode_mask(dam_port port, unsigned char bit_mask)
+{
+       int result;
+       result = 0;
+
+       ModifyRegister32(dam_internal_network_mode_mask <<
+                        dam_internal_network_mode_shift,
+                        bit_mask << dam_internal_network_mode_shift,
+                        _reg_DAM_HPCR(port));
+       return result;
+}
+
+/*!
+ * This function controls whether or not the port is in synchronous mode.
+ * When the synchronous mode is selected, the receive and the transmit sections
+ * use common clock and frame sync signals.
+ * When the synchronous mode is not selected, separate clock and frame sync
+ * signals are used for the transmit and the receive sections.
+ * The defaut value is the synchronous mode selected.
+ *
+ * @param        port              the DAM port to configure
+ * @param        synchronous       the state to assign
+ */
+void dam_set_synchronous(dam_port port, bool synchronous)
+{
+       if (port < 3) {
+               ModifyRegister32(1 << dam_synchronous_mode_shift,
+                                synchronous << dam_synchronous_mode_shift,
+                                _reg_DAM_HPCR(port));
+       } else {
+               ModifyRegister32(1 << dam_synchronous_mode_shift,
+                                synchronous << dam_synchronous_mode_shift,
+                                _reg_DAM_PPCR(port));
+       }
+       return;
+}
+
+/*!
+ * This function swaps the transmit and receive signals from (Da-TxD, Db-RxD)
+ * to (Da-RxD, Db-TxD).
+ * This default signal configuration is Da-TxD, Db-RxD.
+ *
+ * @param        port              the DAM port to configure
+ * @param        value             the switch state
+ */
+void dam_switch_Tx_Rx(dam_port port, bool value)
+{
+       if (port < 3) {
+               ModifyRegister32(1 << dam_transmit_receive_switch_shift,
+                                value << dam_transmit_receive_switch_shift,
+                                _reg_DAM_HPCR(port));
+       } else {
+               ModifyRegister32(1 << dam_transmit_receive_switch_shift,
+                                value << dam_transmit_receive_switch_shift,
+                                _reg_DAM_PPCR(port));
+       }
+       return;
+}
+
+/*!
+ * This function resets the two registers of the selected port.
+ *
+ * @param        port              the DAM port to reset
+ */
+void dam_reset_register(dam_port port)
+{
+       if (port < 3) {
+               ModifyRegister32(0xFFFFFFFF, dam_hpcr_default_value,
+                                _reg_DAM_HPCR(port));
+       } else {
+               ModifyRegister32(0xFFFFFFFF, dam_ppcr_default_value,
+                                _reg_DAM_PPCR(port));
+       }
+       return;
+}
+
+#ifdef TEST_DAM
+
+/*!
+ * This function implements IOCTL controls on a DAM device.
+ *
+ * @param        inode       pointer on the node
+ * @param        file        pointer on the file
+ * @param        cmd         the command
+ * @param        arg         the parameter :\n
+ * DAM_CONFIG_SSI1:\n
+ * data from port 1 to port 4, clock and FS from port 1 (SSI1)\n
+ * DAM_CONFIG_SSI2:\n
+ * data from port 2 to port 5, clock and FS from port 2 (SSI2)\n
+ * DAM_CONFIG_SSI_NETWORK_MODE:\n
+ * network mode for mix digital with data from port 1 to port4,\n
+ * data from port 2 to port 4, clock and FS from port 1 (SSI1)
+ *
+ * @return       This function returns 0 if successful.
+ */
+static int dam_ioctl(struct inode *inode,
+                    struct file *file, unsigned int cmd, unsigned long arg)
+{
+       return 0;
+}
+
+/*!
+ * This function implements the open method on a DAM device.
+ *
+ * @param        inode       pointer on the node
+ * @param        file        pointer on the file
+ *
+ * @return       This function returns 0.
+ */
+static int dam_open(struct inode *inode, struct file *file)
+{
+       /* DBG_PRINTK("ssi : dam_open()\n"); */
+       return 0;
+}
+
+/*!
+ * This function implements the release method on a DAM device.
+ *
+ * @param        inode       pointer on the node
+ * @param        file        pointer on the file
+ *
+ * @return       This function returns 0.
+ */
+static int dam_free(struct inode *inode, struct file *file)
+{
+       /* DBG_PRINTK("ssi : dam_free()\n"); */
+       return 0;
+}
+
+/*!
+ * This structure defines file operations for a DAM device.
+ */
+static struct file_operations dam_fops = {
+
+       /*!
+        * the owner
+        */
+       .owner = THIS_MODULE,
+
+       /*!
+        * the ioctl operation
+        */
+       .ioctl = dam_ioctl,
+
+       /*!
+        * the open operation
+        */
+       .open = dam_open,
+
+       /*!
+        * the release operation
+        */
+       .release = dam_free,
+};
+
+#endif
+
+/*!
+ * This function implements the init function of the DAM device.
+ * This function is called when the module is loaded.
+ *
+ * @return       This function returns 0.
+ */
+static int __init dam_init(void)
+{
+#ifdef TEST_DAM
+       struct device *temp_class;
+       printk(KERN_DEBUG "dam : dam_init(void) \n");
+
+       major_dam = register_chrdev(0, DAM_NAME, &dam_fops);
+       if (major_dam < 0) {
+               printk(KERN_WARNING "Unable to get a major for dam");
+               return major_dam;
+       }
+
+       mxc_dam_class = class_create(THIS_MODULE, DAM_NAME);
+       if (IS_ERR(mxc_dam_class)) {
+               goto err_out;
+       }
+
+       temp_class = device_create(mxc_dam_class, NULL,
+                                  MKDEV(major_dam, 0), NULL, DAM_NAME);
+       if (IS_ERR(temp_class)) {
+               goto err_out;
+       }
+#endif
+       return 0;
+
+      err_out:
+       printk(KERN_ERR "Error creating dam class device.\n");
+       device_destroy(mxc_dam_class, MKDEV(major_dam, 0));
+       class_destroy(mxc_dam_class);
+       unregister_chrdev(major_dam, DAM_NAME);
+       return -1;
+}
+
+/*!
+ * This function implements the exit function of the SPI device.
+ * This function is called when the module is unloaded.
+ *
+ */
+static void __exit dam_exit(void)
+{
+#ifdef TEST_DAM
+       device_destroy(mxc_dam_class, MKDEV(major_dam, 0));
+       class_destroy(mxc_dam_class);
+       unregister_chrdev(major_dam, DAM_NAME);
+       printk(KERN_DEBUG "dam : successfully unloaded\n");
+#endif
+}
+
+module_init(dam_init);
+module_exit(dam_exit);
+
+MODULE_DESCRIPTION("DAM char device driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/gps_ioctrl/Kconfig b/drivers/mxc/gps_ioctrl/Kconfig
new file mode 100644 (file)
index 0000000..0a85d16
--- /dev/null
@@ -0,0 +1,13 @@
+#
+# BROADCOM GPS configuration
+#
+
+menu "Broadcom GPS ioctrl support"
+
+config GPS_IOCTRL
+       tristate "GPS ioctrl support"
+       depends on MACH_MX31_3DS || MACH_MX35_3DS || MACH_MX37_3DS || MACH_MX51_3DS
+       ---help---
+         Say Y to enable Broadcom GPS ioctrl on MXC platform.
+
+endmenu
diff --git a/drivers/mxc/gps_ioctrl/Makefile b/drivers/mxc/gps_ioctrl/Makefile
new file mode 100644 (file)
index 0000000..c52d7c9
--- /dev/null
@@ -0,0 +1,5 @@
+#
+# Makefile for the GPIO device driver module.
+#
+obj-$(CONFIG_GPS_IOCTRL)       += gps_gpiodrv.o
+gps_gpiodrv-objs := agpsgpiodev.o
diff --git a/drivers/mxc/gps_ioctrl/agpsgpiodev.c b/drivers/mxc/gps_ioctrl/agpsgpiodev.c
new file mode 100644 (file)
index 0000000..f478263
--- /dev/null
@@ -0,0 +1,332 @@
+/*
+ * 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 agpsgpiodev.c
+ *
+ * @brief Main file for GPIO kernel module. Contains driver entry/exit
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>          /* Async notification */
+#include <linux/uaccess.h>     /* for get_user, put_user, access_ok */
+#include <linux/sched.h>       /* jiffies */
+#include <linux/poll.h>
+#include <linux/device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/cdev.h>
+#include <linux/fsl_devices.h>
+#include <mach/hardware.h>
+#include "agpsgpiodev.h"
+
+extern void gpio_gps_active(void);
+extern void gpio_gps_inactive(void);
+extern int gpio_gps_access(int para);
+
+struct mxc_gps_platform_data *mxc_gps_ioctrl_data;
+static int Device_Open;                /* Only allow a single user of this device */
+static struct cdev mxc_gps_cdev;
+static dev_t agps_gpio_dev;
+static struct class *gps_class;
+static struct device *gps_class_dev;
+
+/* Write GPIO from user space */
+static int ioctl_writegpio(int arg)
+{
+
+       /* Bit 0 of arg identifies the GPIO pin to write:
+          0 = GPS_RESET_GPIO, 1 = GPS_POWER_GPIO.
+          Bit 1 of arg identifies the value to write (0 or 1). */
+
+       /* Bit 2 should be 0 to show this access is write */
+       return gpio_gps_access(arg & (~0x4));
+}
+
+/* Read GPIO from user space */
+static int ioctl_readgpio(int arg)
+{
+       /* Bit 0 of arg identifies the GPIO pin to read:
+          0 = GPS_RESET_GPIO. 1 = GPS_POWER_GPIO
+          Bit 2 should be 1 to show this access is read */
+       return gpio_gps_access(arg | 0x4);
+}
+
+static int device_open(struct inode *inode, struct file *fp)
+{
+       /* We don't want to talk to two processes at the same time. */
+       if (Device_Open) {
+               printk(KERN_DEBUG "device_open() - Returning EBUSY. \
+                       Device already open... \n");
+               return -EBUSY;
+       }
+       Device_Open++;          /* BUGBUG : Not protected! */
+       try_module_get(THIS_MODULE);
+
+       return 0;
+}
+
+static int device_release(struct inode *inode, struct file *fp)
+{
+       /* We're now ready for our next caller */
+       Device_Open--;
+       module_put(THIS_MODULE);
+
+       return 0;
+}
+
+static int device_ioctl(struct inode *inode, struct file *fp,
+                       unsigned int cmd, unsigned long arg)
+{
+       int err = 0;
+
+       /* Extract the type and number bitfields, and don't decode wrong cmds.
+          Return ENOTTY (inappropriate ioctl) before access_ok() */
+       if (_IOC_TYPE(cmd) != MAJOR_NUM) {
+               printk(KERN_ERR
+                      "device_ioctl() - Error! IOC_TYPE = %d. Expected %d\n",
+                      _IOC_TYPE(cmd), MAJOR_NUM);
+               return -ENOTTY;
+       }
+       if (_IOC_NR(cmd) > IOCTL_MAXNUMBER) {
+               printk(KERN_ERR
+                      "device_ioctl() - Error!"
+                      "IOC_NR = %d greater than max supported(%d)\n",
+                      _IOC_NR(cmd), IOCTL_MAXNUMBER);
+               return -ENOTTY;
+       }
+
+       /* The direction is a bitmask, and VERIFY_WRITE catches R/W transfers.
+          `Type' is user-oriented, while access_ok is kernel-oriented, so the
+          concept of "read" and "write" is reversed. I think this is primarily
+          for good coding practice. You can easily do any kind of R/W access
+          without these checks and IOCTL code can be implemented "randomly"! */
+       if (_IOC_DIR(cmd) & _IOC_READ)
+               err =
+                   !access_ok(VERIFY_WRITE, (void __user *)arg,
+                              _IOC_SIZE(cmd));
+
+       else if (_IOC_DIR(cmd) & _IOC_WRITE)
+               err =
+                   !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
+       if (err) {
+               printk(KERN_ERR
+                      "device_ioctl() - Error! User arg not valid"
+                      "for selected access (R/W/RW). Cmd %d\n",
+                      _IOC_TYPE(cmd));
+               return -EFAULT;
+       }
+
+       /* Note: Read and writing data to user buffer can be done using regular
+          pointer stuff but we may also use get_user() or put_user() */
+
+       /* Cmd and arg has been verified... */
+       switch (cmd) {
+       case IOCTL_WRITEGPIO:
+               return ioctl_writegpio((int)arg);
+       case IOCTL_READGPIO:
+               return ioctl_readgpio((int)arg);
+       default:
+               printk(KERN_ERR "device_ioctl() - Invalid IOCTL (0x%x)\n", cmd);
+               return EINVAL;
+       }
+       return 0;
+}
+
+struct file_operations Fops = {
+       .ioctl = device_ioctl,
+       .open = device_open,
+       .release = device_release,
+};
+
+/* Initialize the module - Register the character device */
+int init_chrdev(struct device *dev)
+{
+       int ret, gps_major;
+
+       ret = alloc_chrdev_region(&agps_gpio_dev, 1, 1, "agps_gpio");
+       gps_major = MAJOR(agps_gpio_dev);
+       if (ret < 0) {
+               dev_err(dev, "can't get major %d\n", gps_major);
+               goto err3;
+       }
+
+       cdev_init(&mxc_gps_cdev, &Fops);
+       mxc_gps_cdev.owner = THIS_MODULE;
+
+       ret = cdev_add(&mxc_gps_cdev, agps_gpio_dev, 1);
+       if (ret) {
+               dev_err(dev, "can't add cdev\n");
+               goto err2;
+       }
+
+       /* create class and device for udev information */
+       gps_class = class_create(THIS_MODULE, "gps");
+       if (IS_ERR(gps_class)) {
+               dev_err(dev, "failed to create gps class\n");
+               ret = -ENOMEM;
+               goto err1;
+       }
+
+       gps_class_dev = device_create(gps_class, NULL, MKDEV(gps_major, 1), NULL,
+                                       AGPSGPIO_DEVICE_FILE_NAME);
+       if (IS_ERR(gps_class_dev)) {
+               dev_err(dev, "failed to create gps gpio class device\n");
+               ret = -ENOMEM;
+               goto err0;
+       }
+
+       return 0;
+err0:
+       class_destroy(gps_class);
+err1:
+       cdev_del(&mxc_gps_cdev);
+err2:
+       unregister_chrdev_region(agps_gpio_dev, 1);
+err3:
+       return ret;
+}
+
+/* Cleanup - unregister the appropriate file from /proc. */
+void cleanup_chrdev(void)
+{
+       /* destroy gps device class */
+       device_destroy(gps_class, MKDEV(MAJOR(agps_gpio_dev), 1));
+       class_destroy(gps_class);
+
+       /* Unregister the device */
+       cdev_del(&mxc_gps_cdev);
+       unregister_chrdev_region(agps_gpio_dev, 1);
+}
+
+/*!
+ * This function initializes the driver in terms of memory of the soundcard
+ * and some basic HW clock settings.
+ *
+ * @return              0 on success, -1 otherwise.
+ */
+static int __init gps_ioctrl_probe(struct platform_device *pdev)
+{
+       struct regulator *gps_regu;
+
+       mxc_gps_ioctrl_data =
+           (struct mxc_gps_platform_data *)pdev->dev.platform_data;
+
+       /* open GPS GPO3 1v8 for GL gps support */
+       if (mxc_gps_ioctrl_data->core_reg != NULL) {
+               mxc_gps_ioctrl_data->gps_regu_core =
+                   regulator_get(&(pdev->dev), mxc_gps_ioctrl_data->core_reg);
+               gps_regu = mxc_gps_ioctrl_data->gps_regu_core;
+               if (!IS_ERR_VALUE((u32)gps_regu)) {
+                       regulator_set_voltage(gps_regu, 1800000, 1800000);
+                       regulator_enable(gps_regu);
+               } else {
+                       return -1;
+               }
+       }
+       /* open GPS GPO1 2v8 for GL gps support */
+       if (mxc_gps_ioctrl_data->analog_reg != NULL) {
+               mxc_gps_ioctrl_data->gps_regu_analog =
+                   regulator_get(&(pdev->dev),
+                                 mxc_gps_ioctrl_data->analog_reg);
+               gps_regu = mxc_gps_ioctrl_data->gps_regu_analog;
+               if (!IS_ERR_VALUE((u32)gps_regu)) {
+                       regulator_set_voltage(gps_regu, 2800000, 2800000);
+                       regulator_enable(gps_regu);
+               } else {
+                       return -1;
+               }
+       }
+       gpio_gps_active();
+
+       /* Register character device */
+       init_chrdev(&(pdev->dev));
+       return 0;
+}
+
+static int gps_ioctrl_remove(struct platform_device *pdev)
+{
+       struct regulator *gps_regu;
+
+       mxc_gps_ioctrl_data =
+           (struct mxc_gps_platform_data *)pdev->dev.platform_data;
+
+       /* Character device cleanup.. */
+       cleanup_chrdev();
+       gpio_gps_inactive();
+
+       /* close GPS GPO3 1v8 for GL gps */
+       gps_regu = mxc_gps_ioctrl_data->gps_regu_core;
+       if (mxc_gps_ioctrl_data->core_reg != NULL) {
+               regulator_disable(gps_regu);
+               regulator_put(gps_regu);
+       }
+       /* close GPS GPO1 2v8 for GL gps */
+       gps_regu = mxc_gps_ioctrl_data->gps_regu_analog;
+       if (mxc_gps_ioctrl_data->analog_reg != NULL) {
+               regulator_disable(gps_regu);
+               regulator_put(gps_regu);
+       }
+
+       return 0;
+}
+
+static int gps_ioctrl_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       /* PowerEn toggle off */
+       ioctl_writegpio(0x1);
+       return 0;
+}
+
+static int gps_ioctrl_resume(struct platform_device *pdev)
+{
+       /* PowerEn pull up */
+       ioctl_writegpio(0x3);
+       return 0;
+}
+
+static struct platform_driver gps_ioctrl_driver = {
+       .probe = gps_ioctrl_probe,
+       .remove = gps_ioctrl_remove,
+       .suspend = gps_ioctrl_suspend,
+       .resume = gps_ioctrl_resume,
+       .driver = {
+                  .name = "gps_ioctrl",
+                  },
+};
+
+/*!
+ * Entry point for GPS ioctrl module.
+ *
+ */
+static int __init gps_ioctrl_init(void)
+{
+       return platform_driver_register(&gps_ioctrl_driver);
+}
+
+/*!
+ * unloading module.
+ *
+ */
+static void __exit gps_ioctrl_exit(void)
+{
+       platform_driver_unregister(&gps_ioctrl_driver);
+}
+
+module_init(gps_ioctrl_init);
+module_exit(gps_ioctrl_exit);
+MODULE_DESCRIPTION("GPIO DEVICE DRIVER");
+MODULE_AUTHOR("Freescale Semiconductor");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/gps_ioctrl/agpsgpiodev.h b/drivers/mxc/gps_ioctrl/agpsgpiodev.h
new file mode 100644 (file)
index 0000000..b8ba140
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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 agpsgpiodev.h
+ *
+ * @brief head file of Simple character device interface for AGPS kernel module.
+ *
+ * @ingroup
+ */
+
+#ifndef AGPSGPIODEV_H
+#define AGPSGPIODEV_H
+
+#include <linux/ioctl.h>
+
+#define USE_BLOCKING           /* Test driver with blocking calls */
+#undef USE_FASYNC              /* Test driver with async notification */
+
+/* The major device number. We can't rely on dynamic registration any more
+   because ioctls need to know it */
+#define MAJOR_NUM 100
+
+#define IOCTL_WRITEGPIO   _IOWR(MAJOR_NUM, 1, char *)
+#define IOCTL_READGPIO    _IOR(MAJOR_NUM, 2, char *)
+#define IOCTL_MAXNUMBER   2
+
+/* The name of the device file */
+#define AGPSGPIO_DEVICE_FILE_NAME "agpsgpio"
+
+/* Exported prototypes */
+int init_chrdev(struct device *dev);
+void cleanup_chrdev(void);
+void wakeup(void);
+
+#endif
diff --git a/drivers/mxc/hmp4e/Kconfig b/drivers/mxc/hmp4e/Kconfig
new file mode 100644 (file)
index 0000000..fdd7dbc
--- /dev/null
@@ -0,0 +1,24 @@
+#
+# MPEG4 Encoder kernel module  configuration
+#
+
+menu "MXC MPEG4 Encoder Kernel module support"
+
+config MXC_HMP4E
+       tristate "MPEG4 Encoder support"
+       depends on ARCH_MXC
+       depends on !ARCH_MX27
+       default y
+       ---help---
+         Say Y to get the MPEG4 Encoder kernel module available on
+         MXC platform.
+
+config MXC_HMP4E_DEBUG
+       bool "MXC MPEG4 Debug messages"
+       depends on MXC_HMP4E != n
+       default n
+       ---help---
+         Say Y here if you need the Encoder driver to print debug messages.
+         This is an option for developers, most people should say N here.
+
+endmenu
diff --git a/drivers/mxc/hmp4e/Makefile b/drivers/mxc/hmp4e/Makefile
new file mode 100644 (file)
index 0000000..3f69ec8
--- /dev/null
@@ -0,0 +1,9 @@
+#
+# Makefile for the MPEG4 Encoder kernel module.
+
+obj-$(CONFIG_MXC_HMP4E)                += mxc_hmp4e.o
+CFLAGS_mxc_hmp4e.o = -DIMX_NEEDS_DEPRECATED_SYMBOLS
+
+ifeq ($(CONFIG_MXC_HMP4E_DEBUG),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
diff --git a/drivers/mxc/hmp4e/mxc_hmp4e.c b/drivers/mxc/hmp4e/mxc_hmp4e.c
new file mode 100644 (file)
index 0000000..85c77dd
--- /dev/null
@@ -0,0 +1,812 @@
+/*
+ * Copyright 2005-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
+ */
+
+/*
+ * Encoder device driver (kernel module)
+ *
+ * Copyright (C) 2005  Hantro Products Oy.
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>                /*  __init,__exit directives */
+#include <linux/mm.h>          /* remap_page_range / remap_pfn_range */
+#include <linux/fs.h>          /* for struct file_operations */
+#include <linux/errno.h>       /* standard error codes */
+#include <linux/platform_device.h>     /* for device registeration for PM */
+#include <linux/delay.h>       /* for msleep_interruptible */
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h> /* for dma_alloc_consistent */
+#include <linux/clk.h>
+#include <asm/uaccess.h>       /* for ioctl __get_user, __put_user */
+#include <mach/hardware.h>
+#include "mxc_hmp4e.h"         /* MPEG4 encoder specific */
+
+/* here's all the must remember stuff */
+typedef struct {
+       ulong buffaddr;
+       u32 buffsize;
+       ulong iobaseaddr;
+       u32 iosize;
+       volatile u32 *hwregs;
+       u32 irq;
+       u16 hwid_offset;
+       u16 intr_offset;
+       u16 busy_offset;
+       u16 type;               /* Encoder type, CIF = 0, VGA = 1 */
+       u16 clk_gate;
+       u16 busy_val;
+       struct fasync_struct *async_queue;
+#ifdef CONFIG_PM
+       s32 suspend_state;
+       wait_queue_head_t power_queue;
+#endif
+} hmp4e_t;
+
+/* and this is our MAJOR; use 0 for dynamic allocation (recommended)*/
+static s32 hmp4e_major;
+
+static u32 hmp4e_phys;
+static struct class *hmp4e_class;
+static hmp4e_t hmp4e_data;
+
+/*! MPEG4 enc clock handle. */
+static struct clk *hmp4e_clk;
+
+/*
+ * avoid "enable_irq(x) unbalanced from ..."
+ * error messages from the kernel, since {ena,dis}able_irq()
+ * calls are stacked in kernel.
+ */
+static bool irq_enable;
+
+ulong base_port = MPEG4_ENC_BASE_ADDR;
+u32 irq = MXC_INT_MPEG4_ENCODER;
+
+module_param(base_port, long, 000);
+module_param(irq, int, 000);
+
+/*!
+ * These variables store the register values when HMP4E is in suspend mode.
+ */
+#ifdef CONFIG_PM
+u32 io_regs[64];
+#endif
+
+static s32 hmp4e_map_buffer(struct file *filp, struct vm_area_struct *vma);
+static s32 hmp4e_map_hwregs(struct file *filp, struct vm_area_struct *vma);
+static void hmp4e_reset(hmp4e_t *dev);
+irqreturn_t hmp4e_isr(s32 irq, void *dev_id);
+
+/*!
+ * This funtion is called to write h/w register.
+ *
+ * @param   val                value to be written into the register
+ * @param   offset     register offset
+ *
+ */
+static inline void hmp4e_write(u32 val, u32 offset)
+{
+       hmp4e_t *dev = &hmp4e_data;
+       __raw_writel(val, (dev->hwregs + offset));
+}
+
+/*!
+ * This funtion is called to read h/w register.
+ *
+ * @param   offset     register offset
+ *
+ * @return  This function returns the value read from the register.
+ *
+ */
+static inline u32 hmp4e_read(u32 offset)
+{
+       hmp4e_t *dev = &hmp4e_data;
+       u32 val;
+
+       val = __raw_readl(dev->hwregs + offset);
+
+       return val;
+}
+
+/*!
+ * The device's mmap method. The VFS has kindly prepared the process's
+ * vm_area_struct for us, so we examine this to see what was requested.
+ *
+ * @param   filp       pointer to struct file
+ * @param   vma                pointer to struct vma_area_struct
+ *
+ * @return  This function returns 0 if successful or -ve value on error.
+ *
+ */
+static s32 hmp4e_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+       s32 result;
+       ulong offset = vma->vm_pgoff << PAGE_SHIFT;
+
+       pr_debug("hmp4e_mmap: size = %lu off = 0x%08lx\n",
+                (unsigned long)(vma->vm_end - vma->vm_start), offset);
+
+       if (offset == 0) {
+               result = hmp4e_map_buffer(filp, vma);
+       } else if (offset == hmp4e_data.iobaseaddr) {
+               result = hmp4e_map_hwregs(filp, vma);
+       } else {
+               pr_debug("hmp4e: mmap invalid value\n");
+               result = -EINVAL;
+       }
+
+       return result;
+}
+
+/*!
+ * This funtion is called to handle ioctls.
+ *
+ * @param   inode      pointer to struct inode
+ * @param   filp       pointer to struct file
+ * @param   cmd                ioctl command
+ * @param   arg                user data
+ *
+ * @return  This function returns 0 if successful or -ve value on error.
+ *
+ */
+static s32 hmp4e_ioctl(struct inode *inode, struct file *filp,
+                      u32 cmd, ulong arg)
+{
+       s32 err = 0, retval = 0;
+       ulong offset = 0;
+       hmp4e_t *dev = &hmp4e_data;
+       write_t bwrite;
+
+#ifdef CONFIG_PM
+       wait_event_interruptible(hmp4e_data.power_queue,
+                                hmp4e_data.suspend_state == 0);
+#endif
+
+       /*
+        * extract the type and number bitfields, and don't decode
+        * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()
+        */
+       if (_IOC_TYPE(cmd) != HMP4E_IOC_MAGIC) {
+               pr_debug("hmp4e: ioctl invalid magic\n");
+               return -ENOTTY;
+       }
+
+       if (_IOC_NR(cmd) > HMP4E_IOC_MAXNR) {
+               pr_debug("hmp4e: ioctl exceeds max ioctl\n");
+               return -ENOTTY;
+       }
+
+       /*
+        * the direction is a bitmask, and VERIFY_WRITE catches R/W
+        * transfers. `Type' is user-oriented, while
+        * access_ok is kernel-oriented, so the concept of "read" and
+        * "write" is reversed
+        */
+       if (_IOC_DIR(cmd) & _IOC_READ) {
+               err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));
+       } else if (_IOC_DIR(cmd) & _IOC_WRITE) {
+               err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));
+       }
+
+       if (err) {
+               pr_debug("hmp4e: ioctl invalid direction\n");
+               return -EFAULT;
+       }
+
+       switch (cmd) {
+       case HMP4E_IOCHARDRESET:
+               break;
+
+       case HMP4E_IOCGBUFBUSADDRESS:
+               retval = __put_user((ulong) hmp4e_phys, (u32 *) arg);
+               break;
+
+       case HMP4E_IOCGBUFSIZE:
+               retval = __put_user(hmp4e_data.buffsize, (u32 *) arg);
+               break;
+
+       case HMP4E_IOCSREGWRITE:
+               if (dev->type != 1) {   /* This ioctl only for VGA */
+                       pr_debug("hmp4e: HMP4E_IOCSREGWRITE invalid\n");
+                       retval = -EINVAL;
+                       break;
+               }
+
+               retval = __copy_from_user(&bwrite, (u32 *) arg,
+                                         sizeof(write_t));
+
+               if (bwrite.offset <= hmp4e_data.iosize - 4) {
+                       hmp4e_write(bwrite.data, (bwrite.offset / 4));
+               } else {
+                       pr_debug("hmp4e: HMP4E_IOCSREGWRITE failed\n");
+                       retval = -EFAULT;
+               }
+               break;
+
+       case HMP4E_IOCXREGREAD:
+               if (dev->type != 1) {   /* This ioctl only for VGA */
+                       pr_debug("hmp4e: HMP4E_IOCSREGWRITE invalid\n");
+                       retval = -EINVAL;
+                       break;
+               }
+
+               retval = __get_user(offset, (ulong *) arg);
+               if (offset <= hmp4e_data.iosize - 4) {
+                       __put_user(hmp4e_read((offset / 4)), (ulong *) arg);
+               } else {
+                       pr_debug("hmp4e: HMP4E_IOCXREGREAD failed\n");
+                       retval = -EFAULT;
+               }
+               break;
+
+       case HMP4E_IOCGHWOFFSET:
+               __put_user(hmp4e_data.iobaseaddr, (ulong *) arg);
+               break;
+
+       case HMP4E_IOCGHWIOSIZE:
+               __put_user(hmp4e_data.iosize, (u32 *) arg);
+               break;
+
+       case HMP4E_IOC_CLI:
+               if (irq_enable == true) {
+                       disable_irq(hmp4e_data.irq);
+                       irq_enable = false;
+               }
+               break;
+
+       case HMP4E_IOC_STI:
+               if (irq_enable == false) {
+                       enable_irq(hmp4e_data.irq);
+                       irq_enable = true;
+               }
+               break;
+
+       default:
+               pr_debug("unknown case %x\n", cmd);
+       }
+
+       return retval;
+}
+
+/*!
+ * This funtion is called when the device is opened.
+ *
+ * @param   inode      pointer to struct inode
+ * @param   filp       pointer to struct file
+ *
+ * @return  This function returns 0 if successful or -ve value on error.
+ *
+ */
+static s32 hmp4e_open(struct inode *inode, struct file *filp)
+{
+       hmp4e_t *dev = &hmp4e_data;
+
+       filp->private_data = (void *)dev;
+
+       if (request_irq(dev->irq, hmp4e_isr, 0, "mxc_hmp4e", dev) != 0) {
+               pr_debug("hmp4e: request irq failed\n");
+               return -EBUSY;
+       }
+
+       if (irq_enable == false) {
+               irq_enable = true;
+       }
+       clk_enable(hmp4e_clk);
+       return 0;
+}
+
+static s32 hmp4e_fasync(s32 fd, struct file *filp, s32 mode)
+{
+       hmp4e_t *dev = (hmp4e_t *) filp->private_data;
+       return fasync_helper(fd, filp, mode, &dev->async_queue);
+}
+
+/*!
+ * This funtion is called when the device is closed.
+ *
+ * @param   inode      pointer to struct inode
+ * @param   filp       pointer to struct file
+ *
+ * @return  This function returns 0.
+ *
+ */
+static s32 hmp4e_release(struct inode *inode, struct file *filp)
+{
+       hmp4e_t *dev = (hmp4e_t *) filp->private_data;
+
+       /* this is necessary if user process exited asynchronously */
+       if (irq_enable == true) {
+               disable_irq(dev->irq);
+               irq_enable = false;
+       }
+
+       /* reset hardware */
+       hmp4e_reset(&hmp4e_data);
+
+       /* free the encoder IRQ */
+       free_irq(dev->irq, (void *)dev);
+
+       /* remove this filp from the asynchronusly notified filp's */
+       hmp4e_fasync(-1, filp, 0);
+       clk_disable(hmp4e_clk);
+       return 0;
+}
+
+/* VFS methods */
+static struct file_operations hmp4e_fops = {
+       .owner = THIS_MODULE,
+       .open = hmp4e_open,
+       .release = hmp4e_release,
+       .ioctl = hmp4e_ioctl,
+       .mmap = hmp4e_mmap,
+       .fasync = hmp4e_fasync,
+};
+
+/*!
+ * This funtion allocates physical contigous memory.
+ *
+ * @param   size       size of memory to be allocated
+ *
+ * @return  This function returns 0 if successful or -ve value on error.
+ *
+ */
+static s32 hmp4e_alloc(u32 size)
+{
+       hmp4e_data.buffsize = PAGE_ALIGN(size);
+       hmp4e_data.buffaddr =
+           (ulong) dma_alloc_coherent(NULL, hmp4e_data.buffsize,
+                                      (dma_addr_t *) &hmp4e_phys,
+                                      GFP_DMA | GFP_KERNEL);
+
+       if (hmp4e_data.buffaddr == 0) {
+               printk(KERN_ERR "hmp4e: couldn't allocate data buffer\n");
+               return -ENOMEM;
+       }
+
+       memset((s8 *) hmp4e_data.buffaddr, 0, hmp4e_data.buffsize);
+       return 0;
+}
+
+/*!
+ * This funtion frees the DMAed memory.
+ */
+static void hmp4e_free(void)
+{
+       if (hmp4e_data.buffaddr != 0) {
+               dma_free_coherent(NULL, hmp4e_data.buffsize,
+                                 (void *)hmp4e_data.buffaddr, hmp4e_phys);
+               hmp4e_data.buffaddr = 0;
+       }
+}
+
+/*!
+ * This funtion maps the shared buffer in memory.
+ *
+ * @param   filp       pointer to struct file
+ * @param   vma                pointer to struct vm_area_struct
+ *
+ * @return  This function returns 0 if successful or -ve value on error.
+ *
+ */
+static s32 hmp4e_map_buffer(struct file *filp, struct vm_area_struct *vma)
+{
+       ulong phys;
+       ulong start = (u32) vma->vm_start;
+       ulong size = (u32) (vma->vm_end - vma->vm_start);
+
+       /* if userspace tries to mmap beyond end of our buffer, fail */
+       if (size > hmp4e_data.buffsize) {
+               pr_debug("hmp4e: hmp4e_map_buffer, invalid size\n");
+               return -EINVAL;
+       }
+
+       vma->vm_flags |= VM_RESERVED | VM_IO;
+       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+       phys = hmp4e_phys;
+
+       if (remap_pfn_range(vma, start, phys >> PAGE_SHIFT, size,
+                           vma->vm_page_prot)) {
+               pr_debug("hmp4e: failed mmapping shared buffer\n");
+               return -EAGAIN;
+       }
+
+       return 0;
+}
+
+/*!
+ * This funtion maps the h/w register space in memory.
+ *
+ * @param   filp       pointer to struct file
+ * @param   vma                pointer to struct vm_area_struct
+ *
+ * @return  This function returns 0 if successful or -ve value on error.
+ *
+ */
+static s32 hmp4e_map_hwregs(struct file *filp, struct vm_area_struct *vma)
+{
+       ulong phys;
+       ulong start = (unsigned long)vma->vm_start;
+       ulong size = (unsigned long)(vma->vm_end - vma->vm_start);
+
+       /* if userspace tries to mmap beyond end of our buffer, fail */
+       if (size > PAGE_SIZE) {
+               pr_debug("hmp4e: hmp4e_map_hwregs, invalid size\n");
+               return -EINVAL;
+       }
+
+       vma->vm_flags |= VM_RESERVED | VM_IO;
+       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+       /* Remember this won't work for vmalloc()d memory ! */
+       phys = hmp4e_data.iobaseaddr;
+
+       if (remap_pfn_range(vma, start, phys >> PAGE_SHIFT, hmp4e_data.iosize,
+                           vma->vm_page_prot)) {
+               pr_debug("hmp4e: failed mmapping HW registers\n");
+               return -EAGAIN;
+       }
+
+       return 0;
+}
+
+/*!
+ * This function is the interrupt service routine.
+ *
+ * @param   irq                the irq number
+ * @param   dev_id     driver data when ISR was regiatered
+ *
+ * @return  The return value is IRQ_HANDLED.
+ *
+ */
+irqreturn_t hmp4e_isr(s32 irq, void *dev_id)
+{
+       hmp4e_t *dev = (hmp4e_t *) dev_id;
+       u32 offset = dev->intr_offset;
+
+       u32 irq_status = hmp4e_read(offset);
+
+       /* clear enc IRQ */
+       hmp4e_write(irq_status & (~0x01), offset);
+
+       if (dev->async_queue)
+               kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
+
+       return IRQ_HANDLED;
+}
+
+/*!
+ * This function is called to reset the encoder.
+ *
+ * @param   dev         pointer to struct hmp4e_data
+ *
+ */
+static void hmp4e_reset(hmp4e_t *dev)
+{
+       s32 i;
+
+       /* enable HCLK for register reset */
+       hmp4e_write(dev->clk_gate, 0);
+
+       /* Reset registers, except ECR0 (0x00) and ID (read-only) */
+       for (i = 1; i < (dev->iosize / 4); i += 1) {
+               if (i == dev->hwid_offset)      /* ID is read only */
+                       continue;
+
+               /* Only for CIF, not used */
+               if ((dev->type == 0) && (i == 14))
+                       continue;
+
+               hmp4e_write(0, i);
+       }
+
+       /* disable HCLK */
+       hmp4e_write(0, 0);
+       return;
+}
+
+/*!
+ * This function is called during the driver binding process. This function
+ * does the hardware initialization.
+ *
+ * @param   dev   the device structure used to store device specific
+ *                information that is used by the suspend, resume and remove
+ *                functions
+ *
+ * @return  The function returns 0 if successful.
+ */
+static s32 hmp4e_probe(struct platform_device *pdev)
+{
+       s32 result;
+       u32 hwid;
+       struct device *temp_class;
+
+       hmp4e_data.iobaseaddr = base_port;
+       hmp4e_data.irq = irq;
+       hmp4e_data.buffaddr = 0;
+
+       /* map hw i/o registers into kernel space */
+       hmp4e_data.hwregs = (volatile void *)IO_ADDRESS(hmp4e_data.iobaseaddr);
+
+       hmp4e_clk = clk_get(&pdev->dev, "mpeg4_clk");
+       if (IS_ERR(hmp4e_clk)) {
+               printk(KERN_INFO "hmp4e: Unable to get clock\n");
+               return -EIO;
+       }
+
+       clk_enable(hmp4e_clk);
+
+       /* check hw id for encoder signature */
+       hwid = hmp4e_read(7);
+       if ((hwid & 0xffff) == 0x1882) {        /* CIF first */
+               hmp4e_data.type = 0;
+               hmp4e_data.iosize = (16 * 4);
+               hmp4e_data.hwid_offset = 7;
+               hmp4e_data.intr_offset = 5;
+               hmp4e_data.clk_gate = (1 << 1);
+               hmp4e_data.buffsize = 512000;
+               hmp4e_data.busy_offset = 0;
+               hmp4e_data.busy_val = 1;
+       } else {
+               hwid = hmp4e_read((0x88 / 4));
+               if ((hwid & 0xffff0000) == 0x52510000) {        /* VGA */
+                       hmp4e_data.type = 1;
+                       hmp4e_data.iosize = (35 * 4);
+                       hmp4e_data.hwid_offset = (0x88 / 4);
+                       hmp4e_data.intr_offset = (0x10 / 4);
+                       hmp4e_data.clk_gate = (1 << 12);
+                       hmp4e_data.buffsize = 1048576;
+                       hmp4e_data.busy_offset = (0x10 / 4);
+                       hmp4e_data.busy_val = (1 << 1);
+               } else {
+                       printk(KERN_INFO "hmp4e: HW ID not found\n");
+                       goto error1;
+               }
+       }
+
+       /* Reset hardware */
+       hmp4e_reset(&hmp4e_data);
+
+       /* allocate memory shared with ewl */
+       result = hmp4e_alloc(hmp4e_data.buffsize);
+       if (result < 0)
+               goto error1;
+
+       result = register_chrdev(hmp4e_major, "hmp4e", &hmp4e_fops);
+       if (result <= 0) {
+               pr_debug("hmp4e: unable to get major %d\n", hmp4e_major);
+               goto error2;
+       }
+
+       hmp4e_major = result;
+
+       hmp4e_class = class_create(THIS_MODULE, "hmp4e");
+       if (IS_ERR(hmp4e_class)) {
+               pr_debug("Error creating hmp4e class.\n");
+               goto error3;
+       }
+
+       temp_class = device_create(hmp4e_class, NULL, MKDEV(hmp4e_major, 0), NULL,
+                                  "hmp4e");
+       if (IS_ERR(temp_class)) {
+               pr_debug("Error creating hmp4e class device.\n");
+               goto error4;
+       }
+
+       platform_set_drvdata(pdev, &hmp4e_data);
+
+#ifdef CONFIG_PM
+       hmp4e_data.async_queue = NULL;
+       hmp4e_data.suspend_state = 0;
+       init_waitqueue_head(&hmp4e_data.power_queue);
+#endif
+
+       printk(KERN_INFO "hmp4e: %s encoder initialized\n",
+              hmp4e_data.type ? "VGA" : "CIF");
+       clk_disable(hmp4e_clk);
+       return 0;
+
+      error4:
+       class_destroy(hmp4e_class);
+      error3:
+       unregister_chrdev(hmp4e_major, "hmp4e");
+      error2:
+       hmp4e_free();
+      error1:
+       clk_disable(hmp4e_clk);
+       clk_put(hmp4e_clk);
+       printk(KERN_INFO "hmp4e: module not inserted\n");
+       return -EIO;
+}
+
+/*!
+ * Dissociates the driver.
+ *
+ * @param   dev   the device structure
+ *
+ * @return  The function always returns 0.
+ */
+static s32 hmp4e_remove(struct platform_device *pdev)
+{
+       device_destroy(hmp4e_class, MKDEV(hmp4e_major, 0));
+       class_destroy(hmp4e_class);
+       unregister_chrdev(hmp4e_major, "hmp4e");
+       hmp4e_free();
+       clk_disable(hmp4e_clk);
+       clk_put(hmp4e_clk);
+       platform_set_drvdata(pdev, NULL);
+       return 0;
+}
+
+#ifdef CONFIG_PM
+/*!
+ * This is the suspend of power management for the Hantro MPEG4 module
+ *
+ * @param        dev            the device
+ * @param        state          the state
+ *
+ * @return       This function always returns 0.
+ */
+static s32 hmp4e_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       s32 i;
+       hmp4e_t *pdata = &hmp4e_data;
+
+       /*
+        * how many times msleep_interruptible will be called before
+        * giving up
+        */
+       s32 timeout = 10;
+
+       pr_debug("hmp4e: Suspend\n");
+       hmp4e_data.suspend_state = 1;
+
+       /* check if encoder is currently running */
+       while ((hmp4e_read(pdata->busy_offset) & (pdata->busy_val)) &&
+              --timeout) {
+               pr_debug("hmp4e: encoder is running, going to sleep\n");
+               msleep_interruptible((unsigned int)30);
+       }
+
+       if (!timeout) {
+               pr_debug("hmp4e: timeout suspending, resetting encoder\n");
+               hmp4e_write(hmp4e_read(pdata->busy_offset) &
+                           (~pdata->busy_val), pdata->busy_offset);
+       }
+
+       /* first read register 0 */
+       io_regs[0] = hmp4e_read(0);
+
+       /* then override HCLK to make sure other registers can be read */
+       hmp4e_write(pdata->clk_gate, 0);
+
+       /* read other registers */
+       for (i = 1; i < (pdata->iosize / 4); i += 1) {
+
+               /* Only for CIF, not used */
+               if ((pdata->type == 0) && (i == 14))
+                       continue;
+
+               io_regs[i] = hmp4e_read(i);
+       }
+
+       /* restore value of register 0 */
+       hmp4e_write(io_regs[0], 0);
+
+       /* stop HCLK */
+       hmp4e_write(0, 0);
+       clk_disable(hmp4e_clk);
+       return 0;
+};
+
+/*!
+ * This is the resume of power management for the Hantro MPEG4 module
+ * It suports RESTORE state.
+ *
+ * @param        pdev            the platform device
+ *
+ * @return       This function always returns 0
+ */
+static s32 hmp4e_resume(struct platform_device *pdev)
+{
+       s32 i;
+       u32 status;
+       hmp4e_t *pdata = &hmp4e_data;
+
+       pr_debug("hmp4e: Resume\n");
+       clk_enable(hmp4e_clk);
+
+       /* override HCLK to make sure registers can be written */
+       hmp4e_write(pdata->clk_gate, 0x00);
+
+       for (i = 1; i < (pdata->iosize / 4); i += 1) {
+               if (i == pdata->hwid_offset)    /* Read only */
+                       continue;
+
+               /* Only for CIF, not used */
+               if ((pdata->type == 0) && (i == 14))
+                       continue;
+
+               hmp4e_write(io_regs[i], i);
+       }
+
+       /* write register 0 last */
+       hmp4e_write(io_regs[0], 0x00);
+
+       /* Clear the suspend flag */
+       hmp4e_data.suspend_state = 0;
+
+       /* Unblock the wait queue */
+       wake_up_interruptible(&hmp4e_data.power_queue);
+
+       /* Continue operations */
+       status = hmp4e_read(pdata->intr_offset);
+       if (status & 0x1) {
+               hmp4e_write(status & (~0x01), pdata->intr_offset);
+               if (hmp4e_data.async_queue)
+                       kill_fasync(&hmp4e_data.async_queue, SIGIO, POLL_IN);
+       }
+
+       return 0;
+};
+
+#endif
+
+static struct platform_driver hmp4e_driver = {
+       .driver = {
+                  .name = "mxc_hmp4e",
+                  },
+       .probe = hmp4e_probe,
+       .remove = hmp4e_remove,
+#ifdef CONFIG_PM
+       .suspend = hmp4e_suspend,
+       .resume = hmp4e_resume,
+#endif
+};
+
+static s32 __init hmp4e_init(void)
+{
+       printk(KERN_INFO "hmp4e: init\n");
+       platform_driver_register(&hmp4e_driver);
+       return 0;
+}
+
+static void __exit hmp4e_cleanup(void)
+{
+       platform_driver_unregister(&hmp4e_driver);
+       printk(KERN_INFO "hmp4e: module removed\n");
+}
+
+module_init(hmp4e_init);
+module_exit(hmp4e_cleanup);
+
+/* module description */
+MODULE_AUTHOR("Hantro Products Oy");
+MODULE_DESCRIPTION("Device driver for Hantro's hardware based MPEG4 encoder");
+MODULE_SUPPORTED_DEVICE("5251/4251 MPEG4 Encoder");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/hmp4e/mxc_hmp4e.h b/drivers/mxc/hmp4e/mxc_hmp4e.h
new file mode 100644 (file)
index 0000000..57ea81b
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2005-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
+ */
+
+/*
+ * Encoder device driver (kernel module headers)
+ *
+ * Copyright (C) 2005  Hantro Products Oy.
+ *
+ * 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 _HMP4ENC_H_
+#define _HMP4ENC_H_
+#include <linux/ioctl.h>       /* needed for the _IOW etc stuff used later */
+
+/* this is for writing data through ioctl to registers*/
+typedef struct {
+       unsigned long data;
+       unsigned long offset;
+} write_t;
+
+/*
+ * Ioctl definitions
+ */
+
+/* Use 'k' as magic number */
+#define HMP4E_IOC_MAGIC  'k'
+/*
+ * S means "Set" through a ptr,
+ * T means "Tell" directly with the argument value
+ * G means "Get": reply by setting through a pointer
+ * Q means "Query": response is on the return value
+ * X means "eXchange": G and S atomically
+ * H means "sHift": T and Q atomically
+ */
+#define HMP4E_IOCGBUFBUSADDRESS        _IOR(HMP4E_IOC_MAGIC,  1, unsigned long *)
+#define HMP4E_IOCGBUFSIZE      _IOR(HMP4E_IOC_MAGIC,  2, unsigned int *)
+#define HMP4E_IOCGHWOFFSET             _IOR(HMP4E_IOC_MAGIC,  3, unsigned long *)
+#define HMP4E_IOCGHWIOSIZE             _IOR(HMP4E_IOC_MAGIC,  4, unsigned int *)
+#define HMP4E_IOC_CLI                  _IO(HMP4E_IOC_MAGIC,   5)
+#define HMP4E_IOC_STI                  _IO(HMP4E_IOC_MAGIC,   6)
+#define HMP4E_IOCHARDRESET     _IO(HMP4E_IOC_MAGIC,   7)
+#define HMP4E_IOCSREGWRITE     _IOW(HMP4E_IOC_MAGIC,  8, write_t)
+#define HMP4E_IOCXREGREAD      _IOWR(HMP4E_IOC_MAGIC, 9, unsigned long)
+
+#define HMP4E_IOC_MAXNR 9
+
+#endif                         /* !_HMP4ENC_H_ */
diff --git a/drivers/mxc/hw_event/Kconfig b/drivers/mxc/hw_event/Kconfig
new file mode 100644 (file)
index 0000000..bcf4796
--- /dev/null
@@ -0,0 +1,11 @@
+menu "MXC HARDWARE EVENT"
+
+config MXC_HWEVENT
+       bool "MXC Hardware Event Handler"
+       default y
+       depends on ARCH_MXC
+       help
+         If you plan to use the Hardware Event Handler in the MXC, say
+         Y here. If unsure, select Y.
+
+endmenu
diff --git a/drivers/mxc/hw_event/Makefile b/drivers/mxc/hw_event/Makefile
new file mode 100644 (file)
index 0000000..a53fe2b
--- /dev/null
@@ -0,0 +1 @@
+obj-$(CONFIG_MXC_HWEVENT)      += mxc_hw_event.o
diff --git a/drivers/mxc/hw_event/mxc_hw_event.c b/drivers/mxc/hw_event/mxc_hw_event.c
new file mode 100644 (file)
index 0000000..2ccd407
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ * 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
+ */
+
+/*!
+ * mxc_hw_event.c
+ * Collect the hardware events, send to user by netlink
+ */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/signal.h>
+#include <linux/freezer.h>
+#include <linux/kthread.h>
+#include <net/sock.h>
+
+#include <mach/hw_events.h>
+
+#define EVENT_POOL_SIZE        10
+
+struct hw_event_elem {
+       struct mxc_hw_event event;
+       struct list_head list;
+};
+
+static struct sock *nl_event_sock;     /* netlink socket */
+static struct list_head event_head;
+static struct list_head free_head;
+static struct hw_event_elem events_pool[EVENT_POOL_SIZE];      /* event pool */
+static DEFINE_SPINLOCK(list_lock);
+static DECLARE_WAIT_QUEUE_HEAD(event_wq);
+static unsigned int seq;       /* send seq */
+static int initialized;
+static struct task_struct *hwevent_kthread;
+
+/*!
+ * main HW event handler thread
+ */
+static int hw_event_thread(void *data)
+{
+       struct sk_buff *skb = NULL;
+       struct nlmsghdr *nlh = NULL;
+       unsigned int size;
+       struct hw_event_elem *event, *n;
+       LIST_HEAD(tmp_head);
+       DEFINE_WAIT(wait);
+
+       while (1) {
+
+               prepare_to_wait(&event_wq, &wait, TASK_INTERRUPTIBLE);
+               /* wait for event coming */
+               if (!freezing(current) && !kthread_should_stop() &&
+                   list_empty(&event_head))
+                       schedule();
+               finish_wait(&event_wq, &wait);
+
+               try_to_freeze();
+
+               if (kthread_should_stop())
+                       break;
+
+               /* fetch event from list */
+               spin_lock_irq(&list_lock);
+               tmp_head = event_head;
+               tmp_head.prev->next = &tmp_head;
+               tmp_head.next->prev = &tmp_head;
+               /* clear the event list head */
+               INIT_LIST_HEAD(&event_head);
+               spin_unlock_irq(&list_lock);
+
+               list_for_each_entry_safe(event, n, &tmp_head, list) {
+
+                       size = NLMSG_SPACE(sizeof(struct mxc_hw_event));
+                       skb = alloc_skb(size, GFP_KERNEL);
+                       if (!skb) {
+                               /* if failed alloc skb, we drop this event */
+                               printk(KERN_WARNING
+                                      "mxc_hw_event: skb_alloc() failed\n");
+                               goto alloc_failure;
+                       }
+
+                       /* put the netlink header struct to skb */
+                       nlh =
+                           NLMSG_PUT(skb, 0, seq++, NLMSG_DONE,
+                                     size - sizeof(*nlh));
+
+                       /* fill the netlink data */
+                       memcpy((struct mxc_hw_event *)NLMSG_DATA(nlh),
+                              &event->event, sizeof(struct mxc_hw_event));
+
+                       /* free the event node, set to unused */
+                       spin_lock_irq(&list_lock);
+                       list_move(&event->list, &free_head);
+                       spin_unlock_irq(&list_lock);
+
+                       /* send to all process that create this socket */
+                       NETLINK_CB(skb).pid = 0;        /* sender pid */
+                       NETLINK_CB(skb).dst_group = HW_EVENT_GROUP;
+                       /* broadcast the event */
+                       netlink_broadcast(nl_event_sock, skb, 0, HW_EVENT_GROUP,
+                                         GFP_KERNEL);
+
+                       continue;
+                     nlmsg_failure:
+                       printk(KERN_WARNING
+                              "mxc_hw_event: No tailroom for NLMSG in skb\n");
+                     alloc_failure:
+                       /* free the event node, set to unused */
+                       spin_lock_irq(&list_lock);
+                       list_del(&event->list);
+                       list_add_tail(&event->list, &free_head);
+                       spin_unlock_irq(&list_lock);
+               }
+       }
+
+       return 0;
+}
+
+/*!
+ *
+ * @priority the event priority, REALTIME, EMERENCY, NORMAL
+ * @new_event event id to be send
+ */
+int hw_event_send(int priority, struct mxc_hw_event *new_event)
+{
+       unsigned int size;
+       struct sk_buff *skb = NULL;
+       struct nlmsghdr *nlh = NULL;
+       struct mxc_hw_event *event;
+       struct hw_event_elem *event_elem;
+       int ret;
+       unsigned long flag;
+       struct list_head *list_node;
+
+       if (!initialized) {
+               pr_info("HW Event module has not been initialized\n");
+               return -1;
+       }
+
+       if (priority == HWE_HIGH_PRIORITY) {
+               /**
+                * the most high priority event,
+                * we send it immediatly.
+                */
+
+               size = NLMSG_SPACE(sizeof(struct mxc_hw_event));
+
+               /* alloc skb */
+               if (in_interrupt()) {
+                       skb = alloc_skb(size, GFP_ATOMIC);
+               } else {
+                       skb = alloc_skb(size, GFP_KERNEL);
+               }
+               if (!skb) {
+                       /* if failed alloc skb, we drop this event */
+                       printk(KERN_WARNING
+                              "hw_event send: skb_alloc() failed\n");
+                       goto send_later;
+               }
+
+               /* put the netlink header struct to skb */
+               nlh = NLMSG_PUT(skb, 0, seq++, NLMSG_DONE, size - sizeof(*nlh));
+
+               /* fill the netlink data */
+               event = (struct mxc_hw_event *)NLMSG_DATA(nlh);
+               memcpy(event, new_event, sizeof(struct mxc_hw_event));
+
+               /* send to all process that create this socket */
+               NETLINK_CB(skb).pid = 0;        /* sender pid */
+               NETLINK_CB(skb).dst_group = HW_EVENT_GROUP;
+               /* broadcast the event */
+               ret = netlink_broadcast(nl_event_sock, skb, 0, HW_EVENT_GROUP,
+                                       in_interrupt() ? GFP_ATOMIC :
+                                       GFP_KERNEL);
+               if (ret) {
+
+                     nlmsg_failure:
+                       /* send failed */
+                       kfree_skb(skb);
+                       goto send_later;
+               }
+
+               return 0;
+       }
+
+      send_later:
+       spin_lock_irqsave(&list_lock, flag);
+       if (list_empty(&free_head)) {
+               spin_unlock_irqrestore(&list_lock, flag);
+               /* no more free event node */
+               printk(KERN_WARNING "mxc_event send: no more free node\n");
+               return -1;
+       }
+
+       /* get a free node from free list, and added to event list */
+       list_node = free_head.next;
+       /* fill event */
+       event_elem = list_entry(list_node, struct hw_event_elem, list);
+       event_elem->event = *new_event;
+       list_move(list_node, &event_head);
+       spin_unlock_irqrestore(&list_lock, flag);
+
+       wake_up(&event_wq);
+
+       return 0;
+}
+
+static int __init mxc_hw_event_init(void)
+{
+       int i;
+
+       /* initial the list head for event and free */
+       INIT_LIST_HEAD(&free_head);
+       INIT_LIST_HEAD(&event_head);
+
+       /* initial the free list */
+       for (i = 0; i < EVENT_POOL_SIZE; i++)
+               list_add_tail(&events_pool[i].list, &free_head);
+
+       /* create netlink kernel sock */
+       nl_event_sock =
+           netlink_kernel_create(&init_net, NETLINK_USERSOCK, 0, NULL, NULL,
+                                 THIS_MODULE);
+       if (!nl_event_sock) {
+               printk(KERN_WARNING
+                      "mxc_hw_event: Fail to create netlink socket.\n");
+               return 1;
+       }
+
+       hwevent_kthread = kthread_run(hw_event_thread, NULL, "hwevent");
+       if (IS_ERR(hwevent_kthread)) {
+               printk(KERN_WARNING
+                      "mxc_hw_event: Fail to create hwevent thread.\n");
+               return 1;
+       }
+
+       initialized = 1;
+
+       return 0;
+}
+
+static void __exit mxc_hw_event_exit(void)
+{
+       kthread_stop(hwevent_kthread);
+       /* wait for thread completion */
+       sock_release(nl_event_sock->sk_socket);
+}
+
+module_init(mxc_hw_event_init);
+module_exit(mxc_hw_event_exit);
+
+EXPORT_SYMBOL(hw_event_send);
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/ipu/Kconfig b/drivers/mxc/ipu/Kconfig
new file mode 100644 (file)
index 0000000..919cb59
--- /dev/null
@@ -0,0 +1,4 @@
+config MXC_IPU_V1
+       bool
+
+source "drivers/mxc/ipu/pf/Kconfig"
diff --git a/drivers/mxc/ipu/Makefile b/drivers/mxc/ipu/Makefile
new file mode 100644 (file)
index 0000000..4e9f19f
--- /dev/null
@@ -0,0 +1,5 @@
+obj-$(CONFIG_MXC_IPU_V1) = mxc_ipu.o
+
+mxc_ipu-objs := ipu_common.o ipu_sdc.o ipu_adc.o ipu_ic.o ipu_csi.o ipu_device.o ipu_calc_stripes_sizes.o
+
+obj-$(CONFIG_MXC_IPU_PF)        +=   pf/
diff --git a/drivers/mxc/ipu/ipu_adc.c b/drivers/mxc/ipu/ipu_adc.c
new file mode 100644 (file)
index 0000000..0c2ce89
--- /dev/null
@@ -0,0 +1,689 @@
+/*
+ * Copyright 2005-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 ipu_adc.c
+ *
+ * @brief IPU ADC functions
+ *
+ * @ingroup IPU
+ */
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/ipu.h>
+
+#include "ipu_prv.h"
+#include "ipu_regs.h"
+#include "ipu_param_mem.h"
+
+/*#define ADC_CHAN1_SA_MASK 0xFF800000 */
+
+static void _ipu_set_cmd_data_mappings(display_port_t disp,
+                                      uint32_t pixel_fmt, int ifc_width);
+
+int32_t _ipu_adc_init_channel(ipu_channel_t chan, display_port_t disp,
+                             mcu_mode_t cmd, int16_t x_pos, int16_t y_pos)
+{
+       uint32_t reg;
+       uint32_t start_addr, stride;
+       unsigned long lock_flags;
+       uint32_t size;
+
+       size = 0;
+
+       switch (disp) {
+       case DISP0:
+               reg = __raw_readl(ADC_DISP0_CONF);
+               stride = reg & ADC_DISP_CONF_SL_MASK;
+               break;
+       case DISP1:
+               reg = __raw_readl(ADC_DISP1_CONF);
+               stride = reg & ADC_DISP_CONF_SL_MASK;
+               break;
+       case DISP2:
+               reg = __raw_readl(ADC_DISP2_CONF);
+               stride = reg & ADC_DISP_CONF_SL_MASK;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (stride == 0)
+               return -EINVAL;
+
+       stride++;
+       start_addr = (y_pos * stride) + x_pos;
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+       reg = __raw_readl(ADC_CONF);
+
+       switch (chan) {
+       case ADC_SYS1:
+               reg &= ~0x00FF4000;
+               reg |=
+                   ((uint32_t) size << 21 | (uint32_t) disp << 19 | (uint32_t)
+                    cmd << 16);
+
+               __raw_writel(start_addr, ADC_SYSCHA1_SA);
+               break;
+
+       case ADC_SYS2:
+               reg &= ~0xFF008000;
+               reg |=
+                   ((uint32_t) size << 29 | (uint32_t) disp << 27 | (uint32_t)
+                    cmd << 24);
+
+               __raw_writel(start_addr, ADC_SYSCHA2_SA);
+               break;
+
+       case CSI_PRP_VF_ADC:
+       case MEM_PRP_VF_ADC:
+               reg &= ~0x000000F9;
+               reg |=
+                   ((uint32_t) size << 5 | (uint32_t) disp << 3 |
+                    ADC_CONF_PRP_EN);
+
+               __raw_writel(start_addr, ADC_PRPCHAN_SA);
+               break;
+
+       case MEM_PP_ADC:
+               reg &= ~0x00003F02;
+               reg |=
+                   ((uint32_t) size << 10 | (uint32_t) disp << 8 |
+                    ADC_CONF_PP_EN);
+
+               __raw_writel(start_addr, ADC_PPCHAN_SA);
+               break;
+       default:
+               spin_unlock_irqrestore(&ipu_lock, lock_flags);
+               return -1;
+               break;
+       }
+       __raw_writel(reg, ADC_CONF);
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+       return 0;
+}
+
+int32_t _ipu_adc_uninit_channel(ipu_channel_t chan)
+{
+       uint32_t reg;
+       unsigned long lock_flags;
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+       reg = __raw_readl(ADC_CONF);
+
+       switch (chan) {
+       case ADC_SYS1:
+               reg &= ~0x00FF4000;
+               break;
+       case ADC_SYS2:
+               reg &= ~0xFF008000;
+               break;
+       case CSI_PRP_VF_ADC:
+       case MEM_PRP_VF_ADC:
+               reg &= ~0x000000F9;
+               break;
+       case MEM_PP_ADC:
+               reg &= ~0x00003F02;
+               break;
+       default:
+               spin_unlock_irqrestore(&ipu_lock, lock_flags);
+               return -1;
+               break;
+       }
+       __raw_writel(reg, ADC_CONF);
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+       return 0;
+}
+
+int32_t ipu_adc_write_template(display_port_t disp, uint32_t *pCmd, bool write)
+{
+       uint32_t ima_addr = 0;
+       uint32_t row_nu;
+       int i;
+
+       /* Set IPU_IMA_ADDR (IPU Internal Memory Access Address) */
+       /* MEM_NU = 0x0001 (CPM) */
+       /* ROW_NU = 2*N ( N is channel number) */
+       /* WORD_NU = 0  */
+       if (write) {
+               row_nu = (uint32_t) disp * 2 * ATM_ADDR_RANGE;
+       } else {
+               row_nu = ((uint32_t) disp * 2 + 1) * ATM_ADDR_RANGE;
+       }
+
+       /* form template addr for IPU_IMA_ADDR */
+       ima_addr = (0x3 << 16 /*Template memory */  | row_nu << 3);
+
+       __raw_writel(ima_addr, IPU_IMA_ADDR);
+
+       /* write template data for IPU_IMA_DATA */
+       for (i = 0; i < TEMPLATE_BUF_SIZE; i++)
+               /* only DATA field are needed */
+               __raw_writel(pCmd[i], IPU_IMA_DATA);
+
+       return 0;
+}
+
+int32_t
+ipu_adc_write_cmd(display_port_t disp, cmddata_t type,
+                 uint32_t cmd, const uint32_t *params, uint16_t numParams)
+{
+       uint16_t i;
+       int disable_di = 0;
+       u32 reg;
+       unsigned long lock_flags;
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+       reg = __raw_readl(IPU_CONF);
+       if ((reg & IPU_CONF_DI_EN) == 0) {
+               disable_di = 1;
+               reg |= IPU_CONF_DI_EN;
+               __raw_writel(reg, IPU_CONF);
+       }
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+       __raw_writel((uint32_t) ((type ? 0x0 : 0x1) | disp << 1 | 0x10),
+                    DI_DISP_LLA_CONF);
+       __raw_writel(cmd, DI_DISP_LLA_DATA);
+       udelay(3);
+
+       __raw_writel((uint32_t) (0x10 | disp << 1 | 0x11), DI_DISP_LLA_CONF);
+       for (i = 0; i < numParams; i++) {
+               __raw_writel(params[i], DI_DISP_LLA_DATA);
+               udelay(3);
+       }
+
+       if (disable_di) {
+               spin_lock_irqsave(&ipu_lock, lock_flags);
+               reg = __raw_readl(IPU_CONF);
+               reg &= ~IPU_CONF_DI_EN;
+               __raw_writel(reg, IPU_CONF);
+               spin_unlock_irqrestore(&ipu_lock, lock_flags);
+       }
+
+       return 0;
+}
+
+int32_t ipu_adc_set_update_mode(ipu_channel_t channel,
+                               ipu_adc_update_mode_t mode,
+                               uint32_t refresh_rate, unsigned long addr,
+                               uint32_t *size)
+{
+       int32_t err = 0;
+       uint32_t ref_per, reg, src = 0;
+       unsigned long lock_flags;
+       uint32_t ipu_freq;
+
+       ipu_freq = clk_get_rate(g_ipu_clk);
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       reg = __raw_readl(IPU_FS_DISP_FLOW);
+       reg &= ~FS_AUTO_REF_PER_MASK;
+       switch (mode) {
+       case IPU_ADC_REFRESH_NONE:
+               src = 0;
+               break;
+       case IPU_ADC_AUTO_REFRESH:
+               if (refresh_rate == 0) {
+                       err = -EINVAL;
+                       goto err0;
+               }
+               ref_per = ipu_freq / ((1UL << 17) * refresh_rate);
+               ref_per--;
+               reg |= ref_per << FS_AUTO_REF_PER_OFFSET;
+
+               src = FS_SRC_AUTOREF;
+               break;
+       case IPU_ADC_AUTO_REFRESH_SNOOP:
+               if (refresh_rate == 0) {
+                       err = -EINVAL;
+                       goto err0;
+               }
+               ref_per = ipu_freq / ((1UL << 17) * refresh_rate);
+               ref_per--;
+               reg |= ref_per << FS_AUTO_REF_PER_OFFSET;
+
+               src = FS_SRC_AUTOREF_SNOOP;
+               break;
+       case IPU_ADC_SNOOPING:
+               src = FS_SRC_SNOOP;
+               break;
+       }
+
+       switch (channel) {
+       case ADC_SYS1:
+               reg &= ~FS_ADC1_SRC_SEL_MASK;
+               reg |= src << FS_ADC1_SRC_SEL_OFFSET;
+               break;
+       case ADC_SYS2:
+               reg &= ~FS_ADC2_SRC_SEL_MASK;
+               reg |= src << FS_ADC2_SRC_SEL_OFFSET;
+               break;
+       default:
+               spin_unlock_irqrestore(&ipu_lock, lock_flags);
+               return -EINVAL;
+       }
+       __raw_writel(reg, IPU_FS_DISP_FLOW);
+
+       /* Setup bus snooping */
+       if ((mode == IPU_ADC_AUTO_REFRESH_SNOOP) || (mode == IPU_ADC_SNOOPING)) {
+               err = mxc_snoop_set_config(0, addr, *size);
+               if (err > 0) {
+                       *size = err;
+                       err = 0;
+               }
+       } else {
+               mxc_snoop_set_config(0, 0, 0);
+       }
+
+      err0:
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+       return err;
+}
+
+int32_t ipu_adc_get_snooping_status(uint32_t *statl, uint32_t *stath)
+{
+       return mxc_snoop_get_status(0, statl, stath);
+}
+
+int32_t ipu_adc_init_panel(display_port_t disp,
+                          uint16_t width, uint16_t height,
+                          uint32_t pixel_fmt,
+                          uint32_t stride,
+                          ipu_adc_sig_cfg_t sig,
+                          display_addressing_t addr,
+                          uint32_t vsync_width, vsync_t mode)
+{
+       uint32_t temp;
+       unsigned long lock_flags;
+       uint32_t ser_conf;
+       uint32_t disp_conf;
+       uint32_t adc_disp_conf;
+       uint32_t adc_disp_vsync;
+       uint32_t old_pol;
+
+       if ((disp != DISP1) && (disp != DISP2) &&
+           (sig.ifc_mode >= IPU_ADC_IFC_MODE_3WIRE_SERIAL)) {
+               return -EINVAL;
+       }
+/*        adc_disp_conf = ((uint32_t)((((size == 3)||(size == 2))?1:0)<<14) |  */
+/*                         (uint32_t)addr<<12 | (stride-1)); */
+       adc_disp_conf = (uint32_t) addr << 12 | (stride - 1);
+
+       _ipu_set_cmd_data_mappings(disp, pixel_fmt, sig.ifc_width);
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+       disp_conf = __raw_readl(DI_DISP_IF_CONF);
+       old_pol = __raw_readl(DI_DISP_SIG_POL);
+       adc_disp_vsync = __raw_readl(ADC_DISP_VSYNC);
+
+       switch (disp) {
+       case DISP0:
+               __raw_writel(adc_disp_conf, ADC_DISP0_CONF);
+               __raw_writel((((height - 1) << 16) | (width - 1)),
+                            ADC_DISP0_SS);
+
+               adc_disp_vsync &= ~(ADC_DISP_VSYNC_D0_MODE_MASK |
+                                   ADC_DISP_VSYNC_D0_WIDTH_MASK);
+               adc_disp_vsync |= (vsync_width << 16) | (uint32_t) mode;
+
+               old_pol &= ~0x2000003FL;
+               old_pol |= sig.data_pol | sig.cs_pol << 1 |
+                   sig.addr_pol << 2 | sig.read_pol << 3 |
+                   sig.write_pol << 4 | sig.Vsync_pol << 5 |
+                   sig.burst_pol << 29;
+               __raw_writel(old_pol, DI_DISP_SIG_POL);
+
+               disp_conf &= ~0x0000001FL;
+               disp_conf |= (sig.burst_mode << 3) | (sig.ifc_mode << 1) |
+                   DI_CONF_DISP0_EN;
+               __raw_writel(disp_conf, DI_DISP_IF_CONF);
+               break;
+       case DISP1:
+               __raw_writel(adc_disp_conf, ADC_DISP1_CONF);
+               __raw_writel((((height - 1) << 16) | (width - 1)),
+                            ADC_DISP12_SS);
+
+               adc_disp_vsync &= ~(ADC_DISP_VSYNC_D12_MODE_MASK |
+                                   ADC_DISP_VSYNC_D12_WIDTH_MASK);
+               adc_disp_vsync |= (vsync_width << 16) | (uint32_t) mode;
+
+               old_pol &= ~0x4000FF00L;
+               old_pol |= (sig.Vsync_pol << 6 | sig.data_pol << 8 |
+                           sig.cs_pol << 9 | sig.addr_pol << 10 |
+                           sig.read_pol << 11 | sig.write_pol << 12 |
+                           sig.clk_pol << 14 | sig.burst_pol << 30);
+               __raw_writel(old_pol, DI_DISP_SIG_POL);
+
+               disp_conf &= ~0x00003F00L;
+               if (sig.ifc_mode >= IPU_ADC_IFC_MODE_3WIRE_SERIAL) {
+                       ser_conf = (sig.ifc_width - 1) <<
+                           DI_SER_DISPx_CONF_SER_BIT_NUM_OFFSET;
+                       if (sig.ser_preamble_len) {
+                               ser_conf |= DI_SER_DISPx_CONF_PREAMBLE_EN;
+                               ser_conf |= sig.ser_preamble <<
+                                   DI_SER_DISPx_CONF_PREAMBLE_OFFSET;
+                               ser_conf |= (sig.ser_preamble_len - 1) <<
+                                   DI_SER_DISPx_CONF_PREAMBLE_LEN_OFFSET;
+                       }
+
+                       ser_conf |=
+                           sig.ser_rw_mode << DI_SER_DISPx_CONF_RW_CFG_OFFSET;
+
+                       if (sig.burst_mode == IPU_ADC_BURST_SERIAL)
+                               ser_conf |= DI_SER_DISPx_CONF_BURST_MODE_EN;
+                       __raw_writel(ser_conf, DI_SER_DISP1_CONF);
+               } else {        /* parallel interface */
+                       disp_conf |= (uint32_t) (sig.burst_mode << 12);
+               }
+               disp_conf |= (sig.ifc_mode << 9) | DI_CONF_DISP1_EN;
+               __raw_writel(disp_conf, DI_DISP_IF_CONF);
+               break;
+       case DISP2:
+               __raw_writel(adc_disp_conf, ADC_DISP2_CONF);
+               __raw_writel((((height - 1) << 16) | (width - 1)),
+                            ADC_DISP12_SS);
+
+               adc_disp_vsync &= ~(ADC_DISP_VSYNC_D12_MODE_MASK |
+                                   ADC_DISP_VSYNC_D12_WIDTH_MASK);
+               adc_disp_vsync |= (vsync_width << 16) | (uint32_t) mode;
+
+               old_pol &= ~0x80FF0000L;
+               temp = (uint32_t) (sig.data_pol << 16 | sig.cs_pol << 17 |
+                                  sig.addr_pol << 18 | sig.read_pol << 19 |
+                                  sig.write_pol << 20 | sig.Vsync_pol << 6 |
+                                  sig.burst_pol << 31 | sig.clk_pol << 22);
+               __raw_writel(temp | old_pol, DI_DISP_SIG_POL);
+
+               disp_conf &= ~0x003F0000L;
+               if (sig.ifc_mode >= IPU_ADC_IFC_MODE_3WIRE_SERIAL) {
+                       ser_conf = (sig.ifc_width - 1) <<
+                           DI_SER_DISPx_CONF_SER_BIT_NUM_OFFSET;
+                       if (sig.ser_preamble_len) {
+                               ser_conf |= DI_SER_DISPx_CONF_PREAMBLE_EN;
+                               ser_conf |= sig.ser_preamble <<
+                                   DI_SER_DISPx_CONF_PREAMBLE_OFFSET;
+                               ser_conf |= (sig.ser_preamble_len - 1) <<
+                                   DI_SER_DISPx_CONF_PREAMBLE_LEN_OFFSET;
+
+                       }
+
+                       ser_conf |=
+                           sig.ser_rw_mode << DI_SER_DISPx_CONF_RW_CFG_OFFSET;
+
+                       if (sig.burst_mode == IPU_ADC_BURST_SERIAL)
+                               ser_conf |= DI_SER_DISPx_CONF_BURST_MODE_EN;
+                       __raw_writel(ser_conf, DI_SER_DISP2_CONF);
+               } else {        /* parallel interface */
+                       disp_conf |= (uint32_t) (sig.burst_mode << 20);
+               }
+               disp_conf |= (sig.ifc_mode << 17) | DI_CONF_DISP2_EN;
+               __raw_writel(disp_conf, DI_DISP_IF_CONF);
+               break;
+       default:
+               break;
+       }
+
+       __raw_writel(adc_disp_vsync, ADC_DISP_VSYNC);
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+       return 0;
+}
+
+int32_t ipu_adc_init_ifc_timing(display_port_t disp, bool read,
+                               uint32_t cycle_time,
+                               uint32_t up_time,
+                               uint32_t down_time,
+                               uint32_t read_latch_time, uint32_t pixel_clk)
+{
+       uint32_t reg;
+       uint32_t time_conf3 = 0;
+       uint32_t clk_per;
+       uint32_t up_per;
+       uint32_t down_per;
+       uint32_t read_per;
+       uint32_t pixclk_per = 0;
+       uint32_t ipu_freq;
+
+       ipu_freq = clk_get_rate(g_ipu_clk);
+
+       clk_per = (cycle_time * (ipu_freq / 1000L) * 16L) / 1000000L;
+       up_per = (up_time * (ipu_freq / 1000L) * 4L) / 1000000L;
+       down_per = (down_time * (ipu_freq / 1000L) * 4L) / 1000000L;
+
+       reg = (clk_per << DISPx_IF_CLK_PER_OFFSET) |
+           (up_per << DISPx_IF_CLK_UP_OFFSET) |
+           (down_per << DISPx_IF_CLK_DOWN_OFFSET);
+
+       if (read) {
+               read_per =
+                   (read_latch_time * (ipu_freq / 1000L) * 4L) / 1000000L;
+               if (pixel_clk)
+                       pixclk_per = (ipu_freq * 16L) / pixel_clk;
+               time_conf3 = (read_per << DISPx_IF_CLK_READ_EN_OFFSET) |
+                   (pixclk_per << DISPx_PIX_CLK_PER_OFFSET);
+       }
+
+       dev_dbg(g_ipu_dev, "DI_DISPx_TIME_CONF_1/2 = 0x%08X\n", reg);
+       dev_dbg(g_ipu_dev, "DI_DISPx_TIME_CONF_3 = 0x%08X\n", time_conf3);
+
+       switch (disp) {
+       case DISP0:
+               if (read) {
+                       __raw_writel(reg, DI_DISP0_TIME_CONF_2);
+                       __raw_writel(time_conf3, DI_DISP0_TIME_CONF_3);
+               } else {
+                       __raw_writel(reg, DI_DISP0_TIME_CONF_1);
+               }
+               break;
+       case DISP1:
+               if (read) {
+                       __raw_writel(reg, DI_DISP1_TIME_CONF_2);
+                       __raw_writel(time_conf3, DI_DISP1_TIME_CONF_3);
+               } else {
+                       __raw_writel(reg, DI_DISP1_TIME_CONF_1);
+               }
+               break;
+       case DISP2:
+               if (read) {
+                       __raw_writel(reg, DI_DISP2_TIME_CONF_2);
+                       __raw_writel(time_conf3, DI_DISP2_TIME_CONF_3);
+               } else {
+                       __raw_writel(reg, DI_DISP2_TIME_CONF_1);
+               }
+               break;
+       default:
+               return -EINVAL;
+               break;
+       }
+
+       return 0;
+}
+
+struct ipu_adc_di_map {
+       uint32_t map_byte1;
+       uint32_t map_byte2;
+       uint32_t map_byte3;
+       uint32_t cycle_cnt;
+};
+
+static const struct ipu_adc_di_map di_mappings[] = {
+       [0] = {
+              /* RGB888, 8-bit bus */
+              .map_byte1 = 0x1600AAAA,
+              .map_byte2 = 0x00E05555,
+              .map_byte2 = 0x00070000,
+              .cycle_cnt = 3,
+              },
+       [1] = {
+              /* RGB666, 8-bit bus */
+              .map_byte1 = 0x1C00AAAF,
+              .map_byte2 = 0x00E0555F,
+              .map_byte3 = 0x0007000F,
+              .cycle_cnt = 3,
+              },
+       [2] = {
+              /* RGB565, 8-bit bus */
+              .map_byte1 = 0x008055BF,
+              .map_byte2 = 0x0142015F,
+              .map_byte3 = 0x0007003F,
+              .cycle_cnt = 2,
+              },
+       [3] = {
+              /* RGB888, 24-bit bus */
+              .map_byte1 = 0x0007000F,
+              .map_byte2 = 0x000F000F,
+              .map_byte3 = 0x0017000F,
+              .cycle_cnt = 1,
+              },
+       [4] = {
+              /* RGB666, 18-bit bus */
+              .map_byte1 = 0x0005000F,
+              .map_byte2 = 0x000B000F,
+              .map_byte3 = 0x0011000F,
+              .cycle_cnt = 1,
+              },
+       [5] = {
+              /* RGB565, 16-bit bus */
+              .map_byte1 = 0x0004003F,
+              .map_byte2 = 0x000A000F,
+              .map_byte3 = 0x000F003F,
+              .cycle_cnt = 1,
+              },
+};
+
+/* Private methods */
+static void _ipu_set_cmd_data_mappings(display_port_t disp,
+                                      uint32_t pixel_fmt, int ifc_width)
+{
+       uint32_t reg;
+       u32 map = 0;
+
+       if (ifc_width == 8) {
+               switch (pixel_fmt) {
+               case IPU_PIX_FMT_BGR24:
+                       map = 0;
+                       break;
+               case IPU_PIX_FMT_RGB666:
+                       map = 1;
+                       break;
+               case IPU_PIX_FMT_RGB565:
+                       map = 2;
+                       break;
+               default:
+                       break;
+               }
+       } else if (ifc_width >= 16) {
+               switch (pixel_fmt) {
+               case IPU_PIX_FMT_BGR24:
+                       map = 3;
+                       break;
+               case IPU_PIX_FMT_RGB666:
+                       map = 4;
+                       break;
+               case IPU_PIX_FMT_RGB565:
+                       map = 5;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       switch (disp) {
+       case DISP0:
+               if (ifc_width == 8) {
+                       __raw_writel(0x00070000, DI_DISP0_CB0_MAP);
+                       __raw_writel(0x0000FFFF, DI_DISP0_CB1_MAP);
+                       __raw_writel(0x0000FFFF, DI_DISP0_CB2_MAP);
+               } else {
+                       __raw_writel(0x00070000, DI_DISP0_CB0_MAP);
+                       __raw_writel(0x000F0000, DI_DISP0_CB1_MAP);
+                       __raw_writel(0x0000FFFF, DI_DISP0_CB2_MAP);
+               }
+               __raw_writel(di_mappings[map].map_byte1, DI_DISP0_DB0_MAP);
+               __raw_writel(di_mappings[map].map_byte2, DI_DISP0_DB1_MAP);
+               __raw_writel(di_mappings[map].map_byte3, DI_DISP0_DB2_MAP);
+               reg = __raw_readl(DI_DISP_ACC_CC);
+               reg &= ~DISP0_IF_CLK_CNT_D_MASK;
+               reg |= (di_mappings[map].cycle_cnt - 1) <<
+                   DISP0_IF_CLK_CNT_D_OFFSET;
+               __raw_writel(reg, DI_DISP_ACC_CC);
+               break;
+       case DISP1:
+               if (ifc_width == 8) {
+                       __raw_writel(0x00070000, DI_DISP1_CB0_MAP);
+                       __raw_writel(0x0000FFFF, DI_DISP1_CB1_MAP);
+                       __raw_writel(0x0000FFFF, DI_DISP1_CB2_MAP);
+               } else {
+                       __raw_writel(0x00070000, DI_DISP1_CB0_MAP);
+                       __raw_writel(0x000F0000, DI_DISP1_CB1_MAP);
+                       __raw_writel(0x0000FFFF, DI_DISP1_CB2_MAP);
+               }
+               __raw_writel(di_mappings[map].map_byte1, DI_DISP1_DB0_MAP);
+               __raw_writel(di_mappings[map].map_byte2, DI_DISP1_DB1_MAP);
+               __raw_writel(di_mappings[map].map_byte3, DI_DISP1_DB2_MAP);
+               reg = __raw_readl(DI_DISP_ACC_CC);
+               reg &= ~DISP1_IF_CLK_CNT_D_MASK;
+               reg |= (di_mappings[map].cycle_cnt - 1) <<
+                   DISP1_IF_CLK_CNT_D_OFFSET;
+               __raw_writel(reg, DI_DISP_ACC_CC);
+               break;
+       case DISP2:
+               if (ifc_width == 8) {
+                       __raw_writel(0x00070000, DI_DISP2_CB0_MAP);
+                       __raw_writel(0x0000FFFF, DI_DISP2_CB1_MAP);
+                       __raw_writel(0x0000FFFF, DI_DISP2_CB2_MAP);
+               } else {
+                       __raw_writel(0x00070000, DI_DISP2_CB0_MAP);
+                       __raw_writel(0x000F0000, DI_DISP2_CB1_MAP);
+                       __raw_writel(0x0000FFFF, DI_DISP2_CB2_MAP);
+               }
+               __raw_writel(di_mappings[map].map_byte1, DI_DISP2_DB0_MAP);
+               __raw_writel(di_mappings[map].map_byte2, DI_DISP2_DB1_MAP);
+               __raw_writel(di_mappings[map].map_byte3, DI_DISP2_DB2_MAP);
+               reg = __raw_readl(DI_DISP_ACC_CC);
+               reg &= ~DISP2_IF_CLK_CNT_D_MASK;
+               reg |= (di_mappings[map].cycle_cnt - 1) <<
+                   DISP2_IF_CLK_CNT_D_OFFSET;
+               __raw_writel(reg, DI_DISP_ACC_CC);
+               break;
+       default:
+               break;
+       }
+}
+
+void ipu_disp_direct_write(ipu_channel_t channel, u32 value, u32 offset)
+{
+       /*TODO*/
+}
+
+int ipu_init_async_panel(int disp, int type, uint32_t cycle_time,
+                        uint32_t pixel_fmt, ipu_adc_sig_cfg_t sig)
+{
+       /*TODO:uniform interface for ipu async panel init*/
+       return -1;
+}
+
+EXPORT_SYMBOL(ipu_adc_write_template);
+EXPORT_SYMBOL(ipu_adc_write_cmd);
+EXPORT_SYMBOL(ipu_adc_set_update_mode);
+EXPORT_SYMBOL(ipu_adc_init_panel);
+EXPORT_SYMBOL(ipu_adc_init_ifc_timing);
diff --git a/drivers/mxc/ipu/ipu_calc_stripes_sizes.c b/drivers/mxc/ipu/ipu_calc_stripes_sizes.c
new file mode 100644 (file)
index 0000000..b6230e8
--- /dev/null
@@ -0,0 +1,374 @@
+/*
+ * 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
+ */
+
+/*
+ * @file ipu_calc_stripes_sizes.c
+ *
+ * @brief IPU IC functions
+ *
+ * @ingroup IPU
+ */
+
+#include <linux/module.h>
+#include <linux/ipu.h>
+#include <asm/div64.h>
+
+#define BPP_32 0
+#define BPP_16 3
+#define BPP_8 5
+#define BPP_24 1
+#define BPP_12 4
+#define BPP_18 2
+
+static u64 _do_div(u64 a, u32 b)
+{
+       u64 div;
+       div = a;
+       do_div(div, b);
+       return div;
+}
+
+static u32 truncate(u32 up, /* 0: down; else: up */
+                                       u64 a, /* must be non-negative */
+                                       u32 b)
+{
+       u32 d;
+       u64 div;
+       div = _do_div(a, b);
+       d = b * (div >> 32);
+       if (up && (a > (((u64)d) << 32)))
+               return d+b;
+       else
+               return d;
+}
+
+static unsigned int f_calc(unsigned int pfs, unsigned int bpp, unsigned int *write)
+{/* return input_f */
+       unsigned int f_calculated = 0;
+       switch (pfs) {
+       case IPU_PIX_FMT_YVU422P:
+       case IPU_PIX_FMT_YUV422P:
+       case IPU_PIX_FMT_YUV420P2:
+       case IPU_PIX_FMT_YUV420P:
+               f_calculated = 16;
+               break;
+
+       case IPU_PIX_FMT_NV12:
+               f_calculated = 8;
+               break;
+
+       default:
+               f_calculated = 0;
+               break;
+
+       }
+       if (!f_calculated) {
+               switch (bpp) {
+               case BPP_32:
+                       f_calculated = 2;
+                       break;
+
+               case BPP_16:
+                       f_calculated = 4;
+                       break;
+
+               case BPP_8:
+               case BPP_24:
+                       f_calculated = 8;
+                       break;
+
+               case BPP_12:
+                       f_calculated = 16;
+                       break;
+
+               case BPP_18:
+                       f_calculated = 32;
+                       break;
+
+               default:
+                       f_calculated = 0;
+                       break;
+                       }
+               }
+       return f_calculated;
+}
+
+
+static unsigned int m_calc(unsigned int pfs)
+{
+       unsigned int m_calculated = 0;
+       switch (pfs) {
+       case IPU_PIX_FMT_YUV420P2:
+       case IPU_PIX_FMT_YUV420P:
+       case IPU_PIX_FMT_YVU422P:
+       case IPU_PIX_FMT_YUV422P:
+       case IPU_PIX_FMT_YVU420P:
+       case IPU_PIX_FMT_NV12:
+               m_calculated = 8;
+               break;
+
+       case IPU_PIX_FMT_YUYV:
+       case IPU_PIX_FMT_UYVY:
+               m_calculated = 2;
+               break;
+
+       default:
+               m_calculated = 1;
+               break;
+
+       }
+       return m_calculated;
+}
+
+
+/* Stripe parameters calculator */
+/**************************************************************************
+Notes:
+MSW = the maximal width allowed for a stripe
+       i.MX31: 720, i.MX35: 800, i.MX37/51/53: 1024
+cirr = the maximal inverse resizing ratio for which overlap in the input
+       is requested; typically cirr~2
+equal_stripes:
+       0: each stripe is allowed to have independent parameters
+               for maximal image quality
+       1: the stripes are requested to have identical parameters
+       (except the base address), for maximal performance
+If performance is the top priority (above image quality)
+       Avoid overlap, by setting CIRR = 0
+               This will also force effectively identical_stripes = 1
+       Choose IF & OF that corresponds to the same IOX/SX for both stripes
+       Choose IFW & OFW such that
+       IFW/IM, IFW/IF, OFW/OM, OFW/OF are even integers
+       The function returns an error status:
+       0: no error
+       1: invalid input parameters -> aborted without result
+               Valid parameters should satisfy the following conditions
+               IFW <= OFW, otherwise downsizing is required
+                                        - which is not supported yet
+               4 <= IFW,OFW, so some interpolation may be needed even without overlap
+               IM, OM, IF, OF should not vanish
+               2*IF <= IFW
+               so the frame can be split to two equal stripes, even without overlap
+               2*(OF+IF/irr_opt) <= OFW
+               so a valid positive INW exists even for equal stripes
+               OF <= MSW, otherwise, the left stripe cannot be sufficiently large
+               MSW < OFW, so splitting to stripes is required
+               OFW <= 2*MSW, so two stripes are sufficient
+               (this also implies that 2<=MSW)
+       2: OF is not a multiple of OM - not fully-supported yet
+       Output is produced but OW is not guaranited to be a multiple of OM
+       4: OFW reduced to be a multiple of OM
+       8: CIRR > 1: truncated to 1
+       Overlap is not supported (and not needed) y for upsizing)
+**************************************************************************/
+int ipu_calc_stripes_sizes(const unsigned int input_frame_width,
+                          /* input frame width;>1 */
+                          unsigned int output_frame_width, /* output frame width; >1 */
+                          const unsigned int maximal_stripe_width,
+                          /* the maximal width allowed for a stripe */
+                          const unsigned long long cirr, /* see above */
+                          const unsigned int equal_stripes, /* see above */
+                          u32 input_pixelformat,/* pixel format after of read channel*/
+                          u32 output_pixelformat,/* pixel format after of write channel*/
+                          struct stripe_param *left,
+                          struct stripe_param *right)
+{
+       const unsigned int irr_frac_bits = 13;
+       const unsigned long irr_steps = 1 << irr_frac_bits;
+       const u64 dirr = ((u64)1) << (32 - 2);
+       /* The maximum relative difference allowed between the irrs */
+       const u64 cr = ((u64)4) << 32;
+       /* The importance ratio between the two terms in the cost function below */
+
+       unsigned int status;
+       unsigned int temp;
+       unsigned int onw_min;
+       unsigned int inw, onw, inw_best = 0;
+       /* number of pixels in the left stripe NOT hidden by the right stripe */
+       u64 irr_opt; /* the optimal inverse resizing ratio */
+       u64 rr_opt; /* the optimal resizing ratio = 1/irr_opt*/
+       u64 dinw; /* the misalignment between the stripes */
+       /* (measured in units of input columns) */
+       u64 difwl, difwr;
+       /* The number of input columns not reflected in the output */
+       /* the resizing ratio used for the right stripe is */
+       /*   left->irr and right->irr respectively */
+       u64 cost, cost_min;
+       u64 div; /* result of division */
+
+       unsigned int input_m, input_f, output_m, output_f; /* parameters for upsizing by stripes */
+
+       status = 0;
+
+       /* M, F calculations */
+       /* read back pfs from params */
+
+       input_f = f_calc(input_pixelformat, 0, NULL);
+       input_m = 16;
+       /* BPP should be used in the out_F calc */
+       /* Temporarily not used */
+       /* out_F = F_calc(idmac->pfs, idmac->bpp, NULL); */
+
+       output_f = 16;
+       output_m = m_calc(output_pixelformat);
+
+
+       if ((output_frame_width < input_frame_width) || (input_frame_width < 4)
+           || (output_frame_width < 4))
+               return 1;
+
+       irr_opt = _do_div((((u64)(input_frame_width - 1)) << 32),
+                         (output_frame_width - 1));
+       rr_opt = _do_div((((u64)(output_frame_width - 1)) << 32),
+                        (input_frame_width - 1));
+
+       if ((input_m == 0) || (output_m == 0) || (input_f == 0) || (output_f == 0)
+           || (input_frame_width < (2 * input_f))
+           || ((((u64)output_frame_width) << 32) <
+               (2 * ((((u64)output_f) << 32) + (input_f * rr_opt))))
+           || (maximal_stripe_width < output_f)
+           || (output_frame_width <= maximal_stripe_width)
+           || ((2 * maximal_stripe_width) < output_frame_width))
+               return 1;
+
+       if (output_f % output_m)
+               status += 2;
+
+       temp = truncate(0, (((u64)output_frame_width) << 32), output_m);
+       if (temp < output_frame_width) {
+               output_frame_width = temp;
+               status += 4;
+       }
+
+       if (equal_stripes) {
+               if ((irr_opt > cirr) /* overlap in the input is not requested */
+                   && ((input_frame_width % (input_m << 1)) == 0)
+                   && ((input_frame_width % (input_f << 1)) == 0)
+                   && ((output_frame_width % (output_m << 1)) == 0)
+                   && ((output_frame_width % (output_f << 1)) == 0)) {
+                       /* without overlap */
+                       left->input_width = right->input_width = right->input_column =
+                               input_frame_width >> 1;
+                       left->output_width = right->output_width = right->output_column =
+                               output_frame_width >> 1;
+                       left->input_column = right->input_column = 0;
+                       div = _do_div(((((u64)irr_steps) << 32) *
+                                      (right->input_width - 1)), (right->output_width - 1));
+                       left->irr = right->irr = truncate(0, div, 1);
+               } else { /* with overlap */
+                       onw = truncate(0, (((u64)output_frame_width) << 32) >> 1,
+                                      output_f);
+                       inw = truncate(0, onw * irr_opt, input_f);
+                       /* this is the maximal inw which allows the same resizing ratio */
+                       /* in both stripes */
+                       onw = truncate(1, (inw * rr_opt), output_f);
+                       div = _do_div((((u64)(irr_steps * inw)) <<
+                                      32), onw);
+                       left->irr = right->irr = truncate(0, div, 1);
+                       left->output_width = right->output_width =
+                               output_frame_width - onw;
+                       /* These are valid assignments for output_width, */
+                       /* assuming output_f is a multiple of output_m */
+                       div = (((u64)(left->output_width-1) * (left->irr)) << 32);
+                       div = (((u64)1) << 32) + _do_div(div, irr_steps);
+
+                       left->input_width = right->input_width = truncate(1, div, input_m);
+
+                       div = _do_div((((u64)((right->output_width - 1) * right->irr)) <<
+                                      32), irr_steps);
+                       difwr = (((u64)(input_frame_width - 1 - inw)) << 32) - div;
+                       div = _do_div((difwr + (((u64)input_f) << 32)), 2);
+                       left->input_column = truncate(0, div, input_f);
+
+
+                       /* This splits the truncated input columns evenly */
+                       /*    between the left and right margins */
+                       right->input_column = left->input_column + inw;
+                       left->output_column = 0;
+                       right->output_column = onw;
+               }
+       } else { /* independent stripes */
+               onw_min = output_frame_width - maximal_stripe_width;
+               /* onw is a multiple of output_f, in the range */
+               /* [max(output_f,output_frame_width-maximal_stripe_width),*/
+               /*min(output_frame_width-2,maximal_stripe_width)] */
+               /* definitely beyond the cost of any valid setting */
+               cost_min = (((u64)input_frame_width) << 32) + cr;
+               onw = truncate(0, ((u64)maximal_stripe_width), output_f);
+               if (output_frame_width - onw == 1)
+                       onw -= output_f; /*  => onw and output_frame_width-1-onw are positive */
+               inw = truncate(0, onw * irr_opt, input_f);
+               /* this is the maximal inw which allows the same resizing ratio */
+               /* in both stripes */
+               onw = truncate(1, inw * rr_opt, output_f);
+               do {
+                       div = _do_div((((u64)(irr_steps * inw)) << 32), onw);
+                       left->irr = truncate(0, div, 1);
+                       div = _do_div((((u64)(onw * left->irr)) << 32),
+                                     irr_steps);
+                       dinw = (((u64)inw) << 32) - div;
+
+                       div = _do_div((((u64)((output_frame_width - 1 - onw) * left->irr)) <<
+                                      32), irr_steps);
+
+                       difwl = (((u64)(input_frame_width - 1 - inw)) << 32) - div;
+
+                       cost = difwl + (((u64)(cr * dinw)) >> 32);
+
+                       if (cost < cost_min) {
+                               inw_best = inw;
+                               cost_min = cost;
+                       }
+
+                       inw -= input_f;
+                       onw = truncate(1, inw * rr_opt, output_f);
+                       /* This is the minimal onw which allows the same resizing ratio */
+                       /*     in both stripes */
+               } while (onw >= onw_min);
+
+               inw = inw_best;
+               onw = truncate(1, inw * rr_opt, output_f);
+               div = _do_div((((u64)(irr_steps * inw)) << 32), onw);
+               left->irr = truncate(0, div, 1);
+
+               left->output_width = onw;
+               right->output_width = output_frame_width - onw;
+               /* These are valid assignments for output_width, */
+               /* assuming output_f is a multiple of output_m */
+               left->input_width = truncate(1, ((u64)(inw + 1)) << 32, input_m);
+               right->input_width = truncate(1, ((u64)(input_frame_width - inw)) <<
+                                             32, input_m);
+
+               div = _do_div((((u64)(irr_steps * (input_frame_width - 1 - inw))) <<
+                              32), (right->output_width - 1));
+               right->irr = truncate(0, div, 1);
+               temp = truncate(0, ((u64)left->irr) * ((((u64)1) << 32) + dirr), 1);
+               if (temp < right->irr)
+                       right->irr = temp;
+               div = _do_div(((u64)((right->output_width - 1) * right->irr) <<
+                              32), irr_steps);
+               difwr = (u64)(input_frame_width - 1 - inw) - div;
+
+
+               div = _do_div((difwr + (((u64)input_f) << 32)), 2);
+               left->input_column = truncate(0, div, input_f);
+
+               /* This splits the truncated input columns evenly */
+               /*    between the left and right margins */
+               right->input_column = left->input_column + inw;
+               left->output_column = 0;
+               right->output_column = onw;
+       }
+       return status;
+}
+EXPORT_SYMBOL(ipu_calc_stripes_sizes);
diff --git a/drivers/mxc/ipu/ipu_common.c b/drivers/mxc/ipu/ipu_common.c
new file mode 100644 (file)
index 0000000..fd43452
--- /dev/null
@@ -0,0 +1,1970 @@
+/*
+ * Copyright 2005-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 ipu_common.c
+ *
+ * @brief This file contains the IPU driver common API functions.
+ *
+ * @ingroup IPU
+ */
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/ipu.h>
+#include <linux/fsl_devices.h>
+
+#include "ipu_prv.h"
+#include "ipu_regs.h"
+#include "ipu_param_mem.h"
+
+/*
+ * This type definition is used to define a node in the GPIO interrupt queue for
+ * registered interrupts for GPIO pins. Each node contains the GPIO signal number
+ * associated with the ISR and the actual ISR function pointer.
+ */
+struct ipu_irq_node {
+       irqreturn_t(*handler) (int, void *);    /*!< the ISR */
+       const char *name;       /*!< device associated with the interrupt */
+       void *dev_id;           /*!< some unique information for the ISR */
+       __u32 flags;            /*!< not used */
+};
+
+/* Globals */
+struct clk *g_ipu_clk;
+struct clk *g_ipu_csi_clk;
+static struct clk *dfm_clk;
+int g_ipu_irq[2];
+int g_ipu_hw_rev;
+bool g_sec_chan_en[21];
+uint32_t g_channel_init_mask;
+DEFINE_SPINLOCK(ipu_lock);
+struct device *g_ipu_dev;
+
+static struct ipu_irq_node ipu_irq_list[IPU_IRQ_COUNT];
+static const char driver_name[] = "mxc_ipu";
+
+static uint32_t g_ipu_config;
+static uint32_t g_channel_init_mask_backup;
+static bool g_csi_used;
+
+/* Static functions */
+static irqreturn_t ipu_irq_handler(int irq, void *desc);
+static void _ipu_pf_init(ipu_channel_params_t *params);
+static void _ipu_pf_uninit(void);
+
+static inline uint32_t channel_2_dma(ipu_channel_t ch, ipu_buffer_t type)
+{
+       return ((type == IPU_INPUT_BUFFER) ? ((uint32_t) ch & 0xFF) :
+               ((type == IPU_OUTPUT_BUFFER) ? (((uint32_t) ch >> 8) & 0xFF)
+                : (((uint32_t) ch >> 16) & 0xFF)));
+};
+
+static inline uint32_t DMAParamAddr(uint32_t dma_ch)
+{
+       return 0x10000 | (dma_ch << 4);
+};
+
+/*!
+ * This function is called by the driver framework to initialize the IPU
+ * hardware.
+ *
+ * @param       dev       The device structure for the IPU passed in by the framework.
+ *
+ * @return      This function returns 0 on success or negative error code on error
+ */
+static
+int ipu_probe(struct platform_device *pdev)
+{
+       struct mxc_ipu_config *ipu_conf = pdev->dev.platform_data;
+
+       spin_lock_init(&ipu_lock);
+
+       g_ipu_dev = &pdev->dev;
+       g_ipu_hw_rev = ipu_conf->rev;
+
+       /* Register IPU interrupts */
+       g_ipu_irq[0] = platform_get_irq(pdev, 0);
+       if (g_ipu_irq[0] < 0)
+               return -EINVAL;
+
+       if (request_irq(g_ipu_irq[0], ipu_irq_handler, 0, driver_name, 0) != 0) {
+               dev_err(g_ipu_dev, "request SYNC interrupt failed\n");
+               return -EBUSY;
+       }
+       /* Some platforms have 2 IPU interrupts */
+       g_ipu_irq[1] = platform_get_irq(pdev, 1);
+       if (g_ipu_irq[1] >= 0) {
+               if (request_irq
+                   (g_ipu_irq[1], ipu_irq_handler, 0, driver_name, 0) != 0) {
+                       dev_err(g_ipu_dev, "request ERR interrupt failed\n");
+                       return -EBUSY;
+               }
+       }
+
+       /* Enable IPU and CSI clocks */
+       /* Get IPU clock freq */
+       g_ipu_clk = clk_get(&pdev->dev, "ipu_clk");
+       dev_dbg(g_ipu_dev, "ipu_clk = %lu\n", clk_get_rate(g_ipu_clk));
+
+       g_ipu_csi_clk = clk_get(&pdev->dev, "csi_clk");
+
+       dfm_clk = clk_get(NULL, "dfm_clk");
+
+       clk_enable(g_ipu_clk);
+
+       /* resetting the CONF register of the IPU */
+       __raw_writel(0x00000000, IPU_CONF);
+
+       __raw_writel(0x00100010L, DI_HSP_CLK_PER);
+
+       /* Set SDC refresh channels as high priority */
+       __raw_writel(0x0000C000L, IDMAC_CHA_PRI);
+
+       /* Set to max back to back burst requests */
+       __raw_writel(0x00000000L, IDMAC_CONF);
+
+       register_ipu_device();
+
+       return 0;
+}
+
+/*!
+ * This function is called to initialize a logical IPU channel.
+ *
+ * @param       channel Input parameter for the logical channel ID to initalize.
+ *
+ * @param       params  Input parameter containing union of channel initialization
+ *                      parameters.
+ *
+ * @return      This function returns 0 on success or negative error code on fail
+ */
+int32_t ipu_init_channel(ipu_channel_t channel, ipu_channel_params_t *params)
+{
+       uint32_t ipu_conf;
+       uint32_t reg;
+       unsigned long lock_flags;
+
+       dev_dbg(g_ipu_dev, "init channel = %d\n", IPU_CHAN_ID(channel));
+
+       if ((channel != MEM_SDC_BG) && (channel != MEM_SDC_FG) &&
+           (channel != MEM_ROT_ENC_MEM) && (channel != MEM_ROT_VF_MEM) &&
+           (channel != MEM_ROT_PP_MEM) && (channel != CSI_MEM)
+           && (params == NULL)) {
+               return -EINVAL;
+       }
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       ipu_conf = __raw_readl(IPU_CONF);
+       if (ipu_conf == 0) {
+               clk_enable(g_ipu_clk);
+       }
+
+       if (g_channel_init_mask & (1L << IPU_CHAN_ID(channel))) {
+               dev_err(g_ipu_dev, "Warning: channel already initialized %d\n",
+                       IPU_CHAN_ID(channel));
+       }
+
+       switch (channel) {
+       case CSI_PRP_VF_MEM:
+               reg = __raw_readl(IPU_FS_PROC_FLOW);
+               __raw_writel(reg & ~FS_VF_IN_VALID, IPU_FS_PROC_FLOW);
+
+               if (params->mem_prp_vf_mem.graphics_combine_en)
+                       g_sec_chan_en[IPU_CHAN_ID(channel)] = true;
+
+               _ipu_ic_init_prpvf(params, true);
+               break;
+       case CSI_PRP_VF_ADC:
+               reg = __raw_readl(IPU_FS_PROC_FLOW);
+               __raw_writel(reg | (FS_DEST_ADC << FS_PRPVF_DEST_SEL_OFFSET),
+                            IPU_FS_PROC_FLOW);
+
+               _ipu_adc_init_channel(CSI_PRP_VF_ADC,
+                                     params->csi_prp_vf_adc.disp,
+                                     WriteTemplateNonSeq,
+                                     params->csi_prp_vf_adc.out_left,
+                                     params->csi_prp_vf_adc.out_top);
+
+               _ipu_ic_init_prpvf(params, true);
+               break;
+       case MEM_PRP_VF_MEM:
+               reg = __raw_readl(IPU_FS_PROC_FLOW);
+               __raw_writel(reg | FS_VF_IN_VALID, IPU_FS_PROC_FLOW);
+
+               if (params->mem_prp_vf_mem.graphics_combine_en)
+                       g_sec_chan_en[IPU_CHAN_ID(channel)] = true;
+
+               _ipu_ic_init_prpvf(params, false);
+               break;
+       case MEM_ROT_VF_MEM:
+               _ipu_ic_init_rotate_vf(params);
+               break;
+       case CSI_PRP_ENC_MEM:
+               reg = __raw_readl(IPU_FS_PROC_FLOW);
+               __raw_writel(reg & ~FS_ENC_IN_VALID, IPU_FS_PROC_FLOW);
+               _ipu_ic_init_prpenc(params, true);
+               break;
+       case MEM_PRP_ENC_MEM:
+               reg = __raw_readl(IPU_FS_PROC_FLOW);
+               __raw_writel(reg | FS_ENC_IN_VALID, IPU_FS_PROC_FLOW);
+               _ipu_ic_init_prpenc(params, false);
+               break;
+       case MEM_ROT_ENC_MEM:
+               _ipu_ic_init_rotate_enc(params);
+               break;
+       case MEM_PP_ADC:
+               reg = __raw_readl(IPU_FS_PROC_FLOW);
+               __raw_writel(reg | (FS_DEST_ADC << FS_PP_DEST_SEL_OFFSET),
+                            IPU_FS_PROC_FLOW);
+
+               _ipu_adc_init_channel(MEM_PP_ADC, params->mem_pp_adc.disp,
+                                     WriteTemplateNonSeq,
+                                     params->mem_pp_adc.out_left,
+                                     params->mem_pp_adc.out_top);
+
+               if (params->mem_pp_adc.graphics_combine_en)
+                       g_sec_chan_en[IPU_CHAN_ID(channel)] = true;
+
+               _ipu_ic_init_pp(params);
+               break;
+       case MEM_PP_MEM:
+               if (params->mem_pp_mem.graphics_combine_en)
+                       g_sec_chan_en[IPU_CHAN_ID(channel)] = true;
+
+               _ipu_ic_init_pp(params);
+               break;
+       case MEM_ROT_PP_MEM:
+               _ipu_ic_init_rotate_pp(params);
+               break;
+       case CSI_MEM:
+               _ipu_ic_init_csi(params);
+               break;
+
+       case MEM_PF_Y_MEM:
+       case MEM_PF_U_MEM:
+       case MEM_PF_V_MEM:
+               /* Enable PF block */
+               _ipu_pf_init(params);
+               break;
+
+       case MEM_SDC_BG:
+               break;
+       case MEM_SDC_FG:
+               break;
+       case ADC_SYS1:
+               _ipu_adc_init_channel(ADC_SYS1, params->adc_sys1.disp,
+                                     params->adc_sys1.ch_mode,
+                                     params->adc_sys1.out_left,
+                                     params->adc_sys1.out_top);
+               break;
+       case ADC_SYS2:
+               _ipu_adc_init_channel(ADC_SYS2, params->adc_sys2.disp,
+                                     params->adc_sys2.ch_mode,
+                                     params->adc_sys2.out_left,
+                                     params->adc_sys2.out_top);
+               break;
+       default:
+               dev_err(g_ipu_dev, "Missing channel initialization\n");
+               spin_unlock_irqrestore(&ipu_lock, lock_flags);
+               return -EINVAL;
+       }
+
+       /* Enable IPU sub module */
+       g_channel_init_mask |= 1L << IPU_CHAN_ID(channel);
+
+       if (g_channel_init_mask & 0x00000066L) {        /*CSI */
+               ipu_conf |= IPU_CONF_CSI_EN;
+               if (cpu_is_mx31() || cpu_is_mx32()) {
+                       g_csi_used = true;
+               }
+       }
+       if (g_channel_init_mask & 0x00001FFFL) {        /*IC */
+               ipu_conf |= IPU_CONF_IC_EN;
+       }
+       if (g_channel_init_mask & 0x00000A10L) {        /*ROT */
+               ipu_conf |= IPU_CONF_ROT_EN;
+       }
+       if (g_channel_init_mask & 0x0001C000L) {        /*SDC */
+               ipu_conf |= IPU_CONF_SDC_EN | IPU_CONF_DI_EN;
+       }
+       if (g_channel_init_mask & 0x00061140L) {        /*ADC */
+               ipu_conf |= IPU_CONF_ADC_EN | IPU_CONF_DI_EN;
+       }
+       if (g_channel_init_mask & 0x00380000L) {        /*PF */
+               ipu_conf |= IPU_CONF_PF_EN;
+       }
+       __raw_writel(ipu_conf, IPU_CONF);
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+       return 0;
+}
+
+/*!
+ * This function is called to uninitialize a logical IPU channel.
+ *
+ * @param       channel Input parameter for the logical channel ID to uninitalize.
+ */
+void ipu_uninit_channel(ipu_channel_t channel)
+{
+       unsigned long lock_flags;
+       uint32_t reg;
+       uint32_t dma, mask = 0;
+       uint32_t ipu_conf;
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       if ((g_channel_init_mask & (1L << IPU_CHAN_ID(channel))) == 0) {
+               dev_err(g_ipu_dev, "Channel already uninitialized %d\n",
+                       IPU_CHAN_ID(channel));
+               spin_unlock_irqrestore(&ipu_lock, lock_flags);
+               return;
+       }
+
+       /* Make sure channel is disabled */
+       /* Get input and output dma channels */
+       dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER);
+       if (dma != IDMA_CHAN_INVALID)
+               mask |= 1UL << dma;
+       dma = channel_2_dma(channel, IPU_INPUT_BUFFER);
+       if (dma != IDMA_CHAN_INVALID)
+               mask |= 1UL << dma;
+       /* Get secondary input dma channel */
+       if (g_sec_chan_en[IPU_CHAN_ID(channel)]) {
+               dma = channel_2_dma(channel, IPU_SEC_INPUT_BUFFER);
+               if (dma != IDMA_CHAN_INVALID) {
+                       mask |= 1UL << dma;
+               }
+       }
+       if (mask & __raw_readl(IDMAC_CHA_EN)) {
+               dev_err(g_ipu_dev,
+                       "Channel %d is not disabled, disable first\n",
+                       IPU_CHAN_ID(channel));
+               spin_unlock_irqrestore(&ipu_lock, lock_flags);
+               return;
+       }
+
+       /* Reset the double buffer */
+       reg = __raw_readl(IPU_CHA_DB_MODE_SEL);
+       __raw_writel(reg & ~mask, IPU_CHA_DB_MODE_SEL);
+
+       g_sec_chan_en[IPU_CHAN_ID(channel)] = false;
+
+       switch (channel) {
+       case CSI_MEM:
+               _ipu_ic_uninit_csi();
+               break;
+       case CSI_PRP_VF_ADC:
+               reg = __raw_readl(IPU_FS_PROC_FLOW);
+               __raw_writel(reg & ~FS_PRPVF_DEST_SEL_MASK, IPU_FS_PROC_FLOW);
+
+               _ipu_adc_uninit_channel(CSI_PRP_VF_ADC);
+
+               /* Fall thru */
+       case CSI_PRP_VF_MEM:
+       case MEM_PRP_VF_MEM:
+               _ipu_ic_uninit_prpvf();
+               break;
+       case MEM_PRP_VF_ADC:
+               break;
+       case MEM_ROT_VF_MEM:
+               _ipu_ic_uninit_rotate_vf();
+               break;
+       case CSI_PRP_ENC_MEM:
+       case MEM_PRP_ENC_MEM:
+               _ipu_ic_uninit_prpenc();
+               break;
+       case MEM_ROT_ENC_MEM:
+               _ipu_ic_uninit_rotate_enc();
+               break;
+       case MEM_PP_ADC:
+               reg = __raw_readl(IPU_FS_PROC_FLOW);
+               __raw_writel(reg & ~FS_PP_DEST_SEL_MASK, IPU_FS_PROC_FLOW);
+
+               _ipu_adc_uninit_channel(MEM_PP_ADC);
+
+               /* Fall thru */
+       case MEM_PP_MEM:
+               _ipu_ic_uninit_pp();
+               break;
+       case MEM_ROT_PP_MEM:
+               _ipu_ic_uninit_rotate_pp();
+               break;
+
+       case MEM_PF_Y_MEM:
+               _ipu_pf_uninit();
+               break;
+       case MEM_PF_U_MEM:
+       case MEM_PF_V_MEM:
+               break;
+
+       case MEM_SDC_BG:
+               break;
+       case MEM_SDC_FG:
+               break;
+       case ADC_SYS1:
+               _ipu_adc_uninit_channel(ADC_SYS1);
+               break;
+       case ADC_SYS2:
+               _ipu_adc_uninit_channel(ADC_SYS2);
+               break;
+       case MEM_SDC_MASK:
+       case CHAN_NONE:
+               break;
+       }
+
+       g_channel_init_mask &= ~(1L << IPU_CHAN_ID(channel));
+
+       ipu_conf = __raw_readl(IPU_CONF);
+       if ((g_channel_init_mask & 0x00000066L) == 0) { /*CSI */
+               ipu_conf &= ~IPU_CONF_CSI_EN;
+       }
+       if ((g_channel_init_mask & 0x00001FFFL) == 0) { /*IC */
+               ipu_conf &= ~IPU_CONF_IC_EN;
+       }
+       if ((g_channel_init_mask & 0x00000A10L) == 0) { /*ROT */
+               ipu_conf &= ~IPU_CONF_ROT_EN;
+       }
+       if ((g_channel_init_mask & 0x0001C000L) == 0) { /*SDC */
+               ipu_conf &= ~IPU_CONF_SDC_EN;
+       }
+       if ((g_channel_init_mask & 0x00061140L) == 0) { /*ADC */
+               ipu_conf &= ~IPU_CONF_ADC_EN;
+       }
+       if ((g_channel_init_mask & 0x0007D140L) == 0) { /*DI */
+               ipu_conf &= ~IPU_CONF_DI_EN;
+       }
+       if ((g_channel_init_mask & 0x00380000L) == 0) { /*PF */
+               ipu_conf &= ~IPU_CONF_PF_EN;
+       }
+       __raw_writel(ipu_conf, IPU_CONF);
+       if (ipu_conf == 0) {
+               clk_disable(g_ipu_clk);
+       }
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+}
+
+/*!
+ * This function is called to initialize a buffer for logical IPU channel.
+ *
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @param       type            Input parameter which buffer to initialize.
+ *
+ * @param       pixel_fmt       Input parameter for pixel format of buffer. Pixel
+ *                              format is a FOURCC ASCII code.
+ *
+ * @param       width           Input parameter for width of buffer in pixels.
+ *
+ * @param       height          Input parameter for height of buffer in pixels.
+ *
+ * @param       stride          Input parameter for stride length of buffer
+ *                              in pixels.
+ *
+ * @param       rot_mode        Input parameter for rotation setting of buffer.
+ *                              A rotation setting other than \b IPU_ROTATE_VERT_FLIP
+ *                              should only be used for input buffers of rotation
+ *                              channels.
+ *
+ * @param       phyaddr_0       Input parameter buffer 0 physical address.
+ *
+ * @param       phyaddr_1       Input parameter buffer 1 physical address.
+ *                              Setting this to a value other than NULL enables
+ *                              double buffering mode.
+ *
+ * @param       u                      private u offset for additional cropping,
+ *                                                             zero if not used.
+ *
+ * @param       v                      private v offset for additional cropping,
+ *                                                             zero if not used.
+ *
+ * @return      This function returns 0 on success or negative error code on fail
+ */
+int32_t ipu_init_channel_buffer(ipu_channel_t channel, ipu_buffer_t type,
+                               uint32_t pixel_fmt,
+                               uint16_t width, uint16_t height,
+                               uint32_t stride,
+                               ipu_rotate_mode_t rot_mode,
+                               dma_addr_t phyaddr_0, dma_addr_t phyaddr_1,
+                               uint32_t u, uint32_t v)
+{
+       uint32_t params[10];
+       unsigned long lock_flags;
+       uint32_t reg;
+       uint32_t dma_chan;
+
+       dma_chan = channel_2_dma(channel, type);
+
+       if (stride < width * bytes_per_pixel(pixel_fmt))
+               stride = width * bytes_per_pixel(pixel_fmt);
+
+       if (dma_chan == IDMA_CHAN_INVALID)
+               return -EINVAL;
+
+       if (stride % 4) {
+               dev_err(g_ipu_dev,
+                       "Stride must be 32-bit aligned, stride = %d\n", stride);
+               return -EINVAL;
+       }
+       /* IC channels' width must be multiple of 8 pixels     */
+       if ((dma_chan <= 13) && (width % 8)) {
+               dev_err(g_ipu_dev, "width must be 8 pixel multiple\n");
+               return -EINVAL;
+       }
+       /* Build parameter memory data for DMA channel */
+       _ipu_ch_param_set_size(params, pixel_fmt, width, height, stride, u, v);
+       _ipu_ch_param_set_buffer(params, phyaddr_0, phyaddr_1);
+       _ipu_ch_param_set_rotation(params, rot_mode);
+       /* Some channels (rotation) have restriction on burst length */
+       if ((dma_chan == 10) || (dma_chan == 11) || (dma_chan == 13)) {
+               _ipu_ch_param_set_burst_size(params, 8);
+       } else if (dma_chan == 24) {    /* PF QP channel */
+               _ipu_ch_param_set_burst_size(params, 4);
+       } else if (dma_chan == 25) {    /* PF H264 BS channel */
+               _ipu_ch_param_set_burst_size(params, 16);
+       } else if (((dma_chan == 14) || (dma_chan == 15)) &&
+                  pixel_fmt == IPU_PIX_FMT_RGB565) {
+               _ipu_ch_param_set_burst_size(params, 16);
+       }
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       _ipu_write_param_mem(DMAParamAddr(dma_chan), params, 10);
+
+       reg = __raw_readl(IPU_CHA_DB_MODE_SEL);
+       if (phyaddr_1) {
+               reg |= 1UL << dma_chan;
+       } else {
+               reg &= ~(1UL << dma_chan);
+       }
+       __raw_writel(reg, IPU_CHA_DB_MODE_SEL);
+
+       /* Reset to buffer 0 */
+       __raw_writel(1UL << dma_chan, IPU_CHA_CUR_BUF);
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+       return 0;
+}
+
+/*!
+ * This function is called to update the physical address of a buffer for
+ * a logical IPU channel.
+ *
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @param       type            Input parameter which buffer to initialize.
+ *
+ * @param       bufNum          Input parameter for which buffer number to update.
+ *                              0 or 1 are the only valid values.
+ *
+ * @param       phyaddr         Input parameter buffer physical address.
+ *
+ * @return      This function returns 0 on success or negative error code on
+ *              fail. This function will fail if the buffer is set to ready.
+ */
+int32_t ipu_update_channel_buffer(ipu_channel_t channel, ipu_buffer_t type,
+                                 uint32_t bufNum, dma_addr_t phyaddr)
+{
+       uint32_t reg;
+       unsigned long lock_flags;
+       uint32_t dma_chan = channel_2_dma(channel, type);
+
+       if (dma_chan == IDMA_CHAN_INVALID)
+               return -EINVAL;
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       if (bufNum == 0) {
+               reg = __raw_readl(IPU_CHA_BUF0_RDY);
+               if (reg & (1UL << dma_chan)) {
+                       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+                       return -EACCES;
+               }
+               __raw_writel(DMAParamAddr(dma_chan) + 0x0008UL, IPU_IMA_ADDR);
+               __raw_writel(phyaddr, IPU_IMA_DATA);
+       } else {
+               reg = __raw_readl(IPU_CHA_BUF1_RDY);
+               if (reg & (1UL << dma_chan)) {
+                       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+                       return -EACCES;
+               }
+               __raw_writel(DMAParamAddr(dma_chan) + 0x0009UL, IPU_IMA_ADDR);
+               __raw_writel(phyaddr, IPU_IMA_DATA);
+       }
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+       dev_dbg(g_ipu_dev, "IPU: update IDMA ch %d buf %d = 0x%08X\n",
+               dma_chan, bufNum, phyaddr);
+       return 0;
+}
+
+/*!
+ * This function is called to initialize a buffer for logical IPU channel.
+ *
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @param       type            Input parameter which buffer to initialize.
+ *
+ * @param       pixel_fmt       Input parameter for pixel format of buffer.
+ *                              Pixel format is a FOURCC ASCII code.
+ *
+ * @param       width           Input parameter for width of buffer in pixels.
+ *
+ * @param       height          Input parameter for height of buffer in pixels.
+ *
+ * @param       stride          Input parameter for stride length of buffer
+ *                              in pixels.
+ *
+ * @param       u                              predefined private u offset for additional cropping,
+ *                                                             zero if not used.
+ *
+ * @param       v                              predefined private v offset for additional cropping,
+ *                                                             zero if not used.
+ *
+ * @param              vertical_offset vertical offset for Y coordinate
+ *                                                             in the existed frame
+ *
+ *
+ * @param              horizontal_offset horizontal offset for X coordinate
+ *                                                             in the existed frame
+ *
+ *
+ * @return      Returns 0 on success or negative error code on fail
+ *              This function will fail if any buffer is set to ready.
+ */
+
+int32_t ipu_update_channel_offset(ipu_channel_t channel, ipu_buffer_t type,
+                               uint32_t pixel_fmt,
+                               uint16_t width, uint16_t height,
+                               uint32_t stride,
+                               uint32_t u, uint32_t v,
+                               uint32_t vertical_offset, uint32_t horizontal_offset)
+{
+       uint32_t reg;
+       int ret = 0;
+       unsigned long lock_flags;
+       uint32_t dma_chan = channel_2_dma(channel, type);
+
+       if (dma_chan == IDMA_CHAN_INVALID)
+               return -EINVAL;
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       ret = -EACCES;
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+       return ret;
+}
+EXPORT_SYMBOL(ipu_update_channel_offset);
+
+/*!
+ * This function is called to set a channel's buffer as ready.
+ *
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @param       type            Input parameter which buffer to initialize.
+ *
+ * @param       bufNum          Input parameter for which buffer number set to
+ *                              ready state.
+ *
+ * @return      This function returns 0 on success or negative error code on fail
+ */
+int32_t ipu_select_buffer(ipu_channel_t channel, ipu_buffer_t type,
+                         uint32_t bufNum)
+{
+       uint32_t dma_chan = channel_2_dma(channel, type);
+
+       if (dma_chan == IDMA_CHAN_INVALID)
+               return -EINVAL;
+
+       if (bufNum == 0) {
+               /*Mark buffer 0 as ready. */
+               __raw_writel(1UL << dma_chan, IPU_CHA_BUF0_RDY);
+       } else {
+               /*Mark buffer 1 as ready. */
+               __raw_writel(1UL << dma_chan, IPU_CHA_BUF1_RDY);
+       }
+       return 0;
+}
+
+/*!
+ * This function check buffer ready for a logical channel.
+ *
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @param       type            Input parameter which buffer to clear.
+ *
+ * @param       bufNum          Input parameter for which buffer number clear
+ *                             ready state.
+ *
+ */
+int32_t ipu_check_buffer_ready(ipu_channel_t channel, ipu_buffer_t type,
+               uint32_t bufNum)
+{
+       uint32_t dma_chan = channel_2_dma(channel, type);
+       uint32_t reg;
+
+       if (dma_chan == IDMA_CHAN_INVALID)
+               return -EINVAL;
+
+       if (bufNum == 0)
+               reg = __raw_readl(IPU_CHA_BUF0_RDY);
+       else
+               reg = __raw_readl(IPU_CHA_BUF1_RDY);
+
+       if (reg & (1UL << dma_chan))
+               return 1;
+       else
+               return 0;
+}
+EXPORT_SYMBOL(ipu_check_buffer_ready);
+
+/*!
+ * This function links 2 channels together for automatic frame
+ * synchronization. The output of the source channel is linked to the input of
+ * the destination channel.
+ *
+ * @param       src_ch          Input parameter for the logical channel ID of
+ *                              the source channel.
+ *
+ * @param       dest_ch         Input parameter for the logical channel ID of
+ *                              the destination channel.
+ *
+ * @return      This function returns 0 on success or negative error code on
+ *              fail.
+ */
+int32_t ipu_link_channels(ipu_channel_t src_ch, ipu_channel_t dest_ch)
+{
+       unsigned long lock_flags;
+       uint32_t out_dma;
+       uint32_t in_dma;
+       bool isProc;
+       uint32_t value;
+       uint32_t mask;
+       uint32_t offset;
+       uint32_t fs_proc_flow;
+       uint32_t fs_disp_flow;
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       fs_proc_flow = __raw_readl(IPU_FS_PROC_FLOW);
+       fs_disp_flow = __raw_readl(IPU_FS_DISP_FLOW);
+
+       out_dma = (1UL << channel_2_dma(src_ch, IPU_OUTPUT_BUFFER));
+       in_dma = (1UL << channel_2_dma(dest_ch, IPU_INPUT_BUFFER));
+
+       /* PROCESS THE OUTPUT DMA CH */
+       switch (out_dma) {
+               /*VF-> */
+       case IDMA_IC_1:
+               pr_debug("Link VF->");
+               isProc = true;
+               mask = FS_PRPVF_DEST_SEL_MASK;
+               offset = FS_PRPVF_DEST_SEL_OFFSET;
+               value = (in_dma == IDMA_IC_11) ? FS_DEST_ROT :  /*->VF_ROT */
+                   (in_dma == IDMA_ADC_SYS1_WR) ? FS_DEST_ADC1 :       /* ->ADC1 */
+                   (in_dma == IDMA_ADC_SYS2_WR) ? FS_DEST_ADC2 :       /* ->ADC2 */
+                   (in_dma == IDMA_SDC_BG) ? FS_DEST_SDC_BG :  /*->SDC_BG */
+                   (in_dma == IDMA_SDC_FG) ? FS_DEST_SDC_FG :  /*->SDC_FG */
+                   (in_dma == IDMA_ADC_SYS1_WR) ? FS_DEST_ADC1 :       /*->ADC1 */
+                   /* ->ADCDirect */
+                   0;
+               break;
+
+               /*VF_ROT-> */
+       case IDMA_IC_9:
+               pr_debug("Link VF_ROT->");
+               isProc = true;
+               mask = FS_PRPVF_ROT_DEST_SEL_MASK;
+               offset = FS_PRPVF_ROT_DEST_SEL_OFFSET;
+               value = (in_dma == IDMA_ADC_SYS1_WR) ? FS_DEST_ADC1 :   /*->ADC1 */
+                   (in_dma == IDMA_ADC_SYS2_WR) ? FS_DEST_ADC2 :       /* ->ADC2 */
+                   (in_dma == IDMA_SDC_BG) ? FS_DEST_SDC_BG :  /*->SDC_BG */
+                   (in_dma == IDMA_SDC_FG) ? FS_DEST_SDC_FG :  /*->SDC_FG */
+                   0;
+               break;
+
+               /*ENC-> */
+       case IDMA_IC_0:
+               pr_debug("Link ENC->");
+               isProc = true;
+               mask = 0;
+               offset = 0;
+               value = (in_dma == IDMA_IC_10) ? FS_PRPENC_DEST_SEL :   /*->ENC_ROT     */
+                   0;
+               break;
+
+               /*PP-> */
+       case IDMA_IC_2:
+               pr_debug("Link PP->");
+               isProc = true;
+               mask = FS_PP_DEST_SEL_MASK;
+               offset = FS_PP_DEST_SEL_OFFSET;
+               value = (in_dma == IDMA_IC_13) ? FS_DEST_ROT :  /*->PP_ROT */
+                   (in_dma == IDMA_ADC_SYS1_WR) ? FS_DEST_ADC1 :       /* ->ADC1 */
+                   (in_dma == IDMA_ADC_SYS2_WR) ? FS_DEST_ADC2 :       /* ->ADC2 */
+                   (in_dma == IDMA_SDC_BG) ? FS_DEST_SDC_BG :  /*->SDC_BG */
+                   (in_dma == IDMA_SDC_FG) ? FS_DEST_SDC_FG :  /*->SDC_FG */
+                   /* ->ADCDirect */
+                   0;
+               break;
+
+               /*PP_ROT-> */
+       case IDMA_IC_12:
+               pr_debug("Link PP_ROT->");
+               isProc = true;
+               mask = FS_PP_ROT_DEST_SEL_MASK;
+               offset = FS_PP_ROT_DEST_SEL_OFFSET;
+               value = (in_dma == IDMA_IC_5) ? FS_DEST_ROT :   /*->PP */
+                   (in_dma == IDMA_ADC_SYS1_WR) ? FS_DEST_ADC1 :       /* ->ADC1 */
+                   (in_dma == IDMA_ADC_SYS2_WR) ? FS_DEST_ADC2 :       /* ->ADC2 */
+                   (in_dma == IDMA_SDC_BG) ? FS_DEST_SDC_BG :  /*->SDC_BG */
+                   (in_dma == IDMA_SDC_FG) ? FS_DEST_SDC_FG :  /*->SDC_FG */
+                   0;
+               break;
+
+               /*PF-> */
+       case IDMA_PF_Y_OUT:
+       case IDMA_PF_U_OUT:
+       case IDMA_PF_V_OUT:
+               pr_debug("Link PF->");
+               isProc = true;
+               mask = FS_PF_DEST_SEL_MASK;
+               offset = FS_PF_DEST_SEL_OFFSET;
+               value = (in_dma == IDMA_IC_5) ? FS_PF_DEST_PP :
+                   (in_dma == IDMA_IC_13) ? FS_PF_DEST_ROT : 0;
+               break;
+
+               /* Invalid Chainings: ENC_ROT-> */
+       default:
+               pr_debug("Link Invalid->");
+               value = 0;
+               break;
+
+       }
+
+       if (value) {
+               if (isProc) {
+                       fs_proc_flow &= ~mask;
+                       fs_proc_flow |= (value << offset);
+               } else {
+                       fs_disp_flow &= ~mask;
+                       fs_disp_flow |= (value << offset);
+               }
+       } else {
+               dev_err(g_ipu_dev, "Invalid channel chaining %d -> %d\n",
+                       out_dma, in_dma);
+               return -EINVAL;
+       }
+
+       /* PROCESS THE INPUT DMA CH */
+       switch (in_dma) {
+               /* ->VF_ROT */
+       case IDMA_IC_11:
+               pr_debug("VF_ROT\n");
+               isProc = true;
+               mask = 0;
+               offset = 0;
+               value = (out_dma == IDMA_IC_1) ? FS_PRPVF_ROT_SRC_SEL : /*VF-> */
+                   0;
+               break;
+
+               /* ->ENC_ROT */
+       case IDMA_IC_10:
+               pr_debug("ENC_ROT\n");
+               isProc = true;
+               mask = 0;
+               offset = 0;
+               value = (out_dma == IDMA_IC_0) ? FS_PRPENC_ROT_SRC_SEL :        /*ENC-> */
+                   0;
+               break;
+
+               /* ->PP */
+       case IDMA_IC_5:
+               pr_debug("PP\n");
+               isProc = true;
+               mask = FS_PP_SRC_SEL_MASK;
+               offset = FS_PP_SRC_SEL_OFFSET;
+               value = (out_dma == IDMA_PF_Y_OUT) ? FS_PP_SRC_PF :     /*PF-> */
+                   (out_dma == IDMA_PF_U_OUT) ? FS_PP_SRC_PF : /*PF-> */
+                   (out_dma == IDMA_PF_V_OUT) ? FS_PP_SRC_PF : /*PF-> */
+                   (out_dma == IDMA_IC_12) ? FS_PP_SRC_ROT :   /*PP_ROT-> */
+                   0;
+               break;
+
+               /* ->PP_ROT */
+       case IDMA_IC_13:
+               pr_debug("PP_ROT\n");
+               isProc = true;
+               mask = FS_PP_ROT_SRC_SEL_MASK;
+               offset = FS_PP_ROT_SRC_SEL_OFFSET;
+               value = (out_dma == IDMA_PF_Y_OUT) ? FS_PP_SRC_PF :     /*PF-> */
+                   (out_dma == IDMA_PF_U_OUT) ? FS_PP_SRC_PF : /*PF-> */
+                   (out_dma == IDMA_PF_V_OUT) ? FS_PP_SRC_PF : /*PF-> */
+                   (out_dma == IDMA_IC_2) ? FS_ROT_SRC_PP :    /*PP-> */
+                   0;
+               break;
+
+               /* ->SDC_BG */
+       case IDMA_SDC_BG:
+               pr_debug("SDC_BG\n");
+               isProc = false;
+               mask = FS_SDC_BG_SRC_SEL_MASK;
+               offset = FS_SDC_BG_SRC_SEL_OFFSET;
+               value = (out_dma == IDMA_IC_9) ? FS_SRC_ROT_VF :        /*VF_ROT-> */
+                   (out_dma == IDMA_IC_12) ? FS_SRC_ROT_PP :   /*PP_ROT-> */
+                   (out_dma == IDMA_IC_1) ? FS_SRC_VF :        /*VF-> */
+                   (out_dma == IDMA_IC_2) ? FS_SRC_PP :        /*PP-> */
+                   0;
+               break;
+
+               /* ->SDC_FG */
+       case IDMA_SDC_FG:
+               pr_debug("SDC_FG\n");
+               isProc = false;
+               mask = FS_SDC_FG_SRC_SEL_MASK;
+               offset = FS_SDC_FG_SRC_SEL_OFFSET;
+               value = (out_dma == IDMA_IC_9) ? FS_SRC_ROT_VF :        /*VF_ROT-> */
+                   (out_dma == IDMA_IC_12) ? FS_SRC_ROT_PP :   /*PP_ROT-> */
+                   (out_dma == IDMA_IC_1) ? FS_SRC_VF :        /*VF-> */
+                   (out_dma == IDMA_IC_2) ? FS_SRC_PP :        /*PP-> */
+                   0;
+               break;
+
+               /* ->ADC1 */
+       case IDMA_ADC_SYS1_WR:
+               pr_debug("ADC_SYS1\n");
+               isProc = false;
+               mask = FS_ADC1_SRC_SEL_MASK;
+               offset = FS_ADC1_SRC_SEL_OFFSET;
+               value = (out_dma == IDMA_IC_9) ? FS_SRC_ROT_VF :        /*VF_ROT-> */
+                   (out_dma == IDMA_IC_12) ? FS_SRC_ROT_PP :   /*PP_ROT-> */
+                   (out_dma == IDMA_IC_1) ? FS_SRC_VF :        /*VF-> */
+                   (out_dma == IDMA_IC_2) ? FS_SRC_PP :        /*PP-> */
+                   0;
+               break;
+
+               /* ->ADC2 */
+       case IDMA_ADC_SYS2_WR:
+               pr_debug("ADC_SYS2\n");
+               isProc = false;
+               mask = FS_ADC2_SRC_SEL_MASK;
+               offset = FS_ADC2_SRC_SEL_OFFSET;
+               value = (out_dma == IDMA_IC_9) ? FS_SRC_ROT_VF :        /*VF_ROT-> */
+                   (out_dma == IDMA_IC_12) ? FS_SRC_ROT_PP :   /*PP_ROT-> */
+                   (out_dma == IDMA_IC_1) ? FS_SRC_VF :        /*VF-> */
+                   (out_dma == IDMA_IC_2) ? FS_SRC_PP :        /*PP-> */
+                   0;
+               break;
+
+               /*Invalid chains: */
+               /* ->ENC, ->VF, ->PF, ->VF_COMBINE, ->PP_COMBINE */
+       default:
+               pr_debug("Invalid\n");
+               value = 0;
+               break;
+
+       }
+
+       if (value) {
+               if (isProc) {
+                       fs_proc_flow &= ~mask;
+                       fs_proc_flow |= (value << offset);
+               } else {
+                       fs_disp_flow &= ~mask;
+                       fs_disp_flow |= (value << offset);
+               }
+       } else {
+               dev_err(g_ipu_dev, "Invalid channel chaining %d -> %d\n",
+                       out_dma, in_dma);
+               return -EINVAL;
+       }
+
+       __raw_writel(fs_proc_flow, IPU_FS_PROC_FLOW);
+       __raw_writel(fs_disp_flow, IPU_FS_DISP_FLOW);
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+       return 0;
+}
+
+/*!
+ * This function unlinks 2 channels and disables automatic frame
+ * synchronization.
+ *
+ * @param       src_ch          Input parameter for the logical channel ID of
+ *                              the source channel.
+ *
+ * @param       dest_ch         Input parameter for the logical channel ID of
+ *                              the destination channel.
+ *
+ * @return      This function returns 0 on success or negative error code on
+ *              fail.
+ */
+int32_t ipu_unlink_channels(ipu_channel_t src_ch, ipu_channel_t dest_ch)
+{
+       unsigned long lock_flags;
+       uint32_t out_dma;
+       uint32_t in_dma;
+       uint32_t fs_proc_flow;
+       uint32_t fs_disp_flow;
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       fs_proc_flow = __raw_readl(IPU_FS_PROC_FLOW);
+       fs_disp_flow = __raw_readl(IPU_FS_DISP_FLOW);
+
+       out_dma = (1UL << channel_2_dma(src_ch, IPU_OUTPUT_BUFFER));
+       in_dma = (1UL << channel_2_dma(dest_ch, IPU_INPUT_BUFFER));
+
+       /*clear the src_ch's output destination */
+       switch (out_dma) {
+               /*VF-> */
+       case IDMA_IC_1:
+               pr_debug("Unlink VF->");
+               fs_proc_flow &= ~FS_PRPVF_DEST_SEL_MASK;
+               break;
+
+               /*VF_ROT-> */
+       case IDMA_IC_9:
+               pr_debug("Unlink VF_Rot->");
+               fs_proc_flow &= ~FS_PRPVF_ROT_DEST_SEL_MASK;
+               break;
+
+               /*ENC-> */
+       case IDMA_IC_0:
+               pr_debug("Unlink ENC->");
+               fs_proc_flow &= ~FS_PRPENC_DEST_SEL;
+               break;
+
+               /*PP-> */
+       case IDMA_IC_2:
+               pr_debug("Unlink PP->");
+               fs_proc_flow &= ~FS_PP_DEST_SEL_MASK;
+               break;
+
+               /*PP_ROT-> */
+       case IDMA_IC_12:
+               pr_debug("Unlink PP_ROT->");
+               fs_proc_flow &= ~FS_PP_ROT_DEST_SEL_MASK;
+               break;
+
+               /*PF-> */
+       case IDMA_PF_Y_OUT:
+       case IDMA_PF_U_OUT:
+       case IDMA_PF_V_OUT:
+               pr_debug("Unlink PF->");
+               fs_proc_flow &= ~FS_PF_DEST_SEL_MASK;
+               break;
+
+       default:                /*ENC_ROT->     */
+               pr_debug("Unlink Invalid->");
+               break;
+       }
+
+       /*clear the dest_ch's input source */
+       switch (in_dma) {
+       /*->VF_ROT*/
+       case IDMA_IC_11:
+               pr_debug("VF_ROT\n");
+               fs_proc_flow &= ~FS_PRPVF_ROT_SRC_SEL;
+               break;
+
+       /*->Enc_ROT*/
+       case IDMA_IC_10:
+               pr_debug("ENC_ROT\n");
+               fs_proc_flow &= ~FS_PRPENC_ROT_SRC_SEL;
+               break;
+
+       /*->PP*/
+       case IDMA_IC_5:
+               pr_debug("PP\n");
+               fs_proc_flow &= ~FS_PP_SRC_SEL_MASK;
+               break;
+
+       /*->PP_ROT*/
+       case IDMA_IC_13:
+               pr_debug("PP_ROT\n");
+               fs_proc_flow &= ~FS_PP_ROT_SRC_SEL_MASK;
+               break;
+
+       /*->SDC_FG*/
+       case IDMA_SDC_FG:
+               pr_debug("SDC_FG\n");
+               fs_disp_flow &= ~FS_SDC_FG_SRC_SEL_MASK;
+               break;
+
+       /*->SDC_BG*/
+       case IDMA_SDC_BG:
+               pr_debug("SDC_BG\n");
+               fs_disp_flow &= ~FS_SDC_BG_SRC_SEL_MASK;
+               break;
+
+       /*->ADC1*/
+       case IDMA_ADC_SYS1_WR:
+               pr_debug("ADC_SYS1\n");
+               fs_disp_flow &= ~FS_ADC1_SRC_SEL_MASK;
+               break;
+
+       /*->ADC2*/
+       case IDMA_ADC_SYS2_WR:
+               pr_debug("ADC_SYS2\n");
+               fs_disp_flow &= ~FS_ADC2_SRC_SEL_MASK;
+               break;
+
+       default:                /*->VF, ->ENC, ->VF_COMBINE, ->PP_COMBINE, ->PF*/
+               pr_debug("Invalid\n");
+               break;
+       }
+
+       __raw_writel(fs_proc_flow, IPU_FS_PROC_FLOW);
+       __raw_writel(fs_disp_flow, IPU_FS_DISP_FLOW);
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+       return 0;
+}
+
+/*!
+ * This function check whether a logical channel was enabled.
+ *
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @return      This function returns 1 while request channel is enabled or
+ *              0 for not enabled.
+ */
+int32_t ipu_is_channel_busy(ipu_channel_t channel)
+{
+       uint32_t reg;
+       uint32_t in_dma;
+       uint32_t out_dma;
+
+       out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER);
+       in_dma = channel_2_dma(channel, IPU_INPUT_BUFFER);
+
+       reg = __raw_readl(IDMAC_CHA_EN);
+
+       if (reg & ((1UL << out_dma) | (1UL << in_dma)))
+               return 1;
+       return 0;
+}
+EXPORT_SYMBOL(ipu_is_channel_busy);
+
+/*!
+ * This function enables a logical channel.
+ *
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @return      This function returns 0 on success or negative error code on
+ *              fail.
+ */
+int32_t ipu_enable_channel(ipu_channel_t channel)
+{
+       uint32_t reg;
+       unsigned long lock_flags;
+       uint32_t in_dma;
+       uint32_t sec_dma;
+       uint32_t out_dma;
+       uint32_t chan_mask = 0;
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       reg = __raw_readl(IDMAC_CHA_EN);
+
+       /* Get input and output dma channels */
+       out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER);
+       if (out_dma != IDMA_CHAN_INVALID)
+               reg |= 1UL << out_dma;
+       in_dma = channel_2_dma(channel, IPU_INPUT_BUFFER);
+       if (in_dma != IDMA_CHAN_INVALID)
+               reg |= 1UL << in_dma;
+
+       /* Get secondary input dma channel */
+       if (g_sec_chan_en[IPU_CHAN_ID(channel)]) {
+               sec_dma = channel_2_dma(channel, IPU_SEC_INPUT_BUFFER);
+               if (sec_dma != IDMA_CHAN_INVALID) {
+                       reg |= 1UL << sec_dma;
+               }
+       }
+
+       __raw_writel(reg | chan_mask, IDMAC_CHA_EN);
+
+       if (IPU_CHAN_ID(channel) <= IPU_CHAN_ID(MEM_PP_ADC)) {
+               _ipu_ic_enable_task(channel);
+       } else if (channel == MEM_SDC_BG) {
+               dev_dbg(g_ipu_dev, "Initializing SDC BG\n");
+               _ipu_sdc_bg_init(NULL);
+       } else if (channel == MEM_SDC_FG) {
+               dev_dbg(g_ipu_dev, "Initializing SDC FG\n");
+               _ipu_sdc_fg_init(NULL);
+       }
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+       return 0;
+}
+
+/*!
+ * This function clear buffer ready for a logical channel.
+ *
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @param       type            Input parameter which buffer to clear.
+ *
+ * @param       bufNum          Input parameter for which buffer number clear
+ *                             ready state.
+ *
+ */
+void ipu_clear_buffer_ready(ipu_channel_t channel, ipu_buffer_t type,
+               uint32_t bufNum)
+{
+       /*TODO*/
+}
+EXPORT_SYMBOL(ipu_clear_buffer_ready);
+
+uint32_t ipu_get_cur_buffer_idx(ipu_channel_t channel, ipu_buffer_t type)
+{
+       uint32_t reg, dma_chan;
+
+       dma_chan = channel_2_dma(channel, type);
+
+       reg = __raw_readl(IPU_CHA_CUR_BUF);
+       if (reg & (1UL << dma_chan))
+               return 1;
+       else
+               return 0;
+}
+EXPORT_SYMBOL(ipu_get_cur_buffer_idx);
+
+/*!
+ * This function disables a logical channel.
+ *
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @param       wait_for_stop   Flag to set whether to wait for channel end
+ *                              of frame or return immediately.
+ *
+ * @return      This function returns 0 on success or negative error code on
+ *              fail.
+ */
+int32_t ipu_disable_channel(ipu_channel_t channel, bool wait_for_stop)
+{
+       uint32_t reg;
+       unsigned long lock_flags;
+       uint32_t sec_dma;
+       uint32_t in_dma;
+       uint32_t out_dma;
+       uint32_t chan_mask = 0;
+       uint32_t timeout;
+       uint32_t eof_intr;
+       uint32_t enabled;
+
+       /* Get input and output dma channels */
+       out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER);
+       if (out_dma != IDMA_CHAN_INVALID)
+               chan_mask = 1UL << out_dma;
+       in_dma = channel_2_dma(channel, IPU_INPUT_BUFFER);
+       if (in_dma != IDMA_CHAN_INVALID)
+               chan_mask |= 1UL << in_dma;
+       sec_dma = channel_2_dma(channel, IPU_SEC_INPUT_BUFFER);
+       if (sec_dma != IDMA_CHAN_INVALID)
+               chan_mask |= 1UL << sec_dma;
+
+       if (wait_for_stop && channel != MEM_SDC_FG && channel != MEM_SDC_BG) {
+               timeout = 40;
+               while ((__raw_readl(IDMAC_CHA_BUSY) & chan_mask) ||
+                      (_ipu_channel_status(channel) == TASK_STAT_ACTIVE)) {
+                       timeout--;
+                       msleep(10);
+                       if (timeout == 0) {
+                               printk
+                                   (KERN_INFO
+                                    "MXC IPU: Warning - timeout waiting for channel to stop,\n"
+                                    "\tbuf0_rdy = 0x%08X, buf1_rdy = 0x%08X\n"
+                                    "\tbusy = 0x%08X, tstat = 0x%08X\n\tmask = 0x%08X\n",
+                                    __raw_readl(IPU_CHA_BUF0_RDY),
+                                    __raw_readl(IPU_CHA_BUF1_RDY),
+                                    __raw_readl(IDMAC_CHA_BUSY),
+                                    __raw_readl(IPU_TASKS_STAT), chan_mask);
+                               break;
+                       }
+               }
+               dev_dbg(g_ipu_dev, "timeout = %d * 10ms\n", 40 - timeout);
+       }
+       /* SDC BG and FG must be disabled before DMA is disabled */
+       if ((channel == MEM_SDC_BG) || (channel == MEM_SDC_FG)) {
+
+               if (channel == MEM_SDC_BG)
+                       eof_intr = IPU_IRQ_SDC_BG_EOF;
+               else
+                       eof_intr = IPU_IRQ_SDC_FG_EOF;
+
+               /* Wait for any buffer flips to finsh */
+               timeout = 4;
+               while (timeout &&
+                      ((__raw_readl(IPU_CHA_BUF0_RDY) & chan_mask) ||
+                       (__raw_readl(IPU_CHA_BUF1_RDY) & chan_mask))) {
+                       msleep(10);
+                       timeout--;
+               }
+
+               spin_lock_irqsave(&ipu_lock, lock_flags);
+               ipu_clear_irq(eof_intr);
+               if (channel == MEM_SDC_BG)
+                       enabled = _ipu_sdc_bg_uninit();
+               else
+                       enabled = _ipu_sdc_fg_uninit();
+               spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+               if (enabled && wait_for_stop) {
+                       timeout = 5;
+               } else {
+                       timeout = 0;
+               }
+               while (timeout && !ipu_get_irq_status(eof_intr)) {
+                       msleep(5);
+                       timeout--;
+               }
+       }
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       /* Disable IC task */
+       if (IPU_CHAN_ID(channel) <= IPU_CHAN_ID(MEM_PP_ADC)) {
+               _ipu_ic_disable_task(channel);
+       }
+
+       /* Disable DMA channel(s) */
+       reg = __raw_readl(IDMAC_CHA_EN);
+       __raw_writel(reg & ~chan_mask, IDMAC_CHA_EN);
+
+       /* Clear DMA related interrupts */
+       __raw_writel(chan_mask, IPU_INT_STAT_1);
+       __raw_writel(chan_mask, IPU_INT_STAT_2);
+       __raw_writel(chan_mask, IPU_INT_STAT_4);
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+       return 0;
+}
+
+int32_t ipu_enable_csi(uint32_t csi)
+{
+       return 0;
+}
+
+
+int32_t ipu_disable_csi(uint32_t csi)
+{
+       return 0;
+}
+
+static
+irqreturn_t ipu_irq_handler(int irq, void *desc)
+{
+       uint32_t line_base = 0;
+       uint32_t line;
+       irqreturn_t result = IRQ_NONE;
+       uint32_t int_stat;
+
+       int_stat = __raw_readl(IPU_INT_STAT_1);
+       int_stat &= __raw_readl(IPU_INT_CTRL_1);
+       __raw_writel(int_stat, IPU_INT_STAT_1);
+       while ((line = ffs(int_stat)) != 0) {
+               int_stat &= ~(1UL << (line - 1));
+               line += line_base - 1;
+               result |=
+                   ipu_irq_list[line].handler(line, ipu_irq_list[line].dev_id);
+       }
+
+       line_base = 32;
+       int_stat = __raw_readl(IPU_INT_STAT_2);
+       int_stat &= __raw_readl(IPU_INT_CTRL_2);
+       __raw_writel(int_stat, IPU_INT_STAT_2);
+       while ((line = ffs(int_stat)) != 0) {
+               int_stat &= ~(1UL << (line - 1));
+               line += line_base - 1;
+               result |=
+                   ipu_irq_list[line].handler(line, ipu_irq_list[line].dev_id);
+       }
+
+       line_base = 64;
+       int_stat = __raw_readl(IPU_INT_STAT_3);
+       int_stat &= __raw_readl(IPU_INT_CTRL_3);
+       __raw_writel(int_stat, IPU_INT_STAT_3);
+       while ((line = ffs(int_stat)) != 0) {
+               int_stat &= ~(1UL << (line - 1));
+               line += line_base - 1;
+               result |=
+                   ipu_irq_list[line].handler(line, ipu_irq_list[line].dev_id);
+       }
+
+       line_base = 96;
+       int_stat = __raw_readl(IPU_INT_STAT_4);
+       int_stat &= __raw_readl(IPU_INT_CTRL_4);
+       __raw_writel(int_stat, IPU_INT_STAT_4);
+       while ((line = ffs(int_stat)) != 0) {
+               int_stat &= ~(1UL << (line - 1));
+               line += line_base - 1;
+               result |=
+                   ipu_irq_list[line].handler(line, ipu_irq_list[line].dev_id);
+       }
+
+       line_base = 128;
+       int_stat = __raw_readl(IPU_INT_STAT_5);
+       int_stat &= __raw_readl(IPU_INT_CTRL_5);
+       __raw_writel(int_stat, IPU_INT_STAT_5);
+       while ((line = ffs(int_stat)) != 0) {
+               int_stat &= ~(1UL << (line - 1));
+               line += line_base - 1;
+               result |=
+                   ipu_irq_list[line].handler(line, ipu_irq_list[line].dev_id);
+       }
+
+       return result;
+}
+
+/*!
+ * This function enables the interrupt for the specified interrupt line.
+ * The interrupt lines are defined in \b ipu_irq_line enum.
+ *
+ * @param       irq             Interrupt line to enable interrupt for.
+ *
+ */
+void ipu_enable_irq(uint32_t irq)
+{
+       uint32_t reg;
+       unsigned long lock_flags;
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       reg = __raw_readl(IPUIRQ_2_CTRLREG(irq));
+       reg |= IPUIRQ_2_MASK(irq);
+       __raw_writel(reg, IPUIRQ_2_CTRLREG(irq));
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+}
+
+/*!
+ * This function disables the interrupt for the specified interrupt line.
+ * The interrupt lines are defined in \b ipu_irq_line enum.
+ *
+ * @param       irq             Interrupt line to disable interrupt for.
+ *
+ */
+void ipu_disable_irq(uint32_t irq)
+{
+       uint32_t reg;
+       unsigned long lock_flags;
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       reg = __raw_readl(IPUIRQ_2_CTRLREG(irq));
+       reg &= ~IPUIRQ_2_MASK(irq);
+       __raw_writel(reg, IPUIRQ_2_CTRLREG(irq));
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+}
+
+/*!
+ * This function clears the interrupt for the specified interrupt line.
+ * The interrupt lines are defined in \b ipu_irq_line enum.
+ *
+ * @param       irq             Interrupt line to clear interrupt for.
+ *
+ */
+void ipu_clear_irq(uint32_t irq)
+{
+       __raw_writel(IPUIRQ_2_MASK(irq), IPUIRQ_2_STATREG(irq));
+}
+
+/*!
+ * This function returns the current interrupt status for the specified interrupt
+ * line. The interrupt lines are defined in \b ipu_irq_line enum.
+ *
+ * @param       irq             Interrupt line to get status for.
+ *
+ * @return      Returns true if the interrupt is pending/asserted or false if
+ *              the interrupt is not pending.
+ */
+bool ipu_get_irq_status(uint32_t irq)
+{
+       uint32_t reg = __raw_readl(IPUIRQ_2_STATREG(irq));
+
+       if (reg & IPUIRQ_2_MASK(irq))
+               return true;
+       else
+               return false;
+}
+
+/*!
+ * This function registers an interrupt handler function for the specified
+ * interrupt line. The interrupt lines are defined in \b ipu_irq_line enum.
+ *
+ * @param       irq             Interrupt line to get status for.
+ *
+ * @param       handler         Input parameter for address of the handler
+ *                              function.
+ *
+ * @param       irq_flags       Flags for interrupt mode. Currently not used.
+ *
+ * @param       devname         Input parameter for string name of driver
+ *                              registering the handler.
+ *
+ * @param       dev_id          Input parameter for pointer of data to be passed
+ *                              to the handler.
+ *
+ * @return      This function returns 0 on success or negative error code on
+ *              fail.
+ */
+int ipu_request_irq(uint32_t irq,
+                   irqreturn_t(*handler) (int, void *),
+                   uint32_t irq_flags, const char *devname, void *dev_id)
+{
+       unsigned long lock_flags;
+
+       BUG_ON(irq >= IPU_IRQ_COUNT);
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       if (ipu_irq_list[irq].handler != NULL) {
+               dev_err(g_ipu_dev,
+                       "ipu_request_irq - handler already installed on irq %d\n",
+                       irq);
+               spin_unlock_irqrestore(&ipu_lock, lock_flags);
+               return -EINVAL;
+       }
+
+       ipu_irq_list[irq].handler = handler;
+       ipu_irq_list[irq].flags = irq_flags;
+       ipu_irq_list[irq].dev_id = dev_id;
+       ipu_irq_list[irq].name = devname;
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+       ipu_enable_irq(irq);    /* enable the interrupt */
+
+       return 0;
+}
+
+/*!
+ * This function unregisters an interrupt handler for the specified interrupt
+ * line. The interrupt lines are defined in \b ipu_irq_line enum.
+ *
+ * @param       irq             Interrupt line to get status for.
+ *
+ * @param       dev_id          Input parameter for pointer of data to be passed
+ *                              to the handler. This must match value passed to
+ *                              ipu_request_irq().
+ *
+ */
+void ipu_free_irq(uint32_t irq, void *dev_id)
+{
+       ipu_disable_irq(irq);   /* disable the interrupt */
+
+       if (ipu_irq_list[irq].dev_id == dev_id) {
+               ipu_irq_list[irq].handler = NULL;
+       }
+}
+
+/*!
+ * This function sets the post-filter pause row for h.264 mode.
+ *
+ * @param       pause_row       The last row to process before pausing.
+ *
+ * @return      This function returns 0 on success or negative error code on
+ *              fail.
+ *
+ */
+int32_t ipu_pf_set_pause_row(uint32_t pause_row)
+{
+       int32_t retval = 0;
+       uint32_t timeout = 5;
+       unsigned long lock_flags;
+       uint32_t reg;
+
+       reg = __raw_readl(IPU_TASKS_STAT);
+       while ((reg & TSTAT_PF_MASK) && ((reg & TSTAT_PF_H264_PAUSE) == 0)) {
+               timeout--;
+               msleep(5);
+               if (timeout == 0) {
+                       dev_err(g_ipu_dev, "PF Timeout - tstat = 0x%08X\n",
+                               __raw_readl(IPU_TASKS_STAT));
+                       retval = -ETIMEDOUT;
+                       goto err0;
+               }
+       }
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       reg = __raw_readl(PF_CONF);
+
+       /* Set the pause row */
+       if (pause_row) {
+               reg &= ~PF_CONF_PAUSE_ROW_MASK;
+               reg |= PF_CONF_PAUSE_EN | pause_row << PF_CONF_PAUSE_ROW_SHIFT;
+       } else {
+               reg &= ~(PF_CONF_PAUSE_EN | PF_CONF_PAUSE_ROW_MASK);
+       }
+       __raw_writel(reg, PF_CONF);
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+      err0:
+       return retval;
+}
+
+/* Private functions */
+void _ipu_write_param_mem(uint32_t addr, uint32_t *data, uint32_t numWords)
+{
+       for (; numWords > 0; numWords--) {
+               dev_dbg(g_ipu_dev,
+                       "write param mem - addr = 0x%08X, data = 0x%08X\n",
+                       addr, *data);
+               __raw_writel(addr, IPU_IMA_ADDR);
+               __raw_writel(*data++, IPU_IMA_DATA);
+               addr++;
+               if ((addr & 0x7) == 5) {
+                       addr &= ~0x7;   /* set to word 0 */
+                       addr += 8;      /* increment to next row */
+               }
+       }
+}
+
+static void _ipu_pf_init(ipu_channel_params_t *params)
+{
+       uint32_t reg;
+
+       /*Setup the type of filtering required */
+       switch (params->mem_pf_mem.operation) {
+       case PF_MPEG4_DEBLOCK:
+       case PF_MPEG4_DERING:
+       case PF_MPEG4_DEBLOCK_DERING:
+               g_sec_chan_en[IPU_CHAN_ID(MEM_PF_Y_MEM)] = true;
+               g_sec_chan_en[IPU_CHAN_ID(MEM_PF_U_MEM)] = false;
+               break;
+       case PF_H264_DEBLOCK:
+               g_sec_chan_en[IPU_CHAN_ID(MEM_PF_Y_MEM)] = true;
+               g_sec_chan_en[IPU_CHAN_ID(MEM_PF_U_MEM)] = true;
+               break;
+       default:
+               g_sec_chan_en[IPU_CHAN_ID(MEM_PF_Y_MEM)] = false;
+               g_sec_chan_en[IPU_CHAN_ID(MEM_PF_U_MEM)] = false;
+               return;
+               break;
+       }
+       reg = params->mem_pf_mem.operation;
+       __raw_writel(reg, PF_CONF);
+}
+
+static void _ipu_pf_uninit(void)
+{
+       __raw_writel(0x0L, PF_CONF);
+       g_sec_chan_en[IPU_CHAN_ID(MEM_PF_Y_MEM)] = false;
+       g_sec_chan_en[IPU_CHAN_ID(MEM_PF_U_MEM)] = false;
+}
+
+uint32_t _ipu_channel_status(ipu_channel_t channel)
+{
+       uint32_t stat = 0;
+       uint32_t task_stat_reg = __raw_readl(IPU_TASKS_STAT);
+
+       switch (channel) {
+       case CSI_MEM:
+               stat =
+                   (task_stat_reg & TSTAT_CSI2MEM_MASK) >>
+                   TSTAT_CSI2MEM_OFFSET;
+               break;
+       case CSI_PRP_VF_ADC:
+       case CSI_PRP_VF_MEM:
+       case MEM_PRP_VF_ADC:
+       case MEM_PRP_VF_MEM:
+               stat = (task_stat_reg & TSTAT_VF_MASK) >> TSTAT_VF_OFFSET;
+               break;
+       case MEM_ROT_VF_MEM:
+               stat =
+                   (task_stat_reg & TSTAT_VF_ROT_MASK) >> TSTAT_VF_ROT_OFFSET;
+               break;
+       case CSI_PRP_ENC_MEM:
+       case MEM_PRP_ENC_MEM:
+               stat = (task_stat_reg & TSTAT_ENC_MASK) >> TSTAT_ENC_OFFSET;
+               break;
+       case MEM_ROT_ENC_MEM:
+               stat =
+                   (task_stat_reg & TSTAT_ENC_ROT_MASK) >>
+                   TSTAT_ENC_ROT_OFFSET;
+               break;
+       case MEM_PP_ADC:
+       case MEM_PP_MEM:
+               stat = (task_stat_reg & TSTAT_PP_MASK) >> TSTAT_PP_OFFSET;
+               break;
+       case MEM_ROT_PP_MEM:
+               stat =
+                   (task_stat_reg & TSTAT_PP_ROT_MASK) >> TSTAT_PP_ROT_OFFSET;
+               break;
+
+       case MEM_PF_Y_MEM:
+       case MEM_PF_U_MEM:
+       case MEM_PF_V_MEM:
+               stat = (task_stat_reg & TSTAT_PF_MASK) >> TSTAT_PF_OFFSET;
+               break;
+       case MEM_SDC_BG:
+               break;
+       case MEM_SDC_FG:
+               break;
+       case ADC_SYS1:
+               stat =
+                   (task_stat_reg & TSTAT_ADCSYS1_MASK) >>
+                   TSTAT_ADCSYS1_OFFSET;
+               break;
+       case ADC_SYS2:
+               stat =
+                   (task_stat_reg & TSTAT_ADCSYS2_MASK) >>
+                   TSTAT_ADCSYS2_OFFSET;
+               break;
+       case MEM_SDC_MASK:
+       default:
+               stat = TASK_STAT_IDLE;
+               break;
+       }
+       return stat;
+}
+
+uint32_t bytes_per_pixel(uint32_t fmt)
+{
+       switch (fmt) {
+       case IPU_PIX_FMT_GENERIC:       /*generic data */
+       case IPU_PIX_FMT_RGB332:
+       case IPU_PIX_FMT_YUV420P:
+       case IPU_PIX_FMT_YUV422P:
+               return 1;
+               break;
+       case IPU_PIX_FMT_RGB565:
+       case IPU_PIX_FMT_YUYV:
+       case IPU_PIX_FMT_UYVY:
+               return 2;
+               break;
+       case IPU_PIX_FMT_BGR24:
+       case IPU_PIX_FMT_RGB24:
+               return 3;
+               break;
+       case IPU_PIX_FMT_GENERIC_32:    /*generic data */
+       case IPU_PIX_FMT_BGR32:
+       case IPU_PIX_FMT_RGB32:
+       case IPU_PIX_FMT_ABGR32:
+               return 4;
+               break;
+       default:
+               return 1;
+               break;
+       }
+       return 0;
+}
+
+void ipu_set_csc_coefficients(ipu_channel_t channel, int32_t param[][3])
+{
+       /* TODO */
+}
+EXPORT_SYMBOL(ipu_set_csc_coefficients);
+
+ipu_color_space_t format_to_colorspace(uint32_t fmt)
+{
+       switch (fmt) {
+       case IPU_PIX_FMT_RGB565:
+       case IPU_PIX_FMT_BGR24:
+       case IPU_PIX_FMT_RGB24:
+       case IPU_PIX_FMT_BGR32:
+       case IPU_PIX_FMT_RGB32:
+               return RGB;
+               break;
+
+       default:
+               return YCbCr;
+               break;
+       }
+       return RGB;
+
+}
+
+static u32 saved_disp3_time_conf;
+static bool in_lpdr_mode;
+static struct clk *default_ipu_parent_clk;
+
+int ipu_lowpwr_display_enable(void)
+{
+       unsigned long rate, div;
+       struct clk *parent_clk = g_ipu_clk;
+
+       if (in_lpdr_mode || IS_ERR(dfm_clk)) {
+               return -EINVAL;
+       }
+
+       if (g_channel_init_mask != (1L << IPU_CHAN_ID(MEM_SDC_BG))) {
+               dev_err(g_ipu_dev, "LPDR mode requires only SDC BG active.\n");
+               return -EINVAL;
+       }
+
+       default_ipu_parent_clk = clk_get_parent(g_ipu_clk);
+       in_lpdr_mode = true;
+
+       /* Calculate current pixel clock rate */
+       rate = clk_get_rate(g_ipu_clk) * 16;
+       saved_disp3_time_conf = __raw_readl(DI_DISP3_TIME_CONF);
+       div = saved_disp3_time_conf & 0xFFF;
+       rate /= div;
+       rate *= 4;              /* min hsp clk is 4x pixel clk */
+
+       /* Initialize DFM clock */
+       rate = clk_round_rate(dfm_clk, rate);
+       clk_set_rate(dfm_clk, rate);
+       clk_enable(dfm_clk);
+
+       /* Wait for next VSYNC */
+       __raw_writel(IPUIRQ_2_MASK(IPU_IRQ_SDC_DISP3_VSYNC),
+                    IPUIRQ_2_STATREG(IPU_IRQ_SDC_DISP3_VSYNC));
+       while ((__raw_readl(IPUIRQ_2_STATREG(IPU_IRQ_SDC_DISP3_VSYNC)) &
+               IPUIRQ_2_MASK(IPU_IRQ_SDC_DISP3_VSYNC)) == 0)
+               msleep_interruptible(1);
+
+       /* Set display clock divider to divide by 4 */
+       __raw_writel(((0x8) << 22) | 0x40, DI_DISP3_TIME_CONF);
+
+       clk_set_parent(parent_clk, dfm_clk);
+
+       return 0;
+}
+
+int ipu_lowpwr_display_disable(void)
+{
+       struct clk *parent_clk = g_ipu_clk;
+
+       if (!in_lpdr_mode || IS_ERR(dfm_clk)) {
+               return -EINVAL;
+       }
+
+       if (g_channel_init_mask != (1L << IPU_CHAN_ID(MEM_SDC_BG))) {
+               dev_err(g_ipu_dev, "LPDR mode requires only SDC BG active.\n");
+               return -EINVAL;
+       }
+
+       in_lpdr_mode = false;
+
+       /* Wait for next VSYNC */
+       __raw_writel(IPUIRQ_2_MASK(IPU_IRQ_SDC_DISP3_VSYNC),
+                    IPUIRQ_2_STATREG(IPU_IRQ_SDC_DISP3_VSYNC));
+       while ((__raw_readl(IPUIRQ_2_STATREG(IPU_IRQ_SDC_DISP3_VSYNC)) &
+               IPUIRQ_2_MASK(IPU_IRQ_SDC_DISP3_VSYNC)) == 0)
+               msleep_interruptible(1);
+
+       __raw_writel(saved_disp3_time_conf, DI_DISP3_TIME_CONF);
+       clk_set_parent(parent_clk, default_ipu_parent_clk);
+       clk_disable(dfm_clk);
+
+       return 0;
+}
+
+static int ipu_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       if (cpu_is_mx31() || cpu_is_mx32()) {
+               /* work-around for i.Mx31 SR mode after camera related test */
+               if (g_csi_used) {
+                       g_ipu_config = __raw_readl(IPU_CONF);
+                       clk_enable(g_ipu_csi_clk);
+                       __raw_writel(0x51, IPU_CONF);
+                       g_channel_init_mask_backup = g_channel_init_mask;
+                       g_channel_init_mask |= 2;
+               }
+       } else if (cpu_is_mx35()) {
+               g_ipu_config = __raw_readl(IPU_CONF);
+               /* Disable the clock of display otherwise the backlight cannot
+                * be close after camera/tvin related test */
+               __raw_writel(g_ipu_config & 0xfbf, IPU_CONF);
+       }
+
+       return 0;
+}
+
+static int ipu_resume(struct platform_device *pdev)
+{
+       if (cpu_is_mx31() || cpu_is_mx32()) {
+               /* work-around for i.Mx31 SR mode after camera related test */
+               if (g_csi_used) {
+                       __raw_writel(g_ipu_config, IPU_CONF);
+                       clk_disable(g_ipu_csi_clk);
+                       g_channel_init_mask = g_channel_init_mask_backup;
+               }
+       } else if (cpu_is_mx35()) {
+               __raw_writel(g_ipu_config, IPU_CONF);
+    }
+
+       return 0;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxcipu_driver = {
+       .driver = {
+                  .name = "mxc_ipu",
+                  },
+       .probe = ipu_probe,
+       .suspend = ipu_suspend,
+       .resume = ipu_resume,
+};
+
+int32_t __init ipu_gen_init(void)
+{
+       int32_t ret;
+
+       ret = platform_driver_register(&mxcipu_driver);
+       return 0;
+}
+
+subsys_initcall(ipu_gen_init);
+
+static void __exit ipu_gen_uninit(void)
+{
+       if (g_ipu_irq[0])
+               free_irq(g_ipu_irq[0], 0);
+       if (g_ipu_irq[1])
+               free_irq(g_ipu_irq[1], 0);
+
+       platform_driver_unregister(&mxcipu_driver);
+}
+
+module_exit(ipu_gen_uninit);
+
+EXPORT_SYMBOL(ipu_init_channel);
+EXPORT_SYMBOL(ipu_uninit_channel);
+EXPORT_SYMBOL(ipu_init_channel_buffer);
+EXPORT_SYMBOL(ipu_unlink_channels);
+EXPORT_SYMBOL(ipu_update_channel_buffer);
+EXPORT_SYMBOL(ipu_select_buffer);
+EXPORT_SYMBOL(ipu_link_channels);
+EXPORT_SYMBOL(ipu_enable_channel);
+EXPORT_SYMBOL(ipu_disable_channel);
+EXPORT_SYMBOL(ipu_enable_csi);
+EXPORT_SYMBOL(ipu_disable_csi);
+EXPORT_SYMBOL(ipu_enable_irq);
+EXPORT_SYMBOL(ipu_disable_irq);
+EXPORT_SYMBOL(ipu_clear_irq);
+EXPORT_SYMBOL(ipu_get_irq_status);
+EXPORT_SYMBOL(ipu_request_irq);
+EXPORT_SYMBOL(ipu_free_irq);
+EXPORT_SYMBOL(ipu_pf_set_pause_row);
+EXPORT_SYMBOL(bytes_per_pixel);
diff --git a/drivers/mxc/ipu/ipu_csi.c b/drivers/mxc/ipu/ipu_csi.c
new file mode 100644 (file)
index 0000000..ec09c67
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2005-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 ipu_csi.c
+ *
+ * @brief IPU CMOS Sensor interface functions
+ *
+ * @ingroup IPU
+ */
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/ipu.h>
+
+#include "ipu_prv.h"
+#include "ipu_regs.h"
+
+static bool gipu_csi_get_mclk_flag;
+static int csi_mclk_flag;
+
+extern void gpio_sensor_suspend(bool flag);
+
+/*!
+ * ipu_csi_init_interface
+ *    Sets initial values for the CSI registers.
+ *    The width and height of the sensor and the actual frame size will be
+ *    set to the same values.
+ * @param       width        Sensor width
+ * @param       height       Sensor height
+ * @param       pixel_fmt    pixel format
+ * @param       sig          ipu_csi_signal_cfg_t structure
+ *
+ * @return      0 for success, -EINVAL for error
+ */
+int32_t
+ipu_csi_init_interface(uint16_t width, uint16_t height, uint32_t pixel_fmt,
+                      ipu_csi_signal_cfg_t sig)
+{
+       uint32_t data = 0;
+
+       /* Set SENS_DATA_FORMAT bits (8 and 9)
+          RGB or YUV444 is 0 which is current value in data so not set explicitly
+          This is also the default value if attempts are made to set it to
+          something invalid. */
+       switch (pixel_fmt) {
+       case IPU_PIX_FMT_UYVY:
+               data = CSI_SENS_CONF_DATA_FMT_YUV422;
+               break;
+       case IPU_PIX_FMT_RGB24:
+       case IPU_PIX_FMT_BGR24:
+               data = CSI_SENS_CONF_DATA_FMT_RGB_YUV444;
+               break;
+       case IPU_PIX_FMT_GENERIC:
+               data = CSI_SENS_CONF_DATA_FMT_BAYER;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* Set the CSI_SENS_CONF register remaining fields */
+       data |= sig.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT |
+           sig.data_pol << CSI_SENS_CONF_DATA_POL_SHIFT |
+           sig.Vsync_pol << CSI_SENS_CONF_VSYNC_POL_SHIFT |
+           sig.Hsync_pol << CSI_SENS_CONF_HSYNC_POL_SHIFT |
+           sig.pixclk_pol << CSI_SENS_CONF_PIX_CLK_POL_SHIFT |
+           sig.ext_vsync << CSI_SENS_CONF_EXT_VSYNC_SHIFT |
+           sig.clk_mode << CSI_SENS_CONF_SENS_PRTCL_SHIFT |
+           sig.sens_clksrc << CSI_SENS_CONF_SENS_CLKSRC_SHIFT;
+
+       __raw_writel(data, CSI_SENS_CONF);
+
+       /* Setup frame size     */
+       __raw_writel(width | height << 16, CSI_SENS_FRM_SIZE);
+
+       __raw_writel(width << 16, CSI_FLASH_STROBE_1);
+       __raw_writel(height << 16 | 0x22, CSI_FLASH_STROBE_2);
+
+       /* Set CCIR registers */
+       if (sig.clk_mode == IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE) {
+               __raw_writel(0x40030, CSI_CCIR_CODE_1);
+               __raw_writel(0xFF0000, CSI_CCIR_CODE_3);
+       } else if (sig.clk_mode == IPU_CSI_CLK_MODE_CCIR656_INTERLACED) {
+               __raw_writel(0xD07DF, CSI_CCIR_CODE_1);
+               __raw_writel(0x40596, CSI_CCIR_CODE_2);
+               __raw_writel(0xFF0000, CSI_CCIR_CODE_3);
+       }
+
+       dev_dbg(g_ipu_dev, "CSI_SENS_CONF = 0x%08X\n",
+               __raw_readl(CSI_SENS_CONF));
+       dev_dbg(g_ipu_dev, "CSI_ACT_FRM_SIZE = 0x%08X\n",
+               __raw_readl(CSI_ACT_FRM_SIZE));
+
+       return 0;
+}
+
+/*!
+ * ipu_csi_flash_strobe
+ *
+ * @param       flag         true to turn on flash strobe
+ *
+ * @return      0 for success
+ */
+void ipu_csi_flash_strobe(bool flag)
+{
+       if (flag == true) {
+               __raw_writel(__raw_readl(CSI_FLASH_STROBE_2) | 0x1,
+                            CSI_FLASH_STROBE_2);
+       }
+}
+
+/*!
+ * ipu_csi_enable_mclk
+ *
+ * @param       src         enum define which source to control the clk
+ *                          CSI_MCLK_VF CSI_MCLK_ENC CSI_MCLK_RAW CSI_MCLK_I2C
+ * @param       flag        true to enable mclk, false to disable mclk
+ * @param       wait        true to wait 100ms make clock stable, false not wait
+ *
+ * @return      0 for success
+ */
+int32_t ipu_csi_enable_mclk(int src, bool flag, bool wait)
+{
+       if (flag == true) {
+               csi_mclk_flag |= src;
+       } else {
+               csi_mclk_flag &= ~src;
+       }
+
+       if (gipu_csi_get_mclk_flag == flag)
+               return 0;
+
+       if (flag == true) {
+               clk_enable(g_ipu_csi_clk);
+               if (wait == true)
+                       msleep(10);
+               /*printk("enable csi clock from source %d\n", src);     */
+               gipu_csi_get_mclk_flag = true;
+       } else if (csi_mclk_flag == 0) {
+               clk_disable(g_ipu_csi_clk);
+               /*printk("disable csi clock from source %d\n", src); */
+               gipu_csi_get_mclk_flag = flag;
+       }
+
+       return 0;
+}
+
+/*!
+ * ipu_csi_read_mclk_flag
+ *
+ * @return  csi_mclk_flag
+ */
+int ipu_csi_read_mclk_flag(void)
+{
+       return csi_mclk_flag;
+}
+
+/*!
+ * ipu_csi_get_window_size
+ *
+ * @param       width        pointer to window width
+ * @param       height       pointer to window height
+ * @param       dummy        dummy for IPUv1 to keep the same interface with IPUv3
+ *
+ */
+void ipu_csi_get_window_size(uint32_t *width, uint32_t *height,
+               uint32_t dummy)
+{
+       uint32_t reg;
+
+       reg = __raw_readl(CSI_ACT_FRM_SIZE);
+       *width = (reg & 0xFFFF) + 1;
+       *height = (reg >> 16 & 0xFFFF) + 1;
+}
+
+/*!
+ * ipu_csi_set_window_size
+ *
+ * @param       width        window width
+ * @param       height       window height
+ * @param       dummy        dummy for IPUv1 to keep the same interface with IPUv3
+ *
+ */
+void ipu_csi_set_window_size(uint32_t width, uint32_t height, uint32_t dummy)
+{
+       __raw_writel((width - 1) | (height - 1) << 16, CSI_ACT_FRM_SIZE);
+}
+
+/*!
+ * ipu_csi_set_window_pos
+ *
+ * @param       left        uint32 window x start
+ * @param       top         uint32 window y start
+ * @param       dummy       dummy for IPUv1 to keep the same interface with IPUv3
+ *
+ */
+void ipu_csi_set_window_pos(uint32_t left, uint32_t top, uint32_t dummy)
+{
+       uint32_t temp = __raw_readl(CSI_OUT_FRM_CTRL);
+       temp &= 0xffff0000;
+       temp = top | (left << 8) | temp;
+       __raw_writel(temp, CSI_OUT_FRM_CTRL);
+}
+
+/*!
+ * ipu_csi_get_sensor_protocol
+ *
+ * @param      csi         csi 0 or csi 1
+ *
+ * @return     Returns sensor protocol
+ */
+int32_t ipu_csi_get_sensor_protocol(uint32_t csi)
+{
+       /* TODO */
+}
+EXPORT_SYMBOL(ipu_csi_get_sensor_protocol);
+
+/* Exported symbols for modules. */
+EXPORT_SYMBOL(ipu_csi_set_window_pos);
+EXPORT_SYMBOL(ipu_csi_set_window_size);
+EXPORT_SYMBOL(ipu_csi_get_window_size);
+EXPORT_SYMBOL(ipu_csi_read_mclk_flag);
+EXPORT_SYMBOL(ipu_csi_enable_mclk);
+EXPORT_SYMBOL(ipu_csi_flash_strobe);
+EXPORT_SYMBOL(ipu_csi_init_interface);
diff --git a/drivers/mxc/ipu/ipu_device.c b/drivers/mxc/ipu/ipu_device.c
new file mode 100644 (file)
index 0000000..bd8d9e3
--- /dev/null
@@ -0,0 +1,696 @@
+/*
+ * Copyright 2005-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 ipu_device.c
+ *
+ * @brief This file contains the IPU driver device interface and fops functions.
+ *
+ * @ingroup IPU
+ */
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/ipu.h>
+
+#include "ipu_prv.h"
+#include "ipu_regs.h"
+#include "ipu_param_mem.h"
+
+/* Strucutures and variables for exporting MXC IPU as device*/
+
+#define MAX_Q_SIZE 10
+
+static int mxc_ipu_major;
+static struct class *mxc_ipu_class;
+
+DEFINE_SPINLOCK(queue_lock);
+static DECLARE_MUTEX(user_mutex);
+
+static wait_queue_head_t waitq;
+static int pending_events;
+int read_ptr;
+int write_ptr;
+
+ipu_event_info events[MAX_Q_SIZE];
+
+int register_ipu_device(void);
+
+/* Static functions */
+
+int get_events(ipu_event_info *p)
+{
+       unsigned long flags;
+       int ret = 0, i, cnt, found = 0;
+       spin_lock_irqsave(&queue_lock, flags);
+       if (pending_events != 0) {
+               if (write_ptr > read_ptr)
+                       cnt = write_ptr - read_ptr;
+               else
+                       cnt = MAX_Q_SIZE - read_ptr + write_ptr;
+               for (i = 0; i < cnt; i++) {
+                       if (p->irq == events[read_ptr].irq) {
+                               *p = events[read_ptr];
+                               events[read_ptr].irq = 0;
+                               read_ptr++;
+                               if (read_ptr >= MAX_Q_SIZE)
+                                       read_ptr = 0;
+                               found = 1;
+                               break;
+                       }
+
+                       if (events[read_ptr].irq) {
+                               events[write_ptr] = events[read_ptr];
+                               events[read_ptr].irq = 0;
+                               write_ptr++;
+                               if (write_ptr >= MAX_Q_SIZE)
+                                       write_ptr = 0;
+                       } else
+                               pending_events--;
+
+                       read_ptr++;
+                       if (read_ptr >= MAX_Q_SIZE)
+                               read_ptr = 0;
+               }
+               if (found)
+                       pending_events--;
+               else
+                       ret = -1;
+       } else {
+               ret = -1;
+       }
+
+       spin_unlock_irqrestore(&queue_lock, flags);
+       return ret;
+}
+
+static irqreturn_t mxc_ipu_generic_handler(int irq, void *dev_id)
+{
+       ipu_event_info e;
+
+       e.irq = irq;
+       e.dev = dev_id;
+       events[write_ptr] = e;
+       write_ptr++;
+       if (write_ptr >= MAX_Q_SIZE)
+               write_ptr = 0;
+       pending_events++;
+       /* Wakeup any blocking user context */
+       wake_up_interruptible(&waitq);
+       return IRQ_HANDLED;
+}
+
+static int mxc_ipu_open(struct inode *inode, struct file *file)
+{
+       int ret = 0;
+       return ret;
+}
+static int mxc_ipu_ioctl(struct inode *inode, struct file *file,
+                        unsigned int cmd, unsigned long arg)
+{
+       int ret = 0;
+
+       switch (cmd) {
+
+       case IPU_INIT_CHANNEL:
+               {
+                       ipu_channel_parm parm;
+                       if (copy_from_user
+                           (&parm, (ipu_channel_parm *) arg,
+                            sizeof(ipu_channel_parm))) {
+                               return -EFAULT;
+                       }
+                       if (!parm.flag) {
+                               ret =
+                                   ipu_init_channel(parm.channel,
+                                                    &parm.params);
+                       } else {
+                               ret = ipu_init_channel(parm.channel, NULL);
+                       }
+               }
+               break;
+
+       case IPU_UNINIT_CHANNEL:
+               {
+                       ipu_channel_t ch;
+                       int __user *argp = (void __user *)arg;
+                       if (get_user(ch, argp))
+                               return -EFAULT;
+                       ipu_uninit_channel(ch);
+               }
+               break;
+
+       case IPU_INIT_CHANNEL_BUFFER:
+               {
+                       ipu_channel_buf_parm parm;
+                       if (copy_from_user
+                           (&parm, (ipu_channel_buf_parm *) arg,
+                            sizeof(ipu_channel_buf_parm))) {
+                               return -EFAULT;
+                       }
+                       ret =
+                           ipu_init_channel_buffer(parm.channel, parm.type,
+                                                   parm.pixel_fmt,
+                                                   parm.width, parm.height,
+                                                   parm.stride,
+                                                   parm.rot_mode,
+                                                   parm.phyaddr_0,
+                                                   parm.phyaddr_1,
+                                                   parm.u_offset,
+                                                   parm.v_offset);
+
+               }
+               break;
+
+       case IPU_UPDATE_CHANNEL_BUFFER:
+               {
+                       ipu_channel_buf_parm parm;
+                       if (copy_from_user
+                           (&parm, (ipu_channel_buf_parm *) arg,
+                            sizeof(ipu_channel_buf_parm))) {
+                               return -EFAULT;
+                       }
+                       if ((parm.phyaddr_0 != (dma_addr_t) NULL)
+                           && (parm.phyaddr_1 == (dma_addr_t) NULL)) {
+                               ret =
+                                   ipu_update_channel_buffer(parm.channel,
+                                                             parm.type,
+                                                             parm.bufNum,
+                                                             parm.phyaddr_0);
+                       } else if ((parm.phyaddr_0 == (dma_addr_t) NULL)
+                                  && (parm.phyaddr_1 != (dma_addr_t) NULL)) {
+                               ret =
+                                   ipu_update_channel_buffer(parm.channel,
+                                                             parm.type,
+                                                             parm.bufNum,
+                                                             parm.phyaddr_1);
+                       } else {
+                               ret = -1;
+                       }
+
+               }
+               break;
+       case IPU_SELECT_CHANNEL_BUFFER:
+               {
+                       ipu_channel_buf_parm parm;
+                       if (copy_from_user
+                           (&parm, (ipu_channel_buf_parm *) arg,
+                            sizeof(ipu_channel_buf_parm))) {
+                               return -EFAULT;
+                       }
+                       ret =
+                           ipu_select_buffer(parm.channel, parm.type,
+                                             parm.bufNum);
+
+               }
+               break;
+       case IPU_LINK_CHANNELS:
+               {
+                       ipu_channel_link link;
+                       if (copy_from_user
+                           (&link, (ipu_channel_link *) arg,
+                            sizeof(ipu_channel_link))) {
+                               return -EFAULT;
+                       }
+                       ret = ipu_link_channels(link.src_ch, link.dest_ch);
+
+               }
+               break;
+       case IPU_UNLINK_CHANNELS:
+               {
+                       ipu_channel_link link;
+                       if (copy_from_user
+                           (&link, (ipu_channel_link *) arg,
+                            sizeof(ipu_channel_link))) {
+                               return -EFAULT;
+                       }
+                       ret = ipu_unlink_channels(link.src_ch, link.dest_ch);
+
+               }
+               break;
+       case IPU_ENABLE_CHANNEL:
+               {
+                       ipu_channel_t ch;
+                       int __user *argp = (void __user *)arg;
+                       if (get_user(ch, argp))
+                               return -EFAULT;
+                       ipu_enable_channel(ch);
+               }
+               break;
+       case IPU_DISABLE_CHANNEL:
+               {
+                       ipu_channel_info info;
+                       if (copy_from_user
+                           (&info, (ipu_channel_info *) arg,
+                            sizeof(ipu_channel_info))) {
+                               return -EFAULT;
+                       }
+                       ret = ipu_disable_channel(info.channel, info.stop);
+               }
+               break;
+       case IPU_ENABLE_IRQ:
+               {
+                       uint32_t irq;
+                       int __user *argp = (void __user *)arg;
+                       if (get_user(irq, argp))
+                               return -EFAULT;
+                       ipu_enable_irq(irq);
+               }
+               break;
+       case IPU_DISABLE_IRQ:
+               {
+                       uint32_t irq;
+                       int __user *argp = (void __user *)arg;
+                       if (get_user(irq, argp))
+                               return -EFAULT;
+                       ipu_disable_irq(irq);
+               }
+               break;
+       case IPU_CLEAR_IRQ:
+               {
+                       uint32_t irq;
+                       int __user *argp = (void __user *)arg;
+                       if (get_user(irq, argp))
+                               return -EFAULT;
+                       ipu_clear_irq(irq);
+               }
+               break;
+       case IPU_FREE_IRQ:
+               {
+                       ipu_irq_info info;
+                       if (copy_from_user
+                           (&info, (ipu_irq_info *) arg,
+                            sizeof(ipu_irq_info))) {
+                               return -EFAULT;
+                       }
+                       ipu_free_irq(info.irq, info.dev_id);
+               }
+               break;
+       case IPU_REQUEST_IRQ_STATUS:
+               {
+                       uint32_t irq;
+                       int __user *argp = (void __user *)arg;
+                       if (get_user(irq, argp))
+                               return -EFAULT;
+                       ret = ipu_get_irq_status(irq);
+               }
+               break;
+       case IPU_SDC_INIT_PANEL:
+               {
+                       ipu_sdc_panel_info sinfo;
+                       if (copy_from_user
+                           (&sinfo, (ipu_sdc_panel_info *) arg,
+                            sizeof(ipu_sdc_panel_info))) {
+                               return -EFAULT;
+                       }
+                       ret =
+                           ipu_sdc_init_panel(sinfo.panel, sinfo.pixel_clk,
+                                              sinfo.width, sinfo.height,
+                                              sinfo.pixel_fmt,
+                                              sinfo.hStartWidth,
+                                              sinfo.hSyncWidth,
+                                              sinfo.hEndWidth,
+                                              sinfo.vStartWidth,
+                                              sinfo.vSyncWidth,
+                                              sinfo.vEndWidth, sinfo.signal);
+               }
+               break;
+       case IPU_SDC_SET_WIN_POS:
+               {
+                       ipu_sdc_window_pos pos;
+                       if (copy_from_user
+                           (&pos, (ipu_sdc_window_pos *) arg,
+                            sizeof(ipu_sdc_window_pos))) {
+                               return -EFAULT;
+                       }
+                       ret =
+                           ipu_disp_set_window_pos(pos.channel, pos.x_pos,
+                                                  pos.y_pos);
+
+               }
+               break;
+       case IPU_SDC_SET_GLOBAL_ALPHA:
+               {
+                       ipu_sdc_global_alpha g;
+                       if (copy_from_user
+                           (&g, (ipu_sdc_global_alpha *) arg,
+                            sizeof(ipu_sdc_global_alpha))) {
+                               return -EFAULT;
+                       }
+                       ret = ipu_sdc_set_global_alpha(g.enable, g.alpha);
+               }
+               break;
+       case IPU_SDC_SET_COLOR_KEY:
+               {
+                       ipu_sdc_color_key c;
+                       if (copy_from_user
+                           (&c, (ipu_sdc_color_key *) arg,
+                            sizeof(ipu_sdc_color_key))) {
+                               return -EFAULT;
+                       }
+                       ret =
+                           ipu_sdc_set_color_key(c.channel, c.enable,
+                                                 c.colorKey);
+               }
+               break;
+       case IPU_SDC_SET_BRIGHTNESS:
+               {
+                       uint8_t b;
+                       int __user *argp = (void __user *)arg;
+                       if (get_user(b, argp))
+                               return -EFAULT;
+                       ret = ipu_sdc_set_brightness(b);
+
+               }
+               break;
+       case IPU_REGISTER_GENERIC_ISR:
+               {
+                       ipu_event_info info;
+                       if (copy_from_user
+                           (&info, (ipu_event_info *) arg,
+                            sizeof(ipu_event_info))) {
+                               return -EFAULT;
+                       }
+                       ret =
+                           ipu_request_irq(info.irq, mxc_ipu_generic_handler,
+                                           0, "video_sink", info.dev);
+               }
+               break;
+       case IPU_GET_EVENT:
+               /* User will have to allocate event_type structure and pass the pointer in arg */
+               {
+                       ipu_event_info info;
+                       int r = -1;
+
+                       if (copy_from_user
+                                       (&info, (ipu_event_info *) arg,
+                                        sizeof(ipu_event_info)))
+                               return -EFAULT;
+
+                       r = get_events(&info);
+                       if (r == -1) {
+                               wait_event_interruptible_timeout(waitq,
+                                               (pending_events != 0), 2 * HZ);
+                               r = get_events(&info);
+                       }
+                       ret = -1;
+                       if (r == 0) {
+                               if (!copy_to_user((ipu_event_info *) arg,
+                                       &info, sizeof(ipu_event_info)))
+                                       ret = 0;
+                       }
+               }
+               break;
+       case IPU_ADC_WRITE_TEMPLATE:
+               {
+                       ipu_adc_template temp;
+                       if (copy_from_user
+                           (&temp, (ipu_adc_template *) arg, sizeof(temp))) {
+                               return -EFAULT;
+                       }
+                       ret =
+                           ipu_adc_write_template(temp.disp, temp.pCmd,
+                                                  temp.write);
+               }
+               break;
+       case IPU_ADC_UPDATE:
+               {
+                       ipu_adc_update update;
+                       if (copy_from_user
+                           (&update, (ipu_adc_update *) arg, sizeof(update))) {
+                               return -EFAULT;
+                       }
+                       ret =
+                           ipu_adc_set_update_mode(update.channel, update.mode,
+                                                   update.refresh_rate,
+                                                   update.addr, update.size);
+               }
+               break;
+       case IPU_ADC_SNOOP:
+               {
+                       ipu_adc_snoop snoop;
+                       if (copy_from_user
+                           (&snoop, (ipu_adc_snoop *) arg, sizeof(snoop))) {
+                               return -EFAULT;
+                       }
+                       ret =
+                           ipu_adc_get_snooping_status(snoop.statl,
+                                                       snoop.stath);
+               }
+               break;
+       case IPU_ADC_CMD:
+               {
+                       ipu_adc_cmd cmd;
+                       if (copy_from_user
+                           (&cmd, (ipu_adc_cmd *) arg, sizeof(cmd))) {
+                               return -EFAULT;
+                       }
+                       ret =
+                           ipu_adc_write_cmd(cmd.disp, cmd.type, cmd.cmd,
+                                             cmd.params, cmd.numParams);
+               }
+               break;
+       case IPU_ADC_INIT_PANEL:
+               {
+                       ipu_adc_panel panel;
+                       if (copy_from_user
+                           (&panel, (ipu_adc_panel *) arg, sizeof(panel))) {
+                               return -EFAULT;
+                       }
+                       ret =
+                           ipu_adc_init_panel(panel.disp, panel.width,
+                                              panel.height, panel.pixel_fmt,
+                                              panel.stride, panel.signal,
+                                              panel.addr, panel.vsync_width,
+                                              panel.mode);
+               }
+               break;
+       case IPU_ADC_IFC_TIMING:
+               {
+                       ipu_adc_ifc_timing t;
+                       if (copy_from_user
+                           (&t, (ipu_adc_ifc_timing *) arg, sizeof(t))) {
+                               return -EFAULT;
+                       }
+                       ret =
+                           ipu_adc_init_ifc_timing(t.disp, t.read,
+                                                   t.cycle_time, t.up_time,
+                                                   t.down_time,
+                                                   t.read_latch_time,
+                                                   t.pixel_clk);
+               }
+               break;
+       case IPU_CSI_INIT_INTERFACE:
+               {
+                       ipu_csi_interface c;
+                       if (copy_from_user
+                           (&c, (ipu_csi_interface *) arg, sizeof(c)))
+                               return -EFAULT;
+                       ret =
+                           ipu_csi_init_interface(c.width, c.height,
+                                                  c.pixel_fmt, c.signal);
+               }
+               break;
+       case IPU_CSI_ENABLE_MCLK:
+               {
+                       ipu_csi_mclk m;
+                       if (copy_from_user(&m, (ipu_csi_mclk *) arg, sizeof(m)))
+                               return -EFAULT;
+                       ret = ipu_csi_enable_mclk(m.src, m.flag, m.wait);
+               }
+               break;
+       case IPU_CSI_READ_MCLK_FLAG:
+               {
+                       ret = ipu_csi_read_mclk_flag();
+               }
+               break;
+       case IPU_CSI_FLASH_STROBE:
+               {
+                       bool strobe;
+                       int __user *argp = (void __user *)arg;
+                       if (get_user(strobe, argp))
+                               return -EFAULT;
+                       ipu_csi_flash_strobe(strobe);
+               }
+               break;
+       case IPU_CSI_GET_WIN_SIZE:
+               {
+                       ipu_csi_window_size w;
+                       int dummy = 0;
+                       ipu_csi_get_window_size(&w.width, &w.height, dummy);
+                       if (copy_to_user
+                           ((ipu_csi_window_size *) arg, &w, sizeof(w)))
+                               return -EFAULT;
+               }
+               break;
+       case IPU_CSI_SET_WIN_SIZE:
+               {
+                       ipu_csi_window_size w;
+                       int dummy = 0;
+                       if (copy_from_user
+                           (&w, (ipu_csi_window_size *) arg, sizeof(w)))
+                               return -EFAULT;
+                       ipu_csi_set_window_size(w.width, w.height, dummy);
+               }
+               break;
+       case IPU_CSI_SET_WINDOW:
+               {
+                       ipu_csi_window p;
+                       int dummy = 0;
+                       if (copy_from_user
+                           (&p, (ipu_csi_window *) arg, sizeof(p)))
+                               return -EFAULT;
+                       ipu_csi_set_window_pos(p.left, p.top, dummy);
+               }
+               break;
+       case IPU_PF_SET_PAUSE_ROW:
+               {
+                       uint32_t p;
+                       int __user *argp = (void __user *)arg;
+                       if (get_user(p, argp))
+                               return -EFAULT;
+                       ret = ipu_pf_set_pause_row(p);
+               }
+               break;
+       case IPU_ALOC_MEM:
+               {
+                       ipu_mem_info info;
+                       if (copy_from_user
+                                       (&info, (ipu_mem_info *) arg,
+                                        sizeof(ipu_mem_info)))
+                               return -EFAULT;
+
+                       info.vaddr = dma_alloc_coherent(0,
+                                       PAGE_ALIGN(info.size),
+                                       &info.paddr,
+                                       GFP_DMA | GFP_KERNEL);
+                       if (info.vaddr == 0) {
+                               printk(KERN_ERR "dma alloc failed!\n");
+                               return -ENOBUFS;
+                       }
+                       if (copy_to_user((ipu_mem_info *) arg, &info,
+                                       sizeof(ipu_mem_info)) > 0)
+                               return -EFAULT;
+               }
+               break;
+       case IPU_FREE_MEM:
+               {
+                       ipu_mem_info info;
+                       if (copy_from_user
+                                       (&info, (ipu_mem_info *) arg,
+                                        sizeof(ipu_mem_info)))
+                               return -EFAULT;
+
+                       if (info.vaddr != 0)
+                               dma_free_coherent(0, PAGE_ALIGN(info.size),
+                                               info.vaddr, info.paddr);
+                       else
+                               return -EFAULT;
+               }
+               break;
+       case IPU_IS_CHAN_BUSY:
+               {
+                       ipu_channel_t chan;
+                       if (copy_from_user
+                                       (&chan, (ipu_channel_t *)arg,
+                                        sizeof(ipu_channel_t)))
+                               return -EFAULT;
+
+                       if (ipu_is_channel_busy(chan))
+                               ret = 1;
+                       else
+                               ret = 0;
+               }
+               break;
+       default:
+               break;
+
+       }
+       return ret;
+}
+
+static int mxc_ipu_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       vma->vm_page_prot = pgprot_writethru(vma->vm_page_prot);
+
+       if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
+                               vma->vm_end - vma->vm_start,
+                               vma->vm_page_prot)) {
+               printk(KERN_ERR
+                               "mmap failed!\n");
+               return -ENOBUFS;
+       }
+       return 0;
+}
+
+static int mxc_ipu_release(struct inode *inode, struct file *file)
+{
+       return 0;
+}
+
+static struct file_operations mxc_ipu_fops = {
+       .owner = THIS_MODULE,
+       .open = mxc_ipu_open,
+       .mmap = mxc_ipu_mmap,
+       .release = mxc_ipu_release,
+       .ioctl = mxc_ipu_ioctl
+};
+
+int register_ipu_device()
+{
+       int ret = 0;
+       struct device *temp;
+       mxc_ipu_major = register_chrdev(0, "mxc_ipu", &mxc_ipu_fops);
+       if (mxc_ipu_major < 0) {
+               printk(KERN_ERR
+                      "Unable to register Mxc Ipu as a char device\n");
+               return mxc_ipu_major;
+       }
+
+       mxc_ipu_class = class_create(THIS_MODULE, "mxc_ipu");
+       if (IS_ERR(mxc_ipu_class)) {
+               printk(KERN_ERR "Unable to create class for Mxc Ipu\n");
+               ret = PTR_ERR(mxc_ipu_class);
+               goto err1;
+       }
+
+       temp = device_create(mxc_ipu_class, NULL, MKDEV(mxc_ipu_major, 0), NULL,
+                            "mxc_ipu");
+
+       if (IS_ERR(temp)) {
+               printk(KERN_ERR "Unable to create class device for Mxc Ipu\n");
+               ret = PTR_ERR(temp);
+               goto err2;
+       }
+       spin_lock_init(&queue_lock);
+       init_waitqueue_head(&waitq);
+       return ret;
+
+      err2:
+       class_destroy(mxc_ipu_class);
+      err1:
+       unregister_chrdev(mxc_ipu_major, "mxc_ipu");
+       return ret;
+
+}
diff --git a/drivers/mxc/ipu/ipu_ic.c b/drivers/mxc/ipu/ipu_ic.c
new file mode 100644 (file)
index 0000000..a00521d
--- /dev/null
@@ -0,0 +1,590 @@
+/*
+ * Copyright 2005-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 ipu_ic.c
+ *
+ * @brief IPU IC functions
+ *
+ * @ingroup IPU
+ */
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/ipu.h>
+
+#include "ipu_prv.h"
+#include "ipu_regs.h"
+#include "ipu_param_mem.h"
+
+enum {
+       IC_TASK_VIEWFINDER,
+       IC_TASK_ENCODER,
+       IC_TASK_POST_PROCESSOR
+};
+
+extern int g_ipu_hw_rev;
+static void _init_csc(uint8_t ic_task, ipu_color_space_t in_format,
+                     ipu_color_space_t out_format);
+static bool _calc_resize_coeffs(uint32_t inSize, uint32_t outSize,
+                               uint32_t *resizeCoeff,
+                               uint32_t *downsizeCoeff);
+
+void _ipu_ic_enable_task(ipu_channel_t channel)
+{
+       uint32_t ic_conf;
+
+       ic_conf = __raw_readl(IC_CONF);
+       switch (channel) {
+       case CSI_PRP_VF_ADC:
+       case MEM_PRP_VF_ADC:
+       case CSI_PRP_VF_MEM:
+       case MEM_PRP_VF_MEM:
+               ic_conf |= IC_CONF_PRPVF_EN;
+               break;
+       case MEM_ROT_VF_MEM:
+               ic_conf |= IC_CONF_PRPVF_ROT_EN;
+               break;
+       case CSI_PRP_ENC_MEM:
+       case MEM_PRP_ENC_MEM:
+               ic_conf |= IC_CONF_PRPENC_EN;
+               break;
+       case MEM_ROT_ENC_MEM:
+               ic_conf |= IC_CONF_PRPENC_ROT_EN;
+               break;
+       case MEM_PP_ADC:
+       case MEM_PP_MEM:
+               ic_conf |= IC_CONF_PP_EN;
+               break;
+       case MEM_ROT_PP_MEM:
+               ic_conf |= IC_CONF_PP_ROT_EN;
+               break;
+       case CSI_MEM:
+               ic_conf |= IC_CONF_RWS_EN | IC_CONF_PRPENC_EN;
+               break;
+       default:
+               break;
+       }
+       __raw_writel(ic_conf, IC_CONF);
+}
+
+void _ipu_ic_disable_task(ipu_channel_t channel)
+{
+       uint32_t ic_conf;
+
+       ic_conf = __raw_readl(IC_CONF);
+       switch (channel) {
+       case CSI_PRP_VF_ADC:
+       case MEM_PRP_VF_ADC:
+       case CSI_PRP_VF_MEM:
+       case MEM_PRP_VF_MEM:
+               ic_conf &= ~IC_CONF_PRPVF_EN;
+               break;
+       case MEM_ROT_VF_MEM:
+               ic_conf &= ~IC_CONF_PRPVF_ROT_EN;
+               break;
+       case CSI_PRP_ENC_MEM:
+       case MEM_PRP_ENC_MEM:
+               ic_conf &= ~IC_CONF_PRPENC_EN;
+               break;
+       case MEM_ROT_ENC_MEM:
+               ic_conf &= ~IC_CONF_PRPENC_ROT_EN;
+               break;
+       case MEM_PP_ADC:
+       case MEM_PP_MEM:
+               ic_conf &= ~IC_CONF_PP_EN;
+               break;
+       case MEM_ROT_PP_MEM:
+               ic_conf &= ~IC_CONF_PP_ROT_EN;
+               break;
+       case CSI_MEM:
+               ic_conf &= ~(IC_CONF_RWS_EN | IC_CONF_PRPENC_EN);
+               break;
+       default:
+               break;
+       }
+       __raw_writel(ic_conf, IC_CONF);
+}
+
+void _ipu_ic_init_prpvf(ipu_channel_params_t *params, bool src_is_csi)
+{
+       uint32_t reg, ic_conf;
+       uint32_t downsizeCoeff, resizeCoeff;
+       ipu_color_space_t in_fmt, out_fmt;
+
+       /* Setup vertical resizing */
+       _calc_resize_coeffs(params->mem_prp_vf_mem.in_height,
+                           params->mem_prp_vf_mem.out_height,
+                           &resizeCoeff, &downsizeCoeff);
+       reg = (downsizeCoeff << 30) | (resizeCoeff << 16);
+
+       /* Setup horizontal resizing */
+       _calc_resize_coeffs(params->mem_prp_vf_mem.in_width,
+                           params->mem_prp_vf_mem.out_width,
+                           &resizeCoeff, &downsizeCoeff);
+       reg |= (downsizeCoeff << 14) | resizeCoeff;
+
+       __raw_writel(reg, IC_PRP_VF_RSC);
+
+       ic_conf = __raw_readl(IC_CONF);
+
+       /* Setup color space conversion */
+       in_fmt = format_to_colorspace(params->mem_prp_vf_mem.in_pixel_fmt);
+       out_fmt = format_to_colorspace(params->mem_prp_vf_mem.out_pixel_fmt);
+       if (in_fmt == RGB) {
+               if ((out_fmt == YCbCr) || (out_fmt == YUV)) {
+                       _init_csc(IC_TASK_VIEWFINDER, RGB, out_fmt);
+                       ic_conf |= IC_CONF_PRPVF_CSC1;  /* Enable RGB->YCBCR CSC */
+               }
+       }
+       if ((in_fmt == YCbCr) || (in_fmt == YUV)) {
+               if (out_fmt == RGB) {
+                       _init_csc(IC_TASK_VIEWFINDER, YCbCr, RGB);
+                       ic_conf |= IC_CONF_PRPVF_CSC1;  /* Enable YCBCR->RGB CSC */
+               } else {
+                       /* TODO: Support YUV<->YCbCr conversion? */
+               }
+       }
+
+       if (params->mem_prp_vf_mem.graphics_combine_en) {
+               ic_conf |= IC_CONF_PRPVF_CMB;
+
+               /* need transparent CSC1 conversion */
+               _init_csc(IC_TASK_POST_PROCESSOR, RGB, RGB);
+               ic_conf |= IC_CONF_PRPVF_CSC1;  /* Enable RGB->RGB CSC */
+
+               if (params->mem_prp_vf_mem.global_alpha_en) {
+                       ic_conf |= IC_CONF_IC_GLB_LOC_A;
+               } else {
+                       ic_conf &= ~IC_CONF_IC_GLB_LOC_A;
+               }
+
+               if (params->mem_prp_vf_mem.key_color_en) {
+                       ic_conf |= IC_CONF_KEY_COLOR_EN;
+               } else {
+                       ic_conf &= ~IC_CONF_KEY_COLOR_EN;
+               }
+       } else {
+               ic_conf &= ~IC_CONF_PP_CMB;
+       }
+
+#ifndef CONFIG_VIRTIO_SUPPORT  /* Setting RWS_EN doesn't work in Virtio */
+       if (src_is_csi) {
+               ic_conf &= ~IC_CONF_RWS_EN;
+       } else {
+               ic_conf |= IC_CONF_RWS_EN;
+       }
+#endif
+       __raw_writel(ic_conf, IC_CONF);
+}
+
+void _ipu_ic_uninit_prpvf(void)
+{
+       uint32_t reg;
+
+       reg = __raw_readl(IC_CONF);
+       reg &= ~(IC_CONF_PRPVF_EN | IC_CONF_PRPVF_CMB |
+                IC_CONF_PRPVF_CSC2 | IC_CONF_PRPVF_CSC1);
+       __raw_writel(reg, IC_CONF);
+}
+
+void _ipu_ic_init_rotate_vf(ipu_channel_params_t *params)
+{
+}
+
+void _ipu_ic_uninit_rotate_vf(void)
+{
+       uint32_t reg;
+       reg = __raw_readl(IC_CONF);
+       reg &= ~IC_CONF_PRPVF_ROT_EN;
+       __raw_writel(reg, IC_CONF);
+}
+
+void _ipu_ic_init_csi(ipu_channel_params_t *params)
+{
+       uint32_t reg;
+       reg = __raw_readl(IC_CONF);
+       reg &= ~IC_CONF_CSI_MEM_WR_EN;
+       __raw_writel(reg, IC_CONF);
+}
+
+void _ipu_ic_uninit_csi(void)
+{
+       uint32_t reg;
+       reg = __raw_readl(IC_CONF);
+       reg &= ~(IC_CONF_RWS_EN | IC_CONF_PRPENC_EN);
+       __raw_writel(reg, IC_CONF);
+}
+
+void _ipu_ic_init_prpenc(ipu_channel_params_t *params, bool src_is_csi)
+{
+       uint32_t reg, ic_conf;
+       uint32_t downsizeCoeff, resizeCoeff;
+       ipu_color_space_t in_fmt, out_fmt;
+
+       /* Setup vertical resizing */
+       _calc_resize_coeffs(params->mem_prp_enc_mem.in_height,
+                           params->mem_prp_enc_mem.out_height,
+                           &resizeCoeff, &downsizeCoeff);
+       reg = (downsizeCoeff << 30) | (resizeCoeff << 16);
+
+       /* Setup horizontal resizing */
+       _calc_resize_coeffs(params->mem_prp_enc_mem.in_width,
+                           params->mem_prp_enc_mem.out_width,
+                           &resizeCoeff, &downsizeCoeff);
+       reg |= (downsizeCoeff << 14) | resizeCoeff;
+
+       __raw_writel(reg, IC_PRP_ENC_RSC);
+
+       ic_conf = __raw_readl(IC_CONF);
+
+       /* Setup color space conversion */
+       in_fmt = format_to_colorspace(params->mem_prp_enc_mem.in_pixel_fmt);
+       out_fmt = format_to_colorspace(params->mem_prp_enc_mem.out_pixel_fmt);
+       if (in_fmt == RGB) {
+               if ((out_fmt == YCbCr) || (out_fmt == YUV)) {
+                       /* TODO: ERROR! */
+               }
+       }
+       if ((in_fmt == YCbCr) || (in_fmt == YUV)) {
+               if (out_fmt == RGB) {
+                       _init_csc(IC_TASK_ENCODER, YCbCr, RGB);
+                       ic_conf |= IC_CONF_PRPENC_CSC1; /* Enable YCBCR->RGB CSC */
+               } else {
+                       /* TODO: Support YUV<->YCbCr conversion? */
+               }
+       }
+
+       if (src_is_csi) {
+               ic_conf &= ~IC_CONF_RWS_EN;
+       } else {
+               ic_conf |= IC_CONF_RWS_EN;
+       }
+
+       __raw_writel(ic_conf, IC_CONF);
+}
+
+void _ipu_ic_uninit_prpenc(void)
+{
+       uint32_t reg;
+
+       reg = __raw_readl(IC_CONF);
+       reg &= ~(IC_CONF_PRPENC_EN | IC_CONF_PRPENC_CSC1);
+       __raw_writel(reg, IC_CONF);
+}
+
+void _ipu_ic_init_rotate_enc(ipu_channel_params_t *params)
+{
+}
+
+void _ipu_ic_uninit_rotate_enc(void)
+{
+       uint32_t reg;
+
+       reg = __raw_readl(IC_CONF);
+       reg &= ~(IC_CONF_PRPENC_ROT_EN);
+       __raw_writel(reg, IC_CONF);
+}
+
+void _ipu_ic_init_pp(ipu_channel_params_t *params)
+{
+       uint32_t reg, ic_conf;
+       uint32_t downsizeCoeff, resizeCoeff;
+       ipu_color_space_t in_fmt, out_fmt;
+
+       /* Setup vertical resizing */
+       _calc_resize_coeffs(params->mem_pp_mem.in_height,
+                           params->mem_pp_mem.out_height,
+                           &resizeCoeff, &downsizeCoeff);
+       reg = (downsizeCoeff << 30) | (resizeCoeff << 16);
+
+       /* Setup horizontal resizing */
+       _calc_resize_coeffs(params->mem_pp_mem.in_width,
+                           params->mem_pp_mem.out_width,
+                           &resizeCoeff, &downsizeCoeff);
+       reg |= (downsizeCoeff << 14) | resizeCoeff;
+
+       __raw_writel(reg, IC_PP_RSC);
+
+       ic_conf = __raw_readl(IC_CONF);
+
+       /* Setup color space conversion */
+       in_fmt = format_to_colorspace(params->mem_pp_mem.in_pixel_fmt);
+       out_fmt = format_to_colorspace(params->mem_pp_mem.out_pixel_fmt);
+       if (in_fmt == RGB) {
+               if ((out_fmt == YCbCr) || (out_fmt == YUV)) {
+                       _init_csc(IC_TASK_POST_PROCESSOR, RGB, out_fmt);
+                       ic_conf |= IC_CONF_PP_CSC2;     /* Enable RGB->YCBCR CSC */
+               }
+       }
+       if ((in_fmt == YCbCr) || (in_fmt == YUV)) {
+               if (out_fmt == RGB) {
+                       _init_csc(IC_TASK_POST_PROCESSOR, YCbCr, RGB);
+                       ic_conf |= IC_CONF_PP_CSC1;     /* Enable YCBCR->RGB CSC */
+               } else {
+                       /* TODO: Support YUV<->YCbCr conversion? */
+               }
+       }
+
+       if (params->mem_pp_mem.graphics_combine_en) {
+               ic_conf |= IC_CONF_PP_CMB;
+
+               /* need transparent CSC1 conversion */
+               _init_csc(IC_TASK_POST_PROCESSOR, RGB, RGB);
+               ic_conf |= IC_CONF_PP_CSC1;     /* Enable RGB->RGB CSC */
+
+               if (params->mem_pp_mem.global_alpha_en) {
+                       ic_conf |= IC_CONF_IC_GLB_LOC_A;
+               } else {
+                       ic_conf &= ~IC_CONF_IC_GLB_LOC_A;
+               }
+
+               if (params->mem_pp_mem.key_color_en) {
+                       ic_conf |= IC_CONF_KEY_COLOR_EN;
+               } else {
+                       ic_conf &= ~IC_CONF_KEY_COLOR_EN;
+               }
+       } else {
+               ic_conf &= ~IC_CONF_PP_CMB;
+       }
+
+       __raw_writel(ic_conf, IC_CONF);
+}
+
+void _ipu_ic_uninit_pp(void)
+{
+       uint32_t reg;
+
+       reg = __raw_readl(IC_CONF);
+       reg &= ~(IC_CONF_PP_EN | IC_CONF_PP_CSC1 | IC_CONF_PP_CSC2 |
+                IC_CONF_PP_CMB);
+       __raw_writel(reg, IC_CONF);
+}
+
+void _ipu_ic_init_rotate_pp(ipu_channel_params_t *params)
+{
+}
+
+void _ipu_ic_uninit_rotate_pp(void)
+{
+       uint32_t reg;
+       reg = __raw_readl(IC_CONF);
+       reg &= ~IC_CONF_PP_ROT_EN;
+       __raw_writel(reg, IC_CONF);
+}
+
+static void _init_csc(uint8_t ic_task, ipu_color_space_t in_format,
+                     ipu_color_space_t out_format)
+{
+/*     Y = R *  .299 + G *  .587 + B *  .114;
+       U = R * -.169 + G * -.332 + B *  .500 + 128.;
+       V = R *  .500 + G * -.419 + B * -.0813 + 128.;*/
+       static const uint32_t rgb2ycbcr_coeff[4][3] = {
+               {0x004D, 0x0096, 0x001D},
+               {0x01D5, 0x01AB, 0x0080},
+               {0x0080, 0x0195, 0x01EB},
+               {0x0000, 0x0200, 0x0200},       /* A0, A1, A2 */
+       };
+
+       /* transparent RGB->RGB matrix for combining
+        */
+       static const uint32_t rgb2rgb_coeff[4][3] = {
+               {0x0080, 0x0000, 0x0000},
+               {0x0000, 0x0080, 0x0000},
+               {0x0000, 0x0000, 0x0080},
+               {0x0000, 0x0000, 0x0000},       /* A0, A1, A2 */
+       };
+
+/*     R = (1.164 * (Y - 16)) + (1.596 * (Cr - 128));
+       G = (1.164 * (Y - 16)) - (0.392 * (Cb - 128)) - (0.813 * (Cr - 128));
+       B = (1.164 * (Y - 16)) + (2.017 * (Cb - 128); */
+       static const uint32_t ycbcr2rgb_coeff[4][3] = {
+               {149, 0, 204},
+               {149, 462, 408},
+               {149, 255, 0},
+               {8192 - 446, 266, 8192 - 554},  /* A0, A1, A2 */
+       };
+
+       uint32_t param[2];
+       uint32_t address = 0;
+
+       if (g_ipu_hw_rev > 1) {
+               if (ic_task == IC_TASK_VIEWFINDER) {
+                       address = 0x645 << 3;
+               } else if (ic_task == IC_TASK_ENCODER) {
+                       address = 0x321 << 3;
+               } else if (ic_task == IC_TASK_POST_PROCESSOR) {
+                       address = 0x96C << 3;
+               } else {
+                       BUG();
+               }
+       } else {
+               if (ic_task == IC_TASK_VIEWFINDER) {
+                       address = 0x5a5 << 3;
+               } else if (ic_task == IC_TASK_ENCODER) {
+                       address = 0x2d1 << 3;
+               } else if (ic_task == IC_TASK_POST_PROCESSOR) {
+                       address = 0x87c << 3;
+               } else {
+                       BUG();
+               }
+       }
+
+       if ((in_format == YCbCr) && (out_format == RGB)) {
+               /* Init CSC1 (YCbCr->RGB) */
+               param[0] =
+                   (ycbcr2rgb_coeff[3][0] << 27) | (ycbcr2rgb_coeff[0][0] <<
+                                                    18) |
+                   (ycbcr2rgb_coeff[1][1] << 9) | ycbcr2rgb_coeff[2][2];
+               /* scale = 2, sat = 0 */
+               param[1] = (ycbcr2rgb_coeff[3][0] >> 5) | (2L << (40 - 32));
+               _ipu_write_param_mem(address, param, 2);
+               dev_dbg(g_ipu_dev,
+                       "addr 0x%04X: word0 = 0x%08X, word1 = 0x%08X\n",
+                       address, param[0], param[1]);
+
+               param[0] =
+                   (ycbcr2rgb_coeff[3][1] << 27) | (ycbcr2rgb_coeff[0][1] <<
+                                                    18) |
+                   (ycbcr2rgb_coeff[1][0] << 9) | ycbcr2rgb_coeff[2][0];
+               param[1] = (ycbcr2rgb_coeff[3][1] >> 5);
+               address += 1L << 3;
+               _ipu_write_param_mem(address, param, 2);
+               dev_dbg(g_ipu_dev,
+                       "addr 0x%04X: word0 = 0x%08X, word1 = 0x%08X\n",
+                       address, param[0], param[1]);
+
+               param[0] =
+                   (ycbcr2rgb_coeff[3][2] << 27) | (ycbcr2rgb_coeff[0][2] <<
+                                                    18) |
+                   (ycbcr2rgb_coeff[1][2] << 9) | ycbcr2rgb_coeff[2][1];
+               param[1] = (ycbcr2rgb_coeff[3][2] >> 5);
+               address += 1L << 3;
+               _ipu_write_param_mem(address, param, 2);
+               dev_dbg(g_ipu_dev,
+                       "addr 0x%04X: word0 = 0x%08X, word1 = 0x%08X\n",
+                       address, param[0], param[1]);
+       } else if ((in_format == RGB) && (out_format == YCbCr)) {
+               /* Init CSC1 (RGB->YCbCr) */
+               param[0] =
+                   (rgb2ycbcr_coeff[3][0] << 27) | (rgb2ycbcr_coeff[0][0] <<
+                                                    18) |
+                   (rgb2ycbcr_coeff[1][1] << 9) | rgb2ycbcr_coeff[2][2];
+               /* scale = 1, sat = 0 */
+               param[1] = (rgb2ycbcr_coeff[3][0] >> 5) | (1UL << 8);
+               _ipu_write_param_mem(address, param, 2);
+               dev_dbg(g_ipu_dev,
+                       "addr 0x%04X: word0 = 0x%08X, word1 = 0x%08X\n",
+                       address, param[0], param[1]);
+
+               param[0] =
+                   (rgb2ycbcr_coeff[3][1] << 27) | (rgb2ycbcr_coeff[0][1] <<
+                                                    18) |
+                   (rgb2ycbcr_coeff[1][0] << 9) | rgb2ycbcr_coeff[2][0];
+               param[1] = (rgb2ycbcr_coeff[3][1] >> 5);
+               address += 1L << 3;
+               _ipu_write_param_mem(address, param, 2);
+               dev_dbg(g_ipu_dev,
+                       "addr 0x%04X: word0 = 0x%08X, word1 = 0x%08X\n",
+                       address, param[0], param[1]);
+
+               param[0] =
+                   (rgb2ycbcr_coeff[3][2] << 27) | (rgb2ycbcr_coeff[0][2] <<
+                                                    18) |
+                   (rgb2ycbcr_coeff[1][2] << 9) | rgb2ycbcr_coeff[2][1];
+               param[1] = (rgb2ycbcr_coeff[3][2] >> 5);
+               address += 1L << 3;
+               _ipu_write_param_mem(address, param, 2);
+               dev_dbg(g_ipu_dev,
+                       "addr 0x%04X: word0 = 0x%08X, word1 = 0x%08X\n",
+                       address, param[0], param[1]);
+       } else if ((in_format == RGB) && (out_format == RGB)) {
+               /* Init CSC1 */
+               param[0] =
+                   (rgb2rgb_coeff[3][0] << 27) | (rgb2rgb_coeff[0][0] << 18) |
+                   (rgb2rgb_coeff[1][1] << 9) | rgb2rgb_coeff[2][2];
+               /* scale = 2, sat = 0 */
+               param[1] = (rgb2rgb_coeff[3][0] >> 5) | (2UL << 8);
+
+               _ipu_write_param_mem(address, param, 2);
+
+               dev_dbg(g_ipu_dev,
+                       "addr 0x%04X: word0 = 0x%08X, word1 = 0x%08X\n",
+                       address, param[0], param[1]);
+
+               param[0] =
+                   (rgb2rgb_coeff[3][1] << 27) | (rgb2rgb_coeff[0][1] << 18) |
+                   (rgb2rgb_coeff[1][0] << 9) | rgb2rgb_coeff[2][0];
+               param[1] = (rgb2rgb_coeff[3][1] >> 5);
+
+               address += 1L << 3;
+               _ipu_write_param_mem(address, param, 2);
+
+               dev_dbg(g_ipu_dev,
+                       "addr 0x%04X: word0 = 0x%08X, word1 = 0x%08X\n",
+                       address, param[0], param[1]);
+
+               param[0] =
+                   (rgb2rgb_coeff[3][2] << 27) | (rgb2rgb_coeff[0][2] << 18) |
+                   (rgb2rgb_coeff[1][2] << 9) | rgb2rgb_coeff[2][1];
+               param[1] = (rgb2rgb_coeff[3][2] >> 5);
+
+               address += 1L << 3;
+               _ipu_write_param_mem(address, param, 2);
+
+               dev_dbg(g_ipu_dev,
+                       "addr 0x%04X: word0 = 0x%08X, word1 = 0x%08X\n",
+                       address, param[0], param[1]);
+       } else {
+               dev_err(g_ipu_dev, "Unsupported color space conversion\n");
+       }
+}
+
+static bool _calc_resize_coeffs(uint32_t inSize, uint32_t outSize,
+                               uint32_t *resizeCoeff,
+                               uint32_t *downsizeCoeff)
+{
+       uint32_t tempSize;
+       uint32_t tempDownsize;
+
+       /* Cannot downsize more than 8:1 */
+       if ((outSize << 3) < inSize) {
+               return false;
+       }
+       /* compute downsizing coefficient */
+       tempDownsize = 0;
+       tempSize = inSize;
+       while ((tempSize >= outSize * 2) && (tempDownsize < 2)) {
+               tempSize >>= 1;
+               tempDownsize++;
+       }
+       *downsizeCoeff = tempDownsize;
+
+       /* compute resizing coefficient using the following equation:
+          resizeCoeff = M*(SI -1)/(SO - 1)
+          where M = 2^13, SI - input size, SO - output size    */
+       *resizeCoeff = (8192L * (tempSize - 1)) / (outSize - 1);
+       if (*resizeCoeff >= 16384L) {
+               dev_err(g_ipu_dev, "Warning! Overflow on resize coeff.\n");
+               *resizeCoeff = 0x3FFF;
+       }
+
+       dev_dbg(g_ipu_dev, "resizing from %u -> %u pixels, "
+               "downsize=%u, resize=%u.%lu (reg=%u)\n", inSize, outSize,
+               *downsizeCoeff, (*resizeCoeff >= 8192L) ? 1 : 0,
+               ((*resizeCoeff & 0x1FFF) * 10000L) / 8192L, *resizeCoeff);
+
+       return true;
+}
diff --git a/drivers/mxc/ipu/ipu_param_mem.h b/drivers/mxc/ipu/ipu_param_mem.h
new file mode 100644 (file)
index 0000000..9d6e287
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2005-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
+ */
+#ifndef __INCLUDE_IPU_PARAM_MEM_H__
+#define __INCLUDE_IPU_PARAM_MEM_H__
+
+#include <linux/types.h>
+
+static __inline void _ipu_ch_param_set_size(uint32_t *params,
+                                           uint32_t pixel_fmt, uint16_t width,
+                                           uint16_t height, uint16_t stride,
+                                           uint32_t u, uint32_t v)
+{
+       uint32_t u_offset = 0;
+       uint32_t v_offset = 0;
+       memset(params, 0, 40);
+
+       params[3] =
+           (uint32_t) ((width - 1) << 12) | ((uint32_t) (height - 1) << 24);
+       params[4] = (uint32_t) (height - 1) >> 8;
+       params[7] = (uint32_t) (stride - 1) << 3;
+
+       switch (pixel_fmt) {
+       case IPU_PIX_FMT_GENERIC:
+               /*Represents 8-bit Generic data */
+               params[7] |= 3 | (7UL << (81 - 64)) | (31L << (89 - 64));       /* BPP & PFS */
+               params[8] = 2;  /* SAT = use 32-bit access */
+               break;
+       case IPU_PIX_FMT_GENERIC_32:
+               /*Represents 32-bit Generic data */
+               params[7] |= (7UL << (81 - 64)) | (7L << (89 - 64));    /* BPP & PFS */
+               params[8] = 2;  /* SAT = use 32-bit access */
+               break;
+       case IPU_PIX_FMT_RGB565:
+               params[7] |= 2L | (4UL << (81 - 64)) | (7L << (89 - 64));       /* BPP & PFS */
+               params[8] = 2 | /* SAT = 32-bit access */
+                   (0UL << (99 - 96)) |        /* Red bit offset */
+                   (5UL << (104 - 96)) |       /* Green bit offset     */
+                   (11UL << (109 - 96)) |      /* Blue bit offset */
+                   (16UL << (114 - 96)) |      /* Alpha bit offset     */
+                   (4UL << (119 - 96)) |       /* Red bit width - 1 */
+                   (5UL << (122 - 96)) |       /* Green bit width - 1 */
+                   (4UL << (125 - 96));        /* Blue bit width - 1 */
+               break;
+       case IPU_PIX_FMT_BGR24: /* 24 BPP & RGB PFS */
+               params[7] |= 1 | (4UL << (81 - 64)) | (7L << (89 - 64));
+               params[8] = 2 | /* SAT = 32-bit access */
+                   (8UL << (104 - 96)) |       /* Green bit offset     */
+                   (16UL << (109 - 96)) |      /* Blue bit offset */
+                   (24UL << (114 - 96)) |      /* Alpha bit offset     */
+                   (7UL << (119 - 96)) |       /* Red bit width - 1 */
+                   (7UL << (122 - 96)) |       /* Green bit width - 1 */
+                   (uint32_t) (7UL << (125 - 96));     /* Blue bit width - 1 */
+               break;
+       case IPU_PIX_FMT_RGB24: /* 24 BPP & RGB PFS     */
+               params[7] |= 1 | (4UL << (81 - 64)) | (7L << (89 - 64));
+               params[8] = 2 | /* SAT = 32-bit access */
+                   (16UL << (99 - 96)) |       /* Red bit offset */
+                   (8UL << (104 - 96)) |       /* Green bit offset */
+                   (0UL << (109 - 96)) |       /* Blue bit offset */
+                   (24UL << (114 - 96)) |      /* Alpha bit offset */
+                   (7UL << (119 - 96)) |       /* Red bit width - 1 */
+                   (7UL << (122 - 96)) |       /* Green bit width - 1 */
+                   (uint32_t) (7UL << (125 - 96));     /* Blue bit width - 1 */
+               break;
+       case IPU_PIX_FMT_BGRA32:
+       case IPU_PIX_FMT_BGR32:
+               /* BPP & pixel fmt */
+               params[7] |= 0 | (4UL << (81 - 64)) | (7 << (89 - 64));
+               params[8] = 2 | /* SAT = 32-bit access */
+                   (8UL << (99 - 96)) |        /* Red bit offset */
+                   (16UL << (104 - 96)) |      /* Green bit offset     */
+                   (24UL << (109 - 96)) |      /* Blue bit offset */
+                   (0UL << (114 - 96)) |       /* Alpha bit offset     */
+                   (7UL << (119 - 96)) |       /* Red bit width - 1 */
+                   (7UL << (122 - 96)) |       /* Green bit width - 1 */
+                   (uint32_t) (7UL << (125 - 96));     /* Blue bit width - 1 */
+               params[9] = 7;  /* Alpha bit width - 1 */
+               break;
+       case IPU_PIX_FMT_RGBA32:
+       case IPU_PIX_FMT_RGB32:
+               /* BPP & pixel fmt */
+               params[7] |= 0 | (4UL << (81 - 64)) | (7 << (89 - 64));
+               params[8] = 2 | /* SAT = 32-bit access */
+                   (24UL << (99 - 96)) |       /* Red bit offset */
+                   (16UL << (104 - 96)) |      /* Green bit offset     */
+                   (8UL << (109 - 96)) |       /* Blue bit offset */
+                   (0UL << (114 - 96)) |       /* Alpha bit offset     */
+                   (7UL << (119 - 96)) |       /* Red bit width - 1 */
+                   (7UL << (122 - 96)) |       /* Green bit width - 1 */
+                   (uint32_t) (7UL << (125 - 96));     /* Blue bit width - 1 */
+               params[9] = 7;  /* Alpha bit width - 1 */
+               break;
+       case IPU_PIX_FMT_ABGR32:
+               /* BPP & pixel fmt */
+               params[7] |= 0 | (4UL << (81 - 64)) | (7 << (89 - 64));
+               params[8] = 2 | /* SAT = 32-bit access */
+                   (0UL << (99 - 96)) |        /* Alpha bit offset     */
+                   (8UL << (104 - 96)) |       /* Blue bit offset */
+                   (16UL << (109 - 96)) |      /* Green bit offset     */
+                   (24UL << (114 - 96)) |      /* Red bit offset */
+                   (7UL << (119 - 96)) |       /* Alpha bit width - 1 */
+                   (7UL << (122 - 96)) |       /* Blue bit width - 1 */
+                   (uint32_t) (7UL << (125 - 96));     /* Green bit width - 1 */
+               params[9] = 7;  /* Red bit width - 1 */
+               break;
+       case IPU_PIX_FMT_UYVY:
+               /* BPP & pixel format */
+               params[7] |= 2 | (6UL << 17) | (7 << (89 - 64));
+               params[8] = 2;  /* SAT = 32-bit access */
+               break;
+       case IPU_PIX_FMT_YUV420P2:
+       case IPU_PIX_FMT_YUV420P:
+               /* BPP & pixel format */
+               params[7] |= 3 | (3UL << 17) | (7 << (89 - 64));
+               params[8] = 2;  /* SAT = 32-bit access */
+               u_offset = (u == 0) ? stride * height : u;
+               v_offset = (v == 0) ? u_offset + u_offset / 4 : v;
+               break;
+       case IPU_PIX_FMT_YVU422P:
+               /* BPP & pixel format */
+               params[7] |= 3 | (2UL << 17) | (7 << (89 - 64));
+               params[8] = 2;  /* SAT = 32-bit access */
+               v_offset = (v == 0) ? stride * height : v;
+               u_offset = (u == 0) ? v_offset + v_offset / 2 : u;
+               break;
+       case IPU_PIX_FMT_YUV422P:
+               /* BPP & pixel format */
+               params[7] |= 3 | (2UL << 17) | (7 << (89 - 64));
+               params[8] = 2;  /* SAT = 32-bit access */
+               u_offset = (u == 0) ? stride * height : u;
+               v_offset = (v == 0) ? u_offset + u_offset / 2 : v;
+               break;
+       default:
+               dev_err(g_ipu_dev, "mxc ipu: unimplemented pixel format\n");
+               break;
+       }
+
+       params[1] = (1UL << (46 - 32)) | (u_offset << (53 - 32));
+       params[2] = u_offset >> (64 - 53);
+       params[2] |= v_offset << (79 - 64);
+       params[3] |= v_offset >> (96 - 79);
+}
+
+static __inline void _ipu_ch_param_set_burst_size(uint32_t *params,
+                                                 uint16_t burst_pixels)
+{
+       params[7] &= ~(0x3FL << (89 - 64));
+       params[7] |= (uint32_t) (burst_pixels - 1) << (89 - 64);
+};
+
+static __inline void _ipu_ch_param_set_buffer(uint32_t *params,
+                                             dma_addr_t buf0, dma_addr_t buf1)
+{
+       params[5] = buf0;
+       params[6] = buf1;
+};
+
+static __inline void _ipu_ch_param_set_rotation(uint32_t *params,
+                                               ipu_rotate_mode_t rot)
+{
+       params[7] |= (uint32_t) rot << (84 - 64);
+};
+
+void _ipu_write_param_mem(uint32_t addr, uint32_t *data, uint32_t numWords);
+
+#endif
diff --git a/drivers/mxc/ipu/ipu_prv.h b/drivers/mxc/ipu/ipu_prv.h
new file mode 100644 (file)
index 0000000..d65e3be
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2005-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
+ */
+#ifndef __INCLUDE_IPU_PRV_H__
+#define __INCLUDE_IPU_PRV_H__
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <mach/hardware.h>
+
+/* Globals */
+extern struct device *g_ipu_dev;
+extern spinlock_t ipu_lock;
+extern struct clk *g_ipu_clk;
+extern struct clk *g_ipu_csi_clk;
+
+int register_ipu_device(void);
+ipu_color_space_t format_to_colorspace(uint32_t fmt);
+
+uint32_t _ipu_channel_status(ipu_channel_t channel);
+
+void _ipu_sdc_fg_init(ipu_channel_params_t *params);
+uint32_t _ipu_sdc_fg_uninit(void);
+void _ipu_sdc_bg_init(ipu_channel_params_t *params);
+uint32_t _ipu_sdc_bg_uninit(void);
+
+void _ipu_ic_enable_task(ipu_channel_t channel);
+void _ipu_ic_disable_task(ipu_channel_t channel);
+void _ipu_ic_init_prpvf(ipu_channel_params_t *params, bool src_is_csi);
+void _ipu_ic_uninit_prpvf(void);
+void _ipu_ic_init_rotate_vf(ipu_channel_params_t *params);
+void _ipu_ic_uninit_rotate_vf(void);
+void _ipu_ic_init_csi(ipu_channel_params_t *params);
+void _ipu_ic_uninit_csi(void);
+void _ipu_ic_init_prpenc(ipu_channel_params_t *params, bool src_is_csi);
+void _ipu_ic_uninit_prpenc(void);
+void _ipu_ic_init_rotate_enc(ipu_channel_params_t *params);
+void _ipu_ic_uninit_rotate_enc(void);
+void _ipu_ic_init_pp(ipu_channel_params_t *params);
+void _ipu_ic_uninit_pp(void);
+void _ipu_ic_init_rotate_pp(ipu_channel_params_t *params);
+void _ipu_ic_uninit_rotate_pp(void);
+
+int32_t _ipu_adc_init_channel(ipu_channel_t chan, display_port_t disp,
+                             mcu_mode_t cmd, int16_t x_pos, int16_t y_pos);
+int32_t _ipu_adc_uninit_channel(ipu_channel_t chan);
+
+#endif                         /* __INCLUDE_IPU_PRV_H__ */
diff --git a/drivers/mxc/ipu/ipu_regs.h b/drivers/mxc/ipu/ipu_regs.h
new file mode 100644 (file)
index 0000000..dce8ae3
--- /dev/null
@@ -0,0 +1,396 @@
+/*
+ * Copyright 2005-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 ipu_regs.h
+ *
+ * @brief IPU Register definitions
+ *
+ * @ingroup IPU
+ */
+#ifndef __IPU_REGS_INCLUDED__
+#define __IPU_REGS_INCLUDED__
+
+#define IPU_REG_BASE    IO_ADDRESS(IPU_CTRL_BASE_ADDR)
+
+/* Register addresses */
+/* IPU Common registers */
+#define IPU_CONF                (IPU_REG_BASE + 0x0000)
+#define IPU_CHA_BUF0_RDY        (IPU_REG_BASE + 0x0004)
+#define IPU_CHA_BUF1_RDY        (IPU_REG_BASE + 0x0008)
+#define IPU_CHA_DB_MODE_SEL     (IPU_REG_BASE + 0x000C)
+#define IPU_CHA_CUR_BUF         (IPU_REG_BASE + 0x0010)
+#define IPU_FS_PROC_FLOW        (IPU_REG_BASE + 0x0014)
+#define IPU_FS_DISP_FLOW        (IPU_REG_BASE + 0x0018)
+#define IPU_TASKS_STAT          (IPU_REG_BASE + 0x001C)
+#define IPU_IMA_ADDR            (IPU_REG_BASE + 0x0020)
+#define IPU_IMA_DATA            (IPU_REG_BASE + 0x0024)
+#define IPU_INT_CTRL_1          (IPU_REG_BASE + 0x0028)
+#define IPU_INT_CTRL_2          (IPU_REG_BASE + 0x002C)
+#define IPU_INT_CTRL_3          (IPU_REG_BASE + 0x0030)
+#define IPU_INT_CTRL_4          (IPU_REG_BASE + 0x0034)
+#define IPU_INT_CTRL_5          (IPU_REG_BASE + 0x0038)
+#define IPU_INT_STAT_1          (IPU_REG_BASE + 0x003C)
+#define IPU_INT_STAT_2          (IPU_REG_BASE + 0x0040)
+#define IPU_INT_STAT_3          (IPU_REG_BASE + 0x0044)
+#define IPU_INT_STAT_4          (IPU_REG_BASE + 0x0048)
+#define IPU_INT_STAT_5          (IPU_REG_BASE + 0x004C)
+#define IPU_BRK_CTRL_1          (IPU_REG_BASE + 0x0050)
+#define IPU_BRK_CTRL_2          (IPU_REG_BASE + 0x0054)
+#define IPU_BRK_STAT            (IPU_REG_BASE + 0x0058)
+#define IPU_DIAGB_CTRL          (IPU_REG_BASE + 0x005C)
+/* CMOS Sensor Interface Registers */
+#define CSI_SENS_CONF           (IPU_REG_BASE + 0x0060)
+#define CSI_SENS_FRM_SIZE       (IPU_REG_BASE + 0x0064)
+#define CSI_ACT_FRM_SIZE        (IPU_REG_BASE + 0x0068)
+#define CSI_OUT_FRM_CTRL        (IPU_REG_BASE + 0x006C)
+#define CSI_TST_CTRL            (IPU_REG_BASE + 0x0070)
+#define CSI_CCIR_CODE_1         (IPU_REG_BASE + 0x0074)
+#define CSI_CCIR_CODE_2         (IPU_REG_BASE + 0x0078)
+#define CSI_CCIR_CODE_3         (IPU_REG_BASE + 0x007C)
+#define CSI_FLASH_STROBE_1      (IPU_REG_BASE + 0x0080)
+#define CSI_FLASH_STROBE_2      (IPU_REG_BASE + 0x0084)
+/* Image Converter Registers */
+#define IC_CONF                 (IPU_REG_BASE + 0x0088)
+#define IC_PRP_ENC_RSC          (IPU_REG_BASE + 0x008C)
+#define IC_PRP_VF_RSC           (IPU_REG_BASE + 0x0090)
+#define IC_PP_RSC               (IPU_REG_BASE + 0x0094)
+#define IC_CMBP_1               (IPU_REG_BASE + 0x0098)
+#define IC_CMBP_2               (IPU_REG_BASE + 0x009C)
+#define PF_CONF                 (IPU_REG_BASE + 0x00A0)
+#define IDMAC_CONF              (IPU_REG_BASE + 0x00A4)
+#define IDMAC_CHA_EN            (IPU_REG_BASE + 0x00A8)
+#define IDMAC_CHA_PRI           (IPU_REG_BASE + 0x00AC)
+#define IDMAC_CHA_BUSY          (IPU_REG_BASE + 0x00B0)
+/* SDC Registers */
+#define SDC_COM_CONF            (IPU_REG_BASE + 0x00B4)
+#define SDC_GW_CTRL             (IPU_REG_BASE + 0x00B8)
+#define SDC_FG_POS              (IPU_REG_BASE + 0x00BC)
+#define SDC_BG_POS              (IPU_REG_BASE + 0x00C0)
+#define SDC_CUR_POS             (IPU_REG_BASE + 0x00C4)
+#define SDC_PWM_CTRL            (IPU_REG_BASE + 0x00C8)
+#define SDC_CUR_MAP             (IPU_REG_BASE + 0x00CC)
+#define SDC_HOR_CONF            (IPU_REG_BASE + 0x00D0)
+#define SDC_VER_CONF            (IPU_REG_BASE + 0x00D4)
+#define SDC_SHARP_CONF_1        (IPU_REG_BASE + 0x00D8)
+#define SDC_SHARP_CONF_2        (IPU_REG_BASE + 0x00DC)
+/* ADC Registers */
+#define ADC_CONF                (IPU_REG_BASE + 0x00E0)
+#define ADC_SYSCHA1_SA          (IPU_REG_BASE + 0x00E4)
+#define ADC_SYSCHA2_SA          (IPU_REG_BASE + 0x00E8)
+#define ADC_PRPCHAN_SA          (IPU_REG_BASE + 0x00EC)
+#define ADC_PPCHAN_SA           (IPU_REG_BASE + 0x00F0)
+#define ADC_DISP0_CONF          (IPU_REG_BASE + 0x00F4)
+#define ADC_DISP0_RD_AP         (IPU_REG_BASE + 0x00F8)
+#define ADC_DISP0_RDM           (IPU_REG_BASE + 0x00FC)
+#define ADC_DISP0_SS            (IPU_REG_BASE + 0x0100)
+#define ADC_DISP1_CONF          (IPU_REG_BASE + 0x0104)
+#define ADC_DISP1_RD_AP         (IPU_REG_BASE + 0x0108)
+#define ADC_DISP1_RDM           (IPU_REG_BASE + 0x010C)
+#define ADC_DISP12_SS           (IPU_REG_BASE + 0x0110)
+#define ADC_DISP2_CONF          (IPU_REG_BASE + 0x0114)
+#define ADC_DISP2_RD_AP         (IPU_REG_BASE + 0x0118)
+#define ADC_DISP2_RDM           (IPU_REG_BASE + 0x011C)
+#define ADC_DISP_VSYNC          (IPU_REG_BASE + 0x0120)
+/* Display Interface re(sters */
+#define DI_DISP_IF_CONF         (IPU_REG_BASE + 0x0124)
+#define DI_DISP_SIG_POL         (IPU_REG_BASE + 0x0128)
+#define DI_SER_DISP1_CONF       (IPU_REG_BASE + 0x012C)
+#define DI_SER_DISP2_CONF       (IPU_REG_BASE + 0x0130)
+#define DI_HSP_CLK_PER          (IPU_REG_BASE + 0x0134)
+#define DI_DISP0_TIME_CONF_1    (IPU_REG_BASE + 0x0138)
+#define DI_DISP0_TIME_CONF_2    (IPU_REG_BASE + 0x013C)
+#define DI_DISP0_TIME_CONF_3    (IPU_REG_BASE + 0x0140)
+#define DI_DISP1_TIME_CONF_1    (IPU_REG_BASE + 0x0144)
+#define DI_DISP1_TIME_CONF_2    (IPU_REG_BASE + 0x0148)
+#define DI_DISP1_TIME_CONF_3    (IPU_REG_BASE + 0x014C)
+#define DI_DISP2_TIME_CONF_1    (IPU_REG_BASE + 0x0150)
+#define DI_DISP2_TIME_CONF_2    (IPU_REG_BASE + 0x0154)
+#define DI_DISP2_TIME_CONF_3    (IPU_REG_BASE + 0x0158)
+#define DI_DISP3_TIME_CONF      (IPU_REG_BASE + 0x015C)
+#define DI_DISP0_DB0_MAP        (IPU_REG_BASE + 0x0160)
+#define DI_DISP0_DB1_MAP        (IPU_REG_BASE + 0x0164)
+#define DI_DISP0_DB2_MAP        (IPU_REG_BASE + 0x0168)
+#define DI_DISP0_CB0_MAP        (IPU_REG_BASE + 0x016C)
+#define DI_DISP0_CB1_MAP        (IPU_REG_BASE + 0x0170)
+#define DI_DISP0_CB2_MAP        (IPU_REG_BASE + 0x0174)
+#define DI_DISP1_DB0_MAP        (IPU_REG_BASE + 0x0178)
+#define DI_DISP1_DB1_MAP        (IPU_REG_BASE + 0x017C)
+#define DI_DISP1_DB2_MAP        (IPU_REG_BASE + 0x0180)
+#define DI_DISP1_CB0_MAP        (IPU_REG_BASE + 0x0184)
+#define DI_DISP1_CB1_MAP        (IPU_REG_BASE + 0x0188)
+#define DI_DISP1_CB2_MAP        (IPU_REG_BASE + 0x018C)
+#define DI_DISP2_DB0_MAP        (IPU_REG_BASE + 0x0190)
+#define DI_DISP2_DB1_MAP        (IPU_REG_BASE + 0x0194)
+#define DI_DISP2_DB2_MAP        (IPU_REG_BASE + 0x0198)
+#define DI_DISP2_CB0_MAP        (IPU_REG_BASE + 0x019C)
+#define DI_DISP2_CB1_MAP        (IPU_REG_BASE + 0x01A0)
+#define DI_DISP2_CB2_MAP        (IPU_REG_BASE + 0x01A4)
+#define DI_DISP3_B0_MAP         (IPU_REG_BASE + 0x01A8)
+#define DI_DISP3_B1_MAP         (IPU_REG_BASE + 0x01AC)
+#define DI_DISP3_B2_MAP         (IPU_REG_BASE + 0x01B0)
+#define DI_DISP_ACC_CC          (IPU_REG_BASE + 0x01B4)
+#define DI_DISP_LLA_CONF        (IPU_REG_BASE + 0x01B8)
+#define DI_DISP_LLA_DATA        (IPU_REG_BASE + 0x01BC)
+
+#define IPUIRQ_2_STATREG(int)   (IPU_INT_STAT_1 + 4*(int>>5))
+#define IPUIRQ_2_CTRLREG(int)   (IPU_INT_CTRL_1 + 4*(int>>5))
+#define IPUIRQ_2_MASK(int)      (1UL << (int & 0x1F))
+
+enum {
+       IPU_CONF_CSI_EN = 0x00000001,
+       IPU_CONF_IC_EN = 0x00000002,
+       IPU_CONF_ROT_EN = 0x00000004,
+       IPU_CONF_PF_EN = 0x00000008,
+       IPU_CONF_SDC_EN = 0x00000010,
+       IPU_CONF_ADC_EN = 0x00000020,
+       IPU_CONF_DI_EN = 0x00000040,
+       IPU_CONF_DU_EN = 0x00000080,
+       IPU_CONF_PXL_ENDIAN = 0x00000100,
+
+       FS_PRPVF_ROT_SRC_SEL = 0x00000040,
+       FS_PRPENC_ROT_SRC_SEL = 0x00000020,
+       FS_PRPENC_DEST_SEL = 0x00000010,
+       FS_PP_SRC_SEL_MASK = 0x00000300,
+       FS_PP_SRC_SEL_OFFSET = 8,
+       FS_PP_ROT_SRC_SEL_MASK = 0x00000C00,
+       FS_PP_ROT_SRC_SEL_OFFSET = 10,
+       FS_PF_DEST_SEL_MASK = 0x00003000,
+       FS_PF_DEST_SEL_OFFSET = 12,
+       FS_PRPVF_DEST_SEL_MASK = 0x00070000,
+       FS_PRPVF_DEST_SEL_OFFSET = 16,
+       FS_PRPVF_ROT_DEST_SEL_MASK = 0x00700000,
+       FS_PRPVF_ROT_DEST_SEL_OFFSET = 20,
+       FS_PP_DEST_SEL_MASK = 0x07000000,
+       FS_PP_DEST_SEL_OFFSET = 24,
+       FS_PP_ROT_DEST_SEL_MASK = 0x70000000,
+       FS_PP_ROT_DEST_SEL_OFFSET = 28,
+       FS_VF_IN_VALID = 0x00000002,
+       FS_ENC_IN_VALID = 0x00000001,
+
+       FS_SDC_BG_SRC_SEL_MASK = 0x00000007,
+       FS_SDC_BG_SRC_SEL_OFFSET = 0,
+       FS_SDC_FG_SRC_SEL_MASK = 0x00000070,
+       FS_SDC_FG_SRC_SEL_OFFSET = 4,
+       FS_ADC1_SRC_SEL_MASK = 0x00000700,
+       FS_ADC1_SRC_SEL_OFFSET = 8,
+       FS_ADC2_SRC_SEL_MASK = 0x00007000,
+       FS_ADC2_SRC_SEL_OFFSET = 12,
+       FS_AUTO_REF_PER_MASK = 0x03FF0000,
+       FS_AUTO_REF_PER_OFFSET = 16,
+
+       FS_DEST_ARM = 0,
+       FS_DEST_ROT = 1,
+       FS_DEST_PP = 1,
+       FS_DEST_ADC1 = 2,
+       FS_DEST_ADC2 = 3,
+       FS_DEST_SDC_BG = 4,
+       FS_DEST_SDC_FG = 5,
+       FS_DEST_ADC = 6,
+
+       FS_SRC_ARM = 0,
+       FS_PP_SRC_PF = 1,
+       FS_PP_SRC_ROT = 2,
+
+       FS_ROT_SRC_PP = 1,
+       FS_ROT_SRC_PF = 2,
+
+       FS_PF_DEST_PP = 1,
+       FS_PF_DEST_ROT = 2,
+
+       FS_SRC_ROT_VF = 1,
+       FS_SRC_ROT_PP = 2,
+       FS_SRC_VF = 3,
+       FS_SRC_PP = 4,
+       FS_SRC_SNOOP = 5,
+       FS_SRC_AUTOREF = 6,
+       FS_SRC_AUTOREF_SNOOP = 7,
+
+       TSTAT_PF_H264_PAUSE = 0x00000001,
+       TSTAT_CSI2MEM_MASK = 0x0000000C,
+       TSTAT_CSI2MEM_OFFSET = 2,
+       TSTAT_VF_MASK = 0x00000600,
+       TSTAT_VF_OFFSET = 9,
+       TSTAT_VF_ROT_MASK = 0x000C0000,
+       TSTAT_VF_ROT_OFFSET = 18,
+       TSTAT_ENC_MASK = 0x00000180,
+       TSTAT_ENC_OFFSET = 7,
+       TSTAT_ENC_ROT_MASK = 0x00030000,
+       TSTAT_ENC_ROT_OFFSET = 16,
+       TSTAT_PP_MASK = 0x00001800,
+       TSTAT_PP_OFFSET = 11,
+       TSTAT_PP_ROT_MASK = 0x00300000,
+       TSTAT_PP_ROT_OFFSET = 20,
+       TSTAT_PF_MASK = 0x00C00000,
+       TSTAT_PF_OFFSET = 22,
+       TSTAT_ADCSYS1_MASK = 0x03000000,
+       TSTAT_ADCSYS1_OFFSET = 24,
+       TSTAT_ADCSYS2_MASK = 0x0C000000,
+       TSTAT_ADCSYS2_OFFSET = 26,
+
+       TASK_STAT_IDLE = 0,
+       TASK_STAT_ACTIVE = 1,
+       TASK_STAT_WAIT4READY = 2,
+
+       /* Register bits */
+       SDC_COM_TFT_COLOR = 0x00000001UL,
+       SDC_COM_FG_EN = 0x00000010UL,
+       SDC_COM_GWSEL = 0x00000020UL,
+       SDC_COM_GLB_A = 0x00000040UL,
+       SDC_COM_KEY_COLOR_G = 0x00000080UL,
+       SDC_COM_BG_EN = 0x00000200UL,
+       SDC_COM_SHARP = 0x00001000UL,
+
+       SDC_V_SYNC_WIDTH_L = 0x00000001UL,
+
+       ADC_CONF_PRP_EN = 0x00000001L,
+       ADC_CONF_PP_EN = 0x00000002L,
+       ADC_CONF_MCU_EN = 0x00000004L,
+
+       ADC_DISP_CONF_SL_MASK = 0x00000FFFL,
+       ADC_DISP_CONF_TYPE_MASK = 0x00003000L,
+       ADC_DISP_CONF_TYPE_XY = 0x00002000L,
+
+       ADC_DISP_VSYNC_D0_MODE_MASK = 0x00000003L,
+       ADC_DISP_VSYNC_D0_WIDTH_MASK = 0x003F0000L,
+       ADC_DISP_VSYNC_D12_MODE_MASK = 0x0000000CL,
+       ADC_DISP_VSYNC_D12_WIDTH_MASK = 0x3F000000L,
+
+       /* Image Converter Register bits */
+       IC_CONF_PRPENC_EN = 0x00000001,
+       IC_CONF_PRPENC_CSC1 = 0x00000002,
+       IC_CONF_PRPENC_ROT_EN = 0x00000004,
+       IC_CONF_PRPVF_EN = 0x00000100,
+       IC_CONF_PRPVF_CSC1 = 0x00000200,
+       IC_CONF_PRPVF_CSC2 = 0x00000400,
+       IC_CONF_PRPVF_CMB = 0x00000800,
+       IC_CONF_PRPVF_ROT_EN = 0x00001000,
+       IC_CONF_PP_EN = 0x00010000,
+       IC_CONF_PP_CSC1 = 0x00020000,
+       IC_CONF_PP_CSC2 = 0x00040000,
+       IC_CONF_PP_CMB = 0x00080000,
+       IC_CONF_PP_ROT_EN = 0x00100000,
+       IC_CONF_IC_GLB_LOC_A = 0x10000000,
+       IC_CONF_KEY_COLOR_EN = 0x20000000,
+       IC_CONF_RWS_EN = 0x40000000,
+       IC_CONF_CSI_MEM_WR_EN = 0x80000000,
+
+       IDMA_CHAN_INVALID = 0x000000FF,
+       IDMA_IC_0 = 0x00000001,
+       IDMA_IC_1 = 0x00000002,
+       IDMA_IC_2 = 0x00000004,
+       IDMA_IC_3 = 0x00000008,
+       IDMA_IC_4 = 0x00000010,
+       IDMA_IC_5 = 0x00000020,
+       IDMA_IC_6 = 0x00000040,
+       IDMA_IC_7 = 0x00000080,
+       IDMA_IC_8 = 0x00000100,
+       IDMA_IC_9 = 0x00000200,
+       IDMA_IC_10 = 0x00000400,
+       IDMA_IC_11 = 0x00000800,
+       IDMA_IC_12 = 0x00001000,
+       IDMA_IC_13 = 0x00002000,
+       IDMA_SDC_BG = 0x00004000,
+       IDMA_SDC_FG = 0x00008000,
+       IDMA_SDC_MASK = 0x00010000,
+       IDMA_SDC_PARTIAL = 0x00020000,
+       IDMA_ADC_SYS1_WR = 0x00040000,
+       IDMA_ADC_SYS2_WR = 0x00080000,
+       IDMA_ADC_SYS1_CMD = 0x00100000,
+       IDMA_ADC_SYS2_CMD = 0x00200000,
+       IDMA_ADC_SYS1_RD = 0x00400000,
+       IDMA_ADC_SYS2_RD = 0x00800000,
+       IDMA_PF_QP = 0x01000000,
+       IDMA_PF_BSP = 0x02000000,
+       IDMA_PF_Y_IN = 0x04000000,
+       IDMA_PF_U_IN = 0x08000000,
+       IDMA_PF_V_IN = 0x10000000,
+       IDMA_PF_Y_OUT = 0x20000000,
+       IDMA_PF_U_OUT = 0x40000000,
+       IDMA_PF_V_OUT = 0x80000000,
+
+       CSI_SENS_CONF_DATA_FMT_SHIFT = 8,
+       CSI_SENS_CONF_DATA_FMT_RGB_YUV444 = 0x00000000L,
+       CSI_SENS_CONF_DATA_FMT_YUV422 = 0x00000200L,
+       CSI_SENS_CONF_DATA_FMT_BAYER = 0x00000300L,
+
+       CSI_SENS_CONF_VSYNC_POL_SHIFT = 0,
+       CSI_SENS_CONF_HSYNC_POL_SHIFT = 1,
+       CSI_SENS_CONF_DATA_POL_SHIFT = 2,
+       CSI_SENS_CONF_PIX_CLK_POL_SHIFT = 3,
+       CSI_SENS_CONF_SENS_PRTCL_SHIFT = 4,
+       CSI_SENS_CONF_SENS_CLKSRC_SHIFT = 7,
+       CSI_SENS_CONF_DATA_WIDTH_SHIFT = 10,
+       CSI_SENS_CONF_EXT_VSYNC_SHIFT = 15,
+       CSI_SENS_CONF_DIVRATIO_SHIFT = 16,
+
+       PF_CONF_TYPE_MASK = 0x00000007,
+       PF_CONF_TYPE_SHIFT = 0,
+       PF_CONF_PAUSE_EN = 0x00000010,
+       PF_CONF_RESET = 0x00008000,
+       PF_CONF_PAUSE_ROW_MASK = 0x00FF0000,
+       PF_CONF_PAUSE_ROW_SHIFT = 16,
+
+       /* DI_DISP_SIG_POL bits */
+       DI_D3_VSYNC_POL_SHIFT = 28,
+       DI_D3_HSYNC_POL_SHIFT = 27,
+       DI_D3_DRDY_SHARP_POL_SHIFT = 26,
+       DI_D3_CLK_POL_SHIFT = 25,
+       DI_D3_DATA_POL_SHIFT = 24,
+
+       /* DI_DISP_IF_CONF bits */
+       DI_D3_CLK_IDLE_SHIFT = 26,
+       DI_D3_CLK_SEL_SHIFT = 25,
+       DI_D3_DATAMSK_SHIFT = 24,
+
+       DISPx_IF_CLK_DOWN_OFFSET = 22,
+       DISPx_IF_CLK_UP_OFFSET = 12,
+       DISPx_IF_CLK_PER_OFFSET = 0,
+       DISPx_IF_CLK_READ_EN_OFFSET = 16,
+       DISPx_PIX_CLK_PER_OFFSET = 0,
+
+       DI_CONF_DISP0_EN = 0x00000001L,
+       DI_CONF_DISP0_IF_MODE_OFFSET = 1,
+       DI_CONF_DISP0_BURST_MODE_OFFSET = 3,
+       DI_CONF_DISP1_EN = 0x00000100L,
+       DI_CONF_DISP1_IF_MODE_OFFSET = 9,
+       DI_CONF_DISP1_BURST_MODE_OFFSET = 12,
+       DI_CONF_DISP2_EN = 0x00010000L,
+       DI_CONF_DISP2_IF_MODE_OFFSET = 17,
+       DI_CONF_DISP2_BURST_MODE_OFFSET = 20,
+
+       DI_SER_DISPx_CONF_SER_BIT_NUM_OFFSET = 16,
+       DI_SER_DISPx_CONF_PREAMBLE_OFFSET = 8,
+       DI_SER_DISPx_CONF_PREAMBLE_LEN_OFFSET = 4,
+       DI_SER_DISPx_CONF_RW_CFG_OFFSET = 1,
+       DI_SER_DISPx_CONF_BURST_MODE_EN = 0x01000000L,
+       DI_SER_DISPx_CONF_PREAMBLE_EN = 0x00000001L,
+
+       /* DI_DISP_ACC_CC */
+       DISP0_IF_CLK_CNT_D_MASK = 0x00000003L,
+       DISP0_IF_CLK_CNT_D_OFFSET = 0,
+       DISP0_IF_CLK_CNT_C_MASK = 0x0000000CL,
+       DISP0_IF_CLK_CNT_C_OFFSET = 2,
+       DISP1_IF_CLK_CNT_D_MASK = 0x00000030L,
+       DISP1_IF_CLK_CNT_D_OFFSET = 4,
+       DISP1_IF_CLK_CNT_C_MASK = 0x000000C0L,
+       DISP1_IF_CLK_CNT_C_OFFSET = 6,
+       DISP2_IF_CLK_CNT_D_MASK = 0x00000300L,
+       DISP2_IF_CLK_CNT_D_OFFSET = 8,
+       DISP2_IF_CLK_CNT_C_MASK = 0x00000C00L,
+       DISP2_IF_CLK_CNT_C_OFFSET = 10,
+       DISP3_IF_CLK_CNT_MASK = 0x00003000L,
+       DISP3_IF_CLK_CNT_OFFSET = 12,
+};
+
+#endif
diff --git a/drivers/mxc/ipu/ipu_sdc.c b/drivers/mxc/ipu/ipu_sdc.c
new file mode 100644 (file)
index 0000000..3a8c411
--- /dev/null
@@ -0,0 +1,357 @@
+/*
+ * Copyright 2005-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 ipu_sdc.c
+ *
+ * @brief IPU SDC submodule API functions
+ *
+ * @ingroup IPU
+ */
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/ipu.h>
+#include "ipu_prv.h"
+#include "ipu_regs.h"
+#include "ipu_param_mem.h"
+
+static uint32_t g_h_start_width;
+static uint32_t g_v_start_width;
+
+static const uint32_t di_mappings[] = {
+       0x1600AAAA, 0x00E05555, 0x00070000, 3,  /* RGB888 */
+       0x0005000F, 0x000B000F, 0x0011000F, 1,  /* RGB666 */
+       0x0011000F, 0x000B000F, 0x0005000F, 1,  /* BGR666 */
+       0x0004003F, 0x000A000F, 0x000F003F, 1   /* RGB565 */
+};
+
+/*!
+ * This function is called to initialize a synchronous LCD panel.
+ *
+ * @param       panel           The type of panel.
+ *
+ * @param       pixel_clk       Desired pixel clock frequency in Hz.
+ *
+ * @param       pixel_fmt       Input parameter for pixel format of buffer. Pixel
+ *                              format is a FOURCC ASCII code.
+ *
+ * @param       width           The width of panel in pixels.
+ *
+ * @param       height          The height of panel in pixels.
+ *
+ * @param       hStartWidth     The number of pixel clocks between the HSYNC
+ *                              signal pulse and the start of valid data.
+ *
+ * @param       hSyncWidth      The width of the HSYNC signal in units of pixel
+ *                              clocks.
+ *
+ * @param       hEndWidth       The number of pixel clocks between the end of
+ *                              valid data and the HSYNC signal for next line.
+ *
+ * @param       vStartWidth     The number of lines between the VSYNC
+ *                              signal pulse and the start of valid data.
+ *
+ * @param       vSyncWidth      The width of the VSYNC signal in units of lines
+ *
+ * @param       vEndWidth       The number of lines between the end of valid
+ *                              data and the VSYNC signal for next frame.
+ *
+ * @param       sig             Bitfield of signal polarities for LCD interface.
+ *
+ * @return      This function returns 0 on success or negative error code on
+ *              fail.
+ */
+int32_t ipu_sdc_init_panel(ipu_panel_t panel,
+                          uint32_t pixel_clk,
+                          uint16_t width, uint16_t height,
+                          uint32_t pixel_fmt,
+                          uint16_t h_start_width, uint16_t h_sync_width,
+                          uint16_t h_end_width, uint16_t v_start_width,
+                          uint16_t v_sync_width, uint16_t v_end_width,
+                          ipu_di_signal_cfg_t sig)
+{
+       unsigned long lock_flags;
+       uint32_t reg;
+       uint32_t old_conf;
+       uint32_t div;
+
+       dev_dbg(g_ipu_dev, "panel size = %d x %d\n", width, height);
+
+       if ((v_sync_width == 0) || (h_sync_width == 0))
+               return EINVAL;
+
+       /* Init panel size and blanking periods */
+       reg =
+           ((uint32_t) (h_sync_width - 1) << 26) |
+           ((uint32_t) (width + h_sync_width + h_start_width + h_end_width - 1)
+            << 16);
+       __raw_writel(reg, SDC_HOR_CONF);
+
+       reg = ((uint32_t) (v_sync_width - 1) << 26) | SDC_V_SYNC_WIDTH_L |
+           ((uint32_t)
+            (height + v_sync_width + v_start_width + v_end_width - 1) << 16);
+       __raw_writel(reg, SDC_VER_CONF);
+
+       g_h_start_width = h_start_width + h_sync_width;
+       g_v_start_width = v_start_width + v_sync_width;
+
+       switch (panel) {
+       case IPU_PANEL_SHARP_TFT:
+               __raw_writel(0x00FD0102L, SDC_SHARP_CONF_1);
+               __raw_writel(0x00F500F4L, SDC_SHARP_CONF_2);
+               __raw_writel(SDC_COM_SHARP | SDC_COM_TFT_COLOR, SDC_COM_CONF);
+               break;
+       case IPU_PANEL_TFT:
+               __raw_writel(SDC_COM_TFT_COLOR, SDC_COM_CONF);
+               break;
+       default:
+               return EINVAL;
+       }
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       /* Init clocking */
+
+       /* Calculate divider */
+       /* fractional part is 4 bits so simply multiple by 2^4 to get fractional part */
+       dev_dbg(g_ipu_dev, "pixel clk = %d\n", pixel_clk);
+       div = (clk_get_rate(g_ipu_clk) * 16) / pixel_clk;
+       if (div < 0x40) {       /* Divider less than 4 */
+               dev_dbg(g_ipu_dev,
+                       "InitPanel() - Pixel clock divider less than 1\n");
+               div = 0x40;
+       }
+       /* DISP3_IF_CLK_DOWN_WR is half the divider value and 2 less fraction bits */
+       /* Subtract 1 extra from DISP3_IF_CLK_DOWN_WR based on timing debug     */
+       /* DISP3_IF_CLK_UP_WR is 0 */
+       __raw_writel((((div / 8) - 1) << 22) | div, DI_DISP3_TIME_CONF);
+
+       /* DI settings */
+       old_conf = __raw_readl(DI_DISP_IF_CONF) & 0x78FFFFFF;
+       old_conf |= sig.datamask_en << DI_D3_DATAMSK_SHIFT |
+           sig.clksel_en << DI_D3_CLK_SEL_SHIFT |
+           sig.clkidle_en << DI_D3_CLK_IDLE_SHIFT;
+       __raw_writel(old_conf, DI_DISP_IF_CONF);
+
+       old_conf = __raw_readl(DI_DISP_SIG_POL) & 0xE0FFFFFF;
+       old_conf |= sig.data_pol << DI_D3_DATA_POL_SHIFT |
+           sig.clk_pol << DI_D3_CLK_POL_SHIFT |
+           sig.enable_pol << DI_D3_DRDY_SHARP_POL_SHIFT |
+           sig.Hsync_pol << DI_D3_HSYNC_POL_SHIFT |
+           sig.Vsync_pol << DI_D3_VSYNC_POL_SHIFT;
+       __raw_writel(old_conf, DI_DISP_SIG_POL);
+
+       switch (pixel_fmt) {
+       case IPU_PIX_FMT_RGB24:
+               __raw_writel(di_mappings[0], DI_DISP3_B0_MAP);
+               __raw_writel(di_mappings[1], DI_DISP3_B1_MAP);
+               __raw_writel(di_mappings[2], DI_DISP3_B2_MAP);
+               __raw_writel(__raw_readl(DI_DISP_ACC_CC) |
+                            ((di_mappings[3] - 1) << 12), DI_DISP_ACC_CC);
+               break;
+       case IPU_PIX_FMT_RGB666:
+               __raw_writel(di_mappings[4], DI_DISP3_B0_MAP);
+               __raw_writel(di_mappings[5], DI_DISP3_B1_MAP);
+               __raw_writel(di_mappings[6], DI_DISP3_B2_MAP);
+               __raw_writel(__raw_readl(DI_DISP_ACC_CC) |
+                            ((di_mappings[7] - 1) << 12), DI_DISP_ACC_CC);
+               break;
+       case IPU_PIX_FMT_BGR666:
+               __raw_writel(di_mappings[8], DI_DISP3_B0_MAP);
+               __raw_writel(di_mappings[9], DI_DISP3_B1_MAP);
+               __raw_writel(di_mappings[10], DI_DISP3_B2_MAP);
+               __raw_writel(__raw_readl(DI_DISP_ACC_CC) |
+                            ((di_mappings[11] - 1) << 12), DI_DISP_ACC_CC);
+               break;
+       default:
+               __raw_writel(di_mappings[12], DI_DISP3_B0_MAP);
+               __raw_writel(di_mappings[13], DI_DISP3_B1_MAP);
+               __raw_writel(di_mappings[14], DI_DISP3_B2_MAP);
+               __raw_writel(__raw_readl(DI_DISP_ACC_CC) |
+                            ((di_mappings[15] - 1) << 12), DI_DISP_ACC_CC);
+               break;
+       }
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+       dev_dbg(g_ipu_dev, "DI_DISP_IF_CONF = 0x%08X\n",
+               __raw_readl(DI_DISP_IF_CONF));
+       dev_dbg(g_ipu_dev, "DI_DISP_SIG_POL = 0x%08X\n",
+               __raw_readl(DI_DISP_SIG_POL));
+       dev_dbg(g_ipu_dev, "DI_DISP3_TIME_CONF = 0x%08X\n",
+               __raw_readl(DI_DISP3_TIME_CONF));
+
+       return 0;
+}
+
+/*!
+ * This function sets the foreground and background plane global alpha blending
+ * modes.
+ *
+ * @param       enable          Boolean to enable or disable global alpha
+ *                              blending. If disabled, per pixel blending is used.
+ *
+ * @param       alpha           Global alpha value.
+ *
+ * @return      This function returns 0 on success or negative error code on fail
+ */
+int32_t ipu_sdc_set_global_alpha(bool enable, uint8_t alpha)
+{
+       uint32_t reg;
+       unsigned long lock_flags;
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       if (enable) {
+               reg = __raw_readl(SDC_GW_CTRL) & 0x00FFFFFFL;
+               __raw_writel(reg | ((uint32_t) alpha << 24), SDC_GW_CTRL);
+
+               reg = __raw_readl(SDC_COM_CONF);
+               __raw_writel(reg | SDC_COM_GLB_A, SDC_COM_CONF);
+       } else {
+               reg = __raw_readl(SDC_COM_CONF);
+               __raw_writel(reg & ~SDC_COM_GLB_A, SDC_COM_CONF);
+       }
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+       return 0;
+}
+
+/*!
+ * This function sets the transparent color key for SDC graphic plane.
+ *
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @param       enable          Boolean to enable or disable color key
+ *
+ * @param       colorKey        24-bit RGB color to use as transparent color key.
+ *
+ * @return      This function returns 0 on success or negative error code on fail
+ */
+int32_t ipu_sdc_set_color_key(ipu_channel_t channel, bool enable,
+                             uint32_t color_key)
+{
+       uint32_t reg, sdc_conf;
+       unsigned long lock_flags;
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       sdc_conf = __raw_readl(SDC_COM_CONF);
+       if (channel == MEM_SDC_BG) {
+               sdc_conf &= ~SDC_COM_GWSEL;
+       } else {
+               sdc_conf |= SDC_COM_GWSEL;
+       }
+
+       if (enable) {
+               reg = __raw_readl(SDC_GW_CTRL) & 0xFF000000L;
+               __raw_writel(reg | (color_key & 0x00FFFFFFL), SDC_GW_CTRL);
+
+               sdc_conf |= SDC_COM_KEY_COLOR_G;
+       } else {
+               sdc_conf &= ~SDC_COM_KEY_COLOR_G;
+       }
+       __raw_writel(sdc_conf, SDC_COM_CONF);
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+       return 0;
+}
+
+int32_t ipu_sdc_set_brightness(uint8_t value)
+{
+       __raw_writel(0x03000000UL | value << 16, SDC_PWM_CTRL);
+       return 0;
+}
+
+/*!
+ * This function sets the window position of the foreground or background plane.
+ * modes.
+ *
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @param       x_pos           The X coordinate position to place window at.
+ *                              The position is relative to the top left corner.
+ *
+ * @param       y_pos           The Y coordinate position to place window at.
+ *                              The position is relative to the top left corner.
+ *
+ * @return      This function returns 0 on success or negative error code on fail
+ */
+int32_t ipu_disp_set_window_pos(ipu_channel_t channel, int16_t x_pos,
+                              int16_t y_pos)
+{
+       x_pos += g_h_start_width;
+       y_pos += g_v_start_width;
+
+       if (channel == MEM_SDC_BG) {
+               __raw_writel((x_pos << 16) | y_pos, SDC_BG_POS);
+       } else if (channel == MEM_SDC_FG) {
+               __raw_writel((x_pos << 16) | y_pos, SDC_FG_POS);
+       } else {
+               return EINVAL;
+       }
+       return 0;
+}
+
+void _ipu_sdc_fg_init(ipu_channel_params_t *params)
+{
+       uint32_t reg;
+       (void)params;
+
+       /* Enable FG channel */
+       reg = __raw_readl(SDC_COM_CONF);
+       __raw_writel(reg | SDC_COM_FG_EN | SDC_COM_BG_EN, SDC_COM_CONF);
+}
+
+uint32_t _ipu_sdc_fg_uninit(void)
+{
+       uint32_t reg;
+
+       /* Disable FG channel */
+       reg = __raw_readl(SDC_COM_CONF);
+       __raw_writel(reg & ~SDC_COM_FG_EN, SDC_COM_CONF);
+
+       return reg & SDC_COM_FG_EN;
+}
+
+void _ipu_sdc_bg_init(ipu_channel_params_t *params)
+{
+       uint32_t reg;
+       (void)params;
+
+       /* Enable FG channel */
+       reg = __raw_readl(SDC_COM_CONF);
+       __raw_writel(reg | SDC_COM_BG_EN, SDC_COM_CONF);
+}
+
+uint32_t _ipu_sdc_bg_uninit(void)
+{
+       uint32_t reg;
+
+       /* Disable BG channel */
+       reg = __raw_readl(SDC_COM_CONF);
+       __raw_writel(reg & ~SDC_COM_BG_EN, SDC_COM_CONF);
+
+       return reg & SDC_COM_BG_EN;
+}
+
+/* Exported symbols for modules. */
+EXPORT_SYMBOL(ipu_sdc_init_panel);
+EXPORT_SYMBOL(ipu_sdc_set_global_alpha);
+EXPORT_SYMBOL(ipu_sdc_set_color_key);
+EXPORT_SYMBOL(ipu_sdc_set_brightness);
+EXPORT_SYMBOL(ipu_disp_set_window_pos);
diff --git a/drivers/mxc/ipu/pf/Kconfig b/drivers/mxc/ipu/pf/Kconfig
new file mode 100644 (file)
index 0000000..fa5a777
--- /dev/null
@@ -0,0 +1,7 @@
+config MXC_IPU_PF
+       tristate "MXC MPEG4/H.264 Post Filter Driver"
+       depends on MXC_IPU_V1
+    default y
+       help
+        Driver for MPEG4 dering and deblock and H.264 deblock
+        using MXC IPU h/w
diff --git a/drivers/mxc/ipu/pf/Makefile b/drivers/mxc/ipu/pf/Makefile
new file mode 100644 (file)
index 0000000..641adf4
--- /dev/null
@@ -0,0 +1 @@
+obj-$(CONFIG_MXC_IPU_PF) += mxc_pf.o
diff --git a/drivers/mxc/ipu/pf/mxc_pf.c b/drivers/mxc/ipu/pf/mxc_pf.c
new file mode 100644 (file)
index 0000000..dca52c7
--- /dev/null
@@ -0,0 +1,991 @@
+/*
+ * Copyright 2005-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 mxc_pf.c
+ *
+ * @brief MXC IPU MPEG4/H.264 Post-filtering driver
+ *
+ * User-level API for IPU Hardware MPEG4/H.264 Post-filtering.
+ *
+ * @ingroup MXC_PF
+ */
+
+#include <linux/pagemap.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/ipu.h>
+#include <linux/mxc_pf.h>
+
+struct mxc_pf_data {
+       pf_operation_t mode;
+       u32 pf_enabled;
+       u32 width;
+       u32 height;
+       u32 stride;
+       uint32_t qp_size;
+       dma_addr_t qp_paddr;
+       void *qp_vaddr;
+       pf_buf buf[PF_MAX_BUFFER_CNT];
+       void *buf_vaddr[PF_MAX_BUFFER_CNT];
+       wait_queue_head_t pf_wait;
+       volatile int done_mask;
+       volatile int wait_mask;
+       volatile int busy_flag;
+       struct semaphore busy_lock;
+};
+
+static struct mxc_pf_data pf_data;
+static u8 open_count;
+static struct class *mxc_pf_class;
+
+/*
+ * Function definitions
+ */
+
+static irqreturn_t mxc_pf_irq_handler(int irq, void *dev_id)
+{
+       struct mxc_pf_data *pf = dev_id;
+
+       if (irq == IPU_IRQ_PF_Y_OUT_EOF) {
+               pf->done_mask |= PF_WAIT_Y;
+       } else if (irq == IPU_IRQ_PF_U_OUT_EOF) {
+               pf->done_mask |= PF_WAIT_U;
+       } else if (irq == IPU_IRQ_PF_V_OUT_EOF) {
+               pf->done_mask |= PF_WAIT_V;
+       } else {
+               return IRQ_NONE;
+       }
+
+       if (pf->wait_mask && ((pf->done_mask & pf->wait_mask) == pf->wait_mask)) {
+               wake_up_interruptible(&pf->pf_wait);
+       }
+       return IRQ_HANDLED;
+}
+
+/*!
+ * This function handles PF_IOCTL_INIT calls. It initializes the PF channels,
+ * interrupt handlers, and channel buffers.
+ *
+ * @return      This function returns 0 on success or negative error code on
+ *              error.
+ */
+static int mxc_pf_init(pf_init_params *pf_init)
+{
+       int err;
+       ipu_channel_params_t params;
+       u32 w;
+       u32 stride;
+       u32 h;
+       u32 qp_size = 0;
+       u32 qp_stride;
+
+       if ((pf_init->pf_mode > 4) || (pf_init->width > 1024) ||
+           (pf_init->height > 1024) || (pf_init->stride < pf_init->width)) {
+               return -EINVAL;
+       }
+
+       pf_data.mode = pf_init->pf_mode;
+       w = pf_data.width = pf_init->width;
+       h = pf_data.height = pf_init->height;
+       stride = pf_data.stride = pf_init->stride;
+       pf_data.qp_size = pf_init->qp_size;
+
+       memset(&params, 0, sizeof(params));
+       params.mem_pf_mem.operation = pf_data.mode;
+       err = ipu_init_channel(MEM_PF_Y_MEM, &params);
+       if (err < 0) {
+               printk(KERN_ERR "mxc_pf: error initializing channel\n");
+               goto err0;
+       }
+
+       err = ipu_init_channel_buffer(MEM_PF_Y_MEM, IPU_INPUT_BUFFER,
+                                     IPU_PIX_FMT_GENERIC, w, h, stride,
+                                     IPU_ROTATE_NONE, 0, 0, 0, 0);
+       if (err < 0) {
+               printk(KERN_ERR "mxc_pf: error initializing Y input buffer\n");
+               goto err0;
+       }
+
+       err = ipu_init_channel_buffer(MEM_PF_Y_MEM, IPU_OUTPUT_BUFFER,
+                                     IPU_PIX_FMT_GENERIC, w, h, stride,
+                                     IPU_ROTATE_NONE, 0, 0, 0, 0);
+       if (err < 0) {
+               printk(KERN_ERR "mxc_pf: error initializing Y output buffer\n");
+               goto err0;
+       }
+
+       w = w / 2;
+       h = h / 2;
+       stride = stride / 2;
+
+       if (pf_data.mode != PF_MPEG4_DERING) {
+               err = ipu_init_channel_buffer(MEM_PF_U_MEM, IPU_INPUT_BUFFER,
+                                             IPU_PIX_FMT_GENERIC, w, h, stride,
+                                             IPU_ROTATE_NONE, 0, 0, 0, 0);
+               if (err < 0) {
+                       printk(KERN_ERR
+                              "mxc_pf: error initializing U input buffer\n");
+                       goto err0;
+               }
+
+               err = ipu_init_channel_buffer(MEM_PF_U_MEM, IPU_OUTPUT_BUFFER,
+                                             IPU_PIX_FMT_GENERIC, w, h, stride,
+                                             IPU_ROTATE_NONE, 0, 0, 0, 0);
+               if (err < 0) {
+                       printk(KERN_ERR
+                              "mxc_pf: error initializing U output buffer\n");
+                       goto err0;
+               }
+
+               err = ipu_init_channel_buffer(MEM_PF_V_MEM, IPU_INPUT_BUFFER,
+                                             IPU_PIX_FMT_GENERIC, w, h, stride,
+                                             IPU_ROTATE_NONE, 0, 0, 0, 0);
+               if (err < 0) {
+                       printk(KERN_ERR
+                              "mxc_pf: error initializing V input buffer\n");
+                       goto err0;
+               }
+
+               err = ipu_init_channel_buffer(MEM_PF_V_MEM, IPU_OUTPUT_BUFFER,
+                                             IPU_PIX_FMT_GENERIC, w, h, stride,
+                                             IPU_ROTATE_NONE, 0, 0, 0, 0);
+               if (err < 0) {
+                       printk(KERN_ERR
+                              "mxc_pf: error initializing V output buffer\n");
+                       goto err0;
+               }
+       }
+
+       /*Setup Channel QF and BSC Params */
+       if (pf_data.mode == PF_H264_DEBLOCK) {
+               w = ((pf_data.width + 15) / 16);
+               h = (pf_data.height + 15) / 16;
+               qp_stride = w;
+               qp_size = 4 * qp_stride * h;
+               pr_debug("H264 QP width = %d, height = %d\n", w, h);
+               err = ipu_init_channel_buffer(MEM_PF_Y_MEM,
+                                             IPU_SEC_INPUT_BUFFER,
+                                             IPU_PIX_FMT_GENERIC_32, w, h,
+                                             qp_stride, IPU_ROTATE_NONE, 0, 0,
+                                             0, 0);
+               if (err < 0) {
+                       printk(KERN_ERR
+                              "mxc_pf: error initializing H264 QP buffer\n");
+                       goto err0;
+               }
+/*                w = (pf_data.width + 3) / 4; */
+               w *= 4;
+               h *= 4;
+               qp_stride = w;
+               err = ipu_init_channel_buffer(MEM_PF_U_MEM,
+                                             IPU_SEC_INPUT_BUFFER,
+                                             IPU_PIX_FMT_GENERIC, w, h,
+                                             qp_stride, IPU_ROTATE_NONE, 0, 0,
+                                             0, 0);
+               if (err < 0) {
+                       printk(KERN_ERR
+                              "mxc_pf: error initializing H264 BSB buffer\n");
+                       goto err0;
+               }
+               qp_size += qp_stride * h;
+       } else {                /* MPEG4 mode */
+
+               w = (pf_data.width + 15) / 16;
+               h = (pf_data.height + 15) / 16;
+               qp_stride = (w + 3) & ~0x3UL;
+               pr_debug("MPEG4 QP width = %d, height = %d, stride = %d\n",
+                        w, h, qp_stride);
+               err = ipu_init_channel_buffer(MEM_PF_Y_MEM,
+                                             IPU_SEC_INPUT_BUFFER,
+                                             IPU_PIX_FMT_GENERIC, w, h,
+                                             qp_stride, IPU_ROTATE_NONE, 0, 0,
+                                             0, 0);
+               if (err < 0) {
+                       printk(KERN_ERR
+                              "mxc_pf: error initializing MPEG4 QP buffer\n");
+                       goto err0;
+               }
+               qp_size = qp_stride * h;
+       }
+
+       /* Support 2 QP buffers */
+       qp_size *= 2;
+
+       if (pf_data.qp_size > qp_size)
+               qp_size = pf_data.qp_size;
+       else
+               pf_data.qp_size = qp_size;
+
+       pf_data.qp_vaddr = dma_alloc_coherent(NULL, pf_data.qp_size,
+                                             &pf_data.qp_paddr,
+                                             GFP_KERNEL | GFP_DMA);
+       if (!pf_data.qp_vaddr)
+               return -ENOMEM;
+
+       pf_init->qp_paddr = pf_data.qp_paddr;
+       pf_init->qp_size = pf_data.qp_size;
+
+       return 0;
+
+      err0:
+       return err;
+}
+
+/*!
+ * This function handles PF_IOCTL_UNINIT calls. It uninitializes the PF
+ * channels and interrupt handlers.
+ *
+ * @return      This function returns 0 on success or negative error code
+ *              on error.
+ */
+static int mxc_pf_uninit(void)
+{
+       pf_data.pf_enabled = 0;
+       ipu_disable_irq(IPU_IRQ_PF_Y_OUT_EOF);
+       ipu_disable_irq(IPU_IRQ_PF_U_OUT_EOF);
+       ipu_disable_irq(IPU_IRQ_PF_V_OUT_EOF);
+
+       ipu_disable_channel(MEM_PF_Y_MEM, true);
+       ipu_disable_channel(MEM_PF_U_MEM, true);
+       ipu_disable_channel(MEM_PF_V_MEM, true);
+       ipu_uninit_channel(MEM_PF_Y_MEM);
+       ipu_uninit_channel(MEM_PF_U_MEM);
+       ipu_uninit_channel(MEM_PF_V_MEM);
+
+       if (pf_data.qp_vaddr) {
+               dma_free_coherent(NULL, pf_data.qp_size, pf_data.qp_vaddr,
+                                 pf_data.qp_paddr);
+               pf_data.qp_vaddr = NULL;
+       }
+
+       return 0;
+}
+
+/*!
+ * This function handles PF_IOCTL_REQBUFS calls. It initializes the PF channels
+ * and channel buffers.
+ *
+ * @param       reqbufs         Input/Output Structure containing buffer mode,
+ *                              type, offset, and size. The offset and size of
+ *                              the buffer are returned for PF_MEMORY_MMAP mode.
+ *
+ * @return      This function returns 0 on success or negative error code
+ *              on error.
+ */
+static int mxc_pf_reqbufs(pf_reqbufs_params *reqbufs)
+{
+       int err;
+       uint32_t buf_size;
+       int i;
+       int alloc_cnt = 0;
+       pf_buf *buf = pf_data.buf;
+       if (reqbufs->count > PF_MAX_BUFFER_CNT) {
+               reqbufs->count = PF_MAX_BUFFER_CNT;
+       }
+       /* Deallocate mmapped buffers */
+       if (reqbufs->count == 0) {
+               for (i = 0; i < PF_MAX_BUFFER_CNT; i++) {
+                       if (buf[i].index != -1) {
+                               dma_free_coherent(NULL, buf[i].size,
+                                                 pf_data.buf_vaddr[i],
+                                                 buf[i].offset);
+                               pf_data.buf_vaddr[i] = NULL;
+                               buf[i].index = -1;
+                               buf[i].size = 0;
+                       }
+               }
+               return 0;
+       }
+
+       buf_size = (pf_data.stride * pf_data.height * 3) / 2;
+       if (reqbufs->req_size > buf_size) {
+               buf_size = reqbufs->req_size;
+               pr_debug("using requested buffer size of %d\n", buf_size);
+       } else {
+               reqbufs->req_size = buf_size;
+               pr_debug("using default buffer size of %d\n", buf_size);
+       }
+
+       for (i = 0; alloc_cnt < reqbufs->count; i++) {
+               buf[i].index = i;
+               buf[i].size = buf_size;
+               pf_data.buf_vaddr[i] = dma_alloc_coherent(NULL, buf[i].size,
+                                                         &buf[i].offset,
+                                                         GFP_KERNEL | GFP_DMA);
+               if (!pf_data.buf_vaddr[i] || !buf[i].offset) {
+                       printk(KERN_ERR
+                              "mxc_pf: unable to allocate IPU buffers.\n");
+                       err = -ENOMEM;
+                       goto err0;
+               }
+               pr_debug("Allocated buffer %d at paddr 0x%08X, vaddr %p\n",
+                        i, buf[i].offset, pf_data.buf_vaddr[i]);
+
+               alloc_cnt++;
+       }
+
+       return 0;
+      err0:
+       for (i = 0; i < alloc_cnt; i++) {
+               dma_free_coherent(NULL, buf[i].size, pf_data.buf_vaddr[i],
+                                 buf[i].offset);
+               pf_data.buf_vaddr[i] = NULL;
+               buf[i].index = -1;
+               buf[i].size = 0;
+       }
+       return err;
+}
+
+/*!
+ * This function handles PF_IOCTL_START calls. It sets the PF channel buffers
+ * addresses and starts the channels
+ *
+ * @return      This function returns 0 on success or negative error code on
+ *              error.
+ */
+static int mxc_pf_start(pf_buf *in, pf_buf *out, int qp_buf)
+{
+       int err;
+       dma_addr_t y_in_paddr;
+       dma_addr_t u_in_paddr;
+       dma_addr_t v_in_paddr;
+       dma_addr_t p1_in_paddr;
+       dma_addr_t p2_in_paddr;
+       dma_addr_t y_out_paddr;
+       dma_addr_t u_out_paddr;
+       dma_addr_t v_out_paddr;
+
+       /* H.264 requires output buffer equal to input */
+       if (pf_data.mode == PF_H264_DEBLOCK)
+               out = in;
+
+       y_in_paddr = in->offset + in->y_offset;
+       if (in->u_offset)
+               u_in_paddr = in->offset + in->u_offset;
+       else
+               u_in_paddr = y_in_paddr + (pf_data.stride * pf_data.height);
+       if (in->v_offset)
+               v_in_paddr = in->offset + in->v_offset;
+       else
+               v_in_paddr = u_in_paddr + (pf_data.stride * pf_data.height) / 4;
+       p1_in_paddr = pf_data.qp_paddr;
+       if (qp_buf)
+               p1_in_paddr += pf_data.qp_size / 2;
+
+       if (pf_data.mode == PF_H264_DEBLOCK) {
+               p2_in_paddr = p1_in_paddr +
+                   ((pf_data.width + 15) / 16) *
+                   ((pf_data.height + 15) / 16) * 4;
+       } else {
+               p2_in_paddr = 0;
+       }
+
+       pr_debug("y_in_paddr = 0x%08X\nu_in_paddr = 0x%08X\n"
+                "v_in_paddr = 0x%08X\n"
+                "qp_paddr = 0x%08X\nbsb_paddr = 0x%08X\n",
+                y_in_paddr, u_in_paddr, v_in_paddr, p1_in_paddr, p2_in_paddr);
+
+       y_out_paddr = out->offset + out->y_offset;
+       if (out->u_offset)
+               u_out_paddr = out->offset + out->u_offset;
+       else
+               u_out_paddr = y_out_paddr + (pf_data.stride * pf_data.height);
+       if (out->v_offset)
+               v_out_paddr = out->offset + out->v_offset;
+       else
+               v_out_paddr =
+                   u_out_paddr + (pf_data.stride * pf_data.height) / 4;
+
+       pr_debug("y_out_paddr = 0x%08X\nu_out_paddr = 0x%08X\n"
+                "v_out_paddr = 0x%08X\n",
+                y_out_paddr, u_out_paddr, v_out_paddr);
+
+       pf_data.done_mask = 0;
+
+       ipu_enable_irq(IPU_IRQ_PF_Y_OUT_EOF);
+       if (pf_data.mode != PF_MPEG4_DERING) {
+               ipu_enable_irq(IPU_IRQ_PF_U_OUT_EOF);
+               ipu_enable_irq(IPU_IRQ_PF_V_OUT_EOF);
+       }
+
+       err = ipu_update_channel_buffer(MEM_PF_Y_MEM, IPU_INPUT_BUFFER, 0,
+                                       y_in_paddr);
+       if (err < 0) {
+               printk(KERN_ERR "mxc_pf: error setting Y input buffer\n");
+               goto err0;
+       }
+
+       err = ipu_update_channel_buffer(MEM_PF_Y_MEM, IPU_OUTPUT_BUFFER, 0,
+                                       y_out_paddr);
+       if (err < 0) {
+               printk(KERN_ERR "mxc_pf: error setting Y output buffer\n");
+               goto err0;
+       }
+
+       if (pf_data.mode != PF_MPEG4_DERING) {
+               err =
+                   ipu_update_channel_buffer(MEM_PF_U_MEM, IPU_INPUT_BUFFER, 0,
+                                             u_in_paddr);
+               if (err < 0) {
+                       printk(KERN_ERR
+                              "mxc_pf: error setting U input buffer\n");
+                       goto err0;
+               }
+
+               err =
+                   ipu_update_channel_buffer(MEM_PF_U_MEM, IPU_OUTPUT_BUFFER,
+                                             0, u_out_paddr);
+               if (err < 0) {
+                       printk(KERN_ERR
+                              "mxc_pf: error setting U output buffer\n");
+                       goto err0;
+               }
+
+               err =
+                   ipu_update_channel_buffer(MEM_PF_V_MEM, IPU_INPUT_BUFFER, 0,
+                                             v_in_paddr);
+               if (err < 0) {
+                       printk(KERN_ERR
+                              "mxc_pf: error setting V input buffer\n");
+                       goto err0;
+               }
+
+               err =
+                   ipu_update_channel_buffer(MEM_PF_V_MEM, IPU_OUTPUT_BUFFER,
+                                             0, v_out_paddr);
+               if (err < 0) {
+                       printk(KERN_ERR
+                              "mxc_pf: error setting V output buffer\n");
+                       goto err0;
+               }
+       }
+
+       err = ipu_update_channel_buffer(MEM_PF_Y_MEM, IPU_SEC_INPUT_BUFFER, 0,
+                                       p1_in_paddr);
+       if (err < 0) {
+               printk(KERN_ERR "mxc_pf: error setting QP buffer\n");
+               goto err0;
+       }
+
+       if (pf_data.mode == PF_H264_DEBLOCK) {
+
+               err = ipu_update_channel_buffer(MEM_PF_U_MEM,
+                                               IPU_SEC_INPUT_BUFFER, 0,
+                                               p2_in_paddr);
+               if (err < 0) {
+                       printk(KERN_ERR
+                              "mxc_pf: error setting H264 BSB buffer\n");
+                       goto err0;
+               }
+               ipu_select_buffer(MEM_PF_U_MEM, IPU_SEC_INPUT_BUFFER, 0);
+       }
+
+       ipu_select_buffer(MEM_PF_Y_MEM, IPU_OUTPUT_BUFFER, 0);
+       ipu_select_buffer(MEM_PF_Y_MEM, IPU_SEC_INPUT_BUFFER, 0);
+       ipu_select_buffer(MEM_PF_Y_MEM, IPU_INPUT_BUFFER, 0);
+       if (pf_data.mode != PF_MPEG4_DERING) {
+               ipu_select_buffer(MEM_PF_U_MEM, IPU_OUTPUT_BUFFER, 0);
+               ipu_select_buffer(MEM_PF_V_MEM, IPU_OUTPUT_BUFFER, 0);
+               ipu_select_buffer(MEM_PF_U_MEM, IPU_INPUT_BUFFER, 0);
+               ipu_select_buffer(MEM_PF_V_MEM, IPU_INPUT_BUFFER, 0);
+       }
+
+       if (!pf_data.pf_enabled) {
+               pf_data.pf_enabled = 1;
+               if (pf_data.mode != PF_MPEG4_DERING) {
+                       ipu_enable_channel(MEM_PF_V_MEM);
+                       ipu_enable_channel(MEM_PF_U_MEM);
+               }
+               ipu_enable_channel(MEM_PF_Y_MEM);
+       }
+
+       return 0;
+      err0:
+       return err;
+}
+
+/*!
+ * Post Filter driver open function. This function implements the Linux
+ * file_operations.open() API function.
+ *
+ * @param       inode           struct inode *
+ *
+ * @param       filp            struct file *
+ *
+ * @return      This function returns 0 on success or negative error code on
+ *              error.
+ */
+static int mxc_pf_open(struct inode *inode, struct file *filp)
+{
+       int i;
+
+       if (open_count) {
+               return -EBUSY;
+       }
+
+       open_count++;
+
+       memset(&pf_data, 0, sizeof(pf_data));
+       for (i = 0; i < PF_MAX_BUFFER_CNT; i++) {
+               pf_data.buf[i].index = -1;
+       }
+       init_waitqueue_head(&pf_data.pf_wait);
+       init_MUTEX(&pf_data.busy_lock);
+
+       pf_data.busy_flag = 1;
+
+       ipu_request_irq(IPU_IRQ_PF_Y_OUT_EOF, mxc_pf_irq_handler,
+                       0, "mxc_ipu_pf", &pf_data);
+
+       ipu_request_irq(IPU_IRQ_PF_U_OUT_EOF, mxc_pf_irq_handler,
+                       0, "mxc_ipu_pf", &pf_data);
+
+       ipu_request_irq(IPU_IRQ_PF_V_OUT_EOF, mxc_pf_irq_handler,
+                       0, "mxc_ipu_pf", &pf_data);
+
+       ipu_disable_irq(IPU_IRQ_PF_Y_OUT_EOF);
+       ipu_disable_irq(IPU_IRQ_PF_U_OUT_EOF);
+       ipu_disable_irq(IPU_IRQ_PF_V_OUT_EOF);
+
+       return 0;
+}
+
+/*!
+ * Post Filter driver release function. This function implements the Linux
+ * file_operations.release() API function.
+ *
+ * @param       inode           struct inode *
+ *
+ * @param       filp            struct file *
+ *
+ * @return      This function returns 0 on success or negative error code on
+ *              error.
+ */
+static int mxc_pf_release(struct inode *inode, struct file *filp)
+{
+       pf_reqbufs_params req_buf;
+
+       if (open_count) {
+               mxc_pf_uninit();
+
+               /* Free any allocated buffers */
+               req_buf.count = 0;
+               mxc_pf_reqbufs(&req_buf);
+
+               ipu_free_irq(IPU_IRQ_PF_V_OUT_EOF, &pf_data);
+               ipu_free_irq(IPU_IRQ_PF_U_OUT_EOF, &pf_data);
+               ipu_free_irq(IPU_IRQ_PF_Y_OUT_EOF, &pf_data);
+               open_count--;
+       }
+       return 0;
+}
+
+/*!
+ * Post Filter driver ioctl function. This function implements the Linux
+ * file_operations.ioctl() API function.
+ *
+ * @param       inode           struct inode *
+ *
+ * @param       filp            struct file *
+ *
+ * @param       cmd             IOCTL command to handle
+ *
+ * @param       arg             Pointer to arguments for IOCTL
+ *
+ * @return      This function returns 0 on success or negative error code on
+ *              error.
+ */
+static int mxc_pf_ioctl(struct inode *inode, struct file *filp,
+                       unsigned int cmd, unsigned long arg)
+{
+       int retval = 0;
+
+       switch (cmd) {
+       case PF_IOCTL_INIT:
+               {
+                       pf_init_params pf_init;
+
+                       pr_debug("PF_IOCTL_INIT\n");
+                       if (copy_from_user(&pf_init, (void *)arg,
+                                          _IOC_SIZE(cmd))) {
+                               retval = -EFAULT;
+                               break;
+                       }
+
+                       retval = mxc_pf_init(&pf_init);
+                       if (retval < 0)
+                               break;
+                       pf_init.qp_paddr = pf_data.qp_paddr;
+                       pf_init.qp_size = pf_data.qp_size;
+
+                       /* Return size of memory allocated */
+                       if (copy_to_user((void *)arg, &pf_init, _IOC_SIZE(cmd))) {
+                               retval = -EFAULT;
+                               break;
+                       }
+
+                       pf_data.busy_flag = 0;
+                       break;
+               }
+       case PF_IOCTL_UNINIT:
+               pr_debug("PF_IOCTL_UNINIT\n");
+               retval = mxc_pf_uninit();
+               break;
+       case PF_IOCTL_REQBUFS:
+               {
+                       pf_reqbufs_params reqbufs;
+                       pr_debug("PF_IOCTL_REQBUFS\n");
+
+                       if (copy_from_user
+                           (&reqbufs, (void *)arg, _IOC_SIZE(cmd))) {
+                               retval = -EFAULT;
+                               break;
+                       }
+
+                       retval = mxc_pf_reqbufs(&reqbufs);
+
+                       /* Return size of memory allocated */
+                       if (copy_to_user((void *)arg, &reqbufs, _IOC_SIZE(cmd))) {
+                               retval = -EFAULT;
+                               break;
+                       }
+
+                       break;
+               }
+       case PF_IOCTL_QUERYBUF:
+               {
+                       pf_buf buf;
+                       pr_debug("PF_IOCTL_QUERYBUF\n");
+
+                       if (copy_from_user(&buf, (void *)arg, _IOC_SIZE(cmd))) {
+                               retval = -EFAULT;
+                               break;
+                       }
+
+                       if ((buf.index < 0) ||
+                           (buf.index >= PF_MAX_BUFFER_CNT) ||
+                           (pf_data.buf[buf.index].index != buf.index)) {
+                               retval = -EINVAL;
+                               break;
+                       }
+                       /* Return size of memory allocated */
+                       if (copy_to_user((void *)arg, &pf_data.buf[buf.index],
+                                        _IOC_SIZE(cmd))) {
+                               retval = -EFAULT;
+                               break;
+                       }
+
+                       break;
+               }
+       case PF_IOCTL_START:
+               {
+                       int index;
+                       pf_start_params start_params;
+                       pr_debug("PF_IOCTL_START\n");
+
+                       if (pf_data.busy_flag) {
+                               retval = -EBUSY;
+                               break;
+                       }
+
+                       if (copy_from_user(&start_params, (void *)arg,
+                                          _IOC_SIZE(cmd))) {
+                               retval = -EFAULT;
+                               break;
+                       }
+                       if (start_params.h264_pause_row >=
+                           ((pf_data.height + 15) / 16)) {
+                               retval = -EINVAL;
+                               break;
+                       }
+
+                       pf_data.busy_flag = 1;
+
+                       index = start_params.in.index;
+                       if ((index >= 0) && (index < PF_MAX_BUFFER_CNT)) {
+                               if (pf_data.buf[index].offset !=
+                                   start_params.in.offset) {
+                                       retval = -EINVAL;
+                                       break;
+                               }
+                       }
+
+                       index = start_params.out.index;
+                       if ((index >= 0) && (index < PF_MAX_BUFFER_CNT)) {
+                               if (pf_data.buf[index].offset !=
+                                   start_params.out.offset) {
+                                       retval = -EINVAL;
+                                       break;
+                               }
+                       }
+
+                       ipu_pf_set_pause_row(start_params.h264_pause_row);
+
+                       retval = mxc_pf_start(&start_params.in, &start_params.out,
+                                         start_params.qp_buf);
+                       /*Update y, u, v buffers in DMA Channels */
+                       if ((retval < 0)
+                               break;
+
+                       pr_debug("PF_IOCTL_START - processing started\n");
+
+                       if (!start_params.wait) {
+                               break;
+                       }
+
+                       pr_debug("PF_IOCTL_START - waiting for completion\n");
+
+                       pf_data.wait_mask = PF_WAIT_ALL;
+                       /* Fall thru to wait */
+               }
+       case PF_IOCTL_WAIT:
+               {
+                       if (!pf_data.wait_mask)
+                               pf_data.wait_mask = (u32) arg;
+
+                       if (pf_data.mode == PF_MPEG4_DERING)
+                               pf_data.wait_mask &= PF_WAIT_Y;
+
+                       if (!pf_data.wait_mask) {
+                               retval = -EINVAL;
+                               break;
+                       }
+
+                       if (!wait_event_interruptible_timeout(pf_data.pf_wait,
+                                                             ((pf_data.
+                                                               done_mask &
+                                                               pf_data.
+                                                               wait_mask) ==
+                                                              pf_data.
+                                                              wait_mask),
+                                                             1 * HZ)) {
+                               pr_debug
+                                   ("PF_IOCTL_WAIT: timeout, done_mask = %d\n",
+                                    pf_data.done_mask);
+                               retval = -ETIME;
+                               break;
+                       } else if (signal_pending(current)) {
+                               pr_debug("PF_IOCTL_WAIT: interrupt received\n");
+                               retval = -ERESTARTSYS;
+                               break;
+                       }
+                       pf_data.busy_flag = 0;
+                       pf_data.wait_mask = 0;
+
+                       pr_debug("PF_IOCTL_WAIT - finished\n");
+                       break;
+               }
+       case PF_IOCTL_RESUME:
+               {
+                       int pause_row;
+                       pr_debug("PF_IOCTL_RESUME\n");
+
+                       if (pf_data.busy_flag == 0) {
+                               retval = -EFAULT;
+                               break;
+                       }
+
+                       if (copy_from_user(&pause_row, (void *)arg,
+                                          _IOC_SIZE(cmd))) {
+                               retval = -EFAULT;
+                               break;
+                       }
+
+                       if (pause_row >= ((pf_data.height + 15) / 16)) {
+                               retval = -EINVAL;
+                               break;
+                       }
+
+                       ipu_pf_set_pause_row(pause_row);
+                       break;
+               }
+
+       default:
+               printk(KERN_ERR "ipu_pf_ioctl not supported ioctl\n");
+               retval = -1;
+       }
+
+       if (retval < 0)
+               pr_debug("return = %d\n", retval);
+       return retval;
+}
+
+/*!
+ * Post Filter driver mmap function. This function implements the Linux
+ * file_operations.mmap() API function for mapping driver buffers to user space.
+ *
+ * @param       file            struct file *
+ *
+ * @param       vma             structure vm_area_struct *
+ *
+ * @return      0 Success, EINTR busy lock error,
+ *                      ENOBUFS remap_page error.
+ */
+static int mxc_pf_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       unsigned long size = vma->vm_end - vma->vm_start;
+       int res = 0;
+
+       pr_debug("pgoff=0x%lx, start=0x%lx, end=0x%lx\n",
+                vma->vm_pgoff, vma->vm_start, vma->vm_end);
+
+       /* make this _really_ smp-safe */
+       if (down_interruptible(&pf_data.busy_lock))
+               return -EINTR;
+
+       /* make buffers write-thru cacheable */
+       vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) &
+                                    ~L_PTE_BUFFERABLE);
+
+       if (remap_pfn_range(vma, vma->vm_start,
+                           vma->vm_pgoff, size, vma->vm_page_prot)) {
+               printk(KERN_ERR "mxc_pf: remap_pfn_range failed\n");
+               res = -ENOBUFS;
+               goto mmap_exit;
+       }
+
+       vma->vm_flags &= ~VM_IO;        /* using shared anonymous pages */
+
+      mmap_exit:
+       up(&pf_data.busy_lock);
+       return res;
+}
+
+/*!
+ * Post Filter driver fsync function. This function implements the Linux
+ * file_operations.fsync() API function.
+ *
+ * The user must call fsync() before reading an output buffer. This
+ * call flushes the L1 and L2 caches
+ *
+ * @param filp          structure file *
+ *
+ * @param dentry        struct dentry *
+ *
+ * @param datasync      unused
+ *
+ * @return  status   POLLIN | POLLRDNORM
+ */
+int mxc_pf_fsync(struct file *filp, struct dentry *dentry, int datasync)
+{
+       flush_cache_all();
+       outer_flush_all();
+       return 0;
+}
+
+/*!
+ * Post Filter driver poll function. This function implements the Linux
+ * file_operations.poll() API function.
+ *
+ * @param file       structure file *
+ *
+ * @param wait       structure poll_table *
+ *
+ * @return  status   POLLIN | POLLRDNORM
+ */
+static unsigned int mxc_pf_poll(struct file *file, poll_table * wait)
+{
+       wait_queue_head_t *queue = NULL;
+       int res = POLLIN | POLLRDNORM;
+
+       if (down_interruptible(&pf_data.busy_lock))
+               return -EINTR;
+
+       queue = &pf_data.pf_wait;
+       poll_wait(file, queue, wait);
+
+       up(&pf_data.busy_lock);
+
+       return res;
+}
+
+/*!
+ * File operation structure functions pointers.
+ */
+static struct file_operations mxc_pf_fops = {
+       .owner = THIS_MODULE,
+       .open = mxc_pf_open,
+       .release = mxc_pf_release,
+       .ioctl = mxc_pf_ioctl,
+       .poll = mxc_pf_poll,
+       .mmap = mxc_pf_mmap,
+       .fsync = mxc_pf_fsync,
+};
+
+static int mxc_pf_major;
+
+/*!
+ * Post Filter driver module initialization function.
+ */
+int mxc_pf_dev_init(void)
+{
+       int ret = 0;
+       struct device *temp_class;
+
+       mxc_pf_major = register_chrdev(0, "mxc_ipu_pf", &mxc_pf_fops);
+
+       if (mxc_pf_major < 0) {
+               printk(KERN_INFO "Unable to get a major for mxc_ipu_pf");
+               return mxc_pf_major;
+       }
+
+       mxc_pf_class = class_create(THIS_MODULE, "mxc_ipu_pf");
+       if (IS_ERR(mxc_pf_class)) {
+               printk(KERN_ERR "Error creating mxc_ipu_pf class.\n");
+               ret = PTR_ERR(mxc_pf_class);
+               goto err_out1;
+       }
+
+       temp_class = device_create(mxc_pf_class, NULL, MKDEV(mxc_pf_major, 0), NULL,
+                                  "mxc_ipu_pf");
+       if (IS_ERR(temp_class)) {
+               printk(KERN_ERR "Error creating mxc_ipu_pf class device.\n");
+               ret = PTR_ERR(temp_class);
+               goto err_out2;
+       }
+
+       printk(KERN_INFO "IPU Post-filter loading\n");
+
+       return 0;
+
+      err_out2:
+       class_destroy(mxc_pf_class);
+      err_out1:
+       unregister_chrdev(mxc_pf_major, "mxc_ipu_pf");
+       return ret;
+}
+
+/*!
+ * Post Filter driver module exit function.
+ */
+static void mxc_pf_exit(void)
+{
+       if (mxc_pf_major > 0) {
+               device_destroy(mxc_pf_class, MKDEV(mxc_pf_major, 0));
+               class_destroy(mxc_pf_class);
+               unregister_chrdev(mxc_pf_major, "mxc_ipu_pf");
+       }
+}
+
+module_init(mxc_pf_dev_init);
+module_exit(mxc_pf_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC MPEG4/H.264 Postfilter Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/ipu3/Kconfig b/drivers/mxc/ipu3/Kconfig
new file mode 100644 (file)
index 0000000..0ae0ffa
--- /dev/null
@@ -0,0 +1,5 @@
+config MXC_IPU_V3
+       bool
+
+config MXC_IPU_V3D
+       bool
diff --git a/drivers/mxc/ipu3/Makefile b/drivers/mxc/ipu3/Makefile
new file mode 100644 (file)
index 0000000..aa3e7b1
--- /dev/null
@@ -0,0 +1,4 @@
+obj-$(CONFIG_MXC_IPU_V3) = mxc_ipu.o
+
+mxc_ipu-objs := ipu_common.o ipu_ic.o ipu_disp.o ipu_capture.o ipu_device.o ipu_calc_stripes_sizes.o
+
diff --git a/drivers/mxc/ipu3/ipu_calc_stripes_sizes.c b/drivers/mxc/ipu3/ipu_calc_stripes_sizes.c
new file mode 100644 (file)
index 0000000..473772a
--- /dev/null
@@ -0,0 +1,375 @@
+/*
+ * 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
+ */
+
+/*
+ * @file ipu_calc_stripes_sizes.c
+ *
+ * @brief IPU IC functions
+ *
+ * @ingroup IPU
+ */
+
+#include <linux/module.h>
+#include <linux/ipu.h>
+#include <asm/div64.h>
+
+#define BPP_32 0
+#define BPP_16 3
+#define BPP_8 5
+#define BPP_24 1
+#define BPP_12 4
+#define BPP_18 2
+
+static u64 _do_div(u64 a, u32 b)
+{
+       u64 div;
+       div = a;
+       do_div(div, b);
+       return div;
+}
+
+static u32 truncate(u32 up, /* 0: down; else: up */
+                                       u64 a, /* must be non-negative */
+                                       u32 b)
+{
+       u32 d;
+       u64 div;
+       div = _do_div(a, b);
+       d = b * (div >> 32);
+       if (up && (a > (((u64)d) << 32)))
+               return d+b;
+       else
+               return d;
+}
+
+static unsigned int f_calc(unsigned int pfs, unsigned int bpp, unsigned int *write)
+{/* return input_f */
+       unsigned int f_calculated = 0;
+       switch (pfs) {
+       case IPU_PIX_FMT_YVU422P:
+       case IPU_PIX_FMT_YUV422P:
+       case IPU_PIX_FMT_YUV420P2:
+       case IPU_PIX_FMT_YUV420P:
+       case IPU_PIX_FMT_YVU420P:
+               f_calculated = 16;
+               break;
+
+       case IPU_PIX_FMT_NV12:
+               f_calculated = 8;
+               break;
+
+       default:
+               f_calculated = 0;
+               break;
+
+       }
+       if (!f_calculated) {
+               switch (bpp) {
+               case BPP_32:
+                       f_calculated = 2;
+                       break;
+
+               case BPP_16:
+                       f_calculated = 4;
+                       break;
+
+               case BPP_8:
+               case BPP_24:
+                       f_calculated = 8;
+                       break;
+
+               case BPP_12:
+                       f_calculated = 16;
+                       break;
+
+               case BPP_18:
+                       f_calculated = 32;
+                       break;
+
+               default:
+                       f_calculated = 0;
+                       break;
+                       }
+               }
+       return f_calculated;
+}
+
+
+static unsigned int m_calc(unsigned int pfs)
+{
+       unsigned int m_calculated = 0;
+       switch (pfs) {
+       case IPU_PIX_FMT_YUV420P2:
+       case IPU_PIX_FMT_YUV420P:
+       case IPU_PIX_FMT_YVU422P:
+       case IPU_PIX_FMT_YUV422P:
+       case IPU_PIX_FMT_YVU420P:
+       case IPU_PIX_FMT_NV12:
+               m_calculated = 8;
+               break;
+
+       case IPU_PIX_FMT_YUYV:
+       case IPU_PIX_FMT_UYVY:
+               m_calculated = 2;
+               break;
+
+       default:
+               m_calculated = 1;
+               break;
+
+       }
+       return m_calculated;
+}
+
+
+/* Stripe parameters calculator */
+/**************************************************************************
+Notes:
+MSW = the maximal width allowed for a stripe
+       i.MX31: 720, i.MX35: 800, i.MX37/51/53: 1024
+cirr = the maximal inverse resizing ratio for which overlap in the input
+       is requested; typically cirr~2
+equal_stripes:
+       0: each stripe is allowed to have independent parameters
+               for maximal image quality
+       1: the stripes are requested to have identical parameters
+       (except the base address), for maximal performance
+If performance is the top priority (above image quality)
+       Avoid overlap, by setting CIRR = 0
+               This will also force effectively identical_stripes = 1
+       Choose IF & OF that corresponds to the same IOX/SX for both stripes
+       Choose IFW & OFW such that
+       IFW/IM, IFW/IF, OFW/OM, OFW/OF are even integers
+       The function returns an error status:
+       0: no error
+       1: invalid input parameters -> aborted without result
+               Valid parameters should satisfy the following conditions
+               IFW <= OFW, otherwise downsizing is required
+                                        - which is not supported yet
+               4 <= IFW,OFW, so some interpolation may be needed even without overlap
+               IM, OM, IF, OF should not vanish
+               2*IF <= IFW
+               so the frame can be split to two equal stripes, even without overlap
+               2*(OF+IF/irr_opt) <= OFW
+               so a valid positive INW exists even for equal stripes
+               OF <= MSW, otherwise, the left stripe cannot be sufficiently large
+               MSW < OFW, so splitting to stripes is required
+               OFW <= 2*MSW, so two stripes are sufficient
+               (this also implies that 2<=MSW)
+       2: OF is not a multiple of OM - not fully-supported yet
+       Output is produced but OW is not guaranited to be a multiple of OM
+       4: OFW reduced to be a multiple of OM
+       8: CIRR > 1: truncated to 1
+       Overlap is not supported (and not needed) y for upsizing)
+**************************************************************************/
+int ipu_calc_stripes_sizes(const unsigned int input_frame_width,
+                          /* input frame width;>1 */
+                          unsigned int output_frame_width, /* output frame width; >1 */
+                          const unsigned int maximal_stripe_width,
+                          /* the maximal width allowed for a stripe */
+                          const unsigned long long cirr, /* see above */
+                          const unsigned int equal_stripes, /* see above */
+                          u32 input_pixelformat,/* pixel format after of read channel*/
+                          u32 output_pixelformat,/* pixel format after of write channel*/
+                          struct stripe_param *left,
+                          struct stripe_param *right)
+{
+       const unsigned int irr_frac_bits = 13;
+       const unsigned long irr_steps = 1 << irr_frac_bits;
+       const u64 dirr = ((u64)1) << (32 - 2);
+       /* The maximum relative difference allowed between the irrs */
+       const u64 cr = ((u64)4) << 32;
+       /* The importance ratio between the two terms in the cost function below */
+
+       unsigned int status;
+       unsigned int temp;
+       unsigned int onw_min;
+       unsigned int inw, onw, inw_best = 0;
+       /* number of pixels in the left stripe NOT hidden by the right stripe */
+       u64 irr_opt; /* the optimal inverse resizing ratio */
+       u64 rr_opt; /* the optimal resizing ratio = 1/irr_opt*/
+       u64 dinw; /* the misalignment between the stripes */
+       /* (measured in units of input columns) */
+       u64 difwl, difwr;
+       /* The number of input columns not reflected in the output */
+       /* the resizing ratio used for the right stripe is */
+       /*   left->irr and right->irr respectively */
+       u64 cost, cost_min;
+       u64 div; /* result of division */
+
+       unsigned int input_m, input_f, output_m, output_f; /* parameters for upsizing by stripes */
+
+       status = 0;
+
+       /* M, F calculations */
+       /* read back pfs from params */
+
+       input_f = f_calc(input_pixelformat, 0, NULL);
+       input_m = 16;
+       /* BPP should be used in the out_F calc */
+       /* Temporarily not used */
+       /* out_F = F_calc(idmac->pfs, idmac->bpp, NULL); */
+
+       output_f = 16;
+       output_m = m_calc(output_pixelformat);
+
+
+       if ((input_frame_width < 4) || (output_frame_width < 4))
+               return 1;
+
+       irr_opt = _do_div((((u64)(input_frame_width - 1)) << 32),
+                         (output_frame_width - 1));
+       rr_opt = _do_div((((u64)(output_frame_width - 1)) << 32),
+                        (input_frame_width - 1));
+
+       if ((input_m == 0) || (output_m == 0) || (input_f == 0) || (output_f == 0)
+           || (input_frame_width < (2 * input_f))
+           || ((((u64)output_frame_width) << 32) <
+               (2 * ((((u64)output_f) << 32) + (input_f * rr_opt))))
+           || (maximal_stripe_width < output_f)
+           || (output_frame_width <= maximal_stripe_width)
+           || ((2 * maximal_stripe_width) < output_frame_width))
+               return 1;
+
+       if (output_f % output_m)
+               status += 2;
+
+       temp = truncate(0, (((u64)output_frame_width) << 32), output_m);
+       if (temp < output_frame_width) {
+               output_frame_width = temp;
+               status += 4;
+       }
+
+       if (equal_stripes) {
+               if ((irr_opt > cirr) /* overlap in the input is not requested */
+                   && ((input_frame_width % (input_m << 1)) == 0)
+                   && ((input_frame_width % (input_f << 1)) == 0)
+                   && ((output_frame_width % (output_m << 1)) == 0)
+                   && ((output_frame_width % (output_f << 1)) == 0)) {
+                       /* without overlap */
+                       left->input_width = right->input_width = right->input_column =
+                               input_frame_width >> 1;
+                       left->output_width = right->output_width = right->output_column =
+                               output_frame_width >> 1;
+                       left->input_column = 0;
+                       left->output_column = 0;
+                       div = _do_div(((((u64)irr_steps) << 32) *
+                                      (right->input_width - 1)), (right->output_width - 1));
+                       left->irr = right->irr = truncate(0, div, 1);
+               } else { /* with overlap */
+                       onw = truncate(0, (((u64)output_frame_width - 1) << 32) >> 1,
+                                      output_f);
+                       inw = truncate(0, onw * irr_opt, input_f);
+                       /* this is the maximal inw which allows the same resizing ratio */
+                       /* in both stripes */
+                       onw = truncate(1, (inw * rr_opt), output_f);
+                       div = _do_div((((u64)(irr_steps * inw)) <<
+                                      32), onw);
+                       left->irr = right->irr = truncate(0, div, 1);
+                       left->output_width = right->output_width =
+                               output_frame_width - onw;
+                       /* These are valid assignments for output_width, */
+                       /* assuming output_f is a multiple of output_m */
+                       div = (((u64)(left->output_width-1) * (left->irr)) << 32);
+                       div = (((u64)1) << 32) + _do_div(div, irr_steps);
+
+                       left->input_width = right->input_width = truncate(1, div, input_m);
+
+                       div = _do_div((((u64)((right->output_width - 1) * right->irr)) <<
+                                      32), irr_steps);
+                       difwr = (((u64)(input_frame_width - 1 - inw)) << 32) - div;
+                       div = _do_div((difwr + (((u64)input_f) << 32)), 2);
+                       left->input_column = truncate(0, div, input_f);
+
+
+                       /* This splits the truncated input columns evenly */
+                       /*    between the left and right margins */
+                       right->input_column = left->input_column + inw;
+                       left->output_column = 0;
+                       right->output_column = onw;
+               }
+       } else { /* independent stripes */
+               onw_min = output_frame_width - maximal_stripe_width;
+               /* onw is a multiple of output_f, in the range */
+               /* [max(output_f,output_frame_width-maximal_stripe_width),*/
+               /*min(output_frame_width-2,maximal_stripe_width)] */
+               /* definitely beyond the cost of any valid setting */
+               cost_min = (((u64)input_frame_width) << 32) + cr;
+               onw = truncate(0, ((u64)maximal_stripe_width), output_f);
+               if (output_frame_width - onw == 1)
+                       onw -= output_f; /*  => onw and output_frame_width-1-onw are positive */
+               inw = truncate(0, onw * irr_opt, input_f);
+               /* this is the maximal inw which allows the same resizing ratio */
+               /* in both stripes */
+               onw = truncate(1, inw * rr_opt, output_f);
+               do {
+                       div = _do_div((((u64)(irr_steps * inw)) << 32), onw);
+                       left->irr = truncate(0, div, 1);
+                       div = _do_div((((u64)(onw * left->irr)) << 32),
+                                     irr_steps);
+                       dinw = (((u64)inw) << 32) - div;
+
+                       div = _do_div((((u64)((output_frame_width - 1 - onw) * left->irr)) <<
+                                      32), irr_steps);
+
+                       difwl = (((u64)(input_frame_width - 1 - inw)) << 32) - div;
+
+                       cost = difwl + (((u64)(cr * dinw)) >> 32);
+
+                       if (cost < cost_min) {
+                               inw_best = inw;
+                               cost_min = cost;
+                       }
+
+                       inw -= input_f;
+                       onw = truncate(1, inw * rr_opt, output_f);
+                       /* This is the minimal onw which allows the same resizing ratio */
+                       /*     in both stripes */
+               } while (onw >= onw_min);
+
+               inw = inw_best;
+               onw = truncate(1, inw * rr_opt, output_f);
+               div = _do_div((((u64)(irr_steps * inw)) << 32), onw);
+               left->irr = truncate(0, div, 1);
+
+               left->output_width = onw;
+               right->output_width = output_frame_width - onw;
+               /* These are valid assignments for output_width, */
+               /* assuming output_f is a multiple of output_m */
+               left->input_width = truncate(1, ((u64)(inw + 1)) << 32, input_m);
+               right->input_width = truncate(1, ((u64)(input_frame_width - inw)) <<
+                                             32, input_m);
+
+               div = _do_div((((u64)(irr_steps * (input_frame_width - 1 - inw))) <<
+                              32), (right->output_width - 1));
+               right->irr = truncate(0, div, 1);
+               temp = truncate(0, ((u64)left->irr) * ((((u64)1) << 32) + dirr), 1);
+               if (temp < right->irr)
+                       right->irr = temp;
+               div = _do_div(((u64)((right->output_width - 1) * right->irr) <<
+                              32), irr_steps);
+               difwr = (u64)(input_frame_width - 1 - inw) - div;
+
+
+               div = _do_div((difwr + (((u64)input_f) << 32)), 2);
+               left->input_column = truncate(0, div, input_f);
+
+               /* This splits the truncated input columns evenly */
+               /*    between the left and right margins */
+               right->input_column = left->input_column + inw;
+               left->output_column = 0;
+               right->output_column = onw;
+       }
+       return status;
+}
+EXPORT_SYMBOL(ipu_calc_stripes_sizes);
diff --git a/drivers/mxc/ipu3/ipu_capture.c b/drivers/mxc/ipu3/ipu_capture.c
new file mode 100644 (file)
index 0000000..32d3522
--- /dev/null
@@ -0,0 +1,835 @@
+/*
+ * 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 ipu_capture.c
+ *
+ * @brief IPU capture dase functions
+ *
+ * @ingroup IPU
+ */
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/ipu.h>
+#include <linux/clk.h>
+
+#include "ipu_prv.h"
+#include "ipu_regs.h"
+
+/*!
+ * ipu_csi_init_interface
+ *     Sets initial values for the CSI registers.
+ *     The width and height of the sensor and the actual frame size will be
+ *     set to the same values.
+ * @param      width           Sensor width
+ * @param       height         Sensor height
+ * @param       pixel_fmt      pixel format
+ * @param       cfg_param      ipu_csi_signal_cfg_t structure
+ * @param       csi             csi 0 or csi 1
+ *
+ * @return      0 for success, -EINVAL for error
+ */
+int32_t
+ipu_csi_init_interface(uint16_t width, uint16_t height, uint32_t pixel_fmt,
+       ipu_csi_signal_cfg_t cfg_param)
+{
+       uint32_t data = 0;
+       uint32_t csi = cfg_param.csi;
+       unsigned long lock_flags;
+
+       /* Set SENS_DATA_FORMAT bits (8, 9 and 10)
+          RGB or YUV444 is 0 which is current value in data so not set
+          explicitly
+          This is also the default value if attempts are made to set it to
+          something invalid. */
+       switch (pixel_fmt) {
+       case IPU_PIX_FMT_YUYV:
+               cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV;
+               break;
+       case IPU_PIX_FMT_UYVY:
+               cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY;
+               break;
+       case IPU_PIX_FMT_RGB24:
+       case IPU_PIX_FMT_BGR24:
+               cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_RGB_YUV444;
+               break;
+       case IPU_PIX_FMT_GENERIC:
+               cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
+               break;
+       case IPU_PIX_FMT_RGB565:
+               cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_RGB565;
+               break;
+       case IPU_PIX_FMT_RGB555:
+               cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_RGB555;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* Set the CSI_SENS_CONF register remaining fields */
+       data |= cfg_param.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT |
+               cfg_param.data_fmt << CSI_SENS_CONF_DATA_FMT_SHIFT |
+               cfg_param.data_pol << CSI_SENS_CONF_DATA_POL_SHIFT |
+               cfg_param.Vsync_pol << CSI_SENS_CONF_VSYNC_POL_SHIFT |
+               cfg_param.Hsync_pol << CSI_SENS_CONF_HSYNC_POL_SHIFT |
+               cfg_param.pixclk_pol << CSI_SENS_CONF_PIX_CLK_POL_SHIFT |
+               cfg_param.ext_vsync << CSI_SENS_CONF_EXT_VSYNC_SHIFT |
+               cfg_param.clk_mode << CSI_SENS_CONF_SENS_PRTCL_SHIFT |
+               cfg_param.pack_tight << CSI_SENS_CONF_PACK_TIGHT_SHIFT |
+               cfg_param.force_eof << CSI_SENS_CONF_FORCE_EOF_SHIFT |
+               cfg_param.data_en_pol << CSI_SENS_CONF_DATA_EN_POL_SHIFT;
+
+       if (g_ipu_clk_enabled == false) {
+               g_ipu_clk_enabled = true;
+               clk_enable(g_ipu_clk);
+       }
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       __raw_writel(data, CSI_SENS_CONF(csi));
+
+       /* Setup sensor frame size */
+       __raw_writel((width - 1) | (height - 1) << 16, CSI_SENS_FRM_SIZE(csi));
+
+       /* Set CCIR registers */
+       if (cfg_param.clk_mode == IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE) {
+               __raw_writel(0x40030, CSI_CCIR_CODE_1(csi));
+               __raw_writel(0xFF0000, CSI_CCIR_CODE_3(csi));
+       } else if (cfg_param.clk_mode == IPU_CSI_CLK_MODE_CCIR656_INTERLACED) {
+               if (width == 720 && height == 625) {
+                       /* PAL case */
+                       /*
+                        * Field0BlankEnd = 0x6, Field0BlankStart = 0x2,
+                        * Field0ActiveEnd = 0x4, Field0ActiveStart = 0
+                        */
+                       __raw_writel(0x40596, CSI_CCIR_CODE_1(csi));
+                       /*
+                        * Field1BlankEnd = 0x7, Field1BlankStart = 0x3,
+                        * Field1ActiveEnd = 0x5, Field1ActiveStart = 0x1
+                        */
+                       __raw_writel(0xD07DF, CSI_CCIR_CODE_2(csi));
+                       __raw_writel(0xFF0000, CSI_CCIR_CODE_3(csi));
+               } else if (width == 720 && height == 525) {
+                       /* NTSC case */
+                       /*
+                        * Field0BlankEnd = 0x7, Field0BlankStart = 0x3,
+                        * Field0ActiveEnd = 0x5, Field0ActiveStart = 0x1
+                        */
+                       __raw_writel(0xD07DF, CSI_CCIR_CODE_1(csi));
+                       /*
+                        * Field1BlankEnd = 0x6, Field1BlankStart = 0x2,
+                        * Field1ActiveEnd = 0x4, Field1ActiveStart = 0
+                        */
+                       __raw_writel(0x40596, CSI_CCIR_CODE_2(csi));
+                       __raw_writel(0xFF0000, CSI_CCIR_CODE_3(csi));
+               } else {
+                       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+                       dev_err(g_ipu_dev, "Unsupported CCIR656 interlaced "
+                                       "video mode\n");
+                       return -EINVAL;
+               }
+               _ipu_csi_ccir_err_detection_enable(csi);
+       } else if ((cfg_param.clk_mode ==
+                       IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR) ||
+               (cfg_param.clk_mode ==
+                       IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR) ||
+               (cfg_param.clk_mode ==
+                       IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR) ||
+               (cfg_param.clk_mode ==
+                       IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR)) {
+               __raw_writel(0x40030, CSI_CCIR_CODE_1(csi));
+               __raw_writel(0xFF0000, CSI_CCIR_CODE_3(csi));
+               _ipu_csi_ccir_err_detection_enable(csi);
+       } else if ((cfg_param.clk_mode == IPU_CSI_CLK_MODE_GATED_CLK) ||
+                  (cfg_param.clk_mode == IPU_CSI_CLK_MODE_NONGATED_CLK)) {
+               _ipu_csi_ccir_err_detection_disable(csi);
+       }
+
+       dev_dbg(g_ipu_dev, "CSI_SENS_CONF = 0x%08X\n",
+               __raw_readl(CSI_SENS_CONF(csi)));
+       dev_dbg(g_ipu_dev, "CSI_ACT_FRM_SIZE = 0x%08X\n",
+               __raw_readl(CSI_ACT_FRM_SIZE(csi)));
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+       return 0;
+}
+EXPORT_SYMBOL(ipu_csi_init_interface);
+
+/*!
+ * ipu_csi_get_sensor_protocol
+ *
+ * @param      csi         csi 0 or csi 1
+ *
+ * @return     Returns sensor protocol
+ */
+int32_t ipu_csi_get_sensor_protocol(uint32_t csi)
+{
+       return (__raw_readl(CSI_SENS_CONF(csi)) &
+               CSI_SENS_CONF_SENS_PRTCL_MASK) >>
+               CSI_SENS_CONF_SENS_PRTCL_SHIFT;
+}
+EXPORT_SYMBOL(ipu_csi_get_sensor_protocol);
+
+/*!
+ * _ipu_csi_mclk_set
+ *
+ * @param      pixel_clk   desired pixel clock frequency in Hz
+ * @param      csi         csi 0 or csi 1
+ *
+ * @return     Returns 0 on success or negative error code on fail
+ */
+int _ipu_csi_mclk_set(uint32_t pixel_clk, uint32_t csi)
+{
+       uint32_t temp;
+       uint32_t div_ratio;
+
+       div_ratio = (clk_get_rate(g_ipu_clk) / pixel_clk) - 1;
+
+       if (div_ratio > 0xFF || div_ratio < 0) {
+               dev_dbg(g_ipu_dev, "The value of pixel_clk extends normal range\n");
+               return -EINVAL;
+       }
+
+       temp = __raw_readl(CSI_SENS_CONF(csi));
+       temp &= ~CSI_SENS_CONF_DIVRATIO_MASK;
+       __raw_writel(temp | (div_ratio << CSI_SENS_CONF_DIVRATIO_SHIFT),
+                       CSI_SENS_CONF(csi));
+
+       return 0;
+}
+
+/*!
+ * ipu_csi_enable_mclk
+ *
+ * @param      csi         csi 0 or csi 1
+ * @param       flag        true to enable mclk, false to disable mclk
+ * @param       wait        true to wait 100ms make clock stable, false not wait
+ *
+ * @return      Returns 0 on success
+ */
+int ipu_csi_enable_mclk(int csi, bool flag, bool wait)
+{
+       if (flag) {
+               clk_enable(g_csi_clk[csi]);
+               if (wait == true)
+                       msleep(10);
+       } else {
+               clk_disable(g_csi_clk[csi]);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(ipu_csi_enable_mclk);
+
+/*!
+ * ipu_csi_get_window_size
+ *
+ * @param      width   pointer to window width
+ * @param      height  pointer to window height
+ * @param      csi     csi 0 or csi 1
+ */
+void ipu_csi_get_window_size(uint32_t *width, uint32_t *height, uint32_t csi)
+{
+       uint32_t reg;
+       unsigned long lock_flags;
+
+       if (g_ipu_clk_enabled == false) {
+               g_ipu_clk_enabled = true;
+               clk_enable(g_ipu_clk);
+       }
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       reg = __raw_readl(CSI_ACT_FRM_SIZE(csi));
+       *width = (reg & 0xFFFF) + 1;
+       *height = (reg >> 16 & 0xFFFF) + 1;
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+}
+EXPORT_SYMBOL(ipu_csi_get_window_size);
+
+/*!
+ * ipu_csi_set_window_size
+ *
+ * @param      width   window width
+ * @param       height window height
+ * @param       csi    csi 0 or csi 1
+ */
+void ipu_csi_set_window_size(uint32_t width, uint32_t height, uint32_t csi)
+{
+       unsigned long lock_flags;
+
+       if (g_ipu_clk_enabled == false) {
+               g_ipu_clk_enabled = true;
+               clk_enable(g_ipu_clk);
+       }
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       __raw_writel((width - 1) | (height - 1) << 16, CSI_ACT_FRM_SIZE(csi));
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+}
+EXPORT_SYMBOL(ipu_csi_set_window_size);
+
+/*!
+ * ipu_csi_set_window_pos
+ *
+ * @param       left   uint32 window x start
+ * @param       top    uint32 window y start
+ * @param       csi    csi 0 or csi 1
+ */
+void ipu_csi_set_window_pos(uint32_t left, uint32_t top, uint32_t csi)
+{
+       uint32_t temp;
+       unsigned long lock_flags;
+
+       if (g_ipu_clk_enabled == false) {
+               g_ipu_clk_enabled = true;
+               clk_enable(g_ipu_clk);
+       }
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       temp = __raw_readl(CSI_OUT_FRM_CTRL(csi));
+       temp &= ~(CSI_HSC_MASK | CSI_VSC_MASK);
+       temp |= ((top << CSI_VSC_SHIFT) | (left << CSI_HSC_SHIFT));
+       __raw_writel(temp, CSI_OUT_FRM_CTRL(csi));
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+}
+EXPORT_SYMBOL(ipu_csi_set_window_pos);
+
+/*!
+ * _ipu_csi_horizontal_downsize_enable
+ *     Enable horizontal downsizing(decimation) by 2.
+ *
+ * @param      csi     csi 0 or csi 1
+ */
+void _ipu_csi_horizontal_downsize_enable(uint32_t csi)
+{
+       uint32_t temp;
+       unsigned long lock_flags;
+
+       if (g_ipu_clk_enabled == false) {
+               g_ipu_clk_enabled = true;
+               clk_enable(g_ipu_clk);
+       }
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       temp = __raw_readl(CSI_OUT_FRM_CTRL(csi));
+       temp |= CSI_HORI_DOWNSIZE_EN;
+       __raw_writel(temp, CSI_OUT_FRM_CTRL(csi));
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+}
+
+/*!
+ * _ipu_csi_horizontal_downsize_disable
+ *     Disable horizontal downsizing(decimation) by 2.
+ *
+ * @param      csi     csi 0 or csi 1
+ */
+void _ipu_csi_horizontal_downsize_disable(uint32_t csi)
+{
+       uint32_t temp;
+       unsigned long lock_flags;
+
+       if (g_ipu_clk_enabled == false) {
+               g_ipu_clk_enabled = true;
+               clk_enable(g_ipu_clk);
+       }
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       temp = __raw_readl(CSI_OUT_FRM_CTRL(csi));
+       temp &= ~CSI_HORI_DOWNSIZE_EN;
+       __raw_writel(temp, CSI_OUT_FRM_CTRL(csi));
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+}
+
+/*!
+ * _ipu_csi_vertical_downsize_enable
+ *     Enable vertical downsizing(decimation) by 2.
+ *
+ * @param      csi     csi 0 or csi 1
+ */
+void _ipu_csi_vertical_downsize_enable(uint32_t csi)
+{
+       uint32_t temp;
+       unsigned long lock_flags;
+
+       if (g_ipu_clk_enabled == false) {
+               g_ipu_clk_enabled = true;
+               clk_enable(g_ipu_clk);
+       }
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       temp = __raw_readl(CSI_OUT_FRM_CTRL(csi));
+       temp |= CSI_VERT_DOWNSIZE_EN;
+       __raw_writel(temp, CSI_OUT_FRM_CTRL(csi));
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+}
+
+/*!
+ * _ipu_csi_vertical_downsize_disable
+ *     Disable vertical downsizing(decimation) by 2.
+ *
+ * @param      csi     csi 0 or csi 1
+ */
+void _ipu_csi_vertical_downsize_disable(uint32_t csi)
+{
+       uint32_t temp;
+       unsigned long lock_flags;
+
+       if (g_ipu_clk_enabled == false) {
+               g_ipu_clk_enabled = true;
+               clk_enable(g_ipu_clk);
+       }
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       temp = __raw_readl(CSI_OUT_FRM_CTRL(csi));
+       temp &= ~CSI_VERT_DOWNSIZE_EN;
+       __raw_writel(temp, CSI_OUT_FRM_CTRL(csi));
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+}
+
+/*!
+ * ipu_csi_set_test_generator
+ *
+ * @param      active       1 for active and 0 for inactive
+ * @param       r_value             red value for the generated pattern of even pixel
+ * @param       g_value      green value for the generated pattern of even
+ *                          pixel
+ * @param       b_value      blue value for the generated pattern of even pixel
+ * @param      pixel_clk   desired pixel clock frequency in Hz
+ * @param       csi          csi 0 or csi 1
+ */
+void ipu_csi_set_test_generator(bool active, uint32_t r_value,
+       uint32_t g_value, uint32_t b_value, uint32_t pix_clk, uint32_t csi)
+{
+       uint32_t temp;
+       unsigned long lock_flags;
+
+       if (g_ipu_clk_enabled == false) {
+               g_ipu_clk_enabled = true;
+               clk_enable(g_ipu_clk);
+       }
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       temp = __raw_readl(CSI_TST_CTRL(csi));
+
+       if (active == false) {
+               temp &= ~CSI_TEST_GEN_MODE_EN;
+               __raw_writel(temp, CSI_TST_CTRL(csi));
+       } else {
+               /* Set sensb_mclk div_ratio*/
+               _ipu_csi_mclk_set(pix_clk, csi);
+
+               temp &= ~(CSI_TEST_GEN_R_MASK | CSI_TEST_GEN_G_MASK |
+                       CSI_TEST_GEN_B_MASK);
+               temp |= CSI_TEST_GEN_MODE_EN;
+               temp |= (r_value << CSI_TEST_GEN_R_SHIFT) |
+                       (g_value << CSI_TEST_GEN_G_SHIFT) |
+                       (b_value << CSI_TEST_GEN_B_SHIFT);
+               __raw_writel(temp, CSI_TST_CTRL(csi));
+       }
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+}
+EXPORT_SYMBOL(ipu_csi_set_test_generator);
+
+/*!
+ * _ipu_csi_ccir_err_detection_en
+ *     Enable error detection and correction for
+ *     CCIR interlaced mode with protection bit.
+ *
+ * @param      csi     csi 0 or csi 1
+ */
+void _ipu_csi_ccir_err_detection_enable(uint32_t csi)
+{
+       uint32_t temp;
+
+       if (g_ipu_clk_enabled == false) {
+               g_ipu_clk_enabled = true;
+               clk_enable(g_ipu_clk);
+       }
+
+       temp = __raw_readl(CSI_CCIR_CODE_1(csi));
+       temp |= CSI_CCIR_ERR_DET_EN;
+       __raw_writel(temp, CSI_CCIR_CODE_1(csi));
+}
+
+/*!
+ * _ipu_csi_ccir_err_detection_disable
+ *     Disable error detection and correction for
+ *     CCIR interlaced mode with protection bit.
+ *
+ * @param      csi     csi 0 or csi 1
+ */
+void _ipu_csi_ccir_err_detection_disable(uint32_t csi)
+{
+       uint32_t temp;
+
+       if (g_ipu_clk_enabled == false) {
+               g_ipu_clk_enabled = true;
+               clk_enable(g_ipu_clk);
+       }
+
+       temp = __raw_readl(CSI_CCIR_CODE_1(csi));
+       temp &= ~CSI_CCIR_ERR_DET_EN;
+       __raw_writel(temp, CSI_CCIR_CODE_1(csi));
+}
+
+/*!
+ * _ipu_csi_set_mipi_di
+ *
+ * @param      num     MIPI data identifier 0-3 handled by CSI
+ * @param      di_val  data identifier value
+ * @param      csi     csi 0 or csi 1
+ *
+ * @return     Returns 0 on success or negative error code on fail
+ */
+int _ipu_csi_set_mipi_di(uint32_t num, uint32_t di_val, uint32_t csi)
+{
+       uint32_t temp;
+       int retval = 0;
+       unsigned long lock_flags;
+
+       if (di_val > 0xFFL) {
+               retval = -EINVAL;
+               goto err;
+       }
+
+       if (g_ipu_clk_enabled == false) {
+               g_ipu_clk_enabled = true;
+               clk_enable(g_ipu_clk);
+       }
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       temp = __raw_readl(CSI_MIPI_DI(csi));
+
+       switch (num) {
+       case IPU_CSI_MIPI_DI0:
+               temp &= ~CSI_MIPI_DI0_MASK;
+               temp |= (di_val << CSI_MIPI_DI0_SHIFT);
+               __raw_writel(temp, CSI_MIPI_DI(csi));
+               break;
+       case IPU_CSI_MIPI_DI1:
+               temp &= ~CSI_MIPI_DI1_MASK;
+               temp |= (di_val << CSI_MIPI_DI1_SHIFT);
+               __raw_writel(temp, CSI_MIPI_DI(csi));
+               break;
+       case IPU_CSI_MIPI_DI2:
+               temp &= ~CSI_MIPI_DI2_MASK;
+               temp |= (di_val << CSI_MIPI_DI2_SHIFT);
+               __raw_writel(temp, CSI_MIPI_DI(csi));
+               break;
+       case IPU_CSI_MIPI_DI3:
+               temp &= ~CSI_MIPI_DI3_MASK;
+               temp |= (di_val << CSI_MIPI_DI3_SHIFT);
+               __raw_writel(temp, CSI_MIPI_DI(csi));
+               break;
+       default:
+               retval = -EINVAL;
+               goto err;
+       }
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+err:
+       return retval;
+}
+
+/*!
+ * _ipu_csi_set_skip_isp
+ *
+ * @param      skip            select frames to be skipped and set the
+ *                             correspond bits to 1
+ * @param      max_ratio       number of frames in a skipping set and the
+ *                             maximum value of max_ratio is 5
+ * @param      csi             csi 0 or csi 1
+ *
+ * @return     Returns 0 on success or negative error code on fail
+ */
+int _ipu_csi_set_skip_isp(uint32_t skip, uint32_t max_ratio, uint32_t csi)
+{
+       uint32_t temp;
+       int retval = 0;
+       unsigned long lock_flags;
+
+       if (max_ratio > 5) {
+               retval = -EINVAL;
+               goto err;
+       }
+
+       if (g_ipu_clk_enabled == false) {
+               g_ipu_clk_enabled = true;
+               clk_enable(g_ipu_clk);
+       }
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       temp = __raw_readl(CSI_SKIP(csi));
+       temp &= ~(CSI_MAX_RATIO_SKIP_ISP_MASK | CSI_SKIP_ISP_MASK);
+       temp |= (max_ratio << CSI_MAX_RATIO_SKIP_ISP_SHIFT) |
+               (skip << CSI_SKIP_ISP_SHIFT);
+       __raw_writel(temp, CSI_SKIP(csi));
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+err:
+       return retval;
+}
+
+/*!
+ * _ipu_csi_set_skip_smfc
+ *
+ * @param      skip            select frames to be skipped and set the
+ *                             correspond bits to 1
+ * @param      max_ratio       number of frames in a skipping set and the
+ *                             maximum value of max_ratio is 5
+ * @param      id              csi to smfc skipping id
+ * @param      csi             csi 0 or csi 1
+ *
+ * @return     Returns 0 on success or negative error code on fail
+ */
+int _ipu_csi_set_skip_smfc(uint32_t skip, uint32_t max_ratio,
+       uint32_t id, uint32_t csi)
+{
+       uint32_t temp;
+       int retval = 0;
+       unsigned long lock_flags;
+
+       if (max_ratio > 5 || id > 3) {
+               retval = -EINVAL;
+               goto err;
+       }
+
+       if (g_ipu_clk_enabled == false) {
+               g_ipu_clk_enabled = true;
+               clk_enable(g_ipu_clk);
+       }
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       temp = __raw_readl(CSI_SKIP(csi));
+       temp &= ~(CSI_MAX_RATIO_SKIP_SMFC_MASK | CSI_ID_2_SKIP_MASK |
+                       CSI_SKIP_SMFC_MASK);
+       temp |= (max_ratio << CSI_MAX_RATIO_SKIP_SMFC_SHIFT) |
+                       (id << CSI_ID_2_SKIP_SHIFT) |
+                       (skip << CSI_SKIP_SMFC_SHIFT);
+       __raw_writel(temp, CSI_SKIP(csi));
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+err:
+       return retval;
+}
+
+/*!
+ * _ipu_smfc_init
+ *     Map CSI frames to IDMAC channels.
+ *
+ * @param      channel         IDMAC channel 0-3
+ * @param      mipi_id         mipi id number 0-3
+ * @param      csi             csi0 or csi1
+ */
+void _ipu_smfc_init(ipu_channel_t channel, uint32_t mipi_id, uint32_t csi)
+{
+       uint32_t temp;
+
+       temp = __raw_readl(SMFC_MAP);
+
+       switch (channel) {
+       case CSI_MEM0:
+               temp &= ~SMFC_MAP_CH0_MASK;
+               temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH0_SHIFT;
+               break;
+       case CSI_MEM1:
+               temp &= ~SMFC_MAP_CH1_MASK;
+               temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH1_SHIFT;
+               break;
+       case CSI_MEM2:
+               temp &= ~SMFC_MAP_CH2_MASK;
+               temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH2_SHIFT;
+               break;
+       case CSI_MEM3:
+               temp &= ~SMFC_MAP_CH3_MASK;
+               temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH3_SHIFT;
+               break;
+       default:
+               return;
+       }
+
+       __raw_writel(temp, SMFC_MAP);
+}
+
+/*!
+ * _ipu_smfc_set_wmc
+ *     Caution: The number of required channels,  the enabled channels
+ *     and the FIFO size per channel are configured restrictedly.
+ *
+ * @param      channel         IDMAC channel 0-3
+ * @param      set             set 1 or clear 0
+ * @param      level           water mark level when FIFO is on the
+ *                             relative size
+ */
+void _ipu_smfc_set_wmc(ipu_channel_t channel, bool set, uint32_t level)
+{
+       uint32_t temp;
+       unsigned long lock_flags;
+
+       if (g_ipu_clk_enabled == false) {
+               g_ipu_clk_enabled = true;
+               clk_enable(g_ipu_clk);
+       }
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       temp = __raw_readl(SMFC_WMC);
+
+       switch (channel) {
+       case CSI_MEM0:
+               if (set == true) {
+                       temp &= ~SMFC_WM0_SET_MASK;
+                       temp |= level << SMFC_WM0_SET_SHIFT;
+               } else {
+                       temp &= ~SMFC_WM0_CLR_MASK;
+                       temp |= level << SMFC_WM0_CLR_SHIFT;
+               }
+               break;
+       case CSI_MEM1:
+               if (set == true) {
+                       temp &= ~SMFC_WM1_SET_MASK;
+                       temp |= level << SMFC_WM1_SET_SHIFT;
+               } else {
+                       temp &= ~SMFC_WM1_CLR_MASK;
+                       temp |= level << SMFC_WM1_CLR_SHIFT;
+               }
+               break;
+       case CSI_MEM2:
+               if (set == true) {
+                       temp &= ~SMFC_WM2_SET_MASK;
+                       temp |= level << SMFC_WM2_SET_SHIFT;
+               } else {
+                       temp &= ~SMFC_WM2_CLR_MASK;
+                       temp |= level << SMFC_WM2_CLR_SHIFT;
+               }
+               break;
+       case CSI_MEM3:
+               if (set == true) {
+                       temp &= ~SMFC_WM3_SET_MASK;
+                       temp |= level << SMFC_WM3_SET_SHIFT;
+               } else {
+                       temp &= ~SMFC_WM3_CLR_MASK;
+                       temp |= level << SMFC_WM3_CLR_SHIFT;
+               }
+               break;
+       default:
+               return;
+       }
+
+       __raw_writel(temp, SMFC_WMC);
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+}
+
+/*!
+ * _ipu_smfc_set_burst_size
+ *
+ * @param      channel         IDMAC channel 0-3
+ * @param      bs              burst size of IDMAC channel,
+ *                             the value programmed here shoud be BURST_SIZE-1
+ */
+void _ipu_smfc_set_burst_size(ipu_channel_t channel, uint32_t bs)
+{
+       uint32_t temp;
+       unsigned long lock_flags;
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       temp = __raw_readl(SMFC_BS);
+
+       switch (channel) {
+       case CSI_MEM0:
+               temp &= ~SMFC_BS0_MASK;
+               temp |= bs << SMFC_BS0_SHIFT;
+               break;
+       case CSI_MEM1:
+               temp &= ~SMFC_BS1_MASK;
+               temp |= bs << SMFC_BS1_SHIFT;
+               break;
+       case CSI_MEM2:
+               temp &= ~SMFC_BS2_MASK;
+               temp |= bs << SMFC_BS2_SHIFT;
+               break;
+       case CSI_MEM3:
+               temp &= ~SMFC_BS3_MASK;
+               temp |= bs << SMFC_BS3_SHIFT;
+               break;
+       default:
+               return;
+       }
+
+       __raw_writel(temp, SMFC_BS);
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+}
+
+/*!
+ * _ipu_csi_init
+ *
+ * @param      channel      IDMAC channel
+ * @param      csi          csi 0 or csi 1
+ *
+ * @return     Returns 0 on success or negative error code on fail
+ */
+int _ipu_csi_init(ipu_channel_t channel, uint32_t csi)
+{
+       uint32_t csi_sens_conf, csi_dest;
+       int retval = 0;
+
+       switch (channel) {
+       case CSI_MEM0:
+       case CSI_MEM1:
+       case CSI_MEM2:
+       case CSI_MEM3:
+               csi_dest = CSI_DATA_DEST_IDMAC;
+               break;
+       case CSI_PRP_ENC_MEM:
+       case CSI_PRP_VF_MEM:
+               csi_dest = CSI_DATA_DEST_IC;
+               break;
+       default:
+               retval = -EINVAL;
+               goto err;
+       }
+
+       csi_sens_conf = __raw_readl(CSI_SENS_CONF(csi));
+       csi_sens_conf &= ~CSI_SENS_CONF_DATA_DEST_MASK;
+       __raw_writel(csi_sens_conf | (csi_dest <<
+               CSI_SENS_CONF_DATA_DEST_SHIFT), CSI_SENS_CONF(csi));
+err:
+       return retval;
+}
diff --git a/drivers/mxc/ipu3/ipu_common.c b/drivers/mxc/ipu3/ipu_common.c
new file mode 100644 (file)
index 0000000..bf09143
--- /dev/null
@@ -0,0 +1,2716 @@
+/*
+ * Copyright 2005-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 ipu_common.c
+ *
+ * @brief This file contains the IPU driver common API functions.
+ *
+ * @ingroup IPU
+ */
+#define DEBUG
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ipu.h>
+#include <linux/clk.h>
+#include <mach/clock.h>
+#include <mach/hardware.h>
+#include <mach/ipu-v3.h>
+#include <mach/devices-common.h>
+
+#include "ipu_prv.h"
+#include "ipu_regs.h"
+#include "ipu_param_mem.h"
+
+struct ipu_irq_node {
+       irqreturn_t(*handler) (int, void *);    /*!< the ISR */
+       const char *name;       /*!< device associated with the interrupt */
+       void *dev_id;           /*!< some unique information for the ISR */
+       __u32 flags;            /*!< not used */
+};
+
+/* Globals */
+struct clk *g_ipu_clk;
+bool g_ipu_clk_enabled;
+struct clk *g_di_clk[2];
+struct clk *g_pixel_clk[2];
+struct clk *g_csi_clk[2];
+unsigned char g_dc_di_assignment[10];
+ipu_channel_t g_ipu_csi_channel[2];
+int g_ipu_irq[2];
+int g_ipu_hw_rev;
+bool g_sec_chan_en[24];
+bool g_thrd_chan_en[24];
+bool g_chan_is_interlaced[52];
+uint32_t g_channel_init_mask;
+uint32_t g_channel_enable_mask;
+DEFINE_SPINLOCK(ipu_lock);
+struct device *g_ipu_dev;
+
+static struct ipu_irq_node ipu_irq_list[IPU_IRQ_COUNT];
+
+static int ipu_dc_use_count;
+static int ipu_dp_use_count;
+static int ipu_dmfc_use_count;
+static int ipu_smfc_use_count;
+static int ipu_ic_use_count;
+static int ipu_rot_use_count;
+static int ipu_vdi_use_count;
+static int ipu_di_use_count[2];
+static int ipu_csi_use_count[2];
+/* Set to the follow using IC direct channel, default non */
+static ipu_channel_t using_ic_dirct_ch;
+
+/* for power gating */
+static uint32_t ipu_conf_reg;
+static uint32_t ic_conf_reg;
+static uint32_t ipu_cha_db_mode_reg[4];
+static uint32_t ipu_cha_cur_buf_reg[4];
+static uint32_t idma_enable_reg[2];
+static uint32_t buf_ready_reg[8];
+
+u32 *ipu_cm_reg;
+u32 *ipu_idmac_reg;
+u32 *ipu_dp_reg;
+u32 *ipu_ic_reg;
+u32 *ipu_dc_reg;
+u32 *ipu_dc_tmpl_reg;
+u32 *ipu_dmfc_reg;
+u32 *ipu_di_reg[2];
+u32 *ipu_smfc_reg;
+u32 *ipu_csi_reg[2];
+u32 *ipu_cpmem_base;
+u32 *ipu_tpmem_base;
+u32 *ipu_disp_base[2];
+u32 *ipu_vdi_reg;
+
+/* Static functions */
+static irqreturn_t ipu_irq_handler(int irq, void *desc);
+
+static inline uint32_t channel_2_dma(ipu_channel_t ch, ipu_buffer_t type)
+{
+       return ((uint32_t) ch >> (6 * type)) & 0x3F;
+};
+
+static inline int _ipu_is_ic_chan(uint32_t dma_chan)
+{
+       return ((dma_chan >= 11) && (dma_chan <= 22) && (dma_chan != 17) && (dma_chan != 18));
+}
+
+static inline int _ipu_is_ic_graphic_chan(uint32_t dma_chan)
+{
+       return (dma_chan == 14 || dma_chan == 15);
+}
+
+/* Either DP BG or DP FG can be graphic window */
+static inline int _ipu_is_dp_graphic_chan(uint32_t dma_chan)
+{
+       return (dma_chan == 23 || dma_chan == 27);
+}
+
+static inline int _ipu_is_irt_chan(uint32_t dma_chan)
+{
+       return ((dma_chan >= 45) && (dma_chan <= 50));
+}
+
+static inline int _ipu_is_dmfc_chan(uint32_t dma_chan)
+{
+       return ((dma_chan >= 23) && (dma_chan <= 29));
+}
+
+static inline int _ipu_is_smfc_chan(uint32_t dma_chan)
+{
+       return ((dma_chan >= 0) && (dma_chan <= 3));
+}
+
+#define idma_is_valid(ch)      (ch != NO_DMA)
+#define idma_mask(ch)          (idma_is_valid(ch) ? (1UL << (ch & 0x1F)) : 0)
+#define idma_is_set(reg, dma)  (__raw_readl(reg(dma)) & idma_mask(dma))
+
+static unsigned long _ipu_pixel_clk_get_rate(struct clk *clk)
+{
+       u32 div = __raw_readl(DI_BS_CLKGEN0(clk->id));
+       if (div == 0)
+               return 0;
+       return  (clk_get_rate(clk->parent) * 16) / div;
+}
+
+static unsigned long _ipu_pixel_clk_round_rate(struct clk *clk, unsigned long rate)
+{
+       u32 div, div1;
+       u32 parent_rate = clk_get_rate(clk->parent) * 16;
+       /*
+        * Calculate divider
+        * Fractional part is 4 bits,
+        * so simply multiply by 2^4 to get fractional part.
+        */
+       div = parent_rate / rate;
+
+       if (div < 0x10)            /* Min DI disp clock divider is 1 */
+               div = 0x10;
+       if (div & ~0xFEF)
+               div &= 0xFF8;
+       else {
+               div1 = div & 0xFE0;
+               if ((parent_rate / div1 - parent_rate / div) < rate / 4)
+                       div = div1;
+               else
+                       div &= 0xFF8;
+       }
+       return parent_rate / div;
+}
+
+static int _ipu_pixel_clk_set_rate(struct clk *clk, unsigned long rate)
+{
+       u32 div = (clk_get_rate(clk->parent) * 16) / rate;
+
+       __raw_writel(div, DI_BS_CLKGEN0(clk->id));
+
+       /* Setup pixel clock timing */
+       /* FIXME: needs to be more flexible */
+       /* Down time is half of period */
+       __raw_writel((div / 16) << 16, DI_BS_CLKGEN1(clk->id));
+
+       return 0;
+}
+
+static int _ipu_pixel_clk_enable(struct clk *clk)
+{
+       u32 disp_gen = __raw_readl(IPU_DISP_GEN);
+       disp_gen |= clk->id ? DI1_COUNTER_RELEASE : DI0_COUNTER_RELEASE;
+       __raw_writel(disp_gen, IPU_DISP_GEN);
+
+       return 0;
+}
+
+static void _ipu_pixel_clk_disable(struct clk *clk)
+{
+       u32 disp_gen = __raw_readl(IPU_DISP_GEN);
+       disp_gen &= clk->id ? ~DI1_COUNTER_RELEASE : ~DI0_COUNTER_RELEASE;
+       __raw_writel(disp_gen, IPU_DISP_GEN);
+}
+
+static int _ipu_pixel_clk_set_parent(struct clk *clk, struct clk *parent)
+{
+       u32 di_gen = __raw_readl(DI_GENERAL(clk->id));
+
+       if (parent == g_ipu_clk)
+               di_gen &= ~DI_GEN_DI_CLK_EXT;
+       else if (!IS_ERR(g_di_clk[clk->id]) && parent == g_di_clk[clk->id])
+               di_gen |= DI_GEN_DI_CLK_EXT;
+       else
+               return -EINVAL;
+
+       __raw_writel(di_gen, DI_GENERAL(clk->id));
+       return 0;
+}
+
+static struct clk pixel_clk[] = {
+       {
+       .id = 0,
+       .get_rate = _ipu_pixel_clk_get_rate,
+       .set_rate = _ipu_pixel_clk_set_rate,
+       .round_rate = _ipu_pixel_clk_round_rate,
+       .set_parent = _ipu_pixel_clk_set_parent,
+       .enable = _ipu_pixel_clk_enable,
+       .disable = _ipu_pixel_clk_disable,
+       },
+       {
+       .id = 1,
+       .get_rate = _ipu_pixel_clk_get_rate,
+       .set_rate = _ipu_pixel_clk_set_rate,
+       .round_rate = _ipu_pixel_clk_round_rate,
+       .set_parent = _ipu_pixel_clk_set_parent,
+       .enable = _ipu_pixel_clk_enable,
+       .disable = _ipu_pixel_clk_disable,
+       },
+};
+
+int __initdata primary_di = { 0 };
+static int __init di_setup(char *__unused)
+{
+       primary_di = 1;
+       return 1;
+}
+__setup("di1_primary", di_setup);
+
+struct platform_device *__init imx_add_ipuv3_fb(
+               const struct ipuv3_fb_platform_data *pdata, int id)
+{
+       if (pdata) {
+               if (pdata->res_size > 0) {
+                       struct resource res[] = {
+                               {
+                                       .start = pdata->res_base,
+                                       .end = pdata->res_base + pdata->res_size - 1,
+                                       .flags = IORESOURCE_MEM,
+                               },
+                       };
+
+                       return imx_add_platform_device_dmamask("mxc_sdc_fb",
+                                       id, res, ARRAY_SIZE(res), pdata,
+                                       sizeof(*pdata), DMA_BIT_MASK(32));
+               } else
+                       return imx_add_platform_device_dmamask("mxc_sdc_fb", id,
+                                       NULL, 0, pdata, sizeof(*pdata),
+                                       DMA_BIT_MASK(32));
+       } else
+               return imx_add_platform_device_dmamask("mxc_sdc_fb", id,
+                               NULL, 0, NULL, 0, DMA_BIT_MASK(32));
+}
+
+static int __init register_fb_device(struct platform_device *pdev)
+{
+       struct imx_ipuv3_platform_data *plat_data = pdev->dev.platform_data;
+
+       if (primary_di) {
+               dev_info(g_ipu_dev, "DI1 is primary\n");
+               /* DI1 -> DP-BG channel: */
+               imx_add_ipuv3_fb(plat_data->fb_head1_platform_data, 1);
+               /* DI0 -> DC channel: */
+               imx_add_ipuv3_fb(plat_data->fb_head0_platform_data, 0);
+       } else {
+               dev_info(g_ipu_dev, "DI0 is primary\n");
+               /* DI0 -> DP-BG channel: */
+               imx_add_ipuv3_fb(plat_data->fb_head0_platform_data, 0);
+               /* DI1 -> DC channel: */
+               imx_add_ipuv3_fb(plat_data->fb_head1_platform_data, 1);
+       }
+
+       /*
+        * DI0/1 DP-FG channel:
+        */
+       imx_add_ipuv3_fb(NULL, 2);
+
+       return 0;
+}
+
+/*!
+ * This function is called by the driver framework to initialize the IPU
+ * hardware.
+ *
+ * @param      dev     The device structure for the IPU passed in by the
+ *                     driver framework.
+ *
+ * @return      Returns 0 on success or negative error code on error
+ */
+static int ipu_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       struct imx_ipuv3_platform_data *plat_data = pdev->dev.platform_data;
+       unsigned long ipu_base;
+
+       spin_lock_init(&ipu_lock);
+
+       g_ipu_hw_rev = plat_data->rev;
+
+       g_ipu_dev = &pdev->dev;
+
+       /* Register IPU interrupts */
+       g_ipu_irq[0] = platform_get_irq(pdev, 0);
+       if (g_ipu_irq[0] < 0)
+               return -EINVAL;
+
+       if (request_irq(g_ipu_irq[0], ipu_irq_handler, 0, pdev->name, 0) != 0) {
+               dev_err(g_ipu_dev, "request SYNC interrupt failed\n");
+               return -EBUSY;
+       }
+       /* Some platforms have 2 IPU interrupts */
+       g_ipu_irq[1] = platform_get_irq(pdev, 1);
+       if (g_ipu_irq[1] >= 0) {
+               if (request_irq
+                   (g_ipu_irq[1], ipu_irq_handler, 0, pdev->name, 0) != 0) {
+                       dev_err(g_ipu_dev, "request ERR interrupt failed\n");
+                       return -EBUSY;
+               }
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (IS_ERR(res))
+               return -ENODEV;
+
+       ipu_base = res->start;
+       if (g_ipu_hw_rev == 3)  /* IPUv3M */
+               ipu_base += IPUV3M_REG_BASE;
+       else                    /* IPUv3D, v3E, v3EX */
+               ipu_base += IPU_REG_BASE;
+
+       ipu_cm_reg = ioremap(ipu_base + IPU_CM_REG_BASE, PAGE_SIZE);
+       ipu_ic_reg = ioremap(ipu_base + IPU_IC_REG_BASE, PAGE_SIZE);
+       ipu_idmac_reg = ioremap(ipu_base + IPU_IDMAC_REG_BASE, PAGE_SIZE);
+       /* DP Registers are accessed thru the SRM */
+       ipu_dp_reg = ioremap(ipu_base + IPU_SRM_REG_BASE, PAGE_SIZE);
+       ipu_dc_reg = ioremap(ipu_base + IPU_DC_REG_BASE, PAGE_SIZE);
+       ipu_dmfc_reg = ioremap(ipu_base + IPU_DMFC_REG_BASE, PAGE_SIZE);
+       ipu_di_reg[0] = ioremap(ipu_base + IPU_DI0_REG_BASE, PAGE_SIZE);
+       ipu_di_reg[1] = ioremap(ipu_base + IPU_DI1_REG_BASE, PAGE_SIZE);
+       ipu_smfc_reg = ioremap(ipu_base + IPU_SMFC_REG_BASE, PAGE_SIZE);
+       ipu_csi_reg[0] = ioremap(ipu_base + IPU_CSI0_REG_BASE, PAGE_SIZE);
+       ipu_csi_reg[1] = ioremap(ipu_base + IPU_CSI1_REG_BASE, PAGE_SIZE);
+       ipu_cpmem_base = ioremap(ipu_base + IPU_CPMEM_REG_BASE, PAGE_SIZE);
+       ipu_tpmem_base = ioremap(ipu_base + IPU_TPM_REG_BASE, SZ_64K);
+       ipu_dc_tmpl_reg = ioremap(ipu_base + IPU_DC_TMPL_REG_BASE, SZ_128K);
+       ipu_disp_base[1] = ioremap(ipu_base + IPU_DISP1_BASE, SZ_4K);
+       ipu_vdi_reg = ioremap(ipu_base + IPU_VDI_REG_BASE, PAGE_SIZE);
+
+       dev_dbg(g_ipu_dev, "IPU VDI Regs = %p\n", ipu_vdi_reg);
+       dev_dbg(g_ipu_dev, "IPU CM Regs = %p\n", ipu_cm_reg);
+       dev_dbg(g_ipu_dev, "IPU IC Regs = %p\n", ipu_ic_reg);
+       dev_dbg(g_ipu_dev, "IPU IDMAC Regs = %p\n", ipu_idmac_reg);
+       dev_dbg(g_ipu_dev, "IPU DP Regs = %p\n", ipu_dp_reg);
+       dev_dbg(g_ipu_dev, "IPU DC Regs = %p\n", ipu_dc_reg);
+       dev_dbg(g_ipu_dev, "IPU DMFC Regs = %p\n", ipu_dmfc_reg);
+       dev_dbg(g_ipu_dev, "IPU DI0 Regs = %p\n", ipu_di_reg[0]);
+       dev_dbg(g_ipu_dev, "IPU DI1 Regs = %p\n", ipu_di_reg[1]);
+       dev_dbg(g_ipu_dev, "IPU SMFC Regs = %p\n", ipu_smfc_reg);
+       dev_dbg(g_ipu_dev, "IPU CSI0 Regs = %p\n", ipu_csi_reg[0]);
+       dev_dbg(g_ipu_dev, "IPU CSI1 Regs = %p\n", ipu_csi_reg[1]);
+       dev_dbg(g_ipu_dev, "IPU CPMem = %p\n", ipu_cpmem_base);
+       dev_dbg(g_ipu_dev, "IPU TPMem = %p\n", ipu_tpmem_base);
+       dev_dbg(g_ipu_dev, "IPU DC Template Mem = %p\n", ipu_dc_tmpl_reg);
+       dev_dbg(g_ipu_dev, "IPU Display Region 1 Mem = %p\n", ipu_disp_base[1]);
+
+       g_pixel_clk[0] = &pixel_clk[0];
+       g_pixel_clk[1] = &pixel_clk[1];
+
+       /* Enable IPU and CSI clocks */
+       /* Get IPU clock freq */
+       g_ipu_clk = clk_get(&pdev->dev, "ipu_clk");
+       dev_dbg(g_ipu_dev, "ipu_clk = %lu\n", clk_get_rate(g_ipu_clk));
+
+       if (plat_data->init)
+               plat_data->init();
+
+       clk_set_parent(g_pixel_clk[0], g_ipu_clk);
+       clk_set_parent(g_pixel_clk[1], g_ipu_clk);
+       clk_enable(g_ipu_clk);
+
+       g_di_clk[0] = clk_get(&pdev->dev, "ipu_di0_clk");
+       g_di_clk[1] = clk_get(&pdev->dev, "ipu_di1_clk");
+
+       /*g_csi_clk[0] = plat_data->csi_clk[0];
+       g_csi_clk[1] = plat_data->csi_clk[1];*/
+
+       __raw_writel(0x807FFFFF, IPU_MEM_RST);
+       while (__raw_readl(IPU_MEM_RST) & 0x80000000)
+               ;
+
+       _ipu_init_dc_mappings();
+
+       /* Enable error interrupts by default */
+       __raw_writel(0xFFFFFFFF, IPU_INT_CTRL(5));
+       __raw_writel(0xFFFFFFFF, IPU_INT_CTRL(6));
+       __raw_writel(0xFFFFFFFF, IPU_INT_CTRL(9));
+       __raw_writel(0xFFFFFFFF, IPU_INT_CTRL(10));
+
+       /* DMFC Init */
+       _ipu_dmfc_init(DMFC_NORMAL, 1);
+
+       /* Set sync refresh channels and CSI->mem channel as high priority */
+       __raw_writel(0x18800001L, IDMAC_CHA_PRI(0));
+
+       /* Set MCU_T to divide MCU access window into 2 */
+       __raw_writel(0x00400000L | (IPU_MCU_T_DEFAULT << 18), IPU_DISP_GEN);
+
+       clk_disable(g_ipu_clk);
+
+       register_fb_device(pdev);
+
+       register_ipu_device();
+
+       return 0;
+}
+
+int ipu_remove(struct platform_device *pdev)
+{
+       if (g_ipu_irq[0])
+               free_irq(g_ipu_irq[0], 0);
+       if (g_ipu_irq[1])
+               free_irq(g_ipu_irq[1], 0);
+
+       clk_put(g_ipu_clk);
+
+       iounmap(ipu_cm_reg);
+       iounmap(ipu_ic_reg);
+       iounmap(ipu_idmac_reg);
+       iounmap(ipu_dc_reg);
+       iounmap(ipu_dp_reg);
+       iounmap(ipu_dmfc_reg);
+       iounmap(ipu_di_reg[0]);
+       iounmap(ipu_di_reg[1]);
+       iounmap(ipu_smfc_reg);
+       iounmap(ipu_csi_reg[0]);
+       iounmap(ipu_csi_reg[1]);
+       iounmap(ipu_cpmem_base);
+       iounmap(ipu_tpmem_base);
+       iounmap(ipu_dc_tmpl_reg);
+       iounmap(ipu_disp_base[1]);
+       iounmap(ipu_vdi_reg);
+
+       return 0;
+}
+
+void ipu_dump_registers(void)
+{
+       printk(KERN_DEBUG "IPU_CONF = \t0x%08X\n", __raw_readl(IPU_CONF));
+       printk(KERN_DEBUG "IDMAC_CONF = \t0x%08X\n", __raw_readl(IDMAC_CONF));
+       printk(KERN_DEBUG "IDMAC_CHA_EN1 = \t0x%08X\n",
+              __raw_readl(IDMAC_CHA_EN(0)));
+       printk(KERN_DEBUG "IDMAC_CHA_EN2 = \t0x%08X\n",
+              __raw_readl(IDMAC_CHA_EN(32)));
+       printk(KERN_DEBUG "IDMAC_CHA_PRI1 = \t0x%08X\n",
+              __raw_readl(IDMAC_CHA_PRI(0)));
+       printk(KERN_DEBUG "IDMAC_CHA_PRI2 = \t0x%08X\n",
+              __raw_readl(IDMAC_CHA_PRI(32)));
+       printk(KERN_DEBUG "IDMAC_BAND_EN1 = \t0x%08X\n",
+              __raw_readl(IDMAC_BAND_EN(0)));
+       printk(KERN_DEBUG "IDMAC_BAND_EN2 = \t0x%08X\n",
+              __raw_readl(IDMAC_BAND_EN(32)));
+       printk(KERN_DEBUG "IPU_CHA_DB_MODE_SEL0 = \t0x%08X\n",
+              __raw_readl(IPU_CHA_DB_MODE_SEL(0)));
+       printk(KERN_DEBUG "IPU_CHA_DB_MODE_SEL1 = \t0x%08X\n",
+              __raw_readl(IPU_CHA_DB_MODE_SEL(32)));
+       printk(KERN_DEBUG "DMFC_WR_CHAN = \t0x%08X\n",
+              __raw_readl(DMFC_WR_CHAN));
+       printk(KERN_DEBUG "DMFC_WR_CHAN_DEF = \t0x%08X\n",
+              __raw_readl(DMFC_WR_CHAN_DEF));
+       printk(KERN_DEBUG "DMFC_DP_CHAN = \t0x%08X\n",
+              __raw_readl(DMFC_DP_CHAN));
+       printk(KERN_DEBUG "DMFC_DP_CHAN_DEF = \t0x%08X\n",
+              __raw_readl(DMFC_DP_CHAN_DEF));
+       printk(KERN_DEBUG "DMFC_IC_CTRL = \t0x%08X\n",
+              __raw_readl(DMFC_IC_CTRL));
+       printk(KERN_DEBUG "IPU_FS_PROC_FLOW1 = \t0x%08X\n",
+              __raw_readl(IPU_FS_PROC_FLOW1));
+       printk(KERN_DEBUG "IPU_FS_PROC_FLOW2 = \t0x%08X\n",
+              __raw_readl(IPU_FS_PROC_FLOW2));
+       printk(KERN_DEBUG "IPU_FS_PROC_FLOW3 = \t0x%08X\n",
+              __raw_readl(IPU_FS_PROC_FLOW3));
+       printk(KERN_DEBUG "IPU_FS_DISP_FLOW1 = \t0x%08X\n",
+              __raw_readl(IPU_FS_DISP_FLOW1));
+}
+
+/*!
+ * This function is called to initialize a logical IPU channel.
+ *
+ * @param       channel Input parameter for the logical channel ID to init.
+ *
+ * @param       params  Input parameter containing union of channel
+ *                      initialization parameters.
+ *
+ * @return      Returns 0 on success or negative error code on fail
+ */
+int32_t ipu_init_channel(ipu_channel_t channel, ipu_channel_params_t *params)
+{
+       int ret = 0;
+       uint32_t ipu_conf;
+       uint32_t reg;
+       unsigned long lock_flags;
+
+       dev_dbg(g_ipu_dev, "init channel = %d\n", IPU_CHAN_ID(channel));
+
+       /* re-enable error interrupts every time a channel is initialized */
+       __raw_writel(0xFFFFFFFF, IPU_INT_CTRL(5));
+       __raw_writel(0xFFFFFFFF, IPU_INT_CTRL(6));
+       __raw_writel(0xFFFFFFFF, IPU_INT_CTRL(9));
+       __raw_writel(0xFFFFFFFF, IPU_INT_CTRL(10));
+
+       if (g_ipu_clk_enabled == false) {
+               g_ipu_clk_enabled = true;
+               clk_enable(g_ipu_clk);
+       }
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       if (g_channel_init_mask & (1L << IPU_CHAN_ID(channel))) {
+               dev_err(g_ipu_dev, "Warning: channel already initialized %d\n",
+                       IPU_CHAN_ID(channel));
+       }
+
+       ipu_conf = __raw_readl(IPU_CONF);
+
+       switch (channel) {
+       case CSI_MEM0:
+       case CSI_MEM1:
+       case CSI_MEM2:
+       case CSI_MEM3:
+               if (params->csi_mem.csi > 1) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               if (params->csi_mem.interlaced)
+                       g_chan_is_interlaced[channel_2_dma(channel,
+                               IPU_OUTPUT_BUFFER)] = true;
+               else
+                       g_chan_is_interlaced[channel_2_dma(channel,
+                               IPU_OUTPUT_BUFFER)] = false;
+
+               ipu_smfc_use_count++;
+               g_ipu_csi_channel[params->csi_mem.csi] = channel;
+
+               /*SMFC setting*/
+               if (params->csi_mem.mipi_en) {
+                       ipu_conf |= (1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET +
+                               params->csi_mem.csi));
+                       _ipu_smfc_init(channel, params->csi_mem.mipi_id,
+                               params->csi_mem.csi);
+               } else {
+                       ipu_conf &= ~(1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET +
+                               params->csi_mem.csi));
+                       _ipu_smfc_init(channel, 0, params->csi_mem.csi);
+               }
+
+               /*CSI data (include compander) dest*/
+               _ipu_csi_init(channel, params->csi_mem.csi);
+               break;
+       case CSI_PRP_ENC_MEM:
+               if (params->csi_prp_enc_mem.csi > 1) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+               if (using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+               using_ic_dirct_ch = CSI_PRP_ENC_MEM;
+
+               ipu_ic_use_count++;
+               g_ipu_csi_channel[params->csi_prp_enc_mem.csi] = channel;
+
+               /*Without SMFC, CSI only support parallel data source*/
+               ipu_conf &= ~(1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET +
+                       params->csi_prp_enc_mem.csi));
+
+               /*CSI0/1 feed into IC*/
+               ipu_conf &= ~IPU_CONF_IC_INPUT;
+               if (params->csi_prp_enc_mem.csi)
+                       ipu_conf |= IPU_CONF_CSI_SEL;
+               else
+                       ipu_conf &= ~IPU_CONF_CSI_SEL;
+
+               /*PRP skip buffer in memory, only valid when RWS_EN is true*/
+               reg = __raw_readl(IPU_FS_PROC_FLOW1);
+               __raw_writel(reg & ~FS_ENC_IN_VALID, IPU_FS_PROC_FLOW1);
+
+               /*CSI data (include compander) dest*/
+               _ipu_csi_init(channel, params->csi_prp_enc_mem.csi);
+               _ipu_ic_init_prpenc(params, true);
+               break;
+       case CSI_PRP_VF_MEM:
+               if (params->csi_prp_vf_mem.csi > 1) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+               if (using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+               using_ic_dirct_ch = CSI_PRP_VF_MEM;
+
+               ipu_ic_use_count++;
+               g_ipu_csi_channel[params->csi_prp_vf_mem.csi] = channel;
+
+               /*Without SMFC, CSI only support parallel data source*/
+               ipu_conf &= ~(1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET +
+                       params->csi_prp_vf_mem.csi));
+
+               /*CSI0/1 feed into IC*/
+               ipu_conf &= ~IPU_CONF_IC_INPUT;
+               if (params->csi_prp_vf_mem.csi)
+                       ipu_conf |= IPU_CONF_CSI_SEL;
+               else
+                       ipu_conf &= ~IPU_CONF_CSI_SEL;
+
+               /*PRP skip buffer in memory, only valid when RWS_EN is true*/
+               reg = __raw_readl(IPU_FS_PROC_FLOW1);
+               __raw_writel(reg & ~FS_VF_IN_VALID, IPU_FS_PROC_FLOW1);
+
+               /*CSI data (include compander) dest*/
+               _ipu_csi_init(channel, params->csi_prp_vf_mem.csi);
+               _ipu_ic_init_prpvf(params, true);
+               break;
+       case MEM_PRP_VF_MEM:
+               ipu_ic_use_count++;
+               reg = __raw_readl(IPU_FS_PROC_FLOW1);
+               __raw_writel(reg | FS_VF_IN_VALID, IPU_FS_PROC_FLOW1);
+
+               if (params->mem_prp_vf_mem.graphics_combine_en)
+                       g_sec_chan_en[IPU_CHAN_ID(channel)] = true;
+               if (params->mem_prp_vf_mem.alpha_chan_en)
+                       g_thrd_chan_en[IPU_CHAN_ID(channel)] = true;
+
+               _ipu_ic_init_prpvf(params, false);
+               break;
+       case MEM_VDI_PRP_VF_MEM:
+               if ((using_ic_dirct_ch == CSI_PRP_VF_MEM) ||
+                    (using_ic_dirct_ch == CSI_PRP_ENC_MEM)) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+               using_ic_dirct_ch = MEM_VDI_PRP_VF_MEM;
+               ipu_ic_use_count++;
+               ipu_vdi_use_count++;
+               reg = __raw_readl(IPU_FS_PROC_FLOW1);
+               reg &= ~FS_VDI_SRC_SEL_MASK;
+               __raw_writel(reg , IPU_FS_PROC_FLOW1);
+
+               if (params->mem_prp_vf_mem.graphics_combine_en)
+                       g_sec_chan_en[IPU_CHAN_ID(channel)] = true;
+               _ipu_ic_init_prpvf(params, false);
+               _ipu_vdi_init(channel, params);
+               break;
+       case MEM_VDI_PRP_VF_MEM_P:
+               _ipu_vdi_init(channel, params);
+               break;
+       case MEM_VDI_PRP_VF_MEM_N:
+               _ipu_vdi_init(channel, params);
+               break;
+       case MEM_ROT_VF_MEM:
+               ipu_ic_use_count++;
+               ipu_rot_use_count++;
+               _ipu_ic_init_rotate_vf(params);
+               break;
+       case MEM_PRP_ENC_MEM:
+               ipu_ic_use_count++;
+               reg = __raw_readl(IPU_FS_PROC_FLOW1);
+               __raw_writel(reg | FS_ENC_IN_VALID, IPU_FS_PROC_FLOW1);
+               _ipu_ic_init_prpenc(params, false);
+               break;
+       case MEM_ROT_ENC_MEM:
+               ipu_ic_use_count++;
+               ipu_rot_use_count++;
+               _ipu_ic_init_rotate_enc(params);
+               break;
+       case MEM_PP_MEM:
+               if (params->mem_pp_mem.graphics_combine_en)
+                       g_sec_chan_en[IPU_CHAN_ID(channel)] = true;
+               if (params->mem_pp_mem.alpha_chan_en)
+                       g_thrd_chan_en[IPU_CHAN_ID(channel)] = true;
+               _ipu_ic_init_pp(params);
+               ipu_ic_use_count++;
+               break;
+       case MEM_ROT_PP_MEM:
+               _ipu_ic_init_rotate_pp(params);
+               ipu_ic_use_count++;
+               ipu_rot_use_count++;
+               break;
+       case MEM_DC_SYNC:
+               if (params->mem_dc_sync.di > 1) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               g_dc_di_assignment[1] = params->mem_dc_sync.di;
+               _ipu_dc_init(1, params->mem_dc_sync.di,
+                            params->mem_dc_sync.interlaced,
+                            params->mem_dc_sync.out_pixel_fmt);
+               ipu_di_use_count[params->mem_dc_sync.di]++;
+               ipu_dc_use_count++;
+               ipu_dmfc_use_count++;
+               break;
+       case MEM_BG_SYNC:
+               if (params->mem_dp_bg_sync.di > 1) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               if (params->mem_dp_bg_sync.alpha_chan_en)
+                       g_thrd_chan_en[IPU_CHAN_ID(channel)] = true;
+
+               g_dc_di_assignment[5] = params->mem_dp_bg_sync.di;
+               _ipu_dp_init(channel, params->mem_dp_bg_sync.in_pixel_fmt,
+                            params->mem_dp_bg_sync.out_pixel_fmt);
+               _ipu_dc_init(5, params->mem_dp_bg_sync.di,
+                            params->mem_dp_bg_sync.interlaced,
+                            params->mem_dp_bg_sync.out_pixel_fmt);
+               ipu_di_use_count[params->mem_dp_bg_sync.di]++;
+               ipu_dc_use_count++;
+               ipu_dp_use_count++;
+               ipu_dmfc_use_count++;
+               break;
+       case MEM_FG_SYNC:
+               _ipu_dp_init(channel, params->mem_dp_fg_sync.in_pixel_fmt,
+                            params->mem_dp_fg_sync.out_pixel_fmt);
+
+               if (params->mem_dp_fg_sync.alpha_chan_en)
+                       g_thrd_chan_en[IPU_CHAN_ID(channel)] = true;
+
+               ipu_dc_use_count++;
+               ipu_dp_use_count++;
+               ipu_dmfc_use_count++;
+               break;
+       case DIRECT_ASYNC0:
+               if (params->direct_async.di > 1) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               g_dc_di_assignment[8] = params->direct_async.di;
+               _ipu_dc_init(8, params->direct_async.di, false, IPU_PIX_FMT_GENERIC);
+               ipu_di_use_count[params->direct_async.di]++;
+               ipu_dc_use_count++;
+               break;
+       case DIRECT_ASYNC1:
+               if (params->direct_async.di > 1) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               g_dc_di_assignment[9] = params->direct_async.di;
+               _ipu_dc_init(9, params->direct_async.di, false, IPU_PIX_FMT_GENERIC);
+               ipu_di_use_count[params->direct_async.di]++;
+               ipu_dc_use_count++;
+               break;
+       default:
+               dev_err(g_ipu_dev, "Missing channel initialization\n");
+               break;
+       }
+
+       /* Enable IPU sub module */
+       g_channel_init_mask |= 1L << IPU_CHAN_ID(channel);
+
+       __raw_writel(ipu_conf, IPU_CONF);
+
+err:
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+       return ret;
+}
+EXPORT_SYMBOL(ipu_init_channel);
+
+/*!
+ * This function is called to uninitialize a logical IPU channel.
+ *
+ * @param       channel Input parameter for the logical channel ID to uninit.
+ */
+void ipu_uninit_channel(ipu_channel_t channel)
+{
+       unsigned long lock_flags;
+       uint32_t reg;
+       uint32_t in_dma, out_dma = 0;
+       uint32_t ipu_conf;
+
+       if ((g_channel_init_mask & (1L << IPU_CHAN_ID(channel))) == 0) {
+               dev_err(g_ipu_dev, "Channel already uninitialized %d\n",
+                       IPU_CHAN_ID(channel));
+               return;
+       }
+
+       /* Make sure channel is disabled */
+       /* Get input and output dma channels */
+       in_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER);
+       out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER);
+
+       if (idma_is_set(IDMAC_CHA_EN, in_dma) ||
+           idma_is_set(IDMAC_CHA_EN, out_dma)) {
+               dev_err(g_ipu_dev,
+                       "Channel %d is not disabled, disable first\n",
+                       IPU_CHAN_ID(channel));
+               return;
+       }
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       ipu_conf = __raw_readl(IPU_CONF);
+
+       /* Reset the double buffer */
+       reg = __raw_readl(IPU_CHA_DB_MODE_SEL(in_dma));
+       __raw_writel(reg & ~idma_mask(in_dma), IPU_CHA_DB_MODE_SEL(in_dma));
+       reg = __raw_readl(IPU_CHA_DB_MODE_SEL(out_dma));
+       __raw_writel(reg & ~idma_mask(out_dma), IPU_CHA_DB_MODE_SEL(out_dma));
+
+       if (_ipu_is_ic_chan(in_dma) || _ipu_is_dp_graphic_chan(in_dma)) {
+               g_sec_chan_en[IPU_CHAN_ID(channel)] = false;
+               g_thrd_chan_en[IPU_CHAN_ID(channel)] = false;
+       }
+
+       switch (channel) {
+       case CSI_MEM0:
+       case CSI_MEM1:
+       case CSI_MEM2:
+       case CSI_MEM3:
+               ipu_smfc_use_count--;
+               if (g_ipu_csi_channel[0] == channel) {
+                       g_ipu_csi_channel[0] = CHAN_NONE;
+               } else if (g_ipu_csi_channel[1] == channel) {
+                       g_ipu_csi_channel[1] = CHAN_NONE;
+               }
+               break;
+       case CSI_PRP_ENC_MEM:
+               ipu_ic_use_count--;
+               if (using_ic_dirct_ch == CSI_PRP_ENC_MEM)
+                       using_ic_dirct_ch = 0;
+               _ipu_ic_uninit_prpenc();
+               if (g_ipu_csi_channel[0] == channel) {
+                       g_ipu_csi_channel[0] = CHAN_NONE;
+               } else if (g_ipu_csi_channel[1] == channel) {
+                       g_ipu_csi_channel[1] = CHAN_NONE;
+               }
+               break;
+       case CSI_PRP_VF_MEM:
+               ipu_ic_use_count--;
+               if (using_ic_dirct_ch == CSI_PRP_VF_MEM)
+                       using_ic_dirct_ch = 0;
+               _ipu_ic_uninit_prpvf();
+               if (g_ipu_csi_channel[0] == channel) {
+                       g_ipu_csi_channel[0] = CHAN_NONE;
+               } else if (g_ipu_csi_channel[1] == channel) {
+                       g_ipu_csi_channel[1] = CHAN_NONE;
+               }
+               break;
+       case MEM_PRP_VF_MEM:
+               ipu_ic_use_count--;
+               _ipu_ic_uninit_prpvf();
+               reg = __raw_readl(IPU_FS_PROC_FLOW1);
+               __raw_writel(reg & ~FS_VF_IN_VALID, IPU_FS_PROC_FLOW1);
+               break;
+       case MEM_VDI_PRP_VF_MEM:
+               ipu_ic_use_count--;
+               ipu_vdi_use_count--;
+               if (using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM)
+                       using_ic_dirct_ch = 0;
+               _ipu_ic_uninit_prpvf();
+               _ipu_vdi_uninit();
+               reg = __raw_readl(IPU_FS_PROC_FLOW1);
+               __raw_writel(reg & ~FS_VF_IN_VALID, IPU_FS_PROC_FLOW1);
+               break;
+       case MEM_VDI_PRP_VF_MEM_P:
+       case MEM_VDI_PRP_VF_MEM_N:
+               break;
+       case MEM_ROT_VF_MEM:
+               ipu_rot_use_count--;
+               ipu_ic_use_count--;
+               _ipu_ic_uninit_rotate_vf();
+               break;
+       case MEM_PRP_ENC_MEM:
+               ipu_ic_use_count--;
+               _ipu_ic_uninit_prpenc();
+               reg = __raw_readl(IPU_FS_PROC_FLOW1);
+               __raw_writel(reg & ~FS_ENC_IN_VALID, IPU_FS_PROC_FLOW1);
+               break;
+       case MEM_ROT_ENC_MEM:
+               ipu_rot_use_count--;
+               ipu_ic_use_count--;
+               _ipu_ic_uninit_rotate_enc();
+               break;
+       case MEM_PP_MEM:
+               ipu_ic_use_count--;
+               _ipu_ic_uninit_pp();
+               break;
+       case MEM_ROT_PP_MEM:
+               ipu_rot_use_count--;
+               ipu_ic_use_count--;
+               _ipu_ic_uninit_rotate_pp();
+               break;
+       case MEM_DC_SYNC:
+               _ipu_dc_uninit(1);
+               ipu_di_use_count[g_dc_di_assignment[1]]--;
+               ipu_dc_use_count--;
+               ipu_dmfc_use_count--;
+               break;
+       case MEM_BG_SYNC:
+               _ipu_dp_uninit(channel);
+               _ipu_dc_uninit(5);
+               ipu_di_use_count[g_dc_di_assignment[5]]--;
+               ipu_dc_use_count--;
+               ipu_dp_use_count--;
+               ipu_dmfc_use_count--;
+               break;
+       case MEM_FG_SYNC:
+               _ipu_dp_uninit(channel);
+               ipu_dc_use_count--;
+               ipu_dp_use_count--;
+               ipu_dmfc_use_count--;
+               break;
+       case DIRECT_ASYNC0:
+               _ipu_dc_uninit(8);
+               ipu_di_use_count[g_dc_di_assignment[8]]--;
+               ipu_dc_use_count--;
+               break;
+       case DIRECT_ASYNC1:
+               _ipu_dc_uninit(9);
+               ipu_di_use_count[g_dc_di_assignment[9]]--;
+               ipu_dc_use_count--;
+               break;
+       default:
+               break;
+       }
+
+       g_channel_init_mask &= ~(1L << IPU_CHAN_ID(channel));
+
+       if (ipu_ic_use_count == 0)
+               ipu_conf &= ~IPU_CONF_IC_EN;
+       if (ipu_vdi_use_count == 0) {
+               ipu_conf &= ~IPU_CONF_ISP_EN;
+               ipu_conf &= ~IPU_CONF_VDI_EN;
+               ipu_conf &= ~IPU_CONF_IC_INPUT;
+       }
+       if (ipu_rot_use_count == 0)
+               ipu_conf &= ~IPU_CONF_ROT_EN;
+       if (ipu_dc_use_count == 0)
+               ipu_conf &= ~IPU_CONF_DC_EN;
+       if (ipu_dp_use_count == 0)
+               ipu_conf &= ~IPU_CONF_DP_EN;
+       if (ipu_dmfc_use_count == 0)
+               ipu_conf &= ~IPU_CONF_DMFC_EN;
+       if (ipu_di_use_count[0] == 0) {
+               ipu_conf &= ~IPU_CONF_DI0_EN;
+       }
+       if (ipu_di_use_count[1] == 0) {
+               ipu_conf &= ~IPU_CONF_DI1_EN;
+       }
+       if (ipu_smfc_use_count == 0)
+               ipu_conf &= ~IPU_CONF_SMFC_EN;
+
+       __raw_writel(ipu_conf, IPU_CONF);
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+       if (ipu_conf == 0) {
+               clk_disable(g_ipu_clk);
+               g_ipu_clk_enabled = false;
+       }
+
+       WARN_ON(ipu_ic_use_count < 0);
+       WARN_ON(ipu_vdi_use_count < 0);
+       WARN_ON(ipu_rot_use_count < 0);
+       WARN_ON(ipu_dc_use_count < 0);
+       WARN_ON(ipu_dp_use_count < 0);
+       WARN_ON(ipu_dmfc_use_count < 0);
+       WARN_ON(ipu_smfc_use_count < 0);
+}
+EXPORT_SYMBOL(ipu_uninit_channel);
+
+/*!
+ * This function is called to initialize a buffer for logical IPU channel.
+ *
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @param       type            Input parameter which buffer to initialize.
+ *
+ * @param       pixel_fmt       Input parameter for pixel format of buffer.
+ *                              Pixel format is a FOURCC ASCII code.
+ *
+ * @param       width           Input parameter for width of buffer in pixels.
+ *
+ * @param       height          Input parameter for height of buffer in pixels.
+ *
+ * @param       stride          Input parameter for stride length of buffer
+ *                              in pixels.
+ *
+ * @param       rot_mode        Input parameter for rotation setting of buffer.
+ *                              A rotation setting other than
+ *                              IPU_ROTATE_VERT_FLIP
+ *                              should only be used for input buffers of
+ *                              rotation channels.
+ *
+ * @param       phyaddr_0       Input parameter buffer 0 physical address.
+ *
+ * @param       phyaddr_1       Input parameter buffer 1 physical address.
+ *                              Setting this to a value other than NULL enables
+ *                              double buffering mode.
+ *
+ * @param       u              private u offset for additional cropping,
+ *                             zero if not used.
+ *
+ * @param       v              private v offset for additional cropping,
+ *                             zero if not used.
+ *
+ * @return      Returns 0 on success or negative error code on fail
+ */
+int32_t ipu_init_channel_buffer(ipu_channel_t channel, ipu_buffer_t type,
+                               uint32_t pixel_fmt,
+                               uint16_t width, uint16_t height,
+                               uint32_t stride,
+                               ipu_rotate_mode_t rot_mode,
+                               dma_addr_t phyaddr_0, dma_addr_t phyaddr_1,
+                               uint32_t u, uint32_t v)
+{
+       unsigned long lock_flags;
+       uint32_t reg;
+       uint32_t dma_chan;
+       uint32_t burst_size;
+
+       dma_chan = channel_2_dma(channel, type);
+       if (!idma_is_valid(dma_chan))
+               return -EINVAL;
+
+       if (stride < width * bytes_per_pixel(pixel_fmt))
+               stride = width * bytes_per_pixel(pixel_fmt);
+
+       if (stride % 4) {
+               dev_err(g_ipu_dev,
+                       "Stride not 32-bit aligned, stride = %d\n", stride);
+               return -EINVAL;
+       }
+       /* IC & IRT channels' width must be multiple of 8 pixels */
+       if ((_ipu_is_ic_chan(dma_chan) || _ipu_is_irt_chan(dma_chan))
+               && (width % 8)) {
+               dev_err(g_ipu_dev, "Width must be 8 pixel multiple\n");
+               return -EINVAL;
+       }
+
+       /* Build parameter memory data for DMA channel */
+       _ipu_ch_param_init(dma_chan, pixel_fmt, width, height, stride, u, v, 0,
+                          phyaddr_0, phyaddr_1);
+
+       /* Set correlative channel parameter of local alpha channel */
+       if ((_ipu_is_ic_graphic_chan(dma_chan) ||
+            _ipu_is_dp_graphic_chan(dma_chan)) &&
+           (g_thrd_chan_en[IPU_CHAN_ID(channel)] == true)) {
+               _ipu_ch_param_set_alpha_use_separate_channel(dma_chan, true);
+               _ipu_ch_param_set_alpha_buffer_memory(dma_chan);
+               _ipu_ch_param_set_alpha_condition_read(dma_chan);
+               /* fix alpha width as 8 and burst size as 16*/
+               _ipu_ch_params_set_alpha_width(dma_chan, 8);
+               _ipu_ch_param_set_burst_size(dma_chan, 16);
+       } else if (_ipu_is_ic_graphic_chan(dma_chan) &&
+                  ipu_pixel_format_has_alpha(pixel_fmt))
+               _ipu_ch_param_set_alpha_use_separate_channel(dma_chan, false);
+
+       if (rot_mode)
+               _ipu_ch_param_set_rotation(dma_chan, rot_mode);
+
+       /* IC and ROT channels have restriction of 8 or 16 pix burst length */
+       if (_ipu_is_ic_chan(dma_chan)) {
+               if ((width % 16) == 0)
+                       _ipu_ch_param_set_burst_size(dma_chan, 16);
+               else
+                       _ipu_ch_param_set_burst_size(dma_chan, 8);
+       } else if (_ipu_is_irt_chan(dma_chan)) {
+               _ipu_ch_param_set_burst_size(dma_chan, 8);
+               _ipu_ch_param_set_block_mode(dma_chan);
+       } else if (_ipu_is_dmfc_chan(dma_chan)) {
+               u32 dmfc_dp_chan, dmfc_wr_chan;
+               /*
+                * non-interleaving format need enlarge burst size
+                * to work-around black flash issue.
+                */
+               if (((dma_chan == 23) || (dma_chan == 27) || (dma_chan == 28))
+                       && ((pixel_fmt == IPU_PIX_FMT_YUV420P) ||
+                       (pixel_fmt == IPU_PIX_FMT_YVU420P) ||
+                       (pixel_fmt == IPU_PIX_FMT_YUV420P2) ||
+                       (pixel_fmt == IPU_PIX_FMT_YVU422P) ||
+                       (pixel_fmt == IPU_PIX_FMT_YUV422P) ||
+                       (pixel_fmt == IPU_PIX_FMT_NV12))) {
+                       if (dma_chan == 23) {
+                               dmfc_dp_chan = __raw_readl(DMFC_DP_CHAN);
+                               dmfc_dp_chan &= ~(0xc0);
+                               dmfc_dp_chan |= 0x40;
+                               __raw_writel(dmfc_dp_chan, DMFC_DP_CHAN);
+                       } else if (dma_chan == 27) {
+                               dmfc_dp_chan = __raw_readl(DMFC_DP_CHAN);
+                               dmfc_dp_chan &= ~(0xc000);
+                               dmfc_dp_chan |= 0x4000;
+                               __raw_writel(dmfc_dp_chan, DMFC_DP_CHAN);
+                       } else if (dma_chan == 28) {
+                               dmfc_wr_chan = __raw_readl(DMFC_WR_CHAN);
+                               dmfc_wr_chan &= ~(0xc0);
+                               dmfc_wr_chan |= 0x40;
+                               __raw_writel(dmfc_wr_chan, DMFC_WR_CHAN);
+                       }
+                       _ipu_ch_param_set_burst_size(dma_chan, 64);
+               } else {
+                       if (dma_chan == 23) {
+                               dmfc_dp_chan = __raw_readl(DMFC_DP_CHAN);
+                               dmfc_dp_chan &= ~(0xc0);
+                               dmfc_dp_chan |= 0x80;
+                               __raw_writel(dmfc_dp_chan, DMFC_DP_CHAN);
+                       } else if (dma_chan == 27) {
+                               dmfc_dp_chan = __raw_readl(DMFC_DP_CHAN);
+                               dmfc_dp_chan &= ~(0xc000);
+                               dmfc_dp_chan |= 0x8000;
+                               __raw_writel(dmfc_dp_chan, DMFC_DP_CHAN);
+                       } else {
+                               dmfc_wr_chan = __raw_readl(DMFC_WR_CHAN);
+                               dmfc_wr_chan &= ~(0xc0);
+                               dmfc_wr_chan |= 0x80;
+                               __raw_writel(dmfc_wr_chan, DMFC_WR_CHAN);
+                       }
+               }
+               spin_lock_irqsave(&ipu_lock, lock_flags);
+               _ipu_dmfc_set_wait4eot(dma_chan, width);
+               spin_unlock_irqrestore(&ipu_lock, lock_flags);
+       }
+
+       if (_ipu_disp_chan_is_interlaced(channel) ||
+               g_chan_is_interlaced[dma_chan])
+               _ipu_ch_param_set_interlaced_scan(dma_chan);
+
+       if (_ipu_is_ic_chan(dma_chan) || _ipu_is_irt_chan(dma_chan)) {
+               burst_size = _ipu_ch_param_get_burst_size(dma_chan);
+               _ipu_ic_idma_init(dma_chan, width, height, burst_size,
+                       rot_mode);
+       } else if (_ipu_is_smfc_chan(dma_chan)) {
+               burst_size = _ipu_ch_param_get_burst_size(dma_chan);
+               if ((pixel_fmt == IPU_PIX_FMT_GENERIC) &&
+                       ((_ipu_ch_param_get_bpp(dma_chan) == 5) ||
+                       (_ipu_ch_param_get_bpp(dma_chan) == 3)))
+                       burst_size = burst_size >> 4;
+               else
+                       burst_size = burst_size >> 2;
+               _ipu_smfc_set_burst_size(channel, burst_size-1);
+       }
+
+       if (idma_is_set(IDMAC_CHA_PRI, dma_chan) && !cpu_is_mx53())
+               _ipu_ch_param_set_high_priority(dma_chan);
+
+       _ipu_ch_param_dump(dma_chan);
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       reg = __raw_readl(IPU_CHA_DB_MODE_SEL(dma_chan));
+       if (phyaddr_1)
+               reg |= idma_mask(dma_chan);
+       else
+               reg &= ~idma_mask(dma_chan);
+       __raw_writel(reg, IPU_CHA_DB_MODE_SEL(dma_chan));
+
+       /* Reset to buffer 0 */
+       __raw_writel(idma_mask(dma_chan), IPU_CHA_CUR_BUF(dma_chan));
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+       return 0;
+}
+EXPORT_SYMBOL(ipu_init_channel_buffer);
+
+/*!
+ * This function is called to update the physical address of a buffer for
+ * a logical IPU channel.
+ *
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @param       type            Input parameter which buffer to initialize.
+ *
+ * @param       bufNum          Input parameter for buffer number to update.
+ *                              0 or 1 are the only valid values.
+ *
+ * @param       phyaddr         Input parameter buffer physical address.
+ *
+ * @return      This function returns 0 on success or negative error code on
+ *              fail. This function will fail if the buffer is set to ready.
+ */
+int32_t ipu_update_channel_buffer(ipu_channel_t channel, ipu_buffer_t type,
+                                 uint32_t bufNum, dma_addr_t phyaddr)
+{
+       uint32_t reg;
+       int ret = 0;
+       unsigned long lock_flags;
+       uint32_t dma_chan = channel_2_dma(channel, type);
+       if (dma_chan == IDMA_CHAN_INVALID)
+               return -EINVAL;
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       if (bufNum == 0)
+               reg = __raw_readl(IPU_CHA_BUF0_RDY(dma_chan));
+       else
+               reg = __raw_readl(IPU_CHA_BUF1_RDY(dma_chan));
+
+       if ((reg & idma_mask(dma_chan)) == 0)
+               _ipu_ch_param_set_buffer(dma_chan, bufNum, phyaddr);
+       else
+               ret = -EACCES;
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+       return ret;
+}
+EXPORT_SYMBOL(ipu_update_channel_buffer);
+
+
+/*!
+ * This function is called to initialize a buffer for logical IPU channel.
+ *
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @param       type            Input parameter which buffer to initialize.
+ *
+ * @param       pixel_fmt       Input parameter for pixel format of buffer.
+ *                              Pixel format is a FOURCC ASCII code.
+ *
+ * @param       width           Input parameter for width of buffer in pixels.
+ *
+ * @param       height          Input parameter for height of buffer in pixels.
+ *
+ * @param       stride          Input parameter for stride length of buffer
+ *                              in pixels.
+ *
+ * @param       u              predefined private u offset for additional cropping,
+ *                                                             zero if not used.
+ *
+ * @param       v              predefined private v offset for additional cropping,
+ *                                                             zero if not used.
+ *
+ * @param                      vertical_offset vertical offset for Y coordinate
+ *                                                             in the existed frame
+ *
+ *
+ * @param                      horizontal_offset horizontal offset for X coordinate
+ *                                                             in the existed frame
+ *
+ *
+ * @return      Returns 0 on success or negative error code on fail
+ *              This function will fail if any buffer is set to ready.
+ */
+
+int32_t ipu_update_channel_offset(ipu_channel_t channel, ipu_buffer_t type,
+                               uint32_t pixel_fmt,
+                               uint16_t width, uint16_t height,
+                               uint32_t stride,
+                               uint32_t u, uint32_t v,
+                               uint32_t vertical_offset, uint32_t horizontal_offset)
+{
+       int ret = 0;
+       unsigned long lock_flags;
+       uint32_t dma_chan = channel_2_dma(channel, type);
+
+       if (dma_chan == IDMA_CHAN_INVALID)
+               return -EINVAL;
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       if ((__raw_readl(IPU_CHA_BUF0_RDY(dma_chan)) & idma_mask(dma_chan)) ||
+               (__raw_readl(IPU_CHA_BUF0_RDY(dma_chan)) & idma_mask(dma_chan)))
+               ret = -EACCES;
+       else
+               _ipu_ch_offset_update(dma_chan, pixel_fmt, width, height, stride,
+                                     u, v, 0, vertical_offset, horizontal_offset);
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+       return ret;
+}
+EXPORT_SYMBOL(ipu_update_channel_offset);
+
+
+/*!
+ * This function is called to set a channel's buffer as ready.
+ *
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @param       type            Input parameter which buffer to initialize.
+ *
+ * @param       bufNum          Input parameter for which buffer number set to
+ *                              ready state.
+ *
+ * @return      Returns 0 on success or negative error code on fail
+ */
+int32_t ipu_select_buffer(ipu_channel_t channel, ipu_buffer_t type,
+                         uint32_t bufNum)
+{
+       uint32_t dma_chan = channel_2_dma(channel, type);
+       uint32_t reg;
+
+       if (dma_chan == IDMA_CHAN_INVALID)
+               return -EINVAL;
+
+       if (bufNum == 0) {
+               /*Mark buffer 0 as ready. */
+               reg = __raw_readl(IPU_CHA_BUF0_RDY(dma_chan));
+               __raw_writel(idma_mask(dma_chan) | reg,
+                            IPU_CHA_BUF0_RDY(dma_chan));
+       } else {
+               /*Mark buffer 1 as ready. */
+               reg = __raw_readl(IPU_CHA_BUF1_RDY(dma_chan));
+               __raw_writel(idma_mask(dma_chan) | reg,
+                            IPU_CHA_BUF1_RDY(dma_chan));
+       }
+       return 0;
+}
+EXPORT_SYMBOL(ipu_select_buffer);
+
+/*!
+ * This function is called to set a channel's buffer as ready.
+ *
+ * @param       bufNum          Input parameter for which buffer number set to
+ *                              ready state.
+ *
+ * @return      Returns 0 on success or negative error code on fail
+ */
+int32_t ipu_select_multi_vdi_buffer(uint32_t bufNum)
+{
+
+       uint32_t dma_chan = channel_2_dma(MEM_VDI_PRP_VF_MEM, IPU_INPUT_BUFFER);
+       uint32_t mask_bit =
+               idma_mask(channel_2_dma(MEM_VDI_PRP_VF_MEM_P, IPU_INPUT_BUFFER))|
+               idma_mask(dma_chan)|
+               idma_mask(channel_2_dma(MEM_VDI_PRP_VF_MEM_N, IPU_INPUT_BUFFER));
+       uint32_t reg;
+
+       if (bufNum == 0) {
+               /*Mark buffer 0 as ready. */
+               reg = __raw_readl(IPU_CHA_BUF0_RDY(dma_chan));
+               __raw_writel(mask_bit | reg, IPU_CHA_BUF0_RDY(dma_chan));
+       } else {
+               /*Mark buffer 1 as ready. */
+               reg = __raw_readl(IPU_CHA_BUF1_RDY(dma_chan));
+               __raw_writel(mask_bit | reg, IPU_CHA_BUF1_RDY(dma_chan));
+       }
+       return 0;
+}
+EXPORT_SYMBOL(ipu_select_multi_vdi_buffer);
+
+#define NA     -1
+static int proc_dest_sel[] = {
+       0, 1, 1, 3, 5, 5, 4, 7, 8, 9, 10, 11, 12, 14, 15, 16,
+       0, 1, 1, 5, 5, 5, 5, 5, 7, 8, 9, 10, 11, 12, 14, 31 };
+static int proc_src_sel[] = { 0, 6, 7, 6, 7, 8, 5, NA, NA, NA,
+  NA, NA, NA, NA, NA,  1,  2,  3,  4,  7,  8, NA, 8, NA };
+static int disp_src_sel[] = { 0, 6, 7, 8, 3, 4, 5, NA, NA, NA,
+  NA, NA, NA, NA, NA,  1, NA,  2, NA,  3,  4,  4,  4,  4 };
+
+
+/*!
+ * This function links 2 channels together for automatic frame
+ * synchronization. The output of the source channel is linked to the input of
+ * the destination channel.
+ *
+ * @param       src_ch          Input parameter for the logical channel ID of
+ *                              the source channel.
+ *
+ * @param       dest_ch         Input parameter for the logical channel ID of
+ *                              the destination channel.
+ *
+ * @return      This function returns 0 on success or negative error code on
+ *              fail.
+ */
+int32_t ipu_link_channels(ipu_channel_t src_ch, ipu_channel_t dest_ch)
+{
+       int retval = 0;
+       unsigned long lock_flags;
+       uint32_t fs_proc_flow1;
+       uint32_t fs_proc_flow2;
+       uint32_t fs_proc_flow3;
+       uint32_t fs_disp_flow1;
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       fs_proc_flow1 = __raw_readl(IPU_FS_PROC_FLOW1);
+       fs_proc_flow2 = __raw_readl(IPU_FS_PROC_FLOW2);
+       fs_proc_flow3 = __raw_readl(IPU_FS_PROC_FLOW3);
+       fs_disp_flow1 = __raw_readl(IPU_FS_DISP_FLOW1);
+
+       switch (src_ch) {
+       case CSI_MEM0:
+               fs_proc_flow3 &= ~FS_SMFC0_DEST_SEL_MASK;
+               fs_proc_flow3 |=
+                       proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+                       FS_SMFC0_DEST_SEL_OFFSET;
+               break;
+       case CSI_MEM1:
+               fs_proc_flow3 &= ~FS_SMFC1_DEST_SEL_MASK;
+               fs_proc_flow3 |=
+                       proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+                       FS_SMFC1_DEST_SEL_OFFSET;
+               break;
+       case CSI_MEM2:
+               fs_proc_flow3 &= ~FS_SMFC2_DEST_SEL_MASK;
+               fs_proc_flow3 |=
+                       proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+                       FS_SMFC2_DEST_SEL_OFFSET;
+               break;
+       case CSI_MEM3:
+               fs_proc_flow3 &= ~FS_SMFC3_DEST_SEL_MASK;
+               fs_proc_flow3 |=
+                       proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+                       FS_SMFC3_DEST_SEL_OFFSET;
+               break;
+       case CSI_PRP_ENC_MEM:
+               fs_proc_flow2 &= ~FS_PRPENC_DEST_SEL_MASK;
+               fs_proc_flow2 |=
+                       proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+                       FS_PRPENC_DEST_SEL_OFFSET;
+               break;
+       case CSI_PRP_VF_MEM:
+               fs_proc_flow2 &= ~FS_PRPVF_DEST_SEL_MASK;
+               fs_proc_flow2 |=
+                       proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+                       FS_PRPVF_DEST_SEL_OFFSET;
+               break;
+       case MEM_PP_MEM:
+               fs_proc_flow2 &= ~FS_PP_DEST_SEL_MASK;
+               fs_proc_flow2 |=
+                   proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+                   FS_PP_DEST_SEL_OFFSET;
+               break;
+       case MEM_ROT_PP_MEM:
+               fs_proc_flow2 &= ~FS_PP_ROT_DEST_SEL_MASK;
+               fs_proc_flow2 |=
+                   proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+                   FS_PP_ROT_DEST_SEL_OFFSET;
+               break;
+       case MEM_PRP_ENC_MEM:
+               fs_proc_flow2 &= ~FS_PRPENC_DEST_SEL_MASK;
+               fs_proc_flow2 |=
+                   proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+                   FS_PRPENC_DEST_SEL_OFFSET;
+               break;
+       case MEM_ROT_ENC_MEM:
+               fs_proc_flow2 &= ~FS_PRPENC_ROT_DEST_SEL_MASK;
+               fs_proc_flow2 |=
+                   proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+                   FS_PRPENC_ROT_DEST_SEL_OFFSET;
+               break;
+       case MEM_PRP_VF_MEM:
+               fs_proc_flow2 &= ~FS_PRPVF_DEST_SEL_MASK;
+               fs_proc_flow2 |=
+                   proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+                   FS_PRPVF_DEST_SEL_OFFSET;
+               break;
+       case MEM_VDI_PRP_VF_MEM:
+               fs_proc_flow2 &= ~FS_PRPVF_DEST_SEL_MASK;
+               fs_proc_flow2 |=
+                   proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+                   FS_PRPVF_DEST_SEL_OFFSET;
+               break;
+       case MEM_ROT_VF_MEM:
+               fs_proc_flow2 &= ~FS_PRPVF_ROT_DEST_SEL_MASK;
+               fs_proc_flow2 |=
+                   proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+                   FS_PRPVF_ROT_DEST_SEL_OFFSET;
+               break;
+       default:
+               retval = -EINVAL;
+               goto err;
+       }
+
+       switch (dest_ch) {
+       case MEM_PP_MEM:
+               fs_proc_flow1 &= ~FS_PP_SRC_SEL_MASK;
+               fs_proc_flow1 |=
+                   proc_src_sel[IPU_CHAN_ID(src_ch)] << FS_PP_SRC_SEL_OFFSET;
+               break;
+       case MEM_ROT_PP_MEM:
+               fs_proc_flow1 &= ~FS_PP_ROT_SRC_SEL_MASK;
+               fs_proc_flow1 |=
+                   proc_src_sel[IPU_CHAN_ID(src_ch)] <<
+                   FS_PP_ROT_SRC_SEL_OFFSET;
+               break;
+       case MEM_PRP_ENC_MEM:
+               fs_proc_flow1 &= ~FS_PRP_SRC_SEL_MASK;
+               fs_proc_flow1 |=
+                   proc_src_sel[IPU_CHAN_ID(src_ch)] << FS_PRP_SRC_SEL_OFFSET;
+               break;
+       case MEM_ROT_ENC_MEM:
+               fs_proc_flow1 &= ~FS_PRPENC_ROT_SRC_SEL_MASK;
+               fs_proc_flow1 |=
+                   proc_src_sel[IPU_CHAN_ID(src_ch)] <<
+                   FS_PRPENC_ROT_SRC_SEL_OFFSET;
+               break;
+       case MEM_PRP_VF_MEM:
+               fs_proc_flow1 &= ~FS_PRP_SRC_SEL_MASK;
+               fs_proc_flow1 |=
+                   proc_src_sel[IPU_CHAN_ID(src_ch)] << FS_PRP_SRC_SEL_OFFSET;
+               break;
+       case MEM_VDI_PRP_VF_MEM:
+               fs_proc_flow1 &= ~FS_PRP_SRC_SEL_MASK;
+               fs_proc_flow1 |=
+                   proc_src_sel[IPU_CHAN_ID(src_ch)] << FS_PRP_SRC_SEL_OFFSET;
+               break;
+       case MEM_ROT_VF_MEM:
+               fs_proc_flow1 &= ~FS_PRPVF_ROT_SRC_SEL_MASK;
+               fs_proc_flow1 |=
+                   proc_src_sel[IPU_CHAN_ID(src_ch)] <<
+                   FS_PRPVF_ROT_SRC_SEL_OFFSET;
+               break;
+       case MEM_DC_SYNC:
+               fs_disp_flow1 &= ~FS_DC1_SRC_SEL_MASK;
+               fs_disp_flow1 |=
+                   disp_src_sel[IPU_CHAN_ID(src_ch)] << FS_DC1_SRC_SEL_OFFSET;
+               break;
+       case MEM_BG_SYNC:
+               fs_disp_flow1 &= ~FS_DP_SYNC0_SRC_SEL_MASK;
+               fs_disp_flow1 |=
+                   disp_src_sel[IPU_CHAN_ID(src_ch)] <<
+                   FS_DP_SYNC0_SRC_SEL_OFFSET;
+               break;
+       case MEM_FG_SYNC:
+               fs_disp_flow1 &= ~FS_DP_SYNC1_SRC_SEL_MASK;
+               fs_disp_flow1 |=
+                   disp_src_sel[IPU_CHAN_ID(src_ch)] <<
+                   FS_DP_SYNC1_SRC_SEL_OFFSET;
+               break;
+       case MEM_DC_ASYNC:
+               fs_disp_flow1 &= ~FS_DC2_SRC_SEL_MASK;
+               fs_disp_flow1 |=
+                   disp_src_sel[IPU_CHAN_ID(src_ch)] << FS_DC2_SRC_SEL_OFFSET;
+               break;
+       case MEM_BG_ASYNC0:
+               fs_disp_flow1 &= ~FS_DP_ASYNC0_SRC_SEL_MASK;
+               fs_disp_flow1 |=
+                   disp_src_sel[IPU_CHAN_ID(src_ch)] <<
+                   FS_DP_ASYNC0_SRC_SEL_OFFSET;
+               break;
+       case MEM_FG_ASYNC0:
+               fs_disp_flow1 &= ~FS_DP_ASYNC1_SRC_SEL_MASK;
+               fs_disp_flow1 |=
+                   disp_src_sel[IPU_CHAN_ID(src_ch)] <<
+                   FS_DP_ASYNC1_SRC_SEL_OFFSET;
+               break;
+       default:
+               retval = -EINVAL;
+               goto err;
+       }
+
+       __raw_writel(fs_proc_flow1, IPU_FS_PROC_FLOW1);
+       __raw_writel(fs_proc_flow2, IPU_FS_PROC_FLOW2);
+       __raw_writel(fs_proc_flow3, IPU_FS_PROC_FLOW3);
+       __raw_writel(fs_disp_flow1, IPU_FS_DISP_FLOW1);
+
+err:
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+       return retval;
+}
+EXPORT_SYMBOL(ipu_link_channels);
+
+/*!
+ * This function unlinks 2 channels and disables automatic frame
+ * synchronization.
+ *
+ * @param       src_ch          Input parameter for the logical channel ID of
+ *                              the source channel.
+ *
+ * @param       dest_ch         Input parameter for the logical channel ID of
+ *                              the destination channel.
+ *
+ * @return      This function returns 0 on success or negative error code on
+ *              fail.
+ */
+int32_t ipu_unlink_channels(ipu_channel_t src_ch, ipu_channel_t dest_ch)
+{
+       int retval = 0;
+       unsigned long lock_flags;
+       uint32_t fs_proc_flow1;
+       uint32_t fs_proc_flow2;
+       uint32_t fs_proc_flow3;
+       uint32_t fs_disp_flow1;
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       fs_proc_flow1 = __raw_readl(IPU_FS_PROC_FLOW1);
+       fs_proc_flow2 = __raw_readl(IPU_FS_PROC_FLOW2);
+       fs_proc_flow3 = __raw_readl(IPU_FS_PROC_FLOW3);
+       fs_disp_flow1 = __raw_readl(IPU_FS_DISP_FLOW1);
+
+       switch (src_ch) {
+       case CSI_MEM0:
+               fs_proc_flow3 &= ~FS_SMFC0_DEST_SEL_MASK;
+               break;
+       case CSI_MEM1:
+               fs_proc_flow3 &= ~FS_SMFC1_DEST_SEL_MASK;
+               break;
+       case CSI_MEM2:
+               fs_proc_flow3 &= ~FS_SMFC2_DEST_SEL_MASK;
+               break;
+       case CSI_MEM3:
+               fs_proc_flow3 &= ~FS_SMFC3_DEST_SEL_MASK;
+               break;
+       case CSI_PRP_ENC_MEM:
+               fs_proc_flow2 &= ~FS_PRPENC_DEST_SEL_MASK;
+               break;
+       case CSI_PRP_VF_MEM:
+               fs_proc_flow2 &= ~FS_PRPVF_DEST_SEL_MASK;
+               break;
+       case MEM_PP_MEM:
+               fs_proc_flow2 &= ~FS_PP_DEST_SEL_MASK;
+               break;
+       case MEM_ROT_PP_MEM:
+               fs_proc_flow2 &= ~FS_PP_ROT_DEST_SEL_MASK;
+               break;
+       case MEM_PRP_ENC_MEM:
+               fs_proc_flow2 &= ~FS_PRPENC_DEST_SEL_MASK;
+               break;
+       case MEM_ROT_ENC_MEM:
+               fs_proc_flow2 &= ~FS_PRPENC_ROT_DEST_SEL_MASK;
+               break;
+       case MEM_PRP_VF_MEM:
+               fs_proc_flow2 &= ~FS_PRPVF_DEST_SEL_MASK;
+               break;
+       case MEM_VDI_PRP_VF_MEM:
+               fs_proc_flow2 &= ~FS_PRPVF_DEST_SEL_MASK;
+               break;
+       case MEM_ROT_VF_MEM:
+               fs_proc_flow2 &= ~FS_PRPVF_ROT_DEST_SEL_MASK;
+               break;
+       default:
+               retval = -EINVAL;
+               goto err;
+       }
+
+       switch (dest_ch) {
+       case MEM_PP_MEM:
+               fs_proc_flow1 &= ~FS_PP_SRC_SEL_MASK;
+               break;
+       case MEM_ROT_PP_MEM:
+               fs_proc_flow1 &= ~FS_PP_ROT_SRC_SEL_MASK;
+               break;
+       case MEM_PRP_ENC_MEM:
+               fs_proc_flow1 &= ~FS_PRP_SRC_SEL_MASK;
+               break;
+       case MEM_ROT_ENC_MEM:
+               fs_proc_flow1 &= ~FS_PRPENC_ROT_SRC_SEL_MASK;
+               break;
+       case MEM_PRP_VF_MEM:
+               fs_proc_flow1 &= ~FS_PRP_SRC_SEL_MASK;
+               break;
+       case MEM_VDI_PRP_VF_MEM:
+               fs_proc_flow1 &= ~FS_PRP_SRC_SEL_MASK;
+               break;
+       case MEM_ROT_VF_MEM:
+               fs_proc_flow1 &= ~FS_PRPVF_ROT_SRC_SEL_MASK;
+               break;
+       case MEM_DC_SYNC:
+               fs_disp_flow1 &= ~FS_DC1_SRC_SEL_MASK;
+               break;
+       case MEM_BG_SYNC:
+               fs_disp_flow1 &= ~FS_DP_SYNC0_SRC_SEL_MASK;
+               break;
+       case MEM_FG_SYNC:
+               fs_disp_flow1 &= ~FS_DP_SYNC1_SRC_SEL_MASK;
+               break;
+       case MEM_DC_ASYNC:
+               fs_disp_flow1 &= ~FS_DC2_SRC_SEL_MASK;
+               break;
+       case MEM_BG_ASYNC0:
+               fs_disp_flow1 &= ~FS_DP_ASYNC0_SRC_SEL_MASK;
+               break;
+       case MEM_FG_ASYNC0:
+               fs_disp_flow1 &= ~FS_DP_ASYNC1_SRC_SEL_MASK;
+               break;
+       default:
+               retval = -EINVAL;
+               goto err;
+       }
+
+       __raw_writel(fs_proc_flow1, IPU_FS_PROC_FLOW1);
+       __raw_writel(fs_proc_flow2, IPU_FS_PROC_FLOW2);
+       __raw_writel(fs_proc_flow3, IPU_FS_PROC_FLOW3);
+       __raw_writel(fs_disp_flow1, IPU_FS_DISP_FLOW1);
+
+err:
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+       return retval;
+}
+EXPORT_SYMBOL(ipu_unlink_channels);
+
+/*!
+ * This function check whether a logical channel was enabled.
+ *
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @return      This function returns 1 while request channel is enabled or
+ *              0 for not enabled.
+ */
+int32_t ipu_is_channel_busy(ipu_channel_t channel)
+{
+       uint32_t reg;
+       uint32_t in_dma;
+       uint32_t out_dma;
+
+       out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER);
+       in_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER);
+
+       reg = __raw_readl(IDMAC_CHA_EN(in_dma));
+       if (reg & idma_mask(in_dma))
+               return 1;
+       reg = __raw_readl(IDMAC_CHA_EN(out_dma));
+       if (reg & idma_mask(out_dma))
+               return 1;
+       return 0;
+}
+EXPORT_SYMBOL(ipu_is_channel_busy);
+
+/*!
+ * This function enables a logical channel.
+ *
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @return      This function returns 0 on success or negative error code on
+ *              fail.
+ */
+int32_t ipu_enable_channel(ipu_channel_t channel)
+{
+       uint32_t reg;
+       unsigned long lock_flags;
+       uint32_t ipu_conf;
+       uint32_t in_dma;
+       uint32_t out_dma;
+       uint32_t sec_dma;
+       uint32_t thrd_dma;
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       if (g_channel_enable_mask & (1L << IPU_CHAN_ID(channel))) {
+               dev_err(g_ipu_dev, "Warning: channel already enabled %d\n",
+                       IPU_CHAN_ID(channel));
+               spin_unlock_irqrestore(&ipu_lock, lock_flags);
+               return -EACCES;
+       }
+
+       /* Get input and output dma channels */
+       out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER);
+       in_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER);
+
+       ipu_conf = __raw_readl(IPU_CONF);
+       if (ipu_di_use_count[0] > 0) {
+               ipu_conf |= IPU_CONF_DI0_EN;
+       }
+       if (ipu_di_use_count[1] > 0) {
+               ipu_conf |= IPU_CONF_DI1_EN;
+       }
+       if (ipu_dp_use_count > 0)
+               ipu_conf |= IPU_CONF_DP_EN;
+       if (ipu_dc_use_count > 0)
+               ipu_conf |= IPU_CONF_DC_EN;
+       if (ipu_dmfc_use_count > 0)
+               ipu_conf |= IPU_CONF_DMFC_EN;
+       if (ipu_ic_use_count > 0)
+               ipu_conf |= IPU_CONF_IC_EN;
+       if (ipu_vdi_use_count > 0) {
+               ipu_conf |= IPU_CONF_ISP_EN;
+               ipu_conf |= IPU_CONF_VDI_EN;
+               ipu_conf |= IPU_CONF_IC_INPUT;
+       }
+       if (ipu_rot_use_count > 0)
+               ipu_conf |= IPU_CONF_ROT_EN;
+       if (ipu_smfc_use_count > 0)
+               ipu_conf |= IPU_CONF_SMFC_EN;
+       __raw_writel(ipu_conf, IPU_CONF);
+
+       if (idma_is_valid(in_dma)) {
+               reg = __raw_readl(IDMAC_CHA_EN(in_dma));
+               __raw_writel(reg | idma_mask(in_dma), IDMAC_CHA_EN(in_dma));
+       }
+       if (idma_is_valid(out_dma)) {
+               reg = __raw_readl(IDMAC_CHA_EN(out_dma));
+               __raw_writel(reg | idma_mask(out_dma), IDMAC_CHA_EN(out_dma));
+       }
+
+       if ((g_sec_chan_en[IPU_CHAN_ID(channel)]) &&
+               ((channel == MEM_PP_MEM) || (channel == MEM_PRP_VF_MEM) ||
+                (channel == MEM_VDI_PRP_VF_MEM))) {
+               sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER);
+               reg = __raw_readl(IDMAC_CHA_EN(sec_dma));
+               __raw_writel(reg | idma_mask(sec_dma), IDMAC_CHA_EN(sec_dma));
+       }
+       if ((g_thrd_chan_en[IPU_CHAN_ID(channel)]) &&
+               ((channel == MEM_PP_MEM) || (channel == MEM_PRP_VF_MEM))) {
+               thrd_dma = channel_2_dma(channel, IPU_ALPHA_IN_BUFFER);
+               reg = __raw_readl(IDMAC_CHA_EN(thrd_dma));
+               __raw_writel(reg | idma_mask(thrd_dma), IDMAC_CHA_EN(thrd_dma));
+
+               sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER);
+               reg = __raw_readl(IDMAC_SEP_ALPHA);
+               __raw_writel(reg | idma_mask(sec_dma), IDMAC_SEP_ALPHA);
+       } else if ((g_thrd_chan_en[IPU_CHAN_ID(channel)]) &&
+                  ((channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC))) {
+               thrd_dma = channel_2_dma(channel, IPU_ALPHA_IN_BUFFER);
+               reg = __raw_readl(IDMAC_CHA_EN(thrd_dma));
+               __raw_writel(reg | idma_mask(thrd_dma), IDMAC_CHA_EN(thrd_dma));
+               reg = __raw_readl(IDMAC_SEP_ALPHA);
+               __raw_writel(reg | idma_mask(in_dma), IDMAC_SEP_ALPHA);
+       }
+
+       if ((channel == MEM_DC_SYNC) || (channel == MEM_BG_SYNC) ||
+           (channel == MEM_FG_SYNC)) {
+               reg = __raw_readl(IDMAC_WM_EN(in_dma));
+               __raw_writel(reg | idma_mask(in_dma), IDMAC_WM_EN(in_dma));
+
+               _ipu_dp_dc_enable(channel);
+       }
+
+       if (_ipu_is_ic_chan(in_dma) || _ipu_is_ic_chan(out_dma) ||
+               _ipu_is_irt_chan(in_dma) || _ipu_is_irt_chan(out_dma))
+               _ipu_ic_enable_task(channel);
+
+       g_channel_enable_mask |= 1L << IPU_CHAN_ID(channel);
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+       return 0;
+}
+EXPORT_SYMBOL(ipu_enable_channel);
+
+/*!
+ * This function check buffer ready for a logical channel.
+ *
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @param       type            Input parameter which buffer to clear.
+ *
+ * @param       bufNum          Input parameter for which buffer number clear
+ *                             ready state.
+ *
+ */
+int32_t ipu_check_buffer_ready(ipu_channel_t channel, ipu_buffer_t type,
+               uint32_t bufNum)
+{
+       uint32_t dma_chan = channel_2_dma(channel, type);
+       uint32_t reg;
+
+       if (dma_chan == IDMA_CHAN_INVALID)
+               return -EINVAL;
+
+       if (bufNum == 0)
+               reg = __raw_readl(IPU_CHA_BUF0_RDY(dma_chan));
+       else
+               reg = __raw_readl(IPU_CHA_BUF1_RDY(dma_chan));
+
+       if (reg & idma_mask(dma_chan))
+               return 1;
+       else
+               return 0;
+}
+EXPORT_SYMBOL(ipu_check_buffer_ready);
+
+/*!
+ * This function clear buffer ready for a logical channel.
+ *
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @param       type            Input parameter which buffer to clear.
+ *
+ * @param       bufNum          Input parameter for which buffer number clear
+ *                             ready state.
+ *
+ */
+void ipu_clear_buffer_ready(ipu_channel_t channel, ipu_buffer_t type,
+               uint32_t bufNum)
+{
+       unsigned long lock_flags;
+       uint32_t dma_ch = channel_2_dma(channel, type);
+
+       if (!idma_is_valid(dma_ch))
+               return;
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       __raw_writel(0xF0000000, IPU_GPR); /* write one to clear */
+       if (bufNum == 0) {
+               if (idma_is_set(IPU_CHA_BUF0_RDY, dma_ch)) {
+                       __raw_writel(idma_mask(dma_ch),
+                                       IPU_CHA_BUF0_RDY(dma_ch));
+               }
+       } else {
+               if (idma_is_set(IPU_CHA_BUF1_RDY, dma_ch)) {
+                       __raw_writel(idma_mask(dma_ch),
+                                       IPU_CHA_BUF1_RDY(dma_ch));
+               }
+       }
+       __raw_writel(0x0, IPU_GPR); /* write one to set */
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+}
+EXPORT_SYMBOL(ipu_clear_buffer_ready);
+
+static irqreturn_t disable_chan_irq_handler(int irq, void *dev_id)
+{
+       struct completion *comp = dev_id;
+
+       complete(comp);
+       return IRQ_HANDLED;
+}
+
+/*!
+ * This function disables a logical channel.
+ *
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @param       wait_for_stop   Flag to set whether to wait for channel end
+ *                              of frame or return immediately.
+ *
+ * @return      This function returns 0 on success or negative error code on
+ *              fail.
+ */
+int32_t ipu_disable_channel(ipu_channel_t channel, bool wait_for_stop)
+{
+       uint32_t reg;
+       unsigned long lock_flags;
+       uint32_t in_dma;
+       uint32_t out_dma;
+       uint32_t sec_dma = NO_DMA;
+       uint32_t thrd_dma = NO_DMA;
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       if ((g_channel_enable_mask & (1L << IPU_CHAN_ID(channel))) == 0) {
+               dev_err(g_ipu_dev, "Channel already disabled %d\n",
+                       IPU_CHAN_ID(channel));
+               spin_unlock_irqrestore(&ipu_lock, lock_flags);
+               return -EACCES;
+       }
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+       /* Get input and output dma channels */
+       out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER);
+       in_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER);
+
+       if ((idma_is_valid(in_dma) &&
+               !idma_is_set(IDMAC_CHA_EN, in_dma))
+               && (idma_is_valid(out_dma) &&
+               !idma_is_set(IDMAC_CHA_EN, out_dma)))
+               return -EINVAL;
+
+       if (g_sec_chan_en[IPU_CHAN_ID(channel)])
+               sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER);
+       if (g_thrd_chan_en[IPU_CHAN_ID(channel)]) {
+               sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER);
+               thrd_dma = channel_2_dma(channel, IPU_ALPHA_IN_BUFFER);
+       }
+
+       if ((channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC) ||
+           (channel == MEM_DC_SYNC)) {
+               if (channel == MEM_FG_SYNC)
+                       ipu_disp_set_window_pos(channel, 0, 0);
+
+               _ipu_dp_dc_disable(channel, false);
+
+               /*
+                * wait for BG channel EOF then disable FG-IDMAC,
+                * it avoid FG NFB4EOF error.
+                */
+               if (channel == MEM_FG_SYNC) {
+                       int timeout = 50;
+
+                       __raw_writel(IPUIRQ_2_MASK(IPU_IRQ_BG_SYNC_EOF),
+                                       IPUIRQ_2_STATREG(IPU_IRQ_BG_SYNC_EOF));
+                       while ((__raw_readl(IPUIRQ_2_STATREG(IPU_IRQ_BG_SYNC_EOF)) &
+                                       IPUIRQ_2_MASK(IPU_IRQ_BG_SYNC_EOF)) == 0) {
+                               msleep(10);
+                               timeout -= 10;
+                               if (timeout <= 0) {
+                                       dev_err(g_ipu_dev, "warning: wait for bg sync eof timeout\n");
+                                       break;
+                               }
+                       }
+               }
+       } else if (wait_for_stop) {
+               while (idma_is_set(IDMAC_CHA_BUSY, in_dma) ||
+                      idma_is_set(IDMAC_CHA_BUSY, out_dma) ||
+                       (g_sec_chan_en[IPU_CHAN_ID(channel)] &&
+                       idma_is_set(IDMAC_CHA_BUSY, sec_dma)) ||
+                       (g_thrd_chan_en[IPU_CHAN_ID(channel)] &&
+                       idma_is_set(IDMAC_CHA_BUSY, thrd_dma))) {
+                       uint32_t ret, irq = 0xffffffff;
+                       DECLARE_COMPLETION_ONSTACK(disable_comp);
+
+                       if (idma_is_set(IDMAC_CHA_BUSY, out_dma))
+                               irq = out_dma;
+                       if (g_sec_chan_en[IPU_CHAN_ID(channel)] &&
+                               idma_is_set(IDMAC_CHA_BUSY, sec_dma))
+                               irq = sec_dma;
+                       if (g_thrd_chan_en[IPU_CHAN_ID(channel)] &&
+                               idma_is_set(IDMAC_CHA_BUSY, thrd_dma))
+                               irq = thrd_dma;
+                       if (idma_is_set(IDMAC_CHA_BUSY, in_dma))
+                               irq = in_dma;
+
+                       if (irq == 0xffffffff) {
+                               dev_err(g_ipu_dev, "warning: no channel busy, break\n");
+                               break;
+                       }
+                       ret = ipu_request_irq(irq, disable_chan_irq_handler, 0, NULL, &disable_comp);
+                       if (ret < 0) {
+                               dev_err(g_ipu_dev, "irq %d in use\n", irq);
+                               break;
+                       } else {
+                               ret = wait_for_completion_timeout(&disable_comp, msecs_to_jiffies(200));
+                               ipu_free_irq(irq, &disable_comp);
+                               if (ret == 0) {
+                                       ipu_dump_registers();
+                                       dev_err(g_ipu_dev, "warning: disable ipu dma channel %d during its busy state\n", irq);
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       if ((channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC) ||
+           (channel == MEM_DC_SYNC)) {
+               reg = __raw_readl(IDMAC_WM_EN(in_dma));
+               __raw_writel(reg & ~idma_mask(in_dma), IDMAC_WM_EN(in_dma));
+       }
+
+       /* Disable IC task */
+       if (_ipu_is_ic_chan(in_dma) || _ipu_is_ic_chan(out_dma) ||
+               _ipu_is_irt_chan(in_dma) || _ipu_is_irt_chan(out_dma))
+               _ipu_ic_disable_task(channel);
+
+       /* Disable DMA channel(s) */
+       if (idma_is_valid(in_dma)) {
+               reg = __raw_readl(IDMAC_CHA_EN(in_dma));
+               __raw_writel(reg & ~idma_mask(in_dma), IDMAC_CHA_EN(in_dma));
+               __raw_writel(idma_mask(in_dma), IPU_CHA_CUR_BUF(in_dma));
+       }
+       if (idma_is_valid(out_dma)) {
+               reg = __raw_readl(IDMAC_CHA_EN(out_dma));
+               __raw_writel(reg & ~idma_mask(out_dma), IDMAC_CHA_EN(out_dma));
+               __raw_writel(idma_mask(out_dma), IPU_CHA_CUR_BUF(out_dma));
+       }
+       if (g_sec_chan_en[IPU_CHAN_ID(channel)] && idma_is_valid(sec_dma)) {
+               reg = __raw_readl(IDMAC_CHA_EN(sec_dma));
+               __raw_writel(reg & ~idma_mask(sec_dma), IDMAC_CHA_EN(sec_dma));
+               __raw_writel(idma_mask(sec_dma), IPU_CHA_CUR_BUF(sec_dma));
+       }
+       if (g_thrd_chan_en[IPU_CHAN_ID(channel)] && idma_is_valid(thrd_dma)) {
+               reg = __raw_readl(IDMAC_CHA_EN(thrd_dma));
+               __raw_writel(reg & ~idma_mask(thrd_dma), IDMAC_CHA_EN(thrd_dma));
+               if (channel == MEM_BG_SYNC || channel == MEM_FG_SYNC) {
+                       reg = __raw_readl(IDMAC_SEP_ALPHA);
+                       __raw_writel(reg & ~idma_mask(in_dma), IDMAC_SEP_ALPHA);
+               } else {
+                       reg = __raw_readl(IDMAC_SEP_ALPHA);
+                       __raw_writel(reg & ~idma_mask(sec_dma), IDMAC_SEP_ALPHA);
+               }
+               __raw_writel(idma_mask(thrd_dma), IPU_CHA_CUR_BUF(thrd_dma));
+       }
+
+       g_channel_enable_mask &= ~(1L << IPU_CHAN_ID(channel));
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+       /* Set channel buffers NOT to be ready */
+       if (idma_is_valid(in_dma)) {
+               ipu_clear_buffer_ready(channel, IPU_VIDEO_IN_BUFFER, 0);
+               ipu_clear_buffer_ready(channel, IPU_VIDEO_IN_BUFFER, 1);
+       }
+       if (idma_is_valid(out_dma)) {
+               ipu_clear_buffer_ready(channel, IPU_OUTPUT_BUFFER, 0);
+               ipu_clear_buffer_ready(channel, IPU_OUTPUT_BUFFER, 1);
+       }
+       if (g_sec_chan_en[IPU_CHAN_ID(channel)] && idma_is_valid(sec_dma)) {
+               ipu_clear_buffer_ready(channel, IPU_GRAPH_IN_BUFFER, 0);
+               ipu_clear_buffer_ready(channel, IPU_GRAPH_IN_BUFFER, 1);
+       }
+       if (g_thrd_chan_en[IPU_CHAN_ID(channel)] && idma_is_valid(thrd_dma)) {
+               ipu_clear_buffer_ready(channel, IPU_ALPHA_IN_BUFFER, 0);
+               ipu_clear_buffer_ready(channel, IPU_ALPHA_IN_BUFFER, 1);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(ipu_disable_channel);
+
+/*!
+ * This function enables CSI.
+ *
+ * @param       csi    csi num 0 or 1
+ *
+ * @return      This function returns 0 on success or negative error code on
+ *              fail.
+ */
+int32_t ipu_enable_csi(uint32_t csi)
+{
+       uint32_t reg;
+       unsigned long lock_flags;
+
+       if (csi > 1) {
+               dev_err(g_ipu_dev, "Wrong csi num_%d\n", csi);
+               return -EINVAL;
+       }
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+       ipu_csi_use_count[csi]++;
+
+       if (ipu_csi_use_count[csi] == 1) {
+               reg = __raw_readl(IPU_CONF);
+               if (csi == 0)
+                       __raw_writel(reg | IPU_CONF_CSI0_EN, IPU_CONF);
+               else
+                       __raw_writel(reg | IPU_CONF_CSI1_EN, IPU_CONF);
+       }
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+       return 0;
+}
+EXPORT_SYMBOL(ipu_enable_csi);
+
+/*!
+ * This function disables CSI.
+ *
+ * @param       csi    csi num 0 or 1
+ *
+ * @return      This function returns 0 on success or negative error code on
+ *              fail.
+ */
+int32_t ipu_disable_csi(uint32_t csi)
+{
+       uint32_t reg;
+       unsigned long lock_flags;
+
+       if (csi > 1) {
+               dev_err(g_ipu_dev, "Wrong csi num_%d\n", csi);
+               return -EINVAL;
+       }
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+       ipu_csi_use_count[csi]--;
+
+       if (ipu_csi_use_count[csi] == 0) {
+               reg = __raw_readl(IPU_CONF);
+               if (csi == 0)
+                       __raw_writel(reg & ~IPU_CONF_CSI0_EN, IPU_CONF);
+               else
+                       __raw_writel(reg & ~IPU_CONF_CSI1_EN, IPU_CONF);
+       }
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+       return 0;
+}
+EXPORT_SYMBOL(ipu_disable_csi);
+
+static irqreturn_t ipu_irq_handler(int irq, void *desc)
+{
+       int i;
+       uint32_t line;
+       irqreturn_t result = IRQ_NONE;
+       uint32_t int_stat;
+       const int err_reg[] = { 5, 6, 9, 10, 0 };
+       const int int_reg[] = { 1, 2, 3, 4, 11, 12, 13, 14, 15, 0 };
+
+       for (i = 0;; i++) {
+               if (err_reg[i] == 0)
+                       break;
+               int_stat = __raw_readl(IPU_INT_STAT(err_reg[i]));
+               int_stat &= __raw_readl(IPU_INT_CTRL(err_reg[i]));
+               if (int_stat) {
+                       __raw_writel(int_stat, IPU_INT_STAT(err_reg[i]));
+                       dev_err(g_ipu_dev,
+                               "IPU Error - IPU_INT_STAT_%d = 0x%08X\n",
+                               err_reg[i], int_stat);
+                       /* Disable interrupts so we only get error once */
+                       int_stat =
+                           __raw_readl(IPU_INT_CTRL(err_reg[i])) & ~int_stat;
+                       __raw_writel(int_stat, IPU_INT_CTRL(err_reg[i]));
+               }
+       }
+
+       for (i = 0;; i++) {
+               if (int_reg[i] == 0)
+                       break;
+               int_stat = __raw_readl(IPU_INT_STAT(int_reg[i]));
+               int_stat &= __raw_readl(IPU_INT_CTRL(int_reg[i]));
+               __raw_writel(int_stat, IPU_INT_STAT(int_reg[i]));
+               while ((line = ffs(int_stat)) != 0) {
+                       line--;
+                       int_stat &= ~(1UL << line);
+                       line += (int_reg[i] - 1) * 32;
+                       result |=
+                           ipu_irq_list[line].handler(line,
+                                                      ipu_irq_list[line].
+                                                      dev_id);
+               }
+       }
+
+       return result;
+}
+
+/*!
+ * This function enables the interrupt for the specified interrupt line.
+ * The interrupt lines are defined in \b ipu_irq_line enum.
+ *
+ * @param       irq             Interrupt line to enable interrupt for.
+ *
+ */
+void ipu_enable_irq(uint32_t irq)
+{
+       uint32_t reg;
+       unsigned long lock_flags;
+
+       if (!g_ipu_clk_enabled)
+               clk_enable(g_ipu_clk);
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       reg = __raw_readl(IPUIRQ_2_CTRLREG(irq));
+       reg |= IPUIRQ_2_MASK(irq);
+       __raw_writel(reg, IPUIRQ_2_CTRLREG(irq));
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+       if (!g_ipu_clk_enabled)
+               clk_disable(g_ipu_clk);
+}
+EXPORT_SYMBOL(ipu_enable_irq);
+
+/*!
+ * This function disables the interrupt for the specified interrupt line.
+ * The interrupt lines are defined in \b ipu_irq_line enum.
+ *
+ * @param       irq             Interrupt line to disable interrupt for.
+ *
+ */
+void ipu_disable_irq(uint32_t irq)
+{
+       uint32_t reg;
+       unsigned long lock_flags;
+
+       if (!g_ipu_clk_enabled)
+               clk_enable(g_ipu_clk);
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       reg = __raw_readl(IPUIRQ_2_CTRLREG(irq));
+       reg &= ~IPUIRQ_2_MASK(irq);
+       __raw_writel(reg, IPUIRQ_2_CTRLREG(irq));
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+       if (!g_ipu_clk_enabled)
+               clk_disable(g_ipu_clk);
+}
+EXPORT_SYMBOL(ipu_disable_irq);
+
+/*!
+ * This function clears the interrupt for the specified interrupt line.
+ * The interrupt lines are defined in \b ipu_irq_line enum.
+ *
+ * @param       irq             Interrupt line to clear interrupt for.
+ *
+ */
+void ipu_clear_irq(uint32_t irq)
+{
+       if (!g_ipu_clk_enabled)
+               clk_enable(g_ipu_clk);
+
+       __raw_writel(IPUIRQ_2_MASK(irq), IPUIRQ_2_STATREG(irq));
+
+       if (!g_ipu_clk_enabled)
+               clk_disable(g_ipu_clk);
+}
+EXPORT_SYMBOL(ipu_clear_irq);
+
+/*!
+ * This function returns the current interrupt status for the specified
+ * interrupt line. The interrupt lines are defined in \b ipu_irq_line enum.
+ *
+ * @param       irq             Interrupt line to get status for.
+ *
+ * @return      Returns true if the interrupt is pending/asserted or false if
+ *              the interrupt is not pending.
+ */
+bool ipu_get_irq_status(uint32_t irq)
+{
+       uint32_t reg;
+
+       if (!g_ipu_clk_enabled)
+               clk_enable(g_ipu_clk);
+
+       reg = __raw_readl(IPUIRQ_2_STATREG(irq));
+
+       if (!g_ipu_clk_enabled)
+               clk_disable(g_ipu_clk);
+
+       if (reg & IPUIRQ_2_MASK(irq))
+               return true;
+       else
+               return false;
+}
+EXPORT_SYMBOL(ipu_get_irq_status);
+
+/*!
+ * This function registers an interrupt handler function for the specified
+ * interrupt line. The interrupt lines are defined in \b ipu_irq_line enum.
+ *
+ * @param       irq             Interrupt line to get status for.
+ *
+ * @param       handler         Input parameter for address of the handler
+ *                              function.
+ *
+ * @param       irq_flags       Flags for interrupt mode. Currently not used.
+ *
+ * @param       devname         Input parameter for string name of driver
+ *                              registering the handler.
+ *
+ * @param       dev_id          Input parameter for pointer of data to be
+ *                              passed to the handler.
+ *
+ * @return      This function returns 0 on success or negative error code on
+ *              fail.
+ */
+int ipu_request_irq(uint32_t irq,
+                   irqreturn_t(*handler) (int, void *),
+                   uint32_t irq_flags, const char *devname, void *dev_id)
+{
+       unsigned long lock_flags;
+
+       BUG_ON(irq >= IPU_IRQ_COUNT);
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       if (ipu_irq_list[irq].handler != NULL) {
+               dev_err(g_ipu_dev,
+                       "handler already installed on irq %d\n", irq);
+               spin_unlock_irqrestore(&ipu_lock, lock_flags);
+               return -EINVAL;
+       }
+
+       ipu_irq_list[irq].handler = handler;
+       ipu_irq_list[irq].flags = irq_flags;
+       ipu_irq_list[irq].dev_id = dev_id;
+       ipu_irq_list[irq].name = devname;
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+       ipu_enable_irq(irq);    /* enable the interrupt */
+
+       return 0;
+}
+EXPORT_SYMBOL(ipu_request_irq);
+
+/*!
+ * This function unregisters an interrupt handler for the specified interrupt
+ * line. The interrupt lines are defined in \b ipu_irq_line enum.
+ *
+ * @param       irq             Interrupt line to get status for.
+ *
+ * @param       dev_id          Input parameter for pointer of data to be passed
+ *                              to the handler. This must match value passed to
+ *                              ipu_request_irq().
+ *
+ */
+void ipu_free_irq(uint32_t irq, void *dev_id)
+{
+       ipu_disable_irq(irq);   /* disable the interrupt */
+
+       if (ipu_irq_list[irq].dev_id == dev_id)
+               ipu_irq_list[irq].handler = NULL;
+}
+EXPORT_SYMBOL(ipu_free_irq);
+
+uint32_t ipu_get_cur_buffer_idx(ipu_channel_t channel, ipu_buffer_t type)
+{
+       uint32_t reg, dma_chan;
+
+       dma_chan = channel_2_dma(channel, type);
+       if (!idma_is_valid(dma_chan))
+               return -EINVAL;
+
+       reg = __raw_readl(IPU_CHA_CUR_BUF(dma_chan/32));
+       if (reg & idma_mask(dma_chan))
+               return 1;
+       else
+               return 0;
+}
+EXPORT_SYMBOL(ipu_get_cur_buffer_idx);
+
+uint32_t _ipu_channel_status(ipu_channel_t channel)
+{
+       uint32_t stat = 0;
+       uint32_t task_stat_reg = __raw_readl(IPU_PROC_TASK_STAT);
+
+       switch (channel) {
+       case MEM_PRP_VF_MEM:
+               stat = (task_stat_reg & TSTAT_VF_MASK) >> TSTAT_VF_OFFSET;
+               break;
+       case MEM_VDI_PRP_VF_MEM:
+               stat = (task_stat_reg & TSTAT_VF_MASK) >> TSTAT_VF_OFFSET;
+               break;
+       case MEM_ROT_VF_MEM:
+               stat =
+                   (task_stat_reg & TSTAT_VF_ROT_MASK) >> TSTAT_VF_ROT_OFFSET;
+               break;
+       case MEM_PRP_ENC_MEM:
+               stat = (task_stat_reg & TSTAT_ENC_MASK) >> TSTAT_ENC_OFFSET;
+               break;
+       case MEM_ROT_ENC_MEM:
+               stat =
+                   (task_stat_reg & TSTAT_ENC_ROT_MASK) >>
+                   TSTAT_ENC_ROT_OFFSET;
+               break;
+       case MEM_PP_MEM:
+               stat = (task_stat_reg & TSTAT_PP_MASK) >> TSTAT_PP_OFFSET;
+               break;
+       case MEM_ROT_PP_MEM:
+               stat =
+                   (task_stat_reg & TSTAT_PP_ROT_MASK) >> TSTAT_PP_ROT_OFFSET;
+               break;
+
+       default:
+               stat = TASK_STAT_IDLE;
+               break;
+       }
+       return stat;
+}
+
+int32_t ipu_swap_channel(ipu_channel_t from_ch, ipu_channel_t to_ch)
+{
+       uint32_t reg;
+       unsigned long lock_flags;
+
+       int from_dma = channel_2_dma(from_ch, IPU_INPUT_BUFFER);
+       int to_dma = channel_2_dma(to_ch, IPU_INPUT_BUFFER);
+
+       /* enable target channel */
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       reg = __raw_readl(IDMAC_CHA_EN(to_dma));
+       __raw_writel(reg | idma_mask(to_dma), IDMAC_CHA_EN(to_dma));
+
+       g_channel_enable_mask |= 1L << IPU_CHAN_ID(to_ch);
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+       /* switch dp dc */
+       _ipu_dp_dc_disable(from_ch, true);
+
+       /* disable source channel */
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       reg = __raw_readl(IDMAC_CHA_EN(from_dma));
+       __raw_writel(reg & ~idma_mask(from_dma), IDMAC_CHA_EN(from_dma));
+       __raw_writel(idma_mask(from_dma), IPU_CHA_CUR_BUF(from_dma));
+
+       g_channel_enable_mask &= ~(1L << IPU_CHAN_ID(from_ch));
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+       return 0;
+}
+EXPORT_SYMBOL(ipu_swap_channel);
+
+uint32_t bytes_per_pixel(uint32_t fmt)
+{
+       switch (fmt) {
+       case IPU_PIX_FMT_GENERIC:       /*generic data */
+       case IPU_PIX_FMT_RGB332:
+       case IPU_PIX_FMT_YUV420P:
+       case IPU_PIX_FMT_YVU420P:
+       case IPU_PIX_FMT_YUV422P:
+               return 1;
+               break;
+       case IPU_PIX_FMT_RGB565:
+       case IPU_PIX_FMT_YUYV:
+       case IPU_PIX_FMT_UYVY:
+               return 2;
+               break;
+       case IPU_PIX_FMT_BGR24:
+       case IPU_PIX_FMT_RGB24:
+               return 3;
+               break;
+       case IPU_PIX_FMT_GENERIC_32:    /*generic data */
+       case IPU_PIX_FMT_BGR32:
+       case IPU_PIX_FMT_BGRA32:
+       case IPU_PIX_FMT_RGB32:
+       case IPU_PIX_FMT_RGBA32:
+       case IPU_PIX_FMT_ABGR32:
+               return 4;
+               break;
+       default:
+               return 1;
+               break;
+       }
+       return 0;
+}
+EXPORT_SYMBOL(bytes_per_pixel);
+
+ipu_color_space_t format_to_colorspace(uint32_t fmt)
+{
+       switch (fmt) {
+       case IPU_PIX_FMT_RGB666:
+       case IPU_PIX_FMT_RGB565:
+       case IPU_PIX_FMT_BGR24:
+       case IPU_PIX_FMT_RGB24:
+       case IPU_PIX_FMT_GBR24:
+       case IPU_PIX_FMT_BGR32:
+       case IPU_PIX_FMT_BGRA32:
+       case IPU_PIX_FMT_RGB32:
+       case IPU_PIX_FMT_RGBA32:
+       case IPU_PIX_FMT_ABGR32:
+       case IPU_PIX_FMT_LVDS666:
+       case IPU_PIX_FMT_LVDS888:
+               return RGB;
+               break;
+
+       default:
+               return YCbCr;
+               break;
+       }
+       return RGB;
+}
+
+bool ipu_pixel_format_has_alpha(uint32_t fmt)
+{
+       switch (fmt) {
+       case IPU_PIX_FMT_RGBA32:
+       case IPU_PIX_FMT_BGRA32:
+       case IPU_PIX_FMT_ABGR32:
+               return true;
+               break;
+       default:
+               return false;
+               break;
+       }
+       return false;
+}
+
+void ipu_set_csc_coefficients(ipu_channel_t channel, int32_t param[][3])
+{
+       _ipu_dp_set_csc_coefficients(channel, param);
+}
+EXPORT_SYMBOL(ipu_set_csc_coefficients);
+
+static int ipu_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct imx_ipuv3_platform_data *plat_data = pdev->dev.platform_data;
+
+       if (g_ipu_clk_enabled) {
+               /* save and disable enabled channels*/
+               idma_enable_reg[0] = __raw_readl(IDMAC_CHA_EN(0));
+               idma_enable_reg[1] = __raw_readl(IDMAC_CHA_EN(32));
+               while ((__raw_readl(IDMAC_CHA_BUSY(0)) & idma_enable_reg[0])
+                       || (__raw_readl(IDMAC_CHA_BUSY(32)) &
+                               idma_enable_reg[1])) {
+                       /* disable channel not busy already */
+                       uint32_t chan_should_disable, timeout = 1000, time = 0;
+
+                       chan_should_disable =
+                               __raw_readl(IDMAC_CHA_BUSY(0))
+                                       ^ idma_enable_reg[0];
+                       __raw_writel((~chan_should_disable) &
+                                       idma_enable_reg[0], IDMAC_CHA_EN(0));
+                       chan_should_disable =
+                               __raw_readl(IDMAC_CHA_BUSY(1))
+                                       ^ idma_enable_reg[1];
+                       __raw_writel((~chan_should_disable) &
+                                       idma_enable_reg[1], IDMAC_CHA_EN(32));
+                       msleep(2);
+                       time += 2;
+                       if (time >= timeout)
+                               return -1;
+               }
+               __raw_writel(0, IDMAC_CHA_EN(0));
+               __raw_writel(0, IDMAC_CHA_EN(32));
+
+               /* save double buffer select regs */
+               ipu_cha_db_mode_reg[0] = __raw_readl(IPU_CHA_DB_MODE_SEL(0));
+               ipu_cha_db_mode_reg[1] = __raw_readl(IPU_CHA_DB_MODE_SEL(32));
+               ipu_cha_db_mode_reg[2] =
+                       __raw_readl(IPU_ALT_CHA_DB_MODE_SEL(0));
+               ipu_cha_db_mode_reg[3] =
+                       __raw_readl(IPU_ALT_CHA_DB_MODE_SEL(32));
+
+               /* save current buffer regs */
+               ipu_cha_cur_buf_reg[0] = __raw_readl(IPU_CHA_CUR_BUF(0));
+               ipu_cha_cur_buf_reg[1] = __raw_readl(IPU_CHA_CUR_BUF(32));
+               ipu_cha_cur_buf_reg[2] = __raw_readl(IPU_ALT_CUR_BUF0);
+               ipu_cha_cur_buf_reg[3] = __raw_readl(IPU_ALT_CUR_BUF1);
+
+               /* save sub-modules status and disable all */
+               ic_conf_reg = __raw_readl(IC_CONF);
+               __raw_writel(0, IC_CONF);
+               ipu_conf_reg = __raw_readl(IPU_CONF);
+               __raw_writel(0, IPU_CONF);
+
+               /* save buf ready regs */
+               buf_ready_reg[0] = __raw_readl(IPU_CHA_BUF0_RDY(0));
+               buf_ready_reg[1] = __raw_readl(IPU_CHA_BUF0_RDY(32));
+               buf_ready_reg[2] = __raw_readl(IPU_CHA_BUF1_RDY(0));
+               buf_ready_reg[3] = __raw_readl(IPU_CHA_BUF1_RDY(32));
+               buf_ready_reg[4] = __raw_readl(IPU_ALT_CHA_BUF0_RDY(0));
+               buf_ready_reg[5] = __raw_readl(IPU_ALT_CHA_BUF0_RDY(32));
+               buf_ready_reg[6] = __raw_readl(IPU_ALT_CHA_BUF1_RDY(0));
+               buf_ready_reg[7] = __raw_readl(IPU_ALT_CHA_BUF1_RDY(32));
+       }
+
+       if (plat_data->pg)
+               plat_data->pg(1);
+
+       return 0;
+}
+
+static int ipu_resume(struct platform_device *pdev)
+{
+       struct imx_ipuv3_platform_data *plat_data = pdev->dev.platform_data;
+
+       if (plat_data->pg)
+               plat_data->pg(0);
+
+       if (g_ipu_clk_enabled) {
+
+               /* restore buf ready regs */
+               __raw_writel(buf_ready_reg[0], IPU_CHA_BUF0_RDY(0));
+               __raw_writel(buf_ready_reg[1], IPU_CHA_BUF0_RDY(32));
+               __raw_writel(buf_ready_reg[2], IPU_CHA_BUF1_RDY(0));
+               __raw_writel(buf_ready_reg[3], IPU_CHA_BUF1_RDY(32));
+               __raw_writel(buf_ready_reg[4], IPU_ALT_CHA_BUF0_RDY(0));
+               __raw_writel(buf_ready_reg[5], IPU_ALT_CHA_BUF0_RDY(32));
+               __raw_writel(buf_ready_reg[6], IPU_ALT_CHA_BUF1_RDY(0));
+               __raw_writel(buf_ready_reg[7], IPU_ALT_CHA_BUF1_RDY(32));
+
+               /* re-enable sub-modules*/
+               __raw_writel(ipu_conf_reg, IPU_CONF);
+               __raw_writel(ic_conf_reg, IC_CONF);
+
+               /* restore double buffer select regs */
+               __raw_writel(ipu_cha_db_mode_reg[0], IPU_CHA_DB_MODE_SEL(0));
+               __raw_writel(ipu_cha_db_mode_reg[1], IPU_CHA_DB_MODE_SEL(32));
+               __raw_writel(ipu_cha_db_mode_reg[2],
+                               IPU_ALT_CHA_DB_MODE_SEL(0));
+               __raw_writel(ipu_cha_db_mode_reg[3],
+                               IPU_ALT_CHA_DB_MODE_SEL(32));
+
+               /* restore current buffer select regs */
+               __raw_writel(~(ipu_cha_cur_buf_reg[0]), IPU_CHA_CUR_BUF(0));
+               __raw_writel(~(ipu_cha_cur_buf_reg[1]), IPU_CHA_CUR_BUF(32));
+               __raw_writel(~(ipu_cha_cur_buf_reg[2]), IPU_ALT_CUR_BUF0);
+               __raw_writel(~(ipu_cha_cur_buf_reg[3]), IPU_ALT_CUR_BUF1);
+
+               /* restart idma channel*/
+               __raw_writel(idma_enable_reg[0], IDMAC_CHA_EN(0));
+               __raw_writel(idma_enable_reg[1], IDMAC_CHA_EN(32));
+       } else {
+               clk_enable(g_ipu_clk);
+               _ipu_dmfc_init(dmfc_type_setup, 1);
+               _ipu_init_dc_mappings();
+
+               /* Set sync refresh channels as high priority */
+               __raw_writel(0x18800000L, IDMAC_CHA_PRI(0));
+               clk_disable(g_ipu_clk);
+       }
+
+       return 0;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxcipu_driver = {
+       .driver = {
+                  .name = "imx-ipuv3",
+                  },
+       .probe = ipu_probe,
+       .remove = ipu_remove,
+       .suspend = ipu_suspend,
+       .resume = ipu_resume,
+};
+
+int32_t __init ipu_gen_init(void)
+{
+       int32_t ret;
+
+       ret = platform_driver_register(&mxcipu_driver);
+       return 0;
+}
+
+subsys_initcall(ipu_gen_init);
+
+static void __exit ipu_gen_uninit(void)
+{
+       platform_driver_unregister(&mxcipu_driver);
+}
+
+module_exit(ipu_gen_uninit);
diff --git a/drivers/mxc/ipu3/ipu_device.c b/drivers/mxc/ipu3/ipu_device.c
new file mode 100644 (file)
index 0000000..057ddf6
--- /dev/null
@@ -0,0 +1,511 @@
+/*
+ * Copyright 2005-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 ipu_device.c
+ *
+ * @brief This file contains the IPUv3 driver device interface and fops functions.
+ *
+ * @ingroup IPU
+ */
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/ipu.h>
+#include <asm/cacheflush.h>
+
+#include "ipu_prv.h"
+#include "ipu_regs.h"
+#include "ipu_param_mem.h"
+
+/* Strucutures and variables for exporting MXC IPU as device*/
+
+static int mxc_ipu_major;
+static struct class *mxc_ipu_class;
+
+DEFINE_SPINLOCK(event_lock);
+
+struct ipu_dev_irq_info {
+       wait_queue_head_t waitq;
+       int irq_pending;
+} irq_info[480];
+
+int register_ipu_device(void);
+
+/* Static functions */
+
+int get_events(ipu_event_info *p)
+{
+       unsigned long flags;
+       int ret = 0;
+
+       spin_lock_irqsave(&event_lock, flags);
+       if (irq_info[p->irq].irq_pending > 0)
+               irq_info[p->irq].irq_pending--;
+       else
+               ret = -1;
+       spin_unlock_irqrestore(&event_lock, flags);
+
+       return ret;
+}
+
+static irqreturn_t mxc_ipu_generic_handler(int irq, void *dev_id)
+{
+       irq_info[irq].irq_pending++;
+
+       /* Wakeup any blocking user context */
+       wake_up_interruptible(&(irq_info[irq].waitq));
+       return IRQ_HANDLED;
+}
+
+static int mxc_ipu_open(struct inode *inode, struct file *file)
+{
+       int ret = 0;
+       return ret;
+}
+
+static long mxc_ipu_ioctl(struct file *file,
+               unsigned int cmd, unsigned long arg)
+{
+       int ret = 0;
+
+       switch (cmd) {
+       case IPU_INIT_CHANNEL:
+               {
+                       ipu_channel_parm parm;
+
+                       if (copy_from_user
+                                       (&parm, (ipu_channel_parm *) arg,
+                                        sizeof(ipu_channel_parm)))
+                               return -EFAULT;
+
+                       if (!parm.flag) {
+                               ret =
+                                       ipu_init_channel(parm.channel,
+                                                       &parm.params);
+                       } else {
+                               ret = ipu_init_channel(parm.channel, NULL);
+                       }
+               }
+               break;
+       case IPU_UNINIT_CHANNEL:
+               {
+               ipu_channel_t ch;
+               int __user *argp = (void __user *)arg;
+               if (get_user(ch, argp))
+                               return -EFAULT;
+                       ipu_uninit_channel(ch);
+               }
+               break;
+       case IPU_INIT_CHANNEL_BUFFER:
+               {
+                       ipu_channel_buf_parm parm;
+                       if (copy_from_user
+                               (&parm, (ipu_channel_buf_parm *) arg,
+                               sizeof(ipu_channel_buf_parm)))
+                               return -EFAULT;
+
+                       ret =
+                               ipu_init_channel_buffer(
+                                               parm.channel, parm.type,
+                                               parm.pixel_fmt,
+                                               parm.width, parm.height,
+                                               parm.stride,
+                                               parm.rot_mode,
+                                               parm.phyaddr_0,
+                                               parm.phyaddr_1,
+                                               parm.u_offset,
+                                               parm.v_offset);
+
+               }
+               break;
+       case IPU_UPDATE_CHANNEL_BUFFER:
+               {
+                       ipu_channel_buf_parm parm;
+                       if (copy_from_user
+                               (&parm, (ipu_channel_buf_parm *) arg,
+                               sizeof(ipu_channel_buf_parm)))
+                               return -EFAULT;
+
+                       if ((parm.phyaddr_0 != (dma_addr_t) NULL)
+                               && (parm.phyaddr_1 == (dma_addr_t) NULL)) {
+                               ret =
+                                       ipu_update_channel_buffer(
+                                                       parm.channel,
+                                                       parm.type,
+                                                       parm.bufNum,
+                                                       parm.phyaddr_0);
+                       } else if ((parm.phyaddr_0 == (dma_addr_t) NULL)
+                               && (parm.phyaddr_1 != (dma_addr_t) NULL)) {
+                               ret =
+                                       ipu_update_channel_buffer(
+                                                       parm.channel,
+                                                       parm.type,
+                                                       parm.bufNum,
+                                                       parm.phyaddr_1);
+                       } else {
+                               ret = -1;
+                       }
+
+               }
+               break;
+       case IPU_SELECT_CHANNEL_BUFFER:
+               {
+                       ipu_channel_buf_parm parm;
+                       if (copy_from_user
+                               (&parm, (ipu_channel_buf_parm *) arg,
+                               sizeof(ipu_channel_buf_parm)))
+                               return -EFAULT;
+
+                       ret =
+                               ipu_select_buffer(parm.channel,
+                                       parm.type, parm.bufNum);
+
+               }
+               break;
+       case IPU_SELECT_MULTI_VDI_BUFFER:
+               {
+                       uint32_t parm;
+                       if (copy_from_user
+                               (&parm, (uint32_t *) arg,
+                               sizeof(uint32_t)))
+                               return -EFAULT;
+
+                       ret = ipu_select_multi_vdi_buffer(parm);
+               }
+               break;
+       case IPU_LINK_CHANNELS:
+               {
+                       ipu_channel_link link;
+                       if (copy_from_user
+                               (&link, (ipu_channel_link *) arg,
+                               sizeof(ipu_channel_link)))
+                               return -EFAULT;
+
+                       ret = ipu_link_channels(link.src_ch,
+                               link.dest_ch);
+
+               }
+               break;
+       case IPU_UNLINK_CHANNELS:
+               {
+                       ipu_channel_link link;
+                       if (copy_from_user
+                               (&link, (ipu_channel_link *) arg,
+                               sizeof(ipu_channel_link)))
+                               return -EFAULT;
+
+                       ret = ipu_unlink_channels(link.src_ch,
+                               link.dest_ch);
+
+               }
+               break;
+       case IPU_ENABLE_CHANNEL:
+               {
+                       ipu_channel_t ch;
+                       int __user *argp = (void __user *)arg;
+                       if (get_user(ch, argp))
+                               return -EFAULT;
+                       ipu_enable_channel(ch);
+               }
+               break;
+       case IPU_DISABLE_CHANNEL:
+               {
+                       ipu_channel_info info;
+                       if (copy_from_user
+                               (&info, (ipu_channel_info *) arg,
+                                sizeof(ipu_channel_info)))
+                               return -EFAULT;
+
+                       ret = ipu_disable_channel(info.channel,
+                               info.stop);
+               }
+               break;
+       case IPU_ENABLE_IRQ:
+               {
+                       uint32_t irq;
+                       int __user *argp = (void __user *)arg;
+                       if (get_user(irq, argp))
+                               return -EFAULT;
+                       ipu_enable_irq(irq);
+               }
+               break;
+       case IPU_DISABLE_IRQ:
+               {
+                       uint32_t irq;
+                       int __user *argp = (void __user *)arg;
+                       if (get_user(irq, argp))
+                               return -EFAULT;
+                       ipu_disable_irq(irq);
+               }
+               break;
+       case IPU_CLEAR_IRQ:
+               {
+                       uint32_t irq;
+                       int __user *argp = (void __user *)arg;
+                       if (get_user(irq, argp))
+                               return -EFAULT;
+                       ipu_clear_irq(irq);
+               }
+               break;
+       case IPU_FREE_IRQ:
+               {
+                       ipu_irq_info info;
+
+                       if (copy_from_user
+                                       (&info, (ipu_irq_info *) arg,
+                                        sizeof(ipu_irq_info)))
+                               return -EFAULT;
+
+                       ipu_free_irq(info.irq, info.dev_id);
+                       irq_info[info.irq].irq_pending = 0;
+               }
+               break;
+       case IPU_REQUEST_IRQ_STATUS:
+               {
+                       uint32_t irq;
+                       int __user *argp = (void __user *)arg;
+                       if (get_user(irq, argp))
+                               return -EFAULT;
+                       ret = ipu_get_irq_status(irq);
+               }
+               break;
+       case IPU_REGISTER_GENERIC_ISR:
+               {
+                       ipu_event_info info;
+                       if (copy_from_user
+                                       (&info, (ipu_event_info *) arg,
+                                        sizeof(ipu_event_info)))
+                               return -EFAULT;
+
+                       ret =
+                               ipu_request_irq(info.irq,
+                                       mxc_ipu_generic_handler,
+                                       0, "video_sink", info.dev);
+                       if (ret == 0)
+                               init_waitqueue_head(&(irq_info[info.irq].waitq));
+               }
+               break;
+       case IPU_GET_EVENT:
+               /* User will have to allocate event_type
+               structure and pass the pointer in arg */
+               {
+                       ipu_event_info info;
+                       int r = -1;
+
+                       if (copy_from_user
+                                       (&info, (ipu_event_info *) arg,
+                                        sizeof(ipu_event_info)))
+                               return -EFAULT;
+
+                       r = get_events(&info);
+                       if (r == -1) {
+                               if ((file->f_flags & O_NONBLOCK) &&
+                                       (irq_info[info.irq].irq_pending == 0))
+                                       return -EAGAIN;
+                               wait_event_interruptible_timeout(irq_info[info.irq].waitq,
+                                               (irq_info[info.irq].irq_pending != 0), 2 * HZ);
+                               r = get_events(&info);
+                       }
+                       ret = -1;
+                       if (r == 0) {
+                               if (!copy_to_user((ipu_event_info *) arg,
+                                       &info, sizeof(ipu_event_info)))
+                                       ret = 0;
+                       }
+               }
+               break;
+       case IPU_ALOC_MEM:
+               {
+                       ipu_mem_info info;
+                       if (copy_from_user
+                                       (&info, (ipu_mem_info *) arg,
+                                        sizeof(ipu_mem_info)))
+                               return -EFAULT;
+
+                       info.vaddr = dma_alloc_coherent(0,
+                                       PAGE_ALIGN(info.size),
+                                       &info.paddr,
+                                       GFP_DMA | GFP_KERNEL);
+                       if (info.vaddr == 0) {
+                               printk(KERN_ERR "dma alloc failed!\n");
+                               return -ENOBUFS;
+                       }
+                       if (copy_to_user((ipu_mem_info *) arg, &info,
+                                       sizeof(ipu_mem_info)) > 0)
+                               return -EFAULT;
+               }
+               break;
+       case IPU_FREE_MEM:
+               {
+                       ipu_mem_info info;
+                       if (copy_from_user
+                                       (&info, (ipu_mem_info *) arg,
+                                        sizeof(ipu_mem_info)))
+                               return -EFAULT;
+
+                       if (info.vaddr)
+                               dma_free_coherent(0, PAGE_ALIGN(info.size),
+                                       info.vaddr, info.paddr);
+                       else
+                               return -EFAULT;
+               }
+               break;
+       case IPU_IS_CHAN_BUSY:
+               {
+                       ipu_channel_t chan;
+                       if (copy_from_user
+                                       (&chan, (ipu_channel_t *)arg,
+                                        sizeof(ipu_channel_t)))
+                               return -EFAULT;
+
+                       if (ipu_is_channel_busy(chan))
+                               ret = 1;
+                       else
+                               ret = 0;
+               }
+               break;
+       case IPU_CALC_STRIPES_SIZE:
+               {
+                       ipu_stripe_parm stripe_parm;
+
+                       if (copy_from_user (&stripe_parm, (ipu_stripe_parm *)arg,
+                                        sizeof(ipu_stripe_parm)))
+                               return -EFAULT;
+                       ipu_calc_stripes_sizes(stripe_parm.input_width,
+                                               stripe_parm.output_width,
+                                               stripe_parm.maximal_stripe_width,
+                                               stripe_parm.cirr,
+                                               stripe_parm.equal_stripes,
+                                               stripe_parm.input_pixelformat,
+                                               stripe_parm.output_pixelformat,
+                                               &stripe_parm.left,
+                                               &stripe_parm.right);
+                       if (copy_to_user((ipu_stripe_parm *) arg, &stripe_parm,
+                                       sizeof(ipu_stripe_parm)) > 0)
+                               return -EFAULT;
+               }
+               break;
+       case IPU_UPDATE_BUF_OFFSET:
+               {
+                       ipu_buf_offset_parm offset_parm;
+
+                       if (copy_from_user (&offset_parm, (ipu_buf_offset_parm *)arg,
+                                        sizeof(ipu_buf_offset_parm)))
+                               return -EFAULT;
+                       ret = ipu_update_channel_offset(offset_parm.channel,
+                                                       offset_parm.type,
+                                                       offset_parm.pixel_fmt,
+                                                       offset_parm.width,
+                                                       offset_parm.height,
+                                                       offset_parm.stride,
+                                                       offset_parm.u_offset,
+                                                       offset_parm.v_offset,
+                                                       offset_parm.vertical_offset,
+                                                       offset_parm.horizontal_offset);
+               }
+               break;
+       case IPU_CSC_UPDATE:
+               {
+                       int param[5][3];
+                       ipu_csc_update csc;
+                       if (copy_from_user(&csc, (void *) arg,
+                                          sizeof(ipu_csc_update)))
+                               return -EFAULT;
+                       if (copy_from_user(&param[0][0], (void *) csc.param,
+                                          sizeof(param)))
+                               return -EFAULT;
+                       ipu_set_csc_coefficients(csc.channel, param);
+               }
+               break;
+       default:
+               break;
+       }
+       return ret;
+}
+
+static int mxc_ipu_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+       if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
+                               vma->vm_end - vma->vm_start,
+                               vma->vm_page_prot)) {
+               printk(KERN_ERR
+                               "mmap failed!\n");
+               return -ENOBUFS;
+       }
+       return 0;
+}
+
+static int mxc_ipu_release(struct inode *inode, struct file *file)
+{
+       return 0;
+}
+
+static struct file_operations mxc_ipu_fops = {
+       .owner = THIS_MODULE,
+       .open = mxc_ipu_open,
+       .mmap = mxc_ipu_mmap,
+       .release = mxc_ipu_release,
+       .unlocked_ioctl = mxc_ipu_ioctl,
+};
+
+int register_ipu_device()
+{
+       int ret = 0;
+       struct device *temp;
+       mxc_ipu_major = register_chrdev(0, "mxc_ipu", &mxc_ipu_fops);
+       if (mxc_ipu_major < 0) {
+               printk(KERN_ERR
+                       "Unable to register Mxc Ipu as a char device\n");
+               return mxc_ipu_major;
+       }
+
+       mxc_ipu_class = class_create(THIS_MODULE, "mxc_ipu");
+       if (IS_ERR(mxc_ipu_class)) {
+               printk(KERN_ERR "Unable to create class for Mxc Ipu\n");
+               ret = PTR_ERR(mxc_ipu_class);
+               goto err1;
+       }
+
+       temp = device_create(mxc_ipu_class, NULL, MKDEV(mxc_ipu_major, 0),
+                       NULL, "mxc_ipu");
+
+       if (IS_ERR(temp)) {
+               printk(KERN_ERR "Unable to create class device for Mxc Ipu\n");
+               ret = PTR_ERR(temp);
+               goto err2;
+       }
+       spin_lock_init(&event_lock);
+
+       return ret;
+
+err2:
+       class_destroy(mxc_ipu_class);
+err1:
+       unregister_chrdev(mxc_ipu_major, "mxc_ipu");
+       return ret;
+
+}
diff --git a/drivers/mxc/ipu3/ipu_disp.c b/drivers/mxc/ipu3/ipu_disp.c
new file mode 100644 (file)
index 0000000..01dd6a6
--- /dev/null
@@ -0,0 +1,1847 @@
+/*
+ * Copyright 2005-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 ipu_disp.c
+ *
+ * @brief IPU display submodule API functions
+ *
+ * @ingroup IPU
+ */
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/ipu.h>
+#include <linux/clk.h>
+#include <asm/atomic.h>
+#include <mach/clock.h>
+#include "ipu_prv.h"
+#include "ipu_regs.h"
+#include "ipu_param_mem.h"
+
+enum csc_type_t {
+       RGB2YUV = 0,
+       YUV2RGB,
+       RGB2RGB,
+       YUV2YUV,
+       CSC_NONE,
+       CSC_NUM
+};
+
+struct dp_csc_param_t {
+       int mode;
+       void *coeff;
+};
+
+#define SYNC_WAVE 0
+#define ASYNC_SER_WAVE 6
+
+/* DC display ID assignments */
+#define DC_DISP_ID_SYNC(di)    (di)
+#define DC_DISP_ID_SERIAL      2
+#define DC_DISP_ID_ASYNC       3
+
+int dmfc_type_setup;
+static int dmfc_size_28, dmfc_size_29, dmfc_size_24, dmfc_size_27, dmfc_size_23;
+
+void _ipu_dmfc_init(int dmfc_type, int first)
+{
+       u32 dmfc_wr_chan, dmfc_dp_chan;
+
+       if (first) {
+               if (dmfc_type_setup > dmfc_type)
+                       dmfc_type = dmfc_type_setup;
+               else
+                       dmfc_type_setup = dmfc_type;
+
+               /* disable DMFC-IC channel*/
+               __raw_writel(0x2, DMFC_IC_CTRL);
+       } else if (dmfc_type_setup >= DMFC_HIGH_RESOLUTION_DC) {
+               printk(KERN_DEBUG "DMFC high resolution has set, will not change\n");
+               return;
+       } else
+               dmfc_type_setup = dmfc_type;
+
+       if (dmfc_type == DMFC_HIGH_RESOLUTION_DC) {
+               /* 1 - segment 0~3;
+                * 5B - segement 4, 5;
+                * 5F - segement 6, 7;
+                * 1C, 2C and 6B, 6F unused;
+                */
+               printk(KERN_INFO "IPU DMFC DC HIGH RESOLUTION: 1(0~3), 5B(4,5), 5F(6,7)\n");
+               dmfc_wr_chan = 0x00000088;
+               dmfc_dp_chan = 0x00009694;
+               dmfc_size_28 = 256*4;
+               dmfc_size_29 = 0;
+               dmfc_size_24 = 0;
+               dmfc_size_27 = 128*4;
+               dmfc_size_23 = 128*4;
+       } else if (dmfc_type == DMFC_HIGH_RESOLUTION_DP) {
+               /* 1 - segment 0, 1;
+                * 5B - segement 2~5;
+                * 5F - segement 6,7;
+                * 1C, 2C and 6B, 6F unused;
+                */
+               printk(KERN_INFO "IPU DMFC DP HIGH RESOLUTION: 1(0,1), 5B(2~5), 5F(6,7)\n");
+               dmfc_wr_chan = 0x00000090;
+               dmfc_dp_chan = 0x0000968a;
+               dmfc_size_28 = 128*4;
+               dmfc_size_29 = 0;
+               dmfc_size_24 = 0;
+               dmfc_size_27 = 128*4;
+               dmfc_size_23 = 256*4;
+       } else if (dmfc_type == DMFC_HIGH_RESOLUTION_ONLY_DP) {
+               /* 5B - segement 0~3;
+                * 5F - segement 4~7;
+                * 1, 1C, 2C and 6B, 6F unused;
+                */
+               printk(KERN_INFO "IPU DMFC ONLY-DP HIGH RESOLUTION: 5B(0~3), 5F(4~7)\n");
+               dmfc_wr_chan = 0x00000000;
+               dmfc_dp_chan = 0x00008c88;
+               dmfc_size_28 = 0;
+               dmfc_size_29 = 0;
+               dmfc_size_24 = 0;
+               dmfc_size_27 = 256*4;
+               dmfc_size_23 = 256*4;
+       } else {
+               /* 1 - segment 0, 1;
+                * 5B - segement 4, 5;
+                * 5F - segement 6, 7;
+                * 1C, 2C and 6B, 6F unused;
+                */
+               printk(KERN_INFO "IPU DMFC NORMAL mode: 1(0~1), 5B(4,5), 5F(6,7)\n");
+               dmfc_wr_chan = 0x00000090;
+               dmfc_dp_chan = 0x00009694;
+               dmfc_size_28 = 128*4;
+               dmfc_size_29 = 0;
+               dmfc_size_24 = 0;
+               dmfc_size_27 = 128*4;
+               dmfc_size_23 = 128*4;
+       }
+       __raw_writel(dmfc_wr_chan, DMFC_WR_CHAN);
+       __raw_writel(0x202020F6, DMFC_WR_CHAN_DEF);
+       __raw_writel(dmfc_dp_chan, DMFC_DP_CHAN);
+       /* Enable chan 5 watermark set at 5 bursts and clear at 7 bursts */
+       __raw_writel(0x2020F6F6, DMFC_DP_CHAN_DEF);
+}
+
+static int __init dmfc_setup(char *options)
+{
+       get_option(&options, &dmfc_type_setup);
+       if (dmfc_type_setup > DMFC_HIGH_RESOLUTION_ONLY_DP)
+               dmfc_type_setup = DMFC_HIGH_RESOLUTION_ONLY_DP;
+       return 1;
+}
+__setup("dmfc=", dmfc_setup);
+
+void _ipu_dmfc_set_wait4eot(int dma_chan, int width)
+{
+       u32 dmfc_gen1 = __raw_readl(DMFC_GENERAL1);
+
+       if (width >= HIGH_RESOLUTION_WIDTH) {
+               if (dma_chan == 23)
+                       _ipu_dmfc_init(DMFC_HIGH_RESOLUTION_DP, 0);
+               else if (dma_chan == 28)
+                       _ipu_dmfc_init(DMFC_HIGH_RESOLUTION_DC, 0);
+       }
+
+       if (dma_chan == 23) { /*5B*/
+               if (dmfc_size_23/width > 3)
+                       dmfc_gen1 |= 1UL << 20;
+               else
+                       dmfc_gen1 &= ~(1UL << 20);
+       } else if (dma_chan == 24) { /*6B*/
+               if (dmfc_size_24/width > 1)
+                       dmfc_gen1 |= 1UL << 22;
+               else
+                       dmfc_gen1 &= ~(1UL << 22);
+       } else if (dma_chan == 27) { /*5F*/
+               if (dmfc_size_27/width > 2)
+                       dmfc_gen1 |= 1UL << 21;
+               else
+                       dmfc_gen1 &= ~(1UL << 21);
+       } else if (dma_chan == 28) { /*1*/
+               if (dmfc_size_28/width > 2)
+                       dmfc_gen1 |= 1UL << 16;
+               else
+                       dmfc_gen1 &= ~(1UL << 16);
+       } else if (dma_chan == 29) { /*6F*/
+               if (dmfc_size_29/width > 1)
+                       dmfc_gen1 |= 1UL << 23;
+               else
+                       dmfc_gen1 &= ~(1UL << 23);
+       }
+
+       __raw_writel(dmfc_gen1, DMFC_GENERAL1);
+}
+
+static void _ipu_di_data_wave_config(int di,
+                                    int wave_gen,
+                                    int access_size, int component_size)
+{
+       u32 reg;
+       reg = (access_size << DI_DW_GEN_ACCESS_SIZE_OFFSET) |
+           (component_size << DI_DW_GEN_COMPONENT_SIZE_OFFSET);
+       __raw_writel(reg, DI_DW_GEN(di, wave_gen));
+}
+
+static void _ipu_di_data_pin_config(int di, int wave_gen, int di_pin, int set,
+                                   int up, int down)
+{
+       u32 reg;
+
+       reg = __raw_readl(DI_DW_GEN(di, wave_gen));
+       reg &= ~(0x3 << (di_pin * 2));
+       reg |= set << (di_pin * 2);
+       __raw_writel(reg, DI_DW_GEN(di, wave_gen));
+
+       __raw_writel((down << 16) | up, DI_DW_SET(di, wave_gen, set));
+}
+
+static void _ipu_di_sync_config(int di, int wave_gen,
+                               int run_count, int run_src,
+                               int offset_count, int offset_src,
+                               int repeat_count, int cnt_clr_src,
+                               int cnt_polarity_gen_en,
+                               int cnt_polarity_clr_src,
+                               int cnt_polarity_trigger_src,
+                               int cnt_up, int cnt_down)
+{
+       u32 reg;
+
+       if ((run_count >= 0x1000) || (offset_count >= 0x1000) || (repeat_count >= 0x1000) ||
+               (cnt_up >= 0x400) || (cnt_down >= 0x400)) {
+               dev_err(g_ipu_dev, "DI%d counters out of range.\n", di);
+               return;
+       }
+
+       reg = (run_count << 19) | (++run_src << 16) |
+           (offset_count << 3) | ++offset_src;
+       __raw_writel(reg, DI_SW_GEN0(di, wave_gen));
+       reg = (cnt_polarity_gen_en << 29) | (++cnt_clr_src << 25) |
+           (++cnt_polarity_trigger_src << 12) | (++cnt_polarity_clr_src << 9);
+       reg |= (cnt_down << 16) | cnt_up;
+       if (repeat_count == 0) {
+               /* Enable auto reload */
+               reg |= 0x10000000;
+       }
+       __raw_writel(reg, DI_SW_GEN1(di, wave_gen));
+       reg = __raw_readl(DI_STP_REP(di, wave_gen));
+       reg &= ~(0xFFFF << (16 * ((wave_gen - 1) & 0x1)));
+       reg |= repeat_count << (16 * ((wave_gen - 1) & 0x1));
+       __raw_writel(reg, DI_STP_REP(di, wave_gen));
+}
+
+static void _ipu_dc_map_link(int current_map,
+                            int base_map_0, int buf_num_0,
+                            int base_map_1, int buf_num_1,
+                            int base_map_2, int buf_num_2)
+{
+       int ptr_0 = base_map_0 * 3 + buf_num_0;
+       int ptr_1 = base_map_1 * 3 + buf_num_1;
+       int ptr_2 = base_map_2 * 3 + buf_num_2;
+       int ptr;
+       u32 reg;
+       ptr = (ptr_2 << 10) +  (ptr_1 << 5) + ptr_0;
+
+       reg = __raw_readl(DC_MAP_CONF_PTR(current_map));
+       reg &= ~(0x1F << ((16 * (current_map & 0x1))));
+       reg |= ptr << ((16 * (current_map & 0x1)));
+       __raw_writel(reg, DC_MAP_CONF_PTR(current_map));
+}
+
+static void _ipu_dc_map_config(int map, int byte_num, int offset, int mask)
+{
+       int ptr = map * 3 + byte_num;
+       u32 reg;
+
+       reg = __raw_readl(DC_MAP_CONF_VAL(ptr));
+       reg &= ~(0xFFFF << (16 * (ptr & 0x1)));
+       reg |= ((offset << 8) | mask) << (16 * (ptr & 0x1));
+       __raw_writel(reg, DC_MAP_CONF_VAL(ptr));
+
+       reg = __raw_readl(DC_MAP_CONF_PTR(map));
+       reg &= ~(0x1F << ((16 * (map & 0x1)) + (5 * byte_num)));
+       reg |= ptr << ((16 * (map & 0x1)) + (5 * byte_num));
+       __raw_writel(reg, DC_MAP_CONF_PTR(map));
+}
+
+static void _ipu_dc_map_clear(int map)
+{
+       u32 reg = __raw_readl(DC_MAP_CONF_PTR(map));
+       __raw_writel(reg & ~(0xFFFF << (16 * (map & 0x1))),
+                    DC_MAP_CONF_PTR(map));
+}
+
+static void _ipu_dc_write_tmpl(int word, u32 opcode, u32 operand, int map,
+                              int wave, int glue, int sync, int stop)
+{
+       u32 reg;
+
+       if (opcode == WRG) {
+               reg = sync;
+               reg |= (glue << 4);
+               reg |= (++wave << 11);
+               reg |= ((operand & 0x1FFFF) << 15);
+               __raw_writel(reg, ipu_dc_tmpl_reg + word * 2);
+
+               reg = (operand >> 17);
+               reg |= opcode << 7;
+               reg |= (stop << 9);
+               __raw_writel(reg, ipu_dc_tmpl_reg + word * 2 + 1);
+       } else {
+               reg = sync;
+               reg |= (glue << 4);
+               reg |= (++wave << 11);
+               reg |= (++map << 15);
+               reg |= (operand << 20) & 0xFFF00000;
+               __raw_writel(reg, ipu_dc_tmpl_reg + word * 2);
+
+               reg = (operand >> 12);
+               reg |= opcode << 4;
+               reg |= (stop << 9);
+               __raw_writel(reg, ipu_dc_tmpl_reg + word * 2 + 1);
+       }
+}
+
+static void _ipu_dc_link_event(int chan, int event, int addr, int priority)
+{
+       u32 reg;
+       u32 address_shift;
+       if (event < DC_EVEN_UGDE0) {
+               reg = __raw_readl(DC_RL_CH(chan, event));
+               reg &= ~(0xFFFF << (16 * (event & 0x1)));
+               reg |= ((addr << 8) | priority) << (16 * (event & 0x1));
+               __raw_writel(reg, DC_RL_CH(chan, event));
+       } else {
+               reg = __raw_readl(DC_UGDE_0((event - DC_EVEN_UGDE0) / 2));
+               if ((event - DC_EVEN_UGDE0) & 0x1) {
+                       reg &= ~(0x2FF << 16);
+                       reg |= (addr << 16);
+                       reg |= priority ? (2 << 24) : 0x0;
+               } else {
+                       reg &= ~0xFC00FFFF;
+                       if (priority)
+                               chan = (chan >> 1) +
+                                       ((((chan & 0x1) + ((chan & 0x2) >> 1))) | (chan >> 3));
+                       else
+                               chan = 0x7;
+                       address_shift = ((event - DC_EVEN_UGDE0) >> 1) ? 7 : 8;
+                       reg |= (addr << address_shift) | (priority << 3) | chan;
+               }
+               __raw_writel(reg, DC_UGDE_0((event - DC_EVEN_UGDE0) / 2));
+       }
+}
+
+/*     Y = R *  1.200 + G *  2.343 + B *  .453 + 0.250;
+       U = R * -.672 + G * -1.328 + B *  2.000 + 512.250.;
+       V = R *  2.000 + G * -1.672 + B * -.328 + 512.250.;*/
+static const int rgb2ycbcr_coeff[5][3] = {
+       {0x4D, 0x96, 0x1D},
+       {-0x2B, -0x55, 0x80},
+       {0x80, -0x6B, -0x15},
+       {0x0000, 0x0200, 0x0200},       /* B0, B1, B2 */
+       {0x2, 0x2, 0x2},        /* S0, S1, S2 */
+};
+
+/*     R = (1.164 * (Y - 16)) + (1.596 * (Cr - 128));
+       G = (1.164 * (Y - 16)) - (0.392 * (Cb - 128)) - (0.813 * (Cr - 128));
+       B = (1.164 * (Y - 16)) + (2.017 * (Cb - 128); */
+static const int ycbcr2rgb_coeff[5][3] = {
+       {0x095, 0x000, 0x0CC},
+       {0x095, 0x3CE, 0x398},
+       {0x095, 0x0FF, 0x000},
+       {0x3E42, 0x010A, 0x3DD6},       /*B0,B1,B2 */
+       {0x1, 0x1, 0x1},        /*S0,S1,S2 */
+};
+
+#define mask_a(a) ((u32)(a) & 0x3FF)
+#define mask_b(b) ((u32)(b) & 0x3FFF)
+
+/* Pls keep S0, S1 and S2 as 0x2 by using this convertion */
+static int _rgb_to_yuv(int n, int red, int green, int blue)
+{
+       int c;
+       c = red * rgb2ycbcr_coeff[n][0];
+       c += green * rgb2ycbcr_coeff[n][1];
+       c += blue * rgb2ycbcr_coeff[n][2];
+       c /= 16;
+       c += rgb2ycbcr_coeff[3][n] * 4;
+       c += 8;
+       c /= 16;
+       if (c < 0)
+               c = 0;
+       if (c > 255)
+               c = 255;
+       return c;
+}
+
+/*
+ * Row is for BG:      RGB2YUV YUV2RGB RGB2RGB YUV2YUV CSC_NONE
+ * Column is for FG:   RGB2YUV YUV2RGB RGB2RGB YUV2YUV CSC_NONE
+ */
+static struct dp_csc_param_t dp_csc_array[CSC_NUM][CSC_NUM] = {
+{{DP_COM_CONF_CSC_DEF_BOTH, &rgb2ycbcr_coeff}, {0, 0}, {0, 0}, {DP_COM_CONF_CSC_DEF_BG, &rgb2ycbcr_coeff}, {DP_COM_CONF_CSC_DEF_BG, &rgb2ycbcr_coeff} },
+{{0, 0}, {DP_COM_CONF_CSC_DEF_BOTH, &ycbcr2rgb_coeff}, {DP_COM_CONF_CSC_DEF_BG, &ycbcr2rgb_coeff}, {0, 0}, {DP_COM_CONF_CSC_DEF_BG, &ycbcr2rgb_coeff} },
+{{0, 0}, {DP_COM_CONF_CSC_DEF_FG, &ycbcr2rgb_coeff}, {0, 0}, {0, 0}, {0, 0} },
+{{DP_COM_CONF_CSC_DEF_FG, &rgb2ycbcr_coeff}, {0, 0}, {0, 0}, {0, 0}, {0, 0} },
+{{DP_COM_CONF_CSC_DEF_FG, &rgb2ycbcr_coeff}, {DP_COM_CONF_CSC_DEF_FG, &ycbcr2rgb_coeff}, {0, 0}, {0, 0}, {0, 0} }
+};
+
+static enum csc_type_t fg_csc_type = CSC_NONE, bg_csc_type = CSC_NONE;
+static int color_key_4rgb = 1;
+
+void __ipu_dp_csc_setup(int dp, struct dp_csc_param_t dp_csc_param,
+                       bool srm_mode_update)
+{
+       u32 reg;
+       const int (*coeff)[5][3];
+
+       if (dp_csc_param.mode >= 0) {
+               reg = __raw_readl(DP_COM_CONF(dp));
+               reg &= ~DP_COM_CONF_CSC_DEF_MASK;
+               reg |= dp_csc_param.mode;
+               __raw_writel(reg, DP_COM_CONF(dp));
+       }
+
+       coeff = dp_csc_param.coeff;
+
+       if (coeff) {
+               __raw_writel(mask_a((*coeff)[0][0]) |
+                               (mask_a((*coeff)[0][1]) << 16), DP_CSC_A_0(dp));
+               __raw_writel(mask_a((*coeff)[0][2]) |
+                               (mask_a((*coeff)[1][0]) << 16), DP_CSC_A_1(dp));
+               __raw_writel(mask_a((*coeff)[1][1]) |
+                               (mask_a((*coeff)[1][2]) << 16), DP_CSC_A_2(dp));
+               __raw_writel(mask_a((*coeff)[2][0]) |
+                               (mask_a((*coeff)[2][1]) << 16), DP_CSC_A_3(dp));
+               __raw_writel(mask_a((*coeff)[2][2]) |
+                               (mask_b((*coeff)[3][0]) << 16) |
+                               ((*coeff)[4][0] << 30), DP_CSC_0(dp));
+               __raw_writel(mask_b((*coeff)[3][1]) | ((*coeff)[4][1] << 14) |
+                               (mask_b((*coeff)[3][2]) << 16) |
+                               ((*coeff)[4][2] << 30), DP_CSC_1(dp));
+       }
+
+       if (srm_mode_update) {
+               reg = __raw_readl(IPU_SRM_PRI2) | 0x8;
+               __raw_writel(reg, IPU_SRM_PRI2);
+       }
+}
+
+int _ipu_dp_init(ipu_channel_t channel, uint32_t in_pixel_fmt,
+                uint32_t out_pixel_fmt)
+{
+       int in_fmt, out_fmt;
+       int dp;
+       int partial = false;
+       uint32_t reg;
+
+       if (channel == MEM_FG_SYNC) {
+               dp = DP_SYNC;
+               partial = true;
+       } else if (channel == MEM_BG_SYNC) {
+               dp = DP_SYNC;
+               partial = false;
+       } else if (channel == MEM_BG_ASYNC0) {
+               dp = DP_ASYNC0;
+               partial = false;
+       } else {
+               return -EINVAL;
+       }
+
+       in_fmt = format_to_colorspace(in_pixel_fmt);
+       out_fmt = format_to_colorspace(out_pixel_fmt);
+
+       if (partial) {
+               if (in_fmt == RGB) {
+                       if (out_fmt == RGB)
+                               fg_csc_type = RGB2RGB;
+                       else
+                               fg_csc_type = RGB2YUV;
+               } else {
+                       if (out_fmt == RGB)
+                               fg_csc_type = YUV2RGB;
+                       else
+                               fg_csc_type = YUV2YUV;
+               }
+       } else {
+               if (in_fmt == RGB) {
+                       if (out_fmt == RGB)
+                               bg_csc_type = RGB2RGB;
+                       else
+                               bg_csc_type = RGB2YUV;
+               } else {
+                       if (out_fmt == RGB)
+                               bg_csc_type = YUV2RGB;
+                       else
+                               bg_csc_type = YUV2YUV;
+               }
+       }
+
+       /* Transform color key from rgb to yuv if CSC is enabled */
+       reg = __raw_readl(DP_COM_CONF(dp));
+       if (color_key_4rgb && (reg & DP_COM_CONF_GWCKE) &&
+                       (((fg_csc_type == RGB2YUV) && (bg_csc_type == YUV2YUV)) ||
+                        ((fg_csc_type == YUV2YUV) && (bg_csc_type == RGB2YUV)) ||
+                        ((fg_csc_type == YUV2YUV) && (bg_csc_type == YUV2YUV)) ||
+                        ((fg_csc_type == YUV2RGB) && (bg_csc_type == YUV2RGB)))) {
+               int red, green, blue;
+               int y, u, v;
+               uint32_t color_key = __raw_readl(DP_GRAPH_WIND_CTRL(dp)) & 0xFFFFFFL;
+
+               dev_dbg(g_ipu_dev, "_ipu_dp_init color key 0x%x need change to yuv fmt!\n", color_key);
+
+               red = (color_key >> 16) & 0xFF;
+               green = (color_key >> 8) & 0xFF;
+               blue = color_key & 0xFF;
+
+               y = _rgb_to_yuv(0, red, green, blue);
+               u = _rgb_to_yuv(1, red, green, blue);
+               v = _rgb_to_yuv(2, red, green, blue);
+               color_key = (y << 16) | (u << 8) | v;
+
+               reg = __raw_readl(DP_GRAPH_WIND_CTRL(dp)) & 0xFF000000L;
+               __raw_writel(reg | color_key, DP_GRAPH_WIND_CTRL(dp));
+               color_key_4rgb = 0;
+
+               dev_dbg(g_ipu_dev, "_ipu_dp_init color key change to yuv fmt 0x%x!\n", color_key);
+       }
+
+       __ipu_dp_csc_setup(dp, dp_csc_array[bg_csc_type][fg_csc_type], true);
+
+       return 0;
+}
+
+void _ipu_dp_uninit(ipu_channel_t channel)
+{
+       int dp;
+       int partial = false;
+
+       if (channel == MEM_FG_SYNC) {
+               dp = DP_SYNC;
+               partial = true;
+       } else if (channel == MEM_BG_SYNC) {
+               dp = DP_SYNC;
+               partial = false;
+       } else if (channel == MEM_BG_ASYNC0) {
+               dp = DP_ASYNC0;
+               partial = false;
+       } else {
+               return;
+       }
+
+       if (partial)
+               fg_csc_type = CSC_NONE;
+       else
+               bg_csc_type = CSC_NONE;
+
+       __ipu_dp_csc_setup(dp, dp_csc_array[bg_csc_type][fg_csc_type], false);
+}
+
+void _ipu_dc_init(int dc_chan, int di, bool interlaced, uint32_t pixel_fmt)
+{
+       u32 reg = 0;
+
+       if ((dc_chan == 1) || (dc_chan == 5)) {
+               if (interlaced) {
+                       _ipu_dc_link_event(dc_chan, DC_EVT_NL, 0, 3);
+                       _ipu_dc_link_event(dc_chan, DC_EVT_EOL, 0, 2);
+                       _ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, 0, 1);
+               } else {
+                       if (di) {
+                               _ipu_dc_link_event(dc_chan, DC_EVT_NL, 2, 3);
+                               _ipu_dc_link_event(dc_chan, DC_EVT_EOL, 3, 2);
+                               _ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, 4, 1);
+                               if ((pixel_fmt == IPU_PIX_FMT_YUYV) ||
+                               (pixel_fmt == IPU_PIX_FMT_UYVY) ||
+                               (pixel_fmt == IPU_PIX_FMT_YVYU) ||
+                               (pixel_fmt == IPU_PIX_FMT_VYUY)) {
+                                       _ipu_dc_link_event(dc_chan, DC_ODD_UGDE1, 9, 5);
+                                       _ipu_dc_link_event(dc_chan, DC_EVEN_UGDE1, 8, 5);
+                               }
+                       } else {
+                               _ipu_dc_link_event(dc_chan, DC_EVT_NL, 5, 3);
+                               _ipu_dc_link_event(dc_chan, DC_EVT_EOL, 6, 2);
+                               _ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, 7, 1);
+                               if ((pixel_fmt == IPU_PIX_FMT_YUYV) ||
+                               (pixel_fmt == IPU_PIX_FMT_UYVY) ||
+                               (pixel_fmt == IPU_PIX_FMT_YVYU) ||
+                               (pixel_fmt == IPU_PIX_FMT_VYUY)) {
+                                       _ipu_dc_link_event(dc_chan, DC_ODD_UGDE0, 10, 5);
+                                       _ipu_dc_link_event(dc_chan, DC_EVEN_UGDE0, 11, 5);
+                               }
+                       }
+               }
+               _ipu_dc_link_event(dc_chan, DC_EVT_NF, 0, 0);
+               _ipu_dc_link_event(dc_chan, DC_EVT_NFIELD, 0, 0);
+               _ipu_dc_link_event(dc_chan, DC_EVT_EOF, 0, 0);
+               _ipu_dc_link_event(dc_chan, DC_EVT_EOFIELD, 0, 0);
+               _ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN, 0, 0);
+               _ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR, 0, 0);
+
+               reg = 0x2;
+               reg |= DC_DISP_ID_SYNC(di) << DC_WR_CH_CONF_PROG_DISP_ID_OFFSET;
+               reg |= di << 2;
+               if (interlaced)
+                       reg |= DC_WR_CH_CONF_FIELD_MODE;
+       } else if ((dc_chan == 8) || (dc_chan == 9)) {
+               /* async channels */
+               _ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_W_0, 0x64, 1);
+               _ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_W_1, 0x64, 1);
+
+               reg = 0x3;
+               reg |= DC_DISP_ID_SERIAL << DC_WR_CH_CONF_PROG_DISP_ID_OFFSET;
+       }
+       __raw_writel(reg, DC_WR_CH_CONF(dc_chan));
+
+       __raw_writel(0x00000000, DC_WR_CH_ADDR(dc_chan));
+
+       __raw_writel(0x00000084, DC_GEN);
+}
+
+void _ipu_dc_uninit(int dc_chan)
+{
+       if ((dc_chan == 1) || (dc_chan == 5)) {
+               _ipu_dc_link_event(dc_chan, DC_EVT_NL, 0, 0);
+               _ipu_dc_link_event(dc_chan, DC_EVT_EOL, 0, 0);
+               _ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, 0, 0);
+               _ipu_dc_link_event(dc_chan, DC_EVT_NF, 0, 0);
+               _ipu_dc_link_event(dc_chan, DC_EVT_NFIELD, 0, 0);
+               _ipu_dc_link_event(dc_chan, DC_EVT_EOF, 0, 0);
+               _ipu_dc_link_event(dc_chan, DC_EVT_EOFIELD, 0, 0);
+               _ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN, 0, 0);
+               _ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR, 0, 0);
+               _ipu_dc_link_event(dc_chan, DC_ODD_UGDE0, 0, 0);
+               _ipu_dc_link_event(dc_chan, DC_EVEN_UGDE0, 0, 0);
+               _ipu_dc_link_event(dc_chan, DC_ODD_UGDE1, 0, 0);
+               _ipu_dc_link_event(dc_chan, DC_EVEN_UGDE1, 0, 0);
+       } else if ((dc_chan == 8) || (dc_chan == 9)) {
+               _ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR_W_0, 0, 0);
+               _ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR_W_1, 0, 0);
+               _ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN_W_0, 0, 0);
+               _ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN_W_1, 0, 0);
+               _ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_W_0, 0, 0);
+               _ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_W_1, 0, 0);
+               _ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR_R_0, 0, 0);
+               _ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR_R_1, 0, 0);
+               _ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN_R_0, 0, 0);
+               _ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN_R_1, 0, 0);
+               _ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_R_0, 0, 0);
+               _ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_R_1, 0, 0);
+       }
+}
+
+int _ipu_disp_chan_is_interlaced(ipu_channel_t channel)
+{
+       if (channel == MEM_DC_SYNC)
+               return !!(__raw_readl(DC_WR_CH_CONF_1) &
+                         DC_WR_CH_CONF_FIELD_MODE);
+       else if ((channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC))
+               return !!(__raw_readl(DC_WR_CH_CONF_5) &
+                         DC_WR_CH_CONF_FIELD_MODE);
+       return 0;
+}
+
+void _ipu_dp_dc_enable(ipu_channel_t channel)
+{
+       int di;
+       uint32_t reg;
+       uint32_t dc_chan;
+       int irq = 0;
+
+       if (channel == MEM_FG_SYNC)
+               irq = IPU_IRQ_DP_SF_END;
+       else if (channel == MEM_DC_SYNC)
+               dc_chan = 1;
+       else if (channel == MEM_BG_SYNC)
+               dc_chan = 5;
+       else
+               return;
+
+       if (channel == MEM_FG_SYNC) {
+               /* Enable FG channel */
+               reg = __raw_readl(DP_COM_CONF(DP_SYNC));
+               __raw_writel(reg | DP_COM_CONF_FG_EN, DP_COM_CONF(DP_SYNC));
+
+               reg = __raw_readl(IPU_SRM_PRI2) | 0x8;
+               __raw_writel(reg, IPU_SRM_PRI2);
+               return;
+       }
+
+       di = g_dc_di_assignment[dc_chan];
+
+       /* Make sure other DC sync channel is not assigned same DI */
+       reg = __raw_readl(DC_WR_CH_CONF(6 - dc_chan));
+       if ((di << 2) == (reg & DC_WR_CH_CONF_PROG_DI_ID)) {
+               reg &= ~DC_WR_CH_CONF_PROG_DI_ID;
+               reg |= di ? 0 : DC_WR_CH_CONF_PROG_DI_ID;
+               __raw_writel(reg, DC_WR_CH_CONF(6 - dc_chan));
+       }
+
+       reg = __raw_readl(DC_WR_CH_CONF(dc_chan));
+       reg |= 4 << DC_WR_CH_CONF_PROG_TYPE_OFFSET;
+       __raw_writel(reg, DC_WR_CH_CONF(dc_chan));
+
+       clk_enable(g_pixel_clk[di]);
+}
+
+static bool dc_swap;
+
+static irqreturn_t dc_irq_handler(int irq, void *dev_id)
+{
+       struct completion *comp = dev_id;
+       uint32_t reg;
+       uint32_t dc_chan;
+
+       if (irq == IPU_IRQ_DC_FC_1)
+               dc_chan = 1;
+       else
+               dc_chan = 5;
+
+       if (!dc_swap) {
+               reg = __raw_readl(DC_WR_CH_CONF(dc_chan));
+               reg &= ~DC_WR_CH_CONF_PROG_TYPE_MASK;
+               __raw_writel(reg, DC_WR_CH_CONF(dc_chan));
+
+               reg = __raw_readl(IPU_DISP_GEN);
+               if (g_dc_di_assignment[dc_chan])
+                       reg &= ~DI1_COUNTER_RELEASE;
+               else
+                       reg &= ~DI0_COUNTER_RELEASE;
+               __raw_writel(reg, IPU_DISP_GEN);
+       }
+
+       complete(comp);
+       return IRQ_HANDLED;
+}
+
+void _ipu_dp_dc_disable(ipu_channel_t channel, bool swap)
+{
+       int ret;
+       unsigned long lock_flags;
+       uint32_t reg;
+       uint32_t csc;
+       uint32_t dc_chan;
+       int irq = 0;
+       int timeout = 50;
+       DECLARE_COMPLETION_ONSTACK(dc_comp);
+
+       dc_swap = swap;
+
+       if (channel == MEM_DC_SYNC) {
+               dc_chan = 1;
+               irq = IPU_IRQ_DC_FC_1;
+       } else if (channel == MEM_BG_SYNC) {
+               dc_chan = 5;
+               irq = IPU_IRQ_DP_SF_END;
+       } else if (channel == MEM_FG_SYNC) {
+               /* Disable FG channel */
+               dc_chan = 5;
+
+               spin_lock_irqsave(&ipu_lock, lock_flags);
+
+               reg = __raw_readl(DP_COM_CONF(DP_SYNC));
+               csc = reg & DP_COM_CONF_CSC_DEF_MASK;
+               if (csc == DP_COM_CONF_CSC_DEF_FG)
+                       reg &= ~DP_COM_CONF_CSC_DEF_MASK;
+
+               reg &= ~DP_COM_CONF_FG_EN;
+               __raw_writel(reg, DP_COM_CONF(DP_SYNC));
+
+               reg = __raw_readl(IPU_SRM_PRI2) | 0x8;
+               __raw_writel(reg, IPU_SRM_PRI2);
+
+               spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+               __raw_writel(IPUIRQ_2_MASK(IPU_IRQ_DP_SF_END),
+                            IPUIRQ_2_STATREG(IPU_IRQ_DP_SF_END));
+               while ((__raw_readl(IPUIRQ_2_STATREG(IPU_IRQ_DP_SF_END)) &
+                       IPUIRQ_2_MASK(IPU_IRQ_DP_SF_END)) == 0) {
+                       msleep(2);
+                       timeout -= 2;
+                       if (timeout <= 0)
+                               break;
+               }
+               return;
+       } else {
+               return;
+       }
+
+       if (!dc_swap)
+               __raw_writel(IPUIRQ_2_MASK(IPU_IRQ_VSYNC_PRE_0
+                       + g_dc_di_assignment[dc_chan]),
+                    IPUIRQ_2_STATREG(IPU_IRQ_VSYNC_PRE_0
+                       + g_dc_di_assignment[dc_chan]));
+       ipu_clear_irq(irq);
+       ret = ipu_request_irq(irq, dc_irq_handler, 0, NULL, &dc_comp);
+       if (ret < 0) {
+               dev_err(g_ipu_dev, "DC irq %d in use\n", irq);
+               return;
+       }
+       ret = wait_for_completion_timeout(&dc_comp, msecs_to_jiffies(50));
+
+       dev_dbg(g_ipu_dev, "DC stop timeout - %d * 10ms\n", 5 - ret);
+       ipu_free_irq(irq, &dc_comp);
+
+       if (dc_swap) {
+               spin_lock_irqsave(&ipu_lock, lock_flags);
+               /* Swap DC channel 1 and 5 settings, and disable old dc chan */
+               reg = __raw_readl(DC_WR_CH_CONF(dc_chan));
+               __raw_writel(reg, DC_WR_CH_CONF(6 - dc_chan));
+               reg &= ~DC_WR_CH_CONF_PROG_TYPE_MASK;
+               reg ^= DC_WR_CH_CONF_PROG_DI_ID;
+               __raw_writel(reg, DC_WR_CH_CONF(dc_chan));
+               spin_unlock_irqrestore(&ipu_lock, lock_flags);
+       } else {
+               /* Clock is already off because it must be done quickly, but
+                  we need to fix the ref count */
+               clk_disable(g_pixel_clk[g_dc_di_assignment[dc_chan]]);
+
+               if (__raw_readl(IPUIRQ_2_STATREG(IPU_IRQ_VSYNC_PRE_0
+                       + g_dc_di_assignment[dc_chan])) &
+                       IPUIRQ_2_MASK(IPU_IRQ_VSYNC_PRE_0
+                       + g_dc_di_assignment[dc_chan]))
+                       dev_dbg(g_ipu_dev,
+                               "VSyncPre occurred before DI%d disable\n",
+                               g_dc_di_assignment[dc_chan]);
+       }
+}
+
+void _ipu_init_dc_mappings(void)
+{
+       /* IPU_PIX_FMT_RGB24 */
+       _ipu_dc_map_clear(0);
+       _ipu_dc_map_config(0, 0, 7, 0xFF);
+       _ipu_dc_map_config(0, 1, 15, 0xFF);
+       _ipu_dc_map_config(0, 2, 23, 0xFF);
+
+       /* IPU_PIX_FMT_RGB666 */
+       _ipu_dc_map_clear(1);
+       _ipu_dc_map_config(1, 0, 5, 0xFC);
+       _ipu_dc_map_config(1, 1, 11, 0xFC);
+       _ipu_dc_map_config(1, 2, 17, 0xFC);
+
+       /* IPU_PIX_FMT_YUV444 */
+       _ipu_dc_map_clear(2);
+       _ipu_dc_map_config(2, 0, 15, 0xFF);
+       _ipu_dc_map_config(2, 1, 23, 0xFF);
+       _ipu_dc_map_config(2, 2, 7, 0xFF);
+
+       /* IPU_PIX_FMT_RGB565 */
+       _ipu_dc_map_clear(3);
+       _ipu_dc_map_config(3, 0, 4, 0xF8);
+       _ipu_dc_map_config(3, 1, 10, 0xFC);
+       _ipu_dc_map_config(3, 2, 15, 0xF8);
+
+       /* IPU_PIX_FMT_LVDS666 */
+       _ipu_dc_map_clear(4);
+       _ipu_dc_map_config(4, 0, 5, 0xFC);
+       _ipu_dc_map_config(4, 1, 13, 0xFC);
+       _ipu_dc_map_config(4, 2, 21, 0xFC);
+
+       /* IPU_PIX_FMT_VYUY 16bit width */
+       _ipu_dc_map_clear(5);
+       _ipu_dc_map_config(5, 0, 7, 0xFF);
+       _ipu_dc_map_config(5, 1, 0, 0x0);
+       _ipu_dc_map_config(5, 2, 15, 0xFF);
+       _ipu_dc_map_clear(6);
+       _ipu_dc_map_config(6, 0, 0, 0x0);
+       _ipu_dc_map_config(6, 1, 7, 0xFF);
+       _ipu_dc_map_config(6, 2, 15, 0xFF);
+
+       /* IPU_PIX_FMT_UYUV 16bit width */
+       _ipu_dc_map_clear(7);
+       _ipu_dc_map_link(7, 6, 0, 6, 1, 6, 2);
+       _ipu_dc_map_clear(8);
+       _ipu_dc_map_link(8, 5, 0, 5, 1, 5, 2);
+
+       /* IPU_PIX_FMT_YUYV 16bit width */
+       _ipu_dc_map_clear(9);
+       _ipu_dc_map_link(9, 5, 2, 5, 1, 5, 0);
+       _ipu_dc_map_clear(10);
+       _ipu_dc_map_link(10, 5, 1, 5, 2, 5, 0);
+
+       /* IPU_PIX_FMT_YVYU 16bit width */
+       _ipu_dc_map_clear(11);
+       _ipu_dc_map_link(11, 5, 1, 5, 2, 5, 0);
+       _ipu_dc_map_clear(12);
+       _ipu_dc_map_link(12, 5, 2, 5, 1, 5, 0);
+
+       /* IPU_PIX_FMT_GBR24 */
+       _ipu_dc_map_clear(13);
+       _ipu_dc_map_link(13, 0, 2, 0, 0, 0, 1);
+}
+
+int _ipu_pixfmt_to_map(uint32_t fmt)
+{
+       switch (fmt) {
+       case IPU_PIX_FMT_GENERIC:
+       case IPU_PIX_FMT_RGB24:
+               return 0;
+       case IPU_PIX_FMT_RGB666:
+               return 1;
+       case IPU_PIX_FMT_YUV444:
+               return 2;
+       case IPU_PIX_FMT_RGB565:
+               return 3;
+       case IPU_PIX_FMT_LVDS666:
+               return 4;
+       case IPU_PIX_FMT_VYUY:
+               return 6;
+       case IPU_PIX_FMT_UYVY:
+               return 8;
+       case IPU_PIX_FMT_YUYV:
+               return 10;
+       case IPU_PIX_FMT_YVYU:
+               return 12;
+       case IPU_PIX_FMT_GBR24:
+               return 13;
+       }
+
+       return -1;
+}
+
+/*!
+ * This function sets the colorspace for of dp.
+ * modes.
+ *
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @param       param          If it's not NULL, update the csc table
+ *                              with this parameter.
+ *
+ * @return      N/A
+ */
+void _ipu_dp_set_csc_coefficients(ipu_channel_t channel, int32_t param[][3])
+{
+       int dp;
+       struct dp_csc_param_t dp_csc_param;
+
+       if (channel == MEM_FG_SYNC)
+               dp = DP_SYNC;
+       else if (channel == MEM_BG_SYNC)
+               dp = DP_SYNC;
+       else if (channel == MEM_BG_ASYNC0)
+               dp = DP_ASYNC0;
+       else
+               return;
+
+       dp_csc_param.mode = -1;
+       dp_csc_param.coeff = param;
+       __ipu_dp_csc_setup(dp, dp_csc_param, true);
+}
+
+/*!
+ * This function is called to adapt synchronous LCD panel to IPU restriction.
+ *
+ */
+void adapt_panel_to_ipu_restricitions(uint16_t *v_start_width,
+                                       uint16_t *v_sync_width,
+                                       uint16_t *v_end_width)
+{
+       if (*v_end_width < 2) {
+               uint16_t diff = 2 - *v_end_width;
+               if (*v_start_width >= diff) {
+                       *v_end_width = 2;
+                       *v_start_width = *v_start_width - diff;
+               } else if (*v_sync_width > diff) {
+                       *v_end_width = 2;
+                       *v_sync_width = *v_sync_width - diff;
+               } else
+                       dev_err(g_ipu_dev, "WARNING: try to adapt timming, but failed\n");
+               dev_err(g_ipu_dev, "WARNING: adapt panel end blank lines\n");
+       }
+}
+
+/*!
+ * This function is called to initialize a synchronous LCD panel.
+ *
+ * @param       disp            The DI the panel is attached to.
+ *
+ * @param       pixel_clk       Desired pixel clock frequency in Hz.
+ *
+ * @param       pixel_fmt       Input parameter for pixel format of buffer.
+ *                              Pixel format is a FOURCC ASCII code.
+ *
+ * @param       width           The width of panel in pixels.
+ *
+ * @param       height          The height of panel in pixels.
+ *
+ * @param       hStartWidth     The number of pixel clocks between the HSYNC
+ *                              signal pulse and the start of valid data.
+ *
+ * @param       hSyncWidth      The width of the HSYNC signal in units of pixel
+ *                              clocks.
+ *
+ * @param       hEndWidth       The number of pixel clocks between the end of
+ *                              valid data and the HSYNC signal for next line.
+ *
+ * @param       vStartWidth     The number of lines between the VSYNC
+ *                              signal pulse and the start of valid data.
+ *
+ * @param       vSyncWidth      The width of the VSYNC signal in units of lines
+ *
+ * @param       vEndWidth       The number of lines between the end of valid
+ *                              data and the VSYNC signal for next frame.
+ *
+ * @param       sig             Bitfield of signal polarities for LCD interface.
+ *
+ * @return      This function returns 0 on success or negative error code on
+ *              fail.
+ */
+int32_t ipu_init_sync_panel(int disp, uint32_t pixel_clk,
+                           uint16_t width, uint16_t height,
+                           uint32_t pixel_fmt,
+                           uint16_t h_start_width, uint16_t h_sync_width,
+                           uint16_t h_end_width, uint16_t v_start_width,
+                           uint16_t v_sync_width, uint16_t v_end_width,
+                           uint32_t v_to_h_sync, ipu_di_signal_cfg_t sig)
+{
+       unsigned long lock_flags;
+       uint32_t field0_offset = 0;
+       uint32_t field1_offset;
+       uint32_t reg;
+       uint32_t di_gen, vsync_cnt;
+       uint32_t div, rounded_pixel_clk;
+       uint32_t h_total, v_total;
+       int map;
+       struct clk *di_parent;
+
+       dev_dbg(g_ipu_dev, "panel size = %d x %d\n", width, height);
+
+       if ((v_sync_width == 0) || (h_sync_width == 0))
+               return EINVAL;
+
+       adapt_panel_to_ipu_restricitions(&v_start_width, &v_sync_width, &v_end_width);
+       h_total = width + h_sync_width + h_start_width + h_end_width;
+       v_total = height + v_sync_width + v_start_width + v_end_width;
+
+       /* Init clocking */
+       dev_dbg(g_ipu_dev, "pixel clk = %d\n", pixel_clk);
+
+       /*clear DI*/
+       __raw_writel((1 << 21), DI_GENERAL(disp));
+
+       di_parent = clk_get_parent(g_di_clk[disp]);
+       if (clk_get(NULL, "tve_clk") == di_parent ||
+               clk_get(NULL, "ldb_di0_clk") == di_parent ||
+               clk_get(NULL, "ldb_di1_clk") == di_parent) {
+               /* if di clk parent is tve/ldb, then keep it;*/
+               dev_dbg(g_ipu_dev, "use special clk parent\n");
+               clk_set_parent(g_pixel_clk[disp], g_di_clk[disp]);
+       } else {
+               /* try ipu clk first*/
+               dev_dbg(g_ipu_dev, "try ipu internal clk\n");
+               clk_set_parent(g_pixel_clk[disp], g_ipu_clk);
+               rounded_pixel_clk = clk_round_rate(g_pixel_clk[disp], pixel_clk);
+               /*
+                * we will only use 1/2 fraction for ipu clk,
+                * so if the clk rate is not fit, try ext clk.
+                */
+               if (!sig.int_clk &&
+                       ((rounded_pixel_clk >= pixel_clk + pixel_clk/16) ||
+                       (rounded_pixel_clk <= pixel_clk - pixel_clk/16))) {
+                       dev_dbg(g_ipu_dev, "try ipu ext di clk\n");
+                       rounded_pixel_clk = pixel_clk * 2;
+                       while (rounded_pixel_clk < 150000000)
+                               rounded_pixel_clk += pixel_clk * 2;
+                       clk_set_rate(di_parent, rounded_pixel_clk);
+                       rounded_pixel_clk =
+                               clk_round_rate(g_di_clk[disp], pixel_clk);
+                       clk_set_rate(g_di_clk[disp], rounded_pixel_clk);
+                       clk_set_parent(g_pixel_clk[disp], g_di_clk[disp]);
+               }
+       }
+       rounded_pixel_clk = clk_round_rate(g_pixel_clk[disp], pixel_clk);
+       clk_set_rate(g_pixel_clk[disp], rounded_pixel_clk);
+       msleep(5);
+       /* Get integer portion of divider */
+       div = clk_get_rate(clk_get_parent(g_pixel_clk[disp])) / rounded_pixel_clk;
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       _ipu_di_data_wave_config(disp, SYNC_WAVE, div - 1, div - 1);
+       _ipu_di_data_pin_config(disp, SYNC_WAVE, DI_PIN15, 3, 0, div * 2);
+
+       map = _ipu_pixfmt_to_map(pixel_fmt);
+       if (map < 0) {
+               dev_dbg(g_ipu_dev, "IPU_DISP: No MAP\n");
+               spin_unlock_irqrestore(&ipu_lock, lock_flags);
+               return -EINVAL;
+       }
+
+       di_gen = __raw_readl(DI_GENERAL(disp));
+
+       if (sig.interlaced) {
+               if (g_ipu_hw_rev >= 2) {
+                       /* Setup internal HSYNC waveform */
+                       _ipu_di_sync_config(
+                                       disp,                   /* display */
+                                       1,                              /* counter */
+                                       h_total/2 - 1,  /* run count */
+                                       DI_SYNC_CLK,             /* run_resolution */
+                                       0,                              /* offset */
+                                       DI_SYNC_NONE,   /* offset resolution */
+                                       0,                              /* repeat count */
+                                       DI_SYNC_NONE,   /* CNT_CLR_SEL */
+                                       0,                              /* CNT_POLARITY_GEN_EN */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_CLR_SEL */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_TRIGGER_SEL */
+                                       0,                              /* COUNT UP */
+                                       0                               /* COUNT DOWN */
+                                       );
+
+                       /* Field 1 VSYNC waveform */
+                       _ipu_di_sync_config(
+                                       disp,                   /* display */
+                                       2,                              /* counter */
+                                       h_total - 1,            /* run count */
+                                       DI_SYNC_CLK,            /* run_resolution */
+                                       0,                              /* offset */
+                                       DI_SYNC_NONE,   /* offset resolution */
+                                       0,                              /* repeat count */
+                                       DI_SYNC_NONE,   /* CNT_CLR_SEL */
+                                       0,                              /* CNT_POLARITY_GEN_EN */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_CLR_SEL */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_TRIGGER_SEL */
+                                       0,                              /* COUNT UP */
+                                       2*div                   /* COUNT DOWN */
+                                       );
+
+                       /* Setup internal HSYNC waveform */
+                       _ipu_di_sync_config(
+                                       disp,                   /* display */
+                                       3,                              /* counter */
+                                       v_total*2 - 1,  /* run count */
+                                       DI_SYNC_INT_HSYNC,      /* run_resolution */
+                                       1,                              /* offset */
+                                       DI_SYNC_INT_HSYNC,      /* offset resolution */
+                                       0,                              /* repeat count */
+                                       DI_SYNC_NONE,   /* CNT_CLR_SEL */
+                                       0,                              /* CNT_POLARITY_GEN_EN */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_CLR_SEL */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_TRIGGER_SEL */
+                                       0,                              /* COUNT UP */
+                                       2*div                   /* COUNT DOWN */
+                                       );
+
+                       /* Active Field ? */
+                       _ipu_di_sync_config(
+                                       disp,                   /* display */
+                                       4,                              /* counter */
+                                       v_total/2 - 1,  /* run count */
+                                       DI_SYNC_HSYNC,  /* run_resolution */
+                                       v_start_width,  /*  offset */
+                                       DI_SYNC_HSYNC,  /* offset resolution */
+                                       2,                              /* repeat count */
+                                       DI_SYNC_VSYNC,  /* CNT_CLR_SEL */
+                                       0,                              /* CNT_POLARITY_GEN_EN */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_CLR_SEL */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_TRIGGER_SEL */
+                                       0,                              /* COUNT UP */
+                                       0                               /* COUNT DOWN */
+                                       );
+
+                       /* Active Line */
+                       _ipu_di_sync_config(
+                                       disp,                   /* display */
+                                       5,                              /* counter */
+                                       0,                              /* run count */
+                                       DI_SYNC_HSYNC,  /* run_resolution */
+                                       0,                              /*  offset */
+                                       DI_SYNC_NONE,   /* offset resolution */
+                                       height/2,               /* repeat count */
+                                       4,                              /* CNT_CLR_SEL */
+                                       0,                              /* CNT_POLARITY_GEN_EN */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_CLR_SEL */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_TRIGGER_SEL */
+                                       0,                              /* COUNT UP */
+                                       0                               /* COUNT DOWN */
+                                       );
+
+                       /* Field 0 VSYNC waveform */
+                       _ipu_di_sync_config(
+                                       disp,                   /* display */
+                                       6,                              /* counter */
+                                       v_total - 1,    /* run count */
+                                       DI_SYNC_HSYNC,  /* run_resolution */
+                                       0,                              /* offset */
+                                       DI_SYNC_NONE,   /* offset resolution */
+                                       0,                              /* repeat count */
+                                       DI_SYNC_NONE,   /* CNT_CLR_SEL  */
+                                       0,                              /* CNT_POLARITY_GEN_EN */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_CLR_SEL */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_TRIGGER_SEL */
+                                       0,                              /* COUNT UP */
+                                       0                               /* COUNT DOWN */
+                                       );
+
+                       /* DC VSYNC waveform */
+                       vsync_cnt = 7;
+                       _ipu_di_sync_config(
+                                       disp,                   /* display */
+                                       7,                              /* counter */
+                                       v_total/2 - 1,  /* run count */
+                                       DI_SYNC_HSYNC,  /* run_resolution  */
+                                       9,                              /* offset  */
+                                       DI_SYNC_HSYNC,  /* offset resolution */
+                                       2,                              /* repeat count */
+                                       DI_SYNC_VSYNC,  /* CNT_CLR_SEL */
+                                       0,                              /* CNT_POLARITY_GEN_EN */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_CLR_SEL */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_TRIGGER_SEL */
+                                       0,                              /* COUNT UP */
+                                       0                               /* COUNT DOWN */
+                                       );
+
+                       /* active pixel waveform */
+                       _ipu_di_sync_config(
+                                       disp,                   /* display */
+                                       8,                              /* counter */
+                                       0,      /* run count  */
+                                       DI_SYNC_CLK,    /* run_resolution */
+                                       h_start_width,                          /* offset  */
+                                       DI_SYNC_CLK,    /* offset resolution */
+                                       width,                          /* repeat count  */
+                                       5,      /* CNT_CLR_SEL  */
+                                       0,                              /* CNT_POLARITY_GEN_EN  */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_CLR_SEL */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_TRIGGER_SEL  */
+                                       0,                              /* COUNT UP  */
+                                       0                               /* COUNT DOWN */
+                                       );
+
+                       /* Second VSYNC */
+                       _ipu_di_sync_config(
+                                       disp,                   /* display */
+                                       9,                              /* counter */
+                                       v_total - 1,    /* run count */
+                                       DI_SYNC_INT_HSYNC,      /* run_resolution */
+                                       v_total/2,                      /* offset  */
+                                       DI_SYNC_INT_HSYNC,      /* offset resolution  */
+                                       0,                              /* repeat count */
+                                       DI_SYNC_HSYNC,  /* CNT_CLR_SEL */
+                                       0,                              /* CNT_POLARITY_GEN_EN  */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_CLR_SEL  */
+                                       DI_SYNC_NONE,   /* CNT_POLARITY_TRIGGER_SEL */
+                                       0,                              /* COUNT UP */
+                                       2*div                   /* COUNT DOWN */
+                                       );
+
+                       /* set gentime select and tag sel */
+                       reg = __raw_readl(DI_SW_GEN1(disp, 9));
+                       reg &= 0x1FFFFFFF;
+                       reg |= (3-1)<<29 | 0x00008000;
+                       __raw_writel(reg, DI_SW_GEN1(disp, 9));
+
+                       __raw_writel(v_total / 2 - 1, DI_SCR_CONF(disp));
+
+                       /* set y_sel = 1 */
+                       di_gen |= 0x10000000;
+                       di_gen |= DI_GEN_POLARITY_5;
+                       di_gen |= DI_GEN_POLARITY_8;
+               } else {
+                       /* Setup internal HSYNC waveform */
+                       _ipu_di_sync_config(disp, 1, h_total - 1, DI_SYNC_CLK,
+                                       0, DI_SYNC_NONE, 0, DI_SYNC_NONE, 0, DI_SYNC_NONE,
+                                       DI_SYNC_NONE, 0, 0);
+
+                       field1_offset = v_sync_width + v_start_width + height / 2 +
+                               v_end_width;
+                       if (sig.odd_field_first) {
+                               field0_offset = field1_offset - 1;
+                               field1_offset = 0;
+                       }
+                       v_total += v_start_width + v_end_width;
+
+                       /* Field 1 VSYNC waveform */
+                       _ipu_di_sync_config(disp, 2, v_total - 1, 1,
+                                       field0_offset,
+                                       field0_offset ? 1 : DI_SYNC_NONE,
+                                       0, DI_SYNC_NONE, 0,
+                                       DI_SYNC_NONE, DI_SYNC_NONE, 0, 4);
+
+                       /* Setup internal HSYNC waveform */
+                       _ipu_di_sync_config(disp, 3, h_total - 1, DI_SYNC_CLK,
+                                       0, DI_SYNC_NONE, 0, DI_SYNC_NONE, 0,
+                                       DI_SYNC_NONE, DI_SYNC_NONE, 0, 4);
+
+                       /* Active Field ? */
+                       _ipu_di_sync_config(disp, 4,
+                                       field0_offset ?
+                                       field0_offset : field1_offset - 2,
+                                       1, v_start_width + v_sync_width, 1, 2, 2,
+                                       0, DI_SYNC_NONE, DI_SYNC_NONE, 0, 0);
+
+                       /* Active Line */
+                       _ipu_di_sync_config(disp, 5, 0, 1,
+                                       0, DI_SYNC_NONE,
+                                       height / 2, 4, 0, DI_SYNC_NONE,
+                                       DI_SYNC_NONE, 0, 0);
+
+                       /* Field 0 VSYNC waveform */
+                       _ipu_di_sync_config(disp, 6, v_total - 1, 1,
+                                       0, DI_SYNC_NONE,
+                                       0, DI_SYNC_NONE, 0, DI_SYNC_NONE,
+                                       DI_SYNC_NONE, 0, 0);
+
+                       /* DC VSYNC waveform */
+                       vsync_cnt = 7;
+                       _ipu_di_sync_config(disp, 7, 0, 1,
+                                       field1_offset,
+                                       field1_offset ? 1 : DI_SYNC_NONE,
+                                       1, 2, 0, DI_SYNC_NONE, DI_SYNC_NONE, 0, 0);
+
+                       /* active pixel waveform */
+                       _ipu_di_sync_config(disp, 8, 0, DI_SYNC_CLK,
+                                       h_sync_width + h_start_width, DI_SYNC_CLK,
+                                       width, 5, 0, DI_SYNC_NONE, DI_SYNC_NONE,
+                                       0, 0);
+
+                       /* ??? */
+                       _ipu_di_sync_config(disp, 9, v_total - 1, 2,
+                                       0, DI_SYNC_NONE,
+                                       0, DI_SYNC_NONE, 6, DI_SYNC_NONE,
+                                       DI_SYNC_NONE, 0, 0);
+
+                       reg = __raw_readl(DI_SW_GEN1(disp, 9));
+                       reg |= 0x8000;
+                       __raw_writel(reg, DI_SW_GEN1(disp, 9));
+
+                       __raw_writel(v_sync_width + v_start_width +
+                                       v_end_width + height / 2 - 1, DI_SCR_CONF(disp));
+               }
+
+               /* Init template microcode */
+               _ipu_dc_write_tmpl(0, WROD(0), 0, map, SYNC_WAVE, 0, 8, 1);
+
+               if (sig.Hsync_pol)
+                       di_gen |= DI_GEN_POLARITY_3;
+               if (sig.Vsync_pol)
+                       di_gen |= DI_GEN_POLARITY_2;
+       } else {
+               /* Setup internal HSYNC waveform */
+               _ipu_di_sync_config(disp, 1, h_total - 1, DI_SYNC_CLK,
+                                       0, DI_SYNC_NONE, 0, DI_SYNC_NONE, 0, DI_SYNC_NONE,
+                                       DI_SYNC_NONE, 0, 0);
+
+               /* Setup external (delayed) HSYNC waveform */
+               _ipu_di_sync_config(disp, DI_SYNC_HSYNC, h_total - 1,
+                                   DI_SYNC_CLK, div * v_to_h_sync, DI_SYNC_CLK,
+                                   0, DI_SYNC_NONE, 1, DI_SYNC_NONE,
+                                   DI_SYNC_CLK, 0, h_sync_width * 2);
+               /* Setup VSYNC waveform */
+               vsync_cnt = DI_SYNC_VSYNC;
+               _ipu_di_sync_config(disp, DI_SYNC_VSYNC, v_total - 1,
+                                   DI_SYNC_INT_HSYNC, 0, DI_SYNC_NONE, 0,
+                                   DI_SYNC_NONE, 1, DI_SYNC_NONE,
+                                   DI_SYNC_INT_HSYNC, 0, v_sync_width * 2);
+               __raw_writel(v_total - 1, DI_SCR_CONF(disp));
+
+               /* Setup active data waveform to sync with DC */
+               _ipu_di_sync_config(disp, 4, 0, DI_SYNC_HSYNC,
+                                   v_sync_width + v_start_width, DI_SYNC_HSYNC, height,
+                                   DI_SYNC_VSYNC, 0, DI_SYNC_NONE,
+                                   DI_SYNC_NONE, 0, 0);
+               _ipu_di_sync_config(disp, 5, 0, DI_SYNC_CLK,
+                                   h_sync_width + h_start_width, DI_SYNC_CLK,
+                                   width, 4, 0, DI_SYNC_NONE, DI_SYNC_NONE, 0,
+                                   0);
+
+               /* set VGA delayed hsync/vsync no matter VGA enabled */
+               if (disp) {
+                       /* couter 7 for VGA delay HSYNC */
+                       _ipu_di_sync_config(disp, 7,
+                                       h_total - 1, DI_SYNC_CLK,
+                                       18, DI_SYNC_CLK,
+                                       0, DI_SYNC_NONE,
+                                       1, DI_SYNC_NONE, DI_SYNC_CLK,
+                                       0, h_sync_width * 2);
+
+                       /* couter 8 for VGA delay VSYNC */
+                       _ipu_di_sync_config(disp, 8,
+                                       v_total - 1, DI_SYNC_INT_HSYNC,
+                                       1, DI_SYNC_INT_HSYNC,
+                                       0, DI_SYNC_NONE,
+                                       1, DI_SYNC_NONE, DI_SYNC_INT_HSYNC,
+                                       0, v_sync_width * 2);
+               }
+
+               /* reset all unused counters */
+               __raw_writel(0, DI_SW_GEN0(disp, 6));
+               __raw_writel(0, DI_SW_GEN1(disp, 6));
+               if (!disp) {
+                       __raw_writel(0, DI_SW_GEN0(disp, 7));
+                       __raw_writel(0, DI_SW_GEN1(disp, 7));
+                       __raw_writel(0, DI_STP_REP(disp, 7));
+                       __raw_writel(0, DI_SW_GEN0(disp, 8));
+                       __raw_writel(0, DI_SW_GEN1(disp, 8));
+                       __raw_writel(0, DI_STP_REP(disp, 8));
+               }
+               __raw_writel(0, DI_SW_GEN0(disp, 9));
+               __raw_writel(0, DI_SW_GEN1(disp, 9));
+               __raw_writel(0, DI_STP_REP(disp, 9));
+
+               reg = __raw_readl(DI_STP_REP(disp, 6));
+               reg &= 0x0000FFFF;
+               __raw_writel(reg, DI_STP_REP(disp, 6));
+
+               /* Init template microcode */
+               if (disp) {
+                       if ((pixel_fmt == IPU_PIX_FMT_YUYV) ||
+                               (pixel_fmt == IPU_PIX_FMT_UYVY) ||
+                               (pixel_fmt == IPU_PIX_FMT_YVYU) ||
+                               (pixel_fmt == IPU_PIX_FMT_VYUY)) {
+                               _ipu_dc_write_tmpl(8, WROD(0), 0, (map - 1), SYNC_WAVE, 0, 5, 1);
+                               _ipu_dc_write_tmpl(9, WROD(0), 0, map, SYNC_WAVE, 0, 5, 1);
+                               /* configure user events according to DISP NUM */
+                               __raw_writel((width - 1), DC_UGDE_3(disp));
+                       }
+                  _ipu_dc_write_tmpl(2, WROD(0), 0, map, SYNC_WAVE, 8, 5, 1);
+                  _ipu_dc_write_tmpl(3, WRG, 0, map, SYNC_WAVE, 4, 5, 1);
+                  _ipu_dc_write_tmpl(4, WROD(0), 0, map, SYNC_WAVE, 0, 5, 1);
+               } else {
+                       if ((pixel_fmt == IPU_PIX_FMT_YUYV) ||
+                               (pixel_fmt == IPU_PIX_FMT_UYVY) ||
+                               (pixel_fmt == IPU_PIX_FMT_YVYU) ||
+                               (pixel_fmt == IPU_PIX_FMT_VYUY)) {
+                               _ipu_dc_write_tmpl(10, WROD(0), 0, (map - 1), SYNC_WAVE, 0, 5, 1);
+                               _ipu_dc_write_tmpl(11, WROD(0), 0, map, SYNC_WAVE, 0, 5, 1);
+                               /* configure user events according to DISP NUM */
+                               __raw_writel(width - 1, DC_UGDE_3(disp));
+                       }
+                  _ipu_dc_write_tmpl(5, WROD(0), 0, map, SYNC_WAVE, 8, 5, 1);
+                  _ipu_dc_write_tmpl(6, WRG, 0, map, SYNC_WAVE, 4, 5, 1);
+                  _ipu_dc_write_tmpl(7, WROD(0), 0, map, SYNC_WAVE, 0, 5, 1);
+               }
+
+               if (sig.Hsync_pol) {
+                       di_gen |= DI_GEN_POLARITY_2;
+                       if (disp)
+                               di_gen |= DI_GEN_POLARITY_7;
+               }
+               if (sig.Vsync_pol) {
+                       di_gen |= DI_GEN_POLARITY_3;
+                       if (disp)
+                               di_gen |= DI_GEN_POLARITY_8;
+               }
+       }
+       /* changinc DISP_CLK polarity: it can be wrong for some applications */
+       if ((pixel_fmt == IPU_PIX_FMT_YUYV) ||
+               (pixel_fmt == IPU_PIX_FMT_UYVY) ||
+               (pixel_fmt == IPU_PIX_FMT_YVYU) ||
+               (pixel_fmt == IPU_PIX_FMT_VYUY))
+                       di_gen |= 0x00020000;
+
+       if (!sig.clk_pol)
+               di_gen |= DI_GEN_POLARITY_DISP_CLK;
+
+       __raw_writel(di_gen, DI_GENERAL(disp));
+
+       __raw_writel((--vsync_cnt << DI_VSYNC_SEL_OFFSET) |
+                       0x00000002, DI_SYNC_AS_GEN(disp));
+       reg = __raw_readl(DI_POL(disp));
+       reg &= ~(DI_POL_DRDY_DATA_POLARITY | DI_POL_DRDY_POLARITY_15);
+       if (sig.enable_pol)
+               reg |= DI_POL_DRDY_POLARITY_15;
+       if (sig.data_pol)
+               reg |= DI_POL_DRDY_DATA_POLARITY;
+       __raw_writel(reg, DI_POL(disp));
+
+       __raw_writel(width, DC_DISP_CONF2(DC_DISP_ID_SYNC(disp)));
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+       return 0;
+}
+EXPORT_SYMBOL(ipu_init_sync_panel);
+
+
+int ipu_init_async_panel(int disp, int type, uint32_t cycle_time,
+                        uint32_t pixel_fmt, ipu_adc_sig_cfg_t sig)
+{
+       unsigned long lock_flags;
+       int map;
+       u32 ser_conf = 0;
+       u32 div;
+       u32 di_clk = clk_get_rate(g_ipu_clk);
+
+       /* round up cycle_time, then calcalate the divider using scaled math */
+       cycle_time += (1000000000UL / di_clk) - 1;
+       div = (cycle_time * (di_clk / 256UL)) / (1000000000UL / 256UL);
+
+       map = _ipu_pixfmt_to_map(pixel_fmt);
+       if (map < 0)
+               return -EINVAL;
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       if (type == IPU_PANEL_SERIAL) {
+               __raw_writel((div << 24) | ((sig.ifc_width - 1) << 4),
+                            DI_DW_GEN(disp, ASYNC_SER_WAVE));
+
+               _ipu_di_data_pin_config(disp, ASYNC_SER_WAVE, DI_PIN_CS,
+                                       0, 0, (div * 2) + 1);
+               _ipu_di_data_pin_config(disp, ASYNC_SER_WAVE, DI_PIN_SER_CLK,
+                                       1, div, div * 2);
+               _ipu_di_data_pin_config(disp, ASYNC_SER_WAVE, DI_PIN_SER_RS,
+                                       2, 0, 0);
+
+               _ipu_dc_write_tmpl(0x64, WROD(0), 0, map, ASYNC_SER_WAVE, 0, 0, 1);
+
+               /* Configure DC for serial panel */
+               __raw_writel(0x14, DC_DISP_CONF1(DC_DISP_ID_SERIAL));
+
+               if (sig.clk_pol)
+                       ser_conf |= DI_SER_CONF_SERIAL_CLK_POL;
+               if (sig.data_pol)
+                       ser_conf |= DI_SER_CONF_SERIAL_DATA_POL;
+               if (sig.rs_pol)
+                       ser_conf |= DI_SER_CONF_SERIAL_RS_POL;
+               if (sig.cs_pol)
+                       ser_conf |= DI_SER_CONF_SERIAL_CS_POL;
+               __raw_writel(ser_conf, DI_SER_CONF(disp));
+       }
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+       return 0;
+}
+EXPORT_SYMBOL(ipu_init_async_panel);
+
+/*!
+ * This function sets the foreground and background plane global alpha blending
+ * modes. This function also sets the DP graphic plane according to the
+ * parameter of IPUv3 DP channel.
+ *
+ * @param      channel         IPUv3 DP channel
+ *
+ * @param       enable          Boolean to enable or disable global alpha
+ *                              blending. If disabled, local blending is used.
+ *
+ * @param       alpha           Global alpha value.
+ *
+ * @return      Returns 0 on success or negative error code on fail
+ */
+int32_t ipu_disp_set_global_alpha(ipu_channel_t channel, bool enable,
+                                 uint8_t alpha)
+{
+       uint32_t reg;
+       uint32_t flow;
+       unsigned long lock_flags;
+       bool bg_chan;
+
+       if (channel == MEM_BG_SYNC || channel == MEM_FG_SYNC)
+               flow = DP_SYNC;
+       else if (channel == MEM_BG_ASYNC0 || channel == MEM_FG_ASYNC0)
+               flow = DP_ASYNC0;
+       else if (channel == MEM_BG_ASYNC1 || channel == MEM_FG_ASYNC1)
+               flow = DP_ASYNC1;
+       else
+               return -EINVAL;
+
+       if (channel == MEM_BG_SYNC || channel == MEM_BG_ASYNC0 ||
+           channel == MEM_BG_ASYNC1)
+               bg_chan = true;
+       else
+               bg_chan = false;
+
+       if (!g_ipu_clk_enabled)
+               clk_enable(g_ipu_clk);
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       if (bg_chan) {
+               reg = __raw_readl(DP_COM_CONF(flow));
+               __raw_writel(reg & ~DP_COM_CONF_GWSEL, DP_COM_CONF(flow));
+       } else {
+               reg = __raw_readl(DP_COM_CONF(flow));
+               __raw_writel(reg | DP_COM_CONF_GWSEL, DP_COM_CONF(flow));
+       }
+
+       if (enable) {
+               reg = __raw_readl(DP_GRAPH_WIND_CTRL(flow)) & 0x00FFFFFFL;
+               __raw_writel(reg | ((uint32_t) alpha << 24),
+                            DP_GRAPH_WIND_CTRL(flow));
+
+               reg = __raw_readl(DP_COM_CONF(flow));
+               __raw_writel(reg | DP_COM_CONF_GWAM, DP_COM_CONF(flow));
+       } else {
+               reg = __raw_readl(DP_COM_CONF(flow));
+               __raw_writel(reg & ~DP_COM_CONF_GWAM, DP_COM_CONF(flow));
+       }
+
+       reg = __raw_readl(IPU_SRM_PRI2) | 0x8;
+       __raw_writel(reg, IPU_SRM_PRI2);
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+       if (!g_ipu_clk_enabled)
+               clk_disable(g_ipu_clk);
+
+       return 0;
+}
+EXPORT_SYMBOL(ipu_disp_set_global_alpha);
+
+/*!
+ * This function sets the transparent color key for SDC graphic plane.
+ *
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @param       enable          Boolean to enable or disable color key
+ *
+ * @param       colorKey        24-bit RGB color for transparent color key.
+ *
+ * @return      Returns 0 on success or negative error code on fail
+ */
+int32_t ipu_disp_set_color_key(ipu_channel_t channel, bool enable,
+                              uint32_t color_key)
+{
+       uint32_t reg, flow;
+       int y, u, v;
+       int red, green, blue;
+       unsigned long lock_flags;
+
+       if (channel == MEM_BG_SYNC || channel == MEM_FG_SYNC)
+               flow = DP_SYNC;
+       else if (channel == MEM_BG_ASYNC0 || channel == MEM_FG_ASYNC0)
+               flow = DP_ASYNC0;
+       else if (channel == MEM_BG_ASYNC1 || channel == MEM_FG_ASYNC1)
+               flow = DP_ASYNC1;
+       else
+               return -EINVAL;
+
+       if (!g_ipu_clk_enabled)
+               clk_enable(g_ipu_clk);
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       color_key_4rgb = 1;
+       /* Transform color key from rgb to yuv if CSC is enabled */
+       if (((fg_csc_type == RGB2YUV) && (bg_csc_type == YUV2YUV)) ||
+                       ((fg_csc_type == YUV2YUV) && (bg_csc_type == RGB2YUV)) ||
+                       ((fg_csc_type == YUV2YUV) && (bg_csc_type == YUV2YUV)) ||
+                       ((fg_csc_type == YUV2RGB) && (bg_csc_type == YUV2RGB))) {
+
+               dev_dbg(g_ipu_dev, "color key 0x%x need change to yuv fmt\n", color_key);
+
+               red = (color_key >> 16) & 0xFF;
+               green = (color_key >> 8) & 0xFF;
+               blue = color_key & 0xFF;
+
+               y = _rgb_to_yuv(0, red, green, blue);
+               u = _rgb_to_yuv(1, red, green, blue);
+               v = _rgb_to_yuv(2, red, green, blue);
+               color_key = (y << 16) | (u << 8) | v;
+
+               color_key_4rgb = 0;
+
+               dev_dbg(g_ipu_dev, "color key change to yuv fmt 0x%x\n", color_key);
+       }
+
+       if (enable) {
+               reg = __raw_readl(DP_GRAPH_WIND_CTRL(flow)) & 0xFF000000L;
+               __raw_writel(reg | color_key, DP_GRAPH_WIND_CTRL(flow));
+
+               reg = __raw_readl(DP_COM_CONF(flow));
+               __raw_writel(reg | DP_COM_CONF_GWCKE, DP_COM_CONF(flow));
+       } else {
+               reg = __raw_readl(DP_COM_CONF(flow));
+               __raw_writel(reg & ~DP_COM_CONF_GWCKE, DP_COM_CONF(flow));
+       }
+
+       reg = __raw_readl(IPU_SRM_PRI2) | 0x8;
+       __raw_writel(reg, IPU_SRM_PRI2);
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+       if (!g_ipu_clk_enabled)
+               clk_disable(g_ipu_clk);
+
+       return 0;
+}
+EXPORT_SYMBOL(ipu_disp_set_color_key);
+
+/*!
+ * This function sets the gamma correction for DP output.
+ *
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @param       enable          Boolean to enable or disable gamma correction.
+ *
+ * @param       constk         Gamma piecewise linear approximation constk coeff.
+ *
+ * @param       slopek         Gamma piecewise linear approximation slopek coeff.
+ *
+ * @return      Returns 0 on success or negative error code on fail
+ */
+int32_t ipu_disp_set_gamma_correction(ipu_channel_t channel, bool enable, int constk[], int slopek[])
+{
+       uint32_t reg, flow, i;
+       unsigned long lock_flags;
+
+       if (channel == MEM_BG_SYNC || channel == MEM_FG_SYNC)
+               flow = DP_SYNC;
+       else if (channel == MEM_BG_ASYNC0 || channel == MEM_FG_ASYNC0)
+               flow = DP_ASYNC0;
+       else if (channel == MEM_BG_ASYNC1 || channel == MEM_FG_ASYNC1)
+               flow = DP_ASYNC1;
+       else
+               return -EINVAL;
+
+       if (!g_ipu_clk_enabled)
+               clk_enable(g_ipu_clk);
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       for (i = 0; i < 8; i++)
+               __raw_writel((constk[2*i] & 0x1ff) | ((constk[2*i+1] & 0x1ff) << 16), DP_GAMMA_C(flow, i));
+       for (i = 0; i < 4; i++)
+               __raw_writel((slopek[4*i] & 0xff) | ((slopek[4*i+1] & 0xff) << 8) |
+                       ((slopek[4*i+2] & 0xff) << 16) | ((slopek[4*i+3] & 0xff) << 24), DP_GAMMA_S(flow, i));
+
+       reg = __raw_readl(DP_COM_CONF(flow));
+       if (enable) {
+               if ((bg_csc_type == RGB2YUV) || (bg_csc_type == YUV2YUV))
+                       reg |= DP_COM_CONF_GAMMA_YUV_EN;
+               else
+                       reg &= ~DP_COM_CONF_GAMMA_YUV_EN;
+               __raw_writel(reg | DP_COM_CONF_GAMMA_EN, DP_COM_CONF(flow));
+       } else
+               __raw_writel(reg & ~DP_COM_CONF_GAMMA_EN, DP_COM_CONF(flow));
+
+       reg = __raw_readl(IPU_SRM_PRI2) | 0x8;
+       __raw_writel(reg, IPU_SRM_PRI2);
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+       if (!g_ipu_clk_enabled)
+               clk_disable(g_ipu_clk);
+
+       return 0;
+}
+EXPORT_SYMBOL(ipu_disp_set_gamma_correction);
+
+/*!
+ * This function sets the window position of the foreground or background plane.
+ * modes.
+ *
+ * @param       channel         Input parameter for the logical channel ID.
+ *
+ * @param       x_pos           The X coordinate position to place window at.
+ *                              The position is relative to the top left corner.
+ *
+ * @param       y_pos           The Y coordinate position to place window at.
+ *                              The position is relative to the top left corner.
+ *
+ * @return      Returns 0 on success or negative error code on fail
+ */
+int32_t ipu_disp_set_window_pos(ipu_channel_t channel, int16_t x_pos,
+                               int16_t y_pos)
+{
+       u32 reg;
+       unsigned long lock_flags;
+       uint32_t flow = 0;
+       uint32_t dp_srm_shift;
+
+       if (channel == MEM_FG_SYNC) {
+               flow = DP_SYNC;
+               dp_srm_shift = 3;
+       } else if (channel == MEM_FG_ASYNC0) {
+               flow = DP_ASYNC0;
+               dp_srm_shift = 5;
+       } else if (channel == MEM_FG_ASYNC1) {
+               flow = DP_ASYNC1;
+               dp_srm_shift = 7;
+       } else
+               return -EINVAL;
+
+       if (!g_ipu_clk_enabled)
+               clk_enable(g_ipu_clk);
+
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       __raw_writel((x_pos << 16) | y_pos, DP_FG_POS(flow));
+
+       if (ipu_is_channel_busy(channel)) {
+               /* controled by FSU if channel enabled */
+               reg = __raw_readl(IPU_SRM_PRI2) & (~(0x3 << dp_srm_shift));
+               reg |= (0x1 << dp_srm_shift);
+               __raw_writel(reg, IPU_SRM_PRI2);
+       } else {
+               /* disable auto swap, controled by MCU if channel disabled */
+               reg = __raw_readl(IPU_SRM_PRI2) & (~(0x3 << dp_srm_shift));
+               __raw_writel(reg, IPU_SRM_PRI2);
+       }
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+       if (!g_ipu_clk_enabled)
+               clk_disable(g_ipu_clk);
+
+       return 0;
+}
+EXPORT_SYMBOL(ipu_disp_set_window_pos);
+
+int32_t ipu_disp_get_window_pos(ipu_channel_t channel, int16_t *x_pos,
+                               int16_t *y_pos)
+{
+       u32 reg;
+       unsigned long lock_flags;
+       uint32_t flow = 0;
+
+       if (channel == MEM_FG_SYNC)
+               flow = DP_SYNC;
+       else if (channel == MEM_FG_ASYNC0)
+               flow = DP_ASYNC0;
+       else if (channel == MEM_FG_ASYNC1)
+               flow = DP_ASYNC1;
+       else
+               return -EINVAL;
+
+       if (!g_ipu_clk_enabled)
+               clk_enable(g_ipu_clk);
+       spin_lock_irqsave(&ipu_lock, lock_flags);
+
+       reg = __raw_readl(DP_FG_POS(flow));
+
+       *x_pos = (reg >> 16) & 0x7FF;
+       *y_pos = reg & 0x7FF;
+
+       spin_unlock_irqrestore(&ipu_lock, lock_flags);
+       if (!g_ipu_clk_enabled)
+               clk_disable(g_ipu_clk);
+
+       return 0;
+}
+EXPORT_SYMBOL(ipu_disp_get_window_pos);
+
+void ipu_disp_direct_write(ipu_channel_t channel, u32 value, u32 offset)
+{
+       if (channel == DIRECT_ASYNC0)
+               __raw_writel(value, ipu_disp_base[0] + offset);
+       else if (channel == DIRECT_ASYNC1)
+               __raw_writel(value, ipu_disp_base[1] + offset);
+}
+EXPORT_SYMBOL(ipu_disp_direct_write);
+
+void ipu_reset_disp_panel(void)
+{
+       uint32_t tmp;
+
+       tmp = __raw_readl(DI_GENERAL(1));
+       __raw_writel(tmp | 0x08, DI_GENERAL(1));
+       msleep(10); /* tRES >= 100us */
+       tmp = __raw_readl(DI_GENERAL(1));
+       __raw_writel(tmp & ~0x08, DI_GENERAL(1));
+       msleep(60);
+
+       return;
+}
+EXPORT_SYMBOL(ipu_reset_disp_panel);
diff --git a/drivers/mxc/ipu3/ipu_ic.c b/drivers/mxc/ipu3/ipu_ic.c
new file mode 100644 (file)
index 0000000..77a67bf
--- /dev/null
@@ -0,0 +1,833 @@
+/*
+ * Copyright 2005-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 ipu_ic.c
+ *
+ * @brief IPU IC functions
+ *
+ * @ingroup IPU
+ */
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/videodev2.h>
+#include <linux/io.h>
+#include <linux/ipu.h>
+
+#include "ipu_prv.h"
+#include "ipu_regs.h"
+#include "ipu_param_mem.h"
+
+enum {
+       IC_TASK_VIEWFINDER,
+       IC_TASK_ENCODER,
+       IC_TASK_POST_PROCESSOR
+};
+
+static void _init_csc(uint8_t ic_task, ipu_color_space_t in_format,
+                     ipu_color_space_t out_format, int csc_index);
+static bool _calc_resize_coeffs(uint32_t inSize, uint32_t outSize,
+                               uint32_t *resizeCoeff,
+                               uint32_t *downsizeCoeff);
+
+void _ipu_vdi_set_top_field_man(bool top_field_0)
+{
+       uint32_t reg;
+
+       reg = __raw_readl(VDI_C);
+       if (top_field_0)
+               reg &= ~VDI_C_TOP_FIELD_MAN_1;
+       else
+               reg |= VDI_C_TOP_FIELD_MAN_1;
+       __raw_writel(reg, VDI_C);
+}
+
+void _ipu_vdi_set_motion(ipu_motion_sel motion_sel)
+{
+       uint32_t reg;
+
+       reg = __raw_readl(VDI_C);
+       reg &= ~(VDI_C_MOT_SEL_FULL | VDI_C_MOT_SEL_MED | VDI_C_MOT_SEL_LOW);
+       if (motion_sel == HIGH_MOTION)
+               reg |= VDI_C_MOT_SEL_FULL;
+       else if (motion_sel == MED_MOTION)
+               reg |= VDI_C_MOT_SEL_MED;
+       else
+               reg |= VDI_C_MOT_SEL_LOW;
+
+       __raw_writel(reg, VDI_C);
+}
+
+void ic_dump_register(void)
+{
+       printk(KERN_DEBUG "IC_CONF = \t0x%08X\n", __raw_readl(IC_CONF));
+       printk(KERN_DEBUG "IC_PRP_ENC_RSC = \t0x%08X\n",
+              __raw_readl(IC_PRP_ENC_RSC));
+       printk(KERN_DEBUG "IC_PRP_VF_RSC = \t0x%08X\n",
+              __raw_readl(IC_PRP_VF_RSC));
+       printk(KERN_DEBUG "IC_PP_RSC = \t0x%08X\n", __raw_readl(IC_PP_RSC));
+       printk(KERN_DEBUG "IC_IDMAC_1 = \t0x%08X\n", __raw_readl(IC_IDMAC_1));
+       printk(KERN_DEBUG "IC_IDMAC_2 = \t0x%08X\n", __raw_readl(IC_IDMAC_2));
+       printk(KERN_DEBUG "IC_IDMAC_3 = \t0x%08X\n", __raw_readl(IC_IDMAC_3));
+}
+
+void _ipu_ic_enable_task(ipu_channel_t channel)
+{
+       uint32_t ic_conf;
+
+       ic_conf = __raw_readl(IC_CONF);
+       switch (channel) {
+       case CSI_PRP_VF_MEM:
+       case MEM_PRP_VF_MEM:
+               ic_conf |= IC_CONF_PRPVF_EN;
+               break;
+       case MEM_VDI_PRP_VF_MEM:
+               ic_conf |= IC_CONF_PRPVF_EN;
+               break;
+       case MEM_ROT_VF_MEM:
+               ic_conf |= IC_CONF_PRPVF_ROT_EN;
+               break;
+       case CSI_PRP_ENC_MEM:
+       case MEM_PRP_ENC_MEM:
+               ic_conf |= IC_CONF_PRPENC_EN;
+               break;
+       case MEM_ROT_ENC_MEM:
+               ic_conf |= IC_CONF_PRPENC_ROT_EN;
+               break;
+       case MEM_PP_MEM:
+               ic_conf |= IC_CONF_PP_EN;
+               break;
+       case MEM_ROT_PP_MEM:
+               ic_conf |= IC_CONF_PP_ROT_EN;
+               break;
+       default:
+               break;
+       }
+       __raw_writel(ic_conf, IC_CONF);
+}
+
+void _ipu_ic_disable_task(ipu_channel_t channel)
+{
+       uint32_t ic_conf;
+
+       ic_conf = __raw_readl(IC_CONF);
+       switch (channel) {
+       case CSI_PRP_VF_MEM:
+       case MEM_PRP_VF_MEM:
+               ic_conf &= ~IC_CONF_PRPVF_EN;
+               break;
+       case MEM_VDI_PRP_VF_MEM:
+               ic_conf &= ~IC_CONF_PRPVF_EN;
+               break;
+       case MEM_ROT_VF_MEM:
+               ic_conf &= ~IC_CONF_PRPVF_ROT_EN;
+               break;
+       case CSI_PRP_ENC_MEM:
+       case MEM_PRP_ENC_MEM:
+               ic_conf &= ~IC_CONF_PRPENC_EN;
+               break;
+       case MEM_ROT_ENC_MEM:
+               ic_conf &= ~IC_CONF_PRPENC_ROT_EN;
+               break;
+       case MEM_PP_MEM:
+               ic_conf &= ~IC_CONF_PP_EN;
+               break;
+       case MEM_ROT_PP_MEM:
+               ic_conf &= ~IC_CONF_PP_ROT_EN;
+               break;
+       default:
+               break;
+       }
+       __raw_writel(ic_conf, IC_CONF);
+}
+
+void _ipu_vdi_init(ipu_channel_t channel, ipu_channel_params_t *params)
+{
+       uint32_t reg;
+       uint32_t pixel_fmt;
+
+       reg = ((params->mem_prp_vf_mem.in_height-1) << 16) |
+         (params->mem_prp_vf_mem.in_width-1);
+       __raw_writel(reg, VDI_FSIZE);
+
+       /* Full motion, only vertical filter is used
+          Burst size is 4 accesses */
+       if (params->mem_prp_vf_mem.in_pixel_fmt ==
+            IPU_PIX_FMT_UYVY ||
+            params->mem_prp_vf_mem.in_pixel_fmt ==
+            IPU_PIX_FMT_YUYV)
+               pixel_fmt = VDI_C_CH_422;
+       else
+               pixel_fmt = VDI_C_CH_420;
+
+       reg = __raw_readl(VDI_C);
+       reg |= pixel_fmt;
+       switch (channel) {
+       case MEM_VDI_PRP_VF_MEM:
+               reg |= VDI_C_BURST_SIZE2_4;
+               break;
+       case MEM_VDI_PRP_VF_MEM_P:
+               reg |= VDI_C_BURST_SIZE1_4 | VDI_C_VWM1_SET_1 | VDI_C_VWM1_CLR_2;
+               break;
+       case MEM_VDI_PRP_VF_MEM_N:
+               reg |= VDI_C_BURST_SIZE3_4 | VDI_C_VWM3_SET_1 | VDI_C_VWM3_CLR_2;
+               break;
+       default:
+               break;
+       }
+       __raw_writel(reg, VDI_C);
+
+       if (params->mem_prp_vf_mem.field_fmt == V4L2_FIELD_INTERLACED_TB)
+               _ipu_vdi_set_top_field_man(false);
+       else if (params->mem_prp_vf_mem.field_fmt == V4L2_FIELD_INTERLACED_BT)
+               _ipu_vdi_set_top_field_man(true);
+
+       _ipu_vdi_set_motion(params->mem_prp_vf_mem.motion_sel);
+
+       reg = __raw_readl(IC_CONF);
+       reg &= ~IC_CONF_RWS_EN;
+       __raw_writel(reg, IC_CONF);
+}
+
+void _ipu_vdi_uninit(void)
+{
+       __raw_writel(0, VDI_FSIZE);
+       __raw_writel(0, VDI_C);
+}
+
+void _ipu_ic_init_prpvf(ipu_channel_params_t *params, bool src_is_csi)
+{
+       uint32_t reg, ic_conf;
+       uint32_t downsizeCoeff, resizeCoeff;
+       ipu_color_space_t in_fmt, out_fmt;
+
+       /* Setup vertical resizing */
+       _calc_resize_coeffs(params->mem_prp_vf_mem.in_height,
+                           params->mem_prp_vf_mem.out_height,
+                           &resizeCoeff, &downsizeCoeff);
+       reg = (downsizeCoeff << 30) | (resizeCoeff << 16);
+
+       /* Setup horizontal resizing */
+       /* Upadeted for IC split case */
+       if (!(params->mem_prp_vf_mem.outh_resize_ratio)) {
+               _calc_resize_coeffs(params->mem_prp_vf_mem.in_width,
+                               params->mem_prp_vf_mem.out_width,
+                               &resizeCoeff, &downsizeCoeff);
+               reg |= (downsizeCoeff << 14) | resizeCoeff;
+       } else
+               reg |= params->mem_prp_vf_mem.outh_resize_ratio;
+
+       __raw_writel(reg, IC_PRP_VF_RSC);
+
+       ic_conf = __raw_readl(IC_CONF);
+
+       /* Setup color space conversion */
+       in_fmt = format_to_colorspace(params->mem_prp_vf_mem.in_pixel_fmt);
+       out_fmt = format_to_colorspace(params->mem_prp_vf_mem.out_pixel_fmt);
+       if (in_fmt == RGB) {
+               if ((out_fmt == YCbCr) || (out_fmt == YUV)) {
+                       /* Enable RGB->YCBCR CSC1 */
+                       _init_csc(IC_TASK_VIEWFINDER, RGB, out_fmt, 1);
+                       ic_conf |= IC_CONF_PRPVF_CSC1;
+               }
+       }
+       if ((in_fmt == YCbCr) || (in_fmt == YUV)) {
+               if (out_fmt == RGB) {
+                       /* Enable YCBCR->RGB CSC1 */
+                       _init_csc(IC_TASK_VIEWFINDER, YCbCr, RGB, 1);
+                       ic_conf |= IC_CONF_PRPVF_CSC1;
+               } else {
+                       /* TODO: Support YUV<->YCbCr conversion? */
+               }
+       }
+
+       if (params->mem_prp_vf_mem.graphics_combine_en) {
+               ic_conf |= IC_CONF_PRPVF_CMB;
+
+               if (!(ic_conf & IC_CONF_PRPVF_CSC1)) {
+                       /* need transparent CSC1 conversion */
+                       _init_csc(IC_TASK_VIEWFINDER, RGB, RGB, 1);
+                       ic_conf |= IC_CONF_PRPVF_CSC1;  /* Enable RGB->RGB CSC */
+               }
+               in_fmt = format_to_colorspace(params->mem_prp_vf_mem.in_g_pixel_fmt);
+               out_fmt = format_to_colorspace(params->mem_prp_vf_mem.out_pixel_fmt);
+               if (in_fmt == RGB) {
+                       if ((out_fmt == YCbCr) || (out_fmt == YUV)) {
+                               /* Enable RGB->YCBCR CSC2 */
+                               _init_csc(IC_TASK_VIEWFINDER, RGB, out_fmt, 2);
+                               ic_conf |= IC_CONF_PRPVF_CSC2;
+                       }
+               }
+               if ((in_fmt == YCbCr) || (in_fmt == YUV)) {
+                       if (out_fmt == RGB) {
+                               /* Enable YCBCR->RGB CSC2 */
+                               _init_csc(IC_TASK_VIEWFINDER, YCbCr, RGB, 2);
+                               ic_conf |= IC_CONF_PRPVF_CSC2;
+                       } else {
+                               /* TODO: Support YUV<->YCbCr conversion? */
+                       }
+               }
+
+               if (params->mem_prp_vf_mem.global_alpha_en) {
+                       ic_conf |= IC_CONF_IC_GLB_LOC_A;
+                       reg = __raw_readl(IC_CMBP_1);
+                       reg &= ~(0xff);
+                       reg |= params->mem_prp_vf_mem.alpha;
+                       __raw_writel(reg, IC_CMBP_1);
+               } else
+                       ic_conf &= ~IC_CONF_IC_GLB_LOC_A;
+
+               if (params->mem_prp_vf_mem.key_color_en) {
+                       ic_conf |= IC_CONF_KEY_COLOR_EN;
+                       __raw_writel(params->mem_prp_vf_mem.key_color,
+                                       IC_CMBP_2);
+               } else
+                       ic_conf &= ~IC_CONF_KEY_COLOR_EN;
+       } else {
+               ic_conf &= ~IC_CONF_PRPVF_CMB;
+       }
+
+       if (src_is_csi)
+               ic_conf &= ~IC_CONF_RWS_EN;
+       else
+               ic_conf |= IC_CONF_RWS_EN;
+
+       __raw_writel(ic_conf, IC_CONF);
+}
+
+void _ipu_ic_uninit_prpvf(void)
+{
+       uint32_t reg;
+
+       reg = __raw_readl(IC_CONF);
+       reg &= ~(IC_CONF_PRPVF_EN | IC_CONF_PRPVF_CMB |
+                IC_CONF_PRPVF_CSC2 | IC_CONF_PRPVF_CSC1);
+       __raw_writel(reg, IC_CONF);
+}
+
+void _ipu_ic_init_rotate_vf(ipu_channel_params_t *params)
+{
+}
+
+void _ipu_ic_uninit_rotate_vf(void)
+{
+       uint32_t reg;
+       reg = __raw_readl(IC_CONF);
+       reg &= ~IC_CONF_PRPVF_ROT_EN;
+       __raw_writel(reg, IC_CONF);
+}
+
+void _ipu_ic_init_prpenc(ipu_channel_params_t *params, bool src_is_csi)
+{
+       uint32_t reg, ic_conf;
+       uint32_t downsizeCoeff, resizeCoeff;
+       ipu_color_space_t in_fmt, out_fmt;
+
+       /* Setup vertical resizing */
+       _calc_resize_coeffs(params->mem_prp_enc_mem.in_height,
+                           params->mem_prp_enc_mem.out_height,
+                           &resizeCoeff, &downsizeCoeff);
+       reg = (downsizeCoeff << 30) | (resizeCoeff << 16);
+
+       /* Setup horizontal resizing */
+       /* Upadeted for IC split case */
+       if (!(params->mem_prp_enc_mem.outh_resize_ratio)) {
+               _calc_resize_coeffs(params->mem_prp_enc_mem.in_width,
+                               params->mem_prp_enc_mem.out_width,
+                               &resizeCoeff, &downsizeCoeff);
+               reg |= (downsizeCoeff << 14) | resizeCoeff;
+       } else
+               reg |= params->mem_prp_enc_mem.outh_resize_ratio;
+
+       __raw_writel(reg, IC_PRP_ENC_RSC);
+
+       ic_conf = __raw_readl(IC_CONF);
+
+       /* Setup color space conversion */
+       in_fmt = format_to_colorspace(params->mem_prp_enc_mem.in_pixel_fmt);
+       out_fmt = format_to_colorspace(params->mem_prp_enc_mem.out_pixel_fmt);
+       if (in_fmt == RGB) {
+               if ((out_fmt == YCbCr) || (out_fmt == YUV)) {
+                       /* Enable RGB->YCBCR CSC1 */
+                       _init_csc(IC_TASK_ENCODER, RGB, out_fmt, 1);
+                       ic_conf |= IC_CONF_PRPENC_CSC1;
+               }
+       }
+       if ((in_fmt == YCbCr) || (in_fmt == YUV)) {
+               if (out_fmt == RGB) {
+                       /* Enable YCBCR->RGB CSC1 */
+                       _init_csc(IC_TASK_ENCODER, YCbCr, RGB, 1);
+                       ic_conf |= IC_CONF_PRPENC_CSC1;
+               } else {
+                       /* TODO: Support YUV<->YCbCr conversion? */
+               }
+       }
+
+       if (src_is_csi)
+               ic_conf &= ~IC_CONF_RWS_EN;
+       else
+               ic_conf |= IC_CONF_RWS_EN;
+
+       __raw_writel(ic_conf, IC_CONF);
+}
+
+void _ipu_ic_uninit_prpenc(void)
+{
+       uint32_t reg;
+
+       reg = __raw_readl(IC_CONF);
+       reg &= ~(IC_CONF_PRPENC_EN | IC_CONF_PRPENC_CSC1);
+       __raw_writel(reg, IC_CONF);
+}
+
+void _ipu_ic_init_rotate_enc(ipu_channel_params_t *params)
+{
+}
+
+void _ipu_ic_uninit_rotate_enc(void)
+{
+       uint32_t reg;
+
+       reg = __raw_readl(IC_CONF);
+       reg &= ~(IC_CONF_PRPENC_ROT_EN);
+       __raw_writel(reg, IC_CONF);
+}
+
+void _ipu_ic_init_pp(ipu_channel_params_t *params)
+{
+       uint32_t reg, ic_conf;
+       uint32_t downsizeCoeff, resizeCoeff;
+       ipu_color_space_t in_fmt, out_fmt;
+
+       /* Setup vertical resizing */
+       if (!(params->mem_pp_mem.outv_resize_ratio)) {
+               _calc_resize_coeffs(params->mem_pp_mem.in_height,
+                           params->mem_pp_mem.out_height,
+                           &resizeCoeff, &downsizeCoeff);
+               reg = (downsizeCoeff << 30) | (resizeCoeff << 16);
+       } else {
+               reg = (params->mem_pp_mem.outv_resize_ratio) << 16;
+       }
+
+       /* Setup horizontal resizing */
+       /* Upadeted for IC split case */
+       if (!(params->mem_pp_mem.outh_resize_ratio)) {
+               _calc_resize_coeffs(params->mem_pp_mem.in_width,
+                                                       params->mem_pp_mem.out_width,
+                                                       &resizeCoeff, &downsizeCoeff);
+               reg |= (downsizeCoeff << 14) | resizeCoeff;
+       } else {
+               reg |= params->mem_pp_mem.outh_resize_ratio;
+       }
+
+       __raw_writel(reg, IC_PP_RSC);
+
+       ic_conf = __raw_readl(IC_CONF);
+
+       /* Setup color space conversion */
+       in_fmt = format_to_colorspace(params->mem_pp_mem.in_pixel_fmt);
+       out_fmt = format_to_colorspace(params->mem_pp_mem.out_pixel_fmt);
+       if (in_fmt == RGB) {
+               if ((out_fmt == YCbCr) || (out_fmt == YUV)) {
+                       /* Enable RGB->YCBCR CSC1 */
+                       _init_csc(IC_TASK_POST_PROCESSOR, RGB, out_fmt, 1);
+                       ic_conf |= IC_CONF_PP_CSC1;
+               }
+       }
+       if ((in_fmt == YCbCr) || (in_fmt == YUV)) {
+               if (out_fmt == RGB) {
+                       /* Enable YCBCR->RGB CSC1 */
+                       _init_csc(IC_TASK_POST_PROCESSOR, YCbCr, RGB, 1);
+                       ic_conf |= IC_CONF_PP_CSC1;
+               } else {
+                       /* TODO: Support YUV<->YCbCr conversion? */
+               }
+       }
+
+       if (params->mem_pp_mem.graphics_combine_en) {
+               ic_conf |= IC_CONF_PP_CMB;
+
+               if (!(ic_conf & IC_CONF_PP_CSC1)) {
+                       /* need transparent CSC1 conversion */
+                       _init_csc(IC_TASK_POST_PROCESSOR, RGB, RGB, 1);
+                       ic_conf |= IC_CONF_PP_CSC1;  /* Enable RGB->RGB CSC */
+               }
+
+               in_fmt = format_to_colorspace(params->mem_pp_mem.in_g_pixel_fmt);
+               out_fmt = format_to_colorspace(params->mem_pp_mem.out_pixel_fmt);
+               if (in_fmt == RGB) {
+                       if ((out_fmt == YCbCr) || (out_fmt == YUV)) {
+                               /* Enable RGB->YCBCR CSC2 */
+                               _init_csc(IC_TASK_POST_PROCESSOR, RGB, out_fmt, 2);
+                               ic_conf |= IC_CONF_PP_CSC2;
+                       }
+               }
+               if ((in_fmt == YCbCr) || (in_fmt == YUV)) {
+                       if (out_fmt == RGB) {
+                               /* Enable YCBCR->RGB CSC2 */
+                               _init_csc(IC_TASK_POST_PROCESSOR, YCbCr, RGB, 2);
+                               ic_conf |= IC_CONF_PP_CSC2;
+                       } else {
+                               /* TODO: Support YUV<->YCbCr conversion? */
+                       }
+               }
+
+               if (params->mem_pp_mem.global_alpha_en) {
+                       ic_conf |= IC_CONF_IC_GLB_LOC_A;
+                       reg = __raw_readl(IC_CMBP_1);
+                       reg &= ~(0xff00);
+                       reg |= (params->mem_pp_mem.alpha << 8);
+                       __raw_writel(reg, IC_CMBP_1);
+               } else
+                       ic_conf &= ~IC_CONF_IC_GLB_LOC_A;
+
+               if (params->mem_pp_mem.key_color_en) {
+                       ic_conf |= IC_CONF_KEY_COLOR_EN;
+                       __raw_writel(params->mem_pp_mem.key_color,
+                                       IC_CMBP_2);
+               } else
+                       ic_conf &= ~IC_CONF_KEY_COLOR_EN;
+       } else {
+               ic_conf &= ~IC_CONF_PP_CMB;
+       }
+
+       __raw_writel(ic_conf, IC_CONF);
+}
+
+void _ipu_ic_uninit_pp(void)
+{
+       uint32_t reg;
+
+       reg = __raw_readl(IC_CONF);
+       reg &= ~(IC_CONF_PP_EN | IC_CONF_PP_CSC1 | IC_CONF_PP_CSC2 |
+                IC_CONF_PP_CMB);
+       __raw_writel(reg, IC_CONF);
+}
+
+void _ipu_ic_init_rotate_pp(ipu_channel_params_t *params)
+{
+}
+
+void _ipu_ic_uninit_rotate_pp(void)
+{
+       uint32_t reg;
+       reg = __raw_readl(IC_CONF);
+       reg &= ~IC_CONF_PP_ROT_EN;
+       __raw_writel(reg, IC_CONF);
+}
+
+int _ipu_ic_idma_init(int dma_chan, uint16_t width, uint16_t height,
+                     int burst_size, ipu_rotate_mode_t rot)
+{
+       u32 ic_idmac_1, ic_idmac_2, ic_idmac_3;
+       u32 temp_rot = bitrev8(rot) >> 5;
+       bool need_hor_flip = false;
+
+       if ((burst_size != 8) && (burst_size != 16)) {
+               dev_dbg(g_ipu_dev, "Illegal burst length for IC\n");
+               return -EINVAL;
+       }
+
+       width--;
+       height--;
+
+       if (temp_rot & 0x2)     /* Need horizontal flip */
+               need_hor_flip = true;
+
+       ic_idmac_1 = __raw_readl(IC_IDMAC_1);
+       ic_idmac_2 = __raw_readl(IC_IDMAC_2);
+       ic_idmac_3 = __raw_readl(IC_IDMAC_3);
+       if (dma_chan == 22) {   /* PP output - CB2 */
+               if (burst_size == 16)
+                       ic_idmac_1 |= IC_IDMAC_1_CB2_BURST_16;
+               else
+                       ic_idmac_1 &= ~IC_IDMAC_1_CB2_BURST_16;
+
+               if (need_hor_flip)
+                       ic_idmac_1 |= IC_IDMAC_1_PP_FLIP_RS;
+               else
+                       ic_idmac_1 &= ~IC_IDMAC_1_PP_FLIP_RS;
+
+               ic_idmac_2 &= ~IC_IDMAC_2_PP_HEIGHT_MASK;
+               ic_idmac_2 |= height << IC_IDMAC_2_PP_HEIGHT_OFFSET;
+
+               ic_idmac_3 &= ~IC_IDMAC_3_PP_WIDTH_MASK;
+               ic_idmac_3 |= width << IC_IDMAC_3_PP_WIDTH_OFFSET;
+
+       } else if (dma_chan == 11) {    /* PP Input - CB5 */
+               if (burst_size == 16)
+                       ic_idmac_1 |= IC_IDMAC_1_CB5_BURST_16;
+               else
+                       ic_idmac_1 &= ~IC_IDMAC_1_CB5_BURST_16;
+       } else if (dma_chan == 47) {    /* PP Rot input */
+               ic_idmac_1 &= ~IC_IDMAC_1_PP_ROT_MASK;
+               ic_idmac_1 |= temp_rot << IC_IDMAC_1_PP_ROT_OFFSET;
+       }
+
+       if (dma_chan == 12) {   /* PRP Input - CB6 */
+               if (burst_size == 16)
+                       ic_idmac_1 |= IC_IDMAC_1_CB6_BURST_16;
+               else
+                       ic_idmac_1 &= ~IC_IDMAC_1_CB6_BURST_16;
+       }
+
+       if (dma_chan == 20) {   /* PRP ENC output - CB0 */
+               if (burst_size == 16)
+                       ic_idmac_1 |= IC_IDMAC_1_CB0_BURST_16;
+               else
+                       ic_idmac_1 &= ~IC_IDMAC_1_CB0_BURST_16;
+
+               if (need_hor_flip)
+                       ic_idmac_1 |= IC_IDMAC_1_PRPENC_FLIP_RS;
+               else
+                       ic_idmac_1 &= ~IC_IDMAC_1_PRPENC_FLIP_RS;
+
+               ic_idmac_2 &= ~IC_IDMAC_2_PRPENC_HEIGHT_MASK;
+               ic_idmac_2 |= height << IC_IDMAC_2_PRPENC_HEIGHT_OFFSET;
+
+               ic_idmac_3 &= ~IC_IDMAC_3_PRPENC_WIDTH_MASK;
+               ic_idmac_3 |= width << IC_IDMAC_3_PRPENC_WIDTH_OFFSET;
+
+       } else if (dma_chan == 45) {    /* PRP ENC Rot input */
+               ic_idmac_1 &= ~IC_IDMAC_1_PRPENC_ROT_MASK;
+               ic_idmac_1 |= temp_rot << IC_IDMAC_1_PRPENC_ROT_OFFSET;
+       }
+
+       if (dma_chan == 21) {   /* PRP VF output - CB1 */
+               if (burst_size == 16)
+                       ic_idmac_1 |= IC_IDMAC_1_CB1_BURST_16;
+               else
+                       ic_idmac_1 &= ~IC_IDMAC_1_CB1_BURST_16;
+
+               if (need_hor_flip)
+                       ic_idmac_1 |= IC_IDMAC_1_PRPVF_FLIP_RS;
+               else
+                       ic_idmac_1 &= ~IC_IDMAC_1_PRPVF_FLIP_RS;
+
+               ic_idmac_2 &= ~IC_IDMAC_2_PRPVF_HEIGHT_MASK;
+               ic_idmac_2 |= height << IC_IDMAC_2_PRPVF_HEIGHT_OFFSET;
+
+               ic_idmac_3 &= ~IC_IDMAC_3_PRPVF_WIDTH_MASK;
+               ic_idmac_3 |= width << IC_IDMAC_3_PRPVF_WIDTH_OFFSET;
+
+       } else if (dma_chan == 46) {    /* PRP VF Rot input */
+               ic_idmac_1 &= ~IC_IDMAC_1_PRPVF_ROT_MASK;
+               ic_idmac_1 |= temp_rot << IC_IDMAC_1_PRPVF_ROT_OFFSET;
+       }
+
+       if (dma_chan == 14) {   /* PRP VF graphics combining input - CB3 */
+               if (burst_size == 16)
+                       ic_idmac_1 |= IC_IDMAC_1_CB3_BURST_16;
+               else
+                       ic_idmac_1 &= ~IC_IDMAC_1_CB3_BURST_16;
+       } else if (dma_chan == 15) {    /* PP graphics combining input - CB4 */
+               if (burst_size == 16)
+                       ic_idmac_1 |= IC_IDMAC_1_CB4_BURST_16;
+               else
+                       ic_idmac_1 &= ~IC_IDMAC_1_CB4_BURST_16;
+       }
+
+       __raw_writel(ic_idmac_1, IC_IDMAC_1);
+       __raw_writel(ic_idmac_2, IC_IDMAC_2);
+       __raw_writel(ic_idmac_3, IC_IDMAC_3);
+
+       return 0;
+}
+
+static void _init_csc(uint8_t ic_task, ipu_color_space_t in_format,
+                     ipu_color_space_t out_format, int csc_index)
+{
+
+/*     Y = R *  .299 + G *  .587 + B *  .114;
+       U = R * -.169 + G * -.332 + B *  .500 + 128.;
+       V = R *  .500 + G * -.419 + B * -.0813 + 128.;*/
+       static const uint32_t rgb2ycbcr_coeff[4][3] = {
+               {0x004D, 0x0096, 0x001D},
+               {0x01D5, 0x01AB, 0x0080},
+               {0x0080, 0x0195, 0x01EB},
+               {0x0000, 0x0200, 0x0200},       /* A0, A1, A2 */
+       };
+
+       /* transparent RGB->RGB matrix for combining
+        */
+       static const uint32_t rgb2rgb_coeff[4][3] = {
+               {0x0080, 0x0000, 0x0000},
+               {0x0000, 0x0080, 0x0000},
+               {0x0000, 0x0000, 0x0080},
+               {0x0000, 0x0000, 0x0000},       /* A0, A1, A2 */
+       };
+
+/*     R = (1.164 * (Y - 16)) + (1.596 * (Cr - 128));
+       G = (1.164 * (Y - 16)) - (0.392 * (Cb - 128)) - (0.813 * (Cr - 128));
+       B = (1.164 * (Y - 16)) + (2.017 * (Cb - 128); */
+       static const uint32_t ycbcr2rgb_coeff[4][3] = {
+               {149, 0, 204},
+               {149, 462, 408},
+               {149, 255, 0},
+               {8192 - 446, 266, 8192 - 554},  /* A0, A1, A2 */
+       };
+
+       uint32_t param;
+       uint32_t *base = NULL;
+
+       if (ic_task == IC_TASK_ENCODER) {
+               base = ipu_tpmem_base + 0x2008 / 4;
+       } else if (ic_task == IC_TASK_VIEWFINDER) {
+               if (csc_index == 1)
+                       base = ipu_tpmem_base + 0x4028 / 4;
+               else
+                       base = ipu_tpmem_base + 0x4040 / 4;
+       } else if (ic_task == IC_TASK_POST_PROCESSOR) {
+               if (csc_index == 1)
+                       base = ipu_tpmem_base + 0x6060 / 4;
+               else
+                       base = ipu_tpmem_base + 0x6078 / 4;
+       } else {
+               BUG();
+       }
+
+       if ((in_format == YCbCr) && (out_format == RGB)) {
+               /* Init CSC (YCbCr->RGB) */
+               param = (ycbcr2rgb_coeff[3][0] << 27) |
+                       (ycbcr2rgb_coeff[0][0] << 18) |
+                       (ycbcr2rgb_coeff[1][1] << 9) | ycbcr2rgb_coeff[2][2];
+               __raw_writel(param, base++);
+               /* scale = 2, sat = 0 */
+               param = (ycbcr2rgb_coeff[3][0] >> 5) | (2L << (40 - 32));
+               __raw_writel(param, base++);
+
+               param = (ycbcr2rgb_coeff[3][1] << 27) |
+                       (ycbcr2rgb_coeff[0][1] << 18) |
+                       (ycbcr2rgb_coeff[1][0] << 9) | ycbcr2rgb_coeff[2][0];
+               __raw_writel(param, base++);
+               param = (ycbcr2rgb_coeff[3][1] >> 5);
+               __raw_writel(param, base++);
+
+               param = (ycbcr2rgb_coeff[3][2] << 27) |
+                       (ycbcr2rgb_coeff[0][2] << 18) |
+                       (ycbcr2rgb_coeff[1][2] << 9) | ycbcr2rgb_coeff[2][1];
+               __raw_writel(param, base++);
+               param = (ycbcr2rgb_coeff[3][2] >> 5);
+               __raw_writel(param, base++);
+       } else if ((in_format == RGB) && (out_format == YCbCr)) {
+               /* Init CSC (RGB->YCbCr) */
+               param = (rgb2ycbcr_coeff[3][0] << 27) |
+                       (rgb2ycbcr_coeff[0][0] << 18) |
+                       (rgb2ycbcr_coeff[1][1] << 9) | rgb2ycbcr_coeff[2][2];
+               __raw_writel(param, base++);
+               /* scale = 1, sat = 0 */
+               param = (rgb2ycbcr_coeff[3][0] >> 5) | (1UL << 8);
+               __raw_writel(param, base++);
+
+               param = (rgb2ycbcr_coeff[3][1] << 27) |
+                       (rgb2ycbcr_coeff[0][1] << 18) |
+                       (rgb2ycbcr_coeff[1][0] << 9) | rgb2ycbcr_coeff[2][0];
+               __raw_writel(param, base++);
+               param = (rgb2ycbcr_coeff[3][1] >> 5);
+               __raw_writel(param, base++);
+
+               param = (rgb2ycbcr_coeff[3][2] << 27) |
+                       (rgb2ycbcr_coeff[0][2] << 18) |
+                       (rgb2ycbcr_coeff[1][2] << 9) | rgb2ycbcr_coeff[2][1];
+               __raw_writel(param, base++);
+               param = (rgb2ycbcr_coeff[3][2] >> 5);
+               __raw_writel(param, base++);
+       } else if ((in_format == RGB) && (out_format == RGB)) {
+               /* Init CSC */
+               param =
+                   (rgb2rgb_coeff[3][0] << 27) | (rgb2rgb_coeff[0][0] << 18) |
+                   (rgb2rgb_coeff[1][1] << 9) | rgb2rgb_coeff[2][2];
+               __raw_writel(param, base++);
+               /* scale = 2, sat = 0 */
+               param = (rgb2rgb_coeff[3][0] >> 5) | (2UL << 8);
+               __raw_writel(param, base++);
+
+               param =
+                   (rgb2rgb_coeff[3][1] << 27) | (rgb2rgb_coeff[0][1] << 18) |
+                   (rgb2rgb_coeff[1][0] << 9) | rgb2rgb_coeff[2][0];
+               __raw_writel(param, base++);
+               param = (rgb2rgb_coeff[3][1] >> 5);
+               __raw_writel(param, base++);
+
+               param =
+                   (rgb2rgb_coeff[3][2] << 27) | (rgb2rgb_coeff[0][2] << 18) |
+                   (rgb2rgb_coeff[1][2] << 9) | rgb2rgb_coeff[2][1];
+               __raw_writel(param, base++);
+               param = (rgb2rgb_coeff[3][2] >> 5);
+               __raw_writel(param, base++);
+       } else {
+               dev_err(g_ipu_dev, "Unsupported color space conversion\n");
+       }
+}
+
+static bool _calc_resize_coeffs(uint32_t inSize, uint32_t outSize,
+                               uint32_t *resizeCoeff,
+                               uint32_t *downsizeCoeff)
+{
+       uint32_t tempSize;
+       uint32_t tempDownsize;
+
+       /* Input size cannot be more than 4096 */
+       /* Output size cannot be more than 1024 */
+       if ((inSize > 4096) || (outSize > 1024))
+               return false;
+
+       /* Cannot downsize more than 8:1 */
+       if ((outSize << 3) < inSize)
+               return false;
+
+       /* Compute downsizing coefficient */
+       /* Output of downsizing unit cannot be more than 1024 */
+       tempDownsize = 0;
+       tempSize = inSize;
+       while (((tempSize > 1024) || (tempSize >= outSize * 2)) &&
+              (tempDownsize < 2)) {
+               tempSize >>= 1;
+               tempDownsize++;
+       }
+       *downsizeCoeff = tempDownsize;
+
+       /* compute resizing coefficient using the following equation:
+          resizeCoeff = M*(SI -1)/(SO - 1)
+          where M = 2^13, SI - input size, SO - output size    */
+       *resizeCoeff = (8192L * (tempSize - 1)) / (outSize - 1);
+       if (*resizeCoeff >= 16384L) {
+               dev_err(g_ipu_dev, "Warning! Overflow on resize coeff.\n");
+               *resizeCoeff = 0x3FFF;
+       }
+
+       dev_dbg(g_ipu_dev, "resizing from %u -> %u pixels, "
+               "downsize=%u, resize=%u.%lu (reg=%u)\n", inSize, outSize,
+               *downsizeCoeff, (*resizeCoeff >= 8192L) ? 1 : 0,
+               ((*resizeCoeff & 0x1FFF) * 10000L) / 8192L, *resizeCoeff);
+
+       return true;
+}
+
+void _ipu_vdi_toggle_top_field_man()
+{
+       uint32_t reg;
+       uint32_t mask_reg;
+
+       reg = __raw_readl(VDI_C);
+       mask_reg = reg & VDI_C_TOP_FIELD_MAN_1;
+       if (mask_reg == VDI_C_TOP_FIELD_MAN_1)
+               reg &= ~VDI_C_TOP_FIELD_MAN_1;
+       else
+               reg |= VDI_C_TOP_FIELD_MAN_1;
+
+       __raw_writel(reg, VDI_C);
+}
+
diff --git a/drivers/mxc/ipu3/ipu_param_mem.h b/drivers/mxc/ipu3/ipu_param_mem.h
new file mode 100644 (file)
index 0000000..00ce4ea
--- /dev/null
@@ -0,0 +1,604 @@
+/*
+ * Copyright 2005-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
+ */
+#ifndef __INCLUDE_IPU_PARAM_MEM_H__
+#define __INCLUDE_IPU_PARAM_MEM_H__
+
+#include <linux/types.h>
+#include <linux/bitrev.h>
+
+extern u32 *ipu_cpmem_base;
+
+struct ipu_ch_param_word {
+       uint32_t data[5];
+       uint32_t res[3];
+};
+
+struct ipu_ch_param {
+       struct ipu_ch_param_word word[2];
+};
+
+#define ipu_ch_param_addr(ch) (((struct ipu_ch_param *)ipu_cpmem_base) + (ch))
+
+#define _param_word(base, w) \
+       (((struct ipu_ch_param *)(base))->word[(w)].data)
+
+#define ipu_ch_param_set_field(base, w, bit, size, v) { \
+       int i = (bit) / 32; \
+       int off = (bit) % 32; \
+       _param_word(base, w)[i] |= (v) << off; \
+       if (((bit)+(size)-1)/32 > i) { \
+               _param_word(base, w)[i + 1] |= (v) >> (off ? (32 - off) : 0); \
+       } \
+}
+
+#define ipu_ch_param_mod_field(base, w, bit, size, v) { \
+       int i = (bit) / 32; \
+       int off = (bit) % 32; \
+       u32 mask = (1UL << size) - 1; \
+       u32 temp = _param_word(base, w)[i]; \
+       temp &= ~(mask << off); \
+       _param_word(base, w)[i] = temp | (v) << off; \
+       if (((bit)+(size)-1)/32 > i) { \
+               temp = _param_word(base, w)[i + 1]; \
+               temp &= ~(mask >> (32 - off)); \
+               _param_word(base, w)[i + 1] = \
+                       temp | ((v) >> (off ? (32 - off) : 0)); \
+       } \
+}
+
+#define ipu_ch_param_read_field(base, w, bit, size) ({ \
+       u32 temp2; \
+       int i = (bit) / 32; \
+       int off = (bit) % 32; \
+       u32 mask = (1UL << size) - 1; \
+       u32 temp1 = _param_word(base, w)[i]; \
+       temp1 = mask & (temp1 >> off); \
+       if (((bit)+(size)-1)/32 > i) { \
+               temp2 = _param_word(base, w)[i + 1]; \
+               temp2 &= mask >> (off ? (32 - off) : 0); \
+               temp1 |= temp2 << (off ? (32 - off) : 0); \
+       } \
+       temp1; \
+})
+
+static inline void _ipu_ch_params_set_packing(struct ipu_ch_param *p,
+                                             int red_width, int red_offset,
+                                             int green_width, int green_offset,
+                                             int blue_width, int blue_offset,
+                                             int alpha_width, int alpha_offset)
+{
+       /* Setup red width and offset */
+       ipu_ch_param_set_field(p, 1, 116, 3, red_width - 1);
+       ipu_ch_param_set_field(p, 1, 128, 5, red_offset);
+       /* Setup green width and offset */
+       ipu_ch_param_set_field(p, 1, 119, 3, green_width - 1);
+       ipu_ch_param_set_field(p, 1, 133, 5, green_offset);
+       /* Setup blue width and offset */
+       ipu_ch_param_set_field(p, 1, 122, 3, blue_width - 1);
+       ipu_ch_param_set_field(p, 1, 138, 5, blue_offset);
+       /* Setup alpha width and offset */
+       ipu_ch_param_set_field(p, 1, 125, 3, alpha_width - 1);
+       ipu_ch_param_set_field(p, 1, 143, 5, alpha_offset);
+}
+
+static inline void _ipu_ch_param_dump(int ch)
+{
+       struct ipu_ch_param *p = ipu_ch_param_addr(ch);
+       pr_debug("ch %d word 0 - %08X %08X %08X %08X %08X\n", ch,
+                p->word[0].data[0], p->word[0].data[1], p->word[0].data[2],
+                p->word[0].data[3], p->word[0].data[4]);
+       pr_debug("ch %d word 1 - %08X %08X %08X %08X %08X\n", ch,
+                p->word[1].data[0], p->word[1].data[1], p->word[1].data[2],
+                p->word[1].data[3], p->word[1].data[4]);
+       pr_debug("PFS 0x%x, ",
+                ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 85, 4));
+       pr_debug("BPP 0x%x, ",
+                ipu_ch_param_read_field(ipu_ch_param_addr(ch), 0, 107, 3));
+       pr_debug("NPB 0x%x\n",
+                ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 78, 7));
+
+       pr_debug("FW %d, ",
+                ipu_ch_param_read_field(ipu_ch_param_addr(ch), 0, 125, 13));
+       pr_debug("FH %d, ",
+                ipu_ch_param_read_field(ipu_ch_param_addr(ch), 0, 138, 12));
+       pr_debug("Stride %d\n",
+                ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 102, 14));
+
+       pr_debug("Width0 %d+1, ",
+                ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 116, 3));
+       pr_debug("Width1 %d+1, ",
+                ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 119, 3));
+       pr_debug("Width2 %d+1, ",
+                ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 122, 3));
+       pr_debug("Width3 %d+1, ",
+                ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 125, 3));
+       pr_debug("Offset0 %d, ",
+                ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 128, 5));
+       pr_debug("Offset1 %d, ",
+                ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 133, 5));
+       pr_debug("Offset2 %d, ",
+                ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 138, 5));
+       pr_debug("Offset3 %d\n",
+                ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 143, 5));
+}
+
+static inline void _ipu_ch_param_init(int ch,
+                                     uint32_t pixel_fmt, uint32_t width,
+                                     uint32_t height, uint32_t stride,
+                                     uint32_t u, uint32_t v,
+                                     uint32_t uv_stride, dma_addr_t addr0,
+                                     dma_addr_t addr1)
+{
+       uint32_t u_offset = 0;
+       uint32_t v_offset = 0;
+       struct ipu_ch_param params;
+
+       memset(&params, 0, sizeof(params));
+
+       ipu_ch_param_set_field(&params, 0, 125, 13, width - 1);
+
+       if ((ch == 8) || (ch == 9) || (ch == 10)) {
+               ipu_ch_param_set_field(&params, 0, 138, 12, (height / 2) - 1);
+               ipu_ch_param_set_field(&params, 1, 102, 14, (stride * 2) - 1);
+       } else {
+               ipu_ch_param_set_field(&params, 0, 138, 12, height - 1);
+               ipu_ch_param_set_field(&params, 1, 102, 14, stride - 1);
+       }
+
+       /* EBA is 8-byte aligned */
+       ipu_ch_param_set_field(&params, 1, 0, 29, addr0 >> 3);
+       ipu_ch_param_set_field(&params, 1, 29, 29, addr1 >> 3);
+       if (addr0%8)
+               dev_warn(g_ipu_dev,
+                        "IDMAC%d's EBA0 is not 8-byte aligned\n", ch);
+       if (addr1%8)
+               dev_warn(g_ipu_dev,
+                        "IDMAC%d's EBA1 is not 8-byte aligned\n", ch);
+
+       switch (pixel_fmt) {
+       case IPU_PIX_FMT_GENERIC:
+               /*Represents 8-bit Generic data */
+               ipu_ch_param_set_field(&params, 0, 107, 3, 5);  /* bits/pixel */
+               ipu_ch_param_set_field(&params, 1, 85, 4, 6);   /* pix format */
+               ipu_ch_param_set_field(&params, 1, 78, 7, 63);  /* burst size */
+
+               break;
+       case IPU_PIX_FMT_GENERIC_32:
+               /*Represents 32-bit Generic data */
+               break;
+       case IPU_PIX_FMT_RGB565:
+               ipu_ch_param_set_field(&params, 0, 107, 3, 3);  /* bits/pixel */
+               ipu_ch_param_set_field(&params, 1, 85, 4, 7);   /* pix format */
+               ipu_ch_param_set_field(&params, 1, 78, 7, 15);  /* burst size */
+
+               _ipu_ch_params_set_packing(&params, 5, 0, 6, 5, 5, 11, 8, 16);
+               break;
+       case IPU_PIX_FMT_BGR24:
+               ipu_ch_param_set_field(&params, 0, 107, 3, 1);  /* bits/pixel */
+               ipu_ch_param_set_field(&params, 1, 85, 4, 7);   /* pix format */
+               ipu_ch_param_set_field(&params, 1, 78, 7, 19);  /* burst size */
+
+               _ipu_ch_params_set_packing(&params, 8, 0, 8, 8, 8, 16, 8, 24);
+               break;
+       case IPU_PIX_FMT_RGB24:
+       case IPU_PIX_FMT_YUV444:
+               ipu_ch_param_set_field(&params, 0, 107, 3, 1);  /* bits/pixel */
+               ipu_ch_param_set_field(&params, 1, 85, 4, 7);   /* pix format */
+               ipu_ch_param_set_field(&params, 1, 78, 7, 19);  /* burst size */
+
+               _ipu_ch_params_set_packing(&params, 8, 16, 8, 8, 8, 0, 8, 24);
+               break;
+       case IPU_PIX_FMT_BGRA32:
+       case IPU_PIX_FMT_BGR32:
+               ipu_ch_param_set_field(&params, 0, 107, 3, 0);  /* bits/pixel */
+               ipu_ch_param_set_field(&params, 1, 85, 4, 7);   /* pix format */
+               ipu_ch_param_set_field(&params, 1, 78, 7, 15);  /* burst size */
+
+               _ipu_ch_params_set_packing(&params, 8, 8, 8, 16, 8, 24, 8, 0);
+               break;
+       case IPU_PIX_FMT_RGBA32:
+       case IPU_PIX_FMT_RGB32:
+               ipu_ch_param_set_field(&params, 0, 107, 3, 0);  /* bits/pixel */
+               ipu_ch_param_set_field(&params, 1, 85, 4, 7);   /* pix format */
+               ipu_ch_param_set_field(&params, 1, 78, 7, 15);  /* burst size */
+
+               _ipu_ch_params_set_packing(&params, 8, 24, 8, 16, 8, 8, 8, 0);
+               break;
+       case IPU_PIX_FMT_ABGR32:
+               ipu_ch_param_set_field(&params, 0, 107, 3, 0);  /* bits/pixel */
+               ipu_ch_param_set_field(&params, 1, 85, 4, 7);   /* pix format */
+               ipu_ch_param_set_field(&params, 1, 78, 7, 15);  /* burst size */
+
+               _ipu_ch_params_set_packing(&params, 8, 0, 8, 8, 8, 16, 8, 24);
+               break;
+       case IPU_PIX_FMT_UYVY:
+               ipu_ch_param_set_field(&params, 0, 107, 3, 3);  /* bits/pixel */
+               ipu_ch_param_set_field(&params, 1, 85, 4, 0xA); /* pix format */
+               if ((ch == 8) || (ch == 9) || (ch == 10)) {
+                       ipu_ch_param_set_field(&params, 1, 78, 7, 15);  /* burst size */
+               } else {
+                       ipu_ch_param_set_field(&params, 1, 78, 7, 31);  /* burst size */
+               }
+               break;
+       case IPU_PIX_FMT_YUYV:
+               ipu_ch_param_set_field(&params, 0, 107, 3, 3);  /* bits/pixel */
+               ipu_ch_param_set_field(&params, 1, 85, 4, 0x8); /* pix format */
+               if ((ch == 8) || (ch == 9) || (ch == 10)) {
+                       ipu_ch_param_set_field(&params, 1, 78, 7, 15);  /* burst size */
+               } else {
+                       ipu_ch_param_set_field(&params, 1, 78, 7, 31);  /* burst size */
+               }
+               break;
+       case IPU_PIX_FMT_YUV420P2:
+       case IPU_PIX_FMT_YUV420P:
+               ipu_ch_param_set_field(&params, 1, 85, 4, 2);   /* pix format */
+
+               if (uv_stride < stride / 2)
+                       uv_stride = stride / 2;
+
+               u_offset = stride * height;
+               v_offset = u_offset + (uv_stride * height / 2);
+               if ((ch == 8) || (ch == 9) || (ch == 10)) {
+                       ipu_ch_param_set_field(&params, 1, 78, 7, 15);  /* burst size */
+                       uv_stride = uv_stride*2;
+               } else {
+                       ipu_ch_param_set_field(&params, 1, 78, 7, 31);  /* burst size */
+               }
+               break;
+       case IPU_PIX_FMT_YVU420P:
+               ipu_ch_param_set_field(&params, 1, 85, 4, 2);   /* pix format */
+
+               if (uv_stride < stride / 2)
+                       uv_stride = stride / 2;
+
+               v_offset = stride * height;
+               u_offset = v_offset + (uv_stride * height / 2);
+               if ((ch == 8) || (ch == 9) || (ch == 10)) {
+                       ipu_ch_param_set_field(&params, 1, 78, 7, 15);  /* burst size */
+                       uv_stride = uv_stride*2;
+               } else {
+                       ipu_ch_param_set_field(&params, 1, 78, 7, 31);  /* burst size */
+               }
+               break;
+       case IPU_PIX_FMT_YVU422P:
+               /* BPP & pixel format */
+               ipu_ch_param_set_field(&params, 1, 85, 4, 1);   /* pix format */
+               ipu_ch_param_set_field(&params, 1, 78, 7, 31);  /* burst size */
+
+               if (uv_stride < stride / 2)
+                       uv_stride = stride / 2;
+
+               v_offset = (v == 0) ? stride * height : v;
+               u_offset = (u == 0) ? v_offset + v_offset / 2 : u;
+               break;
+       case IPU_PIX_FMT_YUV422P:
+               /* BPP & pixel format */
+               ipu_ch_param_set_field(&params, 1, 85, 4, 1);   /* pix format */
+               ipu_ch_param_set_field(&params, 1, 78, 7, 31);  /* burst size */
+
+               if (uv_stride < stride / 2)
+                       uv_stride = stride / 2;
+
+               u_offset = (u == 0) ? stride * height : u;
+               v_offset = (v == 0) ? u_offset + u_offset / 2 : v;
+               break;
+       case IPU_PIX_FMT_NV12:
+               /* BPP & pixel format */
+               ipu_ch_param_set_field(&params, 1, 85, 4, 4);   /* pix format */
+               uv_stride = stride;
+               u_offset = (u == 0) ? stride * height : u;
+               if ((ch == 8) || (ch == 9) || (ch == 10)) {
+                       ipu_ch_param_set_field(&params, 1, 78, 7, 15);  /* burst size */
+                       uv_stride = uv_stride*2;
+               } else {
+                       ipu_ch_param_set_field(&params, 1, 78, 7, 31);  /* burst size */
+               }
+               break;
+       default:
+               dev_err(g_ipu_dev, "mxc ipu: unimplemented pixel format\n");
+               break;
+       }
+       /*set burst size to 16*/
+
+
+       if (uv_stride)
+               ipu_ch_param_set_field(&params, 1, 128, 14, uv_stride - 1);
+
+       /* Get the uv offset from user when need cropping */
+       if (u || v) {
+               u_offset = u;
+               v_offset = v;
+       }
+
+       /* UBO and VBO are 22-bit and 8-byte aligned */
+       if (u_offset/8 > 0x3fffff)
+               dev_warn(g_ipu_dev,
+                        "IDMAC%d's U offset exceeds IPU limitation\n", ch);
+       if (v_offset/8 > 0x3fffff)
+               dev_warn(g_ipu_dev,
+                        "IDMAC%d's V offset exceeds IPU limitation\n", ch);
+       if (u_offset%8)
+               dev_warn(g_ipu_dev,
+                        "IDMAC%d's U offset is not 8-byte aligned\n", ch);
+       if (v_offset%8)
+               dev_warn(g_ipu_dev,
+                        "IDMAC%d's V offset is not 8-byte aligned\n", ch);
+
+       ipu_ch_param_set_field(&params, 0, 46, 22, u_offset / 8);
+       ipu_ch_param_set_field(&params, 0, 68, 22, v_offset / 8);
+
+       pr_debug("initializing idma ch %d @ %p\n", ch, ipu_ch_param_addr(ch));
+       memcpy(ipu_ch_param_addr(ch), &params, sizeof(params));
+};
+
+static inline void _ipu_ch_param_set_burst_size(uint32_t ch,
+                                               uint16_t burst_pixels)
+{
+       ipu_ch_param_mod_field(ipu_ch_param_addr(ch), 1, 78, 7,
+                              burst_pixels - 1);
+};
+
+static inline int _ipu_ch_param_get_burst_size(uint32_t ch)
+{
+       return ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 78, 7) + 1;
+};
+
+static inline int _ipu_ch_param_get_bpp(uint32_t ch)
+{
+       return ipu_ch_param_read_field(ipu_ch_param_addr(ch), 0, 107, 3);
+};
+
+static inline void _ipu_ch_param_set_buffer(uint32_t ch, int bufNum,
+                                           dma_addr_t phyaddr)
+{
+       ipu_ch_param_mod_field(ipu_ch_param_addr(ch), 1, 29 * bufNum, 29,
+                              phyaddr / 8);
+};
+
+static inline void _ipu_ch_param_set_rotation(uint32_t ch,
+                                             ipu_rotate_mode_t rot)
+{
+       u32 temp_rot = bitrev8(rot) >> 5;
+       ipu_ch_param_mod_field(ipu_ch_param_addr(ch), 0, 119, 3, temp_rot);
+};
+
+static inline void _ipu_ch_param_set_block_mode(uint32_t ch)
+{
+       ipu_ch_param_mod_field(ipu_ch_param_addr(ch), 0, 117, 2, 1);
+};
+
+static inline void _ipu_ch_param_set_alpha_use_separate_channel(uint32_t ch,
+                                                               bool option)
+{
+       if (option) {
+               ipu_ch_param_mod_field(ipu_ch_param_addr(ch), 1, 89, 1, 1);
+       } else {
+               ipu_ch_param_mod_field(ipu_ch_param_addr(ch), 1, 89, 1, 0);
+       }
+};
+
+static inline void _ipu_ch_param_set_alpha_condition_read(uint32_t ch)
+{
+       ipu_ch_param_mod_field(ipu_ch_param_addr(ch), 1, 149, 1, 1);
+};
+
+static inline void _ipu_ch_param_set_alpha_buffer_memory(uint32_t ch)
+{
+       int alp_mem_idx;
+
+       switch (ch) {
+       case 14: /* PRP graphic */
+               alp_mem_idx = 0;
+               break;
+       case 15: /* PP graphic */
+               alp_mem_idx = 1;
+               break;
+       case 23: /* DP BG SYNC graphic */
+               alp_mem_idx = 4;
+               break;
+       case 27: /* DP FG SYNC graphic */
+               alp_mem_idx = 2;
+               break;
+       default:
+               dev_err(g_ipu_dev, "unsupported correlative channel of local "
+                       "alpha channel\n");
+               return;
+       }
+
+       ipu_ch_param_mod_field(ipu_ch_param_addr(ch), 1, 90, 3, alp_mem_idx);
+};
+
+static inline void _ipu_ch_param_set_interlaced_scan(uint32_t ch)
+{
+       u32 stride;
+       ipu_ch_param_set_field(ipu_ch_param_addr(ch), 0, 113, 1, 1);
+       stride = ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 102, 14) + 1;
+       /* ILO is 20-bit and 8-byte aligned */
+       if (stride/8 > 0xfffff)
+               dev_warn(g_ipu_dev,
+                        "IDMAC%d's ILO exceeds IPU limitation\n", ch);
+       if (stride%8)
+               dev_warn(g_ipu_dev,
+                        "IDMAC%d's ILO is not 8-byte aligned\n", ch);
+       ipu_ch_param_mod_field(ipu_ch_param_addr(ch), 1, 58, 20, stride / 8);
+       stride *= 2;
+       ipu_ch_param_mod_field(ipu_ch_param_addr(ch), 1, 102, 14, stride - 1);
+};
+
+static inline void _ipu_ch_param_set_high_priority(uint32_t ch)
+{
+       ipu_ch_param_mod_field(ipu_ch_param_addr(ch), 1, 93, 2, 1);
+};
+
+/* IDMAC U/V offset changing support */
+/* U and V input is not affected, */
+/* the update is done by new calculation according to */
+/* vertical_offset and horizontal_offset */
+static inline void _ipu_ch_offset_update(int ch,
+                                       uint32_t pixel_fmt,
+                                       uint32_t width,
+                                       uint32_t height,
+                                       uint32_t stride,
+                                       uint32_t u,
+                                       uint32_t v,
+                                       uint32_t uv_stride,
+                                       uint32_t vertical_offset,
+                                       uint32_t horizontal_offset)
+{
+       uint32_t u_offset = 0;
+       uint32_t v_offset = 0;
+       uint32_t u_fix = 0;
+       uint32_t v_fix = 0;
+
+       switch (pixel_fmt) {
+       case IPU_PIX_FMT_GENERIC:
+       case IPU_PIX_FMT_GENERIC_32:
+       case IPU_PIX_FMT_RGB565:
+       case IPU_PIX_FMT_BGR24:
+       case IPU_PIX_FMT_RGB24:
+       case IPU_PIX_FMT_YUV444:
+       case IPU_PIX_FMT_BGRA32:
+       case IPU_PIX_FMT_BGR32:
+       case IPU_PIX_FMT_RGBA32:
+       case IPU_PIX_FMT_RGB32:
+       case IPU_PIX_FMT_ABGR32:
+       case IPU_PIX_FMT_UYVY:
+       case IPU_PIX_FMT_YUYV:
+               break;
+
+       case IPU_PIX_FMT_YUV420P2:
+       case IPU_PIX_FMT_YUV420P:
+               if (uv_stride < stride / 2)
+                       uv_stride = stride / 2;
+
+               u_offset = stride * (height - vertical_offset - 1) +
+                                       (stride - horizontal_offset) +
+                                       (uv_stride * vertical_offset / 2) +
+                                       horizontal_offset / 2;
+               v_offset = u_offset + (uv_stride * height / 2);
+               u_fix = u ? (u + (uv_stride * vertical_offset / 2) +
+                                       (horizontal_offset / 2) -
+                                       (stride * vertical_offset) - (horizontal_offset)) :
+                                       u_offset;
+               v_fix = v ? (v + (uv_stride * vertical_offset / 2) +
+                                       (horizontal_offset / 2) -
+                                       (stride * vertical_offset) - (horizontal_offset)) :
+                                       v_offset;
+
+               break;
+       case IPU_PIX_FMT_YVU420P:
+               if (uv_stride < stride / 2)
+                       uv_stride = stride / 2;
+
+               v_offset = stride * (height - vertical_offset - 1) +
+                                       (stride - horizontal_offset) +
+                                       (uv_stride * vertical_offset / 2) +
+                                       horizontal_offset / 2;
+               u_offset = v_offset + (uv_stride * height / 2);
+               u_fix = u ? (u + (uv_stride * vertical_offset / 2) +
+                                       (horizontal_offset / 2) -
+                                       (stride * vertical_offset) - (horizontal_offset)) :
+                                       u_offset;
+               v_fix = v ? (v + (uv_stride * vertical_offset / 2) +
+                                       (horizontal_offset / 2) -
+                                       (stride * vertical_offset) - (horizontal_offset)) :
+                                       v_offset;
+
+               break;
+       case IPU_PIX_FMT_YVU422P:
+               if (uv_stride < stride / 2)
+                       uv_stride = stride / 2;
+
+               v_offset = stride * (height - vertical_offset - 1) +
+                                       (stride - horizontal_offset) +
+                                       (uv_stride * vertical_offset) +
+                                       horizontal_offset / 2;
+               u_offset = v_offset + uv_stride * height;
+               u_fix = u ? (u + (uv_stride * vertical_offset) +
+                                       horizontal_offset / 2 -
+                                       (stride * vertical_offset) - (horizontal_offset)) :
+                                       u_offset;
+               v_fix = v ? (v + (uv_stride * vertical_offset) +
+                                       horizontal_offset / 2 -
+                                       (stride * vertical_offset) - (horizontal_offset)) :
+                                       v_offset;
+               break;
+       case IPU_PIX_FMT_YUV422P:
+               if (uv_stride < stride / 2)
+                       uv_stride = stride / 2;
+
+               u_offset = stride * (height - vertical_offset - 1) +
+                                       (stride - horizontal_offset) +
+                                       (uv_stride * vertical_offset) +
+                                       horizontal_offset / 2;
+               v_offset = u_offset + uv_stride * height;
+               u_fix = u ? (u + (uv_stride * vertical_offset) +
+                                       horizontal_offset / 2 -
+                                       (stride * vertical_offset) - (horizontal_offset)) :
+                                       u_offset;
+               v_fix = v ? (v + (uv_stride * vertical_offset) +
+                                       horizontal_offset / 2 -
+                                       (stride * vertical_offset) - (horizontal_offset)) :
+                                       v_offset;
+               break;
+
+       case IPU_PIX_FMT_NV12:
+               uv_stride = stride;
+               u_offset = stride * (height - vertical_offset - 1) +
+                                       (stride - horizontal_offset) +
+                                       (uv_stride * vertical_offset / 2) +
+                                       horizontal_offset;
+               u_fix = u ? (u + (uv_stride * vertical_offset / 2) +
+                                       horizontal_offset -
+                                       (stride * vertical_offset) - (horizontal_offset)) :
+                                       u_offset;
+
+               break;
+       default:
+               dev_err(g_ipu_dev, "mxc ipu: unimplemented pixel format\n");
+               break;
+       }
+
+
+
+       if (u_fix > u_offset)
+               u_offset = u_fix;
+
+       if (v_fix > v_offset)
+               v_offset = v_fix;
+
+       /* UBO and VBO are 22-bit and 8-byte aligned */
+       if (u_offset/8 > 0x3fffff)
+               dev_warn(g_ipu_dev,
+                       "IDMAC%d's U offset exceeds IPU limitation\n", ch);
+       if (v_offset/8 > 0x3fffff)
+               dev_warn(g_ipu_dev,
+                       "IDMAC%d's V offset exceeds IPU limitation\n", ch);
+       if (u_offset%8)
+               dev_warn(g_ipu_dev,
+                       "IDMAC%d's U offset is not 8-byte aligned\n", ch);
+       if (v_offset%8)
+               dev_warn(g_ipu_dev,
+                       "IDMAC%d's V offset is not 8-byte aligned\n", ch);
+
+       ipu_ch_param_mod_field(ipu_ch_param_addr(ch), 0, 46, 22, u_offset / 8);
+       ipu_ch_param_mod_field(ipu_ch_param_addr(ch), 0, 68, 22, v_offset / 8);
+
+};
+
+static inline void _ipu_ch_params_set_alpha_width(uint32_t ch, int alpha_width)
+{
+       ipu_ch_param_set_field(ipu_ch_param_addr(ch), 1, 125, 3, alpha_width - 1);
+};
+
+#endif
diff --git a/drivers/mxc/ipu3/ipu_prv.h b/drivers/mxc/ipu3/ipu_prv.h
new file mode 100644 (file)
index 0000000..794163a
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2005-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
+ */
+#ifndef __INCLUDE_IPU_PRV_H__
+#define __INCLUDE_IPU_PRV_H__
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/fsl_devices.h>
+
+/* Globals */
+extern struct device *g_ipu_dev;
+extern spinlock_t ipu_lock;
+extern bool g_ipu_clk_enabled;
+extern struct clk *g_ipu_clk;
+extern struct clk *g_di_clk[2];
+extern struct clk *g_pixel_clk[2];
+extern struct clk *g_csi_clk[2];
+extern unsigned char g_dc_di_assignment[];
+extern int g_ipu_hw_rev;
+extern int dmfc_type_setup;
+
+#define IDMA_CHAN_INVALID      0xFF
+#define HIGH_RESOLUTION_WIDTH  1024
+
+struct ipu_channel {
+       u8 video_in_dma;
+       u8 alpha_in_dma;
+       u8 graph_in_dma;
+       u8 out_dma;
+};
+
+enum ipu_dmfc_type {
+       DMFC_NORMAL = 0,
+       DMFC_HIGH_RESOLUTION_DC,
+       DMFC_HIGH_RESOLUTION_DP,
+       DMFC_HIGH_RESOLUTION_ONLY_DP,
+};
+
+int register_ipu_device(void);
+ipu_color_space_t format_to_colorspace(uint32_t fmt);
+bool ipu_pixel_format_has_alpha(uint32_t fmt);
+
+void ipu_dump_registers(void);
+
+uint32_t _ipu_channel_status(ipu_channel_t channel);
+
+void _ipu_init_dc_mappings(void);
+int _ipu_dp_init(ipu_channel_t channel, uint32_t in_pixel_fmt,
+                uint32_t out_pixel_fmt);
+void _ipu_dp_uninit(ipu_channel_t channel);
+void _ipu_dc_init(int dc_chan, int di, bool interlaced, uint32_t pixel_fmt);
+void _ipu_dc_uninit(int dc_chan);
+void _ipu_dp_dc_enable(ipu_channel_t channel);
+void _ipu_dp_dc_disable(ipu_channel_t channel, bool swap);
+void _ipu_dmfc_init(int dmfc_type, int first);
+void _ipu_dmfc_set_wait4eot(int dma_chan, int width);
+int _ipu_disp_chan_is_interlaced(ipu_channel_t channel);
+
+void _ipu_ic_enable_task(ipu_channel_t channel);
+void _ipu_ic_disable_task(ipu_channel_t channel);
+void _ipu_ic_init_prpvf(ipu_channel_params_t *params, bool src_is_csi);
+void _ipu_vdi_init(ipu_channel_t channel, ipu_channel_params_t *params);
+void _ipu_vdi_uninit(void);
+void _ipu_ic_uninit_prpvf(void);
+void _ipu_ic_init_rotate_vf(ipu_channel_params_t *params);
+void _ipu_ic_uninit_rotate_vf(void);
+void _ipu_ic_init_csi(ipu_channel_params_t *params);
+void _ipu_ic_uninit_csi(void);
+void _ipu_ic_init_prpenc(ipu_channel_params_t *params, bool src_is_csi);
+void _ipu_ic_uninit_prpenc(void);
+void _ipu_ic_init_rotate_enc(ipu_channel_params_t *params);
+void _ipu_ic_uninit_rotate_enc(void);
+void _ipu_ic_init_pp(ipu_channel_params_t *params);
+void _ipu_ic_uninit_pp(void);
+void _ipu_ic_init_rotate_pp(ipu_channel_params_t *params);
+void _ipu_ic_uninit_rotate_pp(void);
+int _ipu_ic_idma_init(int dma_chan, uint16_t width, uint16_t height,
+                     int burst_size, ipu_rotate_mode_t rot);
+void _ipu_vdi_toggle_top_field_man(void);
+int _ipu_csi_init(ipu_channel_t channel, uint32_t csi);
+void ipu_csi_set_test_generator(bool active, uint32_t r_value,
+               uint32_t g_value, uint32_t b_value,
+               uint32_t pix_clk, uint32_t csi);
+void _ipu_csi_ccir_err_detection_enable(uint32_t csi);
+void _ipu_csi_ccir_err_detection_disable(uint32_t csi);
+void _ipu_smfc_init(ipu_channel_t channel, uint32_t mipi_id, uint32_t csi);
+void _ipu_smfc_set_burst_size(ipu_channel_t channel, uint32_t bs);
+void _ipu_dp_set_csc_coefficients(ipu_channel_t channel, int32_t param[][3]);
+
+#endif                         /* __INCLUDE_IPU_PRV_H__ */
diff --git a/drivers/mxc/ipu3/ipu_regs.h b/drivers/mxc/ipu3/ipu_regs.h
new file mode 100644 (file)
index 0000000..b9392b7
--- /dev/null
@@ -0,0 +1,669 @@
+/*
+ * Copyright (C) 2005-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 ipu_regs.h
+ *
+ * @brief IPU Register definitions
+ *
+ * @ingroup IPU
+ */
+#ifndef __IPU_REGS_INCLUDED__
+#define __IPU_REGS_INCLUDED__
+
+#define IPU_DISP0_BASE         0x00000000
+#define IPU_MCU_T_DEFAULT      8
+#define IPU_DISP1_BASE         (IPU_MCU_T_DEFAULT << 25)
+#define IPU_REG_BASE           0x1E000000
+#define IPUV3M_REG_BASE                0x06000000
+
+#define IPU_CM_REG_BASE                0x00000000
+#define IPU_IDMAC_REG_BASE     0x00008000
+#define IPU_ISP_REG_BASE       0x00010000
+#define IPU_DP_REG_BASE                0x00018000
+#define IPU_IC_REG_BASE                0x00020000
+#define IPU_IRT_REG_BASE       0x00028000
+#define IPU_CSI0_REG_BASE      0x00030000
+#define IPU_CSI1_REG_BASE      0x00038000
+#define IPU_DI0_REG_BASE       0x00040000
+#define IPU_DI1_REG_BASE       0x00048000
+#define IPU_SMFC_REG_BASE      0x00050000
+#define IPU_DC_REG_BASE                0x00058000
+#define IPU_DMFC_REG_BASE      0x00060000
+#define IPU_VDI_REG_BASE       0x00068000
+#define IPU_CPMEM_REG_BASE     0x01000000
+#define IPU_LUT_REG_BASE       0x01020000
+#define IPU_SRM_REG_BASE       0x01040000
+#define IPU_TPM_REG_BASE       0x01060000
+#define IPU_DC_TMPL_REG_BASE   0x01080000
+#define IPU_ISP_TBPR_REG_BASE  0x010C0000
+
+
+extern u32 *ipu_cm_reg;
+extern u32 *ipu_idmac_reg;
+extern u32 *ipu_dp_reg;
+extern u32 *ipu_ic_reg;
+extern u32 *ipu_dc_reg;
+extern u32 *ipu_dc_tmpl_reg;
+extern u32 *ipu_dmfc_reg;
+extern u32 *ipu_di_reg[];
+extern u32 *ipu_smfc_reg;
+extern u32 *ipu_csi_reg[];
+extern u32 *ipu_tpmem_base;
+extern u32 *ipu_disp_base[];
+extern u32 *ipu_vdi_reg;
+
+/* Register addresses */
+/* IPU Common registers */
+#define IPU_CONF               (ipu_cm_reg)
+
+#define IPU_SRM_PRI1           (ipu_cm_reg + 0x00A0/4)
+#define IPU_SRM_PRI2           (ipu_cm_reg + 0x00A4/4)
+#define IPU_FS_PROC_FLOW1      (ipu_cm_reg + 0x00A8/4)
+#define IPU_FS_PROC_FLOW2      (ipu_cm_reg + 0x00AC/4)
+#define IPU_FS_PROC_FLOW3      (ipu_cm_reg + 0x00B0/4)
+#define IPU_FS_DISP_FLOW1      (ipu_cm_reg + 0x00B4/4)
+#define IPU_FS_DISP_FLOW2      (ipu_cm_reg + 0x00B8/4)
+#define IPU_SKIP               (ipu_cm_reg + 0x00BC/4)
+#define IPU_DISP_ALT_CONF      (ipu_cm_reg + 0x00C0/4)
+#define IPU_DISP_GEN           (ipu_cm_reg + 0x00C4/4)
+#define IPU_DISP_ALT1          (ipu_cm_reg + 0x00C8/4)
+#define IPU_DISP_ALT2          (ipu_cm_reg + 0x00CC/4)
+#define IPU_DISP_ALT3          (ipu_cm_reg + 0x00D0/4)
+#define IPU_DISP_ALT4          (ipu_cm_reg + 0x00D4/4)
+#define IPU_SNOOP              (ipu_cm_reg + 0x00D8/4)
+#define IPU_MEM_RST            (ipu_cm_reg + 0x00DC/4)
+#define IPU_PM                 (ipu_cm_reg + 0x00E0/4)
+#define IPU_GPR                        (ipu_cm_reg + 0x00E4/4)
+#define IPU_CHA_DB_MODE_SEL(ch)        (ipu_cm_reg + 0x0150/4 + (ch / 32))
+#define IPU_ALT_CHA_DB_MODE_SEL(ch) (ipu_cm_reg + 0x0168/4 + (ch / 32))
+#define IPU_CHA_CUR_BUF(ch)    ({g_ipu_hw_rev >= 2 ? \
+                                 (ipu_cm_reg + 0x023C/4 + (ch / 32)) : \
+                                 (ipu_cm_reg + 0x0124/4 + (ch / 32)); })
+#define IPU_ALT_CUR_BUF0       ({g_ipu_hw_rev >= 2 ? \
+                                 (ipu_cm_reg + 0x0244/4) : \
+                                 (ipu_cm_reg + 0x012C/4); })
+#define IPU_ALT_CUR_BUF1       ({g_ipu_hw_rev >= 2 ? \
+                                 (ipu_cm_reg + 0x0248/4) : \
+                                 (ipu_cm_reg + 0x0130/4); })
+#define IPU_SRM_STAT           ({g_ipu_hw_rev >= 2 ? \
+                                 (ipu_cm_reg + 0x024C/4) : \
+                                 (ipu_cm_reg + 0x0134/4); })
+#define IPU_PROC_TASK_STAT     ({g_ipu_hw_rev >= 2 ? \
+                                 (ipu_cm_reg + 0x0250/4) : \
+                                 (ipu_cm_reg + 0x0138/4); })
+#define IPU_DISP_TASK_STAT     ({g_ipu_hw_rev >= 2 ? \
+                                 (ipu_cm_reg + 0x0254/4) : \
+                                 (ipu_cm_reg + 0x013C/4); })
+#define IPU_CHA_BUF0_RDY(ch)   ({g_ipu_hw_rev >= 2 ? \
+                                 (ipu_cm_reg + 0x0268/4 + (ch / 32)) : \
+                                 (ipu_cm_reg + 0x0140/4 + (ch / 32)); })
+#define IPU_CHA_BUF1_RDY(ch)   ({g_ipu_hw_rev >= 2 ? \
+                                 (ipu_cm_reg + 0x0270/4 + (ch / 32)) : \
+                                 (ipu_cm_reg + 0x0148/4 + (ch / 32)); })
+#define IPU_ALT_CHA_BUF0_RDY(ch) ({g_ipu_hw_rev >= 2 ? \
+                                  (ipu_cm_reg + 0x0278/4 + (ch / 32)) : \
+                                  (ipu_cm_reg + 0x0158/4 + (ch / 32)); })
+#define IPU_ALT_CHA_BUF1_RDY(ch) ({g_ipu_hw_rev >= 2 ? \
+                                  (ipu_cm_reg + 0x0280/4 + (ch / 32)) : \
+                                  (ipu_cm_reg + 0x0160/4 + (ch / 32)); })
+
+#define IPU_INT_CTRL(n)                (ipu_cm_reg + 0x003C/4 + ((n) - 1))
+#define IPU_INT_CTRL_IRQ(irq)  IPU_INT_CTRL(((irq) / 32))
+#define IPU_INT_STAT_IRQ(irq)  IPU_INT_STAT(((irq) / 32))
+#define IPU_INT_STAT(n)                ({g_ipu_hw_rev >= 2 ? \
+                                 (ipu_cm_reg + 0x0200/4 + ((n) - 1)) : \
+                                 (ipu_cm_reg + 0x00E8/4 + ((n) - 1)); })
+
+#define IPUIRQ_2_STATREG(irq)  (IPU_INT_STAT(1) + ((irq) / 32))
+#define IPUIRQ_2_CTRLREG(irq)  (IPU_INT_CTRL(1) + ((irq) / 32))
+#define IPUIRQ_2_MASK(irq)     (1UL << ((irq) & 0x1F))
+
+#define VDI_FSIZE (ipu_vdi_reg)
+#define VDI_C (ipu_vdi_reg + 0x0004/4)
+
+/* CMOS Sensor Interface Registers */
+#define CSI_SENS_CONF(csi)     (ipu_csi_reg[csi])
+#define CSI_SENS_FRM_SIZE(csi) (ipu_csi_reg[csi] + 0x0004/4)
+#define CSI_ACT_FRM_SIZE(csi)  (ipu_csi_reg[csi] + 0x0008/4)
+#define CSI_OUT_FRM_CTRL(csi)  (ipu_csi_reg[csi] + 0x000C/4)
+#define CSI_TST_CTRL(csi)      (ipu_csi_reg[csi] + 0x0010/4)
+#define CSI_CCIR_CODE_1(csi)   (ipu_csi_reg[csi] + 0x0014/4)
+#define CSI_CCIR_CODE_2(csi)   (ipu_csi_reg[csi] + 0x0018/4)
+#define CSI_CCIR_CODE_3(csi)   (ipu_csi_reg[csi] + 0x001C/4)
+#define CSI_MIPI_DI(csi)       (ipu_csi_reg[csi] + 0x0020/4)
+#define CSI_SKIP(csi)          (ipu_csi_reg[csi] + 0x0024/4)
+#define CSI_CPD_CTRL(csi)      (ipu_csi_reg[csi] + 0x0028/4)
+#define CSI_CPD_RC(csi, n)     (ipu_csi_reg[csi] + 0x002C/4 + n)
+#define CSI_CPD_RS(csi, n)     (ipu_csi_reg[csi] + 0x004C/4 + n)
+#define CSI_CPD_GRC(csi, n)    (ipu_csi_reg[csi] + 0x005C/4 + n)
+#define CSI_CPD_GRS(csi, n)    (ipu_csi_reg[csi] + 0x007C/4 + n)
+#define CSI_CPD_GBC(csi, n)    (ipu_csi_reg[csi] + 0x008C/4 + n)
+#define CSI_CPD_GBS(csi, n)    (ipu_csi_reg[csi] + 0x00AC/4 + n)
+#define CSI_CPD_BC(csi, n)     (ipu_csi_reg[csi] + 0x00BC/4 + n)
+#define CSI_CPD_BS(csi, n)     (ipu_csi_reg[csi] + 0x00DC/4 + n)
+#define CSI_CPD_OFFSET1(csi)   (ipu_csi_reg[csi] + 0x00EC/4)
+#define CSI_CPD_OFFSET2(csi)   (ipu_csi_reg[csi] + 0x00F0/4)
+
+/*SMFC Registers */
+#define SMFC_MAP       (ipu_smfc_reg)
+#define SMFC_WMC       (ipu_smfc_reg + 0x0004/4)
+#define SMFC_BS                (ipu_smfc_reg + 0x0008/4)
+
+/* Image Converter Registers */
+#define IC_CONF                        (ipu_ic_reg)
+#define IC_PRP_ENC_RSC         (ipu_ic_reg + 0x0004/4)
+#define IC_PRP_VF_RSC          (ipu_ic_reg + 0x0008/4)
+#define IC_PP_RSC              (ipu_ic_reg + 0x000C/4)
+#define IC_CMBP_1              (ipu_ic_reg + 0x0010/4)
+#define IC_CMBP_2              (ipu_ic_reg + 0x0014/4)
+#define IC_IDMAC_1             (ipu_ic_reg + 0x0018/4)
+#define IC_IDMAC_2             (ipu_ic_reg + 0x001C/4)
+#define IC_IDMAC_3             (ipu_ic_reg + 0x0020/4)
+#define IC_IDMAC_4             (ipu_ic_reg + 0x0024/4)
+
+#define IDMAC_CONF             (ipu_idmac_reg + 0x0000)
+#define IDMAC_CHA_EN(ch)       (ipu_idmac_reg + 0x0004/4 + (ch/32))
+#define IDMAC_SEP_ALPHA                (ipu_idmac_reg + 0x000C/4)
+#define IDMAC_ALT_SEP_ALPHA    (ipu_idmac_reg + 0x0010/4)
+#define IDMAC_CHA_PRI(ch)      (ipu_idmac_reg + 0x0014/4 + (ch/32))
+#define IDMAC_WM_EN(ch)                (ipu_idmac_reg + 0x001C/4 + (ch/32))
+#define IDMAC_CH_LOCK_EN_1     ({g_ipu_hw_rev >= 2 ? \
+                                 (ipu_idmac_reg + 0x0024/4) : 0; })
+#define IDMAC_CH_LOCK_EN_2     ({g_ipu_hw_rev >= 2 ? \
+                                 (ipu_idmac_reg + 0x0028/4) : \
+                                 (ipu_idmac_reg + 0x0024/4); })
+#define IDMAC_SUB_ADDR_0       ({g_ipu_hw_rev >= 2 ? \
+                                 (ipu_idmac_reg + 0x002C/4) : \
+                                 (ipu_idmac_reg + 0x0028/4); })
+#define IDMAC_SUB_ADDR_1       ({g_ipu_hw_rev >= 2 ? \
+                                 (ipu_idmac_reg + 0x0030/4) : \
+                                 (ipu_idmac_reg + 0x002C/4); })
+#define IDMAC_SUB_ADDR_2       ({g_ipu_hw_rev >= 2 ? \
+                                 (ipu_idmac_reg + 0x0034/4) : \
+                                 (ipu_idmac_reg + 0x0030/4); })
+#define IDMAC_BAND_EN(ch)      ({g_ipu_hw_rev >= 2 ? \
+                                 (ipu_idmac_reg + 0x0040/4 + (ch/32)) : \
+                                 (ipu_idmac_reg + 0x0034/4 + (ch/32)); })
+#define IDMAC_CHA_BUSY(ch)     ({g_ipu_hw_rev >= 2 ? \
+                                 (ipu_idmac_reg + 0x0100/4 + (ch/32)) : \
+                                 (ipu_idmac_reg + 0x0040/4 + (ch/32)); })
+
+#define DI_GENERAL(di)         (ipu_di_reg[di])
+#define DI_BS_CLKGEN0(di)      (ipu_di_reg[di] + 0x0004/4)
+#define DI_BS_CLKGEN1(di)      (ipu_di_reg[di] + 0x0008/4)
+
+#define DI_SW_GEN0(di, gen)    (ipu_di_reg[di] + 0x000C/4 + (gen - 1))
+#define DI_SW_GEN1(di, gen)    (ipu_di_reg[di] + 0x0030/4 + (gen - 1))
+#define DI_STP_REP(di, gen)    (ipu_di_reg[di] + 0x0148/4 + (gen - 1)/2)
+#define DI_SYNC_AS_GEN(di)     (ipu_di_reg[di] + 0x0054/4)
+#define DI_DW_GEN(di, gen)     (ipu_di_reg[di] + 0x0058/4 + gen)
+#define DI_DW_SET(di, gen, set)        (ipu_di_reg[di] + 0x0088/4 + gen + 0xC*set)
+#define DI_SER_CONF(di)                (ipu_di_reg[di] + 0x015C/4)
+#define DI_SSC(di)             (ipu_di_reg[di] + 0x0160/4)
+#define DI_POL(di)             (ipu_di_reg[di] + 0x0164/4)
+#define DI_AW0(di)             (ipu_di_reg[di] + 0x0168/4)
+#define DI_AW1(di)             (ipu_di_reg[di] + 0x016C/4)
+#define DI_SCR_CONF(di)                (ipu_di_reg[di] + 0x0170/4)
+#define DI_STAT(di)            (ipu_di_reg[di] + 0x0174/4)
+
+#define DMFC_RD_CHAN           (ipu_dmfc_reg)
+#define DMFC_WR_CHAN           (ipu_dmfc_reg + 0x0004/4)
+#define DMFC_WR_CHAN_DEF       (ipu_dmfc_reg + 0x0008/4)
+#define DMFC_DP_CHAN           (ipu_dmfc_reg + 0x000C/4)
+#define DMFC_DP_CHAN_DEF       (ipu_dmfc_reg + 0x0010/4)
+#define DMFC_GENERAL1          (ipu_dmfc_reg + 0x0014/4)
+#define DMFC_GENERAL2          (ipu_dmfc_reg + 0x0018/4)
+#define DMFC_IC_CTRL           (ipu_dmfc_reg + 0x001C/4)
+#define DMFC_STAT              (ipu_dmfc_reg + 0x0020/4)
+
+#define DC_MAP_CONF_PTR(n)     (ipu_dc_reg + 0x0108/4 + n/2)
+#define DC_MAP_CONF_VAL(n)     (ipu_dc_reg + 0x0144/4 + n/2)
+
+#define _RL_CH_2_OFFSET(ch)    ((ch == 0) ? 8 : ( \
+                                (ch == 1) ? 0x24 : ( \
+                                (ch == 2) ? 0x40 : ( \
+                                (ch == 5) ? 0x64 : ( \
+                                (ch == 6) ? 0x80 : ( \
+                                (ch == 8) ? 0x9C : ( \
+                                (ch == 9) ? 0xBC : (-1))))))))
+#define DC_RL_CH(ch, evt)      (ipu_dc_reg + _RL_CH_2_OFFSET(ch)/4 + evt/2)
+
+#define DC_EVT_NF              0
+#define DC_EVT_NL              1
+#define DC_EVT_EOF             2
+#define DC_EVT_NFIELD          3
+#define DC_EVT_EOL             4
+#define DC_EVT_EOFIELD         5
+#define DC_EVT_NEW_ADDR                6
+#define DC_EVT_NEW_CHAN                7
+#define DC_EVT_NEW_DATA                8
+
+#define DC_EVT_NEW_ADDR_W_0    0
+#define DC_EVT_NEW_ADDR_W_1    1
+#define DC_EVT_NEW_CHAN_W_0    2
+#define DC_EVT_NEW_CHAN_W_1    3
+#define DC_EVT_NEW_DATA_W_0    4
+#define DC_EVT_NEW_DATA_W_1    5
+#define DC_EVT_NEW_ADDR_R_0    6
+#define DC_EVT_NEW_ADDR_R_1    7
+#define DC_EVT_NEW_CHAN_R_0    8
+#define DC_EVT_NEW_CHAN_R_1    9
+#define DC_EVT_NEW_DATA_R_0    10
+#define DC_EVT_NEW_DATA_R_1    11
+#define DC_EVEN_UGDE0          12
+#define DC_ODD_UGDE0           13
+#define DC_EVEN_UGDE1          14
+#define DC_ODD_UGDE1           15
+#define DC_EVEN_UGDE2          16
+#define DC_ODD_UGDE2           17
+#define DC_EVEN_UGDE3          18
+#define DC_ODD_UGDE3           19
+
+#define dc_ch_offset(ch) \
+({ \
+       const u8 _offset[] = { \
+               0, 0x1C, 0x38, 0x54, 0x58, 0x5C, 0x78, 0, 0x94, 0xB4}; \
+       _offset[ch]; \
+})
+#define DC_WR_CH_CONF(ch)      (ipu_dc_reg + dc_ch_offset(ch)/4)
+#define DC_WR_CH_ADDR(ch)      (ipu_dc_reg + dc_ch_offset(ch)/4 + 4/4)
+
+#define DC_WR_CH_CONF_1                (ipu_dc_reg + 0x001C/4)
+#define DC_WR_CH_ADDR_1                (ipu_dc_reg + 0x0020/4)
+#define DC_WR_CH_CONF_5                (ipu_dc_reg + 0x005C/4)
+#define DC_WR_CH_ADDR_5                (ipu_dc_reg + 0x0060/4)
+#define DC_GEN                 (ipu_dc_reg + 0x00D4/4)
+#define DC_DISP_CONF1(disp)    (ipu_dc_reg + 0x00D8/4 + disp)
+#define DC_DISP_CONF2(disp)    (ipu_dc_reg + 0x00E8/4 + disp)
+#define DC_STAT                        (ipu_dc_reg + 0x01C8/4)
+#define DC_UGDE_0(evt)         (ipu_dc_reg + 0x0174/4 + evt*4)
+#define DC_UGDE_1(evt)         (ipu_dc_reg + 0x0178/4 + evt*4)
+#define DC_UGDE_2(evt)         (ipu_dc_reg + 0x017C/4 + evt*4)
+#define DC_UGDE_3(evt)         (ipu_dc_reg + 0x0180/4 + evt*4)
+
+#define DP_SYNC 0
+#define DP_ASYNC0 0x60
+#define DP_ASYNC1 0xBC
+#define DP_COM_CONF(flow)      (ipu_dp_reg + flow/4)
+#define DP_GRAPH_WIND_CTRL(flow) (ipu_dp_reg + 0x0004/4 + flow/4)
+#define DP_FG_POS(flow)                (ipu_dp_reg + 0x0008/4 + flow/4)
+#define DP_GAMMA_C(flow, i)    (ipu_dp_reg + 0x0014/4 + flow/4 + i)
+#define DP_GAMMA_S(flow, i)    (ipu_dp_reg + 0x0034/4 + flow/4 + i)
+#define DP_CSC_A_0(flow)       (ipu_dp_reg + 0x0044/4 + flow/4)
+#define DP_CSC_A_1(flow)       (ipu_dp_reg + 0x0048/4 + flow/4)
+#define DP_CSC_A_2(flow)       (ipu_dp_reg + 0x004C/4 + flow/4)
+#define DP_CSC_A_3(flow)       (ipu_dp_reg + 0x0050/4 + flow/4)
+#define DP_CSC_0(flow)         (ipu_dp_reg + 0x0054/4 + flow/4)
+#define DP_CSC_1(flow)         (ipu_dp_reg + 0x0058/4 + flow/4)
+
+enum {
+       IPU_CONF_CSI0_EN = 0x00000001,
+       IPU_CONF_CSI1_EN = 0x00000002,
+       IPU_CONF_IC_EN = 0x00000004,
+       IPU_CONF_ROT_EN = 0x00000008,
+       IPU_CONF_ISP_EN = 0x00000010,
+       IPU_CONF_DP_EN = 0x00000020,
+       IPU_CONF_DI0_EN = 0x00000040,
+       IPU_CONF_DI1_EN = 0x00000080,
+       IPU_CONF_DMFC_EN = 0x00000400,
+       IPU_CONF_SMFC_EN = 0x00000100,
+       IPU_CONF_DC_EN = 0x00000200,
+       IPU_CONF_VDI_EN = 0x00001000,
+       IPU_CONF_IDMAC_DIS = 0x00400000,
+       IPU_CONF_IC_DMFC_SEL = 0x02000000,
+       IPU_CONF_IC_DMFC_SYNC = 0x04000000,
+       IPU_CONF_VDI_DMFC_SYNC = 0x08000000,
+       IPU_CONF_CSI0_DATA_SOURCE = 0x10000000,
+       IPU_CONF_CSI0_DATA_SOURCE_OFFSET = 28,
+       IPU_CONF_CSI1_DATA_SOURCE = 0x20000000,
+       IPU_CONF_IC_INPUT = 0x40000000,
+       IPU_CONF_CSI_SEL = 0x80000000,
+
+       DI0_COUNTER_RELEASE = 0x01000000,
+       DI1_COUNTER_RELEASE = 0x02000000,
+
+       FS_PRPVF_ROT_SRC_SEL_MASK = 0x00000F00,
+       FS_PRPVF_ROT_SRC_SEL_OFFSET = 8,
+       FS_PRPENC_ROT_SRC_SEL_MASK = 0x0000000F,
+       FS_PRPENC_ROT_SRC_SEL_OFFSET = 0,
+       FS_PP_ROT_SRC_SEL_MASK = 0x000F0000,
+       FS_PP_ROT_SRC_SEL_OFFSET = 16,
+       FS_PP_SRC_SEL_MASK = 0x0000F000,
+       FS_PP_SRC_SEL_OFFSET = 12,
+       FS_PRP_SRC_SEL_MASK = 0x0F000000,
+       FS_PRP_SRC_SEL_OFFSET = 24,
+       FS_VF_IN_VALID = 0x80000000,
+       FS_ENC_IN_VALID = 0x40000000,
+       FS_VDI_SRC_SEL_MASK = 0x30000000,
+       FS_VDI_SRC_SEL_OFFSET = 28,
+
+
+       FS_PRPENC_DEST_SEL_MASK = 0x0000000F,
+       FS_PRPENC_DEST_SEL_OFFSET = 0,
+       FS_PRPVF_DEST_SEL_MASK = 0x000000F0,
+       FS_PRPVF_DEST_SEL_OFFSET = 4,
+       FS_PRPVF_ROT_DEST_SEL_MASK = 0x00000F00,
+       FS_PRPVF_ROT_DEST_SEL_OFFSET = 8,
+       FS_PP_DEST_SEL_MASK = 0x0000F000,
+       FS_PP_DEST_SEL_OFFSET = 12,
+       FS_PP_ROT_DEST_SEL_MASK = 0x000F0000,
+       FS_PP_ROT_DEST_SEL_OFFSET = 16,
+       FS_PRPENC_ROT_DEST_SEL_MASK = 0x00F00000,
+       FS_PRPENC_ROT_DEST_SEL_OFFSET = 20,
+
+       FS_SMFC0_DEST_SEL_MASK = 0x0000000F,
+       FS_SMFC0_DEST_SEL_OFFSET = 0,
+       FS_SMFC1_DEST_SEL_MASK = 0x00000070,
+       FS_SMFC1_DEST_SEL_OFFSET = 4,
+       FS_SMFC2_DEST_SEL_MASK = 0x00000780,
+       FS_SMFC2_DEST_SEL_OFFSET = 7,
+       FS_SMFC3_DEST_SEL_MASK = 0x00003800,
+       FS_SMFC3_DEST_SEL_OFFSET = 11,
+
+       FS_DC1_SRC_SEL_MASK = 0x00F00000,
+       FS_DC1_SRC_SEL_OFFSET = 20,
+       FS_DC2_SRC_SEL_MASK = 0x000F0000,
+       FS_DC2_SRC_SEL_OFFSET = 16,
+       FS_DP_SYNC0_SRC_SEL_MASK = 0x0000000F,
+       FS_DP_SYNC0_SRC_SEL_OFFSET = 0,
+       FS_DP_SYNC1_SRC_SEL_MASK = 0x000000F0,
+       FS_DP_SYNC1_SRC_SEL_OFFSET = 4,
+       FS_DP_ASYNC0_SRC_SEL_MASK = 0x00000F00,
+       FS_DP_ASYNC0_SRC_SEL_OFFSET = 8,
+       FS_DP_ASYNC1_SRC_SEL_MASK = 0x0000F000,
+       FS_DP_ASYNC1_SRC_SEL_OFFSET = 12,
+
+       FS_AUTO_REF_PER_MASK = 0,
+       FS_AUTO_REF_PER_OFFSET = 16,
+
+       TSTAT_VF_MASK = 0x0000000C,
+       TSTAT_VF_OFFSET = 2,
+       TSTAT_VF_ROT_MASK = 0x00000300,
+       TSTAT_VF_ROT_OFFSET = 8,
+       TSTAT_ENC_MASK = 0x00000003,
+       TSTAT_ENC_OFFSET = 0,
+       TSTAT_ENC_ROT_MASK = 0x000000C0,
+       TSTAT_ENC_ROT_OFFSET = 6,
+       TSTAT_PP_MASK = 0x00000030,
+       TSTAT_PP_OFFSET = 4,
+       TSTAT_PP_ROT_MASK = 0x00000C00,
+       TSTAT_PP_ROT_OFFSET = 10,
+
+       TASK_STAT_IDLE = 0,
+       TASK_STAT_ACTIVE = 1,
+       TASK_STAT_WAIT4READY = 2,
+
+       /* Image Converter Register bits */
+       IC_CONF_PRPENC_EN = 0x00000001,
+       IC_CONF_PRPENC_CSC1 = 0x00000002,
+       IC_CONF_PRPENC_ROT_EN = 0x00000004,
+       IC_CONF_PRPVF_EN = 0x00000100,
+       IC_CONF_PRPVF_CSC1 = 0x00000200,
+       IC_CONF_PRPVF_CSC2 = 0x00000400,
+       IC_CONF_PRPVF_CMB = 0x00000800,
+       IC_CONF_PRPVF_ROT_EN = 0x00001000,
+       IC_CONF_PP_EN = 0x00010000,
+       IC_CONF_PP_CSC1 = 0x00020000,
+       IC_CONF_PP_CSC2 = 0x00040000,
+       IC_CONF_PP_CMB = 0x00080000,
+       IC_CONF_PP_ROT_EN = 0x00100000,
+       IC_CONF_IC_GLB_LOC_A = 0x10000000,
+       IC_CONF_KEY_COLOR_EN = 0x20000000,
+       IC_CONF_RWS_EN = 0x40000000,
+       IC_CONF_CSI_MEM_WR_EN = 0x80000000,
+
+       IC_IDMAC_1_CB0_BURST_16 = 0x00000001,
+       IC_IDMAC_1_CB1_BURST_16 = 0x00000002,
+       IC_IDMAC_1_CB2_BURST_16 = 0x00000004,
+       IC_IDMAC_1_CB3_BURST_16 = 0x00000008,
+       IC_IDMAC_1_CB4_BURST_16 = 0x00000010,
+       IC_IDMAC_1_CB5_BURST_16 = 0x00000020,
+       IC_IDMAC_1_CB6_BURST_16 = 0x00000040,
+       IC_IDMAC_1_CB7_BURST_16 = 0x00000080,
+       IC_IDMAC_1_PRPENC_ROT_MASK = 0x00003800,
+       IC_IDMAC_1_PRPENC_ROT_OFFSET = 11,
+       IC_IDMAC_1_PRPVF_ROT_MASK = 0x0001C000,
+       IC_IDMAC_1_PRPVF_ROT_OFFSET = 14,
+       IC_IDMAC_1_PP_ROT_MASK = 0x000E0000,
+       IC_IDMAC_1_PP_ROT_OFFSET = 17,
+       IC_IDMAC_1_PP_FLIP_RS = 0x00400000,
+       IC_IDMAC_1_PRPVF_FLIP_RS = 0x00200000,
+       IC_IDMAC_1_PRPENC_FLIP_RS = 0x00100000,
+
+       IC_IDMAC_2_PRPENC_HEIGHT_MASK = 0x000003FF,
+       IC_IDMAC_2_PRPENC_HEIGHT_OFFSET = 0,
+       IC_IDMAC_2_PRPVF_HEIGHT_MASK = 0x000FFC00,
+       IC_IDMAC_2_PRPVF_HEIGHT_OFFSET = 10,
+       IC_IDMAC_2_PP_HEIGHT_MASK = 0x3FF00000,
+       IC_IDMAC_2_PP_HEIGHT_OFFSET = 20,
+
+       IC_IDMAC_3_PRPENC_WIDTH_MASK = 0x000003FF,
+       IC_IDMAC_3_PRPENC_WIDTH_OFFSET = 0,
+       IC_IDMAC_3_PRPVF_WIDTH_MASK = 0x000FFC00,
+       IC_IDMAC_3_PRPVF_WIDTH_OFFSET = 10,
+       IC_IDMAC_3_PP_WIDTH_MASK = 0x3FF00000,
+       IC_IDMAC_3_PP_WIDTH_OFFSET = 20,
+
+       CSI_SENS_CONF_DATA_FMT_SHIFT = 8,
+       CSI_SENS_CONF_DATA_FMT_MASK = 0x00000700,
+       CSI_SENS_CONF_DATA_FMT_RGB_YUV444 = 0L,
+       CSI_SENS_CONF_DATA_FMT_YUV422_YUYV = 1L,
+       CSI_SENS_CONF_DATA_FMT_YUV422_UYVY = 2L,
+       CSI_SENS_CONF_DATA_FMT_BAYER = 3L,
+       CSI_SENS_CONF_DATA_FMT_RGB565 = 4L,
+       CSI_SENS_CONF_DATA_FMT_RGB555 = 5L,
+       CSI_SENS_CONF_DATA_FMT_RGB444 = 6L,
+       CSI_SENS_CONF_DATA_FMT_JPEG = 7L,
+
+       CSI_SENS_CONF_VSYNC_POL_SHIFT = 0,
+       CSI_SENS_CONF_HSYNC_POL_SHIFT = 1,
+       CSI_SENS_CONF_DATA_POL_SHIFT = 2,
+       CSI_SENS_CONF_PIX_CLK_POL_SHIFT = 3,
+       CSI_SENS_CONF_SENS_PRTCL_MASK = 0x00000070L,
+       CSI_SENS_CONF_SENS_PRTCL_SHIFT = 4,
+       CSI_SENS_CONF_PACK_TIGHT_SHIFT = 7,
+       CSI_SENS_CONF_DATA_WIDTH_SHIFT = 11,
+       CSI_SENS_CONF_EXT_VSYNC_SHIFT = 15,
+       CSI_SENS_CONF_DIVRATIO_SHIFT = 16,
+
+       CSI_SENS_CONF_DIVRATIO_MASK = 0x00FF0000L,
+       CSI_SENS_CONF_DATA_DEST_SHIFT = 24,
+       CSI_SENS_CONF_DATA_DEST_MASK = 0x07000000L,
+       CSI_SENS_CONF_JPEG8_EN_SHIFT = 27,
+       CSI_SENS_CONF_JPEG_EN_SHIFT = 28,
+       CSI_SENS_CONF_FORCE_EOF_SHIFT = 29,
+       CSI_SENS_CONF_DATA_EN_POL_SHIFT = 31,
+
+       CSI_DATA_DEST_ISP = 1L,
+       CSI_DATA_DEST_IC = 2L,
+       CSI_DATA_DEST_IDMAC = 4L,
+
+       CSI_CCIR_ERR_DET_EN = 0x01000000L,
+       CSI_HORI_DOWNSIZE_EN = 0x80000000L,
+       CSI_VERT_DOWNSIZE_EN = 0x40000000L,
+       CSI_TEST_GEN_MODE_EN = 0x01000000L,
+
+       CSI_HSC_MASK = 0x1FFF0000,
+       CSI_HSC_SHIFT = 16,
+       CSI_VSC_MASK = 0x00000FFF,
+       CSI_VSC_SHIFT = 0,
+
+       CSI_TEST_GEN_R_MASK = 0x000000FFL,
+       CSI_TEST_GEN_R_SHIFT = 0,
+       CSI_TEST_GEN_G_MASK = 0x0000FF00L,
+       CSI_TEST_GEN_G_SHIFT = 8,
+       CSI_TEST_GEN_B_MASK = 0x00FF0000L,
+       CSI_TEST_GEN_B_SHIFT = 16,
+
+       CSI_MIPI_DI0_MASK = 0x000000FFL,
+       CSI_MIPI_DI0_SHIFT = 0,
+       CSI_MIPI_DI1_MASK = 0x0000FF00L,
+       CSI_MIPI_DI1_SHIFT = 8,
+       CSI_MIPI_DI2_MASK = 0x00FF0000L,
+       CSI_MIPI_DI2_SHIFT = 16,
+       CSI_MIPI_DI3_MASK = 0xFF000000L,
+       CSI_MIPI_DI3_SHIFT = 24,
+
+       CSI_MAX_RATIO_SKIP_ISP_MASK = 0x00070000L,
+       CSI_MAX_RATIO_SKIP_ISP_SHIFT = 16,
+       CSI_SKIP_ISP_MASK = 0x00F80000L,
+       CSI_SKIP_ISP_SHIFT = 19,
+       CSI_MAX_RATIO_SKIP_SMFC_MASK = 0x00000007L,
+       CSI_MAX_RATIO_SKIP_SMFC_SHIFT = 0,
+       CSI_SKIP_SMFC_MASK = 0x000000F8L,
+       CSI_SKIP_SMFC_SHIFT = 3,
+       CSI_ID_2_SKIP_MASK = 0x00000300L,
+       CSI_ID_2_SKIP_SHIFT = 8,
+
+       CSI_COLOR_FIRST_ROW_MASK = 0x00000002L,
+       CSI_COLOR_FIRST_COMP_MASK = 0x00000001L,
+
+       SMFC_MAP_CH0_MASK = 0x00000007L,
+       SMFC_MAP_CH0_SHIFT = 0,
+       SMFC_MAP_CH1_MASK = 0x00000038L,
+       SMFC_MAP_CH1_SHIFT = 3,
+       SMFC_MAP_CH2_MASK = 0x000001C0L,
+       SMFC_MAP_CH2_SHIFT = 6,
+       SMFC_MAP_CH3_MASK = 0x00000E00L,
+       SMFC_MAP_CH3_SHIFT = 9,
+
+       SMFC_WM0_SET_MASK = 0x00000007L,
+       SMFC_WM0_SET_SHIFT = 0,
+       SMFC_WM1_SET_MASK = 0x000001C0L,
+       SMFC_WM1_SET_SHIFT = 6,
+       SMFC_WM2_SET_MASK = 0x00070000L,
+       SMFC_WM2_SET_SHIFT = 16,
+       SMFC_WM3_SET_MASK = 0x01C00000L,
+       SMFC_WM3_SET_SHIFT = 22,
+
+       SMFC_WM0_CLR_MASK = 0x00000038L,
+       SMFC_WM0_CLR_SHIFT = 3,
+       SMFC_WM1_CLR_MASK = 0x00000E00L,
+       SMFC_WM1_CLR_SHIFT = 9,
+       SMFC_WM2_CLR_MASK = 0x00380000L,
+       SMFC_WM2_CLR_SHIFT = 19,
+       SMFC_WM3_CLR_MASK = 0x0E000000L,
+       SMFC_WM3_CLR_SHIFT = 25,
+
+       SMFC_BS0_MASK = 0x0000000FL,
+       SMFC_BS0_SHIFT = 0,
+       SMFC_BS1_MASK = 0x000000F0L,
+       SMFC_BS1_SHIFT = 4,
+       SMFC_BS2_MASK = 0x00000F00L,
+       SMFC_BS2_SHIFT = 8,
+       SMFC_BS3_MASK = 0x0000F000L,
+       SMFC_BS3_SHIFT = 12,
+
+       PF_CONF_TYPE_MASK = 0x00000007,
+       PF_CONF_TYPE_SHIFT = 0,
+       PF_CONF_PAUSE_EN = 0x00000010,
+       PF_CONF_RESET = 0x00008000,
+       PF_CONF_PAUSE_ROW_MASK = 0x00FF0000,
+       PF_CONF_PAUSE_ROW_SHIFT = 16,
+
+       DI_DW_GEN_ACCESS_SIZE_OFFSET = 24,
+       DI_DW_GEN_COMPONENT_SIZE_OFFSET = 16,
+
+       DI_GEN_DI_CLK_EXT = 0x100000,
+       DI_GEN_POLARITY_DISP_CLK = 0x00020000,
+       DI_GEN_POLARITY_1 = 0x00000001,
+       DI_GEN_POLARITY_2 = 0x00000002,
+       DI_GEN_POLARITY_3 = 0x00000004,
+       DI_GEN_POLARITY_4 = 0x00000008,
+       DI_GEN_POLARITY_5 = 0x00000010,
+       DI_GEN_POLARITY_6 = 0x00000020,
+       DI_GEN_POLARITY_7 = 0x00000040,
+       DI_GEN_POLARITY_8 = 0x00000080,
+
+       DI_POL_DRDY_DATA_POLARITY = 0x00000080,
+       DI_POL_DRDY_POLARITY_15 = 0x00000010,
+
+       DI_VSYNC_SEL_OFFSET = 13,
+
+       DC_WR_CH_CONF_FIELD_MODE = 0x00000200,
+       DC_WR_CH_CONF_PROG_TYPE_OFFSET = 5,
+       DC_WR_CH_CONF_PROG_TYPE_MASK = 0x000000E0,
+       DC_WR_CH_CONF_PROG_DI_ID = 0x00000004,
+       DC_WR_CH_CONF_PROG_DISP_ID_OFFSET = 3,
+       DC_WR_CH_CONF_PROG_DISP_ID_MASK = 0x00000018,
+
+       DC_UGDE_0_ODD_EN = 0x02000000,
+       DC_UGDE_0_ID_CODED_MASK = 0x00000007,
+       DC_UGDE_0_ID_CODED_OFFSET = 0,
+       DC_UGDE_0_EV_PRIORITY_MASK = 0x00000078,
+       DC_UGDE_0_EV_PRIORITY_OFFSET = 3,
+
+       DP_COM_CONF_FG_EN = 0x00000001,
+       DP_COM_CONF_GWSEL = 0x00000002,
+       DP_COM_CONF_GWAM = 0x00000004,
+       DP_COM_CONF_GWCKE = 0x00000008,
+       DP_COM_CONF_CSC_DEF_MASK = 0x00000300,
+       DP_COM_CONF_CSC_DEF_OFFSET = 8,
+       DP_COM_CONF_CSC_DEF_FG = 0x00000300,
+       DP_COM_CONF_CSC_DEF_BG = 0x00000200,
+       DP_COM_CONF_CSC_DEF_BOTH = 0x00000100,
+       DP_COM_CONF_GAMMA_EN = 0x00001000,
+       DP_COM_CONF_GAMMA_YUV_EN = 0x00002000,
+
+       DI_SER_CONF_LLA_SER_ACCESS = 0x00000020,
+       DI_SER_CONF_SERIAL_CLK_POL = 0x00000010,
+       DI_SER_CONF_SERIAL_DATA_POL = 0x00000008,
+       DI_SER_CONF_SERIAL_RS_POL = 0x00000004,
+       DI_SER_CONF_SERIAL_CS_POL = 0x00000002,
+       DI_SER_CONF_WAIT4SERIAL = 0x00000001,
+
+       VDI_C_CH_420 = 0x00000000,
+       VDI_C_CH_422 = 0x00000002,
+       VDI_C_MOT_SEL_FULL = 0x00000008,
+       VDI_C_MOT_SEL_LOW = 0x00000004,
+       VDI_C_MOT_SEL_MED = 0x00000000,
+       VDI_C_BURST_SIZE1_4 = 0x00000030,
+       VDI_C_BURST_SIZE2_4 = 0x00000300,
+       VDI_C_BURST_SIZE3_4 = 0x00003000,
+       VDI_C_VWM1_SET_1 = 0x00000000,
+       VDI_C_VWM1_CLR_2 = 0x00080000,
+       VDI_C_VWM3_SET_1 = 0x00000000,
+       VDI_C_VWM3_CLR_2 = 0x02000000,
+       VDI_C_TOP_FIELD_MAN_1 = 0x40000000,
+       VDI_C_TOP_FIELD_AUTO_1 = 0x80000000,
+};
+
+enum di_pins {
+       DI_PIN11 = 0,
+       DI_PIN12 = 1,
+       DI_PIN13 = 2,
+       DI_PIN14 = 3,
+       DI_PIN15 = 4,
+       DI_PIN16 = 5,
+       DI_PIN17 = 6,
+       DI_PIN_CS = 7,
+
+       DI_PIN_SER_CLK = 0,
+       DI_PIN_SER_RS = 1,
+};
+
+enum di_sync_wave {
+       DI_SYNC_NONE = -1,
+       DI_SYNC_CLK = 0,
+       DI_SYNC_INT_HSYNC = 1,
+       DI_SYNC_HSYNC = 2,
+       DI_SYNC_VSYNC = 3,
+       DI_SYNC_DE = 5,
+};
+
+/* DC template opcodes */
+#define WROD(lf)               (0x18 | (lf << 1))
+#define WRG                    (0x01)
+
+#endif
diff --git a/drivers/mxc/mcu_pmic/Kconfig b/drivers/mxc/mcu_pmic/Kconfig
new file mode 100644 (file)
index 0000000..cb6815e
--- /dev/null
@@ -0,0 +1,17 @@
+#
+# PMIC Modules configuration
+#
+
+config MXC_PMIC_MC9S08DZ60
+       tristate "MC9S08DZ60 PMIC"
+       depends on ARCH_MXC && I2C
+         ---help---
+         This is the MXC MC9S08DZ60(MCU) PMIC support.
+
+config MXC_MC9SDZ60_RTC
+       tristate "MC9SDZ60 Real Time Clock (RTC) support"
+       depends on MXC_PMIC_MC9SDZ60
+       ---help---
+       This is the MC9SDZ60 RTC module driver. This module provides kernel API
+       for RTC part of MC9SDZ60.
+       If you want MC9SDZ60 RTC support, you should say Y here
diff --git a/drivers/mxc/mcu_pmic/Makefile b/drivers/mxc/mcu_pmic/Makefile
new file mode 100644 (file)
index 0000000..96aae94
--- /dev/null
@@ -0,0 +1,6 @@
+#
+# Makefile for the mc9sdz60 pmic drivers.
+#
+
+obj-$(CONFIG_MXC_PMIC_MC9SDZ60) += pmic_mc9sdz60_mod.o
+pmic_mc9sdz60_mod-objs := mcu_pmic_core.o max8660.o mc9s08dz60.o mcu_pmic_gpio.o
diff --git a/drivers/mxc/mcu_pmic/max8660.c b/drivers/mxc/mcu_pmic/max8660.c
new file mode 100644 (file)
index 0000000..1f3f19b
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * 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 max8660.c
+ * @brief Driver for max8660
+ *
+ * @ingroup pmic
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/proc_fs.h>
+#include <linux/i2c.h>
+#include <linux/mfd/mc9s08dz60/pmic.h>
+#include <asm/uaccess.h>
+#include "mcu_pmic_core.h"
+#include "max8660.h"
+
+/* I2C bus id and device address of mcu */
+#define I2C1_BUS       0
+#define MAX8660_I2C_ADDR                               0x68
+
+static struct i2c_client *max8660_i2c_client;
+
+    /* reg names for max8660
+       REG_MAX8660_OUTPUT_ENABLE_1,
+       REG_MAX8660_OUTPUT_ENABLE_2,
+       REG_MAX8660_VOLT__CHANGE_1,
+       REG_MAX8660_V3_TARGET_VOLT_1,
+       REG_MAX8660_V3_TARGET_VOLT_2,
+       REG_MAX8660_V4_TARGET_VOLT_1,
+       REG_MAX8660_V4_TARGET_VOLT_2,
+       REG_MAX8660_V5_TARGET_VOLT_1,
+       REG_MAX8660_V5_TARGET_VOLT_2,
+       REG_MAX8660_V6V7_TARGET_VOLT,
+       REG_MAX8660_FORCE_PWM
+     */
+
+    /* save down the reg values for the device is write only */
+static u8 max8660_reg_value_table[] = {
+       0x0, 0x0, 0x0, 0x17, 0x17, 0x1F, 0x1F, 0x04, 0x04, 0x0, 0x0
+};
+static int max8660_dev_present;
+
+int is_max8660_present(void)
+{
+       return max8660_dev_present;
+}
+
+int max8660_get_buffered_reg_val(int reg_name, u8 *value)
+{
+       if (!max8660_dev_present)
+               return -1;
+       /* outof range */
+       if (reg_name < REG_MAX8660_OUTPUT_ENABLE_1
+           || reg_name > REG_MAX8660_FORCE_PWM)
+               return -1;
+       *value =
+           max8660_reg_value_table[reg_name - REG_MAX8660_OUTPUT_ENABLE_1];
+       return 0;
+}
+int max8660_save_buffered_reg_val(int reg_name, u8 value)
+{
+
+       /* outof range */
+       if (reg_name < REG_MAX8660_OUTPUT_ENABLE_1
+           || reg_name > REG_MAX8660_FORCE_PWM)
+               return -1;
+       max8660_reg_value_table[reg_name - REG_MAX8660_OUTPUT_ENABLE_1] = value;
+       return 0;
+}
+
+int max8660_write_reg(u8 reg, u8 value)
+{
+       if (max8660_dev_present && (i2c_smbus_write_byte_data(
+               max8660_i2c_client, reg, value) >= 0))
+               return 0;
+       return -1;
+}
+
+/*!
+ * max8660 I2C attach function
+ *
+ * @param adapter            struct i2c_client *
+ * @return  0 for max8660 successfully detected
+ */
+static int max8660_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       int retval;
+       max8660_i2c_client = client;
+       retval = i2c_smbus_write_byte_data(max8660_i2c_client,
+                               MAX8660_OUTPUT_ENABLE_1, 0);
+       if (retval == 0) {
+               max8660_dev_present = 1;
+               pr_info("max8660 probed !\n");
+       } else {
+               max8660_dev_present = 0;
+               pr_info("max8660 not detected!\n");
+       }
+       return retval;
+}
+
+/*!
+ * max8660 I2C detach function
+ *
+ * @param client            struct i2c_client *
+ * @return  0
+ */
+static int max8660_remove(struct i2c_client *client)
+{
+       return 0;
+}
+
+static const struct i2c_device_id max8660_id[] = {
+       { "max8660", 0 },
+       {},
+};
+MODULE_DEVICE_TABLE(i2c, max8660_id);
+
+static struct i2c_driver max8660_i2c_driver = {
+       .driver = {
+                  .owner = THIS_MODULE,
+                  .name = "max8660",},
+       .probe = max8660_probe,
+       .remove = max8660_remove,
+       .id_table = max8660_id,
+};
+
+/* called by pmic core when init*/
+int max8660_init(void)
+{
+       int err;
+       err = i2c_add_driver(&max8660_i2c_driver);
+       return err;
+}
+void max8660_exit(void)
+{
+       i2c_del_driver(&max8660_i2c_driver);
+}
diff --git a/drivers/mxc/mcu_pmic/max8660.h b/drivers/mxc/mcu_pmic/max8660.h
new file mode 100644 (file)
index 0000000..bc32671
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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 max8660.h
+ * @brief Driver for max8660
+ *
+ * @ingroup pmic
+ */
+#ifndef _MAX8660_H_
+#define _MAX8660_H_
+
+#ifdef __KERNEL__
+
+#define MAX8660_OUTPUT_ENABLE_1        0x10
+#define MAX8660_OUTPUT_ENABLE_2        0x12
+#define MAX8660_VOLT_CHANGE_CONTROL            0x20
+#define MAX8660_V3_TARGET_VOLT_1       0x23
+#define MAX8660_V3_TARGET_VOLT_2       0x24
+#define MAX8660_V4_TARGET_VOLT_1       0x29
+#define MAX8660_V4_TARGET_VOLT_2       0x2A
+#define MAX8660_V5_TARGET_VOLT_1       0x32
+#define MAX8660_V5_TARGET_VOLT_2       0x33
+#define MAX8660_V6V7_TARGET_VOLT       0x39
+#define MAX8660_FORCE_PWM                      0x80
+
+int is_max8660_present(void);
+int max8660_write_reg(u8 reg, u8 value);
+int max8660_save_buffered_reg_val(int reg_name, u8 value);
+int max8660_get_buffered_reg_val(int reg_name, u8 *value);
+int max8660_init(void);
+void max8660_exit(void);
+
+extern int reg_max8660_probe(void);
+extern int reg_max8660_remove(void);
+
+#endif                         /* __KERNEL__ */
+
+#endif                         /* _MAX8660_H_ */
diff --git a/drivers/mxc/mcu_pmic/mc9s08dz60.c b/drivers/mxc/mcu_pmic/mc9s08dz60.c
new file mode 100644 (file)
index 0000000..cf0c9a0
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * 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 mc9s08dz60.c
+     * @brief Driver for MC9sdz60
+     *
+     * @ingroup pmic
+     */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/proc_fs.h>
+#include <linux/i2c.h>
+#include <linux/mfd/mc9s08dz60/core.h>
+
+#include <mach/clock.h>
+#include <linux/uaccess.h>
+#include "mc9s08dz60.h"
+
+/* I2C bus id and device address of mcu */
+#define I2C1_BUS       0
+#define MC9S08DZ60_I2C_ADDR    0xD2    /* 7bits I2C address */
+static struct i2c_client *mc9s08dz60_i2c_client;
+
+int mc9s08dz60_read_reg(u8 reg, u8 *value)
+{
+       *value = (u8) i2c_smbus_read_byte_data(mc9s08dz60_i2c_client, reg);
+       return 0;
+}
+
+int mc9s08dz60_write_reg(u8 reg, u8 value)
+{
+       if (i2c_smbus_write_byte_data(mc9s08dz60_i2c_client, reg, value) < 0)
+               return -1;
+       return 0;
+}
+
+static ssize_t mc9s08dz60_show(struct device *dev,
+                           struct device_attribute *attr, char *buf)
+{
+       unsigned int i;
+       u8 value;
+       int offset = 7;
+
+       for (i = 0; i < 7; i++) {
+               mc9s08dz60_read_reg(i, &value);
+               pr_info("reg%02x: %02x\t", i, value);
+               mc9s08dz60_read_reg(i + offset, &value);
+               pr_info("reg%02x: %02x\t", i + offset, value);
+               mc9s08dz60_read_reg(i + offset * 2, &value);
+               pr_info("reg%02x: %02x\t", i + offset * 2, value);
+               mc9s08dz60_read_reg(i + offset * 3, &value);
+               pr_info("reg%02x: %02x\t", i + offset * 3, value);
+               mc9s08dz60_read_reg(i + offset * 4, &value);
+               pr_info("reg%02x: %02x\t", i + offset * 4, value);
+               mc9s08dz60_read_reg(i + offset * 5, &value);
+               pr_info("reg%02x: %02x\n", i + offset * 5, value);
+       }
+
+       return 0;
+}
+
+static ssize_t mc9s08dz60_store(struct device *dev,
+                            struct device_attribute *attr, const char *buf,
+                            size_t count)
+{
+       int ret;
+       unsigned long reg, new_value;
+       u8 value;
+       char *p;
+
+       strict_strtoul(buf, 16, &reg);
+
+       p = NULL;
+       p = memchr(buf, ' ', count);
+
+       if (p == NULL) {
+               mc9s08dz60_read_reg(reg, &value);
+               pr_info("reg%02lu: %06x\n", reg, value);
+               return count;
+       }
+
+       p += 1;
+
+       strict_strtoul(p, 16, &new_value);
+       value = new_value;
+
+       ret = mc9s08dz60_write_reg((u8)reg, value);
+       if (ret == 0)
+               pr_info("write reg%02lx: %06x\n", reg, value);
+       else
+               pr_info("register update failed\n");
+
+       return count;
+}
+
+static struct device_attribute mc9s08dz60_dev_attr = {
+       .attr = {
+                .name = "mc9s08dz60_ctl",
+                .mode = S_IRUSR | S_IWUSR,
+                },
+       .show = mc9s08dz60_show,
+       .store = mc9s08dz60_store,
+};
+
+
+/*!
+ * mc9s08dz60 I2C attach function
+ *
+ * @param adapter            struct i2c_adapter *
+ * @return  0
+ */
+static int mc9s08dz60_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       int ret = 0;
+       struct mc9s08dz60 *mc9s08dz60 = NULL;
+       struct mc9s08dz60_platform_data *plat_data = client->dev.platform_data;
+       pr_info("mc9s08dz60 probing .... \n");
+
+       mc9s08dz60 = kzalloc(sizeof(struct mc9s08dz60), GFP_KERNEL);
+       if (mc9s08dz60 == NULL)
+               return -ENOMEM;
+
+       i2c_set_clientdata(client, mc9s08dz60);
+       mc9s08dz60->dev = &client->dev;
+       mc9s08dz60->i2c_client = client;
+
+       if (plat_data && plat_data->init) {
+               ret = plat_data->init(mc9s08dz60);
+               if (ret != 0)
+                       return -1;
+       }
+
+       ret = device_create_file(&client->dev, &mc9s08dz60_dev_attr);
+       if (ret)
+               dev_err(&client->dev, "create device file failed!\n");
+
+
+       mc9s08dz60_i2c_client = client;
+
+       return 0;
+}
+
+/*!
+ * mc9s08dz60 I2C detach function
+ *
+ * @param client            struct i2c_client *
+ * @return  0
+ */
+static int mc9s08dz60_remove(struct i2c_client *client)
+{
+       return 0;
+}
+
+static const struct i2c_device_id mc9s08dz60_id[] = {
+       { "mc9s08dz60", 0 },
+       {},
+};
+MODULE_DEVICE_TABLE(i2c, mc9s08dz60_id);
+
+static struct i2c_driver mc9s08dz60_i2c_driver = {
+       .driver = {.owner = THIS_MODULE,
+                  .name = "mc9s08dz60",
+                  },
+       .probe = mc9s08dz60_probe,
+       .remove = mc9s08dz60_remove,
+       .id_table = mc9s08dz60_id,
+};
+
+#define SET_BIT_IN_BYTE(byte, pos) (byte |= (0x01 << pos))
+#define CLEAR_BIT_IN_BYTE(byte, pos) (byte &= ~(0x01 << pos))
+
+int mc9s08dz60_init(void)
+{
+       int err;
+       err = i2c_add_driver(&mc9s08dz60_i2c_driver);
+       return err;
+}
+void mc9s08dz60_exit(void)
+{
+       i2c_del_driver(&mc9s08dz60_i2c_driver);
+}
diff --git a/drivers/mxc/mcu_pmic/mc9s08dz60.h b/drivers/mxc/mcu_pmic/mc9s08dz60.h
new file mode 100644 (file)
index 0000000..58e4e81
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * 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 mc9s08dz60.h
+ * @brief Driver for mc9s08dz60
+ *
+ * @ingroup pmic
+ */
+#ifndef _MC9SDZ60_H_
+#define _MC9SDZ60_H_
+
+#define MCU_VERSION    0x00
+/*#define Reserved     0x01*/
+#define MCU_SECS               0x02
+#define MCU_MINS               0x03
+#define MCU_HRS                        0x04
+#define MCU_DAY                        0x05
+#define MCU_DATE               0x06
+#define MCU_MONTH              0x07
+#define MCU_YEAR               0x08
+
+#define MCU_ALARM_SECS 0x09
+#define MCU_ALARM_MINS 0x0A
+#define MCU_ALARM_HRS  0x0B
+/* #define Reserved    0x0C*/
+/* #define Reserved    0x0D*/
+#define MCU_TS_CONTROL 0x0E
+#define MCU_X_LOW      0x0F
+#define MCU_Y_LOW      0x10
+#define MCU_XY_HIGH    0x11
+#define MCU_X_LEFT_LOW 0x12
+#define MCU_X_LEFT_HIGH        0x13
+#define MCU_X_RIGHT    0x14
+#define MCU_Y_TOP_LOW  0x15
+#define MCU_Y_TOP_HIGH 0x16
+#define MCU_Y_BOTTOM   0x17
+/* #define Reserved    0x18*/
+/* #define Reserved    0x19*/
+#define MCU_RESET_1    0x1A
+#define MCU_RESET_2    0x1B
+#define MCU_POWER_CTL  0x1C
+#define MCU_DELAY_CONFIG       0x1D
+/* #define Reserved    0x1E */
+/* #define Reserved    0x1F */
+#define MCU_GPIO_1     0x20
+#define MCU_GPIO_2     0x21
+#define MCU_KPD_1      0x22
+#define MCU_KPD_2      0x23
+#define MCU_KPD_CONTROL        0x24
+#define MCU_INT_ENABLE_1       0x25
+#define MCU_INT_ENABLE_2       0x26
+#define MCU_INT_FLAG_1 0x27
+#define MCU_INT_FLAG_2 0x28
+#define MCU_DES_FLAG           0x29
+int mc9s08dz60_read_reg(u8 reg, u8 *value);
+int mc9s08dz60_write_reg(u8 reg, u8 value);
+int mc9s08dz60_init(void);
+void mc9s08dz60_exit(void);
+
+extern int reg_mc9s08dz60_probe(void);
+extern int reg_mc9s08dz60_remove(void);
+
+#endif /* _MC9SDZ60_H_ */
diff --git a/drivers/mxc/mcu_pmic/mcu_pmic_core.c b/drivers/mxc/mcu_pmic/mcu_pmic_core.c
new file mode 100644 (file)
index 0000000..592f662
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ * 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 mc9s08dz60/mcu_pmic_core.c
+ * @brief This is the main file of mc9s08dz60 Power Control driver.
+ *
+ * @ingroup PMIC_POWER
+ */
+
+/*
+ * Includes
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mfd/mc9s08dz60/pmic.h>
+#include <asm/ioctl.h>
+#include <asm/uaccess.h>
+#include <mach/gpio.h>
+
+#include "mcu_pmic_core.h"
+#include "mc9s08dz60.h"
+#include "max8660.h"
+
+/* bitfield macros for mcu pmic*/
+#define SET_BIT_IN_BYTE(byte, pos) (byte |= (0x01 << pos))
+#define CLEAR_BIT_IN_BYTE(byte, pos) (byte &= ~(0x01 << pos))
+
+
+/* map reg names (enum pmic_reg in pmic_external.h) to real addr*/
+const static u8 mcu_pmic_reg_addr_table[] = {
+       MCU_VERSION,
+       MCU_SECS,
+       MCU_MINS,
+       MCU_HRS,
+       MCU_DAY,
+       MCU_DATE,
+       MCU_MONTH,
+       MCU_YEAR,
+       MCU_ALARM_SECS,
+       MCU_ALARM_MINS,
+       MCU_ALARM_HRS,
+       MCU_TS_CONTROL,
+       MCU_X_LOW,
+       MCU_Y_LOW,
+       MCU_XY_HIGH,
+       MCU_X_LEFT_LOW,
+       MCU_X_LEFT_HIGH,
+       MCU_X_RIGHT,
+       MCU_Y_TOP_LOW,
+       MCU_Y_TOP_HIGH,
+       MCU_Y_BOTTOM,
+       MCU_RESET_1,
+       MCU_RESET_2,
+       MCU_POWER_CTL,
+       MCU_DELAY_CONFIG,
+       MCU_GPIO_1,
+       MCU_GPIO_2,
+       MCU_KPD_1,
+       MCU_KPD_2,
+       MCU_KPD_CONTROL,
+       MCU_INT_ENABLE_1,
+       MCU_INT_ENABLE_2,
+       MCU_INT_FLAG_1,
+       MCU_INT_FLAG_2,
+       MCU_DES_FLAG,
+       MAX8660_OUTPUT_ENABLE_1,
+       MAX8660_OUTPUT_ENABLE_2,
+       MAX8660_VOLT_CHANGE_CONTROL,
+       MAX8660_V3_TARGET_VOLT_1,
+       MAX8660_V3_TARGET_VOLT_2,
+       MAX8660_V4_TARGET_VOLT_1,
+       MAX8660_V4_TARGET_VOLT_2,
+       MAX8660_V5_TARGET_VOLT_1,
+       MAX8660_V5_TARGET_VOLT_2,
+       MAX8660_V6V7_TARGET_VOLT,
+       MAX8660_FORCE_PWM
+};
+
+static int mcu_pmic_read(int reg_num, unsigned int *reg_val)
+{
+       int ret;
+       u8 value = 0;
+       /* mcu ops */
+       if (reg_num >= REG_MCU_VERSION && reg_num <= REG_MCU_DES_FLAG)
+               ret = mc9s08dz60_read_reg(mcu_pmic_reg_addr_table[reg_num],
+                                       &value);
+       else if (reg_num >= REG_MAX8660_OUTPUT_ENABLE_1
+                  && reg_num <= REG_MAX8660_FORCE_PWM)
+               ret = max8660_get_buffered_reg_val(reg_num, &value);
+       else
+               return -1;
+
+       if (ret < 0)
+               return -1;
+       *reg_val = value;
+
+       return 0;
+}
+
+static int mcu_pmic_write(int reg_num, const unsigned int reg_val)
+{
+       int ret;
+       u8 value = reg_val;
+       /* mcu ops */
+       if (reg_num >= REG_MCU_VERSION && reg_num <= REG_MCU_DES_FLAG) {
+
+               ret =
+                   mc9s08dz60_write_reg(
+                       mcu_pmic_reg_addr_table[reg_num], value);
+               if (ret < 0)
+                       return -1;
+       } else if (reg_num >= REG_MAX8660_OUTPUT_ENABLE_1
+                  && reg_num <= REG_MAX8660_FORCE_PWM) {
+               ret =
+                   max8660_write_reg(mcu_pmic_reg_addr_table[reg_num], value);
+
+               if (ret < 0)
+                       return -1;
+
+               ret = max8660_save_buffered_reg_val(reg_num, value);
+       } else
+               return -1;
+
+       return 0;
+}
+
+int mcu_pmic_read_reg(int reg, unsigned int *reg_value,
+                         unsigned int reg_mask)
+{
+       int ret = 0;
+       unsigned int temp = 0;
+
+       ret = mcu_pmic_read(reg, &temp);
+       if (ret != 0)
+               return -1;
+       *reg_value = (temp & reg_mask);
+
+       pr_debug("Read REG[ %d ] = 0x%x\n", reg, *reg_value);
+
+       return ret;
+}
+
+
+int mcu_pmic_write_reg(int reg, unsigned int reg_value,
+                          unsigned int reg_mask)
+{
+       int ret = 0;
+       unsigned int temp = 0;
+
+       ret = mcu_pmic_read(reg, &temp);
+       if (ret != 0)
+               return -1;
+       temp = (temp & (~reg_mask)) | reg_value;
+
+       ret = mcu_pmic_write(reg, temp);
+       if (ret != 0)
+               return -1;
+
+       pr_debug("Write REG[ %d ] = 0x%x\n", reg, reg_value);
+
+       return ret;
+}
+
+/*!
+ * make max8660 - mc9s08dz60 enter low-power mode
+ */
+static void pmic_power_off(void)
+{
+       mcu_pmic_write_reg(REG_MCU_POWER_CTL, 0x10, 0x10);
+}
+
+static int __init mcu_pmic_init(void)
+{
+       int err;
+
+       /* init chips */
+       err = max8660_init();
+       if (err)
+               goto fail1;
+
+       err = mc9s08dz60_init();
+       if (err)
+               goto fail1;
+
+       if (is_max8660_present()) {
+               pr_info("max8660 is present \n");
+               pm_power_off = pmic_power_off;
+       } else
+               pr_debug("max8660 is not present\n");
+       pr_info("mcu_pmic_init completed!\n");
+       return 0;
+
+fail1:
+       pr_err("mcu_pmic_init failed!\n");
+       return err;
+}
+
+static void __exit mcu_pmic_exit(void)
+{
+       reg_max8660_remove();
+       mc9s08dz60_exit();
+       max8660_exit();
+}
+
+subsys_initcall_sync(mcu_pmic_init);
+module_exit(mcu_pmic_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("mcu pmic driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/mcu_pmic/mcu_pmic_core.h b/drivers/mxc/mcu_pmic/mcu_pmic_core.h
new file mode 100644 (file)
index 0000000..2fa5ec4
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * 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
+ */
+
+/*!
+ * @file mcu_pmic_core.h
+ * @brief Driver for max8660
+ *
+ * @ingroup pmic
+ */
+#ifndef _MCU_PMIC_CORE_H_
+#define _MCU_PMIC_CORE_H_
+
+#include <linux/mfd/mc9s08dz60/pmic.h>
+
+#define MAX8660_REG_START (REG_MCU_DES_FLAG + 1)
+enum {
+
+       /* reg names for max8660 */
+       REG_MAX8660_OUTPUT_ENABLE_1 = MAX8660_REG_START,
+       REG_MAX8660_OUTPUT_ENABLE_2,
+       REG_MAX8660_VOLT_CHANGE_CONTROL_1,
+       REG_MAX8660_V3_TARGET_VOLT_1,
+       REG_MAX8660_V3_TARGET_VOLT_2,
+       REG_MAX8660_V4_TARGET_VOLT_1,
+       REG_MAX8660_V4_TARGET_VOLT_2,
+       REG_MAX8660_V5_TARGET_VOLT_1,
+       REG_MAX8660_V5_TARGET_VOLT_2,
+       REG_MAX8660_V6V7_TARGET_VOLT,
+       REG_MAX8660_FORCE_PWM
+};
+
+
+#endif                         /* _MCU_PMIC_CORE_H_ */
diff --git a/drivers/mxc/mcu_pmic/mcu_pmic_gpio.c b/drivers/mxc/mcu_pmic/mcu_pmic_gpio.c
new file mode 100644 (file)
index 0000000..f9cda51
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * 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 mc9s08dz60/mcu_pmic_gpio.c
+ * @brief This is the main file of mc9s08dz60 Power Control driver.
+ *
+ * @ingroup PMIC_POWER
+ */
+
+/*
+ * Includes
+ */
+#include <linux/platform_device.h>
+#include <linux/mfd/mc9s08dz60/pmic.h>
+#include <linux/pmic_status.h>
+#include <linux/ioctl.h>
+
+#define SET_BIT_IN_BYTE(byte, pos) (byte |= (0x01 << pos))
+#define CLEAR_BIT_IN_BYTE(byte, pos) (byte &= ~(0x01 << pos))
+
+int pmic_gpio_set_bit_val(int reg, unsigned int bit,
+                                 unsigned int val)
+{
+       int reg_name;
+       u8 reg_mask = 0;
+
+       if (bit > 7)
+               return -1;
+
+       switch (reg) {
+       case MCU_GPIO_REG_RESET_1:
+               reg_name = REG_MCU_RESET_1;
+               break;
+       case MCU_GPIO_REG_RESET_2:
+               reg_name = REG_MCU_RESET_2;
+               break;
+       case MCU_GPIO_REG_POWER_CONTROL:
+               reg_name = REG_MCU_POWER_CTL;
+               break;
+       case MCU_GPIO_REG_GPIO_CONTROL_1:
+               reg_name = REG_MCU_GPIO_1;
+               break;
+       case MCU_GPIO_REG_GPIO_CONTROL_2:
+               reg_name = REG_MCU_GPIO_2;
+               break;
+       default:
+               return -1;
+       }
+
+       SET_BIT_IN_BYTE(reg_mask, bit);
+       if (0 == val)
+               CHECK_ERROR(mcu_pmic_write_reg(reg_name, 0, reg_mask));
+       else
+               CHECK_ERROR(mcu_pmic_write_reg(reg_name, reg_mask, reg_mask));
+
+       return 0;
+}
+EXPORT_SYMBOL(pmic_gpio_set_bit_val);
+
+int pmic_gpio_get_bit_val(int reg, unsigned int bit,
+                                 unsigned int *val)
+{
+       int reg_name;
+       unsigned int reg_read_val;
+       u8 reg_mask = 0;
+
+       if (bit > 7)
+               return -1;
+
+       switch (reg) {
+       case MCU_GPIO_REG_RESET_1:
+               reg_name = REG_MCU_RESET_1;
+               break;
+       case MCU_GPIO_REG_RESET_2:
+               reg_name = REG_MCU_RESET_2;
+               break;
+       case MCU_GPIO_REG_POWER_CONTROL:
+               reg_name = REG_MCU_POWER_CTL;
+               break;
+       case MCU_GPIO_REG_GPIO_CONTROL_1:
+               reg_name = REG_MCU_GPIO_1;
+               break;
+       case MCU_GPIO_REG_GPIO_CONTROL_2:
+               reg_name = REG_MCU_GPIO_2;
+               break;
+       default:
+               return -1;
+       }
+
+       SET_BIT_IN_BYTE(reg_mask, bit);
+       CHECK_ERROR(mcu_pmic_read_reg(reg_name, &reg_read_val, reg_mask));
+       if (0 == reg_read_val)
+               *val = 0;
+       else
+               *val = 1;
+
+       return 0;
+}
+EXPORT_SYMBOL(pmic_gpio_get_bit_val);
+
+int pmic_gpio_get_designation_bit_val(unsigned int bit,
+                                       unsigned int *val)
+{
+       unsigned int reg_read_val;
+       u8 reg_mask = 0;
+
+       if (bit > 7)
+               return -1;
+
+       SET_BIT_IN_BYTE(reg_mask, bit);
+       CHECK_ERROR(
+               mcu_pmic_read_reg(REG_MCU_DES_FLAG, &reg_read_val, reg_mask));
+       if (0 == reg_read_val)
+               *val = 0;
+       else
+               *val = 1;
+
+       return 0;
+}
+EXPORT_SYMBOL(pmic_gpio_get_designation_bit_val);
diff --git a/drivers/mxc/mlb/Kconfig b/drivers/mxc/mlb/Kconfig
new file mode 100644 (file)
index 0000000..7e3b16c
--- /dev/null
@@ -0,0 +1,13 @@
+#
+# MLB configuration
+#
+
+menu "MXC Media Local Bus Driver"
+
+config MXC_MLB
+       tristate "MLB support"
+       depends on ARCH_MX35 || ARCH_MX53
+       ---help---
+         Say Y to get the MLB support.
+
+endmenu
diff --git a/drivers/mxc/mlb/Makefile b/drivers/mxc/mlb/Makefile
new file mode 100644 (file)
index 0000000..60662eb
--- /dev/null
@@ -0,0 +1,5 @@
+#
+# Makefile for the kernel MLB driver
+#
+
+obj-$(CONFIG_MXC_MLB) += mxc_mlb.o
diff --git a/drivers/mxc/mlb/mxc_mlb.c b/drivers/mxc/mlb/mxc_mlb.c
new file mode 100644 (file)
index 0000000..8ccbf0f
--- /dev/null
@@ -0,0 +1,1060 @@
+/*
+ * linux/drivers/mxc/mlb/mxc_mlb.c
+ *
+ * Copyright (C) 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
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/cdev.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mxc_mlb.h>
+#include <linux/uaccess.h>
+#include <linux/iram_alloc.h>
+#include <linux/fsl_devices.h>
+
+/*!
+ * MLB module memory map registers define
+ */
+#define MLB_REG_DCCR   0x0
+#define MLB_REG_SSCR   0x4
+#define MLB_REG_SDCR   0x8
+#define MLB_REG_SMCR   0xC
+#define MLB_REG_VCCR   0x1C
+#define MLB_REG_SBCR   0x20
+#define MLB_REG_ABCR   0x24
+#define MLB_REG_CBCR   0x28
+#define MLB_REG_IBCR   0x2C
+#define MLB_REG_CICR   0x30
+#define MLB_REG_CECRn  0x40
+#define MLB_REG_CSCRn  0x44
+#define MLB_REG_CCBCRn 0x48
+#define MLB_REG_CNBCRn 0x4C
+#define MLB_REG_LCBCRn 0x280
+
+#define MLB_DCCR_FS_OFFSET     28
+#define MLB_DCCR_EN            (1 << 31)
+#define MLB_DCCR_LBM_OFFSET    30
+#define MLB_DCCR_RESET         (1 << 23)
+#define MLB_CECR_CE            (1 << 31)
+#define MLB_CECR_TR            (1 << 30)
+#define MLB_CECR_CT_OFFSET     28
+#define MLB_CECR_MBS           (1 << 19)
+#define MLB_CSCR_CBPE          (1 << 0)
+#define MLB_CSCR_CBDB          (1 << 1)
+#define MLB_CSCR_CBD           (1 << 2)
+#define MLB_CSCR_CBS           (1 << 3)
+#define MLB_CSCR_BE            (1 << 4)
+#define MLB_CSCR_ABE           (1 << 5)
+#define MLB_CSCR_LFS           (1 << 6)
+#define MLB_CSCR_PBPE          (1 << 8)
+#define MLB_CSCR_PBDB          (1 << 9)
+#define MLB_CSCR_PBD           (1 << 10)
+#define MLB_CSCR_PBS           (1 << 11)
+#define MLB_CSCR_RDY           (1 << 16)
+#define MLB_CSCR_BM            (1 << 31)
+#define MLB_CSCR_BF            (1 << 30)
+#define MLB_SSCR_SDML          (1 << 5)
+
+#define MLB_CONTROL_TX_CHANN           (0 << 4)
+#define MLB_CONTROL_RX_CHANN           (1 << 4)
+#define MLB_ASYNC_TX_CHANN             (2 << 4)
+#define MLB_ASYNC_RX_CHANN             (3 << 4)
+
+#define MLB_MINOR_DEVICES              2
+#define MLB_CONTROL_DEV_NAME           "ctrl"
+#define MLB_ASYNC_DEV_NAME             "async"
+
+#define TX_CHANNEL                     0
+#define RX_CHANNEL                     1
+#define TX_CHANNEL_BUF_SIZE            1024
+#define RX_CHANNEL_BUF_SIZE            (2*1024)
+/* max package data size */
+#define ASYNC_PACKET_SIZE                      1024
+#define CTRL_PACKET_SIZE                       64
+#define RX_RING_NODES                  10
+
+#define MLB_IRAM_SIZE                  (MLB_MINOR_DEVICES * (TX_CHANNEL_BUF_SIZE + RX_CHANNEL_BUF_SIZE))
+#define _get_txchan(dev)               mlb_devinfo[dev].channels[TX_CHANNEL]
+#define _get_rxchan(dev)               mlb_devinfo[dev].channels[RX_CHANNEL]
+
+enum {
+       MLB_CTYPE_SYNC,
+       MLB_CTYPE_ISOC,
+       MLB_CTYPE_ASYNC,
+       MLB_CTYPE_CTRL,
+};
+
+/*!
+ * Rx ring buffer
+ */
+struct mlb_rx_ringnode {
+       int size;
+       char *data;
+};
+
+struct mlb_channel_info {
+
+       /* channel offset in memmap */
+       const unsigned int reg_offset;
+       /* channel address */
+       int address;
+       /*!
+        * channel buffer start address
+        * for Rx, buf_head pointer to a loop ring buffer
+        */
+       unsigned long buf_head;
+       /* physical buffer head address */
+       unsigned long phy_head;
+       /* channel buffer size */
+       unsigned int buf_size;
+       /* channel buffer current ptr */
+       unsigned long buf_ptr;
+       /* buffer spin lock */
+       rwlock_t buf_lock;
+};
+
+struct mlb_dev_info {
+
+       /* device node name */
+       const char dev_name[20];
+       /* channel type */
+       const unsigned int channel_type;
+       /* channel info for tx/rx */
+       struct mlb_channel_info channels[2];
+       /* rx ring buffer */
+       struct mlb_rx_ringnode rx_bufs[RX_RING_NODES];
+       /* rx ring buffer read/write ptr */
+       unsigned int rdpos, wtpos;
+       /* exception event */
+       unsigned long ex_event;
+       /* channel started up or not */
+       atomic_t on;
+       /* device open count */
+       atomic_t opencnt;
+       /* wait queue head for channel */
+       wait_queue_head_t rd_wq;
+       wait_queue_head_t wt_wq;
+       /* spinlock for event access */
+       spinlock_t event_lock;
+};
+
+static struct mlb_dev_info mlb_devinfo[MLB_MINOR_DEVICES] = {
+       {
+        .dev_name = MLB_CONTROL_DEV_NAME,
+        .channel_type = MLB_CTYPE_CTRL,
+        .channels = {
+                     [0] = {
+                            .reg_offset = MLB_CONTROL_TX_CHANN,
+                            .buf_size = TX_CHANNEL_BUF_SIZE,
+                            .buf_lock =
+                            __RW_LOCK_UNLOCKED(mlb_devinfo[0].channels[0].
+                                               buf_lock),
+                            },
+                     [1] = {
+                            .reg_offset = MLB_CONTROL_RX_CHANN,
+                            .buf_size = RX_CHANNEL_BUF_SIZE,
+                            .buf_lock =
+                            __RW_LOCK_UNLOCKED(mlb_devinfo[0].channels[1].
+                                               buf_lock),
+                            },
+                     },
+        .on = ATOMIC_INIT(0),
+        .opencnt = ATOMIC_INIT(0),
+        .rd_wq = __WAIT_QUEUE_HEAD_INITIALIZER(mlb_devinfo[0].rd_wq),
+        .wt_wq = __WAIT_QUEUE_HEAD_INITIALIZER(mlb_devinfo[0].wt_wq),
+        .event_lock = __SPIN_LOCK_UNLOCKED(mlb_devinfo[0].event_lock),
+        },
+       {
+        .dev_name = MLB_ASYNC_DEV_NAME,
+        .channel_type = MLB_CTYPE_ASYNC,
+        .channels = {
+                     [0] = {
+                            .reg_offset = MLB_ASYNC_TX_CHANN,
+                            .buf_size = TX_CHANNEL_BUF_SIZE,
+                            .buf_lock =
+                            __RW_LOCK_UNLOCKED(mlb_devinfo[1].channels[0].
+                                               buf_lock),
+                            },
+                     [1] = {
+                            .reg_offset = MLB_ASYNC_RX_CHANN,
+                            .buf_size = RX_CHANNEL_BUF_SIZE,
+                            .buf_lock =
+                            __RW_LOCK_UNLOCKED(mlb_devinfo[1].channels[1].
+                                               buf_lock),
+                            },
+                     },
+        .on = ATOMIC_INIT(0),
+        .opencnt = ATOMIC_INIT(0),
+        .rd_wq = __WAIT_QUEUE_HEAD_INITIALIZER(mlb_devinfo[1].rd_wq),
+        .wt_wq = __WAIT_QUEUE_HEAD_INITIALIZER(mlb_devinfo[1].wt_wq),
+        .event_lock = __SPIN_LOCK_UNLOCKED(mlb_devinfo[1].event_lock),
+        },
+};
+
+static struct regulator *reg_nvcc;     /* NVCC_MLB regulator */
+static struct clk *mlb_clk;
+static struct cdev mxc_mlb_dev;        /* chareset device */
+static dev_t dev;
+static struct class *mlb_class;        /* device class */
+static struct device *class_dev;
+static unsigned long mlb_base; /* mlb module base address */
+static unsigned int irq;
+static unsigned long iram_base;
+static __iomem void *iram_addr;
+
+/*!
+ * Initial the MLB module device
+ */
+static void mlb_dev_init(void)
+{
+       unsigned long dccr_val;
+       unsigned long phyaddr;
+
+       /* reset the MLB module */
+       __raw_writel(MLB_DCCR_RESET, mlb_base + MLB_REG_DCCR);
+       while (__raw_readl(mlb_base + MLB_REG_DCCR)
+              & MLB_DCCR_RESET)
+               ;
+
+       /*!
+        * Enable MLB device, disable loopback mode,
+        * set default fps to 512, set mlb device address to 0
+        */
+       dccr_val = MLB_DCCR_EN;
+       __raw_writel(dccr_val, mlb_base + MLB_REG_DCCR);
+
+       /* disable all the system interrupt */
+       __raw_writel(0x5F, mlb_base + MLB_REG_SMCR);
+
+       /* write async, control tx/rx base address */
+       phyaddr = _get_txchan(0).phy_head >> 16;
+       __raw_writel(phyaddr << 16 | phyaddr, mlb_base + MLB_REG_CBCR);
+       phyaddr = _get_txchan(1).phy_head >> 16;
+       __raw_writel(phyaddr << 16 | phyaddr, mlb_base + MLB_REG_ABCR);
+
+}
+
+static void mlb_dev_exit(void)
+{
+       __raw_writel(0, mlb_base + MLB_REG_DCCR);
+}
+
+/*!
+ * MLB receive start function
+ *
+ * load phy_head to next buf register to start next rx
+ * here use single-packet buffer, set start=end
+ */
+static void mlb_start_rx(int cdev_id)
+{
+       struct mlb_channel_info *chinfo = &_get_rxchan(cdev_id);
+       unsigned long next;
+
+       next = chinfo->phy_head & 0xFFFC;
+       /* load next buf */
+       __raw_writel((next << 16) | next, mlb_base +
+                    MLB_REG_CNBCRn + chinfo->reg_offset);
+       /* set ready bit to start next rx */
+       __raw_writel(MLB_CSCR_RDY, mlb_base + MLB_REG_CSCRn
+                    + chinfo->reg_offset);
+}
+
+/*!
+ * MLB transmit start function
+ * make sure aquiring the rw buf_lock, when calling this
+ */
+static void mlb_start_tx(int cdev_id)
+{
+       struct mlb_channel_info *chinfo = &_get_txchan(cdev_id);
+       unsigned long begin, end;
+
+       begin = chinfo->phy_head;
+       end = (chinfo->phy_head + chinfo->buf_ptr - chinfo->buf_head) & 0xFFFC;
+       /* load next buf */
+       __raw_writel((begin << 16) | end, mlb_base +
+                    MLB_REG_CNBCRn + chinfo->reg_offset);
+       /* set ready bit to start next tx */
+       __raw_writel(MLB_CSCR_RDY, mlb_base + MLB_REG_CSCRn
+                    + chinfo->reg_offset);
+}
+
+/*!
+ * Enable the MLB channel
+ */
+static void mlb_channel_enable(int chan_dev_id, int on)
+{
+       unsigned long tx_regval = 0, rx_regval = 0;
+       /*!
+        * setup the direction, enable, channel type,
+        * mode select, channel address and mask buf start
+        */
+       if (on) {
+               unsigned int ctype = mlb_devinfo[chan_dev_id].channel_type;
+               tx_regval = MLB_CECR_CE | MLB_CECR_TR | MLB_CECR_MBS |
+                   (ctype << MLB_CECR_CT_OFFSET) |
+                   _get_txchan(chan_dev_id).address;
+               rx_regval = MLB_CECR_CE | MLB_CECR_MBS |
+                   (ctype << MLB_CECR_CT_OFFSET) |
+                   _get_rxchan(chan_dev_id).address;
+
+               atomic_set(&mlb_devinfo[chan_dev_id].on, 1);
+       } else {
+               atomic_set(&mlb_devinfo[chan_dev_id].on, 0);
+       }
+
+       /* update the rx/tx channel entry config */
+       __raw_writel(tx_regval, mlb_base + MLB_REG_CECRn +
+                    _get_txchan(chan_dev_id).reg_offset);
+       __raw_writel(rx_regval, mlb_base + MLB_REG_CECRn +
+                    _get_rxchan(chan_dev_id).reg_offset);
+
+       if (on)
+               mlb_start_rx(chan_dev_id);
+}
+
+/*!
+ * MLB interrupt handler
+ */
+void mlb_tx_isr(int minor, unsigned int cis)
+{
+       struct mlb_channel_info *chinfo = &_get_txchan(minor);
+
+       if (cis & MLB_CSCR_CBD) {
+               /* buffer done, reset the buf_ptr */
+               write_lock(&chinfo->buf_lock);
+               chinfo->buf_ptr = chinfo->buf_head;
+               write_unlock(&chinfo->buf_lock);
+               /* wake up the writer */
+               wake_up_interruptible(&mlb_devinfo[minor].wt_wq);
+       }
+}
+
+void mlb_rx_isr(int minor, unsigned int cis)
+{
+       struct mlb_channel_info *chinfo = &_get_rxchan(minor);
+       unsigned long end;
+       unsigned int len;
+
+       if (cis & MLB_CSCR_CBD) {
+
+               int wpos, rpos;
+
+               rpos = mlb_devinfo[minor].rdpos;
+               wpos = mlb_devinfo[minor].wtpos;
+
+               /* buffer done, get current buffer ptr */
+               end =
+                   __raw_readl(mlb_base + MLB_REG_CCBCRn + chinfo->reg_offset);
+               end >>= 16;     /* end here is phy */
+               len = end - (chinfo->phy_head & 0xFFFC);
+
+               /*!
+                * copy packet from IRAM buf to ring buf.
+                * if the wpos++ == rpos, drop this packet
+                */
+               if (((wpos + 1) % RX_RING_NODES) != rpos) {
+
+#ifdef DEBUG
+                       if (mlb_devinfo[minor].channel_type == MLB_CTYPE_CTRL) {
+                               if (len > CTRL_PACKET_SIZE)
+                                       pr_debug
+                                           ("mxc_mlb: ctrl packet"
+                                            "overflow\n");
+                       } else {
+                               if (len > ASYNC_PACKET_SIZE)
+                                       pr_debug
+                                           ("mxc_mlb: async packet"
+                                            "overflow\n");
+                       }
+#endif
+                       memcpy(mlb_devinfo[minor].rx_bufs[wpos].data,
+                              (const void *)chinfo->buf_head, len);
+                       mlb_devinfo[minor].rx_bufs[wpos].size = len;
+
+                       /* update the ring wpos */
+                       mlb_devinfo[minor].wtpos = (wpos + 1) % RX_RING_NODES;
+
+                       /* wake up the reader */
+                       wake_up_interruptible(&mlb_devinfo[minor].rd_wq);
+
+                       pr_debug("recv package, len:%d, rdpos: %d, wtpos: %d\n",
+                                len, rpos, mlb_devinfo[minor].wtpos);
+               } else {
+                       pr_debug
+                           ("drop package, due to no space, (%d,%d)\n",
+                            rpos, mlb_devinfo[minor].wtpos);
+               }
+
+               /* start next rx */
+               mlb_start_rx(minor);
+       }
+}
+
+static irqreturn_t mlb_isr(int irq, void *dev_id)
+{
+       unsigned long int_status, sscr, tx_cis, rx_cis;
+       struct mlb_dev_info *pdev;
+       int minor;
+
+       sscr = __raw_readl(mlb_base + MLB_REG_SSCR);
+       pr_debug("mxc_mlb: system interrupt:%lx\n", sscr);
+       __raw_writel(0x7F, mlb_base + MLB_REG_SSCR);
+
+       int_status = __raw_readl(mlb_base + MLB_REG_CICR) & 0xFFFF;
+       pr_debug("mxc_mlb: channel interrupt ids: %lx\n", int_status);
+
+       for (minor = 0; minor < MLB_MINOR_DEVICES; minor++) {
+
+               pdev = &mlb_devinfo[minor];
+               tx_cis = rx_cis = 0;
+
+               /* get tx channel interrupt status */
+               if (int_status & (1 << (_get_txchan(minor).reg_offset >> 4)))
+                       tx_cis = __raw_readl(mlb_base + MLB_REG_CSCRn
+                                            + _get_txchan(minor).reg_offset);
+               /* get rx channel interrupt status */
+               if (int_status & (1 << (_get_rxchan(minor).reg_offset >> 4)))
+                       rx_cis = __raw_readl(mlb_base + MLB_REG_CSCRn
+                                            + _get_rxchan(minor).reg_offset);
+
+               if (!tx_cis && !rx_cis)
+                       continue;
+
+               pr_debug("tx/rx int status: 0x%08lx/0x%08lx\n", tx_cis, rx_cis);
+               /* fill exception event */
+               spin_lock(&pdev->event_lock);
+               pdev->ex_event |= tx_cis & 0x303;
+               pdev->ex_event |= (rx_cis & 0x303) << 16;
+               spin_unlock(&pdev->event_lock);
+
+               /* clear the interrupt status */
+               __raw_writel(tx_cis & 0xFFFF, mlb_base + MLB_REG_CSCRn
+                            + _get_txchan(minor).reg_offset);
+               __raw_writel(rx_cis & 0xFFFF, mlb_base + MLB_REG_CSCRn
+                            + _get_rxchan(minor).reg_offset);
+
+               /* handel tx channel */
+               if (tx_cis)
+                       mlb_tx_isr(minor, tx_cis);
+               /* handle rx channel */
+               if (rx_cis)
+                       mlb_rx_isr(minor, rx_cis);
+
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int mxc_mlb_open(struct inode *inode, struct file *filp)
+{
+       int minor;
+
+       minor = MINOR(inode->i_rdev);
+
+       if (minor < 0 || minor >= MLB_MINOR_DEVICES)
+               return -ENODEV;
+
+       /* open for each channel device */
+       if (atomic_cmpxchg(&mlb_devinfo[minor].opencnt, 0, 1) != 0)
+               return -EBUSY;
+
+       /* reset the buffer read/write ptr */
+       _get_txchan(minor).buf_ptr = _get_txchan(minor).buf_head;
+       _get_rxchan(minor).buf_ptr = _get_rxchan(minor).buf_head;
+       mlb_devinfo[minor].rdpos = mlb_devinfo[minor].wtpos = 0;
+       mlb_devinfo[minor].ex_event = 0;
+
+       return 0;
+}
+
+static int mxc_mlb_release(struct inode *inode, struct file *filp)
+{
+       int minor;
+
+       minor = MINOR(inode->i_rdev);
+
+       /* clear channel settings and info */
+       mlb_channel_enable(minor, 0);
+
+       /* decrease the open count */
+       atomic_set(&mlb_devinfo[minor].opencnt, 0);
+
+       return 0;
+}
+
+static int mxc_mlb_ioctl(struct inode *inode, struct file *filp,
+                        unsigned int cmd, unsigned long arg)
+{
+       void __user *argp = (void __user *)arg;
+       unsigned long flags, event;
+       int minor;
+
+       minor = MINOR(inode->i_rdev);
+
+       switch (cmd) {
+
+       case MLB_CHAN_SETADDR:
+               {
+                       unsigned int caddr;
+                       /* get channel address from user space */
+                       if (copy_from_user(&caddr, argp, sizeof(caddr))) {
+                               pr_err("mxc_mlb: copy from user failed\n");
+                               return -EFAULT;
+                       }
+                       _get_txchan(minor).address = (caddr >> 16) & 0xFFFF;
+                       _get_rxchan(minor).address = caddr & 0xFFFF;
+                       break;
+               }
+
+       case MLB_CHAN_STARTUP:
+               if (atomic_read(&mlb_devinfo[minor].on)) {
+                       pr_debug("mxc_mlb: channel areadly startup\n");
+                       break;
+               }
+               mlb_channel_enable(minor, 1);
+               break;
+       case MLB_CHAN_SHUTDOWN:
+               if (atomic_read(&mlb_devinfo[minor].on) == 0) {
+                       pr_debug("mxc_mlb: channel areadly shutdown\n");
+                       break;
+               }
+               mlb_channel_enable(minor, 0);
+               break;
+       case MLB_CHAN_GETEVENT:
+               /* get and clear the ex_event */
+               spin_lock_irqsave(&mlb_devinfo[minor].event_lock, flags);
+               event = mlb_devinfo[minor].ex_event;
+               mlb_devinfo[minor].ex_event = 0;
+               spin_unlock_irqrestore(&mlb_devinfo[minor].event_lock, flags);
+
+               if (event) {
+                       if (copy_to_user(argp, &event, sizeof(event))) {
+                               pr_err("mxc_mlb: copy to user failed\n");
+                               return -EFAULT;
+                       }
+               } else {
+                       pr_debug("mxc_mlb: no exception event now\n");
+                       return -EAGAIN;
+               }
+               break;
+       case MLB_SET_FPS:
+               {
+                       unsigned int fps;
+                       unsigned long dccr_val;
+
+                       /* get fps from user space */
+                       if (copy_from_user(&fps, argp, sizeof(fps))) {
+                               pr_err("mxc_mlb: copy from user failed\n");
+                               return -EFAULT;
+                       }
+
+                       /* check fps value */
+                       if (fps != 256 && fps != 512 && fps != 1024) {
+                               pr_debug("mxc_mlb: invalid fps argument\n");
+                               return -EINVAL;
+                       }
+
+                       dccr_val = __raw_readl(mlb_base + MLB_REG_DCCR);
+                       dccr_val &= ~(0x3 << MLB_DCCR_FS_OFFSET);
+                       dccr_val |= (fps >> 9) << MLB_DCCR_FS_OFFSET;
+                       __raw_writel(dccr_val, mlb_base + MLB_REG_DCCR);
+                       break;
+               }
+
+       case MLB_GET_VER:
+               {
+                       unsigned long version;
+
+                       /* get MLB device module version */
+                       version = __raw_readl(mlb_base + MLB_REG_VCCR);
+
+                       if (copy_to_user(argp, &version, sizeof(version))) {
+                               pr_err("mxc_mlb: copy to user failed\n");
+                               return -EFAULT;
+                       }
+                       break;
+               }
+
+       case MLB_SET_DEVADDR:
+               {
+                       unsigned long dccr_val;
+                       unsigned char devaddr;
+
+                       /* get MLB device address from user space */
+                       if (copy_from_user
+                           (&devaddr, argp, sizeof(unsigned char))) {
+                               pr_err("mxc_mlb: copy from user failed\n");
+                               return -EFAULT;
+                       }
+
+                       dccr_val = __raw_readl(mlb_base + MLB_REG_DCCR);
+                       dccr_val &= ~0xFF;
+                       dccr_val |= devaddr;
+                       __raw_writel(dccr_val, mlb_base + MLB_REG_DCCR);
+
+                       break;
+               }
+       default:
+               pr_info("mxc_mlb: Invalid ioctl command\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/*!
+ * MLB read routine
+ *
+ * Read the current received data from queued buffer,
+ * and free this buffer for hw to fill ingress data.
+ */
+static ssize_t mxc_mlb_read(struct file *filp, char __user *buf,
+                           size_t count, loff_t *f_pos)
+{
+       int minor, ret;
+       int size, rdpos;
+       struct mlb_rx_ringnode *rxbuf;
+
+       minor = MINOR(filp->f_dentry->d_inode->i_rdev);
+
+       rdpos = mlb_devinfo[minor].rdpos;
+       rxbuf = mlb_devinfo[minor].rx_bufs;
+
+       /* check the current rx buffer is available or not */
+       if (rdpos == mlb_devinfo[minor].wtpos) {
+               if (filp->f_flags & O_NONBLOCK)
+                       return -EAGAIN;
+               /* if !O_NONBLOCK, we wait for recv packet */
+               ret = wait_event_interruptible(mlb_devinfo[minor].rd_wq,
+                                               (mlb_devinfo[minor].wtpos !=
+                                               rdpos));
+               if (ret < 0)
+                       return ret;
+       }
+
+       size = rxbuf[rdpos].size;
+       if (size > count) {
+               /* the user buffer is too small */
+               pr_warning
+                   ("mxc_mlb: received data size is bigger than count\n");
+               return -EINVAL;
+       }
+
+       /* copy rx buffer data to user buffer */
+       if (copy_to_user(buf, rxbuf[rdpos].data, size)) {
+               pr_err("mxc_mlb: copy from user failed\n");
+               return -EFAULT;
+       }
+
+       /* update the read ptr */
+       mlb_devinfo[minor].rdpos = (rdpos + 1) % RX_RING_NODES;
+
+       *f_pos = 0;
+
+       return size;
+}
+
+/*!
+ * MLB write routine
+ *
+ * Copy the user data to tx channel buffer,
+ * and prepare the channel current/next buffer ptr.
+ */
+static ssize_t mxc_mlb_write(struct file *filp, const char __user *buf,
+                            size_t count, loff_t *f_pos)
+{
+       int minor;
+       unsigned long flags;
+       DEFINE_WAIT(__wait);
+       int ret;
+
+       minor = MINOR(filp->f_dentry->d_inode->i_rdev);
+
+       if (count > _get_txchan(minor).buf_size) {
+               /* too many data to write */
+               pr_warning("mxc_mlb: overflow write data\n");
+               return -EFBIG;
+       }
+
+       *f_pos = 0;
+
+       /* check the current tx buffer is used or not */
+       write_lock_irqsave(&_get_txchan(minor).buf_lock, flags);
+       if (_get_txchan(minor).buf_ptr != _get_txchan(minor).buf_head) {
+               write_unlock_irqrestore(&_get_txchan(minor).buf_lock, flags);
+
+               /* there's already some datas being transmit now */
+               if (filp->f_flags & O_NONBLOCK)
+                       return -EAGAIN;
+
+               /* if !O_NONBLOCK, we wait for transmit finish */
+               for (;;) {
+                       prepare_to_wait(&mlb_devinfo[minor].wt_wq,
+                                       &__wait, TASK_INTERRUPTIBLE);
+
+                       write_lock_irqsave(&_get_txchan(minor).buf_lock, flags);
+                       if (_get_txchan(minor).buf_ptr ==
+                           _get_txchan(minor).buf_head)
+                               break;
+
+                       write_unlock_irqrestore(&_get_txchan(minor).buf_lock,
+                                               flags);
+                       if (!signal_pending(current)) {
+                               schedule();
+                               continue;
+                       }
+                       return -ERESTARTSYS;
+               }
+               finish_wait(&mlb_devinfo[minor].wt_wq, &__wait);
+       }
+
+       /* copy user buffer to tx buffer */
+       if (copy_from_user((void *)_get_txchan(minor).buf_ptr, buf, count)) {
+               pr_err("mxc_mlb: copy from user failed\n");
+               ret = -EFAULT;
+               goto out;
+       }
+       _get_txchan(minor).buf_ptr += count;
+
+       /* set current/next buffer start/end */
+       mlb_start_tx(minor);
+
+       ret = count;
+
+out:
+       write_unlock_irqrestore(&_get_txchan(minor).buf_lock, flags);
+       return ret;
+}
+
+static unsigned int mxc_mlb_poll(struct file *filp,
+                                struct poll_table_struct *wait)
+{
+       int minor;
+       unsigned int ret = 0;
+       unsigned long flags;
+
+       minor = MINOR(filp->f_dentry->d_inode->i_rdev);
+
+       poll_wait(filp, &mlb_devinfo[minor].rd_wq, wait);
+       poll_wait(filp, &mlb_devinfo[minor].wt_wq, wait);
+
+       /* check the tx buffer is avaiable or not */
+       read_lock_irqsave(&_get_txchan(minor).buf_lock, flags);
+       if (_get_txchan(minor).buf_ptr == _get_txchan(minor).buf_head)
+               ret |= POLLOUT | POLLWRNORM;
+       read_unlock_irqrestore(&_get_txchan(minor).buf_lock, flags);
+
+       /* check the rx buffer filled or not */
+       if (mlb_devinfo[minor].rdpos != mlb_devinfo[minor].wtpos)
+               ret |= POLLIN | POLLRDNORM;
+
+       /* check the exception event */
+       if (mlb_devinfo[minor].ex_event)
+               ret |= POLLIN | POLLRDNORM;
+
+       return ret;
+}
+
+/*!
+ * char dev file operations structure
+ */
+static struct file_operations mxc_mlb_fops = {
+
+       .owner = THIS_MODULE,
+       .open = mxc_mlb_open,
+       .release = mxc_mlb_release,
+       .ioctl = mxc_mlb_ioctl,
+       .poll = mxc_mlb_poll,
+       .read = mxc_mlb_read,
+       .write = mxc_mlb_write,
+};
+
+/*!
+ * This function is called whenever the MLB device is detected.
+ */
+static int __devinit mxc_mlb_probe(struct platform_device *pdev)
+{
+       int ret, mlb_major, i, j;
+       struct mxc_mlb_platform_data *plat_data;
+       struct resource *res;
+       void __iomem *base, *bufaddr;
+       unsigned long phyaddr;
+
+       /* malloc the Rx ring buffer firstly */
+       for (i = 0; i < MLB_MINOR_DEVICES; i++) {
+               char *buf;
+               int bufsize;
+
+               if (mlb_devinfo[i].channel_type == MLB_CTYPE_ASYNC)
+                       bufsize = ASYNC_PACKET_SIZE;
+               else
+                       bufsize = CTRL_PACKET_SIZE;
+
+               buf = kmalloc(bufsize * RX_RING_NODES, GFP_KERNEL);
+               if (buf == NULL) {
+                       ret = -ENOMEM;
+                       dev_err(&pdev->dev, "can not alloc rx buffers\n");
+                       goto err4;
+               }
+               for (j = 0; j < RX_RING_NODES; j++) {
+                       mlb_devinfo[i].rx_bufs[j].data = buf;
+                       buf += bufsize;
+               }
+       }
+
+       /**
+        * Register MLB lld as two character devices
+        * One for Packet date channel, the other for control data channel
+        */
+       ret = alloc_chrdev_region(&dev, 0, MLB_MINOR_DEVICES, "mxc_mlb");
+       mlb_major = MAJOR(dev);
+
+       if (ret < 0) {
+               dev_err(&pdev->dev, "can't get major %d\n", mlb_major);
+               goto err3;
+       }
+
+       cdev_init(&mxc_mlb_dev, &mxc_mlb_fops);
+       mxc_mlb_dev.owner = THIS_MODULE;
+
+       ret = cdev_add(&mxc_mlb_dev, dev, MLB_MINOR_DEVICES);
+       if (ret) {
+               dev_err(&pdev->dev, "can't add cdev\n");
+               goto err2;
+       }
+
+       /* create class and device for udev information */
+       mlb_class = class_create(THIS_MODULE, "mlb");
+       if (IS_ERR(mlb_class)) {
+               dev_err(&pdev->dev, "failed to create mlb class\n");
+               ret = -ENOMEM;
+               goto err2;
+       }
+
+       for (i = 0; i < MLB_MINOR_DEVICES; i++) {
+
+               class_dev = device_create(mlb_class, NULL, MKDEV(mlb_major, i),
+                                         NULL, mlb_devinfo[i].dev_name);
+               if (IS_ERR(class_dev)) {
+                       dev_err(&pdev->dev, "failed to create mlb %s"
+                               " class device\n", mlb_devinfo[i].dev_name);
+                       ret = -ENOMEM;
+                       goto err1;
+               }
+       }
+
+       /* get irq line */
+       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (res == NULL) {
+               dev_err(&pdev->dev, "No mlb irq line provided\n");
+               goto err1;
+       }
+
+       irq = res->start;
+       /* request irq */
+       if (request_irq(irq, mlb_isr, 0, "mlb", NULL)) {
+               dev_err(&pdev->dev, "failed to request irq\n");
+               ret = -EBUSY;
+               goto err1;
+       }
+
+       /* ioremap from phy mlb to kernel space */
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (res == NULL) {
+               dev_err(&pdev->dev, "No mlb base address provided\n");
+               goto err0;
+       }
+
+       base = ioremap(res->start, res->end - res->start);
+       dev_dbg(&pdev->dev, "mapped mlb base address: 0x%08x\n",
+               (unsigned int)base);
+
+       if (base == NULL) {
+               dev_err(&pdev->dev, "failed to do ioremap with mlb base\n");
+               goto err0;
+       }
+       mlb_base = (unsigned long)base;
+
+       /*!
+        * get rx/tx buffer address from platform data
+        * make sure the buf_address is 4bytes aligned
+        *
+        * -------------------   <-- plat_data->buf_address
+        * | minor 0 tx buf  |
+        *  -----------------
+        * | minor 0 rx buf  |
+        *  -----------------
+        * |    ....         |
+        *  -----------------
+        * | minor n tx buf  |
+        *  -----------------
+        * | minor n rx buf  |
+        * -------------------
+        */
+
+       plat_data = (struct mxc_mlb_platform_data *)pdev->dev.platform_data;
+
+       bufaddr = iram_addr = iram_alloc(MLB_IRAM_SIZE, &iram_base);
+       phyaddr = iram_base;
+
+       for (i = 0; i < MLB_MINOR_DEVICES; i++) {
+               /* set the virtual and physical buf head address */
+               _get_txchan(i).buf_head = bufaddr;
+               _get_txchan(i).phy_head = phyaddr;
+
+               bufaddr += TX_CHANNEL_BUF_SIZE;
+               phyaddr += TX_CHANNEL_BUF_SIZE;
+
+               _get_rxchan(i).buf_head = (unsigned long)bufaddr;
+               _get_rxchan(i).phy_head = phyaddr;
+
+               bufaddr += RX_CHANNEL_BUF_SIZE;
+               phyaddr += RX_CHANNEL_BUF_SIZE;
+
+               dev_dbg(&pdev->dev, "phy_head: tx(%lx), rx(%lx)\n",
+                       _get_txchan(i).phy_head, _get_rxchan(i).phy_head);
+               dev_dbg(&pdev->dev, "buf_head: tx(%lx), rx(%lx)\n",
+                       _get_txchan(i).buf_head, _get_rxchan(i).buf_head);
+       }
+
+       /* enable GPIO */
+       gpio_mlb_active();
+
+       if (plat_data->reg_nvcc) {
+               /* power on MLB */
+               reg_nvcc = regulator_get(&pdev->dev, plat_data->reg_nvcc);
+               if (!IS_ERR(reg_nvcc)) {
+                       /* set MAX LDO6 for NVCC to 2.5V */
+                       regulator_set_voltage(reg_nvcc, 2500000, 2500000);
+                       regulator_enable(reg_nvcc);
+               }
+       }
+
+       /* enable clock */
+       mlb_clk = clk_get(&pdev->dev, plat_data->mlb_clk);
+       clk_enable(mlb_clk);
+
+       /* initial MLB module */
+       mlb_dev_init();
+
+       return 0;
+
+err0:
+       free_irq(irq, NULL);
+err1:
+       for (--i; i >= 0; i--)
+               device_destroy(mlb_class, MKDEV(mlb_major, i));
+
+       class_destroy(mlb_class);
+err2:
+       cdev_del(&mxc_mlb_dev);
+err3:
+       unregister_chrdev_region(dev, MLB_MINOR_DEVICES);
+err4:
+       for (i = 0; i < MLB_MINOR_DEVICES; i++)
+               kfree(mlb_devinfo[i].rx_bufs[0].data);
+
+       return ret;
+}
+
+static int __devexit mxc_mlb_remove(struct platform_device *pdev)
+{
+       int i;
+
+       mlb_dev_exit();
+
+       /* disable mlb clock */
+       clk_disable(mlb_clk);
+       clk_put(mlb_clk);
+
+       /* disable mlb power */
+       regulator_disable(reg_nvcc);
+       regulator_put(reg_nvcc);
+
+       /* inactive GPIO */
+       gpio_mlb_inactive();
+
+       iram_free(iram_base, MLB_IRAM_SIZE);
+
+       /* iounmap */
+       iounmap((void *)mlb_base);
+
+       free_irq(irq, NULL);
+
+       /* destroy mlb device class */
+       for (i = MLB_MINOR_DEVICES - 1; i >= 0; i--)
+               device_destroy(mlb_class, MKDEV(MAJOR(dev), i));
+       class_destroy(mlb_class);
+
+       /* Unregister the two MLB devices */
+       cdev_del(&mxc_mlb_dev);
+       unregister_chrdev_region(dev, MLB_MINOR_DEVICES);
+
+       for (i = 0; i < MLB_MINOR_DEVICES; i++)
+               kfree(mlb_devinfo[i].rx_bufs[0].data);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int mxc_mlb_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       return 0;
+}
+
+static int mxc_mlb_resume(struct platform_device *pdev)
+{
+       return 0;
+}
+#else
+#define mxc_mlb_suspend NULL
+#define mxc_mlb_resume NULL
+#endif
+
+/*!
+ * platform driver structure for MLB
+ */
+static struct platform_driver mxc_mlb_driver = {
+       .driver = {
+                  .name = "mxc_mlb"},
+       .probe = mxc_mlb_probe,
+       .remove = __devexit_p(mxc_mlb_remove),
+       .suspend = mxc_mlb_suspend,
+       .resume = mxc_mlb_resume,
+};
+
+static int __init mxc_mlb_init(void)
+{
+       return platform_driver_register(&mxc_mlb_driver);
+}
+
+static void __exit mxc_mlb_exit(void)
+{
+       platform_driver_unregister(&mxc_mlb_driver);
+}
+
+module_init(mxc_mlb_init);
+module_exit(mxc_mlb_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MLB low level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pmic/Kconfig b/drivers/mxc/pmic/Kconfig
new file mode 100644 (file)
index 0000000..4ee9111
--- /dev/null
@@ -0,0 +1,64 @@
+#
+# PMIC device driver configuration
+#
+
+menu "MXC PMIC support"
+
+config MXC_PMIC
+       boolean
+
+config MXC_PMIC_MC13783
+       tristate "MC13783 PMIC"
+       depends on ARCH_MXC && SPI
+       select MXC_PMIC
+       ---help---
+         This is the MXC MC13783(PMIC) support. It include
+         ADC, Audio, Battery, Connectivity, Light, Power and RTC.
+
+config MXC_PMIC_MC13892
+       tristate "MC13892 PMIC"
+       depends on ARCH_MXC && (I2C || SPI)
+       select MXC_PMIC
+       ---help---
+         This is the MXC MC13892(PMIC) support. It include
+         ADC, Battery, Connectivity, Light, Power and RTC.
+
+config MXC_PMIC_I2C
+       bool "Support PMIC I2C Interface"
+       depends on MXC_PMIC_MC13892 && I2C
+
+config MXC_PMIC_SPI
+       bool "Support PMIC SPI Interface"
+       depends on (MXC_PMIC_MC13892 || MXC_PMIC_MC13783) && SPI
+
+config MXC_PMIC_MC34704
+       tristate "MC34704 PMIC"
+       depends on ARCH_MXC && I2C
+       select MXC_PMIC
+         ---help---
+         This is the MXC MC34704 PMIC support.
+
+config MXC_PMIC_MC9SDZ60
+       tristate "MC9sDZ60 PMIC"
+       depends on ARCH_MXC && I2C
+       select MXC_PMIC
+         ---help---
+         This is the MXC MC9sDZ60(MCU) PMIC support.
+
+config MXC_PMIC_CHARDEV
+       tristate "MXC PMIC device interface"
+       depends on MXC_PMIC
+       help
+         Say Y here to use "pmic" device files, found in the /dev directory
+         on the system.  They make it possible to have user-space programs
+         use or controll PMIC. Mainly its useful for notifying PMIC events
+         to user-space programs.
+
+comment "MXC PMIC Client Drivers"
+       depends on MXC_PMIC
+
+source "drivers/mxc/pmic/mc13783/Kconfig"
+
+source "drivers/mxc/pmic/mc13892/Kconfig"
+
+endmenu
diff --git a/drivers/mxc/pmic/Makefile b/drivers/mxc/pmic/Makefile
new file mode 100644 (file)
index 0000000..385c07e
--- /dev/null
@@ -0,0 +1,7 @@
+#
+# Makefile for the MXC PMIC drivers.
+#
+
+obj-y                          += core/
+obj-$(CONFIG_MXC_PMIC_MC13783) += mc13783/
+obj-$(CONFIG_MXC_PMIC_MC13892) += mc13892/
diff --git a/drivers/mxc/pmic/core/Makefile b/drivers/mxc/pmic/core/Makefile
new file mode 100644 (file)
index 0000000..bb42231
--- /dev/null
@@ -0,0 +1,21 @@
+#
+# Makefile for the PMIC core drivers.
+#
+obj-$(CONFIG_MXC_PMIC_MC13783) += pmic_mc13783_mod.o
+pmic_mc13783_mod-objs := pmic_external.o pmic_event.o pmic_common.o pmic_core_spi.o mc13783.o
+
+obj-$(CONFIG_MXC_PMIC_MC13892) += pmic_mc13892_mod.o
+pmic_mc13892_mod-objs := pmic_external.o pmic_event.o pmic_common.o mc13892.o
+
+ifneq ($(CONFIG_MXC_PMIC_SPI),)
+pmic_mc13892_mod-objs += pmic_core_spi.o
+endif
+
+ifneq ($(CONFIG_MXC_PMIC_I2C),)
+pmic_mc13892_mod-objs += pmic_core_i2c.o
+endif
+
+obj-$(CONFIG_MXC_PMIC_MC34704) += pmic_mc34704_mod.o
+pmic_mc34704_mod-objs := pmic_external.o pmic_event.o mc34704.o
+
+obj-$(CONFIG_MXC_PMIC_CHARDEV) += pmic-dev.o
diff --git a/drivers/mxc/pmic/core/mc13783.c b/drivers/mxc/pmic/core/mc13783.c
new file mode 100644 (file)
index 0000000..4d64ab7
--- /dev/null
@@ -0,0 +1,380 @@
+/*
+ * 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 pmic/core/mc13783.c
+ * @brief This file contains MC13783 specific PMIC code. This implementaion
+ * may differ for each PMIC chip.
+ *
+ * @ingroup PMIC_CORE
+ */
+
+/*
+ * Includes
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/pmic_external.h>
+#include <linux/pmic_status.h>
+#include <linux/spi/spi.h>
+#include <linux/mfd/mc13783/core.h>
+
+#include <asm/uaccess.h>
+
+#include "pmic.h"
+
+/*
+ * Defines
+ */
+#define EVENT_MASK_0                   0x697fdf
+#define EVENT_MASK_1                   0x3efffb
+#define MXC_PMIC_FRAME_MASK            0x00FFFFFF
+#define MXC_PMIC_MAX_REG_NUM           0x3F
+#define MXC_PMIC_REG_NUM_SHIFT         0x19
+#define MXC_PMIC_WRITE_BIT_SHIFT       31
+
+static unsigned int events_enabled0;
+static unsigned int events_enabled1;
+struct mxc_pmic pmic_drv_data;
+
+/*!
+ * This function is called to read a register on PMIC.
+ *
+ * @param        reg_num     number of the pmic register to be read
+ * @param        reg_val   return value of register
+ *
+ * @return       Returns 0 on success -1 on failure.
+ */
+int pmic_read(unsigned int reg_num, unsigned int *reg_val)
+{
+       unsigned int frame = 0;
+       int ret = 0;
+
+       if (reg_num > MXC_PMIC_MAX_REG_NUM)
+               return PMIC_ERROR;
+
+       frame |= reg_num << MXC_PMIC_REG_NUM_SHIFT;
+
+       ret = spi_rw(pmic_drv_data.spi, (u8 *) &frame, 1);
+
+       *reg_val = frame & MXC_PMIC_FRAME_MASK;
+
+       return ret;
+}
+
+/*!
+ * This function is called to write a value to the register on PMIC.
+ *
+ * @param        reg_num     number of the pmic register to be written
+ * @param        reg_val   value to be written
+ *
+ * @return       Returns 0 on success -1 on failure.
+ */
+int pmic_write(int reg_num, const unsigned int reg_val)
+{
+       unsigned int frame = 0;
+       int ret = 0;
+
+       if (reg_num > MXC_PMIC_MAX_REG_NUM)
+               return PMIC_ERROR;
+
+       frame |= (1 << MXC_PMIC_WRITE_BIT_SHIFT);
+
+       frame |= reg_num << MXC_PMIC_REG_NUM_SHIFT;
+
+       frame |= reg_val & MXC_PMIC_FRAME_MASK;
+
+       ret = spi_rw(pmic_drv_data.spi, (u8 *) &frame, 1);
+
+       return ret;
+}
+
+void *pmic_alloc_data(struct device *dev)
+{
+       struct mc13783 *mc13783;
+
+       mc13783 = kzalloc(sizeof(struct mc13783), GFP_KERNEL);
+       if (mc13783 == NULL)
+               return NULL;
+
+       mc13783->dev = dev;
+
+       return (void *)mc13783;
+}
+
+/*!
+ * This function initializes the SPI device parameters for this PMIC.
+ *
+ * @param    spi       the SPI slave device(PMIC)
+ *
+ * @return   None
+ */
+int pmic_spi_setup(struct spi_device *spi)
+{
+       /* Setup the SPI slave i.e.PMIC */
+       pmic_drv_data.spi = spi;
+
+       spi->mode = SPI_MODE_2 | SPI_CS_HIGH;
+       spi->bits_per_word = 32;
+
+       return spi_setup(spi);
+}
+
+/*!
+ * This function initializes the PMIC registers.
+ *
+ * @return   None
+ */
+int pmic_init_registers(void)
+{
+       CHECK_ERROR(pmic_write(REG_INTERRUPT_MASK_0, MXC_PMIC_FRAME_MASK));
+       CHECK_ERROR(pmic_write(REG_INTERRUPT_MASK_1, MXC_PMIC_FRAME_MASK));
+       CHECK_ERROR(pmic_write(REG_INTERRUPT_STATUS_0, MXC_PMIC_FRAME_MASK));
+       CHECK_ERROR(pmic_write(REG_INTERRUPT_STATUS_1, MXC_PMIC_FRAME_MASK));
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function returns the PMIC version in system.
+ *
+ * @param      ver     pointer to the pmic_version_t structure
+ *
+ * @return       This function returns PMIC version.
+ */
+void pmic_get_revision(pmic_version_t *ver)
+{
+       int rev_id = 0;
+       int rev1 = 0;
+       int rev2 = 0;
+       int finid = 0;
+       int icid = 0;
+
+       ver->id = PMIC_MC13783;
+       pmic_read(REG_REVISION, &rev_id);
+
+       rev1 = (rev_id & 0x018) >> 3;
+       rev2 = (rev_id & 0x007);
+       icid = (rev_id & 0x01C0) >> 6;
+       finid = (rev_id & 0x01E00) >> 9;
+
+       /* Ver 0.2 is actually 3.2a.  Report as 3.2 */
+       if ((rev1 == 0) && (rev2 == 2)) {
+               rev1 = 3;
+       }
+
+       if (rev1 == 0 || icid != 2) {
+               ver->revision = -1;
+               printk(KERN_NOTICE
+                      "mc13783: Not detected.\tAccess failed\t!!!\n");
+       } else {
+               ver->revision = ((rev1 * 10) + rev2);
+               printk(KERN_INFO "mc13783 Rev %d.%d FinVer %x detected\n", rev1,
+                      rev2, finid);
+       }
+
+       return;
+
+}
+
+/*!
+ * This function reads the interrupt status registers of PMIC
+ * and determine the current active events.
+ *
+ * @param      active_events array pointer to be used to return active
+ *             event numbers.
+ *
+ * @return       This function returns PMIC version.
+ */
+unsigned int pmic_get_active_events(unsigned int *active_events)
+{
+       unsigned int count = 0;
+       unsigned int status0, status1;
+       int bit_set;
+
+       pmic_read(REG_INTERRUPT_STATUS_0, &status0);
+       pmic_read(REG_INTERRUPT_STATUS_1, &status1);
+       pmic_write(REG_INTERRUPT_STATUS_0, status0);
+       pmic_write(REG_INTERRUPT_STATUS_1, status1);
+       status0 &= events_enabled0;
+       status1 &= events_enabled1;
+
+       while (status0) {
+               bit_set = ffs(status0) - 1;
+               *(active_events + count) = bit_set;
+               count++;
+               status0 ^= (1 << bit_set);
+       }
+       while (status1) {
+               bit_set = ffs(status1) - 1;
+               *(active_events + count) = bit_set + 24;
+               count++;
+               status1 ^= (1 << bit_set);
+       }
+
+       return count;
+}
+
+/*!
+ * This function unsets a bit in mask register of pmic to unmask an event IT.
+ *
+ * @param      event   the event to be unmasked
+ *
+ * @return    This function returns PMIC_SUCCESS on SUCCESS, error on FAILURE.
+ */
+int pmic_event_unmask(type_event event)
+{
+       unsigned int event_mask = 0;
+       unsigned int mask_reg = 0;
+       unsigned int event_bit = 0;
+       int ret;
+
+       if (event < EVENT_E1HZI) {
+               mask_reg = REG_INTERRUPT_MASK_0;
+               event_mask = EVENT_MASK_0;
+               event_bit = (1 << event);
+               events_enabled0 |= event_bit;
+       } else {
+               event -= 24;
+               mask_reg = REG_INTERRUPT_MASK_1;
+               event_mask = EVENT_MASK_1;
+               event_bit = (1 << event);
+               events_enabled1 |= event_bit;
+       }
+
+       if ((event_bit & event_mask) == 0) {
+               pr_debug("Error: unmasking a reserved/unused event\n");
+               return PMIC_ERROR;
+       }
+
+       ret = pmic_write_reg(mask_reg, 0, event_bit);
+
+       pr_debug("Enable Event : %d\n", event);
+
+       return ret;
+}
+
+/*!
+ * This function sets a bit in mask register of pmic to disable an event IT.
+ *
+ * @param      event   the event to be masked
+ *
+ * @return     This function returns PMIC_SUCCESS on SUCCESS, error on FAILURE.
+ */
+int pmic_event_mask(type_event event)
+{
+       unsigned int event_mask = 0;
+       unsigned int mask_reg = 0;
+       unsigned int event_bit = 0;
+       int ret;
+
+       if (event < EVENT_E1HZI) {
+               mask_reg = REG_INTERRUPT_MASK_0;
+               event_mask = EVENT_MASK_0;
+               event_bit = (1 << event);
+               events_enabled0 &= ~event_bit;
+       } else {
+               event -= 24;
+               mask_reg = REG_INTERRUPT_MASK_1;
+               event_mask = EVENT_MASK_1;
+               event_bit = (1 << event);
+               events_enabled1 &= ~event_bit;
+       }
+
+       if ((event_bit & event_mask) == 0) {
+               pr_debug("Error: masking a reserved/unused event\n");
+               return PMIC_ERROR;
+       }
+
+       ret = pmic_write_reg(mask_reg, event_bit, event_bit);
+
+       pr_debug("Disable Event : %d\n", event);
+
+       return ret;
+}
+
+/*!
+ * This function is called to read all sensor bits of PMIC.
+ *
+ * @param        sensor    Sensor to be checked.
+ *
+ * @return       This function returns true if the sensor bit is high;
+ *               or returns false if the sensor bit is low.
+ */
+bool pmic_check_sensor(t_sensor sensor)
+{
+       unsigned int reg_val = 0;
+
+       CHECK_ERROR(pmic_read_reg
+                   (REG_INTERRUPT_SENSE_0, &reg_val, PMIC_ALL_BITS));
+
+       if ((1 << sensor) & reg_val)
+               return true;
+       else
+               return false;
+}
+
+/*!
+ * This function checks one sensor of PMIC.
+ *
+ * @param        sensor_bits  structure of all sensor bits.
+ *
+ * @return    This function returns PMIC_SUCCESS on SUCCESS, error on FAILURE.
+ */
+
+PMIC_STATUS pmic_get_sensors(t_sensor_bits *sensor_bits)
+{
+       int sense_0 = 0;
+       int sense_1 = 0;
+
+       memset(sensor_bits, 0, sizeof(t_sensor_bits));
+
+       pmic_read_reg(REG_INTERRUPT_SENSE_0, &sense_0, 0xffffff);
+       pmic_read_reg(REG_INTERRUPT_SENSE_1, &sense_1, 0xffffff);
+
+       sensor_bits->sense_chgdets = (sense_0 & (1 << 6)) ? true : false;
+       sensor_bits->sense_chgovs = (sense_0 & (1 << 7)) ? true : false;
+       sensor_bits->sense_chgrevs = (sense_0 & (1 << 8)) ? true : false;
+       sensor_bits->sense_chgshorts = (sense_0 & (1 << 9)) ? true : false;
+       sensor_bits->sense_cccvs = (sense_0 & (1 << 10)) ? true : false;
+       sensor_bits->sense_chgcurrs = (sense_0 & (1 << 11)) ? true : false;
+       sensor_bits->sense_bpons = (sense_0 & (1 << 12)) ? true : false;
+       sensor_bits->sense_lobatls = (sense_0 & (1 << 13)) ? true : false;
+       sensor_bits->sense_lobaths = (sense_0 & (1 << 14)) ? true : false;
+       sensor_bits->sense_usb4v4s = (sense_0 & (1 << 16)) ? true : false;
+       sensor_bits->sense_usb2v0s = (sense_0 & (1 << 17)) ? true : false;
+       sensor_bits->sense_usb0v8s = (sense_0 & (1 << 18)) ? true : false;
+       sensor_bits->sense_id_floats = (sense_0 & (1 << 19)) ? true : false;
+       sensor_bits->sense_id_gnds = (sense_0 & (1 << 20)) ? true : false;
+       sensor_bits->sense_se1s = (sense_0 & (1 << 21)) ? true : false;
+       sensor_bits->sense_ckdets = (sense_0 & (1 << 22)) ? true : false;
+
+       sensor_bits->sense_onofd1s = (sense_1 & (1 << 3)) ? true : false;
+       sensor_bits->sense_onofd2s = (sense_1 & (1 << 4)) ? true : false;
+       sensor_bits->sense_onofd3s = (sense_1 & (1 << 5)) ? true : false;
+       sensor_bits->sense_pwrrdys = (sense_1 & (1 << 11)) ? true : false;
+       sensor_bits->sense_thwarnhs = (sense_1 & (1 << 12)) ? true : false;
+       sensor_bits->sense_thwarnls = (sense_1 & (1 << 13)) ? true : false;
+       sensor_bits->sense_clks = (sense_1 & (1 << 14)) ? true : false;
+       sensor_bits->sense_mc2bs = (sense_1 & (1 << 17)) ? true : false;
+       sensor_bits->sense_hsdets = (sense_1 & (1 << 18)) ? true : false;
+       sensor_bits->sense_hsls = (sense_1 & (1 << 19)) ? true : false;
+       sensor_bits->sense_alspths = (sense_1 & (1 << 20)) ? true : false;
+       sensor_bits->sense_ahsshorts = (sense_1 & (1 << 21)) ? true : false;
+       return PMIC_SUCCESS;
+}
+
+EXPORT_SYMBOL(pmic_check_sensor);
+EXPORT_SYMBOL(pmic_get_sensors);
diff --git a/drivers/mxc/pmic/core/mc13892.c b/drivers/mxc/pmic/core/mc13892.c
new file mode 100644 (file)
index 0000000..5d04aad
--- /dev/null
@@ -0,0 +1,335 @@
+/*
+ * 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 pmic/core/mc13892.c
+ * @brief This file contains MC13892 specific PMIC code. This implementaion
+ * may differ for each PMIC chip.
+ *
+ * @ingroup PMIC_CORE
+ */
+
+/*
+ * Includes
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/pmic_external.h>
+#include <linux/pmic_status.h>
+#include <linux/mfd/mc13892/core.h>
+
+#include <asm/mach-types.h>
+#include <asm/uaccess.h>
+
+#include "pmic.h"
+
+/*
+ * Defines
+ */
+#define MC13892_I2C_RETRY_TIMES 10
+#define MXC_PMIC_FRAME_MASK            0x00FFFFFF
+#define MXC_PMIC_MAX_REG_NUM           0x3F
+#define MXC_PMIC_REG_NUM_SHIFT         0x19
+#define MXC_PMIC_WRITE_BIT_SHIFT               31
+
+static unsigned int events_enabled0;
+static unsigned int events_enabled1;
+static struct mxc_pmic pmic_drv_data;
+#ifndef CONFIG_MXC_PMIC_I2C
+struct i2c_client *mc13892_client;
+#endif
+
+int pmic_i2c_24bit_read(struct i2c_client *client, unsigned int reg_num,
+                       unsigned int *value)
+{
+       unsigned char buf[3];
+       int ret;
+       int i;
+
+       memset(buf, 0, 3);
+       for (i = 0; i < MC13892_I2C_RETRY_TIMES; i++) {
+               ret = i2c_smbus_read_i2c_block_data(client, reg_num, 3, buf);
+               if (ret == 3)
+                       break;
+               msleep(1);
+       }
+
+       if (ret == 3) {
+               *value = buf[0] << 16 | buf[1] << 8 | buf[2];
+               return ret;
+       } else {
+               pr_err("24bit read error, ret = %d\n", ret);
+               return -1;      /* return -1 on failure */
+       }
+}
+
+int pmic_i2c_24bit_write(struct i2c_client *client,
+                        unsigned int reg_num, unsigned int reg_val)
+{
+       char buf[3];
+       int ret;
+       int i;
+
+       buf[0] = (reg_val >> 16) & 0xff;
+       buf[1] = (reg_val >> 8) & 0xff;
+       buf[2] = (reg_val) & 0xff;
+
+       for (i = 0; i < MC13892_I2C_RETRY_TIMES; i++) {
+               ret = i2c_smbus_write_i2c_block_data(client, reg_num, 3, buf);
+               if (ret == 0)
+                       break;
+               msleep(1);
+       }
+       if (i == MC13892_I2C_RETRY_TIMES)
+               pr_err("24bit write error, ret = %d\n", ret);
+
+       return ret;
+}
+
+int pmic_read(int reg_num, unsigned int *reg_val)
+{
+       unsigned int frame = 0;
+       int ret = 0;
+
+       if (pmic_drv_data.spi != NULL) {
+               if (reg_num > MXC_PMIC_MAX_REG_NUM)
+                       return PMIC_ERROR;
+
+               frame |= reg_num << MXC_PMIC_REG_NUM_SHIFT;
+
+               ret = spi_rw(pmic_drv_data.spi, (u8 *) &frame, 1);
+
+               *reg_val = frame & MXC_PMIC_FRAME_MASK;
+       } else {
+               if (mc13892_client == NULL)
+                       return PMIC_ERROR;
+
+               if (pmic_i2c_24bit_read(mc13892_client, reg_num, reg_val) == -1)
+                       return PMIC_ERROR;
+       }
+
+       return PMIC_SUCCESS;
+}
+
+int pmic_write(int reg_num, const unsigned int reg_val)
+{
+       unsigned int frame = 0;
+       int ret = 0;
+
+       if (pmic_drv_data.spi != NULL) {
+               if (reg_num > MXC_PMIC_MAX_REG_NUM)
+                       return PMIC_ERROR;
+
+               frame |= (1 << MXC_PMIC_WRITE_BIT_SHIFT);
+
+               frame |= reg_num << MXC_PMIC_REG_NUM_SHIFT;
+
+               frame |= reg_val & MXC_PMIC_FRAME_MASK;
+
+               ret = spi_rw(pmic_drv_data.spi, (u8 *) &frame, 1);
+
+               return ret;
+       } else {
+               if (mc13892_client == NULL)
+                       return PMIC_ERROR;
+
+               return pmic_i2c_24bit_write(mc13892_client, reg_num, reg_val);
+       }
+}
+
+void *pmic_alloc_data(struct device *dev)
+{
+       struct mc13892 *mc13892;
+
+       mc13892 = kzalloc(sizeof(struct mc13892), GFP_KERNEL);
+       if (mc13892 == NULL)
+               return NULL;
+
+       mc13892->dev = dev;
+
+       return (void *)mc13892;
+}
+
+/*!
+ * This function initializes the SPI device parameters for this PMIC.
+ *
+ * @param    spi       the SPI slave device(PMIC)
+ *
+ * @return   None
+ */
+int pmic_spi_setup(struct spi_device *spi)
+{
+       /* Setup the SPI slave i.e.PMIC */
+       pmic_drv_data.spi = spi;
+
+       spi->mode = SPI_MODE_0 | SPI_CS_HIGH;
+       spi->bits_per_word = 32;
+
+       return spi_setup(spi);
+}
+
+int pmic_init_registers(void)
+{
+       CHECK_ERROR(pmic_write(REG_INT_MASK0, 0xFFFFFF));
+       CHECK_ERROR(pmic_write(REG_INT_MASK0, 0xFFFFFF));
+       CHECK_ERROR(pmic_write(REG_INT_STATUS0, 0xFFFFFF));
+       CHECK_ERROR(pmic_write(REG_INT_STATUS1, 0xFFFFFF));
+       /* disable auto charge */
+       if (machine_is_mx51_3ds())
+               CHECK_ERROR(pmic_write(REG_CHARGE, 0xB40003));
+
+       pm_power_off = mc13892_power_off;
+
+       return PMIC_SUCCESS;
+}
+
+unsigned int pmic_get_active_events(unsigned int *active_events)
+{
+       unsigned int count = 0;
+       unsigned int status0, status1;
+       int bit_set;
+
+       pmic_read(REG_INT_STATUS0, &status0);
+       pmic_read(REG_INT_STATUS1, &status1);
+       pmic_write(REG_INT_STATUS0, status0);
+       pmic_write(REG_INT_STATUS1, status1);
+       status0 &= events_enabled0;
+       status1 &= events_enabled1;
+
+       while (status0) {
+               bit_set = ffs(status0) - 1;
+               *(active_events + count) = bit_set;
+               count++;
+               status0 ^= (1 << bit_set);
+       }
+       while (status1) {
+               bit_set = ffs(status1) - 1;
+               *(active_events + count) = bit_set + 24;
+               count++;
+               status1 ^= (1 << bit_set);
+       }
+
+       return count;
+}
+
+#define EVENT_MASK_0                   0x387fff
+#define EVENT_MASK_1                   0x1177ef
+
+int pmic_event_unmask(type_event event)
+{
+       unsigned int event_mask = 0;
+       unsigned int mask_reg = 0;
+       unsigned int event_bit = 0;
+       int ret;
+
+       if (event < EVENT_1HZI) {
+               mask_reg = REG_INT_MASK0;
+               event_mask = EVENT_MASK_0;
+               event_bit = (1 << event);
+               events_enabled0 |= event_bit;
+       } else {
+               event -= 24;
+               mask_reg = REG_INT_MASK1;
+               event_mask = EVENT_MASK_1;
+               event_bit = (1 << event);
+               events_enabled1 |= event_bit;
+       }
+
+       if ((event_bit & event_mask) == 0) {
+               pr_debug("Error: unmasking a reserved/unused event\n");
+               return PMIC_ERROR;
+       }
+
+       ret = pmic_write_reg(mask_reg, 0, event_bit);
+
+       pr_debug("Enable Event : %d\n", event);
+
+       return ret;
+}
+
+int pmic_event_mask(type_event event)
+{
+       unsigned int event_mask = 0;
+       unsigned int mask_reg = 0;
+       unsigned int event_bit = 0;
+       int ret;
+
+       if (event < EVENT_1HZI) {
+               mask_reg = REG_INT_MASK0;
+               event_mask = EVENT_MASK_0;
+               event_bit = (1 << event);
+               events_enabled0 &= ~event_bit;
+       } else {
+               event -= 24;
+               mask_reg = REG_INT_MASK1;
+               event_mask = EVENT_MASK_1;
+               event_bit = (1 << event);
+               events_enabled1 &= ~event_bit;
+       }
+
+       if ((event_bit & event_mask) == 0) {
+               pr_debug("Error: masking a reserved/unused event\n");
+               return PMIC_ERROR;
+       }
+
+       ret = pmic_write_reg(mask_reg, event_bit, event_bit);
+
+       pr_debug("Disable Event : %d\n", event);
+
+       return ret;
+}
+
+/*!
+ * This function returns the PMIC version in system.
+ *
+ * @param      ver     pointer to the pmic_version_t structure
+ *
+ * @return       This function returns PMIC version.
+ */
+void pmic_get_revision(pmic_version_t *ver)
+{
+       int rev_id = 0;
+       int rev1 = 0;
+       int rev2 = 0;
+       int finid = 0;
+       int icid = 0;
+
+       ver->id = PMIC_MC13892;
+       pmic_read(REG_IDENTIFICATION, &rev_id);
+
+       rev1 = (rev_id & 0x018) >> 3;
+       rev2 = (rev_id & 0x007);
+       icid = (rev_id & 0x01C0) >> 6;
+       finid = (rev_id & 0x01E00) >> 9;
+
+       ver->revision = ((rev1 * 10) + rev2);
+       printk(KERN_INFO "mc13892 Rev %d.%d FinVer %x detected\n", rev1,
+              rev2, finid);
+}
+
+void mc13892_power_off(void)
+{
+       unsigned int value;
+
+       pmic_read_reg(REG_POWER_CTL0, &value, 0xffffff);
+
+       value |= 0x000008;
+
+       pmic_write_reg(REG_POWER_CTL0, value, 0xffffff);
+}
diff --git a/drivers/mxc/pmic/core/mc34704.c b/drivers/mxc/pmic/core/mc34704.c
new file mode 100644 (file)
index 0000000..8e0d6b5
--- /dev/null
@@ -0,0 +1,329 @@
+/*
+ * 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 pmic/core/mc34704.c
+ * @brief This file contains MC34704 specific PMIC code.
+ *
+ * @ingroup PMIC_CORE
+ */
+
+/*
+ * Includes
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/mfd/mc34704/core.h>
+#include <linux/pmic_external.h>
+#include <linux/pmic_status.h>
+
+#include "pmic.h"
+
+/*
+ * Globals
+ */
+static pmic_version_t mxc_pmic_version = {
+       .id = PMIC_MC34704,
+       .revision = 0,
+};
+static unsigned int events_enabled;
+unsigned int active_events[MAX_ACTIVE_EVENTS];
+struct i2c_client *mc34704_client;
+static void pmic_trigger_poll(void);
+
+#define MAX_MC34704_REG 0x59
+static unsigned int mc34704_reg_readonly[MAX_MC34704_REG / 32 + 1] = {
+       (1 << 0x03) || (1 << 0x05) || (1 << 0x07) || (1 << 0x09) ||
+           (1 << 0x0B) || (1 << 0x0E) || (1 << 0x11) || (1 << 0x14) ||
+           (1 << 0x17) || (1 << 0x18),
+       0,
+};
+static unsigned int mc34704_reg_written[MAX_MC34704_REG / 32 + 1];
+static unsigned char mc34704_shadow_regs[MAX_MC34704_REG - 1];
+#define IS_READONLY(r) ((1 << ((r) % 32)) & mc34704_reg_readonly[(r) / 32])
+#define WAS_WRITTEN(r) ((1 << ((r) % 32)) & mc34704_reg_written[(r) / 32])
+#define MARK_WRITTEN(r) do { \
+       mc34704_reg_written[(r) / 32] |= (1 << ((r) % 32)); \
+} while (0)
+
+int pmic_read(int reg_nr, unsigned int *reg_val)
+{
+       int c;
+
+       /*
+        * Use the shadow register if we've written to it
+        */
+       if (WAS_WRITTEN(reg_nr)) {
+               *reg_val = mc34704_shadow_regs[reg_nr];
+               return PMIC_SUCCESS;
+       }
+
+       /*
+        * Otherwise, actually read the real register.
+        * Write-only registers will read as zero.
+        */
+       c = i2c_smbus_read_byte_data(mc34704_client, reg_nr);
+       if (c == -1) {
+               pr_debug("mc34704: error reading register 0x%02x\n", reg_nr);
+               return PMIC_ERROR;
+       } else {
+               *reg_val = c;
+               return PMIC_SUCCESS;
+       }
+}
+
+int pmic_write(int reg_nr, const unsigned int reg_val)
+{
+       int ret;
+
+       ret = i2c_smbus_write_byte_data(mc34704_client, reg_nr, reg_val);
+       if (ret == -1) {
+               return PMIC_ERROR;
+       } else {
+               /*
+                * Update our software copy of the register since you
+                * can't read what you wrote.
+                */
+               if (!IS_READONLY(reg_nr)) {
+                       mc34704_shadow_regs[reg_nr] = reg_val;
+                       MARK_WRITTEN(reg_nr);
+               }
+               return PMIC_SUCCESS;
+       }
+}
+
+unsigned int pmic_get_active_events(unsigned int *active_events)
+{
+       unsigned int count = 0;
+       unsigned int faults;
+       int bit_set;
+
+       /* Check for any relevant PMIC faults */
+       pmic_read(REG_MC34704_FAULTS, &faults);
+       faults &= events_enabled;
+
+       /*
+        * Mask all active events, because there is no way to acknowledge
+        * or dismiss them in the PMIC -- they're sticky.
+        */
+       events_enabled &= ~faults;
+
+       /* Account for all unmasked faults */
+       while (faults) {
+               bit_set = ffs(faults) - 1;
+               *(active_events + count) = bit_set;
+               count++;
+               faults ^= (1 << bit_set);
+       }
+       return count;
+}
+
+int pmic_event_unmask(type_event event)
+{
+       unsigned int event_bit = 0;
+       unsigned int prior_events = events_enabled;
+
+       event_bit = (1 << event);
+       events_enabled |= event_bit;
+
+       pr_debug("Enable Event : %d\n", event);
+
+       /* start the polling task as needed */
+       if (events_enabled && prior_events == 0)
+               pmic_trigger_poll();
+
+       return 0;
+}
+
+int pmic_event_mask(type_event event)
+{
+       unsigned int event_bit = 0;
+
+       event_bit = (1 << event);
+       events_enabled &= ~event_bit;
+
+       pr_debug("Disable Event : %d\n", event);
+
+       return 0;
+}
+
+/*!
+ * PMIC event polling task.  This task is called periodically to poll
+ * for possible MC34704 events (No interrupt supplied by the hardware).
+ */
+static void pmic_event_task(struct work_struct *work);
+DECLARE_DELAYED_WORK(pmic_ws, pmic_event_task);
+
+static void pmic_trigger_poll(void)
+{
+       schedule_delayed_work(&pmic_ws, HZ / 10);
+}
+
+static void pmic_event_task(struct work_struct *work)
+{
+       unsigned int count = 0;
+       int i;
+
+       count = pmic_get_active_events(active_events);
+       pr_debug("active events number %d\n", count);
+
+       /* call handlers for all active events */
+       for (i = 0; i < count; i++)
+               pmic_event_callback(active_events[i]);
+
+       /* re-trigger this task, but only if somebody is watching */
+       if (events_enabled)
+               pmic_trigger_poll();
+
+       return;
+}
+
+pmic_version_t pmic_get_version(void)
+{
+       return mxc_pmic_version;
+}
+EXPORT_SYMBOL(pmic_get_version);
+
+int __devinit pmic_init_registers(void)
+{
+       /*
+        * Set some registers to what they should be,
+        * if for no other reason than to initialize our
+        * software register copies.
+        */
+       CHECK_ERROR(pmic_write(REG_MC34704_GENERAL2, 0x09));
+       CHECK_ERROR(pmic_write(REG_MC34704_VGSET1, 0));
+       CHECK_ERROR(pmic_write(REG_MC34704_REG2SET1, 0));
+       CHECK_ERROR(pmic_write(REG_MC34704_REG3SET1, 0));
+       CHECK_ERROR(pmic_write(REG_MC34704_REG4SET1, 0));
+       CHECK_ERROR(pmic_write(REG_MC34704_REG5SET1, 0));
+
+       return PMIC_SUCCESS;
+}
+
+static int __devinit is_chip_onboard(struct i2c_client *client)
+{
+       int val;
+
+       /*
+        * This PMIC has no version or ID register, so just see
+        * if it ACK's and returns 0 on some write-only register as
+        * evidence of its presence.
+        */
+       val = i2c_smbus_read_byte_data(client, REG_MC34704_GENERAL2);
+       if (val != 0)
+               return -1;
+
+       return 0;
+}
+
+static int __devinit pmic_probe(struct i2c_client *client,
+                               const struct i2c_device_id *id)
+{
+       int ret = 0;
+       struct mc34704 *mc34704;
+       struct mc34704_platform_data *plat_data = client->dev.platform_data;
+
+       if (!plat_data || !plat_data->init)
+               return -ENODEV;
+
+       ret = is_chip_onboard(client);
+
+       if (ret == -1)
+               return -ENODEV;
+
+       mc34704 = kzalloc(sizeof(struct mc34704), GFP_KERNEL);
+       if (mc34704 == NULL)
+               return -ENOMEM;
+
+       i2c_set_clientdata(client, mc34704);
+       mc34704->dev = &client->dev;
+       mc34704->i2c_client = client;
+
+       mc34704_client = client;
+
+       /* Initialize the PMIC event handling */
+       pmic_event_list_init();
+
+       /* Initialize PMI registers */
+       if (pmic_init_registers() != PMIC_SUCCESS)
+               return PMIC_ERROR;
+
+       ret = plat_data->init(mc34704);
+       if (ret != 0)
+               return PMIC_ERROR;
+
+       dev_info(&client->dev, "Loaded\n");
+
+       return PMIC_SUCCESS;
+}
+
+static int pmic_remove(struct i2c_client *client)
+{
+       return 0;
+}
+
+static int pmic_suspend(struct i2c_client *client, pm_message_t state)
+{
+       return 0;
+}
+
+static int pmic_resume(struct i2c_client *client)
+{
+       return 0;
+}
+
+static const struct i2c_device_id mc34704_id[] = {
+       {"mc34704", 0},
+       {},
+};
+
+MODULE_DEVICE_TABLE(i2c, mc34704_id);
+
+static struct i2c_driver pmic_driver = {
+       .driver = {
+                  .name = "mc34704",
+                  .bus = NULL,
+                  },
+       .probe = pmic_probe,
+       .remove = pmic_remove,
+       .suspend = pmic_suspend,
+       .resume = pmic_resume,
+       .id_table = mc34704_id,
+};
+
+static int __init pmic_init(void)
+{
+       return i2c_add_driver(&pmic_driver);
+}
+
+static void __exit pmic_exit(void)
+{
+       i2c_del_driver(&pmic_driver);
+}
+
+/*
+ * Module entry points
+ */
+subsys_initcall_sync(pmic_init);
+module_exit(pmic_exit);
+
+MODULE_DESCRIPTION("MC34704 PMIC driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pmic/core/pmic-dev.c b/drivers/mxc/pmic/core/pmic-dev.c
new file mode 100644 (file)
index 0000000..4bbebb6
--- /dev/null
@@ -0,0 +1,316 @@
+/*
+ * Copyright 2005-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 pmic-dev.c
+ * @brief This provides /dev interface to the user program. They make it
+ * possible to have user-space programs use or control PMIC. Mainly its
+ * useful for notifying PMIC events to user-space programs.
+ *
+ * @ingroup PMIC_CORE
+ */
+
+/*
+ * Includes
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/kdev_t.h>
+#include <linux/circ_buf.h>
+#include <linux/major.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/signal.h>
+#include <linux/slab.h>
+#include <linux/pmic_external.h>
+
+#include <asm/uaccess.h>
+
+#define PMIC_NAME      "pmic"
+#define CIRC_BUF_MAX   16
+
+static int pmic_major;
+static struct class *pmic_class;
+static struct fasync_struct *pmic_dev_queue;
+
+static DECLARE_MUTEX(event_mutex);
+static struct circ_buf pmic_events;
+
+static void callbackfn(void *event)
+{
+       printk(KERN_INFO "\n\n DETECTED PMIC EVENT : %d\n\n",
+              (unsigned int)event);
+}
+
+static void user_notify_callback(void *event)
+{
+       down(&event_mutex);
+       if (CIRC_SPACE(pmic_events.head, pmic_events.tail, CIRC_BUF_MAX)) {
+               pmic_events.buf[pmic_events.head] = (int)event;
+               pmic_events.head = (pmic_events.head + 1) & (CIRC_BUF_MAX - 1);
+       } else {
+               pr_info("Failed to notify event to the user\n");
+       }
+       up(&event_mutex);
+
+       kill_fasync(&pmic_dev_queue, SIGIO, POLL_IN);
+}
+
+/*!
+ * This function implements IOCTL controls on a PMIC device.
+ *
+ * @param        inode       pointer on the node
+ * @param        file        pointer on the file
+ * @param        cmd         the command
+ * @param        arg         the parameter
+ * @return       This function returns 0 if successful.
+ */
+static int pmic_dev_ioctl(struct inode *inode, struct file *file,
+                         unsigned int cmd, unsigned long arg)
+{
+       register_info reg_info;
+       pmic_event_callback_t event_sub;
+       type_event event = EVENT_NB;
+       int ret = 0;
+
+       if (_IOC_TYPE(cmd) != 'P')
+               return -ENOTTY;
+
+       switch (cmd) {
+       case PMIC_READ_REG:
+               if (copy_from_user(&reg_info, (register_info *) arg,
+                                  sizeof(register_info))) {
+                       return -EFAULT;
+               }
+               ret =
+                   pmic_read_reg(reg_info.reg, &(reg_info.reg_value),
+                                 0x00ffffff);
+               pr_debug("read reg %d %x\n", reg_info.reg, reg_info.reg_value);
+               if (copy_to_user((register_info *) arg, &reg_info,
+                                sizeof(register_info))) {
+                       return -EFAULT;
+               }
+               break;
+
+       case PMIC_WRITE_REG:
+               if (copy_from_user(&reg_info, (register_info *) arg,
+                                  sizeof(register_info))) {
+                       return -EFAULT;
+               }
+               ret =
+                   pmic_write_reg(reg_info.reg, reg_info.reg_value,
+                                  0x00ffffff);
+               pr_debug("write reg %d %x\n", reg_info.reg, reg_info.reg_value);
+               if (copy_to_user((register_info *) arg, &reg_info,
+                                sizeof(register_info))) {
+                       return -EFAULT;
+               }
+               break;
+
+       case PMIC_SUBSCRIBE:
+               if (get_user(event, (int __user *)arg)) {
+                       return -EFAULT;
+               }
+               event_sub.func = callbackfn;
+               event_sub.param = (void *)event;
+               ret = pmic_event_subscribe(event, event_sub);
+               pr_debug("subscribe done\n");
+               break;
+
+       case PMIC_UNSUBSCRIBE:
+               if (get_user(event, (int __user *)arg)) {
+                       return -EFAULT;
+               }
+               event_sub.func = callbackfn;
+               event_sub.param = (void *)event;
+               ret = pmic_event_unsubscribe(event, event_sub);
+               pr_debug("unsubscribe done\n");
+               break;
+
+       case PMIC_NOTIFY_USER:
+               if (get_user(event, (int __user *)arg)) {
+                       return -EFAULT;
+               }
+               event_sub.func = user_notify_callback;
+               event_sub.param = (void *)event;
+               ret = pmic_event_subscribe(event, event_sub);
+               break;
+
+       case PMIC_GET_NOTIFY:
+               down(&event_mutex);
+               if (CIRC_CNT(pmic_events.head, pmic_events.tail, CIRC_BUF_MAX)) {
+                       event = (int)pmic_events.buf[pmic_events.tail];
+                       pmic_events.tail = (pmic_events.tail + 1) & (CIRC_BUF_MAX - 1);
+               } else {
+                       pr_info("No valid notified event\n");
+               }
+               up(&event_mutex);
+
+               if (put_user(event, (int __user *)arg)) {
+                       return -EFAULT;
+               }
+               break;
+
+       default:
+               printk(KERN_ERR "%d unsupported ioctl command\n", (int)cmd);
+               return -EINVAL;
+       }
+
+       return ret;
+}
+
+/*!
+ * This function implements the open method on a PMIC device.
+ *
+ * @param        inode       pointer on the node
+ * @param        file        pointer on the file
+ * @return       This function returns 0.
+ */
+static int pmic_dev_open(struct inode *inode, struct file *file)
+{
+       pr_debug("open\n");
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function implements the release method on a PMIC device.
+ *
+ * @param        inode       pointer on the node
+ * @param        file        pointer on the file
+ *
+ * @return       This function returns 0.
+ */
+static int pmic_dev_free(struct inode *inode, struct file *file)
+{
+       pr_debug("free\n");
+       return PMIC_SUCCESS;
+}
+
+static int pmic_dev_fasync(int fd, struct file *filp, int mode)
+{
+       return fasync_helper(fd, filp, mode, &pmic_dev_queue);
+}
+
+/*!
+ * This structure defines file operations for a PMIC device.
+ */
+static struct file_operations pmic_fops = {
+       /*!
+        * the owner
+        */
+       .owner = THIS_MODULE,
+       /*!
+        * the ioctl operation
+        */
+       .ioctl = pmic_dev_ioctl,
+       /*!
+        * the open operation
+        */
+       .open = pmic_dev_open,
+       /*!
+        * the release operation
+        */
+       .release = pmic_dev_free,
+       /*!
+        * the release operation
+        */
+       .fasync = pmic_dev_fasync,
+};
+
+/*!
+ * This function implements the init function of the PMIC char device.
+ * This function is called when the module is loaded. It registers
+ * the character device for PMIC to be used by user-space programs.
+ *
+ * @return       This function returns 0.
+ */
+static int __init pmic_dev_init(void)
+{
+       int ret = 0;
+       struct device *pmic_device;
+       pmic_version_t pmic_ver;
+
+       pmic_ver = pmic_get_version();
+       if (pmic_ver.revision < 0) {
+               printk(KERN_ERR "No PMIC device found\n");
+               return -ENODEV;
+       }
+
+       pmic_major = register_chrdev(0, PMIC_NAME, &pmic_fops);
+       if (pmic_major < 0) {
+               printk(KERN_ERR "unable to get a major for pmic\n");
+               return pmic_major;
+       }
+
+       pmic_class = class_create(THIS_MODULE, PMIC_NAME);
+       if (IS_ERR(pmic_class)) {
+               printk(KERN_ERR "Error creating pmic class.\n");
+               ret = PMIC_ERROR;
+               goto err;
+       }
+
+       pmic_device = device_create(pmic_class, NULL, MKDEV(pmic_major, 0), NULL,
+                                   PMIC_NAME);
+       if (IS_ERR(pmic_device)) {
+               printk(KERN_ERR "Error creating pmic class device.\n");
+               ret = PMIC_ERROR;
+               goto err1;
+       }
+
+       pmic_events.buf = kmalloc(CIRC_BUF_MAX * sizeof(char), GFP_KERNEL);
+       if (NULL == pmic_events.buf) {
+               ret = -ENOMEM;
+               goto err2;
+       }
+       pmic_events.head = pmic_events.tail = 0;
+
+       printk(KERN_INFO "PMIC Character device: successfully loaded\n");
+       return ret;
+      err2:
+       device_destroy(pmic_class, MKDEV(pmic_major, 0));
+      err1:
+       class_destroy(pmic_class);
+      err:
+       unregister_chrdev(pmic_major, PMIC_NAME);
+       return ret;
+
+}
+
+/*!
+ * This function implements the exit function of the PMIC character device.
+ * This function is called when the module is unloaded. It unregisters
+ * the PMIC character device.
+ *
+ */
+static void __exit pmic_dev_exit(void)
+{
+       device_destroy(pmic_class, MKDEV(pmic_major, 0));
+       class_destroy(pmic_class);
+
+       unregister_chrdev(pmic_major, PMIC_NAME);
+
+       printk(KERN_INFO "PMIC Character device: successfully unloaded\n");
+}
+
+/*
+ * Module entry points
+ */
+
+module_init(pmic_dev_init);
+module_exit(pmic_dev_exit);
+
+MODULE_DESCRIPTION("PMIC Protocol /dev entries driver");
+MODULE_AUTHOR("FreeScale");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pmic/core/pmic.h b/drivers/mxc/pmic/core/pmic.h
new file mode 100644 (file)
index 0000000..7849e8d
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * 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
+ */
+#ifndef __PMIC_H__
+#define __PMIC_H__
+
+ /*!
+  * @file pmic.h
+  * @brief This file contains prototypes of all the functions to be
+  * defined for each PMIC chip. The implementation of these may differ
+  * from PMIC chip to PMIC chip.
+  *
+  * @ingroup PMIC_CORE
+  */
+
+#include <linux/spi/spi.h>
+
+#define MAX_ACTIVE_EVENTS              10
+
+/*!
+ * This structure is a way for the PMIC core driver to define their own
+ * \b spi_device structure. This structure includes the core \b spi_device
+ * structure that is provided by Linux SPI Framework/driver as an
+ * element and may contain other elements that are required by core driver.
+ */
+struct mxc_pmic {
+       /*!
+        * Master side proxy for an SPI slave device(PMIC)
+        */
+       struct spi_device *spi;
+};
+
+/*!
+ * This function is called to transfer data to PMIC on SPI.
+ *
+ * @param    spi       the SPI slave device(PMIC)
+ * @param    buf       the pointer to the data buffer
+ * @param    len       the length of the data to be transferred
+ *
+ * @return   Returns 0 on success -1 on failure.
+ */
+static inline int spi_rw(struct spi_device *spi, u8 * buf, size_t len)
+{
+       struct spi_transfer t = {
+               .tx_buf = (const void *)buf,
+               .rx_buf = buf,
+               .len = len,
+               .cs_change = 0,
+               .delay_usecs = 0,
+       };
+       struct spi_message m;
+
+       spi_message_init(&m);
+       spi_message_add_tail(&t, &m);
+       if (spi_sync(spi, &m) != 0 || m.status != 0)
+               return PMIC_ERROR;
+       return len - m.actual_length;
+}
+
+/*!
+ * This function returns the PMIC version in system.
+ *
+ * @param      ver     pointer to the pmic_version_t structure
+ *
+ * @return       This function returns PMIC version.
+ */
+void pmic_get_revision(pmic_version_t *ver);
+
+/*!
+ * This function initializes the SPI device parameters for this PMIC.
+ *
+ * @param    spi       the SPI slave device(PMIC)
+ *
+ * @return   None
+ */
+int pmic_spi_setup(struct spi_device *spi);
+
+/*!
+ * This function initializes the PMIC registers.
+ *
+ * @return   None
+ */
+int pmic_init_registers(void);
+
+/*!
+ * This function reads the interrupt status registers of PMIC
+ * and determine the current active events.
+ *
+ * @param      active_events array pointer to be used to return active
+ *             event numbers.
+ *
+ * @return       This function returns PMIC version.
+ */
+unsigned int pmic_get_active_events(unsigned int *active_events);
+
+/*!
+ * This function sets a bit in mask register of pmic to disable an event IT.
+ *
+ * @param      event   the event to be masked
+ *
+ * @return     This function returns PMIC_SUCCESS on SUCCESS, error on FAILURE.
+ */
+int pmic_event_mask(type_event event);
+
+/*!
+ * This function unsets a bit in mask register of pmic to unmask an event IT.
+ *
+ * @param      event   the event to be unmasked
+ *
+ * @return    This function returns PMIC_SUCCESS on SUCCESS, error on FAILURE.
+ */
+int pmic_event_unmask(type_event event);
+
+#ifdef CONFIG_MXC_PMIC_FIXARB
+extern PMIC_STATUS pmic_fix_arbitration(struct spi_device *spi);
+#else
+static inline PMIC_STATUS pmic_fix_arbitration(struct spi_device *spi)
+{
+       return PMIC_SUCCESS;
+}
+#endif
+
+void *pmic_alloc_data(struct device *dev);
+
+int pmic_start_event_thread(int irq_num);
+
+void pmic_stop_event_thread(void);
+
+#endif                         /* __PMIC_H__ */
diff --git a/drivers/mxc/pmic/core/pmic_common.c b/drivers/mxc/pmic/core/pmic_common.c
new file mode 100644 (file)
index 0000000..fa40b83
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * 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
+ */
+
+/*!
+ * @file pmic_common.c
+ * @brief This is the common file for the PMIC Core/Protocol driver.
+ *
+ * @ingroup PMIC_CORE
+ */
+
+/*
+ * Includes
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kthread.h>
+#include <linux/pmic_external.h>
+#include <linux/pmic_status.h>
+
+#include <asm/uaccess.h>
+
+#include "pmic.h"
+
+/*
+ * Global variables
+ */
+pmic_version_t mxc_pmic_version;
+unsigned int active_events[MAX_ACTIVE_EVENTS];
+
+
+static struct completion event_completion;
+static struct task_struct *tstask;
+
+static int pmic_event_thread_func(void *v)
+{
+       unsigned int loop;
+       unsigned int count = 0;
+       unsigned int irq = (int)v;
+
+       while (1) {
+               wait_for_completion_interruptible(
+                               &event_completion);
+               if (kthread_should_stop())
+                       break;
+
+               count = pmic_get_active_events(
+                               active_events);
+               pr_debug("active events number %d\n", count);
+
+       do {
+               for (loop = 0; loop < count; loop++)
+                       pmic_event_callback(active_events[loop]);
+
+               count = pmic_get_active_events(active_events);
+
+       } while (count != 0);
+               enable_irq(irq);
+       }
+
+       return 0;
+}
+
+int pmic_start_event_thread(int irq_num)
+{
+       int ret = 0;
+
+       if (tstask)
+               return ret;
+
+       init_completion(&event_completion);
+
+       tstask = kthread_run(pmic_event_thread_func,
+               (void *)irq_num, "pmic-event-thread");
+       ret = IS_ERR(tstask) ? -1 : 0;
+       if (IS_ERR(tstask))
+               tstask = NULL;
+       return ret;
+}
+
+void pmic_stop_event_thread(void)
+{
+       if (tstask) {
+               complete(&event_completion);
+               kthread_stop(tstask);
+       }
+}
+
+/*!
+ * This function is called when pmic interrupt occurs on the processor.
+ * It is the interrupt handler for the pmic module.
+ *
+ * @param        irq        the irq number
+ * @param        dev_id     the pointer on the device
+ *
+ * @return       The function returns IRQ_HANDLED when handled.
+ */
+irqreturn_t pmic_irq_handler(int irq, void *dev_id)
+{
+       disable_irq_nosync(irq);
+       complete(&event_completion);
+
+       return IRQ_HANDLED;
+}
+
+/*!
+ * This function is used to determine the PMIC type and its revision.
+ *
+ * @return      Returns the PMIC type and its revision.
+ */
+
+pmic_version_t pmic_get_version(void)
+{
+       return mxc_pmic_version;
+}
+EXPORT_SYMBOL(pmic_get_version);
diff --git a/drivers/mxc/pmic/core/pmic_core_i2c.c b/drivers/mxc/pmic/core/pmic_core_i2c.c
new file mode 100644 (file)
index 0000000..40d029a
--- /dev/null
@@ -0,0 +1,348 @@
+/*
+ * 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 pmic_core_i2c.c
+ * @brief This is the main file for the PMIC Core/Protocol driver. i2c
+ * should be providing the interface between the PMIC and the MCU.
+ *
+ * @ingroup PMIC_CORE
+ */
+
+/*
+ * Includes
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/i2c.h>
+#include <linux/mfd/mc13892/core.h>
+#include <linux/pmic_external.h>
+#include <linux/pmic_status.h>
+#include <linux/uaccess.h>
+#include <mach/hardware.h>
+
+#include "pmic.h"
+
+#define MC13892_GENERATION_ID_LSH      6
+#define MC13892_IC_ID_LSH              13
+
+#define MC13892_GENERATION_ID_WID      3
+#define MC13892_IC_ID_WID              6
+
+#define MC13892_GEN_ID_VALUE   0x7
+#define MC13892_IC_ID_VALUE            1
+
+/*
+ * Global variables
+ */
+struct i2c_client *mc13892_client;
+
+extern pmic_version_t mxc_pmic_version;
+extern irqreturn_t pmic_irq_handler(int irq, void *dev_id);
+/*
+ * Platform device structure for PMIC client drivers
+ */
+static struct platform_device adc_ldm = {
+       .name = "pmic_adc",
+       .id = 1,
+};
+static struct platform_device battery_ldm = {
+       .name = "pmic_battery",
+       .id = 1,
+};
+static struct platform_device power_ldm = {
+       .name = "pmic_power",
+       .id = 1,
+};
+static struct platform_device rtc_ldm = {
+       .name = "pmic_rtc",
+       .id = 1,
+};
+static struct platform_device light_ldm = {
+       .name = "pmic_light",
+       .id = 1,
+};
+static struct platform_device rleds_ldm = {
+       .name = "pmic_leds",
+       .id = 'r',
+};
+static struct platform_device gleds_ldm = {
+       .name = "pmic_leds",
+       .id = 'g',
+};
+static struct platform_device bleds_ldm = {
+       .name = "pmic_leds",
+       .id = 'b',
+};
+
+static void pmic_pdev_register(struct device *dev)
+{
+       platform_device_register(&adc_ldm);
+
+       if (!cpu_is_mx53())
+               platform_device_register(&battery_ldm);
+
+       platform_device_register(&rtc_ldm);
+       platform_device_register(&power_ldm);
+       platform_device_register(&light_ldm);
+       platform_device_register(&rleds_ldm);
+       platform_device_register(&gleds_ldm);
+       platform_device_register(&bleds_ldm);
+}
+
+/*!
+ * This function unregisters platform device structures for
+ * PMIC client drivers.
+ */
+static void pmic_pdev_unregister(void)
+{
+       platform_device_unregister(&adc_ldm);
+       platform_device_unregister(&battery_ldm);
+       platform_device_unregister(&rtc_ldm);
+       platform_device_unregister(&power_ldm);
+       platform_device_unregister(&light_ldm);
+}
+
+static int __devinit is_chip_onboard(struct i2c_client *client)
+{
+       unsigned int ret = 0;
+
+       /*bind the right device to the driver */
+       if (pmic_i2c_24bit_read(client, REG_IDENTIFICATION, &ret) == -1)
+               return -1;
+
+       if (MC13892_GEN_ID_VALUE != BITFEXT(ret, MC13892_GENERATION_ID)) {
+               /*compare the address value */
+               dev_err(&client->dev,
+                       "read generation ID 0x%x is not equal to 0x%x!\n",
+                       BITFEXT(ret, MC13892_GENERATION_ID),
+                       MC13892_GEN_ID_VALUE);
+               return -1;
+       }
+
+       return 0;
+}
+
+static ssize_t mc13892_show(struct device *dev,
+                           struct device_attribute *attr, char *buf)
+{
+       int i, value;
+       int offset = (REG_TEST4 + 1) / 4;
+
+       for (i = 0; i < offset; i++) {
+               pmic_read(i, &value);
+               pr_info("reg%02d: %06x\t\t", i, value);
+               pmic_read(i + offset, &value);
+               pr_info("reg%02d: %06x\t\t", i + offset, value);
+               pmic_read(i + offset * 2, &value);
+               pr_info("reg%02d: %06x\t\t", i + offset * 2, value);
+               pmic_read(i + offset * 3, &value);
+               pr_info("reg%02d: %06x\n", i + offset * 3, value);
+       }
+
+       return 0;
+}
+
+static ssize_t mc13892_store(struct device *dev,
+                            struct device_attribute *attr, const char *buf,
+                            size_t count)
+{
+       int reg, value, ret;
+       char *p;
+
+       reg = simple_strtoul(buf, NULL, 10);
+
+       p = NULL;
+       p = memchr(buf, ' ', count);
+
+       if (p == NULL) {
+               pmic_read(reg, &value);
+               pr_debug("reg%02d: %06x\n", reg, value);
+               return count;
+       }
+
+       p += 1;
+
+       value = simple_strtoul(p, NULL, 16);
+
+       ret = pmic_write(reg, value);
+       if (ret == 0)
+               pr_debug("write reg%02d: %06x\n", reg, value);
+       else
+               pr_debug("register update failed\n");
+
+       return count;
+}
+
+static struct device_attribute mc13892_dev_attr = {
+       .attr = {
+                .name = "mc13892_ctl",
+                .mode = S_IRUSR | S_IWUSR,
+                },
+       .show = mc13892_show,
+       .store = mc13892_store,
+};
+
+static int __devinit pmic_probe(struct i2c_client *client,
+                               const struct i2c_device_id *id)
+{
+       int ret = 0;
+       int pmic_irq;
+       struct mc13892 *mc13892;
+       struct mc13892_platform_data *plat_data = client->dev.platform_data;
+
+       ret = is_chip_onboard(client);
+       if (ret == -1)
+               return -ENODEV;
+
+       mc13892 = kzalloc(sizeof(struct mc13892), GFP_KERNEL);
+       if (mc13892 == NULL)
+               return -ENOMEM;
+
+       i2c_set_clientdata(client, mc13892);
+       mc13892->dev = &client->dev;
+       mc13892->i2c_client = client;
+
+       /* so far, we got matched chip on board */
+
+       mc13892_client = client;
+
+       /* Initialize the PMIC event handling */
+       pmic_event_list_init();
+
+       /* Initialize GPIO for PMIC Interrupt */
+       gpio_pmic_active();
+
+       /* Get the PMIC Version */
+       pmic_get_revision(&mxc_pmic_version);
+       if (mxc_pmic_version.revision < 0) {
+               dev_err((struct device *)client,
+                       "PMIC not detected!!! Access Failed\n");
+               return -ENODEV;
+       } else {
+               dev_dbg((struct device *)client,
+                       "Detected pmic core IC version number is %d\n",
+                       mxc_pmic_version.revision);
+       }
+
+       /* Initialize the PMIC parameters */
+       ret = pmic_init_registers();
+       if (ret != PMIC_SUCCESS)
+               return PMIC_ERROR;
+
+       pmic_irq = (int)(client->irq);
+       if (pmic_irq == 0)
+               return PMIC_ERROR;
+
+       ret = pmic_start_event_thread(pmic_irq);
+       if (ret) {
+               pr_err("mc13892 pmic driver init: \
+                       fail to start event thread\n");
+               return PMIC_ERROR;
+       }
+
+       /* Set and install PMIC IRQ handler */
+
+       set_irq_type(pmic_irq, IRQF_TRIGGER_HIGH);
+
+       ret =
+           request_irq(pmic_irq, pmic_irq_handler, 0, "PMIC_IRQ",
+                       0);
+
+       if (ret) {
+               dev_err(&client->dev, "request irq %d error!\n", pmic_irq);
+               return ret;
+       }
+       enable_irq_wake(pmic_irq);
+
+       if (plat_data && plat_data->init) {
+               ret = plat_data->init(mc13892);
+               if (ret != 0)
+                       return PMIC_ERROR;
+       }
+
+       ret = device_create_file(&client->dev, &mc13892_dev_attr);
+       if (ret)
+               dev_err(&client->dev, "create device file failed!\n");
+
+       pmic_pdev_register(&client->dev);
+
+       dev_info(&client->dev, "Loaded\n");
+
+       return PMIC_SUCCESS;
+}
+
+static int pmic_remove(struct i2c_client *client)
+{
+       int pmic_irq = (int)(client->irq);
+
+       pmic_stop_event_thread();
+       free_irq(pmic_irq, 0);
+       pmic_pdev_unregister();
+       return 0;
+}
+
+static int pmic_suspend(struct i2c_client *client, pm_message_t state)
+{
+       return 0;
+}
+
+static int pmic_resume(struct i2c_client *client)
+{
+       return 0;
+}
+
+static const struct i2c_device_id mc13892_id[] = {
+       {"mc13892", 0},
+       {},
+};
+
+MODULE_DEVICE_TABLE(i2c, mc13892_id);
+
+static struct i2c_driver pmic_driver = {
+       .driver = {
+                  .name = "mc13892",
+                  .bus = NULL,
+                  },
+       .probe = pmic_probe,
+       .remove = pmic_remove,
+       .suspend = pmic_suspend,
+       .resume = pmic_resume,
+       .id_table = mc13892_id,
+};
+
+static int __init pmic_init(void)
+{
+       return i2c_add_driver(&pmic_driver);
+}
+
+static void __exit pmic_exit(void)
+{
+       i2c_del_driver(&pmic_driver);
+}
+
+/*
+ * Module entry points
+ */
+subsys_initcall_sync(pmic_init);
+module_exit(pmic_exit);
+
+MODULE_DESCRIPTION("Core/Protocol driver for PMIC");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pmic/core/pmic_core_spi.c b/drivers/mxc/pmic/core/pmic_core_spi.c
new file mode 100644 (file)
index 0000000..df0a895
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+ * 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 pmic_core_spi.c
+ * @brief This is the main file for the PMIC Core/Protocol driver. SPI
+ * should be providing the interface between the PMIC and the MCU.
+ *
+ * @ingroup PMIC_CORE
+ */
+
+/*
+ * Includes
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/spi/spi.h>
+#include <linux/pmic_external.h>
+#include <linux/pmic_status.h>
+
+#include <asm/uaccess.h>
+
+#include "pmic.h"
+
+/*
+ * Static functions
+ */
+static void pmic_pdev_register(void);
+static void pmic_pdev_unregister(void);
+
+/*
+ * Platform device structure for PMIC client drivers
+ */
+static struct platform_device adc_ldm = {
+       .name = "pmic_adc",
+       .id = 1,
+};
+static struct platform_device battery_ldm = {
+       .name = "pmic_battery",
+       .id = 1,
+};
+static struct platform_device power_ldm = {
+       .name = "pmic_power",
+       .id = 1,
+};
+static struct platform_device rtc_ldm = {
+       .name = "pmic_rtc",
+       .id = 1,
+};
+static struct platform_device light_ldm = {
+       .name = "pmic_light",
+       .id = 1,
+};
+static struct platform_device rleds_ldm = {
+       .name = "pmic_leds",
+       .id = 'r',
+};
+static struct platform_device gleds_ldm = {
+       .name = "pmic_leds",
+       .id = 'g',
+};
+static struct platform_device bleds_ldm = {
+       .name = "pmic_leds",
+       .id = 'b',
+};
+
+/*
+ * External functions
+ */
+extern void pmic_event_list_init(void);
+extern void pmic_event_callback(type_event event);
+extern void gpio_pmic_active(void);
+extern irqreturn_t pmic_irq_handler(int irq, void *dev_id);
+extern pmic_version_t mxc_pmic_version;
+
+/*!
+ * This function registers platform device structures for
+ * PMIC client drivers.
+ */
+static void pmic_pdev_register(void)
+{
+       platform_device_register(&adc_ldm);
+       platform_device_register(&battery_ldm);
+       platform_device_register(&rtc_ldm);
+       platform_device_register(&power_ldm);
+       platform_device_register(&light_ldm);
+       platform_device_register(&rleds_ldm);
+       platform_device_register(&gleds_ldm);
+       platform_device_register(&bleds_ldm);
+}
+
+/*!
+ * This function unregisters platform device structures for
+ * PMIC client drivers.
+ */
+static void pmic_pdev_unregister(void)
+{
+       platform_device_unregister(&adc_ldm);
+       platform_device_unregister(&battery_ldm);
+       platform_device_unregister(&rtc_ldm);
+       platform_device_unregister(&power_ldm);
+       platform_device_unregister(&light_ldm);
+}
+
+/*!
+ * This function puts the SPI slave device in low-power mode/state.
+ *
+ * @param      spi     the SPI slave device
+ * @param      message the power state to enter
+ *
+ * @return     Returns 0 on SUCCESS and error on FAILURE.
+ */
+static int pmic_suspend(struct spi_device *spi, pm_message_t message)
+{
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function brings the SPI slave device back from low-power mode/state.
+ *
+ * @param      spi     the SPI slave device
+ *
+ * @return     Returns 0 on SUCCESS and error on FAILURE.
+ */
+static int pmic_resume(struct spi_device *spi)
+{
+       return PMIC_SUCCESS;
+}
+
+static struct spi_driver pmic_driver;
+
+/*!
+ * 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 pmic_probe(struct spi_device *spi)
+{
+       int ret = 0;
+       struct pmic_platform_data *plat_data = spi->dev.platform_data;
+
+       /* Initialize the PMIC parameters */
+       ret = pmic_spi_setup(spi);
+       if (ret != PMIC_SUCCESS) {
+               return PMIC_ERROR;
+       }
+
+       /* Initialize the PMIC event handling */
+       pmic_event_list_init();
+
+       /* Initialize GPIO for PMIC Interrupt */
+       gpio_pmic_active();
+
+       /* Get the PMIC Version */
+       pmic_get_revision(&mxc_pmic_version);
+       if (mxc_pmic_version.revision < 0) {
+               dev_err((struct device *)spi,
+                       "PMIC not detected!!! Access Failed\n");
+               return -ENODEV;
+       } else {
+               dev_dbg((struct device *)spi,
+                       "Detected pmic core IC version number is %d\n",
+                       mxc_pmic_version.revision);
+       }
+
+       spi_set_drvdata(spi, pmic_alloc_data(&(spi->dev)));
+
+       /* Initialize the PMIC parameters */
+       ret = pmic_init_registers();
+       if (ret != PMIC_SUCCESS) {
+               kfree(spi_get_drvdata(spi));
+               spi_set_drvdata(spi, NULL);
+               return PMIC_ERROR;
+       }
+
+       ret = pmic_start_event_thread(spi->irq);
+       if (ret) {
+               pr_err("mc13892 pmic driver init: \
+                       fail to start event thread\n");
+               kfree(spi_get_drvdata(spi));
+               spi_set_drvdata(spi, NULL);
+               return PMIC_ERROR;
+       }
+
+       /* Set and install PMIC IRQ handler */
+       set_irq_type(spi->irq, IRQF_TRIGGER_HIGH);
+       ret = request_irq(spi->irq, pmic_irq_handler, 0, "PMIC_IRQ", 0);
+       if (ret) {
+               kfree(spi_get_drvdata(spi));
+               spi_set_drvdata(spi, NULL);
+               dev_err((struct device *)spi, "gpio1: irq%d error.", spi->irq);
+               return ret;
+       }
+
+       enable_irq_wake(spi->irq);
+
+       if (plat_data && plat_data->init) {
+               ret = plat_data->init(spi_get_drvdata(spi));
+               if (ret != 0) {
+                       kfree(spi_get_drvdata(spi));
+                       spi_set_drvdata(spi, NULL);
+                       return PMIC_ERROR;
+               }
+       }
+
+       power_ldm.dev.platform_data = spi->dev.platform_data;
+
+       pmic_pdev_register();
+
+       printk(KERN_INFO "Device %s probed\n", dev_name(&spi->dev));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function is called whenever the SPI slave device is removed.
+ *
+ * @param      spi     the SPI slave device
+ *
+ * @return     Returns 0 on SUCCESS and error on FAILURE.
+ */
+static int __devexit pmic_remove(struct spi_device *spi)
+{
+       pmic_stop_event_thread();
+       free_irq(spi->irq, 0);
+
+       pmic_pdev_unregister();
+
+       printk(KERN_INFO "Device %s removed\n", dev_name(&spi->dev));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct spi_driver pmic_driver = {
+       .driver = {
+                  .name = "pmic_spi",
+                  .bus = &spi_bus_type,
+                  .owner = THIS_MODULE,
+                  },
+       .probe = pmic_probe,
+       .remove = __devexit_p(pmic_remove),
+       .suspend = pmic_suspend,
+       .resume = pmic_resume,
+};
+
+/*
+ * Initialization and Exit
+ */
+
+/*!
+ * This function implements the init function of the PMIC device.
+ * This function is called when the module is loaded. It registers
+ * the PMIC Protocol driver.
+ *
+ * @return       This function returns 0.
+ */
+static int __init pmic_init(void)
+{
+       return spi_register_driver(&pmic_driver);
+}
+
+/*!
+ * This function implements the exit function of the PMIC device.
+ * This function is called when the module is unloaded. It unregisters
+ * the PMIC Protocol driver.
+ *
+ */
+static void __exit pmic_exit(void)
+{
+       pr_debug("Unregistering the PMIC Protocol Driver\n");
+       spi_unregister_driver(&pmic_driver);
+}
+
+/*
+ * Module entry points
+ */
+subsys_initcall_sync(pmic_init);
+module_exit(pmic_exit);
+
+MODULE_DESCRIPTION("Core/Protocol driver for PMIC");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pmic/core/pmic_event.c b/drivers/mxc/pmic/core/pmic_event.c
new file mode 100644 (file)
index 0000000..5ab593b
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ * 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 pmic_event.c
+ * @brief This file manage all event of PMIC component.
+ *
+ * It contains event subscription, unsubscription and callback
+ * launch methods implemeted.
+ *
+ * @ingroup PMIC_CORE
+ */
+
+/*
+ * Includes
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/pmic_external.h>
+#include <linux/pmic_status.h>
+#include "pmic.h"
+
+/*!
+ * This structure is used to keep a list of subscribed
+ * callbacks for an event.
+ */
+typedef struct {
+       /*!
+        * Keeps a list of subscribed clients to an event.
+        */
+       struct list_head list;
+
+       /*!
+        * Callback function with parameter, called when event occurs
+        */
+       pmic_event_callback_t callback;
+} pmic_event_callback_list_t;
+
+/* Create a mutex to be used to prevent concurrent access to the event list */
+static DEFINE_MUTEX(event_mutex);
+
+/* This is a pointer to the event handler array. It defines the currently
+ * active set of events and user-defined callback functions.
+ */
+static struct list_head pmic_events[PMIC_MAX_EVENTS];
+
+/*!
+ * This function initializes event list for PMIC event handling.
+ *
+ */
+void pmic_event_list_init(void)
+{
+       int i;
+
+       for (i = 0; i < PMIC_MAX_EVENTS; i++) {
+               INIT_LIST_HEAD(&pmic_events[i]);
+       }
+
+       mutex_init(&event_mutex);
+       return;
+}
+
+/*!
+ * This function is used to subscribe on an event.
+ *
+ * @param      event   the event number to be subscribed
+ * @param      callback the callback funtion to be subscribed
+ *
+ * @return       This function returns 0 on SUCCESS, error on FAILURE.
+ */
+PMIC_STATUS pmic_event_subscribe(type_event event,
+                                pmic_event_callback_t callback)
+{
+       pmic_event_callback_list_t *new = NULL;
+
+       pr_debug("Event:%d Subscribe\n", event);
+
+       /* Check whether the event & callback are valid? */
+       if (event >= PMIC_MAX_EVENTS) {
+               pr_debug("Invalid Event:%d\n", event);
+               return -EINVAL;
+       }
+       if (NULL == callback.func) {
+               pr_debug("Null or Invalid Callback\n");
+               return -EINVAL;
+       }
+
+       /* Create a new linked list entry */
+       new = kmalloc(sizeof(pmic_event_callback_list_t), GFP_KERNEL);
+       if (NULL == new) {
+               return -ENOMEM;
+       }
+       /* Initialize the list node fields */
+       new->callback.func = callback.func;
+       new->callback.param = callback.param;
+       INIT_LIST_HEAD(&new->list);
+
+       /* Obtain the lock to access the list */
+       if (mutex_lock_interruptible(&event_mutex)) {
+               kfree(new);
+               return PMIC_SYSTEM_ERROR_EINTR;
+       }
+
+       /* Unmask the requested event */
+       if (list_empty(&pmic_events[event])) {
+               if (pmic_event_unmask(event) != PMIC_SUCCESS) {
+                       kfree(new);
+                       mutex_unlock(&event_mutex);
+                       return PMIC_ERROR;
+               }
+       }
+
+       /* Add this entry to the event list */
+       list_add_tail(&new->list, &pmic_events[event]);
+
+       /* Release the lock */
+       mutex_unlock(&event_mutex);
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function is used to unsubscribe on an event.
+ *
+ * @param      event   the event number to be unsubscribed
+ * @param      callback the callback funtion to be unsubscribed
+ *
+ * @return       This function returns 0 on SUCCESS, error on FAILURE.
+ */
+PMIC_STATUS pmic_event_unsubscribe(type_event event,
+                                  pmic_event_callback_t callback)
+{
+       struct list_head *p;
+       struct list_head *n;
+       pmic_event_callback_list_t *temp = NULL;
+       int ret = PMIC_EVENT_NOT_SUBSCRIBED;
+
+       pr_debug("Event:%d Unsubscribe\n", event);
+
+       /* Check whether the event & callback are valid? */
+       if (event >= PMIC_MAX_EVENTS) {
+               pr_debug("Invalid Event:%d\n", event);
+               return -EINVAL;
+       }
+
+       if (NULL == callback.func) {
+               pr_debug("Null or Invalid Callback\n");
+               return -EINVAL;
+       }
+
+       /* Obtain the lock to access the list */
+       if (mutex_lock_interruptible(&event_mutex)) {
+               return PMIC_SYSTEM_ERROR_EINTR;
+       }
+
+       /* Find the entry in the list */
+       list_for_each_safe(p, n, &pmic_events[event]) {
+               temp = list_entry(p, pmic_event_callback_list_t, list);
+               if (temp->callback.func == callback.func
+                   && temp->callback.param == callback.param) {
+                       /* Remove the entry from the list */
+                       list_del(p);
+                       kfree(temp);
+                       ret = PMIC_SUCCESS;
+                       break;
+               }
+       }
+
+       /* Unmask the requested event */
+       if (list_empty(&pmic_events[event])) {
+               if (pmic_event_mask(event) != PMIC_SUCCESS) {
+                       ret = PMIC_UNSUBSCRIBE_ERROR;
+               }
+       }
+
+       /* Release the lock */
+       mutex_unlock(&event_mutex);
+
+       return ret;
+}
+
+/*!
+ * This function calls all callback of a specific event.
+ *
+ * @param      event   the active event number
+ *
+ * @return     None
+ */
+void pmic_event_callback(type_event event)
+{
+       struct list_head *p;
+       pmic_event_callback_list_t *temp = NULL;
+
+       /* Obtain the lock to access the list */
+       if (mutex_lock_interruptible(&event_mutex)) {
+               return;
+       }
+
+       if (list_empty(&pmic_events[event])) {
+               pr_debug("PMIC Event:%d detected. No callback subscribed\n",
+                        event);
+               mutex_unlock(&event_mutex);
+               return;
+       }
+
+       list_for_each(p, &pmic_events[event]) {
+               temp = list_entry(p, pmic_event_callback_list_t, list);
+               temp->callback.func(temp->callback.param);
+       }
+
+       /* Release the lock */
+       mutex_unlock(&event_mutex);
+
+       return;
+
+}
+
+EXPORT_SYMBOL(pmic_event_subscribe);
+EXPORT_SYMBOL(pmic_event_unsubscribe);
diff --git a/drivers/mxc/pmic/core/pmic_external.c b/drivers/mxc/pmic/core/pmic_external.c
new file mode 100644 (file)
index 0000000..b9faae0
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * 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 pmic_external.c
+ * @brief This file contains all external functions of PMIC drivers.
+ *
+ * @ingroup PMIC_CORE
+ */
+
+/*
+ * Includes
+ */
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+
+#include <linux/pmic_external.h>
+#include <linux/pmic_status.h>
+
+/*
+ * External Functions
+ */
+extern int pmic_read(int reg_num, unsigned int *reg_val);
+extern int pmic_write(int reg_num, const unsigned int reg_val);
+
+/*!
+ * This function is called by PMIC clients to read a register on PMIC.
+ *
+ * @param        reg        number of register
+ * @param        reg_value  return value of register
+ * @param        reg_mask   Bitmap mask indicating which bits to modify
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_read_reg(int reg, unsigned int *reg_value,
+                         unsigned int reg_mask)
+{
+       int ret = 0;
+       unsigned int temp = 0;
+
+       ret = pmic_read(reg, &temp);
+       if (ret != PMIC_SUCCESS) {
+               return PMIC_ERROR;
+       }
+       *reg_value = (temp & reg_mask);
+
+       pr_debug("Read REG[ %d ] = 0x%x\n", reg, *reg_value);
+
+       return ret;
+}
+
+/*!
+ * This function is called by PMIC clients to write a register on PMIC.
+ *
+ * @param        reg        number of register
+ * @param        reg_value  New value of register
+ * @param        reg_mask   Bitmap mask indicating which bits to modify
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_write_reg(int reg, unsigned int reg_value,
+                          unsigned int reg_mask)
+{
+       int ret = 0;
+       unsigned int temp = 0;
+
+       ret = pmic_read(reg, &temp);
+       if (ret != PMIC_SUCCESS) {
+               return PMIC_ERROR;
+       }
+       temp = (temp & (~reg_mask)) | reg_value;
+#ifdef CONFIG_MXC_PMIC_MC13783
+       if (reg == REG_POWER_MISCELLANEOUS)
+               temp &= 0xFFFE7FFF;
+#endif
+       ret = pmic_write(reg, temp);
+       if (ret != PMIC_SUCCESS) {
+               return PMIC_ERROR;
+       }
+
+       pr_debug("Write REG[ %d ] = 0x%x\n", reg, reg_value);
+
+       return ret;
+}
+
+EXPORT_SYMBOL(pmic_read_reg);
+EXPORT_SYMBOL(pmic_write_reg);
diff --git a/drivers/mxc/pmic/mc13783/Kconfig b/drivers/mxc/pmic/mc13783/Kconfig
new file mode 100644 (file)
index 0000000..02496c6
--- /dev/null
@@ -0,0 +1,55 @@
+#
+# PMIC Modules configuration
+#
+
+config MXC_MC13783_ADC
+       tristate "MC13783 ADC support"
+       depends on MXC_PMIC_MC13783
+       ---help---
+       This is the MC13783 ADC module driver. This module provides kernel API
+       for the ADC system of MC13783.
+       It controls also the touch screen interface.
+       If you want MC13783 ADC support, you should say Y here
+
+config MXC_MC13783_AUDIO
+       tristate "MC13783 Audio support"
+       depends on MXC_PMIC_MC13783
+       ---help---
+       This is the MC13783 audio module driver. This module provides kernel API
+       for audio part of MC13783.
+       If you want MC13783 audio support, you should say Y here
+config MXC_MC13783_RTC
+       tristate "MC13783 Real Time Clock (RTC) support"
+       depends on MXC_PMIC_MC13783
+       ---help---
+       This is the MC13783 RTC module driver. This module provides kernel API
+       for RTC part of MC13783.
+       If you want MC13783 RTC support, you should say Y here
+config MXC_MC13783_LIGHT
+       tristate "MC13783 Light and Backlight support"
+       depends on MXC_PMIC_MC13783
+       ---help---
+       This is the MC13783 Light module driver. This module provides kernel API
+       for led and backlight control part of MC13783.
+       If you want MC13783 Light support, you should say Y here
+config MXC_MC13783_BATTERY
+       tristate "MC13783 Battery API support"
+       depends on MXC_PMIC_MC13783
+       ---help---
+       This is the MC13783 battery module driver. This module provides kernel API
+       for battery control part of MC13783.
+       If you want MC13783 battery support, you should say Y here
+config MXC_MC13783_CONNECTIVITY
+       tristate "MC13783 Connectivity API support"
+       depends on MXC_PMIC_MC13783
+       ---help---
+       This is the MC13783 connectivity module driver. This module provides kernel API
+       for USB/RS232 connectivity control part of MC13783.
+       If you want MC13783 connectivity support, you should say Y here
+config MXC_MC13783_POWER
+       tristate "MC13783 Power API support"
+       depends on MXC_PMIC_MC13783
+       ---help---
+       This is the MC13783 power and supplies module driver. This module provides kernel API
+       for power and regulator control part of MC13783.
+       If you want MC13783 power support, you should say Y here
diff --git a/drivers/mxc/pmic/mc13783/Makefile b/drivers/mxc/pmic/mc13783/Makefile
new file mode 100644 (file)
index 0000000..7bbba23
--- /dev/null
@@ -0,0 +1,18 @@
+#
+# Makefile for the mc13783 pmic drivers.
+#
+
+obj-$(CONFIG_MXC_MC13783_ADC) += pmic_adc-mod.o
+obj-$(CONFIG_MXC_MC13783_AUDIO) += pmic_audio-mod.o
+obj-$(CONFIG_MXC_MC13783_RTC) += pmic_rtc-mod.o
+obj-$(CONFIG_MXC_MC13783_LIGHT) += pmic_light-mod.o
+obj-$(CONFIG_MXC_MC13783_BATTERY) += pmic_battery-mod.o
+obj-$(CONFIG_MXC_MC13783_CONNECTIVITY) += pmic_convity-mod.o
+obj-$(CONFIG_MXC_MC13783_POWER) += pmic_power-mod.o
+pmic_adc-mod-objs := pmic_adc.o
+pmic_audio-mod-objs := pmic_audio.o
+pmic_rtc-mod-objs := pmic_rtc.o
+pmic_light-mod-objs := pmic_light.o
+pmic_battery-mod-objs := pmic_battery.o
+pmic_convity-mod-objs := pmic_convity.o
+pmic_power-mod-objs := pmic_power.o
diff --git a/drivers/mxc/pmic/mc13783/pmic_adc.c b/drivers/mxc/pmic/mc13783/pmic_adc.c
new file mode 100644 (file)
index 0000000..b8ec3b8
--- /dev/null
@@ -0,0 +1,1541 @@
+/*
+ * 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 mc13783/pmic_adc.c
+ * @brief This is the main file of PMIC(mc13783) ADC driver.
+ *
+ * @ingroup PMIC_ADC
+ */
+
+/*
+ * Includes
+ */
+
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/pmic_adc.h>
+#include <linux/pmic_status.h>
+
+#include "../core/pmic.h"
+#include "pmic_adc_defs.h"
+
+#define NB_ADC_REG      5
+
+static int pmic_adc_major;
+
+/* internal function */
+static void callback_tsi(void *);
+static void callback_adcdone(void *);
+static void callback_adcbisdone(void *);
+static void callback_adc_comp_high(void *);
+
+/*!
+ * Number of users waiting in suspendq
+ */
+static int swait;
+
+/*!
+ * To indicate whether any of the adc devices are suspending
+ */
+static int suspend_flag;
+
+/*!
+ * The suspendq is used by blocking application calls
+ */
+static wait_queue_head_t suspendq;
+
+static struct class *pmic_adc_class;
+
+/*
+ * ADC mc13783 API
+ */
+/* EXPORTED FUNCTIONS */
+EXPORT_SYMBOL(pmic_adc_init);
+EXPORT_SYMBOL(pmic_adc_deinit);
+EXPORT_SYMBOL(pmic_adc_convert);
+EXPORT_SYMBOL(pmic_adc_convert_8x);
+EXPORT_SYMBOL(pmic_adc_convert_multichnnel);
+EXPORT_SYMBOL(pmic_adc_set_touch_mode);
+EXPORT_SYMBOL(pmic_adc_get_touch_mode);
+EXPORT_SYMBOL(pmic_adc_get_touch_sample);
+EXPORT_SYMBOL(pmic_adc_get_battery_current);
+EXPORT_SYMBOL(pmic_adc_active_comparator);
+EXPORT_SYMBOL(pmic_adc_deactive_comparator);
+
+static DECLARE_COMPLETION(adcdone_it);
+static DECLARE_COMPLETION(adcbisdone_it);
+static DECLARE_COMPLETION(adc_tsi);
+static pmic_event_callback_t tsi_event;
+static pmic_event_callback_t event_adc;
+static pmic_event_callback_t event_adc_bis;
+static pmic_event_callback_t adc_comp_h;
+static bool data_ready_adc_1;
+static bool data_ready_adc_2;
+static bool adc_ts;
+static bool wait_ts;
+static bool monitor_en;
+static bool monitor_adc;
+static t_check_mode wcomp_mode;
+static DECLARE_MUTEX(convert_mutex);
+
+void (*monitoring_cb) (void);  /*call back to be called when event is detected. */
+
+static DECLARE_WAIT_QUEUE_HEAD(queue_adc_busy);
+static t_adc_state adc_dev[2];
+
+static unsigned channel_num[] = {
+       0,
+       1,
+       3,
+       4,
+       2,
+       12,
+       13,
+       14,
+       15,
+       -1,
+       5,
+       6,
+       7,
+       8,
+       9,
+       10,
+       11,
+       7,
+       6,
+       -1,
+       -1,
+       -1,
+       -1,
+       5,
+       7
+};
+
+static bool pmic_adc_ready;
+
+int is_pmic_adc_ready()
+{
+       return pmic_adc_ready;
+}
+EXPORT_SYMBOL(is_pmic_adc_ready);
+
+
+/*!
+ * This is the suspend of power management for the mc13783 ADC API.
+ * It supports SAVE and POWER_DOWN state.
+ *
+ * @param        pdev           the device
+ * @param        state          the state
+ *
+ * @return       This function returns 0 if successful.
+ */
+static int pmic_adc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       unsigned int reg_value = 0;
+       suspend_flag = 1;
+       CHECK_ERROR(pmic_write_reg(REG_ADC_0, DEF_ADC_0, PMIC_ALL_BITS));
+       CHECK_ERROR(pmic_write_reg(REG_ADC_1, reg_value, PMIC_ALL_BITS));
+       CHECK_ERROR(pmic_write_reg(REG_ADC_2, reg_value, PMIC_ALL_BITS));
+       CHECK_ERROR(pmic_write_reg(REG_ADC_3, DEF_ADC_3, PMIC_ALL_BITS));
+       CHECK_ERROR(pmic_write_reg(REG_ADC_4, reg_value, PMIC_ALL_BITS));
+
+       return 0;
+};
+
+/*!
+ * This is the resume of power management for the mc13783 adc API.
+ * It supports RESTORE state.
+ *
+ * @param        pdev           the device
+ *
+ * @return       This function returns 0 if successful.
+ */
+static int pmic_adc_resume(struct platform_device *pdev)
+{
+       /* nothing for mc13783 adc */
+       unsigned int adc_0_reg, adc_1_reg;
+       suspend_flag = 0;
+
+       /* let interrupt of TSI again */
+       adc_0_reg = ADC_WAIT_TSI_0;
+       CHECK_ERROR(pmic_write_reg(REG_ADC_0, adc_0_reg, PMIC_ALL_BITS));
+       adc_1_reg = ADC_WAIT_TSI_1 | (ADC_BIS * adc_ts);
+       CHECK_ERROR(pmic_write_reg(REG_ADC_1, adc_1_reg, PMIC_ALL_BITS));
+
+       while (swait > 0) {
+               swait--;
+               wake_up_interruptible(&suspendq);
+       }
+
+       return 0;
+};
+
+/*
+ * Call back functions
+ */
+
+/*!
+ * This is the callback function called on TSI mc13783 event, used in synchronous call.
+ */
+static void callback_tsi(void *unused)
+{
+       pr_debug("*** TSI IT mc13783 PMIC_ADC_GET_TOUCH_SAMPLE ***\n");
+       if (wait_ts) {
+               complete(&adc_tsi);
+               pmic_event_mask(EVENT_TSI);
+       }
+}
+
+/*!
+ * This is the callback function called on ADCDone mc13783 event.
+ */
+static void callback_adcdone(void *unused)
+{
+       if (data_ready_adc_1) {
+               complete(&adcdone_it);
+       }
+}
+
+/*!
+ * This is the callback function called on ADCDone mc13783 event.
+ */
+static void callback_adcbisdone(void *unused)
+{
+       pr_debug("* adcdone bis it callback *\n");
+       if (data_ready_adc_2) {
+               complete(&adcbisdone_it);
+       }
+}
+
+/*!
+ * This is the callback function called on mc13783 event.
+ */
+static void callback_adc_comp_high(void *unused)
+{
+       pr_debug("* adc comp it high *\n");
+       if (wcomp_mode == CHECK_HIGH || wcomp_mode == CHECK_LOW_OR_HIGH) {
+               /* launch callback */
+               if (monitoring_cb != NULL) {
+                       monitoring_cb();
+               }
+       }
+}
+
+/*!
+ * This function performs filtering and rejection of excessive noise prone
+ * samples.
+ *
+ * @param        ts_curr     Touch screen value
+ *
+ * @return       This function returns 0 on success, -1 otherwise.
+ */
+static int pmic_adc_filter(t_touch_screen *ts_curr)
+{
+       unsigned int ydiff1, ydiff2, ydiff3, xdiff1, xdiff2, xdiff3;
+       unsigned int sample_sumx, sample_sumy;
+       static unsigned int prev_x[FILTLEN], prev_y[FILTLEN];
+       int index = 0;
+       unsigned int y_curr, x_curr;
+       static int filt_count;
+       /* Added a variable filt_type to decide filtering at run-time */
+       unsigned int filt_type = 0;
+
+       if (ts_curr->contact_resistance == 0) {
+               ts_curr->x_position = 0;
+               ts_curr->y_position = 0;
+               filt_count = 0;
+               return 0;
+       }
+
+       ydiff1 = abs(ts_curr->y_position1 - ts_curr->y_position2);
+       ydiff2 = abs(ts_curr->y_position2 - ts_curr->y_position3);
+       ydiff3 = abs(ts_curr->y_position1 - ts_curr->y_position3);
+       if ((ydiff1 > DELTA_Y_MAX) ||
+           (ydiff2 > DELTA_Y_MAX) || (ydiff3 > DELTA_Y_MAX)) {
+               pr_debug("pmic_adc_filter: Ret pos 1\n");
+               return -1;
+       }
+
+       xdiff1 = abs(ts_curr->x_position1 - ts_curr->x_position2);
+       xdiff2 = abs(ts_curr->x_position2 - ts_curr->x_position3);
+       xdiff3 = abs(ts_curr->x_position1 - ts_curr->x_position3);
+
+       if ((xdiff1 > DELTA_X_MAX) ||
+           (xdiff2 > DELTA_X_MAX) || (xdiff3 > DELTA_X_MAX)) {
+               pr_debug("mc13783_adc_filter: Ret pos 2\n");
+               return -1;
+       }
+       /* Compute two closer values among the three available Y readouts */
+
+       if (ydiff1 < ydiff2) {
+               if (ydiff1 < ydiff3) {
+                       /* Sample 0 & 1 closest together */
+                       sample_sumy = ts_curr->y_position1 +
+                           ts_curr->y_position2;
+               } else {
+                       /* Sample 0 & 2 closest together */
+                       sample_sumy = ts_curr->y_position1 +
+                           ts_curr->y_position3;
+               }
+       } else {
+               if (ydiff2 < ydiff3) {
+                       /* Sample 1 & 2 closest together */
+                       sample_sumy = ts_curr->y_position2 +
+                           ts_curr->y_position3;
+               } else {
+                       /* Sample 0 & 2 closest together */
+                       sample_sumy = ts_curr->y_position1 +
+                           ts_curr->y_position3;
+               }
+       }
+
+       /*
+        * Compute two closer values among the three available X
+        * readouts
+        */
+       if (xdiff1 < xdiff2) {
+               if (xdiff1 < xdiff3) {
+                       /* Sample 0 & 1 closest together */
+                       sample_sumx = ts_curr->x_position1 +
+                           ts_curr->x_position2;
+               } else {
+                       /* Sample 0 & 2 closest together */
+                       sample_sumx = ts_curr->x_position1 +
+                           ts_curr->x_position3;
+               }
+       } else {
+               if (xdiff2 < xdiff3) {
+                       /* Sample 1 & 2 closest together */
+                       sample_sumx = ts_curr->x_position2 +
+                           ts_curr->x_position3;
+               } else {
+                       /* Sample 0 & 2 closest together */
+                       sample_sumx = ts_curr->x_position1 +
+                           ts_curr->x_position3;
+               }
+       }
+       /*
+        * Wait FILTER_MIN_DELAY number of samples to restart
+        * filtering
+        */
+       if (filt_count < FILTER_MIN_DELAY) {
+               /*
+                * Current output is the average of the two closer
+                * values and no filtering is used
+                */
+               y_curr = (sample_sumy / 2);
+               x_curr = (sample_sumx / 2);
+               ts_curr->y_position = y_curr;
+               ts_curr->x_position = x_curr;
+               filt_count++;
+       } else {
+               if (abs(sample_sumx - (prev_x[0] + prev_x[1])) >
+                   (DELTA_X_MAX * 16)) {
+                       pr_debug("pmic_adc_filter: : Ret pos 3\n");
+                       return -1;
+               }
+               if (abs(sample_sumy - (prev_y[0] + prev_y[1])) >
+                   (DELTA_Y_MAX * 16)) {
+                       return -1;
+               }
+               sample_sumy /= 2;
+               sample_sumx /= 2;
+               /* Use hard filtering if the sample difference < 10 */
+               if ((abs(sample_sumy - prev_y[0]) > 10) ||
+                   (abs(sample_sumx - prev_x[0]) > 10)) {
+                       filt_type = 1;
+               }
+
+               /*
+                * Current outputs are the average of three previous
+                * values and the present readout
+                */
+               y_curr = sample_sumy;
+               for (index = 0; index < FILTLEN; index++) {
+                       if (filt_type == 0) {
+                               y_curr = y_curr + (prev_y[index]);
+                       } else {
+                               y_curr = y_curr + (prev_y[index] / 3);
+                       }
+               }
+               if (filt_type == 0) {
+                       y_curr = y_curr >> 2;
+               } else {
+                       y_curr = y_curr >> 1;
+               }
+               ts_curr->y_position = y_curr;
+
+               x_curr = sample_sumx;
+               for (index = 0; index < FILTLEN; index++) {
+                       if (filt_type == 0) {
+                               x_curr = x_curr + (prev_x[index]);
+                       } else {
+                               x_curr = x_curr + (prev_x[index] / 3);
+                       }
+               }
+               if (filt_type == 0) {
+                       x_curr = x_curr >> 2;
+               } else {
+                       x_curr = x_curr >> 1;
+               }
+               ts_curr->x_position = x_curr;
+
+       }
+
+       /* Update previous X and Y values */
+       for (index = (FILTLEN - 1); index > 0; index--) {
+               prev_x[index] = prev_x[index - 1];
+               prev_y[index] = prev_y[index - 1];
+       }
+
+       /*
+        * Current output will be the most recent past for the
+        * next sample
+        */
+       prev_y[0] = y_curr;
+       prev_x[0] = x_curr;
+
+       return 0;
+}
+
+/*!
+ * This function implements the open method on a MC13783 ADC device.
+ *
+ * @param        inode       pointer on the node
+ * @param        file        pointer on the file
+ * @return       This function returns 0.
+ */
+static int pmic_adc_open(struct inode *inode, struct file *file)
+{
+       while (suspend_flag == 1) {
+               swait++;
+               /* Block if the device is suspended */
+               if (wait_event_interruptible(suspendq, (suspend_flag == 0))) {
+                       return -ERESTARTSYS;
+               }
+       }
+       pr_debug("mc13783_adc : mc13783_adc_open()\n");
+       return 0;
+}
+
+/*!
+ * This function implements the release method on a MC13783 ADC device.
+ *
+ * @param        inode       pointer on the node
+ * @param        file        pointer on the file
+ * @return       This function returns 0.
+ */
+static int pmic_adc_free(struct inode *inode, struct file *file)
+{
+       pr_debug("mc13783_adc : mc13783_adc_free()\n");
+       return 0;
+}
+
+/*!
+ * This function initializes all ADC registers with default values. This
+ * function also registers the interrupt events.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+int pmic_adc_init(void)
+{
+       unsigned int reg_value = 0, i = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+       for (i = 0; i < ADC_NB_AVAILABLE; i++) {
+               adc_dev[i] = ADC_FREE;
+       }
+       CHECK_ERROR(pmic_write_reg(REG_ADC_0, DEF_ADC_0, PMIC_ALL_BITS));
+       CHECK_ERROR(pmic_write_reg(REG_ADC_1, reg_value, PMIC_ALL_BITS));
+       CHECK_ERROR(pmic_write_reg(REG_ADC_2, reg_value, PMIC_ALL_BITS));
+       CHECK_ERROR(pmic_write_reg(REG_ADC_3, DEF_ADC_3, PMIC_ALL_BITS));
+       CHECK_ERROR(pmic_write_reg(REG_ADC_4, reg_value, PMIC_ALL_BITS));
+       reg_value = 0x001000;
+       CHECK_ERROR(pmic_write_reg(REG_ARBITRATION_PERIPHERAL_AUDIO, reg_value,
+                                  0xFFFFFF));
+
+       data_ready_adc_1 = false;
+       data_ready_adc_2 = false;
+       adc_ts = false;
+       wait_ts = false;
+       monitor_en = false;
+       monitor_adc = false;
+       wcomp_mode = CHECK_LOW;
+       monitoring_cb = NULL;
+       /* sub to ADCDone IT */
+       event_adc.param = NULL;
+       event_adc.func = callback_adcdone;
+       CHECK_ERROR(pmic_event_subscribe(EVENT_ADCDONEI, event_adc));
+
+       /* sub to ADCDoneBis IT */
+       event_adc_bis.param = NULL;
+       event_adc_bis.func = callback_adcbisdone;
+       CHECK_ERROR(pmic_event_subscribe(EVENT_ADCBISDONEI, event_adc_bis));
+
+       /* sub to Touch Screen IT */
+       tsi_event.param = NULL;
+       tsi_event.func = callback_tsi;
+       CHECK_ERROR(pmic_event_subscribe(EVENT_TSI, tsi_event));
+
+       /* ADC reading above high limit */
+       adc_comp_h.param = NULL;
+       adc_comp_h.func = callback_adc_comp_high;
+       CHECK_ERROR(pmic_event_subscribe(EVENT_WHIGHI, adc_comp_h));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function disables the ADC, de-registers the interrupt events.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_adc_deinit(void)
+{
+       CHECK_ERROR(pmic_event_unsubscribe(EVENT_ADCDONEI, event_adc));
+       CHECK_ERROR(pmic_event_unsubscribe(EVENT_ADCBISDONEI, event_adc_bis));
+       CHECK_ERROR(pmic_event_unsubscribe(EVENT_TSI, tsi_event));
+       CHECK_ERROR(pmic_event_unsubscribe(EVENT_WHIGHI, adc_comp_h));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function initializes adc_param structure.
+ *
+ * @param        adc_param     Structure to be initialized.
+ *
+ * @return       This function returns 0 if successful.
+ */
+int mc13783_adc_init_param(t_adc_param *adc_param)
+{
+       int i = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+       adc_param->delay = 0;
+       adc_param->conv_delay = false;
+       adc_param->single_channel = false;
+       adc_param->group = false;
+       adc_param->channel_0 = BATTERY_VOLTAGE;
+       adc_param->channel_1 = BATTERY_VOLTAGE;
+       adc_param->read_mode = 0;
+       adc_param->wait_tsi = 0;
+       adc_param->chrgraw_devide_5 = true;
+       adc_param->read_ts = false;
+       adc_param->ts_value.x_position = 0;
+       adc_param->ts_value.y_position = 0;
+       adc_param->ts_value.contact_resistance = 0;
+       for (i = 0; i <= MAX_CHANNEL; i++) {
+               adc_param->value[i] = 0;
+       }
+       return 0;
+}
+
+/*!
+ * This function starts the convert.
+ *
+ * @param        adc_param      contains all adc configuration and return value.
+ *
+ * @return       This function returns 0 if successful.
+ */
+PMIC_STATUS mc13783_adc_convert(t_adc_param *adc_param)
+{
+       bool use_bis = false;
+       unsigned int adc_0_reg = 0, adc_1_reg = 0, reg_1 = 0, result_reg =
+           0, i = 0;
+       unsigned int result = 0, temp = 0;
+       pmic_version_t mc13783_ver;
+       pr_debug("mc13783 ADC - mc13783_adc_convert ....\n");
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       if (adc_param->wait_tsi) {
+               /* we need to set ADCEN 1 for TSI interrupt on mc13783 1.x */
+               /* configure adc to wait tsi interrupt */
+               INIT_COMPLETION(adc_tsi);
+               pr_debug("mc13783 ADC - pmic_write_reg ....\n");
+               /*for ts don't use bis */
+               adc_0_reg = 0x001c00 | (ADC_BIS * 0);
+               pmic_event_unmask(EVENT_TSI);
+               CHECK_ERROR(pmic_write_reg
+                           (REG_ADC_0, adc_0_reg, PMIC_ALL_BITS));
+               /*for ts don't use bis */
+               adc_1_reg = 0x200001 | (ADC_BIS * 0);
+               CHECK_ERROR(pmic_write_reg
+                           (REG_ADC_1, adc_1_reg, PMIC_ALL_BITS));
+               pr_debug("wait tsi ....\n");
+               wait_ts = true;
+               wait_for_completion_interruptible(&adc_tsi);
+               wait_ts = false;
+       }
+       if (adc_param->read_ts == false)
+               down(&convert_mutex);
+       use_bis = mc13783_adc_request(adc_param->read_ts);
+       if (use_bis < 0) {
+               pr_debug("process has received a signal and got interrupted\n");
+               return -EINTR;
+       }
+
+       /* CONFIGURE ADC REG 0 */
+       adc_0_reg = 0;
+       adc_1_reg = 0;
+       if (adc_param->read_ts == false) {
+               adc_0_reg = adc_param->read_mode & 0x00003F;
+               /* add auto inc */
+               adc_0_reg |= ADC_INC;
+               if (use_bis) {
+                       /* add adc bis */
+                       adc_0_reg |= ADC_BIS;
+               }
+               mc13783_ver = pmic_get_version();
+               if (mc13783_ver.revision >= 20) {
+                       if (adc_param->chrgraw_devide_5) {
+                               adc_0_reg |= ADC_CHRGRAW_D5;
+                       }
+               }
+               if (adc_param->single_channel) {
+                       adc_1_reg |= ADC_SGL_CH;
+               }
+
+               if (adc_param->conv_delay) {
+                       adc_1_reg |= ADC_ATO;
+               }
+
+               if (adc_param->group) {
+                       adc_1_reg |= ADC_ADSEL;
+               }
+
+               if (adc_param->single_channel) {
+                       adc_1_reg |= ADC_SGL_CH;
+               }
+
+               adc_1_reg |= (adc_param->channel_0 << ADC_CH_0_POS) &
+                   ADC_CH_0_MASK;
+               adc_1_reg |= (adc_param->channel_1 << ADC_CH_1_POS) &
+                   ADC_CH_1_MASK;
+       } else {
+               adc_0_reg = 0x003c00 | (ADC_BIS * use_bis) | ADC_INC;
+       }
+       pr_debug("Write Reg %i = %x\n", REG_ADC_0, adc_0_reg);
+       /*Change has been made here */
+       CHECK_ERROR(pmic_write_reg(REG_ADC_0, adc_0_reg,
+                                  ADC_INC | ADC_BIS | ADC_CHRGRAW_D5 |
+                                  0xfff00ff));
+       /* CONFIGURE ADC REG 1 */
+       if (adc_param->read_ts == false) {
+               adc_1_reg |= ADC_NO_ADTRIG;
+               adc_1_reg |= ADC_EN;
+               adc_1_reg |= (adc_param->delay << ADC_DELAY_POS) &
+                   ADC_DELAY_MASK;
+               if (use_bis) {
+                       adc_1_reg |= ADC_BIS;
+               }
+       } else {
+               /* configure and start convert to read x and y position */
+               /* configure to read 2 value in channel selection 1 & 2 */
+               adc_1_reg = 0x100409 | (ADC_BIS * use_bis) | ADC_NO_ADTRIG;
+       }
+       reg_1 = adc_1_reg;
+       if (use_bis == 0) {
+               data_ready_adc_1 = false;
+               adc_1_reg |= ASC_ADC;
+               data_ready_adc_1 = true;
+               pr_debug("Write Reg %i = %x\n", REG_ADC_1, adc_1_reg);
+               INIT_COMPLETION(adcdone_it);
+               CHECK_ERROR(pmic_write_reg(REG_ADC_1, adc_1_reg,
+                                          ADC_SGL_CH | ADC_ATO | ADC_ADSEL
+                                          | ADC_CH_0_MASK | ADC_CH_1_MASK |
+                                          ADC_NO_ADTRIG | ADC_EN |
+                                          ADC_DELAY_MASK | ASC_ADC | ADC_BIS));
+               pr_debug("wait adc done \n");
+               wait_for_completion_interruptible(&adcdone_it);
+               data_ready_adc_1 = false;
+       } else {
+               data_ready_adc_2 = false;
+               adc_1_reg |= ASC_ADC;
+               data_ready_adc_2 = true;
+               INIT_COMPLETION(adcbisdone_it);
+               CHECK_ERROR(pmic_write_reg(REG_ADC_1, adc_1_reg, 0xFFFFFF));
+               temp = 0x800000;
+               CHECK_ERROR(pmic_write_reg(REG_ADC_3, temp, 0xFFFFFF));
+               temp = 0x001000;
+               pmic_write_reg(REG_ARBITRATION_PERIPHERAL_AUDIO, temp,
+                              0xFFFFFF);
+               pr_debug("wait adc done bis\n");
+               wait_for_completion_interruptible(&adcbisdone_it);
+               data_ready_adc_2 = false;
+       }
+       /* read result and store in adc_param */
+       result = 0;
+       if (use_bis == 0) {
+               result_reg = REG_ADC_2;
+       } else {
+               result_reg = REG_ADC_4;
+       }
+       CHECK_ERROR(pmic_write_reg(REG_ADC_1, 4 << ADC_CH_1_POS,
+                                  ADC_CH_0_MASK | ADC_CH_1_MASK));
+
+       for (i = 0; i <= 3; i++) {
+               CHECK_ERROR(pmic_read_reg(result_reg, &result, PMIC_ALL_BITS));
+               pr_debug("result %i = %x\n", result_reg, result);
+               adc_param->value[i] = ((result & ADD1_RESULT_MASK) >> 2);
+               adc_param->value[i + 4] = ((result & ADD2_RESULT_MASK) >> 14);
+       }
+       if (adc_param->read_ts) {
+               adc_param->ts_value.x_position = adc_param->value[2];
+               adc_param->ts_value.x_position1 = adc_param->value[0];
+               adc_param->ts_value.x_position2 = adc_param->value[1];
+               adc_param->ts_value.x_position3 = adc_param->value[2];
+               adc_param->ts_value.y_position1 = adc_param->value[3];
+               adc_param->ts_value.y_position2 = adc_param->value[4];
+               adc_param->ts_value.y_position3 = adc_param->value[5];
+               adc_param->ts_value.y_position = adc_param->value[5];
+               adc_param->ts_value.contact_resistance = adc_param->value[6];
+
+       }
+
+       /*if (adc_param->read_ts) {
+          adc_param->ts_value.x_position = adc_param->value[2];
+          adc_param->ts_value.y_position = adc_param->value[5];
+          adc_param->ts_value.contact_resistance = adc_param->value[6];
+          } */
+       mc13783_adc_release(use_bis);
+       if (adc_param->read_ts == false)
+               up(&convert_mutex);
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function select the required read_mode for a specific channel.
+ *
+ * @param        channel   The channel to be sampled
+ *
+ * @return       This function returns the requires read_mode
+ */
+t_reading_mode mc13783_set_read_mode(t_channel channel)
+{
+       t_reading_mode read_mode = 0;
+
+       switch (channel) {
+       case LICELL:
+               read_mode = M_LITHIUM_CELL;
+               break;
+       case CHARGE_CURRENT:
+               read_mode = M_CHARGE_CURRENT;
+               break;
+       case BATTERY_CURRENT:
+               read_mode = M_BATTERY_CURRENT;
+               break;
+       case THERMISTOR:
+               read_mode = M_THERMISTOR;
+               break;
+       case DIE_TEMP:
+               read_mode = M_DIE_TEMPERATURE;
+               break;
+       case USB_ID:
+               read_mode = M_UID;
+               break;
+       default:
+               read_mode = 0;
+       }
+
+       return read_mode;
+}
+
+/*!
+ * This function triggers a conversion and returns one sampling result of one
+ * channel.
+ *
+ * @param        channel   The channel to be sampled
+ * @param        result    The pointer to the conversion result. The memory
+ *                         should be allocated by the caller of this function.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_adc_convert(t_channel channel, unsigned short *result)
+{
+       t_adc_param adc_param;
+       PMIC_STATUS ret;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       channel = channel_num[channel];
+       if (channel == -1) {
+               pr_debug("Wrong channel ID\n");
+               return PMIC_PARAMETER_ERROR;
+       }
+       mc13783_adc_init_param(&adc_param);
+       pr_debug("pmic_adc_convert\n");
+       adc_param.read_ts = false;
+       adc_param.read_mode = mc13783_set_read_mode(channel);
+
+       adc_param.single_channel = true;
+       /* Find the group */
+       if ((channel >= 0) && (channel <= 7)) {
+               adc_param.channel_0 = channel;
+               adc_param.group = false;
+       } else if ((channel >= 8) && (channel <= 15)) {
+               adc_param.channel_0 = channel & 0x07;
+               adc_param.group = true;
+       } else {
+               return PMIC_PARAMETER_ERROR;
+       }
+       ret = mc13783_adc_convert(&adc_param);
+       *result = adc_param.value[0];
+       return ret;
+}
+
+/*!
+ * This function triggers a conversion and returns eight sampling results of
+ * one channel.
+ *
+ * @param        channel   The channel to be sampled
+ * @param        result    The pointer to array to store eight sampling results.
+ *                         The memory should be allocated by the caller of this
+ *                         function.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_adc_convert_8x(t_channel channel, unsigned short *result)
+{
+       t_adc_param adc_param;
+       int i;
+       PMIC_STATUS ret;
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       channel = channel_num[channel];
+
+       if (channel == -1) {
+               pr_debug("Wrong channel ID\n");
+               return PMIC_PARAMETER_ERROR;
+       }
+       mc13783_adc_init_param(&adc_param);
+       pr_debug("pmic_adc_convert_8x\n");
+       adc_param.read_ts = false;
+       adc_param.single_channel = true;
+       adc_param.read_mode = mc13783_set_read_mode(channel);
+       if ((channel >= 0) && (channel <= 7)) {
+               adc_param.channel_0 = channel;
+               adc_param.channel_1 = channel;
+               adc_param.group = false;
+       } else if ((channel >= 8) && (channel <= 15)) {
+               adc_param.channel_0 = channel & 0x07;
+               adc_param.channel_1 = channel & 0x07;
+               adc_param.group = true;
+       } else {
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       ret = mc13783_adc_convert(&adc_param);
+       for (i = 0; i <= 7; i++) {
+               result[i] = adc_param.value[i];
+       }
+       return ret;
+}
+
+/*!
+ * This function triggers a conversion and returns sampling results of each
+ * specified channel.
+ *
+ * @param        channels  This input parameter is bitmap to specify channels
+ *                         to be sampled.
+ * @param        result    The pointer to array to store sampling results.
+ *                         The memory should be allocated by the caller of this
+ *                         function.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_adc_convert_multichnnel(t_channel channels,
+                                        unsigned short *result)
+{
+       t_adc_param adc_param;
+       int i;
+       PMIC_STATUS ret;
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+       mc13783_adc_init_param(&adc_param);
+       pr_debug("pmic_adc_convert_multichnnel\n");
+
+       channels = channel_num[channels];
+
+       if (channels == -1) {
+               pr_debug("Wrong channel ID\n");
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       adc_param.read_ts = false;
+       adc_param.single_channel = false;
+       if ((channels >= 0) && (channels <= 7)) {
+               adc_param.channel_0 = channels;
+               adc_param.channel_1 = ((channels + 4) % 4) + 4;
+               adc_param.group = false;
+       } else if ((channels >= 8) && (channels <= 15)) {
+               channels = channels & 0x07;
+               adc_param.channel_0 = channels;
+               adc_param.channel_1 = ((channels + 4) % 4) + 4;
+               adc_param.group = true;
+       } else {
+               return PMIC_PARAMETER_ERROR;
+       }
+       adc_param.read_mode = 0x00003f;
+       adc_param.read_ts = false;
+       ret = mc13783_adc_convert(&adc_param);
+
+       for (i = 0; i <= 7; i++) {
+               result[i] = adc_param.value[i];
+       }
+       return ret;
+}
+
+/*!
+ * This function sets touch screen operation mode.
+ *
+ * @param        touch_mode   Touch screen operation mode.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_adc_set_touch_mode(t_touch_mode touch_mode)
+{
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+       CHECK_ERROR(pmic_write_reg(REG_ADC_0,
+                                  BITFVAL(MC13783_ADC0_TS_M, touch_mode),
+                                  BITFMASK(MC13783_ADC0_TS_M)));
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function retrieves the current touch screen operation mode.
+ *
+ * @param        touch_mode   Pointer to the retrieved touch screen operation
+ *                            mode.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_adc_get_touch_mode(t_touch_mode *touch_mode)
+{
+       unsigned int value;
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+       CHECK_ERROR(pmic_read_reg(REG_ADC_0, &value, PMIC_ALL_BITS));
+
+       *touch_mode = BITFEXT(value, MC13783_ADC0_TS_M);
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function retrieves the current touch screen (X,Y) coordinates.
+ *
+ * @param        touch_sample Pointer to touch sample.
+ * @param        wait  indicates whether this call must block or not.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_adc_get_touch_sample(t_touch_screen *touch_sample, int wait)
+{
+       if (mc13783_adc_read_ts(touch_sample, wait) != 0)
+               return PMIC_ERROR;
+       if (0 == pmic_adc_filter(touch_sample))
+               return PMIC_SUCCESS;
+       else
+               return PMIC_ERROR;
+}
+
+/*!
+ * This function read the touch screen value.
+ *
+ * @param        ts_value    return value of touch screen
+ * @param        wait_tsi    if true, this function is synchronous (wait in TSI event).
+ *
+ * @return       This function returns 0.
+ */
+PMIC_STATUS mc13783_adc_read_ts(t_touch_screen *ts_value, int wait_tsi)
+{
+       t_adc_param param;
+       pr_debug("mc13783_adc : mc13783_adc_read_ts\n");
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+       if (wait_ts) {
+               pr_debug("mc13783_adc : error TS busy \n");
+               return PMIC_ERROR;
+       }
+       mc13783_adc_init_param(&param);
+       param.wait_tsi = wait_tsi;
+       param.read_ts = true;
+       if (mc13783_adc_convert(&param) != 0)
+               return PMIC_ERROR;
+       /* check if x-y is ok */
+       if ((param.ts_value.x_position1 < TS_X_MAX) &&
+           (param.ts_value.x_position1 >= TS_X_MIN) &&
+           (param.ts_value.y_position1 < TS_Y_MAX) &&
+           (param.ts_value.y_position1 >= TS_Y_MIN) &&
+           (param.ts_value.x_position2 < TS_X_MAX) &&
+           (param.ts_value.x_position2 >= TS_X_MIN) &&
+           (param.ts_value.y_position2 < TS_Y_MAX) &&
+           (param.ts_value.y_position2 >= TS_Y_MIN) &&
+           (param.ts_value.x_position3 < TS_X_MAX) &&
+           (param.ts_value.x_position3 >= TS_X_MIN) &&
+           (param.ts_value.y_position3 < TS_Y_MAX) &&
+           (param.ts_value.y_position3 >= TS_Y_MIN)) {
+               ts_value->x_position = param.ts_value.x_position;
+               ts_value->x_position1 = param.ts_value.x_position1;
+               ts_value->x_position2 = param.ts_value.x_position2;
+               ts_value->x_position3 = param.ts_value.x_position3;
+               ts_value->y_position = param.ts_value.y_position;
+               ts_value->y_position1 = param.ts_value.y_position1;
+               ts_value->y_position2 = param.ts_value.y_position2;
+               ts_value->y_position3 = param.ts_value.y_position3;
+               ts_value->contact_resistance =
+                   param.ts_value.contact_resistance + 1;
+
+       } else {
+               ts_value->x_position = 0;
+               ts_value->y_position = 0;
+               ts_value->contact_resistance = 0;
+
+       }
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function starts a Battery Current mode conversion.
+ *
+ * @param        mode      Conversion mode.
+ * @param        result    Battery Current measurement result.
+ *                         if \a mode = ADC_8CHAN_1X, the result is \n
+ *                             result[0] = (BATTP - BATT_I) \n
+ *                         if \a mode = ADC_1CHAN_8X, the result is \n
+ *                             result[0] = BATTP \n
+ *                             result[1] = BATT_I \n
+ *                             result[2] = BATTP \n
+ *                             result[3] = BATT_I \n
+ *                             result[4] = BATTP \n
+ *                             result[5] = BATT_I \n
+ *                             result[6] = BATTP \n
+ *                             result[7] = BATT_I
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_adc_get_battery_current(t_conversion_mode mode,
+                                        unsigned short *result)
+{
+       PMIC_STATUS ret;
+       t_channel channel;
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+       channel = BATTERY_CURRENT;
+       if (mode == ADC_8CHAN_1X) {
+               ret = pmic_adc_convert(channel, result);
+       } else {
+               ret = pmic_adc_convert_8x(channel, result);
+       }
+       return ret;
+}
+
+/*!
+ * This function request a ADC.
+ *
+ * @return      This function returns index of ADC to be used (0 or 1) if successful.
+ * return -1 if error.
+ */
+int mc13783_adc_request(bool read_ts)
+{
+       int adc_index = -1;
+       if (read_ts != 0) {
+               /*for ts we use bis=0 */
+               if (adc_dev[0] == ADC_USED)
+                       return -1;
+               /*no wait here */
+               adc_dev[0] = ADC_USED;
+               adc_index = 0;
+       } else {
+               /*for other adc use bis = 1 */
+               if (adc_dev[1] == ADC_USED) {
+                       return -1;
+                       /*no wait here */
+               }
+               adc_dev[1] = ADC_USED;
+               adc_index = 1;
+       }
+       pr_debug("mc13783_adc : request ADC %d\n", adc_index);
+       return adc_index;
+}
+
+/*!
+ * This function release an ADC.
+ *
+ * @param        adc_index     index of ADC to be released.
+ *
+ * @return       This function returns 0 if successful.
+ */
+int mc13783_adc_release(int adc_index)
+{
+       while (suspend_flag == 1) {
+               swait++;
+               /* Block if the device is suspended */
+               if (wait_event_interruptible(suspendq, (suspend_flag == 0))) {
+                       return -ERESTARTSYS;
+               }
+       }
+
+       pr_debug("mc13783_adc : release ADC %d\n", adc_index);
+       if ((adc_dev[adc_index] == ADC_MONITORING) ||
+           (adc_dev[adc_index] == ADC_USED)) {
+               adc_dev[adc_index] = ADC_FREE;
+               wake_up(&queue_adc_busy);
+               return 0;
+       }
+       return -1;
+}
+
+/*!
+ * This function initializes monitoring structure.
+ *
+ * @param        monitor     Structure to be initialized.
+ *
+ * @return       This function returns 0 if successful.
+ */
+int mc13783_adc_init_monitor_param(t_monitoring_param *monitor)
+{
+       pr_debug("mc13783_adc : init monitor\n");
+       monitor->delay = 0;
+       monitor->conv_delay = false;
+       monitor->channel = BATTERY_VOLTAGE;
+       monitor->read_mode = 0;
+       monitor->comp_low = 0;
+       monitor->comp_high = 0;
+       monitor->group = 0;
+       monitor->check_mode = CHECK_LOW_OR_HIGH;
+       monitor->callback = NULL;
+       return 0;
+}
+
+/*!
+ * This function actives the comparator.  When comparator is active and ADC
+ * is enabled, the 8th converted value will be digitally compared against the
+ * window defined by WLOW and WHIGH registers.
+ *
+ * @param        low      Comparison window low threshold (WLOW).
+ * @param        high     Comparison window high threshold (WHIGH).
+ * @param        channel  The channel to be sampled
+ * @param        callback Callback function to be called when the converted
+ *                        value is beyond the comparison window.  The callback
+ *                        function will pass a parameter of type
+ *                        \b t_comp_expection to indicate the reason of
+ *                        comparator exception.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_adc_active_comparator(unsigned char low,
+                                      unsigned char high,
+                                      t_channel channel,
+                                      t_comparator_cb callback)
+{
+       bool use_bis = false;
+       unsigned int adc_0_reg = 0, adc_1_reg = 0, adc_3_reg = 0;
+       t_monitoring_param monitoring;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+       if (monitor_en) {
+               pr_debug("mc13783_adc : monitoring already configured\n");
+               return PMIC_ERROR;
+       }
+       monitor_en = true;
+       mc13783_adc_init_monitor_param(&monitoring);
+       monitoring.comp_low = low;
+       monitoring.comp_high = high;
+       monitoring.channel = channel;
+       monitoring.callback = (void *)callback;
+
+       use_bis = mc13783_adc_request(false);
+       if (use_bis < 0) {
+               pr_debug("mc13783_adc : request error\n");
+               return PMIC_ERROR;
+       }
+       monitor_adc = use_bis;
+
+       adc_0_reg = 0;
+
+       /* TO DO ADOUT CONFIGURE */
+       adc_0_reg = monitoring.read_mode & ADC_MODE_MASK;
+       if (use_bis) {
+               /* add adc bis */
+               adc_0_reg |= ADC_BIS;
+       }
+       adc_0_reg |= ADC_WCOMP;
+
+       /* CONFIGURE ADC REG 1 */
+       adc_1_reg = 0;
+       adc_1_reg |= ADC_EN;
+       if (monitoring.conv_delay) {
+               adc_1_reg |= ADC_ATO;
+       }
+       if (monitoring.group) {
+               adc_1_reg |= ADC_ADSEL;
+       }
+       adc_1_reg |= (monitoring.channel << ADC_CH_0_POS) & ADC_CH_0_MASK;
+       adc_1_reg |= (monitoring.delay << ADC_DELAY_POS) & ADC_DELAY_MASK;
+       if (use_bis) {
+               adc_1_reg |= ADC_BIS;
+       }
+
+       adc_3_reg |= (monitoring.comp_high << ADC_WCOMP_H_POS) &
+           ADC_WCOMP_H_MASK;
+       adc_3_reg |= (monitoring.comp_low << ADC_WCOMP_L_POS) &
+           ADC_WCOMP_L_MASK;
+       if (use_bis) {
+               adc_3_reg |= ADC_BIS;
+       }
+
+       wcomp_mode = monitoring.check_mode;
+       /* call back to be called when event is detected. */
+       monitoring_cb = monitoring.callback;
+
+       CHECK_ERROR(pmic_write_reg(REG_ADC_0, adc_0_reg, PMIC_ALL_BITS));
+       CHECK_ERROR(pmic_write_reg(REG_ADC_1, adc_1_reg, PMIC_ALL_BITS));
+       CHECK_ERROR(pmic_write_reg(REG_ADC_3, adc_3_reg, PMIC_ALL_BITS));
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function deactivates the comparator.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_adc_deactive_comparator(void)
+{
+       unsigned int reg_value = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+       if (!monitor_en) {
+               pr_debug("mc13783_adc : adc monitoring free\n");
+               return PMIC_ERROR;
+       }
+
+       if (monitor_en) {
+               reg_value = ADC_BIS;
+       }
+
+       /* clear all reg value */
+       CHECK_ERROR(pmic_write_reg(REG_ADC_0, reg_value, PMIC_ALL_BITS));
+       CHECK_ERROR(pmic_write_reg(REG_ADC_1, reg_value, PMIC_ALL_BITS));
+       CHECK_ERROR(pmic_write_reg(REG_ADC_3, reg_value, PMIC_ALL_BITS));
+
+       reg_value = 0;
+
+       if (monitor_adc) {
+               CHECK_ERROR(pmic_write_reg
+                           (REG_ADC_4, reg_value, PMIC_ALL_BITS));
+       } else {
+               CHECK_ERROR(pmic_write_reg
+                           (REG_ADC_2, reg_value, PMIC_ALL_BITS));
+       }
+
+       mc13783_adc_release(monitor_adc);
+       monitor_en = false;
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function implements IOCTL controls on a MC13783 ADC device.
+ *
+ * @param        inode       pointer on the node
+ * @param        file        pointer on the file
+ * @param        cmd         the command
+ * @param        arg         the parameter
+ * @return       This function returns 0 if successful.
+ */
+static int pmic_adc_ioctl(struct inode *inode, struct file *file,
+                         unsigned int cmd, unsigned long arg)
+{
+       t_adc_convert_param *convert_param;
+       t_touch_mode touch_mode;
+       t_touch_screen touch_sample;
+       unsigned short b_current;
+       t_adc_comp_param *comp_param;
+       if ((_IOC_TYPE(cmd) != 'p') && (_IOC_TYPE(cmd) != 'D'))
+               return -ENOTTY;
+
+       while (suspend_flag == 1) {
+               swait++;
+               /* Block if the device is suspended */
+               if (wait_event_interruptible(suspendq, (suspend_flag == 0))) {
+                       return -ERESTARTSYS;
+               }
+       }
+
+       convert_param = kmalloc(sizeof(t_adc_convert_param), GFP_KERNEL);
+       comp_param = kmalloc(sizeof(t_adc_comp_param), GFP_KERNEL);
+       switch (cmd) {
+       case PMIC_ADC_INIT:
+               pr_debug("init adc\n");
+               CHECK_ERROR(pmic_adc_init());
+               break;
+
+       case PMIC_ADC_DEINIT:
+               pr_debug("deinit adc\n");
+               CHECK_ERROR(pmic_adc_deinit());
+               break;
+
+       case PMIC_ADC_CONVERT:
+               if (convert_param  == NULL)
+                       return -ENOMEM;
+
+               if (copy_from_user(convert_param, (t_adc_convert_param *) arg,
+                                  sizeof(t_adc_convert_param))) {
+                       kfree(convert_param);
+                       return -EFAULT;
+               }
+               CHECK_ERROR_KFREE(pmic_adc_convert(convert_param->channel,
+                                                  convert_param->result),
+                                 (kfree(convert_param)));
+
+               if (copy_to_user((t_adc_convert_param *) arg, convert_param,
+                                sizeof(t_adc_convert_param))) {
+                       kfree(convert_param);
+                       return -EFAULT;
+               }
+               kfree(convert_param);
+               break;
+
+       case PMIC_ADC_CONVERT_8X:
+               if (convert_param == NULL)
+                       return -ENOMEM;
+
+               if (copy_from_user(convert_param, (t_adc_convert_param *) arg,
+                                  sizeof(t_adc_convert_param))) {
+                       kfree(convert_param);
+                       return -EFAULT;
+               }
+               CHECK_ERROR_KFREE(pmic_adc_convert_8x(convert_param->channel,
+                                                     convert_param->result),
+                                 (kfree(convert_param)));
+
+               if (copy_to_user((t_adc_convert_param *) arg, convert_param,
+                                sizeof(t_adc_convert_param))) {
+                       kfree(convert_param);
+                       return -EFAULT;
+               }
+               kfree(convert_param);
+               break;
+
+       case PMIC_ADC_CONVERT_MULTICHANNEL:
+               if (convert_param == NULL)
+                       return -ENOMEM;
+
+               if (copy_from_user(convert_param, (t_adc_convert_param *) arg,
+                                  sizeof(t_adc_convert_param))) {
+                       kfree(convert_param);
+                       return -EFAULT;
+               }
+
+               CHECK_ERROR_KFREE(pmic_adc_convert_multichnnel
+                                 (convert_param->channel,
+                                  convert_param->result),
+                                 (kfree(convert_param)));
+
+               if (copy_to_user((t_adc_convert_param *) arg, convert_param,
+                                sizeof(t_adc_convert_param))) {
+                       kfree(convert_param);
+                       return -EFAULT;
+               }
+               kfree(convert_param);
+               break;
+
+       case PMIC_ADC_SET_TOUCH_MODE:
+               CHECK_ERROR(pmic_adc_set_touch_mode((t_touch_mode) arg));
+               break;
+
+       case PMIC_ADC_GET_TOUCH_MODE:
+               CHECK_ERROR(pmic_adc_get_touch_mode(&touch_mode));
+               if (copy_to_user((t_touch_mode *) arg, &touch_mode,
+                                sizeof(t_touch_mode))) {
+                       return -EFAULT;
+               }
+               break;
+
+       case PMIC_ADC_GET_TOUCH_SAMPLE:
+               pr_debug("pmic_adc_ioctl: " "PMIC_ADC_GET_TOUCH_SAMPLE\n");
+               CHECK_ERROR(pmic_adc_get_touch_sample(&touch_sample, 1));
+               if (copy_to_user((t_touch_screen *) arg, &touch_sample,
+                                sizeof(t_touch_screen))) {
+                       return -EFAULT;
+               }
+               break;
+
+       case PMIC_ADC_GET_BATTERY_CURRENT:
+               CHECK_ERROR(pmic_adc_get_battery_current(ADC_8CHAN_1X,
+                                                        &b_current));
+               if (copy_to_user((unsigned short *)arg, &b_current,
+                                sizeof(unsigned short))) {
+
+                       return -EFAULT;
+               }
+               break;
+
+       case PMIC_ADC_ACTIVATE_COMPARATOR:
+               if (comp_param == NULL)
+                       return -ENOMEM;
+
+               if (copy_from_user(comp_param, (t_adc_comp_param *) arg,
+                                  sizeof(t_adc_comp_param))) {
+                       kfree(comp_param);
+                       return -EFAULT;
+               }
+               CHECK_ERROR_KFREE(pmic_adc_active_comparator(comp_param->wlow,
+                                                            comp_param->whigh,
+                                                            comp_param->
+                                                            channel,
+                                                            comp_param->
+                                                            callback),
+                                 (kfree(comp_param)));
+               break;
+
+       case PMIC_ADC_DEACTIVE_COMPARATOR:
+               CHECK_ERROR(pmic_adc_deactive_comparator());
+               break;
+
+       default:
+               pr_debug("pmic_adc_ioctl: unsupported ioctl command 0x%x\n",
+                        cmd);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static struct file_operations mc13783_adc_fops = {
+       .owner = THIS_MODULE,
+       .ioctl = pmic_adc_ioctl,
+       .open = pmic_adc_open,
+       .release = pmic_adc_free,
+};
+
+static int pmic_adc_module_probe(struct platform_device *pdev)
+{
+       int ret = 0;
+       struct device *temp_class;
+
+       pmic_adc_major = register_chrdev(0, "pmic_adc", &mc13783_adc_fops);
+
+       if (pmic_adc_major < 0) {
+               pr_debug(KERN_ERR "Unable to get a major for pmic_adc\n");
+               return pmic_adc_major;
+       }
+       init_waitqueue_head(&suspendq);
+
+       pmic_adc_class = class_create(THIS_MODULE, "pmic_adc");
+       if (IS_ERR(pmic_adc_class)) {
+               pr_debug(KERN_ERR "Error creating pmic_adc class.\n");
+               ret = PTR_ERR(pmic_adc_class);
+               goto err_out1;
+       }
+
+       temp_class = device_create(pmic_adc_class, NULL,
+                                  MKDEV(pmic_adc_major, 0), NULL, "pmic_adc");
+       if (IS_ERR(temp_class)) {
+               pr_debug(KERN_ERR "Error creating pmic_adc class device.\n");
+               ret = PTR_ERR(temp_class);
+               goto err_out2;
+       }
+
+       ret = pmic_adc_init();
+       if (ret != PMIC_SUCCESS) {
+               pr_debug(KERN_ERR "Error in pmic_adc_init.\n");
+               goto err_out4;
+       }
+
+       pmic_adc_ready = 1;
+       pr_debug(KERN_INFO "PMIC ADC successfully probed\n");
+       return ret;
+
+      err_out4:
+       device_destroy(pmic_adc_class, MKDEV(pmic_adc_major, 0));
+      err_out2:
+       class_destroy(pmic_adc_class);
+      err_out1:
+       unregister_chrdev(pmic_adc_major, "pmic_adc");
+       return ret;
+}
+
+static int pmic_adc_module_remove(struct platform_device *pdev)
+{
+       pmic_adc_ready = 0;
+       pmic_adc_deinit();
+       device_destroy(pmic_adc_class, MKDEV(pmic_adc_major, 0));
+       class_destroy(pmic_adc_class);
+       unregister_chrdev(pmic_adc_major, "pmic_adc");
+       pr_debug(KERN_INFO "PMIC ADC successfully removed\n");
+       return 0;
+}
+
+static struct platform_driver pmic_adc_driver_ldm = {
+       .driver = {
+                  .name = "pmic_adc",
+                  },
+       .suspend = pmic_adc_suspend,
+       .resume = pmic_adc_resume,
+       .probe = pmic_adc_module_probe,
+       .remove = pmic_adc_module_remove,
+};
+
+/*
+ * Initialization and Exit
+ */
+static int __init pmic_adc_module_init(void)
+{
+       pr_debug("PMIC ADC driver loading...\n");
+       return platform_driver_register(&pmic_adc_driver_ldm);
+}
+
+static void __exit pmic_adc_module_exit(void)
+{
+       platform_driver_unregister(&pmic_adc_driver_ldm);
+       pr_debug("PMIC ADC driver successfully unloaded\n");
+}
+
+/*
+ * Module entry points
+ */
+
+module_init(pmic_adc_module_init);
+module_exit(pmic_adc_module_exit);
+
+MODULE_DESCRIPTION("PMIC ADC device driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pmic/mc13783/pmic_adc_defs.h b/drivers/mxc/pmic/mc13783/pmic_adc_defs.h
new file mode 100644 (file)
index 0000000..ad051dc
--- /dev/null
@@ -0,0 +1,321 @@
+/*
+ * 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 mc13783/pmic_adc_defs.h
+ * @brief This header contains all defines for PMIC(mc13783) ADC driver.
+ *
+ * @ingroup PMIC_ADC
+ */
+
+#ifndef __MC13783_ADC__DEFS_H__
+#define __MC13783_ADC__DEFS_H__
+
+#define MC13783_ADC_DEVICE "/dev/mc13783_adc"
+
+#define         DEF_ADC_0     0x008000
+#define         DEF_ADC_3     0x000080
+
+#define ADC_NB_AVAILABLE        2
+
+#define MAX_CHANNEL             7
+
+/*
+ * Maximun allowed variation in the three X/Y co-ordinates acquired from
+ * touch-screen
+ */
+#define DELTA_Y_MAX             50
+#define DELTA_X_MAX             50
+
+/* Upon clearing the filter, this is the delay in restarting the filter */
+#define FILTER_MIN_DELAY        4
+
+/* Length of X and Y Touch screen filters */
+#define FILTLEN                 3
+
+#define TS_X_MAX                1000
+#define TS_Y_MAX                1000
+
+#define TS_X_MIN                80
+#define TS_Y_MIN                80
+
+#define MC13783_ADC0_TS_M_LSH  14
+#define MC13783_ADC0_TS_M_WID  3
+/*
+ * ADC 0
+ */
+#define ADC_WAIT_TSI_0         0x001C00
+
+/*
+ * ADC 1
+ */
+
+#define ADC_EN                  0x000001
+#define ADC_SGL_CH              0x000002
+#define ADC_ADSEL               0x000008
+#define ADC_CH_0_POS            5
+#define ADC_CH_0_MASK           0x0000E0
+#define ADC_CH_1_POS            8
+#define ADC_CH_1_MASK           0x000700
+#define ADC_DELAY_POS           11
+#define ADC_DELAY_MASK          0x07F800
+#define ADC_ATO                 0x080000
+#define ASC_ADC                 0x100000
+#define ADC_WAIT_TSI_1         0x300001
+#define ADC_CHRGRAW_D5          0x008000
+
+/*
+ * ADC 2 - 4
+ */
+#define ADD1_RESULT_MASK        0x00000FFC
+#define ADD2_RESULT_MASK        0x00FFC000
+#define ADC_TS_MASK             0x00FFCFFC
+
+/*
+ * ADC 3
+ */
+#define ADC_INC                 0x030000
+#define ADC_BIS                 0x800000
+
+/*
+ * ADC 3
+ */
+#define ADC_NO_ADTRIG           0x200000
+#define ADC_WCOMP               0x040000
+#define ADC_WCOMP_H_POS         0
+#define ADC_WCOMP_L_POS         9
+#define ADC_WCOMP_H_MASK        0x00003F
+#define ADC_WCOMP_L_MASK        0x007E00
+
+#define ADC_MODE_MASK           0x00003F
+
+/*
+ * Interrupt Status 0
+ */
+#define ADC_INT_BISDONEI        0x02
+
+/*!
+ * Define state mode of ADC.
+ */
+typedef enum adc_state {
+       /*!
+        * Free.
+        */
+       ADC_FREE,
+       /*!
+        * Used.
+        */
+       ADC_USED,
+       /*!
+        * Monitoring
+        */
+       ADC_MONITORING,
+} t_adc_state;
+
+/*!
+ * This enumeration, is used to configure the mode of ADC.
+ */
+typedef enum reading_mode {
+       /*!
+        * Enables lithium cell reading
+        */
+       M_LITHIUM_CELL = 0x000001,
+       /*!
+        * Enables charge current reading
+        */
+       M_CHARGE_CURRENT = 0x000002,
+       /*!
+        * Enables battery current reading
+        */
+       M_BATTERY_CURRENT = 0x000004,
+       /*!
+        * Enables thermistor reading
+        */
+       M_THERMISTOR = 0x000008,
+       /*!
+        * Enables die temperature reading
+        */
+       M_DIE_TEMPERATURE = 0x000010,
+       /*!
+        * Enables UID reading
+        */
+       M_UID = 0x000020,
+} t_reading_mode;
+
+/*!
+ * This enumeration, is used to configure the monitoring mode.
+ */
+typedef enum check_mode {
+       /*!
+        * Comparator low level
+        */
+       CHECK_LOW,
+       /*!
+        * Comparator high level
+        */
+       CHECK_HIGH,
+       /*!
+        * Comparator low or high level
+        */
+       CHECK_LOW_OR_HIGH,
+} t_check_mode;
+
+/*!
+ * This structure is used to configure and report adc value.
+ */
+typedef struct {
+       /*!
+        * Delay before first conversion
+        */
+       unsigned int delay;
+       /*!
+        * sets the ATX bit for delay on all conversion
+        */
+       bool conv_delay;
+       /*!
+        * Sets the single channel mode
+        */
+       bool single_channel;
+       /*!
+        * Selects the set of inputs
+        */
+       bool group;
+       /*!
+        * Channel selection 1
+        */
+       t_channel channel_0;
+       /*!
+        * Channel selection 2
+        */
+       t_channel channel_1;
+       /*!
+        * Used to configure ADC mode with t_reading_mode
+        */
+       t_reading_mode read_mode;
+       /*!
+        * Sets the Touch screen mode
+        */
+       bool read_ts;
+       /*!
+        * Wait TSI event before touch screen reading
+        */
+       bool wait_tsi;
+       /*!
+        * Sets CHRGRAW scaling to divide by 5
+        * Only supported on 2.0 and higher
+        */
+       bool chrgraw_devide_5;
+       /*!
+        * Return ADC values
+        */
+       unsigned int value[8];
+       /*!
+        * Return touch screen values
+        */
+       t_touch_screen ts_value;
+} t_adc_param;
+
+/*!
+ * This structure is used to configure the monitoring mode of ADC.
+ */
+typedef struct {
+       /*!
+        * Delay before first conversion
+        */
+       unsigned int delay;
+       /*!
+        * sets the ATX bit for delay on all conversion
+        */
+       bool conv_delay;
+       /*!
+        * Channel selection 1
+        */
+       t_channel channel;
+       /*!
+        * Selects the set of inputs
+        */
+       bool group;
+       /*!
+        * Used to configure ADC mode with t_reading_mode
+        */
+       unsigned int read_mode;
+       /*!
+        * Comparator low level in WCOMP mode
+        */
+       unsigned int comp_low;
+       /*!
+        * Comparator high level in WCOMP mode
+        */
+       unsigned int comp_high;
+       /*!
+        * Sets type of monitoring (low, high or both)
+        */
+       t_check_mode check_mode;
+       /*!
+        * Callback to be launched when event is detected
+        */
+       void (*callback) (void);
+} t_monitoring_param;
+
+/*!
+ * This function performs filtering and rejection of excessive noise prone
+ * samples.
+ *
+ * @param        ts_curr     Touch screen value
+ *
+ * @return       This function returns 0 on success, -1 otherwise.
+ */
+static int pmic_adc_filter(t_touch_screen *ts_curr);
+
+/*!
+ * This function request a ADC.
+ *
+ * @return      This function returns index of ADC to be used (0 or 1) if successful.
+ * return -1 if error.
+ */
+int mc13783_adc_request(bool read_ts);
+
+/*!
+ * This function is used to update buffer of touch screen value in read mode.
+ */
+void update_buffer(void);
+
+/*!
+ * This function release an ADC.
+ *
+ * @param        adc_index     index of ADC to be released.
+ *
+ * @return       This function returns 0 if successful.
+ */
+int mc13783_adc_release(int adc_index);
+
+/*!
+ * This function select the required read_mode for a specific channel.
+ *
+ * @param        channel   The channel to be sampled
+ *
+ * @return       This function returns the requires read_mode
+ */
+t_reading_mode mc13783_set_read_mode(t_channel channel);
+
+/*!
+ * This function read the touch screen value.
+ *
+ * @param        touch_sample    return value of touch screen
+ * @param        wait_tsi    if true, this function is synchronous (wait in TSI event).
+ *
+ * @return       This function returns 0.
+ */
+PMIC_STATUS mc13783_adc_read_ts(t_touch_screen *touch_sample, int wait_tsi);
+
+#endif                         /* __MC13783_ADC__DEFS_H__ */
diff --git a/drivers/mxc/pmic/mc13783/pmic_audio.c b/drivers/mxc/pmic/mc13783/pmic_audio.c
new file mode 100644 (file)
index 0000000..470c93e
--- /dev/null
@@ -0,0 +1,5873 @@
+/*
+ * 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 mc13783/pmic_audio.c
+ * @brief Implementation of the PMIC(mc13783) Audio driver APIs.
+ *
+ * The PMIC Audio driver and this API were developed to support the
+ * audio playback, recording, and mixing capabilities of the power
+ * management ICs that are available from Freescale Semiconductor, Inc.
+ *
+ * The following operating modes are supported:
+ *
+ * @verbatim
+       Operating Mode               mc13783
+       ---------------------------- -------
+       Stereo DAC Playback           Yes
+       Stereo DAC Input Mixing       Yes
+       Voice CODEC Playback          Yes
+       Voice CODEC Input Mixing      Yes
+       Voice CODEC Mono Recording    Yes
+       Voice CODEC Stereo Recording  Yes
+       Microphone Bias Control       Yes
+       Output Amplifier Control      Yes
+       Output Mixing Control         Yes
+       Input Amplifier Control       Yes
+       Master/Slave Mode Select      Yes
+       Anti Pop Bias Circuit Control Yes
+   @endverbatim
+ *
+ * Note that the Voice CODEC may also be referred to as the Telephone
+ * CODEC in the PMIC DTS documentation. Also note that, while the power
+ * management ICs do provide similar audio capabilities, each PMIC may
+ * support additional configuration settings and features. Therefore, it
+ * is highly recommended that the appropriate power management IC DTS
+ * documents be used in conjunction with this API interface.
+ *
+ * @ingroup PMIC_AUDIO
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>   /* For tasklet interface.              */
+#include <linux/platform_device.h>     /* For kernel module interface.        */
+#include <linux/init.h>
+#include <linux/spinlock.h>    /* For spinlock interface.             */
+#include <linux/pmic_adc.h>    /* For PMIC ADC driver interface.      */
+#include <linux/pmic_status.h>
+#include <mach/pmic_audio.h>   /* For PMIC Audio driver interface.    */
+
+/*
+ * mc13783 PMIC Audio API
+ */
+
+/* EXPORTED FUNCTIONS */
+EXPORT_SYMBOL(MIN_STDAC_SAMPLING_RATE_HZ);
+EXPORT_SYMBOL(MAX_STDAC_SAMPLING_RATE_HZ);
+EXPORT_SYMBOL(pmic_audio_open);
+EXPORT_SYMBOL(pmic_audio_close);
+EXPORT_SYMBOL(pmic_audio_set_protocol);
+EXPORT_SYMBOL(pmic_audio_get_protocol);
+EXPORT_SYMBOL(pmic_audio_enable);
+EXPORT_SYMBOL(pmic_audio_disable);
+EXPORT_SYMBOL(pmic_audio_reset);
+EXPORT_SYMBOL(pmic_audio_reset_all);
+EXPORT_SYMBOL(pmic_audio_set_callback);
+EXPORT_SYMBOL(pmic_audio_clear_callback);
+EXPORT_SYMBOL(pmic_audio_get_callback);
+EXPORT_SYMBOL(pmic_audio_antipop_enable);
+EXPORT_SYMBOL(pmic_audio_antipop_disable);
+EXPORT_SYMBOL(pmic_audio_digital_filter_reset);
+EXPORT_SYMBOL(pmic_audio_vcodec_set_clock);
+EXPORT_SYMBOL(pmic_audio_vcodec_get_clock);
+EXPORT_SYMBOL(pmic_audio_vcodec_set_rxtx_timeslot);
+EXPORT_SYMBOL(pmic_audio_vcodec_get_rxtx_timeslot);
+EXPORT_SYMBOL(pmic_audio_vcodec_set_secondary_txslot);
+EXPORT_SYMBOL(pmic_audio_vcodec_get_secondary_txslot);
+EXPORT_SYMBOL(pmic_audio_vcodec_set_config);
+EXPORT_SYMBOL(pmic_audio_vcodec_clear_config);
+EXPORT_SYMBOL(pmic_audio_vcodec_get_config);
+EXPORT_SYMBOL(pmic_audio_vcodec_enable_bypass);
+EXPORT_SYMBOL(pmic_audio_vcodec_disable_bypass);
+EXPORT_SYMBOL(pmic_audio_stdac_set_clock);
+EXPORT_SYMBOL(pmic_audio_stdac_get_clock);
+EXPORT_SYMBOL(pmic_audio_stdac_set_rxtx_timeslot);
+EXPORT_SYMBOL(pmic_audio_stdac_get_rxtx_timeslot);
+EXPORT_SYMBOL(pmic_audio_stdac_set_config);
+EXPORT_SYMBOL(pmic_audio_stdac_clear_config);
+EXPORT_SYMBOL(pmic_audio_stdac_get_config);
+EXPORT_SYMBOL(pmic_audio_input_set_config);
+EXPORT_SYMBOL(pmic_audio_input_clear_config);
+EXPORT_SYMBOL(pmic_audio_input_get_config);
+EXPORT_SYMBOL(pmic_audio_vcodec_set_mic);
+EXPORT_SYMBOL(pmic_audio_vcodec_get_mic);
+EXPORT_SYMBOL(pmic_audio_vcodec_set_mic_on_off);
+EXPORT_SYMBOL(pmic_audio_vcodec_get_mic_on_off);
+EXPORT_SYMBOL(pmic_audio_vcodec_set_record_gain);
+EXPORT_SYMBOL(pmic_audio_vcodec_get_record_gain);
+EXPORT_SYMBOL(pmic_audio_vcodec_enable_micbias);
+EXPORT_SYMBOL(pmic_audio_vcodec_disable_micbias);
+EXPORT_SYMBOL(pmic_audio_vcodec_enable_mixer);
+EXPORT_SYMBOL(pmic_audio_vcodec_disable_mixer);
+EXPORT_SYMBOL(pmic_audio_stdac_enable_mixer);
+EXPORT_SYMBOL(pmic_audio_stdac_disable_mixer);
+EXPORT_SYMBOL(pmic_audio_output_set_port);
+EXPORT_SYMBOL(pmic_audio_output_get_port);
+EXPORT_SYMBOL(pmic_audio_output_clear_port);
+EXPORT_SYMBOL(pmic_audio_output_set_stereo_in_gain);
+EXPORT_SYMBOL(pmic_audio_output_get_stereo_in_gain);
+EXPORT_SYMBOL(pmic_audio_output_set_pgaGain);
+EXPORT_SYMBOL(pmic_audio_output_get_pgaGain);
+EXPORT_SYMBOL(pmic_audio_output_enable_mixer);
+EXPORT_SYMBOL(pmic_audio_output_disable_mixer);
+EXPORT_SYMBOL(pmic_audio_output_set_balance);
+EXPORT_SYMBOL(pmic_audio_output_get_balance);
+EXPORT_SYMBOL(pmic_audio_output_enable_mono_adder);
+EXPORT_SYMBOL(pmic_audio_output_disable_mono_adder);
+EXPORT_SYMBOL(pmic_audio_output_set_mono_adder_gain);
+EXPORT_SYMBOL(pmic_audio_output_get_mono_adder_gain);
+EXPORT_SYMBOL(pmic_audio_output_set_config);
+EXPORT_SYMBOL(pmic_audio_output_clear_config);
+EXPORT_SYMBOL(pmic_audio_output_get_config);
+EXPORT_SYMBOL(pmic_audio_output_enable_phantom_ground);
+EXPORT_SYMBOL(pmic_audio_output_disable_phantom_ground);
+EXPORT_SYMBOL(pmic_audio_set_autodetect);
+#ifdef DEBUG_AUDIO
+EXPORT_SYMBOL(pmic_audio_dump_registers);
+#endif                         /* DEBUG_AUDIO */
+/*!
+ * Define the minimum sampling rate (in Hz) that is supported by the
+ * Stereo DAC.
+ */
+const unsigned MIN_STDAC_SAMPLING_RATE_HZ = 8000;
+
+/*!
+ * Define the maximum sampling rate (in Hz) that is supported by the
+ * Stereo DAC.
+ */
+const unsigned MAX_STDAC_SAMPLING_RATE_HZ = 96000;
+
+/*! @def SET_BITS
+ * Set a register field to a given value.
+ */
+#define SET_BITS(reg, field, value)    (((value) << reg.field.offset) & \
+               reg.field.mask)
+/*! @def GET_BITS
+ * Get the current value of a given register field.
+ */
+#define GET_BITS(reg, field, value)    (((value) & reg.field.mask) >> \
+               reg.field.offset)
+
+/*!
+ * @brief Define the possible states for a device handle.
+ *
+ * This enumeration is used to track the current state of each device handle.
+ */
+typedef enum {
+       HANDLE_FREE,            /*!< Handle is available for use. */
+       HANDLE_IN_USE           /*!< Handle is currently in use.  */
+} HANDLE_STATE;
+
+/*!
+ * @brief Identifies the hardware interrupt source.
+ *
+ * This enumeration identifies which of the possible hardware interrupt
+ * sources actually caused the current interrupt handler to be called.
+ */
+typedef enum {
+       CORE_EVENT_MC2BI,       /*!< Microphone Bias 2 detect. */
+       CORE_EVENT_HSDETI,      /*!< Detect Headset attach  */
+       CORE_EVENT_HSLI,        /*!< Detect Stereo Headset  */
+       CORE_EVENT_ALSPTHI,     /*!< Detect Thermal shutdown of ALSP  */
+       CORE_EVENT_AHSSHORTI    /*!< Detect Short circuit on AHS outputs  */
+} PMIC_CORE_EVENT;
+
+/*!
+ * @brief This structure is used to track the state of a microphone input.
+ */
+typedef struct {
+       PMIC_AUDIO_INPUT_PORT mic;      /*!< Microphone input port.      */
+       PMIC_AUDIO_INPUT_MIC_STATE micOnOff;    /*!< Microphone On/Off state.    */
+       PMIC_AUDIO_MIC_AMP_MODE ampMode;        /*!< Input amplifier mode.       */
+       PMIC_AUDIO_MIC_GAIN gain;       /*!< Input amplifier gain level. */
+} PMIC_MICROPHONE_STATE;
+
+/*!
+ * @brief Tracks whether a headset is currently attached or not.
+ */
+typedef enum {
+       NO_HEADSET,             /*!< No headset currently attached. */
+       HEADSET_ON              /*!< Headset has been attached.     */
+} HEADSET_STATUS;
+
+/*!
+ * @brief mc13783 only enum that indicates the path to output taken
+ * by the voice codec output
+ */
+typedef enum {
+       VCODEC_DIRECT_OUT,      /*!< Vcodec signal out direct */
+       VCODEC_MIXER_OUT        /*!< Output via the mixer     */
+} PMIC_AUDIO_VCODEC_OUTPUT_PATH;
+
+/*!
+ * @brief This structure is used to define a specific hardware register field.
+ *
+ * All hardware register fields are defined using an offset to the LSB
+ * and a mask. The offset is used to right shift a register value before
+ * applying the mask to actually obtain the value of the field.
+ */
+typedef struct {
+       const unsigned char offset;     /*!< Offset of LSB of register field.          */
+       const unsigned int mask;        /*!< Mask value used to isolate register field. */
+} REGFIELD;
+
+/*!
+ * @brief This structure lists all fields of the AUD_CODEC hardware register.
+ */
+typedef struct {
+       REGFIELD CDCSSISEL;     /*!< codec SSI bus select              */
+       REGFIELD CDCCLKSEL;     /*!< Codec clock input select          */
+       REGFIELD CDCSM;         /*!< Codec slave / master select       */
+       REGFIELD CDCBCLINV;     /*!< Codec bitclock inversion          */
+       REGFIELD CDCFSINV;      /*!< Codec framesync inversion         */
+       REGFIELD CDCFS;         /*!< Bus protocol selection - 2 bits   */
+       REGFIELD CDCCLK;        /*!< Codec clock setting - 3 bits      */
+       REGFIELD CDCFS8K16K;    /*!< Codec framesync select            */
+       REGFIELD CDCEN;         /*!< Codec enable                      */
+       REGFIELD CDCCLKEN;      /*!< Codec clocking enable             */
+       REGFIELD CDCTS;         /*!< Codec SSI tristate                */
+       REGFIELD CDCDITH;       /*!< Codec dithering                   */
+       REGFIELD CDCRESET;      /*!< Codec filter reset                */
+       REGFIELD CDCBYP;        /*!< Codec bypass                      */
+       REGFIELD CDCALM;        /*!< Codec analog loopback             */
+       REGFIELD CDCDLM;        /*!< Codec digital loopback            */
+       REGFIELD AUDIHPF;       /*!< Transmit high pass filter enable  */
+       REGFIELD AUDOHPF;       /*!< Receive high pass filter enable   */
+} REGISTER_AUD_CODEC;
+
+/*!
+ * @brief This variable is used to access the AUD_CODEC hardware register.
+ *
+ * This variable defines how to access all of the fields within the
+ * AUD_CODEC hardware register. The initial values consist of the offset
+ * and mask values needed to access each of the register fields.
+ */
+static const REGISTER_AUD_CODEC regAUD_CODEC = {
+       {0, 0x000001},          /* CDCSSISEL    */
+       {1, 0x000002},          /* CDCCLKSEL    */
+       {2, 0x000004},          /* CDCSM        */
+       {3, 0x000008},          /* CDCBCLINV    */
+       {4, 0x000010},          /* CDCFSINV     */
+       {5, 0x000060},          /* CDCFS        */
+       {7, 0x000380},          /* CDCCLK       */
+       {10, 0x000400},         /* CDCFS8K16K   */
+       {11, 0x000800},         /* CDCEN        */
+       {12, 0x001000},         /* CDCCLKEN     */
+       {13, 0x002000},         /* CDCTS        */
+       {14, 0x004000},         /* CDCDITH      */
+       {15, 0x008000},         /* CDCRESET     */
+       {16, 0x010000},         /* CDCBYP       */
+       {17, 0x020000},         /* CDCALM       */
+       {18, 0x040000},         /* CDCDLM       */
+       {19, 0x080000},         /* AUDIHPF      */
+       {20, 0x100000}          /* AUDOHPF      */
+       /* Unused       */
+       /* Unused       */
+       /* Unused       */
+
+};
+
+/*!
+ * @brief This structure lists all fields of the ST_DAC hardware register.
+ */
+ /* VVV */
+typedef struct {
+       REGFIELD STDCSSISEL;    /*!< Stereo DAC SSI bus select                            */
+       REGFIELD STDCCLKSEL;    /*!< Stereo DAC clock input select                        */
+       REGFIELD STDCSM;        /*!< Stereo DAC slave / master select                     */
+       REGFIELD STDCBCLINV;    /*!< Stereo DAC bitclock inversion                        */
+       REGFIELD STDCFSINV;     /*!< Stereo DAC framesync inversion                       */
+       REGFIELD STDCFS;        /*!< Bus protocol selection - 2 bits                      */
+       REGFIELD STDCCLK;       /*!< Stereo DAC clock setting - 3 bits                    */
+       REGFIELD STDCFSDLYB;    /*!< Stereo DAC framesync delay bar                       */
+       REGFIELD STDCEN;        /*!< Stereo DAC enable                                    */
+       REGFIELD STDCCLKEN;     /*!< Stereo DAC clocking enable                           */
+       REGFIELD STDCRESET;     /*!< Stereo DAC filter reset                              */
+       REGFIELD SPDIF;         /*!< Stereo DAC SSI SPDIF mode. Mode no longer available. */
+       REGFIELD SR;            /*!< Stereo DAC sample rate - 4 bits                      */
+} REGISTER_ST_DAC;
+
+/*!
+ * @brief This variable is used to access the ST_DAC hardware register.
+ *
+ * This variable defines how to access all of the fields within the
+ * ST_DAC hardware register. The initial values consist of the offset
+ * and mask values needed to access each of the register fields.
+ */
+static const REGISTER_ST_DAC regST_DAC = {
+       {0, 0x000001},          /* STDCSSISEL        */
+       {1, 0x000002},          /* STDCCLKSEL        */
+       {2, 0x000004},          /* STDCSM            */
+       {3, 0x000008},          /* STDCBCLINV        */
+       {4, 0x000010},          /* STDCFSINV         */
+       {5, 0x000060},          /* STDCFS            */
+       {7, 0x000380},          /* STDCCLK           */
+       {10, 0x000400},         /* STDCFSDLYB        */
+       {11, 0x000800},         /* STDCEN            */
+       {12, 0x001000},         /* STDCCLKEN         */
+       {15, 0x008000},         /* STDCRESET         */
+       {16, 0x010000},         /* SPDIF             */
+       {17, 0x1E0000}          /* SR                */
+};
+
+/*!
+ * @brief This structure lists all of the fields in the SSI_NETWORK hardware register.
+ */
+typedef struct {
+       REGFIELD CDCTXRXSLOT;   /*!< Codec timeslot assignment  - 2 bits                                  */
+       REGFIELD CDCTXSECSLOT;  /*!< Codec secondary transmit timeslot - 2 bits                           */
+       REGFIELD CDCRXSECSLOT;  /*!< Codec secondary receive timeslot - 2 bits                            */
+       REGFIELD CDCRXSECGAIN;  /*!< Codec secondary receive channel gain setting - 2 bits                */
+       REGFIELD CDCSUMGAIN;    /*!< Codec summed receive signal gain setting                             */
+       REGFIELD CDCFSDLY;      /*!< Codec framesync delay                                                */
+       REGFIELD STDCSLOTS;     /*!< Stereo DAC number of timeslots select  - 2 bits                      */
+       REGFIELD STDCRXSLOT;    /*!< Stereo DAC timeslot assignment - 2 bits                              */
+       REGFIELD STDCRXSECSLOT; /*!< Stereo DAC secondary receive timeslot  - 2 bits                      */
+       REGFIELD STDCRXSECGAIN; /*!< Stereo DAC secondary receive channel gain setting   - 2 bits         */
+       REGFIELD STDCSUMGAIN;   /*!< Stereo DAC summed receive signal gain setting                        */
+} REGISTER_SSI_NETWORK;
+
+/*!
+ * @brief This variable is used to access the SSI_NETWORK hardware register.
+ *
+ * This variable defines how to access all of the fields within the
+ * SSI_NETWORK hardware register. The initial values consist of the offset
+ * and mask values needed to access each of the register fields.
+ */
+static const REGISTER_SSI_NETWORK regSSI_NETWORK = {
+       {2, 0x00000c},          /* CDCTXRXSLOT          */
+       {4, 0x000030},          /* CDCTXSECSLOT         */
+       {6, 0x0000c0},          /* CDCRXSECSLOT         */
+       {8, 0x000300},          /* CDCRXSECGAIN         */
+       {10, 0x000400},         /* CDCSUMGAIN           */
+       {11, 0x000800},         /* CDCFSDLY             */
+       {12, 0x003000},         /* STDCSLOTS            */
+       {14, 0x00c000},         /* STDCRXSLOT           */
+       {16, 0x030000},         /* STDCRXSECSLOT        */
+       {18, 0x0c0000},         /* STDCRXSECGAIN        */
+       {20, 0x100000}          /* STDCSUMGAIN          */
+};
+
+/*!
+ * @brief This structure lists all fields of the AUDIO_TX hardware register.
+ *
+ *
+ */
+typedef struct {
+       REGFIELD MC1BEN;        /*!< Microphone bias 1 enable                                */
+       REGFIELD MC2BEN;        /*!< Microphone bias 2 enable                                */
+       REGFIELD MC2BDETDBNC;   /*!< Microphone bias detect debounce setting                 */
+       REGFIELD MC2BDETEN;     /*!< Microphone bias 2 detect enable                         */
+       REGFIELD AMC1REN;       /*!< Amplifier Amc1R enable                                  */
+       REGFIELD AMC1RITOV;     /*!< Amplifier Amc1R current to voltage mode enable          */
+       REGFIELD AMC1LEN;       /*!< Amplifier Amc1L enable                                  */
+       REGFIELD AMC1LITOV;     /*!< Amplifier Amc1L current to voltage mode enable          */
+       REGFIELD AMC2EN;        /*!< Amplifier Amc2 enable                                   */
+       REGFIELD AMC2ITOV;      /*!< Amplifier Amc2 current to voltage mode enable           */
+       REGFIELD ATXINEN;       /*!< Amplifier Atxin enable                                  */
+       REGFIELD ATXOUTEN;      /*!< Reserved for output TXOUT enable, currently not used    */
+       REGFIELD RXINREC;       /*!< RXINR/RXINL to voice CODEC ADC routing enable           */
+       REGFIELD PGATXR;        /*!< Transmit gain setting right - 5 bits                    */
+       REGFIELD PGATXL;        /*!< Transmit gain setting left  - 5 bits                    */
+} REGISTER_AUDIO_TX;
+
+/*!
+ * @brief This variable is used to access the AUDIO_TX hardware register.
+ *
+ * This variable defines how to access all of the fields within the
+ * AUDIO_TX hardware register. The initial values consist of the offset
+ * and mask values needed to access each of the register fields.
+ */
+static const REGISTER_AUDIO_TX regAUDIO_TX = {
+       {0, 0x000001},          /* MC1BEN              */
+       {1, 0x000002},          /* MC2BEN              */
+       {2, 0x000004},          /* MC2BDETDBNC         */
+       {3, 0x000008},          /* MC2BDETEN           */
+       {5, 0x000020},          /* AMC1REN             */
+       {6, 0x000040},          /* AMC1RITOV           */
+       {7, 0x000080},          /* AMC1LEN             */
+       {8, 0x000100},          /* AMC1LITOV           */
+       {9, 0x000200},          /* AMC2EN              */
+       {10, 0x000400},         /* AMC2ITOV            */
+       {11, 0x000800},         /* ATXINEN             */
+       {12, 0x001000},         /* ATXOUTEN            */
+       {13, 0x002000},         /* RXINREC             */
+       {14, 0x07c000},         /* PGATXR              */
+       {19, 0xf80000}          /* PGATXL              */
+};
+
+/*!
+ * @brief This structure lists all fields of the AUDIO_RX_0 hardware register.
+ */
+typedef struct {
+       REGFIELD VAUDIOON;      /*!<    Forces VAUDIO in active on mode               */
+       REGFIELD BIASEN;        /*!<    Audio bias enable                             */
+       REGFIELD BIASSPEED;     /*!<    Turn on ramp speed of the audio bias          */
+       REGFIELD ASPEN;         /*!<    Amplifier Asp enable                          */
+       REGFIELD ASPSEL;        /*!<    Asp input selector                            */
+       REGFIELD ALSPEN;        /*!<    Amplifier Alsp enable                         */
+       REGFIELD ALSPREF;       /*!<    Bias Alsp at common audio reference           */
+       REGFIELD ALSPSEL;       /*!<    Alsp input selector                           */
+       REGFIELD LSPLEN;        /*!<    Output LSPL enable                            */
+       REGFIELD AHSREN;        /*!<    Amplifier AhsR enable                         */
+       REGFIELD AHSLEN;        /*!<    Amplifier AhsL enable                         */
+       REGFIELD AHSSEL;        /*!<    Ahsr and Ahsl input selector                  */
+       REGFIELD HSPGDIS;       /*!<    Phantom ground disable                        */
+       REGFIELD HSDETEN;       /*!<    Headset detect enable                         */
+       REGFIELD HSDETAUTOB;    /*!<    Amplifier state determined by headset detect  */
+       REGFIELD ARXOUTREN;     /*!<    Output RXOUTR enable                          */
+       REGFIELD ARXOUTLEN;     /*!<    Output RXOUTL enable                          */
+       REGFIELD ARXOUTSEL;     /*!<    Arxout input selector                         */
+       REGFIELD CDCOUTEN;      /*!<    Output CDCOUT enable                          */
+       REGFIELD HSLDETEN;      /*!<    Headset left channel detect enable            */
+       REGFIELD ADDCDC;        /*!<    Adder channel codec selection                 */
+       REGFIELD ADDSTDC;       /*!<    Adder channel stereo DAC selection            */
+       REGFIELD ADDRXIN;       /*!<    Adder channel line in selection               */
+} REGISTER_AUDIO_RX_0;
+
+/*!
+ * @brief This variable is used to access the AUDIO_RX_0 hardware register.
+ *
+ * This variable defines how to access all of the fields within the
+ * AUDIO_RX_0 hardware register. The initial values consist of the offset
+ * and mask values needed to access each of the register fields.
+ */
+static const REGISTER_AUDIO_RX_0 regAUDIO_RX_0 = {
+       {0, 0x000001},          /* VAUDIOON    */
+       {1, 0x000002},          /* BIASEN      */
+       {2, 0x000004},          /* BIASSPEED   */
+       {3, 0x000008},          /* ASPEN       */
+       {4, 0x000010},          /* ASPSEL      */
+       {5, 0x000020},          /* ALSPEN      */
+       {6, 0x000040},          /* ALSPREF     */
+       {7, 0x000080},          /* ALSPSEL     */
+       {8, 0x000100},          /* LSPLEN      */
+       {9, 0x000200},          /* AHSREN      */
+       {10, 0x000400},         /* AHSLEN      */
+       {11, 0x000800},         /* AHSSEL      */
+       {12, 0x001000},         /* HSPGDIS     */
+       {13, 0x002000},         /* HSDETEN     */
+       {14, 0x004000},         /* HSDETAUTOB  */
+       {15, 0x008000},         /* ARXOUTREN   */
+       {16, 0x010000},         /* ARXOUTLEN   */
+       {17, 0x020000},         /* ARXOUTSEL   */
+       {18, 0x040000},         /* CDCOUTEN    */
+       {19, 0x080000},         /* HSLDETEN    */
+       {21, 0x200000},         /* ADDCDC      */
+       {22, 0x400000},         /* ADDSTDC     */
+       {23, 0x800000}          /* ADDRXIN     */
+};
+
+/*!
+ * @brief This structure lists all fields of the AUDIO_RX_1 hardware register.
+ */
+typedef struct {
+       REGFIELD PGARXEN;       /*!<    Codec receive PGA enable                 */
+       REGFIELD PGARX;         /*!<    Codec receive gain setting - 4 bits      */
+       REGFIELD PGASTEN;       /*!<    Stereo DAC PGA enable                    */
+       REGFIELD PGAST;         /*!<    Stereo DAC gain setting  - 4 bits        */
+       REGFIELD ARXINEN;       /*!<    Amplifier Arx enable                     */
+       REGFIELD ARXIN;         /*!<    Amplifier Arx additional gain setting    */
+       REGFIELD PGARXIN;       /*!<    PGArxin gain setting  - 4 bits           */
+       REGFIELD MONO;          /*!<    Mono adder setting   - 2 bits            */
+       REGFIELD BAL;           /*!<    Balance control      - 3 bits            */
+       REGFIELD BALLR;         /*!<    Left / right balance                     */
+} REGISTER_AUDIO_RX_1;
+
+/*!
+ * @brief This variable is used to access the AUDIO_RX_1 hardware register.
+ *
+ * This variable defines how to access all of the fields within the
+ * AUDIO_RX_1 hardware register. The initial values consist of the offset
+ * and mask values needed to access each of the register fields.
+ */
+static const REGISTER_AUDIO_RX_1 regAUDIO_RX_1 = {
+       {0, 0x000001},          /* PGARXEN    */
+       {1, 0x00001e},          /* PGARX      */
+       {5, 0x000020},          /* PGASTEN   */
+       {6, 0x0003c0},          /* PGAST       */
+       {10, 0x000400},         /* ARXINEN      */
+       {11, 0x000800},         /* ARXIN      */
+       {12, 0x00f000},         /* PGARXIN     */
+       {16, 0x030000},         /* MONO     */
+       {18, 0x1c0000},         /* BAL      */
+       {21, 0x200000}          /* BALLR      */
+};
+
+/*! Define a mask to access the entire hardware register. */
+static const unsigned int REG_FULLMASK = 0xffffff;
+
+/*! Reset value for the AUD_CODEC register. */
+static const unsigned int RESET_AUD_CODEC = 0x180027;
+
+/*! Reset value for the ST_DAC register.
+ *
+ *  Note that we avoid resetting any of the arbitration bits.
+ */
+static const unsigned int RESET_ST_DAC = 0x0E0004;
+
+/*! Reset value for the SSI_NETWORK register. */
+static const unsigned int RESET_SSI_NETWORK = 0x013060;
+
+/*! Reset value for the AUDIO_TX register.
+ *
+ *  Note that we avoid resetting any of the arbitration bits.
+ */
+static const unsigned int RESET_AUDIO_TX = 0x420000;
+
+/*! Reset value for the AUDIO_RX_0 register. */
+static const unsigned int RESET_AUDIO_RX_0 = 0x001000;
+
+/*! Reset value for the AUDIO_RX_1 register. */
+static const unsigned int RESET_AUDIO_RX_1 = 0x00D35A;
+
+/*! Reset mask for the SSI network Vcodec part. first 12 bits
+ * 0 - 11 */
+static const unsigned int REG_SSI_VCODEC_MASK = 0x000fff;
+
+/*! Reset mask for the SSI network STDAC part. last 12 bits
+ * 12 - 24 */
+static const unsigned int REG_SSI_STDAC_MASK = 0xfff000;
+
+/*! Constant NULL value for initializing/reseting the audio handles. */
+static const PMIC_AUDIO_HANDLE AUDIO_HANDLE_NULL = (PMIC_AUDIO_HANDLE) NULL;
+
+/*!
+ * @brief This structure maintains the current state of the Stereo DAC.
+ */
+typedef struct {
+       PMIC_AUDIO_HANDLE handle;       /*!< Handle used to access
+                                          the Stereo DAC.         */
+       HANDLE_STATE handleState;       /*!< Current handle state.   */
+       PMIC_AUDIO_DATA_BUS busID;      /*!< Data bus used to access
+                                          the Stereo DAC.         */
+       bool protocol_set;
+       PMIC_AUDIO_BUS_PROTOCOL protocol;       /*!< Data bus protocol.      */
+       PMIC_AUDIO_BUS_MODE masterSlave;        /*!< Master/Slave mode
+                                                  select.                 */
+       PMIC_AUDIO_NUMSLOTS numSlots;   /*!< Number of timeslots
+                                          used.                   */
+       PMIC_AUDIO_CALLBACK callback;   /*!< Event notification
+                                          callback function
+                                          pointer.                */
+       PMIC_AUDIO_EVENTS eventMask;    /*!< Event notification mask. */
+       PMIC_AUDIO_CLOCK_IN_SOURCE clockIn;     /*!< Stereo DAC clock input
+                                                  source select.          */
+       PMIC_AUDIO_STDAC_SAMPLING_RATE samplingRate;    /*!< Stereo DAC sampling rate
+                                                          select.                 */
+       PMIC_AUDIO_STDAC_CLOCK_IN_FREQ clockFreq;       /*!< Stereo DAC clock input
+                                                          frequency.              */
+       PMIC_AUDIO_CLOCK_INVERT invert; /*!< Stereo DAC clock signal
+                                          invert select.          */
+       PMIC_AUDIO_STDAC_TIMESLOTS timeslot;    /*!< Stereo DAC data
+                                                  timeslots select.       */
+       PMIC_AUDIO_STDAC_CONFIG config; /*!< Stereo DAC configuration
+                                          options.                */
+} PMIC_AUDIO_STDAC_STATE;
+
+/*!
+ * @brief This variable maintains the current state of the Stereo DAC.
+ *
+ * This variable tracks the current state of the Stereo DAC audio hardware
+ * along with any information that is required by the device driver to
+ * manage the hardware (e.g., callback functions and event notification
+ * masks).
+ *
+ * The initial values represent the reset/power on state of the Stereo DAC.
+ */
+static PMIC_AUDIO_STDAC_STATE stDAC = {
+       (PMIC_AUDIO_HANDLE) NULL,       /* handle       */
+       HANDLE_FREE,            /* handleState  */
+       AUDIO_DATA_BUS_1,       /* busID        */
+       false,
+       NORMAL_MSB_JUSTIFIED_MODE,      /* protocol     */
+       BUS_MASTER_MODE,        /* masterSlave  */
+       USE_2_TIMESLOTS,        /* numSlots     */
+       (PMIC_AUDIO_CALLBACK) NULL,     /* callback     */
+       (PMIC_AUDIO_EVENTS) NULL,       /* eventMask    */
+       CLOCK_IN_CLIA,          /* clockIn      */
+       STDAC_RATE_44_1_KHZ,    /* samplingRate */
+       STDAC_CLI_13MHZ,        /* clockFreq    */
+       NO_INVERT,              /* invert       */
+       USE_TS0_TS1,            /* timeslot     */
+       (PMIC_AUDIO_STDAC_CONFIG) 0     /* config       */
+};
+
+/*!
+ * @brief This structure maintains the current state of the Voice CODEC.
+ */
+typedef struct {
+       PMIC_AUDIO_HANDLE handle;       /*!< Handle used to access
+                                          the Voice CODEC.       */
+       HANDLE_STATE handleState;       /*!< Current handle state.  */
+       PMIC_AUDIO_DATA_BUS busID;      /*!< Data bus used to access
+                                          the Voice CODEC.       */
+       bool protocol_set;
+       PMIC_AUDIO_BUS_PROTOCOL protocol;       /*!< Data bus protocol.     */
+       PMIC_AUDIO_BUS_MODE masterSlave;        /*!< Master/Slave mode
+                                                  select.                */
+       PMIC_AUDIO_NUMSLOTS numSlots;   /*!< Number of timeslots
+                                          used.                  */
+       PMIC_AUDIO_CALLBACK callback;   /*!< Event notification
+                                          callback function
+                                          pointer.               */
+       PMIC_AUDIO_EVENTS eventMask;    /*!< Event notification
+                                          mask.                  */
+       PMIC_AUDIO_CLOCK_IN_SOURCE clockIn;     /*!< Voice CODEC clock input
+                                                  source select.         */
+       PMIC_AUDIO_VCODEC_SAMPLING_RATE samplingRate;   /*!< Voice CODEC sampling
+                                                          rate select.           */
+       PMIC_AUDIO_VCODEC_CLOCK_IN_FREQ clockFreq;      /*!< Voice CODEC clock input
+                                                          frequency.             */
+       PMIC_AUDIO_CLOCK_INVERT invert; /*!< Voice CODEC clock
+                                          signal invert select.  */
+       PMIC_AUDIO_VCODEC_TIMESLOT timeslot;    /*!< Voice CODEC data
+                                                  timeslot select.       */
+       PMIC_AUDIO_VCODEC_TIMESLOT secondaryTXtimeslot;
+
+       PMIC_AUDIO_VCODEC_CONFIG config;        /*!< Voice CODEC
+                                                  configuration
+                                                  options.            */
+       PMIC_MICROPHONE_STATE leftChannelMic;   /*!< Left channel
+                                                  microphone
+                                                  configuration.      */
+       PMIC_MICROPHONE_STATE rightChannelMic;  /*!< Right channel
+                                                  microphone
+                                                  configuration.      */
+} PMIC_AUDIO_VCODEC_STATE;
+
+/*!
+ * @brief This variable maintains the current state of the Voice CODEC.
+ *
+ * This variable tracks the current state of the Voice CODEC audio hardware
+ * along with any information that is required by the device driver to
+ * manage the hardware (e.g., callback functions and event notification
+ * masks).
+ *
+ * The initial values represent the reset/power on state of the Voice CODEC.
+ */
+static PMIC_AUDIO_VCODEC_STATE vCodec = {
+       (PMIC_AUDIO_HANDLE) NULL,       /* handle          */
+       HANDLE_FREE,            /* handleState     */
+       AUDIO_DATA_BUS_2,       /* busID           */
+       false,
+       NETWORK_MODE,           /* protocol        */
+       BUS_SLAVE_MODE,         /* masterSlave     */
+       USE_4_TIMESLOTS,        /* numSlots        */
+       (PMIC_AUDIO_CALLBACK) NULL,     /* callback        */
+       (PMIC_AUDIO_EVENTS) NULL,       /* eventMask       */
+       CLOCK_IN_CLIB,          /* clockIn         */
+       VCODEC_RATE_8_KHZ,      /* samplingRate    */
+       VCODEC_CLI_13MHZ,       /* clockFreq       */
+       NO_INVERT,              /* invert          */
+       USE_TS0,                /* timeslot pri    */
+       USE_TS2,                /* timeslot sec TX */
+       INPUT_HIGHPASS_FILTER | OUTPUT_HIGHPASS_FILTER, /* config          */
+       /* leftChannelMic  */
+       {NO_MIC,                /*    mic          */
+        MICROPHONE_OFF,        /*    micOnOff     */
+        AMP_OFF,               /*    ampMode      */
+        MIC_GAIN_0DB           /*    gain         */
+        },
+       /* rightChannelMic */
+       {NO_MIC,                /*    mic          */
+        MICROPHONE_OFF,        /*    micOnOff     */
+        AMP_OFF,               /*    ampMode      */
+        MIC_GAIN_0DB           /*    gain         */
+        }
+};
+
+/*!
+ * @brief This maintains the current state of the External Stereo Input.
+ */
+typedef struct {
+       PMIC_AUDIO_HANDLE handle;       /*!< Handle used to access the
+                                          External Stereo Inputs.     */
+       HANDLE_STATE handleState;       /*!< Current handle state.       */
+       PMIC_AUDIO_CALLBACK callback;   /*!< Event notification callback
+                                          function pointer.           */
+       PMIC_AUDIO_EVENTS eventMask;    /*!< Event notification mask.    */
+       PMIC_AUDIO_STEREO_IN_GAIN inputGain;    /*!< External Stereo Input
+                                                  amplifier gain level.       */
+} PMIC_AUDIO_EXT_STEREO_IN_STATE;
+
+/*!
+ * @brief This maintains the current state of the External Stereo Input.
+ *
+ * This variable tracks the current state of the External Stereo Input audio
+ * hardware along with any information that is required by the device driver
+ * to manage the hardware (e.g., callback functions and event notification
+ * masks).
+ *
+ * The initial values represent the reset/power on state of the External
+ * Stereo Input.
+ */
+static PMIC_AUDIO_EXT_STEREO_IN_STATE extStereoIn = {
+       (PMIC_AUDIO_HANDLE) NULL,       /* handle      */
+       HANDLE_FREE,            /* handleState */
+       (PMIC_AUDIO_CALLBACK) NULL,     /* callback    */
+       (PMIC_AUDIO_EVENTS) NULL,       /* eventMask   */
+       STEREO_IN_GAIN_0DB      /* inputGain   */
+};
+
+/*!
+ * @brief This maintains the current state of the callback & Eventmask.
+ */
+typedef struct {
+       PMIC_AUDIO_CALLBACK callback;   /*!< Event notification callback
+                                          function pointer.           */
+       PMIC_AUDIO_EVENTS eventMask;    /*!< Event notification mask.    */
+} PMIC_AUDIO_EVENT_STATE;
+
+static PMIC_AUDIO_EVENT_STATE event_state = {
+       (PMIC_AUDIO_CALLBACK) NULL,     /*Callback */
+       (PMIC_AUDIO_EVENTS) NULL,       /* EventMask */
+
+};
+
+/*!
+ * @brief This maintains the current state of the Audio Output Section.
+ */
+typedef struct {
+       PMIC_AUDIO_OUTPUT_PORT outputPort;      /*!< Current audio
+                                                  output port.     */
+       PMIC_AUDIO_OUTPUT_PGA_GAIN vCodecoutputPGAGain; /*!< Output PGA gain
+                                                          level codec      */
+       PMIC_AUDIO_OUTPUT_PGA_GAIN stDacoutputPGAGain;  /*!< Output PGA gain
+                                                          level stDAC      */
+       PMIC_AUDIO_OUTPUT_PGA_GAIN extStereooutputPGAGain;      /*!< Output PGA gain
+                                                                  level stereo ext */
+       PMIC_AUDIO_OUTPUT_BALANCE_GAIN balanceLeftGain; /*!< Left channel
+                                                          balance gain
+                                                          level.           */
+       PMIC_AUDIO_OUTPUT_BALANCE_GAIN balanceRightGain;        /*!< Right channel
+                                                                  balance gain
+                                                                  level.           */
+       PMIC_AUDIO_MONO_ADDER_OUTPUT_GAIN monoAdderGain;        /*!< Mono adder gain
+                                                                  level.           */
+       PMIC_AUDIO_OUTPUT_CONFIG config;        /*!< Audio output
+                                                  section config
+                                                  options.         */
+       PMIC_AUDIO_VCODEC_OUTPUT_PATH vCodecOut;
+
+} PMIC_AUDIO_AUDIO_OUTPUT_STATE;
+
+/*!
+ * @brief This variable maintains the current state of the Audio Output Section.
+ *
+ * This variable tracks the current state of the Audio Output Section.
+ *
+ * The initial values represent the reset/power on state of the Audio
+ * Output Section.
+ */
+static PMIC_AUDIO_AUDIO_OUTPUT_STATE audioOutput = {
+       (PMIC_AUDIO_OUTPUT_PORT) NULL,  /* outputPort       */
+       OUTPGA_GAIN_0DB,        /* outputPGAGain    */
+       OUTPGA_GAIN_0DB,        /* outputPGAGain    */
+       OUTPGA_GAIN_0DB,        /* outputPGAGain    */
+       BAL_GAIN_0DB,           /* balanceLeftGain  */
+       BAL_GAIN_0DB,           /* balanceRightGain */
+       MONOADD_GAIN_0DB,       /* monoAdderGain    */
+       (PMIC_AUDIO_OUTPUT_CONFIG) 0,   /* config           */
+       VCODEC_DIRECT_OUT
+};
+
+/*! The current headset status. */
+static HEADSET_STATUS headsetState = NO_HEADSET;
+
+/* Removed PTT variable */
+/*! Define a 1 ms wait interval that is needed to ensure that certain
+ *  hardware operations are successfully completed.
+ */
+static const unsigned long delay_1ms = (HZ / 1000);
+
+/*!
+ * @brief This spinlock is used to provide mutual exclusion.
+ *
+ * Create a spinlock that can be used to provide mutually exclusive
+ * read/write access to the globally accessible data structures
+ * that were defined above. Mutually exclusive access is required to
+ * ensure that the audio data structures are consistent at all times
+ * when possibly accessed by multiple threads of execution (for example,
+ * while simultaneously handling a user request and an interrupt event).
+ *
+ * We need to use a spinlock whenever we do need to provide mutual
+ * exclusion while possibly executing in a hardware interrupt context.
+ * Spinlocks should be held for the minimum time that is necessary
+ * because hardware interrupts are disabled while a spinlock is held.
+ *
+ */
+static DEFINE_SPINLOCK(lock);
+
+/*!
+ * @brief This mutex is used to provide mutual exclusion.
+ *
+ * Create a mutex that can be used to provide mutually exclusive
+ * read/write access to the globally accessible data structures
+ * that were defined above. Mutually exclusive access is required to
+ * ensure that the audio data structures are consistent at all times
+ * when possibly accessed by multiple threads of execution.
+ *
+ * Note that we use a mutex instead of the spinlock whenever disabling
+ * interrupts while in the critical section is not required. This helps
+ * to minimize kernel interrupt handling latency.
+ */
+static DECLARE_MUTEX(mutex);
+
+/*!
+ * @brief Global variable to track currently active interrupt events.
+ *
+ * This global variable is used to keep track of all of the currently
+ * active interrupt events for the audio driver. Note that access to this
+ * variable may occur while within an interrupt context and, therefore,
+ * must be guarded by using a spinlock.
+ */
+/* static PMIC_CORE_EVENT eventID = 0; */
+
+/* Prototypes for all static audio driver functions. */
+/*
+static PMIC_STATUS pmic_audio_mic_boost_enable(void);
+static PMIC_STATUS pmic_audio_mic_boost_disable(void);*/
+static PMIC_STATUS pmic_audio_close_handle(const PMIC_AUDIO_HANDLE handle);
+static PMIC_STATUS pmic_audio_reset_device(const PMIC_AUDIO_HANDLE handle);
+
+static PMIC_STATUS pmic_audio_deregister(void *callback,
+                                        PMIC_AUDIO_EVENTS * const eventMask);
+
+/*************************************************************************
+ * Audio device access APIs.
+ *************************************************************************
+ */
+
+/*!
+ * @name General Setup and Configuration APIs
+ * Functions for general setup and configuration of the PMIC Audio
+ * hardware.
+ */
+/*@{*/
+
+PMIC_STATUS pmic_audio_set_autodetect(int val)
+{
+       PMIC_STATUS status;
+       unsigned int reg_mask = 0, reg_write = 0;
+       reg_mask = SET_BITS(regAUDIO_RX_0, VAUDIOON, 1);
+       status = pmic_write_reg(REG_AUDIO_RX_0, reg_mask, reg_mask);
+       if (status != PMIC_SUCCESS)
+               return status;
+       reg_mask = 0;
+       if (val == 1) {
+               reg_write = SET_BITS(regAUDIO_RX_0, HSDETEN, 1) |
+                   SET_BITS(regAUDIO_RX_0, HSDETAUTOB, 1);
+       } else {
+               reg_write = 0;
+       }
+       reg_mask =
+           SET_BITS(regAUDIO_RX_0, HSDETEN, 1) | SET_BITS(regAUDIO_RX_0,
+                                                          HSDETAUTOB, 1);
+       status = pmic_write_reg(REG_AUDIO_RX_0, reg_write, reg_mask);
+
+       return status;
+}
+
+/*!
+ * @brief Request exclusive access to the PMIC Audio hardware.
+ *
+ * Attempt to open and gain exclusive access to a key PMIC audio hardware
+ * component (e.g., the Stereo DAC or the Voice CODEC). Depending upon the
+ * type of audio operation that is desired and the nature of the audio data
+ * stream, the Stereo DAC and/or the Voice CODEC will be a required hardware
+ * component and needs to be acquired by calling this function.
+ *
+ * If the open request is successful, then a numeric handle is returned
+ * and this handle must be used in all subsequent function calls to complete
+ * the configuration of either the Stereo DAC or the Voice CODEC and along
+ * with any other associated audio hardware components that will be needed.
+ *
+ * The same handle must also be used in the close call when use of the PMIC
+ * audio hardware is no longer required.
+ *
+ * The open request will fail if the requested audio hardware component has
+ * already been acquired by a previous open call but not yet closed.
+ *
+ * @param  handle          Device handle to be used for subsequent PMIC
+ *                              audio API calls.
+ * @param  device          The required PMIC audio hardware component.
+ *
+ * @retval      PMIC_SUCCESS         If the open request was successful
+ * @retval      PMIC_PARAMETER_ERROR If the handle argument is NULL.
+ * @retval      PMIC_ERROR           If the audio hardware component is
+ *                                   unavailable.
+ */
+PMIC_STATUS pmic_audio_open(PMIC_AUDIO_HANDLE * const handle,
+                           const PMIC_AUDIO_SOURCE device)
+{
+       PMIC_STATUS rc = PMIC_ERROR;
+
+       if (handle == (PMIC_AUDIO_HANDLE *) NULL) {
+               /* Do not dereference a NULL pointer. */
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       /* We only need to acquire a mutex here because the interrupt handler
+        * never modifies the device handle or device handle state. Therefore,
+        * we don't need to worry about conflicts with the interrupt handler
+        * or the need to execute in an interrupt context.
+        *
+        * But we do need a critical section here to avoid problems in case
+        * multiple calls to pmic_audio_open() are made since we can only allow
+        * one of them to succeed.
+        */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       /* Check the current device handle state and acquire the handle if
+        * it is available.
+        */
+
+       if ((device == STEREO_DAC) && (stDAC.handleState == HANDLE_FREE)) {
+               stDAC.handle = (PMIC_AUDIO_HANDLE) (&stDAC);
+               stDAC.handleState = HANDLE_IN_USE;
+               *handle = stDAC.handle;
+               rc = PMIC_SUCCESS;
+       } else if ((device == VOICE_CODEC)
+                  && (vCodec.handleState == HANDLE_FREE)) {
+               vCodec.handle = (PMIC_AUDIO_HANDLE) (&vCodec);
+               vCodec.handleState = HANDLE_IN_USE;
+               *handle = vCodec.handle;
+               rc = PMIC_SUCCESS;
+       } else if ((device == EXTERNAL_STEREO_IN) &&
+                  (extStereoIn.handleState == HANDLE_FREE)) {
+               extStereoIn.handle = (PMIC_AUDIO_HANDLE) (&extStereoIn);
+               extStereoIn.handleState = HANDLE_IN_USE;
+               *handle = extStereoIn.handle;
+               rc = PMIC_SUCCESS;
+       } else {
+               *handle = AUDIO_HANDLE_NULL;
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * @brief Terminate further access to the PMIC audio hardware.
+ *
+ * Terminate further access to the PMIC audio hardware that was previously
+ * acquired by calling pmic_audio_open(). This now allows another thread to
+ * successfully call pmic_audio_open() to gain access.
+ *
+ * Note that we will shutdown/reset the Voice CODEC or Stereo DAC as well as
+ * any associated audio input/output components that are no longer required.
+ *
+ * @param   handle          Device handle from pmic_audio_open() call.
+ *
+ * @retval      PMIC_SUCCESS         If the close request was successful.
+ * @retval      PMIC_PARAMETER_ERROR If the handle is invalid.
+ */
+PMIC_STATUS pmic_audio_close(const PMIC_AUDIO_HANDLE handle)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+       /* We need a critical section here to maintain a consistent state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       /* We can now call pmic_audio_close_handle() to actually do the work. */
+       rc = pmic_audio_close_handle(handle);
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * @brief Configure the data bus protocol to be used.
+ *
+ * Provide the parameters needed to properly configure the audio data bus
+ * protocol so that data can be read/written to either the Stereo DAC or
+ * the Voice CODEC.
+ *
+ * @param   handle          Device handle from pmic_audio_open() call.
+ * @param   busID           Select data bus to be used.
+ * @param   protocol        Select the data bus protocol.
+ * @param   masterSlave     Select the data bus timing mode.
+ * @param   numSlots        Define the number of timeslots (only if in
+ *                              master mode).
+ *
+ * @retval      PMIC_SUCCESS         If the protocol was successful configured.
+ * @retval      PMIC_PARAMETER_ERROR If the handle or the protocol parameters
+ *                                   are invalid.
+ */
+PMIC_STATUS pmic_audio_set_protocol(const PMIC_AUDIO_HANDLE handle,
+                                   const PMIC_AUDIO_DATA_BUS busID,
+                                   const PMIC_AUDIO_BUS_PROTOCOL protocol,
+                                   const PMIC_AUDIO_BUS_MODE masterSlave,
+                                   const PMIC_AUDIO_NUMSLOTS numSlots)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       const unsigned int ST_DAC_MASK = SET_BITS(regST_DAC, STDCSSISEL, 1) |
+           SET_BITS(regST_DAC, STDCFS, 3) | SET_BITS(regST_DAC, STDCSM, 1);
+
+       unsigned int reg_mask;
+       /*unsigned int VCODEC_MASK = SET_BITS(regAUD_CODEC, CDCSSISEL, 1) |
+          SET_BITS(regAUD_CODEC, CDCFS, 3) | SET_BITS(regAUD_CODEC, CDCSM, 1); */
+
+       unsigned int SSI_NW_MASK = SET_BITS(regSSI_NETWORK, STDCSLOTS, 1);
+       unsigned int reg_value = 0;
+       unsigned int ssi_nw_value = 0;
+
+       /* Enter a critical section so that we can ensure only one
+        * state change request is completed at a time.
+        */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if (handle == (PMIC_AUDIO_HANDLE) NULL) {
+               rc = PMIC_PARAMETER_ERROR;
+       } else {
+               if ((handle == vCodec.handle) &&
+                   (vCodec.handleState == HANDLE_IN_USE)) {
+                       if ((stDAC.handleState == HANDLE_IN_USE) &&
+                           (stDAC.busID == busID) && (stDAC.protocol_set)) {
+                               pr_debug("The requested bus already in USE\n");
+                               rc = PMIC_PARAMETER_ERROR;
+                       } else if ((masterSlave == BUS_MASTER_MODE)
+                                  && (numSlots != USE_4_TIMESLOTS)) {
+                               pr_debug
+                                   ("mc13783 supports only 4 slots in Master mode\n");
+                               rc = PMIC_NOT_SUPPORTED;
+                       } else if ((masterSlave == BUS_SLAVE_MODE)
+                                  && (numSlots != USE_4_TIMESLOTS)) {
+                               pr_debug
+                                   ("Driver currently supports only 4 slots in Slave mode\n");
+                               rc = PMIC_NOT_SUPPORTED;
+                       } else if (!((protocol == NETWORK_MODE) ||
+                                    (protocol == I2S_MODE))) {
+                               pr_debug
+                                   ("mc13783 Voice codec works only in Network and I2S modes\n");
+                               rc = PMIC_NOT_SUPPORTED;
+                       } else {
+                               pr_debug
+                                   ("Proceeding to configure Voice Codec\n");
+                               if (busID == AUDIO_DATA_BUS_1) {
+                                       reg_value =
+                                           SET_BITS(regAUD_CODEC, CDCSSISEL,
+                                                    0);
+                               } else {
+                                       reg_value =
+                                           SET_BITS(regAUD_CODEC, CDCSSISEL,
+                                                    1);
+                               }
+                               reg_mask = SET_BITS(regAUD_CODEC, CDCSSISEL, 1);
+                               if (PMIC_SUCCESS !=
+                                   pmic_write_reg(REG_AUDIO_CODEC,
+                                                  reg_value, reg_mask))
+                                       return PMIC_ERROR;
+
+                               if (masterSlave == BUS_MASTER_MODE) {
+                                       reg_value =
+                                           SET_BITS(regAUD_CODEC, CDCSM, 0);
+                               } else {
+                                       reg_value =
+                                           SET_BITS(regAUD_CODEC, CDCSM, 1);
+                               }
+                               reg_mask = SET_BITS(regAUD_CODEC, CDCSM, 1);
+                               if (PMIC_SUCCESS !=
+                                   pmic_write_reg(REG_AUDIO_CODEC,
+                                                  reg_value, reg_mask))
+                                       return PMIC_ERROR;
+
+                               if (protocol == NETWORK_MODE) {
+                                       reg_value =
+                                           SET_BITS(regAUD_CODEC, CDCFS, 1);
+                               } else {        /* protocol == I2S, other options have been already eliminated */
+                                       reg_value =
+                                           SET_BITS(regAUD_CODEC, CDCFS, 2);
+                               }
+                               reg_mask = SET_BITS(regAUD_CODEC, CDCFS, 3);
+                               if (PMIC_SUCCESS !=
+                                   pmic_write_reg(REG_AUDIO_CODEC,
+                                                  reg_value, reg_mask))
+                                       return PMIC_ERROR;
+
+                               ssi_nw_value =
+                                   SET_BITS(regSSI_NETWORK, CDCFSDLY, 1);
+                               /*if (pmic_write_reg
+                                  (REG_AUDIO_CODEC, reg_value,
+                                  VCODEC_MASK) != PMIC_SUCCESS) {
+                                  rc = PMIC_ERROR;
+                                  } else { */
+                               vCodec.busID = busID;
+                               vCodec.protocol = protocol;
+                               vCodec.masterSlave = masterSlave;
+                               vCodec.numSlots = numSlots;
+                               vCodec.protocol_set = true;
+
+                               pr_debug
+                                   ("mc13783 Voice codec successfully configured\n");
+                               rc = PMIC_SUCCESS;
+                       }
+
+               } else if ((handle == stDAC.handle) &&
+                          (stDAC.handleState == HANDLE_IN_USE)) {
+                       if ((vCodec.handleState == HANDLE_IN_USE) &&
+                           (vCodec.busID == busID) && (vCodec.protocol_set)) {
+                               pr_debug("The requested bus already in USE\n");
+                               rc = PMIC_PARAMETER_ERROR;
+                       } else if (((protocol == NORMAL_MSB_JUSTIFIED_MODE) ||
+                                   (protocol == I2S_MODE))
+                                  && (numSlots != USE_2_TIMESLOTS)) {
+                               pr_debug
+                                   ("STDAC uses only 2 slots in Normal and I2S modes\n");
+                               rc = PMIC_PARAMETER_ERROR;
+                       } else if ((protocol == NETWORK_MODE) &&
+                                  !((numSlots == USE_2_TIMESLOTS) ||
+                                    (numSlots == USE_4_TIMESLOTS) ||
+                                    (numSlots == USE_8_TIMESLOTS))) {
+                               pr_debug
+                                   ("STDAC uses only 2,4 or 8 slots in Network mode\n");
+                               rc = PMIC_PARAMETER_ERROR;
+                       } else if (protocol == SPD_IF_MODE) {
+                               pr_debug
+                                   ("STDAC driver currently does not support SPD IF mode\n");
+                               rc = PMIC_NOT_SUPPORTED;
+                       } else {
+                               pr_debug
+                                   ("Proceeding to configure Stereo DAC\n");
+                               if (busID == AUDIO_DATA_BUS_1) {
+                                       reg_value =
+                                           SET_BITS(regST_DAC, STDCSSISEL, 0);
+                               } else {
+                                       reg_value =
+                                           SET_BITS(regST_DAC, STDCSSISEL, 1);
+                               }
+                               if (masterSlave == BUS_MASTER_MODE) {
+                                       reg_value |=
+                                           SET_BITS(regST_DAC, STDCSM, 0);
+                               } else {
+                                       reg_value |=
+                                           SET_BITS(regST_DAC, STDCSM, 1);
+                               }
+                               if (protocol == NETWORK_MODE) {
+                                       reg_value |=
+                                           SET_BITS(regST_DAC, STDCFS, 1);
+                               } else if (protocol ==
+                                          NORMAL_MSB_JUSTIFIED_MODE) {
+                                       reg_value |=
+                                           SET_BITS(regST_DAC, STDCFS, 0);
+                               } else {        /* I2S mode as the other option has already been eliminated */
+                                       reg_value |=
+                                           SET_BITS(regST_DAC, STDCFS, 2);
+                               }
+
+                               if (pmic_write_reg
+                                   (REG_AUDIO_STEREO_DAC,
+                                    reg_value, ST_DAC_MASK) != PMIC_SUCCESS) {
+                                       rc = PMIC_ERROR;
+                               } else {
+                                       if (numSlots == USE_2_TIMESLOTS) {
+                                               reg_value =
+                                                   SET_BITS(regSSI_NETWORK,
+                                                            STDCSLOTS, 3);
+                                       } else if (numSlots == USE_4_TIMESLOTS) {
+                                               reg_value =
+                                                   SET_BITS(regSSI_NETWORK,
+                                                            STDCSLOTS, 2);
+                                       } else {        /* Use 8 timeslots - L , R and 6 other */
+                                               reg_value =
+                                                   SET_BITS(regSSI_NETWORK,
+                                                            STDCSLOTS, 1);
+                                       }
+                                       if (pmic_write_reg
+                                           (REG_AUDIO_SSI_NETWORK,
+                                            reg_value,
+                                            SSI_NW_MASK) != PMIC_SUCCESS) {
+                                               rc = PMIC_ERROR;
+                                       } else {
+                                               stDAC.busID = busID;
+                                               stDAC.protocol = protocol;
+                                               stDAC.protocol_set = true;
+                                               stDAC.masterSlave = masterSlave;
+                                               stDAC.numSlots = numSlots;
+                                               pr_debug
+                                                   ("mc13783 Stereo DAC successfully configured\n");
+                                               rc = PMIC_SUCCESS;
+                                       }
+                               }
+
+                       }
+               } else {
+                       rc = PMIC_PARAMETER_ERROR;
+                       /* Handle  can only be Voice Codec or Stereo DAC */
+                       pr_debug("Handles only STDAC and VCODEC\n");
+               }
+
+       }
+       /* Exit critical section. */
+       up(&mutex);
+       return rc;
+}
+
+/*!
+ * @brief Retrieve the current data bus protocol configuration.
+ *
+ * Retrieve the parameters that define the current audio data bus protocol.
+ *
+ * @param  handle          Device handle from pmic_audio_open() call.
+ * @param  busID           The data bus being used.
+ * @param  protocol        The data bus protocol being used.
+ * @param  masterSlave     The data bus timing mode being used.
+ * @param  numSlots        The number of timeslots being used (if in
+ *                              master mode).
+ *
+ * @retval      PMIC_SUCCESS         If the protocol was successful retrieved.
+ * @retval      PMIC_PARAMETER_ERROR If the handle is invalid.
+ */
+PMIC_STATUS pmic_audio_get_protocol(const PMIC_AUDIO_HANDLE handle,
+                                   PMIC_AUDIO_DATA_BUS * const busID,
+                                   PMIC_AUDIO_BUS_PROTOCOL * const protocol,
+                                   PMIC_AUDIO_BUS_MODE * const masterSlave,
+                                   PMIC_AUDIO_NUMSLOTS * const numSlots)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+       if ((busID != (PMIC_AUDIO_DATA_BUS *) NULL) &&
+           (protocol != (PMIC_AUDIO_BUS_PROTOCOL *) NULL) &&
+           (masterSlave != (PMIC_AUDIO_BUS_MODE *) NULL) &&
+           (numSlots != (PMIC_AUDIO_NUMSLOTS *) NULL)) {
+               /* Enter a critical section so that we return a consistent state. */
+               if (down_interruptible(&mutex))
+                       return PMIC_SYSTEM_ERROR_EINTR;
+
+               if ((handle == stDAC.handle) &&
+                   (stDAC.handleState == HANDLE_IN_USE)) {
+                       *busID = stDAC.busID;
+                       *protocol = stDAC.protocol;
+                       *masterSlave = stDAC.masterSlave;
+                       *numSlots = stDAC.numSlots;
+                       rc = PMIC_SUCCESS;
+               } else if ((handle == vCodec.handle) &&
+                          (vCodec.handleState == HANDLE_IN_USE)) {
+                       *busID = vCodec.busID;
+                       *protocol = vCodec.protocol;
+                       *masterSlave = vCodec.masterSlave;
+                       *numSlots = vCodec.numSlots;
+                       rc = PMIC_SUCCESS;
+               }
+
+               /* Exit critical section. */
+               up(&mutex);
+       }
+
+       return rc;
+}
+
+/*!
+ * @brief Enable the Stereo DAC or the Voice CODEC.
+ *
+ * Explicitly enable the Stereo DAC or the Voice CODEC to begin audio
+ * playback or recording as required. This should only be done after
+ * successfully configuring all of the associated audio components (e.g.,
+ * microphones, amplifiers, etc.).
+ *
+ * Note that the timed delays used in this function are necessary to
+ * ensure reliable operation of the Voice CODEC and Stereo DAC. The
+ * Stereo DAC seems to be particularly sensitive and it has been observed
+ * to fail to generate the required master mode clock signals if it is
+ * not allowed enough time to initialize properly.
+ *
+ * @param      handle          Device handle from pmic_audio_open() call.
+ *
+ * @retval      PMIC_SUCCESS         If the device was successful enabled.
+ * @retval      PMIC_PARAMETER_ERROR If the handle is invalid.
+ * @retval      PMIC_ERROR           If the device could not be enabled.
+ */
+PMIC_STATUS pmic_audio_enable(const PMIC_AUDIO_HANDLE handle)
+{
+       const unsigned int AUDIO_BIAS_ENABLE = SET_BITS(regAUDIO_RX_0,
+                                                       VAUDIOON, 1);
+       const unsigned int STDAC_ENABLE = SET_BITS(regST_DAC, STDCEN, 1);
+       const unsigned int VCODEC_ENABLE = SET_BITS(regAUD_CODEC, CDCEN, 1);
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       unsigned int reg_write = 0;
+       unsigned int reg_mask = 0;
+
+       /* Use a critical section to ensure a consistent hardware state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE)) {
+               pmic_write_reg(REG_AUDIO_RX_0, AUDIO_BIAS_ENABLE,
+                              AUDIO_BIAS_ENABLE);
+               reg_mask =
+                   SET_BITS(regAUDIO_RX_0, HSDETEN,
+                            1) | SET_BITS(regAUDIO_RX_0, HSDETAUTOB, 1);
+               reg_write =
+                   SET_BITS(regAUDIO_RX_0, HSDETEN,
+                            1) | SET_BITS(regAUDIO_RX_0, HSDETAUTOB, 1);
+               rc = pmic_write_reg(REG_AUDIO_RX_0, reg_write, reg_mask);
+               if (rc == PMIC_SUCCESS)
+                       pr_debug("pmic_audio_enable\n");
+               /* We can enable the Stereo DAC. */
+               rc = pmic_write_reg(REG_AUDIO_STEREO_DAC,
+                                   STDAC_ENABLE, STDAC_ENABLE);
+               /*pmic_read_reg(REG_AUDIO_STEREO_DAC, &reg_value); */
+               if (rc != PMIC_SUCCESS) {
+                       pr_debug("Failed to enable the Stereo DAC\n");
+                       rc = PMIC_ERROR;
+               }
+       } else if ((handle == vCodec.handle)
+                  && (vCodec.handleState == HANDLE_IN_USE)) {
+               /* Must first set the audio bias bit to power up the audio circuits. */
+               pmic_write_reg(REG_AUDIO_RX_0, AUDIO_BIAS_ENABLE,
+                              AUDIO_BIAS_ENABLE);
+               reg_mask = SET_BITS(regAUDIO_RX_0, HSDETEN, 1) |
+                   SET_BITS(regAUDIO_RX_0, HSDETAUTOB, 1);
+               reg_write = SET_BITS(regAUDIO_RX_0, HSDETEN, 1) |
+                   SET_BITS(regAUDIO_RX_0, HSDETAUTOB, 1);
+               rc = pmic_write_reg(REG_AUDIO_RX_0, reg_write, reg_mask);
+
+               /* Then we can enable the Voice CODEC. */
+               rc = pmic_write_reg(REG_AUDIO_CODEC, VCODEC_ENABLE,
+                                   VCODEC_ENABLE);
+
+               /* pmic_read_reg(REG_AUDIO_CODEC, &reg_value); */
+               if (rc != PMIC_SUCCESS) {
+                       pr_debug("Failed to enable the Voice codec\n");
+                       rc = PMIC_ERROR;
+               }
+       }
+       /* Exit critical section. */
+       up(&mutex);
+       return rc;
+}
+
+/*!
+ * @brief Disable the Stereo DAC or the Voice CODEC.
+ *
+ * Explicitly disable the Stereo DAC or the Voice CODEC to end audio
+ * playback or recording as required.
+ *
+ * @param      handle          Device handle from pmic_audio_open() call.
+ *
+ * @retval      PMIC_SUCCESS         If the device was successful disabled.
+ * @retval      PMIC_PARAMETER_ERROR If the handle is invalid.
+ * @retval      PMIC_ERROR           If the device could not be disabled.
+ */
+PMIC_STATUS pmic_audio_disable(const PMIC_AUDIO_HANDLE handle)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       const unsigned int STDAC_DISABLE = SET_BITS(regST_DAC, STDCEN, 1);
+       const unsigned int VCODEC_DISABLE = SET_BITS(regAUD_CODEC, CDCEN, 1);
+
+       /* Use a critical section to ensure a consistent hardware state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+       if ((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE)) {
+               rc = pmic_write_reg(REG_AUDIO_STEREO_DAC, 0, STDAC_DISABLE);
+       } else if ((handle == vCodec.handle)
+                  && (vCodec.handleState == HANDLE_IN_USE)) {
+               rc = pmic_write_reg(REG_AUDIO_CODEC, 0, VCODEC_DISABLE);
+       }
+       if (rc == PMIC_SUCCESS) {
+               pr_debug("Disabled successfully\n");
+       }
+       /* Exit critical section. */
+       up(&mutex);
+       return rc;
+}
+
+/*!
+ * @brief Reset the selected audio hardware control registers to their
+ *        power on state.
+ *
+ * This resets all of the audio hardware control registers currently
+ * associated with the device handle back to their power on states. For
+ * example, if the handle is associated with the Stereo DAC and a
+ * specific output port and output amplifiers, then this function will
+ * reset all of those components to their initial power on state.
+ *
+ * @param       handle          Device handle from pmic_audio_open() call.
+ *
+ * @retval      PMIC_SUCCESS         If the reset operation was successful.
+ * @retval      PMIC_PARAMETER_ERROR If the handle is invalid.
+ * @retval      PMIC_ERROR           If the reset was unsuccessful.
+ */
+PMIC_STATUS pmic_audio_reset(const PMIC_AUDIO_HANDLE handle)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+       /* Use a critical section to ensure a consistent hardware state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       rc = pmic_audio_reset_device(handle);
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * @brief Reset all audio hardware control registers to their power on state.
+ *
+ * This resets all of the audio hardware control registers back to their
+ * power on states. Use this function with care since it also invalidates
+ * (i.e., automatically closes) all currently opened device handles.
+ *
+ * @retval      PMIC_SUCCESS         If the reset operation was successful.
+ * @retval      PMIC_ERROR           If the reset was unsuccessful.
+ */
+PMIC_STATUS pmic_audio_reset_all(void)
+{
+       PMIC_STATUS rc = PMIC_SUCCESS;
+       unsigned int audio_ssi_reset = 0;
+       unsigned int audio_rx1_reset = 0;
+       /* We need a critical section here to maintain a consistent state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       /* First close all opened device handles, also deregisters callbacks. */
+       pmic_audio_close_handle(stDAC.handle);
+       pmic_audio_close_handle(vCodec.handle);
+       pmic_audio_close_handle(extStereoIn.handle);
+
+       if (pmic_write_reg(REG_AUDIO_RX_1, RESET_AUDIO_RX_1,
+                          PMIC_ALL_BITS) != PMIC_SUCCESS) {
+               rc = PMIC_ERROR;
+       } else {
+               audio_rx1_reset = 1;
+       }
+       if (pmic_write_reg(REG_AUDIO_SSI_NETWORK, RESET_SSI_NETWORK,
+                          PMIC_ALL_BITS) != PMIC_SUCCESS) {
+               rc = PMIC_ERROR;
+       } else {
+               audio_ssi_reset = 1;
+       }
+       if (pmic_write_reg
+           (REG_AUDIO_STEREO_DAC, RESET_ST_DAC,
+            PMIC_ALL_BITS) != PMIC_SUCCESS) {
+               rc = PMIC_ERROR;
+       } else {
+               /* Also reset the driver state information to match. Note that we
+                * keep the device handle and event callback settings unchanged
+                * since these don't affect the actual hardware and we rely on
+                * the user to explicitly close the handle or deregister callbacks
+                */
+               if (audio_ssi_reset) {
+                       /* better to check if SSI is also reset as some fields are represennted in SSI reg */
+                       stDAC.busID = AUDIO_DATA_BUS_1;
+                       stDAC.protocol = NORMAL_MSB_JUSTIFIED_MODE;
+                       stDAC.masterSlave = BUS_MASTER_MODE;
+                       stDAC.protocol_set = false;
+                       stDAC.numSlots = USE_2_TIMESLOTS;
+                       stDAC.clockIn = CLOCK_IN_CLIA;
+                       stDAC.samplingRate = STDAC_RATE_44_1_KHZ;
+                       stDAC.clockFreq = STDAC_CLI_13MHZ;
+                       stDAC.invert = NO_INVERT;
+                       stDAC.timeslot = USE_TS0_TS1;
+                       stDAC.config = (PMIC_AUDIO_STDAC_CONFIG) 0;
+               }
+       }
+
+       if (pmic_write_reg(REG_AUDIO_CODEC, RESET_AUD_CODEC,
+                          PMIC_ALL_BITS) != PMIC_SUCCESS) {
+               rc = PMIC_ERROR;
+       } else {
+               /* Also reset the driver state information to match. Note that we
+                * keep the device handle and event callback settings unchanged
+                * since these don't affect the actual hardware and we rely on
+                * the user to explicitly close the handle or deregister callbacks
+                */
+               if (audio_ssi_reset) {
+                       vCodec.busID = AUDIO_DATA_BUS_2;
+                       vCodec.protocol = NETWORK_MODE;
+                       vCodec.masterSlave = BUS_SLAVE_MODE;
+                       vCodec.protocol_set = false;
+                       vCodec.numSlots = USE_4_TIMESLOTS;
+                       vCodec.clockIn = CLOCK_IN_CLIB;
+                       vCodec.samplingRate = VCODEC_RATE_8_KHZ;
+                       vCodec.clockFreq = VCODEC_CLI_13MHZ;
+                       vCodec.invert = NO_INVERT;
+                       vCodec.timeslot = USE_TS0;
+                       vCodec.config =
+                           INPUT_HIGHPASS_FILTER | OUTPUT_HIGHPASS_FILTER;
+               }
+       }
+
+       if (pmic_write_reg(REG_AUDIO_RX_0, RESET_AUDIO_RX_0,
+                          PMIC_ALL_BITS) != PMIC_SUCCESS) {
+               rc = PMIC_ERROR;
+       } else {
+               /* Also reset the driver state information to match. */
+               audioOutput.outputPort = (PMIC_AUDIO_OUTPUT_PORT) NULL;
+               audioOutput.vCodecoutputPGAGain = OUTPGA_GAIN_0DB;
+               audioOutput.stDacoutputPGAGain = OUTPGA_GAIN_0DB;
+               audioOutput.extStereooutputPGAGain = OUTPGA_GAIN_0DB;
+               audioOutput.balanceLeftGain = BAL_GAIN_0DB;
+               audioOutput.balanceRightGain = BAL_GAIN_0DB;
+               audioOutput.monoAdderGain = MONOADD_GAIN_0DB;
+               audioOutput.config = (PMIC_AUDIO_OUTPUT_CONFIG) 0;
+               audioOutput.vCodecOut = VCODEC_DIRECT_OUT;
+       }
+
+       if (pmic_write_reg(REG_AUDIO_TX, RESET_AUDIO_TX,
+                          PMIC_ALL_BITS) != PMIC_SUCCESS) {
+               rc = PMIC_ERROR;
+       } else {
+               /* Also reset the driver state information to match. Note that we
+                * reset the vCodec fields since all of the input/recording
+                * devices are only connected to the Voice CODEC and are managed
+                * as part of the Voice CODEC state.
+                */
+               if (audio_rx1_reset) {
+                       vCodec.leftChannelMic.mic = NO_MIC;
+                       vCodec.leftChannelMic.micOnOff = MICROPHONE_OFF;
+                       vCodec.leftChannelMic.ampMode = CURRENT_TO_VOLTAGE;
+                       vCodec.leftChannelMic.gain = MIC_GAIN_0DB;
+                       vCodec.rightChannelMic.mic = NO_MIC;
+                       vCodec.rightChannelMic.micOnOff = MICROPHONE_OFF;
+                       vCodec.rightChannelMic.ampMode = AMP_OFF;
+                       vCodec.rightChannelMic.gain = MIC_GAIN_0DB;
+               }
+       }
+       /* Finally, also reset any global state variables. */
+       headsetState = NO_HEADSET;
+       /* Exit the critical section. */
+       up(&mutex);
+       return rc;
+}
+
+/*!
+ * @brief Set the Audio callback function.
+ *
+ * Register a callback function that will be used to signal PMIC audio
+ * events. For example, the OSS audio driver should register a callback
+ * function in order to be notified of headset connect/disconnect events.
+ *
+ * @param   func            A pointer to the callback function.
+ * @param   eventMask       A mask selecting events to be notified.
+ * @param   hs_state        To know the headset state.
+ *
+ *
+ *
+ * @retval      PMIC_SUCCESS         If the callback was successfully
+ *                                   registered.
+ * @retval      PMIC_PARAMETER_ERROR If the handle or the eventMask is invalid.
+ */
+PMIC_STATUS pmic_audio_set_callback(void *func,
+                                   const PMIC_AUDIO_EVENTS eventMask,
+                                   PMIC_HS_STATE *hs_state)
+{
+       unsigned long flags;
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       pmic_event_callback_t eventNotify;
+
+       /* We need to start a critical section here to ensure a consistent state
+        * in case simultaneous calls to pmic_audio_set_callback() are made. In
+        * that case, we must serialize the calls to ensure that the "callback"
+        * and "eventMask" state variables are always consistent.
+        *
+        * Note that we don't actually need to acquire the spinlock until later
+        * when we are finally ready to update the "callback" and "eventMask"
+        * state variables which are shared with the interrupt handler.
+        */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       rc = PMIC_ERROR;
+       /* Register for PMIC events from the core protocol driver. */
+       if (eventMask & MICROPHONE_DETECTED) {
+               /* We need to register for the A1 amplifier interrupt. */
+               eventNotify.func = func;
+               eventNotify.param = (void *)(CORE_EVENT_MC2BI);
+               rc = pmic_event_subscribe(EVENT_MC2BI, eventNotify);
+
+               if (rc != PMIC_SUCCESS) {
+                       pr_debug
+                           ("%s: pmic_event_subscribe() for EVENT_HSDETI "
+                            "failed\n", __FILE__);
+                       goto End;
+               }
+       }
+
+       if (eventMask & HEADSET_DETECTED) {
+               /* We need to register for the A1 amplifier interrupt. */
+               eventNotify.func = func;
+               eventNotify.param = (void *)(CORE_EVENT_HSDETI);
+               rc = pmic_event_subscribe(EVENT_HSDETI, eventNotify);
+
+               if (rc != PMIC_SUCCESS) {
+                       pr_debug
+                           ("%s: pmic_event_subscribe() for EVENT_HSDETI "
+                            "failed\n", __FILE__);
+                       goto Cleanup_HDT;
+               }
+
+       }
+       if (eventMask & HEADSET_STEREO) {
+               /* We need to register for the A1 amplifier interrupt. */
+               eventNotify.func = func;
+               eventNotify.param = (void *)(CORE_EVENT_HSLI);
+               rc = pmic_event_subscribe(EVENT_HSLI, eventNotify);
+
+               if (rc != PMIC_SUCCESS) {
+                       pr_debug
+                           ("%s: pmic_event_subscribe() for EVENT_HSLI "
+                            "failed\n", __FILE__);
+                       goto Cleanup_HST;
+               }
+       }
+       if (eventMask & HEADSET_THERMAL_SHUTDOWN) {
+               /* We need to register for the A1 amplifier interrupt. */
+               eventNotify.func = func;
+               eventNotify.param = (void *)(CORE_EVENT_ALSPTHI);
+               rc = pmic_event_subscribe(EVENT_ALSPTHI, eventNotify);
+
+               if (rc != PMIC_SUCCESS) {
+                       pr_debug
+                           ("%s: pmic_event_subscribe() for EVENT_ALSPTHI "
+                            "failed\n", __FILE__);
+                       goto Cleanup_TSD;
+               }
+               pr_debug("Registered for EVENT_ALSPTHI\n");
+       }
+       if (eventMask & HEADSET_SHORT_CIRCUIT) {
+               /* We need to register for the A1 amplifier interrupt. */
+               eventNotify.func = func;
+               eventNotify.param = (void *)(CORE_EVENT_AHSSHORTI);
+               rc = pmic_event_subscribe(EVENT_AHSSHORTI, eventNotify);
+
+               if (rc != PMIC_SUCCESS) {
+                       pr_debug
+                           ("%s: pmic_event_subscribe() for EVENT_AHSSHORTI "
+                            "failed\n", __FILE__);
+                       goto Cleanup_HShort;
+               }
+               pr_debug("Registered for EVENT_AHSSHORTI\n");
+       }
+
+       /* We also need the spinlock here to avoid possible problems
+        * with the interrupt handler  when we update the
+        * "callback" and "eventMask" state variables.
+        */
+       spin_lock_irqsave(&lock, flags);
+
+       /* Successfully registered for all events. */
+       event_state.callback = func;
+       event_state.eventMask = eventMask;
+
+       /* The spinlock is no longer needed now that we've finished
+        * updating the "callback" and "eventMask" state variables.
+        */
+       spin_unlock_irqrestore(&lock, flags);
+
+       goto End;
+
+       /* This section unregisters any already registered events if we should
+        * encounter an error partway through the registration process. Note
+        * that we don't check the return status here since it is already set
+        * to PMIC_ERROR before we get here.
+        */
+      Cleanup_HShort:
+
+       if (eventMask & HEADSET_SHORT_CIRCUIT) {
+               eventNotify.func = func;
+               eventNotify.param = (void *)(CORE_EVENT_AHSSHORTI);
+               pmic_event_unsubscribe(EVENT_AHSSHORTI, eventNotify);
+       }
+
+      Cleanup_TSD:
+
+       if (eventMask & HEADSET_THERMAL_SHUTDOWN) {
+               eventNotify.func = func;
+               eventNotify.param = (void *)(CORE_EVENT_ALSPTHI);
+               pmic_event_unsubscribe(EVENT_ALSPTHI, eventNotify);
+       }
+
+      Cleanup_HST:
+
+       if (eventMask & HEADSET_STEREO) {
+               eventNotify.func = func;
+               eventNotify.param = (void *)(CORE_EVENT_HSLI);
+               pmic_event_unsubscribe(EVENT_HSLI, eventNotify);
+       }
+
+      Cleanup_HDT:
+
+       if (eventMask & HEADSET_DETECTED) {
+               eventNotify.func = func;
+               eventNotify.param = (void *)(CORE_EVENT_HSDETI);
+               pmic_event_unsubscribe(EVENT_HSDETI, eventNotify);
+       }
+
+      End:
+       /* Exit the critical section. */
+       up(&mutex);
+       return rc;
+}
+
+/*!
+ * @brief Deregisters the existing audio callback function.
+ *
+ * Deregister the callback function that was previously registered by calling
+ * pmic_audio_set_callback().
+ *
+ *
+ * @retval      PMIC_SUCCESS         If the callback was successfully
+ *                                   deregistered.
+ * @retval      PMIC_PARAMETER_ERROR If the handle is invalid.
+ */
+PMIC_STATUS pmic_audio_clear_callback(void)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+       /* We need a critical section to maintain a consistent state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if (event_state.callback != (PMIC_AUDIO_CALLBACK) NULL) {
+               rc = pmic_audio_deregister(&(event_state.callback),
+                                          &(event_state.eventMask));
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+       return rc;
+}
+
+/*!
+ * @brief Get the current audio callback function settings.
+ *
+ * Get the current callback function and event mask.
+ *
+ * @param   func            The current callback function.
+ * @param   eventMask       The current event selection mask.
+ *
+ * @retval      PMIC_SUCCESS         If the callback information was
+ *                                   successfully retrieved.
+ * @retval      PMIC_PARAMETER_ERROR If the handle is invalid.
+ */
+PMIC_STATUS pmic_audio_get_callback(PMIC_AUDIO_CALLBACK * const func,
+                                   PMIC_AUDIO_EVENTS * const eventMask)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+       /* We only need to acquire the mutex here because we will not be updating
+        * anything that may affect the interrupt handler. We just need to ensure
+        * that the callback fields are not changed while we are in the critical
+        * section by calling either pmic_audio_set_callback() or
+        * pmic_audio_clear_callback().
+        */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((func != (PMIC_AUDIO_CALLBACK *) NULL) &&
+           (eventMask != (PMIC_AUDIO_EVENTS *) NULL)) {
+
+               *func = event_state.callback;
+               *eventMask = event_state.eventMask;
+
+               rc = PMIC_SUCCESS;
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+       return rc;
+}
+
+/*!
+ * @brief Enable the anti-pop circuitry to avoid extra noise when inserting
+ *        or removing a external device (e.g., a headset).
+ *
+ * Enable the use of the built-in anti-pop circuitry to prevent noise from
+ * being generated when an external audio device is inserted or removed
+ * from an audio plug. A slow ramp speed may be needed to avoid extra noise.
+ *
+ * @param       rampSpeed       The desired anti-pop circuitry ramp speed.
+ *
+ * @retval      PMIC_SUCCESS         If the anti-pop circuitry was successfully
+ *                                   enabled.
+ * @retval      PMIC_ERROR           If the anti-pop circuitry could not be
+ *                                   enabled.
+ */
+PMIC_STATUS pmic_audio_antipop_enable(const PMIC_AUDIO_ANTI_POP_RAMP_SPEED
+                                     rampSpeed)
+{
+       PMIC_STATUS rc = PMIC_ERROR;
+       unsigned int reg_value = 0;
+       const unsigned int reg_mask = SET_BITS(regAUDIO_RX_0, BIASEN, 1) |
+           SET_BITS(regAUDIO_RX_0, BIASSPEED, 1);
+
+       /* No critical section required here since we are not updating any
+        * global data.
+        */
+
+       /*
+        * Antipop is enabled by enabling the BIAS (BIASEN) and setting the
+        * BIASSPEED .
+        * BIASEN is just to make sure that BIAS is enabled
+        */
+       reg_value = SET_BITS(regAUDIO_RX_0, BIASEN, 1)
+           | SET_BITS(regAUDIO_RX_0, BIASSPEED, 0) | SET_BITS(regAUDIO_RX_0,
+                                                              HSLDETEN, 1);
+       rc = pmic_write_reg(REG_AUDIO_RX_0, reg_value, reg_mask);
+       return rc;
+}
+
+/*!
+ * @brief Disable the anti-pop circuitry.
+ *
+ * Disable the use of the built-in anti-pop circuitry to prevent noise from
+ * being generated when an external audio device is inserted or removed
+ * from an audio plug.
+ *
+ * @retval      PMIC_SUCCESS         If the anti-pop circuitry was successfully
+ *                                   disabled.
+ * @retval      PMIC_ERROR           If the anti-pop circuitry could not be
+ *                                   disabled.
+ */
+PMIC_STATUS pmic_audio_antipop_disable(void)
+{
+       PMIC_STATUS rc = PMIC_ERROR;
+       const unsigned int reg_mask = SET_BITS(regAUDIO_RX_0, BIASSPEED, 1) |
+           SET_BITS(regAUDIO_RX_0, BIASEN, 1);
+       const unsigned int reg_write = SET_BITS(regAUDIO_RX_0, BIASSPEED, 1) |
+           SET_BITS(regAUDIO_RX_0, BIASEN, 0);
+
+       /* No critical section required here since we are not updating any
+        * global data.
+        */
+
+       /*
+        * Antipop is disabled by setting BIASSPEED  = 0. BIASEN bit remains set
+        * as only antipop needs to be disabled
+        */
+       rc = pmic_write_reg(REG_AUDIO_RX_0, reg_write, reg_mask);
+
+       return rc;
+}
+
+/*!
+ * @brief Performs a reset of the Voice CODEC/Stereo DAC digital filter.
+ *
+ * The digital filter should be reset whenever the clock or sampling rate
+ * configuration has been changed.
+ *
+ * @param       handle          Device handle from pmic_audio_open() call.
+ *
+ * @retval      PMIC_SUCCESS         If the digital filter was successfully
+ *                                   reset.
+ * @retval      PMIC_ERROR           If the digital filter could not be reset.
+ */
+PMIC_STATUS pmic_audio_digital_filter_reset(const PMIC_AUDIO_HANDLE handle)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       unsigned int reg_mask = 0;
+
+       /* No critical section required here since we are not updating any
+        * global data.
+        */
+
+       if ((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE)) {
+               reg_mask = SET_BITS(regST_DAC, STDCRESET, 1);
+               if (pmic_write_reg(REG_AUDIO_STEREO_DAC, reg_mask,
+                                  reg_mask) != PMIC_SUCCESS) {
+                       rc = PMIC_ERROR;
+               } else {
+                       pr_debug("STDAC filter reset\n");
+               }
+
+       } else if ((handle == vCodec.handle) &&
+                  (vCodec.handleState == HANDLE_IN_USE)) {
+               reg_mask = SET_BITS(regAUD_CODEC, CDCRESET, 1);
+               if (pmic_write_reg(REG_AUDIO_CODEC, reg_mask,
+                                  reg_mask) != PMIC_SUCCESS) {
+                       rc = PMIC_ERROR;
+               } else {
+                       pr_debug("CODEC filter reset\n");
+               }
+       }
+       return rc;
+}
+
+/*!
+ * @brief Get the most recent PTT button voltage reading.
+ *
+ * This feature is not supported by mc13783
+ * @param       level                PTT button level.
+ *
+ * @retval      PMIC_SUCCESS         If the most recent PTT button voltage was
+ *                                   returned.
+ * @retval      PMIC_PARAMETER_ERROR If a NULL pointer argument was given.
+ */
+PMIC_STATUS pmic_audio_get_ptt_button_level(unsigned int *const level)
+{
+       PMIC_STATUS rc = PMIC_NOT_SUPPORTED;
+       return rc;
+}
+
+#ifdef DEBUG_AUDIO
+
+/*!
+ * @brief Provide a hexadecimal dump of all PMIC audio registers (DEBUG only)
+ *
+ * This function is intended strictly for debugging purposes only and will
+ * print the current values of the following PMIC registers:
+ *
+ * - AUD_CODEC
+ * - ST_DAC
+ * - AUDIO_RX_0
+ * - AUDIO_RX_1
+ * - AUDIO_TX
+ * - AUDIO_SSI_NW
+ *
+ * The register fields will not be decoded.
+ *
+ * Note that we don't dump any of the arbitration bits because we cannot
+ * access the true arbitration bit settings when reading the registers
+ * from the secondary SPI bus.
+ *
+ * Also note that we must not call this function with interrupts disabled,
+ * for example, while holding a spinlock, because calls to pmic_read_reg()
+ * eventually end up in the SPI driver which will want to perform a
+ * schedule() operation. If schedule() is called with interrupts disabled,
+ * then you will see messages like the following:
+ *
+ * BUG: scheduling while atomic: ...
+ *
+ */
+void pmic_audio_dump_registers(void)
+{
+       unsigned int reg_value = 0;
+
+       /* Dump the AUD_CODEC (Voice CODEC) register. */
+       if (pmic_read_reg(REG_AUDIO_CODEC, &reg_value, REG_FULLMASK)
+           == PMIC_SUCCESS) {
+               pr_debug("Audio Codec = 0x%x\n", reg_value);
+       } else {
+               pr_debug("Failed to read audio codec\n");
+       }
+
+       /* Dump the ST DAC (Stereo DAC) register. */
+       if (pmic_read_reg
+           (REG_AUDIO_STEREO_DAC, &reg_value, REG_FULLMASK) == PMIC_SUCCESS) {
+               pr_debug("Stereo DAC = 0x%x\n", reg_value);
+       } else {
+               pr_debug("Failed to read Stereo DAC\n");
+       }
+
+       /* Dump the SSI NW register. */
+       if (pmic_read_reg
+           (REG_AUDIO_SSI_NETWORK, &reg_value, REG_FULLMASK) == PMIC_SUCCESS) {
+               pr_debug("SSI Network = 0x%x\n", reg_value);
+       } else {
+               pr_debug("Failed to read SSI network\n");
+       }
+
+       /* Dump the Audio RX 0 register. */
+       if (pmic_read_reg(REG_AUDIO_RX_0, &reg_value, REG_FULLMASK)
+           == PMIC_SUCCESS) {
+               pr_debug("Audio RX 0 = 0x%x\n", reg_value);
+       } else {
+               pr_debug("Failed to read audio RX 0\n");
+       }
+
+       /* Dump the Audio RX 1 register. */
+       if (pmic_read_reg(REG_AUDIO_RX_1, &reg_value, REG_FULLMASK)
+           == PMIC_SUCCESS) {
+               pr_debug("Audio RX 1 = 0x%x\n", reg_value);
+       } else {
+               pr_debug("Failed to read audio RX 1\n");
+       }
+       /* Dump the Audio TX register. */
+       if (pmic_read_reg(REG_AUDIO_TX, &reg_value, REG_FULLMASK) ==
+           PMIC_SUCCESS) {
+               pr_debug("Audio Tx = 0x%x\n", reg_value);
+       } else {
+               pr_debug("Failed to read audio TX\n");
+       }
+
+}
+
+#endif                         /* DEBUG_AUDIO */
+
+/*@}*/
+
+/*************************************************************************
+ * General Voice CODEC configuration.
+ *************************************************************************
+ */
+
+/*!
+ * @name General Voice CODEC Setup and Configuration APIs
+ * Functions for general setup and configuration of the PMIC Voice
+ * CODEC hardware.
+ */
+/*@{*/
+
+/*!
+ * @brief Set the Voice CODEC clock source and operating characteristics.
+ *
+ * Define the Voice CODEC clock source and operating characteristics. This
+ * must be done before the Voice CODEC is enabled.
+ *
+ *
+ *
+ * @param       handle          Device handle from pmic_audio_open() call.
+ * @param       clockIn         Select the clock signal source.
+ * @param       clockFreq       Select the clock signal frequency.
+ * @param       samplingRate    Select the audio data sampling rate.
+ * @param       invert          Enable inversion of the frame sync and/or
+ *                              bit clock inputs.
+ *
+ * @retval      PMIC_SUCCESS         If the Voice CODEC clock settings were
+ *                                   successfully configured.
+ * @retval      PMIC_PARAMETER_ERROR If the handle or clock configuration was
+ *                                   invalid.
+ * @retval      PMIC_ERROR           If the Voice CODEC clock configuration
+ *                                   could not be set.
+ */
+PMIC_STATUS pmic_audio_vcodec_set_clock(const PMIC_AUDIO_HANDLE handle,
+                                       const PMIC_AUDIO_CLOCK_IN_SOURCE
+                                       clockIn,
+                                       const PMIC_AUDIO_VCODEC_CLOCK_IN_FREQ
+                                       clockFreq,
+                                       const PMIC_AUDIO_VCODEC_SAMPLING_RATE
+                                       samplingRate,
+                                       const PMIC_AUDIO_CLOCK_INVERT invert)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       unsigned int reg_value = 0;
+       unsigned int reg_mask = 0;
+
+       /* Use a critical section to ensure a consistent hardware state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       /* Validate all of the calling parameters. */
+       if (handle == (PMIC_AUDIO_HANDLE) NULL) {
+               rc = PMIC_PARAMETER_ERROR;
+       } else if ((handle == vCodec.handle) &&
+                  (vCodec.handleState == HANDLE_IN_USE)) {
+               if ((clockIn != CLOCK_IN_CLIA) && (clockIn != CLOCK_IN_CLIB)) {
+                       rc = PMIC_PARAMETER_ERROR;
+               } else if (!((clockFreq >= VCODEC_CLI_13MHZ)
+                            && (clockFreq <= VCODEC_CLI_33_6MHZ))) {
+                       rc = PMIC_PARAMETER_ERROR;
+               } else if ((samplingRate != VCODEC_RATE_8_KHZ)
+                          && (samplingRate != VCODEC_RATE_16_KHZ)) {
+                       rc = PMIC_PARAMETER_ERROR;
+               } else if (!((invert >= NO_INVERT)
+                            && (invert <= INVERT_FRAMESYNC))) {
+                       rc = PMIC_PARAMETER_ERROR;
+               } else {
+                       /*reg_mask = SET_BITS(regAUD_CODEC, CDCCLK, 7) |
+                          SET_BITS(regAUD_CODEC, CDCCLKSEL, 1) |
+                          SET_BITS(regAUD_CODEC, CDCFS8K16K, 1) |
+                          SET_BITS(regAUD_CODEC, CDCBCLINV, 1) |
+                          SET_BITS(regAUD_CODEC, CDCFSINV, 1); */
+                       if (clockIn == CLOCK_IN_CLIA) {
+                               reg_value =
+                                   SET_BITS(regAUD_CODEC, CDCCLKSEL, 0);
+                       } else {
+                               reg_value =
+                                   SET_BITS(regAUD_CODEC, CDCCLKSEL, 1);
+                       }
+                       reg_mask = SET_BITS(regAUD_CODEC, CDCCLKSEL, 1);
+                       if (PMIC_SUCCESS !=
+                           pmic_write_reg(REG_AUDIO_CODEC,
+                                          reg_value, reg_mask))
+                               return PMIC_ERROR;
+
+                       reg_value = 0;
+                       if (clockFreq == VCODEC_CLI_13MHZ) {
+                               reg_value |= SET_BITS(regAUD_CODEC, CDCCLK, 0);
+                       } else if (clockFreq == VCODEC_CLI_15_36MHZ) {
+                               reg_value |= SET_BITS(regAUD_CODEC, CDCCLK, 1);
+                       } else if (clockFreq == VCODEC_CLI_16_8MHZ) {
+                               reg_value |= SET_BITS(regAUD_CODEC, CDCCLK, 2);
+                       } else if (clockFreq == VCODEC_CLI_26MHZ) {
+                               reg_value |= SET_BITS(regAUD_CODEC, CDCCLK, 4);
+                       } else {
+                               reg_value |= SET_BITS(regAUD_CODEC, CDCCLK, 7);
+                       }
+                       reg_mask = SET_BITS(regAUD_CODEC, CDCCLK, 7);
+                       if (PMIC_SUCCESS !=
+                           pmic_write_reg(REG_AUDIO_CODEC,
+                                          reg_value, reg_mask))
+                               return PMIC_ERROR;
+
+                       reg_value = 0;
+                       reg_mask = 0;
+
+                       if (samplingRate == VCODEC_RATE_8_KHZ) {
+                               reg_value |=
+                                   SET_BITS(regAUD_CODEC, CDCFS8K16K, 0);
+                       } else {
+                               reg_value |=
+                                   SET_BITS(regAUD_CODEC, CDCFS8K16K, 1);
+                       }
+                       reg_mask = SET_BITS(regAUD_CODEC, CDCFS8K16K, 1);
+                       if (PMIC_SUCCESS !=
+                           pmic_write_reg(REG_AUDIO_CODEC,
+                                          reg_value, reg_mask))
+                               return PMIC_ERROR;
+                       reg_value = 0;
+                       reg_mask =
+                           SET_BITS(regAUD_CODEC, CDCBCLINV,
+                                    1) | SET_BITS(regAUD_CODEC, CDCFSINV, 1);
+
+                       if (invert & INVERT_BITCLOCK) {
+                               reg_value |=
+                                   SET_BITS(regAUD_CODEC, CDCBCLINV, 1);
+                       }
+                       if (invert & INVERT_FRAMESYNC) {
+                               reg_value |=
+                                   SET_BITS(regAUD_CODEC, CDCFSINV, 1);
+                       }
+                       if (invert & NO_INVERT) {
+                               reg_value |=
+                                   SET_BITS(regAUD_CODEC, CDCBCLINV, 0);
+                               reg_value |=
+                                   SET_BITS(regAUD_CODEC, CDCFSINV, 0);
+                       }
+                       if (pmic_write_reg
+                           (REG_AUDIO_CODEC, reg_value,
+                            reg_mask) != PMIC_SUCCESS) {
+                               rc = PMIC_ERROR;
+                       } else {
+                               pr_debug("CODEC clock set\n");
+                               vCodec.clockIn = clockIn;
+                               vCodec.clockFreq = clockFreq;
+                               vCodec.samplingRate = samplingRate;
+                               vCodec.invert = invert;
+                       }
+
+               }
+
+       } else {
+               rc = PMIC_PARAMETER_ERROR;
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * @brief Get the Voice CODEC clock source and operating characteristics.
+ *
+ * Get the current Voice CODEC clock source and operating characteristics.
+ *
+ * @param      handle          Device handle from pmic_audio_open() call.
+ * @param      clockIn         The clock signal source.
+ * @param      clockFreq       The clock signal frequency.
+ * @param      samplingRate    The audio data sampling rate.
+ * @param       invert          Inversion of the frame sync and/or
+ *                              bit clock inputs is enabled/disabled.
+ *
+ * @retval      PMIC_SUCCESS         If the Voice CODEC clock settings were
+ *                                   successfully retrieved.
+ * @retval      PMIC_PARAMETER_ERROR If the handle invalid.
+ * @retval      PMIC_ERROR           If the Voice CODEC clock configuration
+ *                                   could not be retrieved.
+ */
+PMIC_STATUS pmic_audio_vcodec_get_clock(const PMIC_AUDIO_HANDLE handle,
+                                       PMIC_AUDIO_CLOCK_IN_SOURCE *
+                                       const clockIn,
+                                       PMIC_AUDIO_VCODEC_CLOCK_IN_FREQ *
+                                       const clockFreq,
+                                       PMIC_AUDIO_VCODEC_SAMPLING_RATE *
+                                       const samplingRate,
+                                       PMIC_AUDIO_CLOCK_INVERT * const invert)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+       /* Use a critical section to ensure that we return a consistent state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle == vCodec.handle) &&
+           (vCodec.handleState == HANDLE_IN_USE) &&
+           (clockIn != (PMIC_AUDIO_CLOCK_IN_SOURCE *) NULL) &&
+           (clockFreq != (PMIC_AUDIO_VCODEC_CLOCK_IN_FREQ *) NULL) &&
+           (samplingRate != (PMIC_AUDIO_VCODEC_SAMPLING_RATE *) NULL) &&
+           (invert != (PMIC_AUDIO_CLOCK_INVERT *) NULL)) {
+               *clockIn = vCodec.clockIn;
+               *clockFreq = vCodec.clockFreq;
+               *samplingRate = vCodec.samplingRate;
+               *invert = vCodec.invert;
+
+               rc = PMIC_SUCCESS;
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * @brief Set the Voice CODEC primary audio channel timeslot.
+ *
+ * Set the Voice CODEC primary audio channel timeslot. This function must be
+ * used if the default timeslot for the primary audio channel is to be changed.
+ *
+ * @param       handle          Device handle from pmic_audio_open() call.
+ * @param       timeslot        Select the primary audio channel timeslot.
+ *
+ * @retval      PMIC_SUCCESS         If the Voice CODEC primary audio channel
+ *                                   timeslot was successfully configured.
+ * @retval      PMIC_PARAMETER_ERROR If the handle or audio channel timeslot
+ *                                   was invalid.
+ * @retval      PMIC_ERROR           If the Voice CODEC primary audio channel
+ *                                   timeslot could not be set.
+ */
+PMIC_STATUS pmic_audio_vcodec_set_rxtx_timeslot(const PMIC_AUDIO_HANDLE handle,
+                                               const PMIC_AUDIO_VCODEC_TIMESLOT
+                                               timeslot)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       unsigned int reg_write = 0;
+       const unsigned int reg_mask = SET_BITS(regSSI_NETWORK, CDCTXRXSLOT, 3);
+
+       /* Use a critical section to ensure a consistent hardware state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle == vCodec.handle) &&
+           (vCodec.handleState == HANDLE_IN_USE) &&
+           ((timeslot == USE_TS0) || (timeslot == USE_TS1) ||
+            (timeslot == USE_TS2) || (timeslot == USE_TS3))) {
+               reg_write = SET_BITS(regSSI_NETWORK, CDCTXRXSLOT, timeslot);
+
+               rc = pmic_write_reg(REG_AUDIO_SSI_NETWORK, reg_write, reg_mask);
+
+               if (rc == PMIC_SUCCESS) {
+                       vCodec.timeslot = timeslot;
+               }
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * @brief Get the current Voice CODEC primary audio channel timeslot.
+ *
+ * Get the current Voice CODEC primary audio channel timeslot.
+ *
+ * @param      handle          Device handle from pmic_audio_open() call.
+ * @param       timeslot        The primary audio channel timeslot.
+ *
+ * @retval      PMIC_SUCCESS         If the Voice CODEC primary audio channel
+ *                                   timeslot was successfully retrieved.
+ * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval      PMIC_ERROR           If the Voice CODEC primary audio channel
+ *                                   timeslot could not be retrieved.
+ */
+PMIC_STATUS pmic_audio_vcodec_get_rxtx_timeslot(const PMIC_AUDIO_HANDLE handle,
+                                               PMIC_AUDIO_VCODEC_TIMESLOT *
+                                               const timeslot)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+       /* Use a critical section to ensure a consistent hardware state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle == vCodec.handle) &&
+           (vCodec.handleState == HANDLE_IN_USE) &&
+           (timeslot != (PMIC_AUDIO_VCODEC_TIMESLOT *) NULL)) {
+               *timeslot = vCodec.timeslot;
+
+               rc = PMIC_SUCCESS;
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * @brief Set the Voice CODEC secondary recording audio channel timeslot.
+ *
+ * Set the Voice CODEC secondary audio channel timeslot. This function must be
+ * used if the default timeslot for the secondary audio channel is to be
+ * changed. The secondary audio channel timeslot is used to transmit the audio
+ * data that was recorded by the Voice CODEC from the secondary audio input
+ * channel.
+ *
+ * @param   handle          Device handle from pmic_audio_open() call.
+ * @param   timeslot        Select the secondary audio channel timeslot.
+ *
+ * @retval      PMIC_SUCCESS         If the Voice CODEC secondary audio channel
+ *                                   timeslot was successfully configured.
+ * @retval      PMIC_PARAMETER_ERROR If the handle or audio channel timeslot
+ *                                   was invalid.
+ * @retval      PMIC_ERROR           If the Voice CODEC secondary audio channel
+ *                                   timeslot could not be set.
+ */
+PMIC_STATUS pmic_audio_vcodec_set_secondary_txslot(const PMIC_AUDIO_HANDLE
+                                                  handle,
+                                                  const
+                                                  PMIC_AUDIO_VCODEC_TIMESLOT
+                                                  timeslot)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       unsigned int reg_mask = SET_BITS(regSSI_NETWORK, CDCTXSECSLOT, 3);
+       unsigned int reg_write = 0;
+
+       /* Use a critical section to ensure a consistent hardware state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
+               /* How to handle primary slot and secondary slot being the same */
+               if ((timeslot >= USE_TS0) && (timeslot <= USE_TS3)
+                   && (timeslot != vCodec.timeslot)) {
+                       reg_write =
+                           SET_BITS(regSSI_NETWORK, CDCTXSECSLOT, timeslot);
+
+                       rc = pmic_write_reg(REG_AUDIO_SSI_NETWORK,
+                                           reg_write, reg_mask);
+
+                       if (rc == PMIC_SUCCESS) {
+                               vCodec.secondaryTXtimeslot = timeslot;
+                       }
+               }
+
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * @brief Get the Voice CODEC secondary recording audio channel timeslot.
+ *
+ * Get the Voice CODEC secondary audio channel timeslot.
+ *
+ * @param       handle          Device handle from pmic_audio_open() call.
+ * @param       timeslot        The secondary audio channel timeslot.
+ *
+ * @retval      PMIC_SUCCESS         If the Voice CODEC secondary audio channel
+ *                                   timeslot was successfully retrieved.
+ * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval      PMIC_ERROR           If the Voice CODEC secondary audio channel
+ *                                   timeslot could not be retrieved.
+ */
+PMIC_STATUS pmic_audio_vcodec_get_secondary_txslot(const PMIC_AUDIO_HANDLE
+                                                  handle,
+                                                  PMIC_AUDIO_VCODEC_TIMESLOT *
+                                                  const timeslot)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+       /* Use a critical section to ensure a consistent hardware state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle == vCodec.handle) &&
+           (vCodec.handleState == HANDLE_IN_USE) &&
+           (timeslot != (PMIC_AUDIO_VCODEC_TIMESLOT *) NULL)) {
+               rc = PMIC_SUCCESS;
+               *timeslot = vCodec.secondaryTXtimeslot;
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+       return rc;
+}
+
+/*!
+ * @brief Set/Enable the Voice CODEC options.
+ *
+ * Set or enable various Voice CODEC options. The available options include
+ * the use of dithering, highpass digital filters, and loopback modes.
+ *
+ * @param       handle          Device handle from pmic_audio_open() call.
+ * @param       config          The Voice CODEC options to enable.
+ *
+ * @retval      PMIC_SUCCESS         If the Voice CODEC options were
+ *                                   successfully configured.
+ * @retval      PMIC_PARAMETER_ERROR If the handle or Voice CODEC options
+ *                                   were invalid.
+ * @retval      PMIC_ERROR           If the Voice CODEC options could not be
+ *                                   successfully set/enabled.
+ */
+PMIC_STATUS pmic_audio_vcodec_set_config(const PMIC_AUDIO_HANDLE handle,
+                                        const PMIC_AUDIO_VCODEC_CONFIG config)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       unsigned int reg_write = 0;
+       unsigned int reg_mask = 0;
+
+       /* Use a critical section to ensure a consistent hardware state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
+               if (config & DITHERING) {
+                       reg_write = SET_BITS(regAUD_CODEC, CDCDITH, 0);
+                       reg_mask = SET_BITS(regAUD_CODEC, CDCDITH, 1);
+               }
+
+               if (config & INPUT_HIGHPASS_FILTER) {
+                       reg_write |= SET_BITS(regAUD_CODEC, AUDIHPF, 1);
+                       reg_mask |= SET_BITS(regAUD_CODEC, AUDIHPF, 1);
+               }
+
+               if (config & OUTPUT_HIGHPASS_FILTER) {
+                       reg_write |= SET_BITS(regAUD_CODEC, AUDOHPF, 1);
+                       reg_mask |= SET_BITS(regAUD_CODEC, AUDOHPF, 1);
+               }
+
+               if (config & DIGITAL_LOOPBACK) {
+                       reg_write |= SET_BITS(regAUD_CODEC, CDCDLM, 1);
+                       reg_mask |= SET_BITS(regAUD_CODEC, CDCDLM, 1);
+               }
+
+               if (config & ANALOG_LOOPBACK) {
+                       reg_write |= SET_BITS(regAUD_CODEC, CDCALM, 1);
+                       reg_mask |= SET_BITS(regAUD_CODEC, CDCALM, 1);
+               }
+
+               if (config & VCODEC_MASTER_CLOCK_OUTPUTS) {
+                       reg_write |= SET_BITS(regAUD_CODEC, CDCCLKEN, 1) |
+                           SET_BITS(regAUD_CODEC, CDCTS, 0);
+                       reg_mask |= SET_BITS(regAUD_CODEC, CDCCLKEN, 1) |
+                           SET_BITS(regAUD_CODEC, CDCTS, 1);
+
+               }
+
+               if (config & TRISTATE_TS) {
+                       reg_write |= SET_BITS(regAUD_CODEC, CDCTS, 1);
+                       reg_mask |= SET_BITS(regAUD_CODEC, CDCTS, 1);
+               }
+
+               if (reg_mask == 0) {
+                       /* We should not reach this point without having to configure
+                        * anything so we flag it as an error.
+                        */
+                       rc = PMIC_ERROR;
+               } else {
+                       rc = pmic_write_reg(REG_AUDIO_CODEC,
+                                           reg_write, reg_mask);
+               }
+
+               if (rc == PMIC_SUCCESS) {
+                       vCodec.config |= config;
+               }
+       }
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * @brief Clear/Disable the Voice CODEC options.
+ *
+ * Clear or disable various Voice CODEC options.
+ *
+ * @param   handle          Device handle from pmic_audio_open() call.
+ * @param   config          The Voice CODEC options to be cleared/disabled.
+ *
+ * @retval      PMIC_SUCCESS         If the Voice CODEC options were
+ *                                   successfully cleared/disabled.
+ * @retval      PMIC_PARAMETER_ERROR If the handle or the Voice CODEC options
+ *                                   were invalid.
+ * @retval      PMIC_ERROR           If the Voice CODEC options could not be
+ *                                   cleared/disabled.
+ */
+PMIC_STATUS pmic_audio_vcodec_clear_config(const PMIC_AUDIO_HANDLE handle,
+                                          const PMIC_AUDIO_VCODEC_CONFIG
+                                          config)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       unsigned int reg_write = 0;
+       unsigned int reg_mask = 0;
+
+       /* Use a critical section to ensure a consistent hardware state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
+               if (config & DITHERING) {
+                       reg_mask = SET_BITS(regAUD_CODEC, CDCDITH, 1);
+                       reg_write = SET_BITS(regAUD_CODEC, CDCDITH, 1);
+               }
+
+               if (config & INPUT_HIGHPASS_FILTER) {
+                       reg_mask |= SET_BITS(regAUD_CODEC, AUDIHPF, 1);
+               }
+
+               if (config & OUTPUT_HIGHPASS_FILTER) {
+                       reg_mask |= SET_BITS(regAUD_CODEC, AUDOHPF, 1);
+               }
+
+               if (config & DIGITAL_LOOPBACK) {
+                       reg_mask |= SET_BITS(regAUD_CODEC, CDCDLM, 1);
+               }
+
+               if (config & ANALOG_LOOPBACK) {
+                       reg_mask |= SET_BITS(regAUD_CODEC, CDCALM, 1);
+               }
+
+               if (config & VCODEC_MASTER_CLOCK_OUTPUTS) {
+                       reg_mask |= SET_BITS(regAUD_CODEC, CDCCLKEN, 1);
+               }
+
+               if (config & TRISTATE_TS) {
+                       reg_mask |= SET_BITS(regAUD_CODEC, CDCTS, 1);
+               }
+
+               if (reg_mask == 0) {
+                       /* We should not reach this point without having to configure
+                        * anything so we flag it as an error.
+                        */
+                       rc = PMIC_ERROR;
+               } else {
+                       rc = pmic_write_reg(REG_AUDIO_CODEC,
+                                           reg_write, reg_mask);
+               }
+
+               if (rc == PMIC_SUCCESS) {
+                       vCodec.config |= config;
+               }
+
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * @brief Get the current Voice CODEC options.
+ *
+ * Get the current Voice CODEC options.
+ *
+ * @param      handle          Device handle from pmic_audio_open() call.
+ * @param      config          The current set of Voice CODEC options.
+ *
+ * @retval      PMIC_SUCCESS         If the Voice CODEC options were
+ *                                   successfully retrieved.
+ * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval      PMIC_ERROR           If the Voice CODEC options could not be
+ *                                   retrieved.
+ */
+PMIC_STATUS pmic_audio_vcodec_get_config(const PMIC_AUDIO_HANDLE handle,
+                                        PMIC_AUDIO_VCODEC_CONFIG *
+                                        const config)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+       /* Use a critical section to ensure a consistent hardware state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle == vCodec.handle) &&
+           (vCodec.handleState == HANDLE_IN_USE) &&
+           (config != (PMIC_AUDIO_VCODEC_CONFIG *) NULL)) {
+               *config = vCodec.config;
+
+               rc = PMIC_SUCCESS;
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * @brief Enable the Voice CODEC bypass audio pathway.
+ *
+ * Enables the Voice CODEC bypass pathway for audio data. This allows direct
+ * output of the voltages on the TX data bus line to the output amplifiers
+ * (bypassing the digital-to-analog converters within the Voice CODEC).
+ *
+ * @param       handle          Device handle from pmic_audio_open() call.
+ *
+ * @retval      PMIC_SUCCESS         If the Voice CODEC bypass was successfully
+ *                                   enabled.
+ * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval      PMIC_ERROR           If the Voice CODEC bypass could not be
+ *                                   enabled.
+ */
+PMIC_STATUS pmic_audio_vcodec_enable_bypass(const PMIC_AUDIO_HANDLE handle)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       const unsigned int reg_write = SET_BITS(regAUD_CODEC, CDCBYP, 1);
+       const unsigned int reg_mask = SET_BITS(regAUD_CODEC, CDCBYP, 1);
+
+       /* No critical section required here since we are not updating any
+        * global data.
+        */
+
+       if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
+               rc = pmic_write_reg(REG_AUDIO_CODEC, reg_write, reg_mask);
+       }
+
+       return rc;
+}
+
+/*!
+ * @brief Disable the Voice CODEC bypass audio pathway.
+ *
+ * Disables the Voice CODEC bypass pathway for audio data. This means that
+ * the TX data bus line will deliver digital data to the digital-to-analog
+ * converters within the Voice CODEC.
+ *
+ * @param       handle          Device handle from pmic_audio_open() call.
+ *
+ * @retval      PMIC_SUCCESS         If the Voice CODEC bypass was successfully
+ *                                   disabled.
+ * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval      PMIC_ERROR           If the Voice CODEC bypass could not be
+ *                                   disabled.
+ */
+PMIC_STATUS pmic_audio_vcodec_disable_bypass(const PMIC_AUDIO_HANDLE handle)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       const unsigned int reg_write = 0;
+       const unsigned int reg_mask = SET_BITS(regAUD_CODEC, CDCBYP, 1);
+
+       /* No critical section required here since we are not updating any
+        * global data.
+        */
+
+       if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
+               rc = pmic_write_reg(REG_AUDIO_CODEC, reg_write, reg_mask);
+       }
+
+       return rc;
+}
+
+/*@}*/
+
+/*************************************************************************
+ * General Stereo DAC configuration.
+ *************************************************************************
+ */
+
+/*!
+ * @name General Stereo DAC Setup and Configuration APIs
+ * Functions for general setup and configuration of the PMIC Stereo
+ * DAC hardware.
+ */
+/*@{*/
+
+/*!
+ * @brief Set the Stereo DAC clock source and operating characteristics.
+ *
+ * Define the Stereo DAC clock source and operating characteristics. This
+ * must be done before the Stereo DAC is enabled.
+ *
+ *
+ * @param   handle          Device handle from pmic_audio_open() call.
+ * @param   clockIn         Select the clock signal source.
+ * @param   clockFreq       Select the clock signal frequency.
+ * @param   samplingRate    Select the audio data sampling rate.
+ * @param   invert          Enable inversion of the frame sync and/or
+ *                              bit clock inputs.
+ *
+ * @retval      PMIC_SUCCESS         If the Stereo DAC clock settings were
+ *                                   successfully configured.
+ * @retval      PMIC_PARAMETER_ERROR If the handle or clock configuration was
+ *                                   invalid.
+ * @retval      PMIC_ERROR           If the Stereo DAC clock configuration
+ *                                   could not be set.
+ */
+PMIC_STATUS pmic_audio_stdac_set_clock(const PMIC_AUDIO_HANDLE handle,
+                                      const PMIC_AUDIO_CLOCK_IN_SOURCE clockIn,
+                                      const PMIC_AUDIO_STDAC_CLOCK_IN_FREQ
+                                      clockFreq,
+                                      const PMIC_AUDIO_STDAC_SAMPLING_RATE
+                                      samplingRate,
+                                      const PMIC_AUDIO_CLOCK_INVERT invert)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       unsigned int reg_value = 0;
+       unsigned int reg_mask = 0;
+
+       /* Use a critical section to ensure a consistent hardware state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+       /* Validate all of the calling parameters. */
+       if (handle == (PMIC_AUDIO_HANDLE) NULL) {
+               rc = PMIC_PARAMETER_ERROR;
+       } else if ((handle == stDAC.handle) &&
+                  (stDAC.handleState == HANDLE_IN_USE)) {
+               if ((clockIn != CLOCK_IN_CLIA) && (clockIn != CLOCK_IN_CLIB)) {
+                       rc = PMIC_PARAMETER_ERROR;
+               } else if ((stDAC.masterSlave == BUS_MASTER_MODE)
+                          && !((clockFreq >= STDAC_CLI_3_36864MHZ)
+                               && (clockFreq <= STDAC_CLI_33_6MHZ))) {
+                       rc = PMIC_PARAMETER_ERROR;
+               } else if ((stDAC.masterSlave == BUS_SLAVE_MODE)
+                          && !((clockFreq >= STDAC_MCLK_PLL_DISABLED)
+                               && (clockFreq <= STDAC_BCLK_IN_PLL))) {
+                       rc = PMIC_PARAMETER_ERROR;
+               } else if (!((samplingRate >= STDAC_RATE_8_KHZ)
+                            && (samplingRate <= STDAC_RATE_96_KHZ))) {
+                       rc = PMIC_PARAMETER_ERROR;
+               }
+               /*
+                  else if(!((invert >= NO_INVERT) && (invert <= INVERT_FRAMESYNC)))
+                  {
+                  rc = PMIC_PARAMETER_ERROR;
+                  } */
+               else {
+                       reg_mask = SET_BITS(regST_DAC, STDCCLK, 7) |
+                           SET_BITS(regST_DAC, STDCCLKSEL, 1) |
+                           SET_BITS(regST_DAC, SR, 15) |
+                           SET_BITS(regST_DAC, STDCBCLINV, 1) |
+                           SET_BITS(regST_DAC, STDCFSINV, 1);
+                       if (clockIn == CLOCK_IN_CLIA) {
+                               reg_value = SET_BITS(regST_DAC, STDCCLKSEL, 0);
+                       } else {
+                               reg_value = SET_BITS(regST_DAC, STDCCLKSEL, 1);
+                       }
+                       /* How to take care of sample rates in SLAVE mode */
+                       if ((clockFreq == STDAC_CLI_3_36864MHZ)
+                           || ((clockFreq == STDAC_FSYNC_IN_PLL))) {
+                               reg_value |= SET_BITS(regST_DAC, STDCCLK, 6);
+                       } else if ((clockFreq == STDAC_CLI_12MHZ)
+                                  || (clockFreq == STDAC_MCLK_PLL_DISABLED)) {
+                               reg_value |= SET_BITS(regST_DAC, STDCCLK, 5);
+                       } else if (clockFreq == STDAC_CLI_13MHZ) {
+                               reg_value |= SET_BITS(regST_DAC, STDCCLK, 0);
+                       } else if (clockFreq == STDAC_CLI_15_36MHZ) {
+                               reg_value |= SET_BITS(regST_DAC, STDCCLK, 1);
+                       } else if (clockFreq == STDAC_CLI_16_8MHZ) {
+                               reg_value |= SET_BITS(regST_DAC, STDCCLK, 2);
+                       } else if (clockFreq == STDAC_CLI_26MHZ) {
+                               reg_value |= SET_BITS(regST_DAC, STDCCLK, 4);
+                       } else if ((clockFreq == STDAC_CLI_33_6MHZ)
+                                  || (clockFreq == STDAC_BCLK_IN_PLL)) {
+                               reg_value |= SET_BITS(regST_DAC, STDCCLK, 7);
+                       }
+
+                       reg_value |= SET_BITS(regST_DAC, SR, samplingRate);
+
+                       if (invert & INVERT_BITCLOCK) {
+                               reg_value |= SET_BITS(regST_DAC, STDCBCLINV, 1);
+                       }
+                       if (invert & INVERT_FRAMESYNC) {
+                               reg_value |= SET_BITS(regST_DAC, STDCFSINV, 1);
+                       }
+                       if (invert & NO_INVERT) {
+                               reg_value |= SET_BITS(regST_DAC, STDCBCLINV, 0);
+                               reg_value |= SET_BITS(regST_DAC, STDCFSINV, 0);
+                       }
+                       if (pmic_write_reg
+                           (REG_AUDIO_STEREO_DAC, reg_value,
+                            reg_mask) != PMIC_SUCCESS) {
+                               rc = PMIC_ERROR;
+                       } else {
+                               pr_debug("STDAC clock set\n");
+                               rc = PMIC_SUCCESS;
+                               stDAC.clockIn = clockIn;
+                               stDAC.clockFreq = clockFreq;
+                               stDAC.samplingRate = samplingRate;
+                               stDAC.invert = invert;
+                       }
+
+               }
+
+       } else {
+               rc = PMIC_PARAMETER_ERROR;
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * @brief Get the Stereo DAC clock source and operating characteristics.
+ *
+ * Get the current Stereo DAC clock source and operating characteristics.
+ *
+ * @param  handle          Device handle from pmic_audio_open() call.
+ * @param  clockIn         The clock signal source.
+ * @param  clockFreq       The clock signal frequency.
+ * @param  samplingRate    The audio data sampling rate.
+ * @param  invert          Inversion of the frame sync and/or
+ *                         bit clock inputs is enabled/disabled.
+ *
+ * @retval      PMIC_SUCCESS         If the Stereo DAC clock settings were
+ *                                   successfully retrieved.
+ * @retval      PMIC_PARAMETER_ERROR If the handle invalid.
+ * @retval      PMIC_ERROR           If the Stereo DAC clock configuration
+ *                                   could not be retrieved.
+ */
+PMIC_STATUS pmic_audio_stdac_get_clock(const PMIC_AUDIO_HANDLE handle,
+                                      PMIC_AUDIO_CLOCK_IN_SOURCE *
+                                      const clockIn,
+                                      PMIC_AUDIO_STDAC_SAMPLING_RATE *
+                                      const samplingRate,
+                                      PMIC_AUDIO_STDAC_CLOCK_IN_FREQ *
+                                      const clockFreq,
+                                      PMIC_AUDIO_CLOCK_INVERT * const invert)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+       /* Use a critical section to ensure a consistent hardware state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle == stDAC.handle) &&
+           (stDAC.handleState == HANDLE_IN_USE) &&
+           (clockIn != (PMIC_AUDIO_CLOCK_IN_SOURCE *) NULL) &&
+           (samplingRate != (PMIC_AUDIO_STDAC_SAMPLING_RATE *) NULL) &&
+           (clockFreq != (PMIC_AUDIO_STDAC_CLOCK_IN_FREQ *) NULL) &&
+           (invert != (PMIC_AUDIO_CLOCK_INVERT *) NULL)) {
+               *clockIn = stDAC.clockIn;
+               *samplingRate = stDAC.samplingRate;
+               *clockFreq = stDAC.clockFreq;
+               *invert = stDAC.invert;
+               rc = PMIC_SUCCESS;
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * @brief Set the Stereo DAC primary audio channel timeslot.
+ *
+ * Set the Stereo DAC primary audio channel timeslot. This function must be
+ * used if the default timeslot for the primary audio channel is to be changed.
+ *
+ * @param       handle          Device handle from pmic_audio_open() call.
+ * @param      timeslot        Select the primary audio channel timeslot.
+ *
+ * @retval      PMIC_SUCCESS         If the Stereo DAC primary audio channel
+ *                                   timeslot was successfully configured.
+ * @retval      PMIC_PARAMETER_ERROR If the handle or audio channel timeslot
+ *                                   was invalid.
+ * @retval      PMIC_ERROR           If the Stereo DAC primary audio channel
+ *                                   timeslot could not be set.
+ */
+PMIC_STATUS pmic_audio_stdac_set_rxtx_timeslot(const PMIC_AUDIO_HANDLE handle,
+                                              const PMIC_AUDIO_STDAC_TIMESLOTS
+                                              timeslot)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       unsigned int reg_mask = SET_BITS(regSSI_NETWORK, STDCRXSLOT, 3);
+
+       /* Use a critical section to ensure a consistent hardware state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE)) {
+               if ((timeslot == USE_TS0_TS1) || (timeslot == USE_TS2_TS3)
+                   || (timeslot == USE_TS4_TS5) || (timeslot == USE_TS6_TS7)) {
+                       if (pmic_write_reg
+                           (REG_AUDIO_SSI_NETWORK, timeslot,
+                            reg_mask) != PMIC_SUCCESS) {
+                               rc = PMIC_ERROR;
+                       } else {
+                               pr_debug("STDAC primary timeslot set\n");
+                               stDAC.timeslot = timeslot;
+                               rc = PMIC_SUCCESS;
+                       }
+
+               } else {
+                       rc = PMIC_PARAMETER_ERROR;
+               }
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * @brief Get the current Stereo DAC primary audio channel timeslot.
+ *
+ * Get the current Stereo DAC primary audio channel timeslot.
+ *
+ * @param   handle          Device handle from pmic_audio_open() call.
+ * @param   timeslot        The primary audio channel timeslot.
+ *
+ * @retval      PMIC_SUCCESS         If the Stereo DAC primary audio channel
+ *                                   timeslot was successfully retrieved.
+ * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval      PMIC_ERROR           If the Stereo DAC primary audio channel
+ *                                   timeslot could not be retrieved.
+ */
+PMIC_STATUS pmic_audio_stdac_get_rxtx_timeslot(const PMIC_AUDIO_HANDLE handle,
+                                              PMIC_AUDIO_STDAC_TIMESLOTS *
+                                              const timeslot)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+       /* Use a critical section to ensure a consistent hardware state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle == stDAC.handle) &&
+           (stDAC.handleState == HANDLE_IN_USE) &&
+           (timeslot != (PMIC_AUDIO_STDAC_TIMESLOTS *) NULL)) {
+               *timeslot = stDAC.timeslot;
+
+               rc = PMIC_SUCCESS;
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * @brief Set/Enable the Stereo DAC options.
+ *
+ * Set or enable various Stereo DAC options. The available options include
+ * resetting the digital filter and enabling the bus master clock outputs.
+ *
+ * @param   handle          Device handle from pmic_audio_open() call.
+ * @param   config          The Stereo DAC options to enable.
+ *
+ * @retval      PMIC_SUCCESS         If the Stereo DAC options were
+ *                                   successfully configured.
+ * @retval      PMIC_PARAMETER_ERROR If the handle or Stereo DAC options
+ *                                   were invalid.
+ * @retval      PMIC_ERROR           If the Stereo DAC options could not be
+ *                                   successfully set/enabled.
+ */
+PMIC_STATUS pmic_audio_stdac_set_config(const PMIC_AUDIO_HANDLE handle,
+                                       const PMIC_AUDIO_STDAC_CONFIG config)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       unsigned int reg_write = 0;
+       unsigned int reg_mask = 0;
+
+       /* Use a critical section to ensure a consistent hardware state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE)) {
+               if (config & STDAC_MASTER_CLOCK_OUTPUTS) {
+                       reg_write |= SET_BITS(regST_DAC, STDCCLKEN, 1);
+                       reg_mask |= SET_BITS(regST_DAC, STDCCLKEN, 1);
+               }
+
+               rc = pmic_write_reg(REG_AUDIO_STEREO_DAC, reg_write, reg_mask);
+
+               if (rc == PMIC_SUCCESS) {
+                       stDAC.config |= config;
+                       pr_debug("STDAC config set\n");
+
+               }
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * @brief Clear/Disable the Stereo DAC options.
+ *
+ * Clear or disable various Stereo DAC options.
+ *
+ * @param       handle          Device handle from pmic_audio_open() call.
+ * @param       config          The Stereo DAC options to be cleared/disabled.
+ *
+ * @retval      PMIC_SUCCESS         If the Stereo DAC options were
+ *                                   successfully cleared/disabled.
+ * @retval      PMIC_PARAMETER_ERROR If the handle or the Stereo DAC options
+ *                                   were invalid.
+ * @retval      PMIC_ERROR           If the Stereo DAC options could not be
+ *                                   cleared/disabled.
+ */
+PMIC_STATUS pmic_audio_stdac_clear_config(const PMIC_AUDIO_HANDLE handle,
+                                         const PMIC_AUDIO_STDAC_CONFIG config)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+       unsigned int reg_write = 0;
+       unsigned int reg_mask = 0;
+
+       /* Use a critical section to ensure a consistent hardware state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE)) {
+
+               if (config & STDAC_MASTER_CLOCK_OUTPUTS) {
+                       reg_mask |= SET_BITS(regST_DAC, STDCCLKEN, 1);
+               }
+
+               if (reg_mask != 0) {
+                       rc = pmic_write_reg(REG_AUDIO_STEREO_DAC,
+                                           reg_write, reg_mask);
+
+                       if (rc == PMIC_SUCCESS) {
+                               stDAC.config &= ~config;
+                       }
+               }
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * @brief Get the current Stereo DAC options.
+ *
+ * Get the current Stereo DAC options.
+ *
+ * @param      handle          Device handle from pmic_audio_open() call.
+ * @param      config          The current set of Stereo DAC options.
+ *
+ * @retval      PMIC_SUCCESS         If the Stereo DAC options were
+ *                                   successfully retrieved.
+ * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval      PMIC_ERROR           If the Stereo DAC options could not be
+ *                                   retrieved.
+ */
+PMIC_STATUS pmic_audio_stdac_get_config(const PMIC_AUDIO_HANDLE handle,
+                                       PMIC_AUDIO_STDAC_CONFIG * const config)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+       /* Use a critical section to ensure a consistent hardware state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle == stDAC.handle) &&
+           (stDAC.handleState == HANDLE_IN_USE) &&
+           (config != (PMIC_AUDIO_STDAC_CONFIG *) NULL)) {
+               *config = stDAC.config;
+
+               rc = PMIC_SUCCESS;
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*@}*/
+
+/*************************************************************************
+ * Audio input section configuration.
+ *************************************************************************
+ */
+
+/*!
+ * @name Audio Input Setup and Configuration APIs
+ * Functions for general setup and configuration of the PMIC audio
+ * input hardware.
+ */
+/*@{*/
+
+/*!
+ * @brief Set/Enable the audio input section options.
+ *
+ * Set or enable various audio input section options. The only available
+ * option right now is to enable the automatic disabling of the microphone
+ * input amplifiers when a microphone/headset is inserted or removed.
+ * NOT SUPPORTED BY MC13783
+ *
+ * @param      handle          Device handle from pmic_audio_open() call.
+ * @param      config          The audio input section options to enable.
+ *
+ * @retval      PMIC_SUCCESS         If the audio input section options were
+ *                                   successfully configured.
+ * @retval      PMIC_PARAMETER_ERROR If the handle or audio input section
+ *                                   options were invalid.
+ * @retval      PMIC_ERROR           If the audio input section options could
+ *                                   not be successfully set/enabled.
+ */
+PMIC_STATUS pmic_audio_input_set_config(const PMIC_AUDIO_HANDLE handle,
+                                       const PMIC_AUDIO_INPUT_CONFIG config)
+{
+       PMIC_STATUS rc = PMIC_NOT_SUPPORTED;
+       return rc;
+}
+
+/*!
+ * @brief Clear/Disable the audio input section options.
+ *
+ * Clear or disable various audio input section options.
+ *
+ * @param      handle          Device handle from pmic_audio_open() call.
+ * @param      config          The audio input section options to be
+ *                              cleared/disabled.
+ * NOT SUPPORTED BY MC13783
+ *
+ * @retval      PMIC_SUCCESS         If the audio input section options were
+ *                                   successfully cleared/disabled.
+ * @retval      PMIC_PARAMETER_ERROR If the handle or the audio input section
+ *                                   options were invalid.
+ * @retval      PMIC_ERROR           If the audio input section options could
+ *                                   not be cleared/disabled.
+ */
+PMIC_STATUS pmic_audio_input_clear_config(const PMIC_AUDIO_HANDLE handle,
+                                         const PMIC_AUDIO_INPUT_CONFIG config)
+{
+       PMIC_STATUS rc = PMIC_NOT_SUPPORTED;
+       return rc;
+
+}
+
+/*!
+ * @brief Get the current audio input section options.
+ *
+ * Get the current audio input section options.
+ *
+ * @param[in]   handle          Device handle from pmic_audio_open() call.
+ * @param[out]  config          The current set of audio input section options.
+ * NOT SUPPORTED BY MC13783
+ *
+ * @retval      PMIC_SUCCESS         If the audio input section options were
+ *                                   successfully retrieved.
+ * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval      PMIC_ERROR           If the audio input section options could
+ *                                   not be retrieved.
+ */
+PMIC_STATUS pmic_audio_input_get_config(const PMIC_AUDIO_HANDLE handle,
+                                       PMIC_AUDIO_INPUT_CONFIG * const config)
+{
+       PMIC_STATUS rc = PMIC_NOT_SUPPORTED;
+       return rc;
+}
+
+/*@}*/
+
+/*************************************************************************
+ * Audio recording using the Voice CODEC.
+ *************************************************************************
+ */
+
+/*!
+ * @name Audio Recording Using the Voice CODEC Setup and Configuration APIs
+ * Functions for general setup and configuration of the PMIC Voice CODEC
+ * to perform audio recording.
+ */
+/*@{*/
+
+/*!
+ * @brief Select the microphone inputs to be used for Voice CODEC recording.
+ *
+ * Select left (mc13783-only) and right microphone inputs for Voice CODEC
+ * recording. It is possible to disable or not use a particular microphone
+ * input channel by specifying NO_MIC as a parameter.
+ *
+ * @param       handle          Device handle from pmic_audio_open() call.
+ * @param       leftChannel     Select the left microphone input channel.
+ * @param       rightChannel    Select the right microphone input channel.
+ *
+ * @retval      PMIC_SUCCESS         If the microphone input channels were
+ *                                   successfully enabled.
+ * @retval      PMIC_PARAMETER_ERROR If the handle or microphone input ports
+ *                                   were invalid.
+ * @retval      PMIC_ERROR           If the microphone input channels could
+ *                                   not be successfully enabled.
+ */
+PMIC_STATUS pmic_audio_vcodec_set_mic(const PMIC_AUDIO_HANDLE handle,
+                                     const PMIC_AUDIO_INPUT_PORT leftChannel,
+                                     const PMIC_AUDIO_INPUT_PORT rightChannel)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       unsigned int reg_write = 0;
+       unsigned int reg_mask = 0;
+
+       /* Use a critical section to ensure a consistent hardware state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
+               if (!((leftChannel == NO_MIC) || (leftChannel == MIC1_LEFT))) {
+                       rc = PMIC_PARAMETER_ERROR;
+               } else if (!((rightChannel == NO_MIC)
+                            || (rightChannel == MIC1_RIGHT_MIC_MONO)
+                            || (rightChannel == TXIN_EXT)
+                            || (rightChannel == MIC2_AUX))) {
+                       rc = PMIC_PARAMETER_ERROR;
+               } else {
+                       if (leftChannel == NO_MIC) {
+                       } else {        /* Left channel MIC enable */
+                               reg_mask = SET_BITS(regAUDIO_TX, AMC1LEN, 1) |
+                                   SET_BITS(regAUDIO_TX, RXINREC, 1);
+                               reg_write = SET_BITS(regAUDIO_TX, AMC1LEN, 1) |
+                                   SET_BITS(regAUDIO_TX, RXINREC, 0);
+                       }
+                       /*For right channel enable one and clear the other two as well as RXINREC */
+                       if (rightChannel == NO_MIC) {
+                       } else if (rightChannel == MIC1_RIGHT_MIC_MONO) {
+                               reg_mask |= SET_BITS(regAUDIO_TX, AMC1REN, 1) |
+                                   SET_BITS(regAUDIO_TX, RXINREC, 1) |
+                                   SET_BITS(regAUDIO_TX, AMC2EN, 1) |
+                                   SET_BITS(regAUDIO_TX, ATXINEN, 1);
+                               reg_write |= SET_BITS(regAUDIO_TX, AMC1REN, 1) |
+                                   SET_BITS(regAUDIO_TX, RXINREC, 0) |
+                                   SET_BITS(regAUDIO_TX, AMC2EN, 0) |
+                                   SET_BITS(regAUDIO_TX, ATXINEN, 0);
+                       } else if (rightChannel == MIC2_AUX) {
+                               reg_mask |= SET_BITS(regAUDIO_TX, AMC1REN, 1) |
+                                   SET_BITS(regAUDIO_TX, RXINREC, 1) |
+                                   SET_BITS(regAUDIO_TX, AMC2EN, 1) |
+                                   SET_BITS(regAUDIO_TX, ATXINEN, 1);
+                               reg_write |= SET_BITS(regAUDIO_TX, AMC1REN, 0) |
+                                   SET_BITS(regAUDIO_TX, RXINREC, 0) |
+                                   SET_BITS(regAUDIO_TX, AMC2EN, 1) |
+                                   SET_BITS(regAUDIO_TX, ATXINEN, 0);
+                       } else {        /* TX line in */
+                               reg_mask |= SET_BITS(regAUDIO_TX, AMC1REN, 1) |
+                                   SET_BITS(regAUDIO_TX, RXINREC, 1) |
+                                   SET_BITS(regAUDIO_TX, AMC2EN, 1) |
+                                   SET_BITS(regAUDIO_TX, ATXINEN, 1);
+                               reg_write |= SET_BITS(regAUDIO_TX, AMC1REN, 0) |
+                                   SET_BITS(regAUDIO_TX, RXINREC, 0) |
+                                   SET_BITS(regAUDIO_TX, AMC2EN, 0) |
+                                   SET_BITS(regAUDIO_TX, ATXINEN, 1);
+                       }
+
+                       if (reg_mask == 0) {
+                               rc = PMIC_PARAMETER_ERROR;
+                       } else {
+                               rc = pmic_write_reg(REG_AUDIO_TX,
+                                                   reg_write, reg_mask);
+
+                               if (rc == PMIC_SUCCESS) {
+                                       pr_debug
+                                           ("MIC inputs configured successfully\n");
+                                       vCodec.leftChannelMic.mic = leftChannel;
+                                       vCodec.rightChannelMic.mic =
+                                           rightChannel;
+
+                               }
+                       }
+               }
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * @brief Get the current microphone inputs being used for Voice CODEC
+ *        recording.
+ *
+ * Get the left (mc13783-only) and right microphone inputs currently being
+ * used for Voice CODEC recording.
+ *
+ * @param      handle          Device handle from pmic_audio_open() call.
+ * @param      leftChannel     The left microphone input channel.
+ * @param      rightChannel    The right microphone input channel.
+ *
+ * @retval      PMIC_SUCCESS         If the microphone input channels were
+ *                                   successfully retrieved.
+ * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval      PMIC_ERROR           If the microphone input channels could
+ *                                   not be retrieved.
+ */
+PMIC_STATUS pmic_audio_vcodec_get_mic(const PMIC_AUDIO_HANDLE handle,
+                                     PMIC_AUDIO_INPUT_PORT * const leftChannel,
+                                     PMIC_AUDIO_INPUT_PORT *
+                                     const rightChannel)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+       /* Use a critical section to ensure a consistent hardware state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle == vCodec.handle) &&
+           (vCodec.handleState == HANDLE_IN_USE) &&
+           (leftChannel != (PMIC_AUDIO_INPUT_PORT *) NULL) &&
+           (rightChannel != (PMIC_AUDIO_INPUT_PORT *) NULL)) {
+               *leftChannel = vCodec.leftChannelMic.mic;
+               *rightChannel = vCodec.rightChannelMic.mic;
+               rc = PMIC_SUCCESS;
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+       return rc;
+}
+
+/*!
+ * @brief Enable/disable the microphone input.
+ *
+ * This function enables/disables the current microphone input channel. The
+ * input amplifier is automatically turned off when the microphone input is
+ * disabled.
+ *
+ * @param       handle          Device handle from pmic_audio_open() call.
+ * @param       leftChannel     The left microphone input channel state.
+ * @param       rightChannel    the right microphone input channel state.
+ *
+ * @retval      PMIC_SUCCESS         If the microphone input channels were
+ *                                   successfully reconfigured.
+ * @retval      PMIC_PARAMETER_ERROR If the handle or microphone input states
+ *                                   were invalid.
+ * @retval      PMIC_ERROR           If the microphone input channels could
+ *                                   not be reconfigured.
+ */
+PMIC_STATUS pmic_audio_vcodec_set_mic_on_off(const PMIC_AUDIO_HANDLE handle,
+                                            const PMIC_AUDIO_INPUT_MIC_STATE
+                                            leftChannel,
+                                            const PMIC_AUDIO_INPUT_MIC_STATE
+                                            rightChannel)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       unsigned int reg_write = 0;
+       unsigned int reg_mask = 0;
+       unsigned int curr_left = 0;
+       unsigned int curr_right = 0;
+
+       /* Use a critical section to ensure a consistent hardware state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
+               curr_left = vCodec.leftChannelMic.mic;
+               curr_right = vCodec.rightChannelMic.mic;
+               if ((curr_left == NO_MIC) && (curr_right == NO_MIC)) {
+                       rc = PMIC_PARAMETER_ERROR;
+               } else {
+                       if (curr_left == MIC1_LEFT) {
+                               if ((leftChannel == MICROPHONE_ON) &&
+                                   (vCodec.leftChannelMic.micOnOff ==
+                                    MICROPHONE_OFF)) {
+                                       /* Enable the microphone */
+                                       reg_mask |=
+                                           SET_BITS(regAUDIO_TX, AMC1LEN,
+                                                    1) | SET_BITS(regAUDIO_TX,
+                                                                  RXINREC, 1);
+                                       reg_write |=
+                                           SET_BITS(regAUDIO_TX, AMC1LEN,
+                                                    1) | SET_BITS(regAUDIO_TX,
+                                                                  RXINREC, 0);
+
+                               } else if ((leftChannel == MICROPHONE_OFF) &&
+                                          (vCodec.leftChannelMic.micOnOff ==
+                                           MICROPHONE_ON)) {
+                                       /* Disable the microphone */
+                                       reg_mask |=
+                                           SET_BITS(regAUDIO_TX, AMC1LEN,
+                                                    1) | SET_BITS(regAUDIO_TX,
+                                                                  RXINREC, 1);
+                                       reg_write |=
+                                           SET_BITS(regAUDIO_TX, AMC1LEN,
+                                                    0) | SET_BITS(regAUDIO_TX,
+                                                                  RXINREC, 0);
+
+                               } else {
+                                       /* Both are in same state . Nothing to be done */
+                               }
+
+                       }
+                       if (curr_right == MIC1_RIGHT_MIC_MONO) {
+                               if ((rightChannel == MICROPHONE_ON) &&
+                                   (vCodec.leftChannelMic.micOnOff ==
+                                    MICROPHONE_OFF)) {
+                                       /* Enable the microphone */
+                                       reg_mask |=
+                                           SET_BITS(regAUDIO_TX, AMC1REN,
+                                                    1) | SET_BITS(regAUDIO_TX,
+                                                                  RXINREC,
+                                                                  1) |
+                                           SET_BITS(regAUDIO_TX, AMC2EN,
+                                                    1) | SET_BITS(regAUDIO_TX,
+                                                                  ATXINEN, 1);
+                                       reg_write |=
+                                           SET_BITS(regAUDIO_TX, AMC1REN,
+                                                    1) | SET_BITS(regAUDIO_TX,
+                                                                  RXINREC,
+                                                                  0) |
+                                           SET_BITS(regAUDIO_TX, AMC2EN,
+                                                    0) | SET_BITS(regAUDIO_TX,
+                                                                  ATXINEN, 0);
+                               } else if ((rightChannel == MICROPHONE_OFF)
+                                          && (vCodec.leftChannelMic.micOnOff ==
+                                              MICROPHONE_ON)) {
+                                       /* Disable the microphone */
+                                       reg_mask |=
+                                           SET_BITS(regAUDIO_TX, AMC1REN,
+                                                    1) | SET_BITS(regAUDIO_TX,
+                                                                  RXINREC,
+                                                                  1) |
+                                           SET_BITS(regAUDIO_TX, AMC2EN,
+                                                    1) | SET_BITS(regAUDIO_TX,
+                                                                  ATXINEN, 1);
+                                       reg_write |=
+                                           SET_BITS(regAUDIO_TX, AMC1REN,
+                                                    0) | SET_BITS(regAUDIO_TX,
+                                                                  RXINREC,
+                                                                  0) |
+                                           SET_BITS(regAUDIO_TX, AMC2EN,
+                                                    0) | SET_BITS(regAUDIO_TX,
+                                                                  ATXINEN, 0);
+                               } else {
+                                       /* Both are in same state . Nothing to be done */
+                               }
+                       } else if (curr_right == MIC2_AUX) {
+                               if ((rightChannel == MICROPHONE_ON)
+                                   && (vCodec.leftChannelMic.micOnOff ==
+                                       MICROPHONE_OFF)) {
+                                       /* Enable the microphone */
+                                       reg_mask |=
+                                           SET_BITS(regAUDIO_TX, AMC1REN,
+                                                    1) | SET_BITS(regAUDIO_TX,
+                                                                  RXINREC,
+                                                                  1) |
+                                           SET_BITS(regAUDIO_TX, AMC2EN,
+                                                    1) | SET_BITS(regAUDIO_TX,
+                                                                  ATXINEN, 1);
+                                       reg_write |=
+                                           SET_BITS(regAUDIO_TX, AMC1REN,
+                                                    0) | SET_BITS(regAUDIO_TX,
+                                                                  RXINREC,
+                                                                  0) |
+                                           SET_BITS(regAUDIO_TX, AMC2EN,
+                                                    1) | SET_BITS(regAUDIO_TX,
+                                                                  ATXINEN, 0);
+                               } else if ((rightChannel == MICROPHONE_OFF)
+                                          && (vCodec.leftChannelMic.micOnOff ==
+                                              MICROPHONE_ON)) {
+                                       /* Disable the microphone */
+                                       reg_mask |=
+                                           SET_BITS(regAUDIO_TX, AMC1REN,
+                                                    1) | SET_BITS(regAUDIO_TX,
+                                                                  RXINREC,
+                                                                  1) |
+                                           SET_BITS(regAUDIO_TX, AMC2EN,
+                                                    1) | SET_BITS(regAUDIO_TX,
+                                                                  ATXINEN, 1);
+                                       reg_write |=
+                                           SET_BITS(regAUDIO_TX, AMC1REN,
+                                                    0) | SET_BITS(regAUDIO_TX,
+                                                                  RXINREC,
+                                                                  0) |
+                                           SET_BITS(regAUDIO_TX, AMC2EN,
+                                                    0) | SET_BITS(regAUDIO_TX,
+                                                                  ATXINEN, 0);
+                               } else {
+                                       /* Both are in same state . Nothing to be done */
+                               }
+                       } else if (curr_right == TXIN_EXT) {
+                               if ((rightChannel == MICROPHONE_ON)
+                                   && (vCodec.leftChannelMic.micOnOff ==
+                                       MICROPHONE_OFF)) {
+                                       /* Enable the microphone */
+                                       reg_mask |=
+                                           SET_BITS(regAUDIO_TX, AMC1REN,
+                                                    1) | SET_BITS(regAUDIO_TX,
+                                                                  RXINREC,
+                                                                  1) |
+                                           SET_BITS(regAUDIO_TX, AMC2EN,
+                                                    1) | SET_BITS(regAUDIO_TX,
+                                                                  ATXINEN, 1);
+                                       reg_write |=
+                                           SET_BITS(regAUDIO_TX, AMC1REN,
+                                                    0) | SET_BITS(regAUDIO_TX,
+                                                                  RXINREC,
+                                                                  0) |
+                                           SET_BITS(regAUDIO_TX, AMC2EN,
+                                                    0) | SET_BITS(regAUDIO_TX,
+                                                                  ATXINEN, 1);
+                               } else if ((rightChannel == MICROPHONE_OFF)
+                                          && (vCodec.leftChannelMic.micOnOff ==
+                                              MICROPHONE_ON)) {
+                                       /* Disable the microphone */
+                                       reg_mask |=
+                                           SET_BITS(regAUDIO_TX, AMC1REN,
+                                                    1) | SET_BITS(regAUDIO_TX,
+                                                                  RXINREC,
+                                                                  1) |
+                                           SET_BITS(regAUDIO_TX, AMC2EN,
+                                                    1) | SET_BITS(regAUDIO_TX,
+                                                                  ATXINEN, 1);
+                                       reg_write |=
+                                           SET_BITS(regAUDIO_TX, AMC1REN,
+                                                    0) | SET_BITS(regAUDIO_TX,
+                                                                  RXINREC,
+                                                                  0) |
+                                           SET_BITS(regAUDIO_TX, AMC2EN,
+                                                    0) | SET_BITS(regAUDIO_TX,
+                                                                  ATXINEN, 0);
+                               } else {
+                                       /* Both are in same state . Nothing to be done */
+                               }
+                       }
+                       if (reg_mask == 0) {
+                       } else {
+                               rc = pmic_write_reg(REG_AUDIO_TX,
+                                                   reg_write, reg_mask);
+
+                               if (rc == PMIC_SUCCESS) {
+                                       pr_debug
+                                           ("MIC states configured successfully\n");
+                                       vCodec.leftChannelMic.micOnOff =
+                                           leftChannel;
+                                       vCodec.rightChannelMic.micOnOff =
+                                           rightChannel;
+                               }
+                       }
+               }
+
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * @brief Return the current state of the microphone inputs.
+ *
+ * This function returns the current state (on/off) of the microphone
+ * input channels.
+ *
+ * @param       handle          Device handle from pmic_audio_open() call.
+ * @param      leftChannel     The current left microphone input channel
+ *                              state.
+ * @param      rightChannel    the current right microphone input channel
+ *                              state.
+ *
+ * @retval      PMIC_SUCCESS         If the microphone input channel states
+ *                                   were successfully retrieved.
+ * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval      PMIC_ERROR           If the microphone input channel states
+ *                                   could not be retrieved.
+ */
+PMIC_STATUS pmic_audio_vcodec_get_mic_on_off(const PMIC_AUDIO_HANDLE handle,
+                                            PMIC_AUDIO_INPUT_MIC_STATE *
+                                            const leftChannel,
+                                            PMIC_AUDIO_INPUT_MIC_STATE *
+                                            const rightChannel)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+       /* Use a critical section to ensure a consistent hardware state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle == vCodec.handle) &&
+           (vCodec.handleState == HANDLE_IN_USE) &&
+           (leftChannel != (PMIC_AUDIO_INPUT_MIC_STATE *) NULL) &&
+           (rightChannel != (PMIC_AUDIO_INPUT_MIC_STATE *) NULL)) {
+               *leftChannel = vCodec.leftChannelMic.micOnOff;
+               *rightChannel = vCodec.rightChannelMic.micOnOff;
+
+               rc = PMIC_SUCCESS;
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * @brief Set the microphone input amplifier mode and gain level.
+ *
+ * This function sets the current microphone input amplifier operating mode
+ * and gain level.
+ *
+ * @param       handle           Device handle from pmic_audio_open() call.
+ * @param       leftChannelMode  The left microphone input amplifier mode.
+ * @param       leftChannelGain  The left microphone input amplifier gain level.
+ * @param       rightChannelMode The right microphone input amplifier mode.
+ * @param       rightChannelGain The right microphone input amplifier gain
+ *                               level.
+ *
+ * @retval      PMIC_SUCCESS         If the microphone input amplifiers were
+ *                                   successfully reconfigured.
+ * @retval      PMIC_PARAMETER_ERROR If the handle or microphone input amplifier
+ *                                   modes or gain levels were invalid.
+ * @retval      PMIC_ERROR           If the microphone input amplifiers could
+ *                                   not be reconfigured.
+ */
+PMIC_STATUS pmic_audio_vcodec_set_record_gain(const PMIC_AUDIO_HANDLE handle,
+                                             const PMIC_AUDIO_MIC_AMP_MODE
+                                             leftChannelMode,
+                                             const PMIC_AUDIO_MIC_GAIN
+                                             leftChannelGain,
+                                             const PMIC_AUDIO_MIC_AMP_MODE
+                                             rightChannelMode,
+                                             const PMIC_AUDIO_MIC_GAIN
+                                             rightChannelGain)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       unsigned int reg_write = 0;
+       unsigned int reg_mask = 0;
+
+       /* Use a critical section to ensure a consistent hardware state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
+               if (!(((leftChannelGain >= MIC_GAIN_MINUS_8DB)
+                      && (leftChannelGain <= MIC_GAIN_PLUS_23DB))
+                     && ((rightChannelGain >= MIC_GAIN_MINUS_8DB)
+                         && (rightChannelGain <= MIC_GAIN_PLUS_23DB)))) {
+                       rc = PMIC_PARAMETER_ERROR;
+                       pr_debug("VCODEC set record gain - wrong gain value\n");
+               } else if (((leftChannelMode != AMP_OFF)
+                           && (leftChannelMode != VOLTAGE_TO_VOLTAGE)
+                           && (leftChannelMode != CURRENT_TO_VOLTAGE))
+                          || ((rightChannelMode != VOLTAGE_TO_VOLTAGE)
+                              && (rightChannelMode != CURRENT_TO_VOLTAGE)
+                              && (rightChannelMode != AMP_OFF))) {
+                       rc = PMIC_PARAMETER_ERROR;
+                       pr_debug("VCODEC set record gain - wrong amp mode\n");
+               } else {
+                       if (vCodec.leftChannelMic.mic == MIC1_LEFT) {
+                               reg_mask = SET_BITS(regAUDIO_TX, AMC1LITOV, 1) |
+                                   SET_BITS(regAUDIO_TX, PGATXL, 31);
+                               if (leftChannelMode == VOLTAGE_TO_VOLTAGE) {
+                                       reg_write =
+                                           SET_BITS(regAUDIO_TX, AMC1LITOV, 0);
+                               } else {
+                                       reg_write =
+                                           SET_BITS(regAUDIO_TX, AMC1LITOV, 1);
+                               }
+                               reg_write |=
+                                   SET_BITS(regAUDIO_TX, PGATXL,
+                                            leftChannelGain);
+                       }
+                       if (vCodec.rightChannelMic.mic == MIC1_RIGHT_MIC_MONO) {
+                               reg_mask |=
+                                   SET_BITS(regAUDIO_TX, AMC1RITOV,
+                                            1) | SET_BITS(regAUDIO_TX, PGATXR,
+                                                          31);
+                               if (rightChannelMode == VOLTAGE_TO_VOLTAGE) {
+                                       reg_write |=
+                                           SET_BITS(regAUDIO_TX, AMC1RITOV, 0);
+                               } else {
+                                       reg_write |=
+                                           SET_BITS(regAUDIO_TX, AMC1RITOV, 1);
+                               }
+                               reg_write |=
+                                   SET_BITS(regAUDIO_TX, PGATXR,
+                                            rightChannelGain);
+                       } else if (vCodec.rightChannelMic.mic == MIC2_AUX) {
+                               reg_mask |= SET_BITS(regAUDIO_TX, AMC2ITOV, 1);
+                               reg_mask |= SET_BITS(regAUDIO_TX, PGATXR, 31);
+                               if (rightChannelMode == VOLTAGE_TO_VOLTAGE) {
+                                       reg_write |=
+                                           SET_BITS(regAUDIO_TX, AMC2ITOV, 0);
+                               } else {
+                                       reg_write |=
+                                           SET_BITS(regAUDIO_TX, AMC2ITOV, 1);
+                               }
+                               reg_write |=
+                                   SET_BITS(regAUDIO_TX, PGATXR,
+                                            rightChannelGain);
+                       } else if (vCodec.rightChannelMic.mic == TXIN_EXT) {
+                               reg_mask |= SET_BITS(regAUDIO_TX, PGATXR, 31);
+                               /* No current to voltage option for TX IN amplifier */
+                               reg_write |=
+                                   SET_BITS(regAUDIO_TX, PGATXR,
+                                            rightChannelGain);
+                       }
+
+                       if (reg_mask == 0) {
+                       } else {
+                               rc = pmic_write_reg(REG_AUDIO_TX,
+                                                   reg_write, reg_mask);
+                               reg_write =
+                                   SET_BITS(regAUDIO_TX, PGATXL,
+                                            leftChannelGain);
+                               reg_mask = SET_BITS(regAUDIO_TX, PGATXL, 31);
+                               rc = pmic_write_reg(REG_AUDIO_TX,
+                                                   reg_write, reg_mask);
+
+                               if (rc == PMIC_SUCCESS) {
+                                       pr_debug("MIC amp mode and gain set\n");
+                                       vCodec.leftChannelMic.ampMode =
+                                           leftChannelMode;
+                                       vCodec.leftChannelMic.gain =
+                                           leftChannelGain;
+                                       vCodec.rightChannelMic.ampMode =
+                                           rightChannelMode;
+                                       vCodec.rightChannelMic.gain =
+                                           rightChannelGain;
+
+                               }
+                       }
+               }
+       }
+
+       /* Exit critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * @brief Get the current microphone input amplifier mode and gain level.
+ *
+ * This function gets the current microphone input amplifier operating mode
+ * and gain level.
+ *
+ * @param      handle           Device handle from pmic_audio_open() call.
+ * @param      leftChannelMode  The left microphone input amplifier mode.
+ * @param      leftChannelGain  The left microphone input amplifier gain level.
+ * @param      rightChannelMode The right microphone input amplifier mode.
+ * @param       rightChannelGain The right microphone input amplifier gain
+ *                               level.
+ *
+ * @retval      PMIC_SUCCESS         If the microphone input amplifier modes
+ *                                   and gain levels were successfully
+ *                                   retrieved.
+ * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval      PMIC_ERROR           If the microphone input amplifier modes
+ *                                   and gain levels could not be retrieved.
+ */
+PMIC_STATUS pmic_audio_vcodec_get_record_gain(const PMIC_AUDIO_HANDLE handle,
+                                             PMIC_AUDIO_MIC_AMP_MODE *
+                                             const leftChannelMode,
+                                             PMIC_AUDIO_MIC_GAIN *
+                                             const leftChannelGain,
+                                             PMIC_AUDIO_MIC_AMP_MODE *
+                                             const rightChannelMode,
+                                             PMIC_AUDIO_MIC_GAIN *
+                                             const rightChannelGain)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+       /* Use a critical section to ensure a consistent hardware state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle == vCodec.handle) &&
+           (vCodec.handleState == HANDLE_IN_USE) &&
+           (leftChannelMode != (PMIC_AUDIO_MIC_AMP_MODE *) NULL) &&
+           (leftChannelGain != (PMIC_AUDIO_MIC_GAIN *) NULL) &&
+           (rightChannelMode != (PMIC_AUDIO_MIC_AMP_MODE *) NULL) &&
+           (rightChannelGain != (PMIC_AUDIO_MIC_GAIN *) NULL)) {
+               *leftChannelMode = vCodec.leftChannelMic.ampMode;
+               *leftChannelGain = vCodec.leftChannelMic.gain;
+               *rightChannelMode = vCodec.rightChannelMic.ampMode;
+               *rightChannelGain = vCodec.rightChannelMic.gain;
+
+               rc = PMIC_SUCCESS;
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * @brief Enable a microphone bias circuit.
+ *
+ * This function enables one of the available microphone bias circuits.
+ *
+ * @param       handle           Device handle from pmic_audio_open() call.
+ * @param       biasCircuit      The microphone bias circuit to be enabled.
+ *
+ * @retval      PMIC_SUCCESS         If the microphone bias circuit was
+ *                                   successfully enabled.
+ * @retval      PMIC_PARAMETER_ERROR If the handle or selected microphone bias
+ *                                   circuit was invalid.
+ * @retval      PMIC_ERROR           If the microphone bias circuit could not
+ *                                   be enabled.
+ */
+PMIC_STATUS pmic_audio_vcodec_enable_micbias(const PMIC_AUDIO_HANDLE handle,
+                                            const PMIC_AUDIO_MIC_BIAS
+                                            biasCircuit)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       unsigned int reg_write = 0;
+       unsigned int reg_mask = 0;
+
+       /* No critical section required here since we are not updating any
+        * global data.
+        */
+
+       if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
+               if (biasCircuit & MIC_BIAS1) {
+                       reg_write = SET_BITS(regAUDIO_TX, MC1BEN, 1);
+                       reg_mask = SET_BITS(regAUDIO_TX, MC1BEN, 1);
+               }
+               if (biasCircuit & MIC_BIAS2) {
+                       reg_write |= SET_BITS(regAUDIO_TX, MC2BEN, 1);
+                       reg_mask |= SET_BITS(regAUDIO_TX, MC2BEN, 1);
+               }
+               if (reg_mask != 0) {
+                       rc = pmic_write_reg(REG_AUDIO_TX, reg_write, reg_mask);
+               }
+       }
+
+       return rc;
+}
+
+/*!
+ * @brief Disable a microphone bias circuit.
+ *
+ * This function disables one of the available microphone bias circuits.
+ *
+ * @param      handle           Device handle from pmic_audio_open() call.
+ * @param      biasCircuit      The microphone bias circuit to be disabled.
+ *
+ * @retval      PMIC_SUCCESS         If the microphone bias circuit was
+ *                                   successfully disabled.
+ * @retval      PMIC_PARAMETER_ERROR If the handle or selected microphone bias
+ *                                   circuit was invalid.
+ * @retval      PMIC_ERROR           If the microphone bias circuit could not
+ *                                   be disabled.
+ */
+PMIC_STATUS pmic_audio_vcodec_disable_micbias(const PMIC_AUDIO_HANDLE handle,
+                                             const PMIC_AUDIO_MIC_BIAS
+                                             biasCircuit)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       unsigned int reg_write = 0;
+       unsigned int reg_mask = 0;
+
+       /* No critical section required here since we are not updating any
+        * global data.
+        */
+
+       if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
+               if (biasCircuit & MIC_BIAS1) {
+                       reg_mask = SET_BITS(regAUDIO_TX, MC1BEN, 1);
+               }
+
+               if (biasCircuit & MIC_BIAS2) {
+                       reg_mask |= SET_BITS(regAUDIO_TX, MC2BEN, 1);
+               }
+
+               if (reg_mask != 0) {
+                       rc = pmic_write_reg(REG_AUDIO_TX, reg_write, reg_mask);
+               }
+       }
+
+       return rc;
+}
+
+/*@}*/
+
+/*************************************************************************
+ * Audio Playback Using the Voice CODEC.
+ *************************************************************************
+ */
+
+/*!
+ * @name Audio Playback Using the Voice CODEC Setup and Configuration APIs
+ * Functions for general setup and configuration of the PMIC Voice CODEC
+ * to perform audio playback.
+ */
+/*@{*/
+
+/*!
+ * @brief Configure and enable the Voice CODEC mixer.
+ *
+ * This function configures and enables the Voice CODEC mixer.
+ *
+ * @param       handle              Device handle from pmic_audio_open() call.
+ * @param       rxSecondaryTimeslot The timeslot used for the secondary audio
+ *                                  channel.
+ * @param       gainIn              The secondary audio channel gain level.
+ * @param       gainOut             The mixer output gain level.
+ *
+ * @retval      PMIC_SUCCESS         If the Voice CODEC mixer was successfully
+ *                                   configured and enabled.
+ * @retval      PMIC_PARAMETER_ERROR If the handle or mixer configuration
+ *                                   was invalid.
+ * @retval      PMIC_ERROR           If the Voice CODEC mixer could not be
+ *                                   reconfigured or enabled.
+ */
+PMIC_STATUS pmic_audio_vcodec_enable_mixer(const PMIC_AUDIO_HANDLE handle,
+                                          const PMIC_AUDIO_VCODEC_TIMESLOT
+                                          rxSecondaryTimeslot,
+                                          const PMIC_AUDIO_VCODEC_MIX_IN_GAIN
+                                          gainIn,
+                                          const PMIC_AUDIO_VCODEC_MIX_OUT_GAIN
+                                          gainOut)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       unsigned int reg_write = 0;
+       unsigned int reg_mask = 0;
+
+       /* No critical section required here since we are not updating any
+        * global data.
+        */
+
+       if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
+               if (!((rxSecondaryTimeslot >= USE_TS0)
+                     && (rxSecondaryTimeslot <= USE_TS3))) {
+                       pr_debug
+                           ("VCODEC enable mixer - wrong sec rx timeslot\n");
+               } else if (!((gainIn >= VCODEC_NO_MIX)
+                            && (gainIn <= VCODEC_MIX_IN_MINUS_12DB))) {
+                       pr_debug("VCODEC enable mixer - wrong mix in gain\n");
+
+               } else if (!((gainOut >= VCODEC_MIX_OUT_0DB)
+                            && (gainOut <= VCODEC_MIX_OUT_MINUS_6DB))) {
+                       pr_debug("VCODEC enable mixer - wrong mix out gain\n");
+               } else {
+
+                       reg_mask = SET_BITS(regSSI_NETWORK, CDCRXSECSLOT, 3) |
+                           SET_BITS(regSSI_NETWORK, CDCRXSECGAIN, 3) |
+                           SET_BITS(regSSI_NETWORK, CDCSUMGAIN, 1);
+                       reg_write =
+                           SET_BITS(regSSI_NETWORK, CDCRXSECSLOT,
+                                    rxSecondaryTimeslot) |
+                           SET_BITS(regSSI_NETWORK, CDCRXSECGAIN,
+                                    gainIn) | SET_BITS(regSSI_NETWORK,
+                                                       CDCSUMGAIN, gainOut);
+                       rc = pmic_write_reg(REG_AUDIO_SSI_NETWORK,
+                                           reg_write, reg_mask);
+                       if (rc == PMIC_SUCCESS) {
+                               pr_debug("Vcodec mixer enabled\n");
+                       }
+               }
+       }
+
+       return rc;
+}
+
+/*!
+ * @brief Disable the Voice CODEC mixer.
+ *
+ * This function disables the Voice CODEC mixer.
+ *
+ * @param       handle              Device handle from pmic_audio_open() call.
+ *
+ * @retval      PMIC_SUCCESS         If the Voice CODEC mixer was successfully
+ *                                   disabled.
+ * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval      PMIC_ERROR           If the Voice CODEC mixer could not be
+ *                                   disabled.
+ */
+PMIC_STATUS pmic_audio_vcodec_disable_mixer(const PMIC_AUDIO_HANDLE handle)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       unsigned int reg_mask;
+
+       if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
+               reg_mask = SET_BITS(regSSI_NETWORK, CDCRXSECGAIN, 1);
+               rc = pmic_write_reg(REG_AUDIO_SSI_NETWORK,
+                                   VCODEC_NO_MIX, reg_mask);
+
+               if (rc == PMIC_SUCCESS) {
+                       pr_debug("Vcodec mixer disabled\n");
+               }
+
+       }
+
+       return rc;
+}
+
+/*@}*/
+
+/*************************************************************************
+ * Audio Playback Using the Stereo DAC.
+ *************************************************************************
+ */
+
+/*!
+ * @name Audio Playback Using the Stereo DAC Setup and Configuration APIs
+ * Functions for general setup and configuration of the PMIC Stereo DAC
+ * to perform audio playback.
+ */
+/*@{*/
+
+/*!
+ * @brief Configure and enable the Stereo DAC mixer.
+ *
+ * This function configures and enables the Stereo DAC mixer.
+ *
+ * @param      handle              Device handle from pmic_audio_open() call.
+ * @param      rxSecondaryTimeslot The timeslot used for the secondary audio
+ *                                  channel.
+ * @param      gainIn              The secondary audio channel gain level.
+ * @param      gainOut             The mixer output gain level.
+ *
+ * @retval      PMIC_SUCCESS         If the Stereo DAC mixer was successfully
+ *                                   configured and enabled.
+ * @retval      PMIC_PARAMETER_ERROR If the handle or mixer configuration
+ *                                   was invalid.
+ * @retval      PMIC_ERROR           If the Stereo DAC mixer could not be
+ *                                   reconfigured or enabled.
+ */
+PMIC_STATUS pmic_audio_stdac_enable_mixer(const PMIC_AUDIO_HANDLE handle,
+                                         const PMIC_AUDIO_STDAC_TIMESLOTS
+                                         rxSecondaryTimeslot,
+                                         const PMIC_AUDIO_STDAC_MIX_IN_GAIN
+                                         gainIn,
+                                         const PMIC_AUDIO_STDAC_MIX_OUT_GAIN
+                                         gainOut)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       unsigned int reg_write = 0;
+       unsigned int reg_mask = 0;
+
+       /* No critical section required here since we are not updating any
+        * global data.
+        */
+
+       if ((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE)) {
+               if (!((rxSecondaryTimeslot >= USE_TS0_TS1)
+                     && (rxSecondaryTimeslot <= USE_TS6_TS7))) {
+                       rc = PMIC_PARAMETER_ERROR;
+                       pr_debug("STDAC enable mixer - wrong sec timeslot\n");
+               } else if (!((gainIn >= STDAC_NO_MIX)
+                            && (gainIn <= STDAC_MIX_IN_MINUS_12DB))) {
+                       rc = PMIC_PARAMETER_ERROR;
+                       pr_debug("STDAC enable mixer - wrong mix in gain\n");
+               } else if (!((gainOut >= STDAC_MIX_OUT_0DB)
+                            && (gainOut <= STDAC_MIX_OUT_MINUS_6DB))) {
+                       rc = PMIC_PARAMETER_ERROR;
+                       pr_debug("STDAC enable mixer - wrong mix out gain\n");
+               } else {
+
+                       reg_mask = SET_BITS(regSSI_NETWORK, STDCRXSECSLOT, 3) |
+                           SET_BITS(regSSI_NETWORK, STDCRXSECGAIN, 3) |
+                           SET_BITS(regSSI_NETWORK, STDCSUMGAIN, 1);
+                       reg_write =
+                           SET_BITS(regSSI_NETWORK, STDCRXSECSLOT,
+                                    rxSecondaryTimeslot) |
+                           SET_BITS(regSSI_NETWORK, STDCRXSECGAIN,
+                                    gainIn) | SET_BITS(regSSI_NETWORK,
+                                                       STDCSUMGAIN, gainOut);
+                       rc = pmic_write_reg(REG_AUDIO_SSI_NETWORK,
+                                           reg_write, reg_mask);
+                       if (rc == PMIC_SUCCESS) {
+                               pr_debug("STDAC mixer enabled\n");
+                       }
+               }
+
+       }
+
+       return rc;
+}
+
+/*!
+ * @brief Disable the Stereo DAC mixer.
+ *
+ * This function disables the Stereo DAC mixer.
+ *
+ * @param       handle              Device handle from pmic_audio_open() call.
+ *
+ * @retval      PMIC_SUCCESS         If the Stereo DAC mixer was successfully
+ *                                   disabled.
+ * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval      PMIC_ERROR           If the Stereo DAC mixer could not be
+ *                                   disabled.
+ */
+PMIC_STATUS pmic_audio_stdac_disable_mixer(const PMIC_AUDIO_HANDLE handle)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       const unsigned int reg_write = 0;
+       const unsigned int reg_mask =
+           SET_BITS(regSSI_NETWORK, STDCRXSECGAIN, 1);
+
+       /* No critical section required here since we are not updating any
+        * global data.
+        */
+
+       if ((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE)) {
+               rc = pmic_write_reg(REG_AUDIO_SSI_NETWORK, reg_write, reg_mask);
+       }
+
+       return rc;
+}
+
+/*@}*/
+
+/*************************************************************************
+ * Audio Output Control
+ *************************************************************************
+ */
+
+/*!
+ * @name Audio Output Section Setup and Configuration APIs
+ * Functions for general setup and configuration of the PMIC audio output
+ * section to support playback.
+ */
+/*@{*/
+
+/*!
+ * @brief Select the audio output ports.
+ *
+ * This function selects the audio output ports to be used. This also enables
+ * the appropriate output amplifiers.
+ *
+ * @param   handle              Device handle from pmic_audio_open() call.
+ * @param   port                The audio output ports to be used.
+ *
+ * @retval      PMIC_SUCCESS         If the audio output ports were successfully
+ *                                   acquired.
+ * @retval      PMIC_PARAMETER_ERROR If the handle or output ports were
+ *                                   invalid.
+ * @retval      PMIC_ERROR           If the audio output ports could not be
+ *                                   acquired.
+ */
+PMIC_STATUS pmic_audio_output_set_port(const PMIC_AUDIO_HANDLE handle,
+                                      const PMIC_AUDIO_OUTPUT_PORT port)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       unsigned int reg_write = 0;
+       unsigned int reg_mask = 0;
+
+       /* Use a critical section to ensure a consistent hardware state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((port == MONO_ALERT) || (port == MONO_EXTOUT)) {
+               rc = PMIC_NOT_SUPPORTED;
+       } else {
+               if (((handle == stDAC.handle)
+                    && (stDAC.handleState == HANDLE_IN_USE))
+                   || ((handle == extStereoIn.handle)
+                       && (extStereoIn.handleState == HANDLE_IN_USE))
+                   || ((handle == vCodec.handle)
+                       && (vCodec.handleState == HANDLE_IN_USE)
+                       && (audioOutput.vCodecOut == VCODEC_MIXER_OUT))) {
+                       /* Stereo signal and MIXER source needs to be routed to the port
+                          / Avoid Codec direct out */
+
+                       if (port & MONO_SPEAKER) {
+                               reg_mask = SET_BITS(regAUDIO_RX_0, ASPEN, 1) |
+                                   SET_BITS(regAUDIO_RX_0, ASPSEL, 1);
+                               reg_write = SET_BITS(regAUDIO_RX_0, ASPEN, 1) |
+                                   SET_BITS(regAUDIO_RX_0, ASPSEL, 1);
+                       }
+                       if (port & MONO_LOUDSPEAKER) {
+                               reg_mask |= SET_BITS(regAUDIO_RX_0, ALSPEN, 1) |
+                                   SET_BITS(regAUDIO_RX_0, ALSPREF, 1) |
+                                   SET_BITS(regAUDIO_RX_0, ALSPSEL, 1);
+                               reg_write |=
+                                   SET_BITS(regAUDIO_RX_0, ALSPEN,
+                                            1) | SET_BITS(regAUDIO_RX_0,
+                                                          ALSPREF,
+                                                          1) |
+                                   SET_BITS(regAUDIO_RX_0, ALSPSEL, 1);
+                       }
+                       if (port & STEREO_HEADSET_LEFT) {
+                               reg_mask |= SET_BITS(regAUDIO_RX_0, AHSLEN, 1) |
+                                   SET_BITS(regAUDIO_RX_0, AHSSEL, 1);
+                               reg_write |=
+                                   SET_BITS(regAUDIO_RX_0, AHSLEN,
+                                            1) | SET_BITS(regAUDIO_RX_0,
+                                                          AHSSEL, 1);
+                       }
+                       if (port & STEREO_HEADSET_RIGHT) {
+                               reg_mask |= SET_BITS(regAUDIO_RX_0, AHSREN, 1) |
+                                   SET_BITS(regAUDIO_RX_0, AHSSEL, 1);
+                               reg_write |=
+                                   SET_BITS(regAUDIO_RX_0, AHSREN,
+                                            1) | SET_BITS(regAUDIO_RX_0,
+                                                          AHSSEL, 1);
+                       }
+                       if (port & STEREO_OUT_LEFT) {
+                               reg_mask |=
+                                   SET_BITS(regAUDIO_RX_0, ARXOUTLEN,
+                                            1) | SET_BITS(regAUDIO_RX_0,
+                                                          ARXOUTSEL, 1);
+                               reg_write |=
+                                   SET_BITS(regAUDIO_RX_0, ARXOUTLEN,
+                                            1) | SET_BITS(regAUDIO_RX_0,
+                                                          ARXOUTSEL, 1);
+                       }
+                       if (port & STEREO_OUT_RIGHT) {
+                               reg_mask |=
+                                   SET_BITS(regAUDIO_RX_0, ARXOUTREN,
+                                            1) | SET_BITS(regAUDIO_RX_0,
+                                                          ARXOUTSEL, 1);
+                               reg_write |=
+                                   SET_BITS(regAUDIO_RX_0, ARXOUTREN,
+                                            1) | SET_BITS(regAUDIO_RX_0,
+                                                          ARXOUTSEL, 1);
+                       }
+                       if (port & STEREO_LEFT_LOW_POWER) {
+                               reg_mask |= SET_BITS(regAUDIO_RX_0, LSPLEN, 1);
+
+                               reg_write |= SET_BITS(regAUDIO_RX_0, LSPLEN, 1);
+                       }
+               } else if ((handle == vCodec.handle)
+                          && (vCodec.handleState == HANDLE_IN_USE)
+                          && (audioOutput.vCodecOut == VCODEC_DIRECT_OUT)) {
+                       if (port & MONO_SPEAKER) {
+                               reg_mask = SET_BITS(regAUDIO_RX_0, ASPEN, 1) |
+                                   SET_BITS(regAUDIO_RX_0, ASPSEL, 1);
+                               reg_write = SET_BITS(regAUDIO_RX_0, ASPEN, 1) |
+                                   SET_BITS(regAUDIO_RX_0, ASPSEL, 0);
+                       }
+                       if (port & MONO_LOUDSPEAKER) {
+                               reg_mask |= SET_BITS(regAUDIO_RX_0, ALSPEN, 1) |
+                                   SET_BITS(regAUDIO_RX_0, ALSPREF, 1) |
+                                   SET_BITS(regAUDIO_RX_0, ALSPSEL, 1);
+                               reg_write |=
+                                   SET_BITS(regAUDIO_RX_0, ALSPEN,
+                                            1) | SET_BITS(regAUDIO_RX_0,
+                                                          ALSPREF,
+                                                          1) |
+                                   SET_BITS(regAUDIO_RX_0, ALSPSEL, 0);
+                       }
+
+                       if (port & STEREO_HEADSET_LEFT) {
+                               reg_mask |= SET_BITS(regAUDIO_RX_0, AHSLEN, 1) |
+                                   SET_BITS(regAUDIO_RX_0, AHSSEL, 1);
+                               reg_write |=
+                                   SET_BITS(regAUDIO_RX_0, AHSLEN,
+                                            1) | SET_BITS(regAUDIO_RX_0,
+                                                          AHSSEL, 0);
+                       }
+                       if (port & STEREO_HEADSET_RIGHT) {
+                               reg_mask |= SET_BITS(regAUDIO_RX_0, AHSREN, 1) |
+                                   SET_BITS(regAUDIO_RX_0, AHSSEL, 1);
+                               reg_write |=
+                                   SET_BITS(regAUDIO_RX_0, AHSREN,
+                                            1) | SET_BITS(regAUDIO_RX_0,
+                                                          AHSSEL, 0);
+                       }
+                       if (port & STEREO_OUT_LEFT) {
+                               reg_mask |=
+                                   SET_BITS(regAUDIO_RX_0, ARXOUTLEN,
+                                            1) | SET_BITS(regAUDIO_RX_0,
+                                                          ARXOUTSEL, 1);
+                               reg_write |=
+                                   SET_BITS(regAUDIO_RX_0, ARXOUTLEN,
+                                            1) | SET_BITS(regAUDIO_RX_0,
+                                                          ARXOUTSEL, 0);
+                       }
+                       if (port & STEREO_OUT_RIGHT) {
+                               reg_mask |=
+                                   SET_BITS(regAUDIO_RX_0, ARXOUTREN,
+                                            1) | SET_BITS(regAUDIO_RX_0,
+                                                          ARXOUTSEL, 1);
+                               reg_write |=
+                                   SET_BITS(regAUDIO_RX_0, ARXOUTREN,
+                                            1) | SET_BITS(regAUDIO_RX_0,
+                                                          ARXOUTSEL, 0);
+                       }
+                       if (port & MONO_CDCOUT) {
+                               reg_mask |=
+                                   SET_BITS(regAUDIO_RX_0, CDCOUTEN, 1);
+
+                               reg_write |=
+                                   SET_BITS(regAUDIO_RX_0, CDCOUTEN, 1);
+                       }
+               }
+
+               if (reg_mask == 0) {
+
+               } else {
+                       rc = pmic_write_reg(REG_AUDIO_RX_0,
+                                           reg_write, reg_mask);
+
+                       if (rc == PMIC_SUCCESS) {
+                               pr_debug("output ports  enabled\n");
+                               audioOutput.outputPort = port;
+
+                       }
+               }
+       }
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * @brief Deselect/disable the audio output ports.
+ *
+ * This function disables the audio output ports that were previously enabled
+ * by calling pmic_audio_output_set_port().
+ *
+ * @param   handle              Device handle from pmic_audio_open() call.
+ * @param   port                The audio output ports to be disabled.
+ *
+ * @retval      PMIC_SUCCESS         If the audio output ports were successfully
+ *                                   disabled.
+ * @retval      PMIC_PARAMETER_ERROR If the handle or output ports were
+ *                                   invalid.
+ * @retval      PMIC_ERROR           If the audio output ports could not be
+ *                                   disabled.
+ */
+PMIC_STATUS pmic_audio_output_clear_port(const PMIC_AUDIO_HANDLE handle,
+                                        const PMIC_AUDIO_OUTPUT_PORT port)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       unsigned int reg_write = 0;
+       unsigned int reg_mask = 0;
+
+       /* Use a critical section to ensure a consistent hardware state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((port == MONO_ALERT) || (port == MONO_EXTOUT)) {
+               rc = PMIC_NOT_SUPPORTED;
+       } else {
+               if (((handle == stDAC.handle)
+                    && (stDAC.handleState == HANDLE_IN_USE))
+                   || ((handle == extStereoIn.handle)
+                       && (extStereoIn.handleState == HANDLE_IN_USE))
+                   || ((handle == vCodec.handle)
+                       && (vCodec.handleState == HANDLE_IN_USE)
+                       && (audioOutput.vCodecOut == VCODEC_MIXER_OUT))) {
+                       /* Stereo signal and MIXER source needs to be routed to the port /
+                          Avoid Codec direct out */
+                       if (port & MONO_SPEAKER) {
+                               reg_mask = SET_BITS(regAUDIO_RX_0, ASPEN, 1);
+                               reg_write = SET_BITS(regAUDIO_RX_0, ASPEN, 0);
+                       }
+                       if (port & MONO_LOUDSPEAKER) {
+                               reg_mask |= SET_BITS(regAUDIO_RX_0, ALSPEN, 1) |
+                                   SET_BITS(regAUDIO_RX_0, ALSPREF, 1);
+
+                               reg_write |=
+                                   SET_BITS(regAUDIO_RX_0, ALSPEN,
+                                            0) | SET_BITS(regAUDIO_RX_0,
+                                                          ALSPREF, 0);
+
+                       }
+                       if (port & STEREO_HEADSET_LEFT) {
+                               reg_mask |= SET_BITS(regAUDIO_RX_0, AHSLEN, 1);
+                               reg_write |= SET_BITS(regAUDIO_RX_0, AHSLEN, 0);
+                       }
+                       if (port & STEREO_HEADSET_RIGHT) {
+                               reg_mask |= SET_BITS(regAUDIO_RX_0, AHSREN, 1);
+                               reg_write |= SET_BITS(regAUDIO_RX_0, AHSREN, 0);
+                       }
+                       if (port & STEREO_OUT_LEFT) {
+                               reg_mask |=
+                                   SET_BITS(regAUDIO_RX_0, ARXOUTLEN, 1);
+                               reg_write |=
+                                   SET_BITS(regAUDIO_RX_0, ARXOUTLEN, 0);
+                       }
+                       if (port & STEREO_OUT_RIGHT) {
+                               reg_mask |=
+                                   SET_BITS(regAUDIO_RX_0, ARXOUTREN, 1);
+                               reg_write |=
+                                   SET_BITS(regAUDIO_RX_0, ARXOUTREN, 0);
+                       }
+                       if (port & STEREO_LEFT_LOW_POWER) {
+                               reg_mask |= SET_BITS(regAUDIO_RX_0, LSPLEN, 1);
+                               reg_write |= SET_BITS(regAUDIO_RX_0, LSPLEN, 0);
+                       }
+               } else if ((handle == vCodec.handle)
+                          && (vCodec.handleState == HANDLE_IN_USE)
+                          && (audioOutput.vCodecOut == VCODEC_DIRECT_OUT)) {
+                       if (port & MONO_SPEAKER) {
+                               reg_mask = SET_BITS(regAUDIO_RX_0, ASPEN, 1);
+                               reg_write = SET_BITS(regAUDIO_RX_0, ASPEN, 0);
+                       }
+                       if (port & MONO_LOUDSPEAKER) {
+                               reg_mask |= SET_BITS(regAUDIO_RX_0, ALSPEN, 1) |
+                                   SET_BITS(regAUDIO_RX_0, ALSPREF, 1);
+                               reg_write |=
+                                   SET_BITS(regAUDIO_RX_0, ALSPEN,
+                                            0) | SET_BITS(regAUDIO_RX_0,
+                                                          ALSPREF, 0);
+                       }
+                       if (port & STEREO_HEADSET_LEFT) {
+                               reg_mask |= SET_BITS(regAUDIO_RX_0, AHSLEN, 1);
+                               reg_write |= SET_BITS(regAUDIO_RX_0, AHSLEN, 0);
+                       }
+                       if (port & STEREO_HEADSET_RIGHT) {
+                               reg_mask |= SET_BITS(regAUDIO_RX_0, AHSREN, 1);
+                               reg_write |= SET_BITS(regAUDIO_RX_0, AHSREN, 0);
+                       }
+                       if (port & STEREO_OUT_LEFT) {
+                               reg_mask |=
+                                   SET_BITS(regAUDIO_RX_0, ARXOUTLEN, 1);
+                               reg_write |=
+                                   SET_BITS(regAUDIO_RX_0, ARXOUTLEN, 0);
+                       }
+                       if (port & STEREO_OUT_RIGHT) {
+                               reg_mask |=
+                                   SET_BITS(regAUDIO_RX_0, ARXOUTREN, 1);
+                               reg_write |=
+                                   SET_BITS(regAUDIO_RX_0, ARXOUTREN, 0);
+                       }
+                       if (port & MONO_CDCOUT) {
+                               reg_mask |=
+                                   SET_BITS(regAUDIO_RX_0, CDCOUTEN, 1);
+                               reg_write |=
+                                   SET_BITS(regAUDIO_RX_0, CDCOUTEN, 0);
+                       }
+               }
+#ifdef CONFIG_HEADSET_DETECT_ENABLE
+
+               if (port & STEREO_HEADSET_LEFT) {
+                       reg_mask |= SET_BITS(regAUDIO_RX_0, AHSLEN, 1);
+                       reg_write |= SET_BITS(regAUDIO_RX_0, AHSLEN, 0);
+               }
+               if (port & STEREO_HEADSET_RIGHT) {
+                       reg_mask |= SET_BITS(regAUDIO_RX_0, AHSREN, 1);
+                       reg_write |= SET_BITS(regAUDIO_RX_0, AHSREN, 0);
+               }
+#endif
+
+               if (reg_mask == 0) {
+
+               } else {
+                       rc = pmic_write_reg(REG_AUDIO_RX_0,
+                                           reg_write, reg_mask);
+                       if (rc == PMIC_SUCCESS) {
+                               pr_debug("output ports disabled\n");
+                               audioOutput.outputPort &= ~port;
+                       }
+               }
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * @brief Get the current audio output ports.
+ *
+ * This function retrieves the audio output ports that are currently being
+ * used.
+ *
+ * @param   handle              Device handle from pmic_audio_open() call.
+ * @param   port                The audio output ports currently being used.
+ *
+ * @retval      PMIC_SUCCESS         If the audio output ports were successfully
+ *                                   retrieved.
+ * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval      PMIC_ERROR           If the audio output ports could not be
+ *                                   retrieved.
+ */
+PMIC_STATUS pmic_audio_output_get_port(const PMIC_AUDIO_HANDLE handle,
+                                      PMIC_AUDIO_OUTPUT_PORT * const port)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+       /* Use a critical section to ensure a consistent hardware state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((((handle == stDAC.handle) &&
+             (stDAC.handleState == HANDLE_IN_USE)) ||
+            ((handle == vCodec.handle) &&
+             (vCodec.handleState == HANDLE_IN_USE)) ||
+            ((handle == extStereoIn.handle) &&
+             (extStereoIn.handleState == HANDLE_IN_USE))) &&
+           (port != (PMIC_AUDIO_OUTPUT_PORT *) NULL)) {
+               *port = audioOutput.outputPort;
+
+               rc = PMIC_SUCCESS;
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * @brief Set the gain level for the external stereo inputs.
+ *
+ * This function sets the gain levels for the external stereo inputs.
+ *
+ * @param   handle              Device handle from pmic_audio_open() call.
+ * @param   gain                The external stereo input gain level.
+ *
+ * @retval      PMIC_SUCCESS         If the gain level was successfully set.
+ * @retval      PMIC_PARAMETER_ERROR If the handle or gain level was invalid.
+ * @retval      PMIC_ERROR           If the gain level could not be set.
+ */
+PMIC_STATUS pmic_audio_output_set_stereo_in_gain(const PMIC_AUDIO_HANDLE handle,
+                                                const PMIC_AUDIO_STEREO_IN_GAIN
+                                                gain)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       unsigned int reg_mask = SET_BITS(regAUDIO_RX_1, ARXINEN, 1) |
+           SET_BITS(regAUDIO_RX_1, ARXIN, 1);
+       unsigned int reg_write = SET_BITS(regAUDIO_RX_1, ARXINEN, 1);
+
+       /* No critical section required here since we are not updating any
+        * global data.
+        */
+
+       /* The ARX amplifier for stereo is also enabled over here */
+
+       if ((gain == STEREO_IN_GAIN_0DB) || (gain == STEREO_IN_GAIN_PLUS_18DB)) {
+               if ((handle == extStereoIn.handle) &&
+                   (extStereoIn.handleState == HANDLE_IN_USE)) {
+
+                       if (gain == STEREO_IN_GAIN_0DB) {
+                               reg_write |= SET_BITS(regAUDIO_RX_1, ARXIN, 1);
+                       } else {
+                               reg_write |= SET_BITS(regAUDIO_RX_1, ARXIN, 0);
+                       }
+
+                       rc = pmic_write_reg(REG_AUDIO_RX_1,
+                                           reg_write, reg_mask);
+
+                       if (rc == PMIC_SUCCESS) {
+                               pr_debug("Ext stereo gain set\n");
+                               extStereoIn.inputGain = gain;
+
+                       }
+
+               } else {
+                       rc = PMIC_PARAMETER_ERROR;
+               }
+       }
+
+       return rc;
+}
+
+/*!
+ * @brief Get the current gain level for the external stereo inputs.
+ *
+ * This function retrieves the current gain levels for the external stereo
+ * inputs.
+ *
+ * @param   handle              Device handle from pmic_audio_open() call.
+ * @param   gain                The current external stereo input gain
+ *                                  level.
+ *
+ * @retval      PMIC_SUCCESS         If the gain level was successfully
+ *                                   retrieved.
+ * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval      PMIC_ERROR           If the gain level could not be retrieved.
+ */
+PMIC_STATUS pmic_audio_output_get_stereo_in_gain(const PMIC_AUDIO_HANDLE handle,
+                                                PMIC_AUDIO_STEREO_IN_GAIN *
+                                                const gain)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+       /* Use a critical section to ensure a consistent hardware state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle == extStereoIn.handle) &&
+           (extStereoIn.handleState == HANDLE_IN_USE) &&
+           (gain != (PMIC_AUDIO_STEREO_IN_GAIN *) NULL)) {
+               *gain = extStereoIn.inputGain;
+               rc = PMIC_SUCCESS;
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * @brief Set the output PGA gain level.
+ *
+ * This function sets the audio output PGA gain level.
+ *
+ * @param   handle              Device handle from pmic_audio_open() call.
+ * @param   gain                The output PGA gain level.
+ *
+ * @retval      PMIC_SUCCESS         If the gain level was successfully set.
+ * @retval      PMIC_PARAMETER_ERROR If the handle or gain level was invalid.
+ * @retval      PMIC_ERROR           If the gain level could not be set.
+ */
+PMIC_STATUS pmic_audio_output_set_pgaGain(const PMIC_AUDIO_HANDLE handle,
+                                         const PMIC_AUDIO_OUTPUT_PGA_GAIN gain)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       unsigned int reg_write = 0;
+       unsigned int reg_mask = 0;
+       unsigned int reg_gain;
+
+       /* Use a critical section to ensure a consistent hardware state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if (!((gain >= OUTPGA_GAIN_MINUS_33DB)
+             && (gain <= OUTPGA_GAIN_PLUS_6DB))) {
+               rc = PMIC_NOT_SUPPORTED;
+               pr_debug("output set PGA gain - wrong gain value\n");
+       } else {
+               reg_gain = gain + 2;
+               if ((handle == extStereoIn.handle) &&
+                   (extStereoIn.handleState == HANDLE_IN_USE)) {
+                       reg_mask = SET_BITS(regAUDIO_RX_1, ARXIN, 15) |
+                           SET_BITS(regAUDIO_RX_1, ARXINEN, 1);
+                       reg_write = SET_BITS(regAUDIO_RX_1, ARXIN, reg_gain) |
+                           SET_BITS(regAUDIO_RX_1, ARXINEN, 1);
+               } else if ((handle == vCodec.handle) &&
+                          (vCodec.handleState == HANDLE_IN_USE)) {
+                       reg_mask = SET_BITS(regAUDIO_RX_1, PGARX, 15);
+                       reg_write = SET_BITS(regAUDIO_RX_1, PGARX, reg_gain);
+               } else if ((handle == stDAC.handle) &&
+                          (stDAC.handleState == HANDLE_IN_USE)) {
+                       reg_mask = SET_BITS(regAUDIO_RX_1, PGAST, 15);
+                       reg_write = SET_BITS(regAUDIO_RX_1, PGAST, reg_gain);
+               }
+
+               if (reg_mask == 0) {
+
+               } else {
+                       rc = pmic_write_reg(REG_AUDIO_RX_1,
+                                           reg_write, reg_mask);
+
+                       if (rc == PMIC_SUCCESS) {
+                               pr_debug("Output PGA gains set\n");
+
+                               if (handle == stDAC.handle) {
+                                       audioOutput.stDacoutputPGAGain = gain;
+                               } else if (handle == vCodec.handle) {
+                                       audioOutput.vCodecoutputPGAGain = gain;
+                               } else {
+                                       audioOutput.extStereooutputPGAGain =
+                                           gain;
+                               }
+                       } else {
+                               pr_debug
+                                   ("Error writing PGA gains to register\n");
+                       }
+               }
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * @brief Get the output PGA gain level.
+ *
+ * This function retrieves the current audio output PGA gain level.
+ *
+ * @param   handle              Device handle from pmic_audio_open() call.
+ * @param   gain                The current output PGA gain level.
+ *
+ * @retval      PMIC_SUCCESS         If the gain level was successfully
+ *                                   retrieved.
+ * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval      PMIC_ERROR           If the gain level could not be retrieved.
+ */
+PMIC_STATUS pmic_audio_output_get_pgaGain(const PMIC_AUDIO_HANDLE handle,
+                                         PMIC_AUDIO_OUTPUT_PGA_GAIN *
+                                         const gain)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+       /* Use a critical section to ensure a consistent hardware state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if (gain != (PMIC_AUDIO_OUTPUT_PGA_GAIN *) NULL) {
+               if ((handle == extStereoIn.handle) &&
+                   (extStereoIn.handleState == HANDLE_IN_USE)) {
+                       *gain = audioOutput.extStereooutputPGAGain;
+                       rc = PMIC_SUCCESS;
+               } else if ((handle == vCodec.handle) &&
+                          (vCodec.handleState == HANDLE_IN_USE)) {
+                       *gain = audioOutput.vCodecoutputPGAGain;
+                       rc = PMIC_SUCCESS;
+               } else if ((handle == stDAC.handle) &&
+                          (stDAC.handleState == HANDLE_IN_USE)) {
+                       *gain = audioOutput.stDacoutputPGAGain;
+                       rc = PMIC_SUCCESS;
+               } else {
+                       rc = PMIC_PARAMETER_ERROR;
+               }
+       } else {
+               rc = PMIC_PARAMETER_ERROR;
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * @brief Enable the output mixer.
+ *
+ * This function enables the output mixer for the audio stream that
+ * corresponds to the current handle (i.e., the Voice CODEC, Stereo DAC, or
+ * the external stereo inputs).
+ *
+ * @param   handle              Device handle from pmic_audio_open() call.
+ *
+ * @retval      PMIC_SUCCESS         If the mixer was successfully enabled.
+ * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval      PMIC_ERROR           If the mixer could not be enabled.
+ */
+PMIC_STATUS pmic_audio_output_enable_mixer(const PMIC_AUDIO_HANDLE handle)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       unsigned int reg_mask = 0;
+       unsigned int reg_write = 0;
+       unsigned int reg_mask_mix = 0;
+       unsigned int reg_write_mix = 0;
+
+       /* No critical section required here since we are not updating any
+        * global data.
+        */
+
+       if (((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE))) {
+               reg_mask = SET_BITS(regAUDIO_RX_1, PGASTEN, 1);
+               reg_write = SET_BITS(regAUDIO_RX_1, PGASTEN, 1);
+               reg_mask_mix = SET_BITS(regAUDIO_RX_0, ADDSTDC, 1);
+               reg_write_mix = SET_BITS(regAUDIO_RX_0, ADDSTDC, 1);
+       } else if ((handle == vCodec.handle) &&
+                  (vCodec.handleState == HANDLE_IN_USE)) {
+               reg_mask = SET_BITS(regAUDIO_RX_1, PGARXEN, 1);
+               reg_write = SET_BITS(regAUDIO_RX_1, PGARXEN, 1);
+               audioOutput.vCodecOut = VCODEC_MIXER_OUT;
+
+               reg_mask_mix = SET_BITS(regAUDIO_RX_0, ADDCDC, 1);
+               reg_write_mix = SET_BITS(regAUDIO_RX_0, ADDCDC, 1);
+       } else if ((handle == extStereoIn.handle) &&
+                  (extStereoIn.handleState == HANDLE_IN_USE)) {
+               reg_mask = SET_BITS(regAUDIO_RX_1, ARXINEN, 1);
+               reg_write = SET_BITS(regAUDIO_RX_1, ARXINEN, 1);
+               reg_mask_mix = SET_BITS(regAUDIO_RX_0, ADDRXIN, 1);
+               reg_write_mix = SET_BITS(regAUDIO_RX_0, ADDRXIN, 1);
+       }
+
+       if (reg_mask == 0) {
+
+       } else {
+               rc = pmic_write_reg(REG_AUDIO_RX_1, reg_write, reg_mask);
+
+               if (rc == PMIC_SUCCESS) {
+
+                       rc = pmic_write_reg(REG_AUDIO_RX_0,
+                                           reg_write_mix, reg_mask_mix);
+                       if (rc == PMIC_SUCCESS) {
+                               pr_debug("Output PGA mixers enabled\n");
+                               rc = PMIC_SUCCESS;
+                       }
+
+               } else {
+                       pr_debug("Error writing mixer enable to register\n");
+               }
+
+       }
+
+       return rc;
+}
+
+/*!
+ * @brief Disable the output mixer.
+ *
+ * This function disables the output mixer for the audio stream that
+ * corresponds to the current handle (i.e., the Voice CODEC, Stereo DAC, or
+ * the external stereo inputs).
+ *
+ * @param   handle              Device handle from pmic_audio_open() call.
+ *
+ * @retval      PMIC_SUCCESS         If the mixer was successfully disabled.
+ * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval      PMIC_ERROR           If the mixer could not be disabled.
+ */
+PMIC_STATUS pmic_audio_output_disable_mixer(const PMIC_AUDIO_HANDLE handle)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       unsigned int reg_mask = 0;
+       unsigned int reg_write = 0;
+
+       unsigned int reg_mask_mix = 0;
+       unsigned int reg_write_mix = 0;
+
+       /* No critical section required here since we are not updating any
+        * global data.
+        */
+       if (((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE))) {
+               /*reg_mask = SET_BITS(regAUDIO_RX_1, PGASTEN, 1);
+                  reg_write = SET_BITS(regAUDIO_RX_1, PGASTEN, 0); */
+
+               reg_mask_mix = SET_BITS(regAUDIO_RX_0, ADDSTDC, 1);
+               reg_write_mix = SET_BITS(regAUDIO_RX_0, ADDSTDC, 0);
+       } else if ((handle == vCodec.handle) &&
+                  (vCodec.handleState == HANDLE_IN_USE)) {
+               reg_mask = SET_BITS(regAUDIO_RX_1, PGARXEN, 1);
+               reg_write = SET_BITS(regAUDIO_RX_1, PGARXEN, 0);
+               audioOutput.vCodecOut = VCODEC_DIRECT_OUT;
+
+               reg_mask_mix = SET_BITS(regAUDIO_RX_0, ADDCDC, 1);
+               reg_write_mix = SET_BITS(regAUDIO_RX_0, ADDCDC, 0);
+       } else if ((handle == extStereoIn.handle) &&
+                  (extStereoIn.handleState == HANDLE_IN_USE)) {
+               /*reg_mask = SET_BITS(regAUDIO_RX_1, ARXINEN, 1);
+                  reg_write = SET_BITS(regAUDIO_RX_1, ARXINEN, 0); */
+
+               reg_mask_mix = SET_BITS(regAUDIO_RX_0, ADDRXIN, 1);
+               reg_write_mix = SET_BITS(regAUDIO_RX_0, ADDRXIN, 1);
+       }
+
+       if (reg_mask == 0) {
+
+       } else {
+               rc = pmic_write_reg(REG_AUDIO_RX_1, reg_write, reg_mask);
+
+               if (rc == PMIC_SUCCESS) {
+
+                       rc = pmic_write_reg(REG_AUDIO_RX_0,
+                                           reg_write_mix, reg_mask_mix);
+                       if (rc == PMIC_SUCCESS) {
+                               pr_debug("Output PGA mixers disabled\n");
+                       }
+               }
+       }
+       return rc;
+}
+
+/*!
+ * @brief Configure and enable the output balance amplifiers.
+ *
+ * This function configures and enables the output balance amplifiers.
+ *
+ * @param   handle              Device handle from pmic_audio_open() call.
+ * @param   leftGain            The desired left channel gain level.
+ * @param   rightGain           The desired right channel gain level.
+ *
+ * @retval      PMIC_SUCCESS         If the output balance amplifiers were
+ *                                   successfully configured and enabled.
+ * @retval      PMIC_PARAMETER_ERROR If the handle or gain levels were invalid.
+ * @retval      PMIC_ERROR           If the output balance amplifiers could not
+ *                                   be reconfigured or enabled.
+ */
+PMIC_STATUS pmic_audio_output_set_balance(const PMIC_AUDIO_HANDLE handle,
+                                         const PMIC_AUDIO_OUTPUT_BALANCE_GAIN
+                                         leftGain,
+                                         const PMIC_AUDIO_OUTPUT_BALANCE_GAIN
+                                         rightGain)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       unsigned int reg_mask = 0;
+       unsigned int reg_write = 0;
+       unsigned int reg_mask_ch = 0;
+       unsigned int reg_write_ch = 0;
+
+       /* No critical section required here since we are not updating any
+        * global data.
+        */
+
+       if (!((leftGain >= BAL_GAIN_MINUS_21DB) && (leftGain <= BAL_GAIN_0DB))) {
+               rc = PMIC_PARAMETER_ERROR;
+       } else if (!((rightGain >= BAL_GAIN_MINUS_21DB)
+                    && (rightGain <= BAL_GAIN_0DB))) {
+               rc = PMIC_PARAMETER_ERROR;
+       } else {
+               if (((handle == stDAC.handle) &&
+                    (stDAC.handleState == HANDLE_IN_USE)) ||
+                   ((handle == vCodec.handle) &&
+                    (vCodec.handleState == HANDLE_IN_USE)) ||
+                   ((handle == extStereoIn.handle) &&
+                    (extStereoIn.handleState == HANDLE_IN_USE))) {
+                       /* In mc13783 only one channel can be attenuated wrt the other.
+                        * It is not possible to specify attenuation for both
+                        * This function will return an error if both channels
+                        * are required to be attenuated
+                        * The BALLR bit is set/reset depending on whether leftGain
+                        * or rightGain is specified*/
+                       if ((rightGain == BAL_GAIN_0DB)
+                           && (leftGain == BAL_GAIN_0DB)) {
+                               /* Nothing to be done */
+                       } else if ((rightGain != BAL_GAIN_0DB)
+                                  && (leftGain == BAL_GAIN_0DB)) {
+                               /* Attenuate right channel */
+                               reg_mask = SET_BITS(regAUDIO_RX_1, BAL, 7);
+                               reg_mask_ch = SET_BITS(regAUDIO_RX_1, BALLR, 1);
+                               reg_write =
+                                   SET_BITS(regAUDIO_RX_1, BAL,
+                                            (BAL_GAIN_0DB - rightGain));
+                               /* The enum and the register values are reversed in order .. */
+                               reg_write_ch =
+                                   SET_BITS(regAUDIO_RX_1, BALLR, 0);
+                               /* BALLR = 0 selects right channel for atten */
+                       } else if ((rightGain == BAL_GAIN_0DB)
+                                  && (leftGain != BAL_GAIN_0DB)) {
+                               /* Attenuate left channel */
+
+                               reg_mask = SET_BITS(regAUDIO_RX_1, BAL, 7);
+                               reg_mask_ch = SET_BITS(regAUDIO_RX_1, BALLR, 1);
+                               reg_write =
+                                   SET_BITS(regAUDIO_RX_1, BAL,
+                                            (BAL_GAIN_0DB - leftGain));
+                               reg_write_ch =
+                                   SET_BITS(regAUDIO_RX_1, BALLR, 1);
+                               /* BALLR = 1 selects left channel for atten */
+                       } else {
+                               rc = PMIC_PARAMETER_ERROR;
+                       }
+
+                       if ((reg_mask == 0) || (reg_mask_ch == 0)) {
+
+                       } else {
+                               rc = pmic_write_reg(REG_AUDIO_RX_1,
+                                                   reg_write_ch, reg_mask_ch);
+
+                               if (rc == PMIC_SUCCESS) {
+                                       rc = pmic_write_reg(REG_AUDIO_RX_1,
+                                                           reg_write,
+                                                           reg_mask);
+
+                                       if (rc == PMIC_SUCCESS) {
+                                               pr_debug
+                                                   ("Output balance attenuation set\n");
+                                               audioOutput.balanceLeftGain =
+                                                   leftGain;
+                                               audioOutput.balanceRightGain =
+                                                   rightGain;
+                                       }
+                               }
+                       }
+               }
+       }
+       return rc;
+}
+
+/*!
+ * @brief Get the current output balance amplifier gain levels.
+ *
+ * This function retrieves the current output balance amplifier gain levels.
+ *
+ * @param      handle              Device handle from pmic_audio_open() call.
+ * @param      leftGain            The current left channel gain level.
+ * @param      rightGain           The current right channel gain level.
+ *
+ * @retval      PMIC_SUCCESS         If the output balance amplifier gain levels
+ *                                   were successfully retrieved.
+ * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval      PMIC_ERROR           If the output balance amplifier gain levels
+ *                                   could be retrieved.
+ */
+PMIC_STATUS pmic_audio_output_get_balance(const PMIC_AUDIO_HANDLE handle,
+                                         PMIC_AUDIO_OUTPUT_BALANCE_GAIN *
+                                         const leftGain,
+                                         PMIC_AUDIO_OUTPUT_BALANCE_GAIN *
+                                         const rightGain)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+       /* Use a critical section to ensure a consistent hardware state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((((handle == stDAC.handle) &&
+             (stDAC.handleState == HANDLE_IN_USE)) ||
+            ((handle == vCodec.handle) &&
+             (vCodec.handleState == HANDLE_IN_USE)) ||
+            ((handle == extStereoIn.handle) &&
+             (extStereoIn.handleState == HANDLE_IN_USE))) &&
+           ((leftGain != (PMIC_AUDIO_OUTPUT_BALANCE_GAIN *) NULL) &&
+            (rightGain != (PMIC_AUDIO_OUTPUT_BALANCE_GAIN *) NULL))) {
+               *leftGain = audioOutput.balanceLeftGain;
+               *rightGain = audioOutput.balanceRightGain;
+
+               rc = PMIC_SUCCESS;
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * @brief Configure and enable the output mono adder.
+ *
+ * This function configures and enables the output mono adder.
+ *
+ * @param   handle              Device handle from pmic_audio_open() call.
+ * @param   mode                The desired mono adder operating mode.
+ *
+ * @retval      PMIC_SUCCESS         If the mono adder was successfully
+ *                                   configured and enabled.
+ * @retval      PMIC_PARAMETER_ERROR If the handle or mono adder mode was
+ *                                   invalid.
+ * @retval      PMIC_ERROR           If the mono adder could not be reconfigured
+ *                                   or enabled.
+ */
+PMIC_STATUS pmic_audio_output_enable_mono_adder(const PMIC_AUDIO_HANDLE handle,
+                                               const PMIC_AUDIO_MONO_ADDER_MODE
+                                               mode)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       unsigned int reg_write = 0;
+       unsigned int reg_mask = SET_BITS(regAUDIO_RX_1, MONO, 3);
+
+       /* No critical section required here since we are not updating any
+        * global data.
+        */
+
+       if ((mode >= MONO_ADDER_OFF) && (mode <= STEREO_OPPOSITE_PHASE)) {
+               if (((handle == stDAC.handle) &&
+                    (stDAC.handleState == HANDLE_IN_USE)) ||
+                   ((handle == vCodec.handle) &&
+                    (vCodec.handleState == HANDLE_IN_USE)) ||
+                   ((handle == extStereoIn.handle) &&
+                    (extStereoIn.handleState == HANDLE_IN_USE))) {
+                       if (mode == MONO_ADDER_OFF) {
+                               reg_write = SET_BITS(regAUDIO_RX_1, MONO, 0);
+                       } else if (mode == MONO_ADD_LEFT_RIGHT) {
+                               reg_write = SET_BITS(regAUDIO_RX_1, MONO, 2);
+                       } else if (mode == MONO_ADD_OPPOSITE_PHASE) {
+                               reg_write = SET_BITS(regAUDIO_RX_1, MONO, 3);
+                       } else {        /* stereo opposite */
+
+                               reg_write = SET_BITS(regAUDIO_RX_1, MONO, 1);
+                       }
+
+                       rc = pmic_write_reg(REG_AUDIO_RX_1,
+                                           reg_write, reg_mask);
+
+                       if (rc == PMIC_SUCCESS) {
+                               pr_debug("Output mono adder mode set\n");
+
+                       }
+
+               } else {
+                       rc = PMIC_PARAMETER_ERROR;
+               }
+       } else {
+               rc = PMIC_PARAMETER_ERROR;
+       }
+       return rc;
+}
+
+/*!
+ * @brief Disable the output mono adder.
+ *
+ * This function disables the output mono adder.
+ *
+ * @param   handle              Device handle from pmic_audio_open() call.
+ *
+ * @retval      PMIC_SUCCESS         If the mono adder was successfully
+ *                                   disabled.
+ * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval      PMIC_ERROR           If the mono adder could not be disabled.
+ */
+PMIC_STATUS pmic_audio_output_disable_mono_adder(const PMIC_AUDIO_HANDLE handle)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       const unsigned int reg_write = 0;
+       const unsigned int reg_mask = SET_BITS(regAUDIO_RX_1, MONO, 3);
+
+       /* No critical section required here since we are not updating any
+        * global data.
+        */
+
+       if (((handle == stDAC.handle) &&
+            (stDAC.handleState == HANDLE_IN_USE)) ||
+           ((handle == vCodec.handle) &&
+            (vCodec.handleState == HANDLE_IN_USE)) ||
+           ((handle == extStereoIn.handle) &&
+            (extStereoIn.handleState == HANDLE_IN_USE))) {
+               rc = pmic_write_reg(REG_AUDIO_RX_1, reg_write, reg_mask);
+       }
+
+       return rc;
+}
+
+/*!
+ * @brief Configure the mono adder output gain level.
+ *
+ * This function configures the mono adder output amplifier gain level.
+ *
+ * @param   handle              Device handle from pmic_audio_open() call.
+ * @param   gain                The desired output gain level.
+ *
+ * @retval      PMIC_SUCCESS         If the mono adder output amplifier gain
+ *                                   level was successfully set.
+ * @retval      PMIC_PARAMETER_ERROR If the handle or gain level was invalid.
+ * @retval      PMIC_ERROR           If the mono adder output amplifier gain
+ *                                   level could not be reconfigured.
+ */
+PMIC_STATUS pmic_audio_output_set_mono_adder_gain(const PMIC_AUDIO_HANDLE
+                                                 handle,
+                                                 const
+                                                 PMIC_AUDIO_MONO_ADDER_OUTPUT_GAIN
+                                                 gain)
+{
+       PMIC_STATUS rc = PMIC_NOT_SUPPORTED;
+       return rc;
+}
+
+/*!
+ * @brief Get the current mono adder output gain level.
+ *
+ * This function retrieves the current mono adder output amplifier gain level.
+ *
+ * @param   handle              Device handle from pmic_audio_open() call.
+ * @param   gain                The current output gain level.
+ *
+ * @retval      PMIC_SUCCESS         If the mono adder output amplifier gain
+ *                                   level was successfully retrieved.
+ * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval      PMIC_ERROR           If the mono adder output amplifier gain
+ *                                   level could not be retrieved.
+ */
+PMIC_STATUS pmic_audio_output_get_mono_adder_gain(const PMIC_AUDIO_HANDLE
+                                                 handle,
+                                                 PMIC_AUDIO_MONO_ADDER_OUTPUT_GAIN
+                                                 * const gain)
+{
+       PMIC_STATUS rc = PMIC_NOT_SUPPORTED;
+       return rc;
+}
+
+/*!
+ * @brief Set various audio output section options.
+ *
+ * This function sets one or more audio output section configuration
+ * options. The currently supported options include whether to disable
+ * the non-inverting mono speaker output, enabling the loudspeaker common
+ * bias circuit, enabling detection of headset insertion/removal, and
+ * whether to automatically disable the headset amplifiers when a headset
+ * insertion/removal has been detected.
+ *
+ * @param   handle              Device handle from pmic_audio_open() call.
+ * @param   config              The desired audio output section
+ *                                  configuration options to be set.
+ *
+ * @retval      PMIC_SUCCESS         If the desired configuration options were
+ *                                   all successfully set.
+ * @retval      PMIC_PARAMETER_ERROR If the handle or configuration options
+ *                                   were invalid.
+ * @retval      PMIC_ERROR           If the desired configuration options
+ *                                   could not be set.
+ */
+PMIC_STATUS pmic_audio_output_set_config(const PMIC_AUDIO_HANDLE handle,
+                                        const PMIC_AUDIO_OUTPUT_CONFIG config)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       unsigned int reg_mask = 0;
+       unsigned int reg_write = 0;
+
+       /* Use a critical section to ensure a consistent hardware state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if (((handle == stDAC.handle) &&
+            (stDAC.handleState == HANDLE_IN_USE)) ||
+           ((handle == vCodec.handle) &&
+            (vCodec.handleState == HANDLE_IN_USE)) ||
+           ((handle == extStereoIn.handle) &&
+            (extStereoIn.handleState == HANDLE_IN_USE))) {
+               if (config & MONO_SPEAKER_INVERT_OUT_ONLY) {
+                       /* If this is one of the parameters */
+                       rc = PMIC_NOT_SUPPORTED;
+               } else {
+                       if (config & MONO_LOUDSPEAKER_COMMON_BIAS) {
+                               reg_mask = SET_BITS(regAUDIO_RX_0, ALSPREF, 1);
+                               reg_write = SET_BITS(regAUDIO_RX_0, ALSPREF, 1);
+                       }
+                       if (config & HEADSET_DETECT_ENABLE) {
+                               reg_mask |= SET_BITS(regAUDIO_RX_0, HSDETEN, 1);
+                               reg_write |=
+                                   SET_BITS(regAUDIO_RX_0, HSDETEN, 1);
+                       }
+                       if (config & STEREO_HEADSET_AMP_AUTO_DISABLE) {
+                               reg_mask |=
+                                   SET_BITS(regAUDIO_RX_0, HSDETAUTOB, 1);
+                               reg_write |=
+                                   SET_BITS(regAUDIO_RX_0, HSDETAUTOB, 1);
+                       }
+
+                       if (reg_mask == 0) {
+                               rc = PMIC_PARAMETER_ERROR;
+                       } else {
+                               rc = pmic_write_reg(REG_AUDIO_RX_0,
+                                                   reg_write, reg_mask);
+
+                               if (rc == PMIC_SUCCESS) {
+                                       pr_debug("Output config set\n");
+                                       audioOutput.config |= config;
+
+                               }
+                       }
+               }
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * @brief Clear various audio output section options.
+ *
+ * This function clears one or more audio output section configuration
+ * options.
+ *
+ * @param   handle              Device handle from pmic_audio_open() call.
+ * @param   config              The desired audio output section
+ *                                  configuration options to be cleared.
+ *
+ * @retval      PMIC_SUCCESS         If the desired configuration options were
+ *                                   all successfully cleared.
+ * @retval      PMIC_PARAMETER_ERROR If the handle or configuration options
+ *                                   were invalid.
+ * @retval      PMIC_ERROR           If the desired configuration options
+ *                                   could not be cleared.
+ */
+PMIC_STATUS pmic_audio_output_clear_config(const PMIC_AUDIO_HANDLE handle,
+                                          const PMIC_AUDIO_OUTPUT_CONFIG
+                                          config)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       /*unsigned int reg_write_RX = 0;
+          unsigned int reg_mask_RX  = 0;
+          unsigned int reg_write_TX = 0;
+          unsigned int reg_mask_TX  = 0; */
+       unsigned int reg_mask = 0;
+       unsigned int reg_write = 0;
+
+       /* Use a critical section to ensure a consistent hardware state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if (((handle == stDAC.handle) &&
+            (stDAC.handleState == HANDLE_IN_USE)) ||
+           ((handle == vCodec.handle) &&
+            (vCodec.handleState == HANDLE_IN_USE)) ||
+           ((handle == extStereoIn.handle) &&
+            (extStereoIn.handleState == HANDLE_IN_USE))) {
+               if (config & MONO_SPEAKER_INVERT_OUT_ONLY) {
+                       /* If this is one of the parameters */
+                       rc = PMIC_NOT_SUPPORTED;
+               } else {
+                       if (config & MONO_LOUDSPEAKER_COMMON_BIAS) {
+                               reg_mask = SET_BITS(regAUDIO_RX_0, ALSPREF, 1);
+                               reg_write = SET_BITS(regAUDIO_RX_0, ALSPREF, 0);
+                       }
+
+                       if (config & HEADSET_DETECT_ENABLE) {
+                               reg_mask |= SET_BITS(regAUDIO_RX_0, HSDETEN, 1);
+                               reg_write |=
+                                   SET_BITS(regAUDIO_RX_0, HSDETEN, 0);
+                       }
+
+                       if (config & STEREO_HEADSET_AMP_AUTO_DISABLE) {
+                               reg_mask |=
+                                   SET_BITS(regAUDIO_RX_0, HSDETAUTOB, 1);
+                               reg_write |=
+                                   SET_BITS(regAUDIO_RX_0, HSDETAUTOB, 0);
+                       }
+
+                       if (reg_mask == 0) {
+                               rc = PMIC_PARAMETER_ERROR;
+                       } else {
+                               rc = pmic_write_reg(REG_AUDIO_RX_0,
+                                                   reg_write, reg_mask);
+
+                               if (rc == PMIC_SUCCESS) {
+                                       pr_debug("Output config cleared\n");
+                                       audioOutput.config &= ~config;
+
+                               }
+                       }
+               }
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * @brief Get the current audio output section options.
+ *
+ * This function retrieves the current audio output section configuration
+ * option settings.
+ *
+ * @param   handle              Device handle from pmic_audio_open() call.
+ * @param  config              The current audio output section
+ *                                  configuration option settings.
+ *
+ * @retval      PMIC_SUCCESS         If the current configuration options were
+ *                                   successfully retrieved.
+ * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval      PMIC_ERROR           If the current configuration options
+ *                                   could not be retrieved.
+ */
+PMIC_STATUS pmic_audio_output_get_config(const PMIC_AUDIO_HANDLE handle,
+                                        PMIC_AUDIO_OUTPUT_CONFIG *
+                                        const config)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+       /* Use a critical section to ensure a consistent hardware state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((((handle == stDAC.handle) &&
+             (stDAC.handleState == HANDLE_IN_USE)) ||
+            ((handle == vCodec.handle) &&
+             (vCodec.handleState == HANDLE_IN_USE)) ||
+            ((handle == extStereoIn.handle) &&
+             (extStereoIn.handleState == HANDLE_IN_USE))) &&
+           (config != (PMIC_AUDIO_OUTPUT_CONFIG *) NULL)) {
+               *config = audioOutput.config;
+
+               rc = PMIC_SUCCESS;
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * @brief Enable the phantom ground circuit that is used to help identify
+ *        the type of headset that has been inserted.
+ *
+ * This function enables the phantom ground circuit that is used to help
+ * identify the type of headset (e.g., stereo or mono) that has been inserted.
+ *
+ * @param   handle              Device handle from pmic_audio_open() call.
+ *
+ * @retval      PMIC_SUCCESS         If the phantom ground circuit was
+ *                                   successfully enabled.
+ * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval      PMIC_ERROR           If the phantom ground circuit could not
+ *                                   be enabled.
+ */
+PMIC_STATUS pmic_audio_output_enable_phantom_ground()
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       const unsigned int reg_mask = SET_BITS(regAUDIO_RX_0, HSPGDIS, 1);
+
+       /* No critical section required here since we are not updating any
+        * global data.
+        */
+
+       rc = pmic_write_reg(REG_AUDIO_RX_0, 0, reg_mask);
+       if (rc == PMIC_SUCCESS) {
+               pr_debug("Phantom ground enabled\n");
+
+       }
+       return rc;
+}
+
+/*!
+ * @brief Disable the phantom ground circuit that is used to help identify
+ *        the type of headset that has been inserted.
+ *
+ * This function disables the phantom ground circuit that is used to help
+ * identify the type of headset (e.g., stereo or mono) that has been inserted.
+ *
+ * @param   handle              Device handle from pmic_audio_open() call.
+ *
+ * @retval      PMIC_SUCCESS         If the phantom ground circuit was
+ *                                   successfully disabled.
+ * @retval      PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval      PMIC_ERROR           If the phantom ground circuit could not
+ *                                   be disabled.
+ */
+PMIC_STATUS pmic_audio_output_disable_phantom_ground()
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       const unsigned int reg_mask = SET_BITS(regAUDIO_RX_0, HSPGDIS, 1);
+
+       /* No critical section required here since we are not updating any
+        * global data.
+        */
+
+       rc = pmic_write_reg(REG_AUDIO_RX_0, 1, reg_mask);
+       if (rc == PMIC_SUCCESS) {
+               pr_debug("Phantom ground disabled\n");
+
+       }
+       return rc;
+}
+
+/*@}*/
+
+/**************************************************************************
+ * Static functions.
+ **************************************************************************
+ */
+
+/*!
+ * @name Audio Driver Internal Support Functions
+ * These non-exported internal functions are used to support the functionality
+ * of the exported audio APIs.
+ */
+/*@{*/
+
+/*!
+ * @brief Enables the 5.6V boost for the microphone bias 2 circuit.
+ *
+ * This function enables the switching regulator SW3 and configures it to
+ * provide the 5.6V boost that is required for driving the microphone bias 2
+ * circuit when using a 5-pole jack configuration (which is the case for the
+ * Sphinx board).
+ *
+ * @retval      PMIC_SUCCESS         The 5.6V boost was successfully enabled.
+ * @retval      PMIC_ERROR           Failed to enable the 5.6V boost.
+ */
+/*
+static PMIC_STATUS pmic_audio_mic_boost_enable(void)
+{
+       PMIC_STATUS rc = PMIC_NOT_SUPPORTED;
+
+       return rc;
+}
+*/
+/*!
+ * @brief Disables the 5.6V boost for the microphone bias 2 circuit.
+ *
+ * This function disables the switching regulator SW3 to turn off the 5.6V
+ * boost for the microphone bias 2 circuit.
+ *
+ * @retval      PMIC_SUCCESS         The 5.6V boost was successfully disabled.
+ * @retval      PMIC_ERROR           Failed to disable the 5.6V boost.
+ */
+/*
+static PMIC_STATUS pmic_audio_mic_boost_disable(void)
+{
+       PMIC_STATUS rc = PMIC_NOT_SUPPORTED;
+
+       return rc;
+}
+*/
+
+/*!
+ * @brief Free a device handle previously acquired by calling pmic_audio_open().
+ *
+ * Terminate further access to the PMIC audio hardware that was previously
+ * acquired by calling pmic_audio_open(). This now allows another thread to
+ * successfully call pmic_audio_open() to gain access.
+ *
+ * Note that we will shutdown/reset the Voice CODEC or Stereo DAC as well as
+ * any associated audio input/output components that are no longer required.
+ *
+ * Also note that this function should only be called with the mutex already
+ * acquired.
+ *
+ * @param   handle          Device handle from pmic_audio_open() call.
+ *
+ * @retval      PMIC_SUCCESS         If the close request was successful.
+ * @retval      PMIC_PARAMETER_ERROR If the handle is invalid.
+ */
+static PMIC_STATUS pmic_audio_close_handle(const PMIC_AUDIO_HANDLE handle)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+       /* Match up the handle to the audio device and then close it. */
+       if ((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE)) {
+               /* Also shutdown the Stereo DAC hardware. The simplest way to
+                * do this is to simply call pmic_audio_reset_device() which will
+                * restore the ST_DAC register to it's initial power-on state.
+                *
+                * This will also shutdown the audio output section if no one
+                * else is still using it.
+                */
+               rc = pmic_audio_reset_device(stDAC.handle);
+
+               if (rc == PMIC_SUCCESS) {
+                       stDAC.handle = AUDIO_HANDLE_NULL;
+                       stDAC.handleState = HANDLE_FREE;
+               }
+       } else if ((handle == vCodec.handle) &&
+                  (vCodec.handleState == HANDLE_IN_USE)) {
+               /* Also shutdown the Voice CODEC and audio input hardware. The
+                * simplest way to do this is to simply call pmic_audio_reset_device()
+                * which will restore the AUD_CODEC register to it's initial
+                * power-on state.
+                *
+                * This will also shutdown the audio output section if no one
+                * else is still using it.
+                */
+               rc = pmic_audio_reset_device(vCodec.handle);
+               if (rc == PMIC_SUCCESS) {
+                       vCodec.handle = AUDIO_HANDLE_NULL;
+                       vCodec.handleState = HANDLE_FREE;
+               }
+       } else if ((handle == extStereoIn.handle) &&
+                  (extStereoIn.handleState == HANDLE_IN_USE)) {
+
+               /* Call pmic_audio_reset_device() here to shutdown the audio output
+                * section if no one else is still using it.
+                */
+               rc = pmic_audio_reset_device(extStereoIn.handle);
+
+               if (rc == PMIC_SUCCESS) {
+                       extStereoIn.handle = AUDIO_HANDLE_NULL;
+                       extStereoIn.handleState = HANDLE_FREE;
+               }
+       }
+
+       return rc;
+}
+
+/*!
+ * @brief Reset the selected audio hardware control registers to their
+ *        power on state.
+ *
+ * This resets all of the audio hardware control registers currently
+ * associated with the device handle back to their power on states. For
+ * example, if the handle is associated with the Stereo DAC and a
+ * specific output port and output amplifiers, then this function will
+ * reset all of those components to their initial power on state.
+ *
+ * This function can only be called if the mutex has already been acquired.
+ *
+ * @param   handle          Device handle from pmic_audio_open() call.
+ *
+ * @retval      PMIC_SUCCESS         If the reset operation was successful.
+ * @retval      PMIC_PARAMETER_ERROR If the handle is invalid.
+ * @retval      PMIC_ERROR           If the reset was unsuccessful.
+ */
+static PMIC_STATUS pmic_audio_reset_device(const PMIC_AUDIO_HANDLE handle)
+{
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       unsigned int reg_mask = 0;
+
+       if ((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE)) {
+               /* Also shutdown the audio output section if nobody else is using it.
+                  if ((vCodec.handleState == HANDLE_FREE) &&
+                  (extStereoIn.handleState == HANDLE_FREE))
+                  {
+                  pmic_write_reg(REG_RX_AUD_AMPS, RESET_RX_AUD_AMPS,
+                  REG_FULLMASK);
+                  } */
+
+               rc = pmic_write_reg(REG_AUDIO_STEREO_DAC,
+                                   RESET_ST_DAC, REG_FULLMASK);
+
+               if (rc == PMIC_SUCCESS) {
+                       rc = pmic_write_reg(REG_AUDIO_SSI_NETWORK,
+                                           RESET_SSI_NETWORK,
+                                           REG_SSI_STDAC_MASK);
+                       if (rc == PMIC_SUCCESS) {
+                               /* Also reset the driver state information to match. Note that we
+                                * keep the device handle and event callback settings unchanged
+                                * since these don't affect the actual hardware and we rely on
+                                * the user to explicitly close the handle or deregister callbacks
+                                */
+                               stDAC.busID = AUDIO_DATA_BUS_1;
+                               stDAC.protocol = NORMAL_MSB_JUSTIFIED_MODE;
+                               stDAC.protocol_set = false;
+                               stDAC.masterSlave = BUS_MASTER_MODE;
+                               stDAC.numSlots = USE_2_TIMESLOTS;
+                               stDAC.clockIn = CLOCK_IN_CLIA;
+                               stDAC.samplingRate = STDAC_RATE_44_1_KHZ;
+                               stDAC.clockFreq = STDAC_CLI_13MHZ;
+                               stDAC.invert = NO_INVERT;
+                               stDAC.timeslot = USE_TS0_TS1;
+                               stDAC.config = (PMIC_AUDIO_STDAC_CONFIG) 0;
+
+                       }
+               }
+       } else if ((handle == vCodec.handle)
+                  && (vCodec.handleState == HANDLE_IN_USE)) {
+               /* Disable the audio input section when disabling the Voice CODEC. */
+               pmic_write_reg(REG_AUDIO_TX, RESET_AUDIO_TX, REG_FULLMASK);
+
+               rc = pmic_write_reg(REG_AUDIO_CODEC,
+                                   RESET_AUD_CODEC, REG_FULLMASK);
+
+               if (rc == PMIC_SUCCESS) {
+                       rc = pmic_write_reg(REG_AUDIO_SSI_NETWORK,
+                                           RESET_SSI_NETWORK,
+                                           REG_SSI_VCODEC_MASK);
+                       if (rc == PMIC_SUCCESS) {
+
+                               /* Also reset the driver state information to match. Note that we
+                                * keep the device handle and event callback settings unchanged
+                                * since these don't affect the actual hardware and we rely on
+                                * the user to explicitly close the handle or deregister callbacks
+                                */
+                               vCodec.busID = AUDIO_DATA_BUS_2;
+                               vCodec.protocol = NETWORK_MODE;
+                               vCodec.protocol_set = false;
+                               vCodec.masterSlave = BUS_SLAVE_MODE;
+                               vCodec.numSlots = USE_4_TIMESLOTS;
+                               vCodec.clockIn = CLOCK_IN_CLIB;
+                               vCodec.samplingRate = VCODEC_RATE_8_KHZ;
+                               vCodec.clockFreq = VCODEC_CLI_13MHZ;
+                               vCodec.invert = NO_INVERT;
+                               vCodec.timeslot = USE_TS0;
+                               vCodec.config =
+                                   INPUT_HIGHPASS_FILTER |
+                                   OUTPUT_HIGHPASS_FILTER;
+
+                       }
+               }
+
+       } else if ((handle == extStereoIn.handle) &&
+                  (extStereoIn.handleState == HANDLE_IN_USE)) {
+               /* Disable the Ext stereo Amplifier and disable it as analog mixer input */
+               reg_mask = SET_BITS(regAUDIO_RX_1, ARXINEN, 1);
+               pmic_write_reg(REG_AUDIO_RX_1, 0, reg_mask);
+
+               reg_mask = SET_BITS(regAUDIO_RX_0, ADDRXIN, 1);
+               pmic_write_reg(REG_AUDIO_RX_0, 0, reg_mask);
+
+               /* We don't need to reset any other registers for this case. */
+               rc = PMIC_SUCCESS;
+       }
+
+       return rc;
+}
+
+/*!
+ * @brief Deregister the callback function and event mask currently associated
+ *        with an audio device handle.
+ *
+ * This function deregisters any existing callback function and event mask for
+ * the given audio device handle. This is done by either calling the
+ * pmic_audio_clear_callback() API or by closing the device handle.
+ *
+ * Note that this function should only be called with the mutex already
+ * acquired. We will also acquire the spinlock here to prevent possible
+ * race conditions with the interrupt handler.
+ *
+ * @param[in]   callback            The current event callback function pointer.
+ * @param[in]   eventMask           The current audio event mask.
+ *
+ * @retval      PMIC_SUCCESS         If the callback function and event mask
+ *                                   were both successfully deregistered.
+ * @retval      PMIC_ERROR           If either the callback function or the
+ *                                   event mask was not successfully
+ *                                   deregistered.
+ */
+
+static PMIC_STATUS pmic_audio_deregister(void *callback,
+                                        PMIC_AUDIO_EVENTS * const eventMask)
+{
+       unsigned long flags;
+       pmic_event_callback_t eventNotify;
+       PMIC_STATUS rc = PMIC_SUCCESS;
+
+       /* Deregister each of the PMIC events that we had previously
+        * registered for by calling pmic_event_subscribe().
+        */
+       if (*eventMask & (HEADSET_DETECTED)) {
+               /* We need to deregister for the A1 amplifier interrupt. */
+               eventNotify.func = callback;
+               eventNotify.param = (void *)(CORE_EVENT_HSDETI);
+               if (pmic_event_unsubscribe(EVENT_HSDETI, eventNotify) ==
+                   PMIC_SUCCESS) {
+                       *eventMask &= ~(HEADSET_DETECTED);
+                       pr_debug("Deregistered for EVENT_HSDETI\n");
+               } else {
+                       rc = PMIC_ERROR;
+               }
+       }
+
+       if (*eventMask & (HEADSET_STEREO)) {
+               /* We need to deregister for the A1 amplifier interrupt. */
+               eventNotify.func = callback;
+               eventNotify.param = (void *)(CORE_EVENT_HSLI);
+               if (pmic_event_unsubscribe(EVENT_HSLI, eventNotify) ==
+                   PMIC_SUCCESS) {
+                       *eventMask &= ~(HEADSET_STEREO);
+                       pr_debug("Deregistered for EVENT_HSLI\n");
+               } else {
+                       rc = PMIC_ERROR;
+               }
+       }
+       if (*eventMask & (HEADSET_THERMAL_SHUTDOWN)) {
+               /* We need to deregister for the A1 amplifier interrupt. */
+               eventNotify.func = callback;
+               eventNotify.param = (void *)(CORE_EVENT_ALSPTHI);
+               if (pmic_event_unsubscribe(EVENT_ALSPTHI, eventNotify) ==
+                   PMIC_SUCCESS) {
+                       *eventMask &= ~(HEADSET_THERMAL_SHUTDOWN);
+                       pr_debug("Deregistered for EVENT_ALSPTHI\n");
+               } else {
+                       rc = PMIC_ERROR;
+               }
+       }
+       if (*eventMask & (HEADSET_SHORT_CIRCUIT)) {
+               /* We need to deregister for the A1 amplifier interrupt. */
+               eventNotify.func = callback;
+               eventNotify.param = (void *)(CORE_EVENT_AHSSHORTI);
+               if (pmic_event_unsubscribe(EVENT_AHSSHORTI, eventNotify) ==
+                   PMIC_SUCCESS) {
+                       *eventMask &= ~(HEADSET_SHORT_CIRCUIT);
+                       pr_debug("Deregistered for EVENT_AHSSHORTI\n");
+               } else {
+                       rc = PMIC_ERROR;
+               }
+       }
+
+       if (rc == PMIC_SUCCESS) {
+               /* We need to grab the spinlock here to create a critical section to
+                * avoid any possible race conditions with the interrupt handler
+                */
+               spin_lock_irqsave(&lock, flags);
+
+               /* Restore the initial reset values for the callback function
+                * and event mask parameters. This should be NULL and zero,
+                * respectively.
+                */
+               callback = NULL;
+               *eventMask = 0;
+
+               /* Exit the critical section. */
+               spin_unlock_irqrestore(&lock, flags);
+       }
+
+       return rc;
+}
+
+/*!
+ * @brief enable/disable fm output.
+ *
+ * @param[in]   enable            true to enable false to disable
+ */
+PMIC_STATUS pmic_audio_fm_output_enable(bool enable)
+{
+       unsigned int reg_mask = 0;
+       unsigned int reg_write = 0;
+       PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+       if (enable) {
+               pmic_audio_antipop_enable(ANTI_POP_RAMP_FAST);
+               reg_mask |= SET_BITS(regAUDIO_RX_0, AHSLEN, 1);
+               reg_write |= SET_BITS(regAUDIO_RX_0, AHSLEN, 1);
+               reg_mask |= SET_BITS(regAUDIO_RX_0, AHSREN, 1);
+               reg_write |= SET_BITS(regAUDIO_RX_0, AHSREN, 1);
+
+               reg_mask |= SET_BITS(regAUDIO_RX_0, AHSSEL, 1);
+               reg_write |= SET_BITS(regAUDIO_RX_0, AHSSEL, 1);
+
+               reg_mask |= SET_BITS(regAUDIO_RX_0, ADDRXIN, 1);
+               reg_write |= SET_BITS(regAUDIO_RX_0, ADDRXIN, 1);
+
+               reg_mask |= SET_BITS(regAUDIO_RX_0, HSPGDIS, 1);
+               reg_write |= SET_BITS(regAUDIO_RX_0, HSPGDIS, 0);
+       } else {
+               reg_mask |= SET_BITS(regAUDIO_RX_0, ADDRXIN, 1);
+               reg_write |= SET_BITS(regAUDIO_RX_0, ADDRXIN, 0);
+       }
+       rc = pmic_write_reg(REG_AUDIO_RX_0, reg_write, reg_mask);
+       if (rc != PMIC_SUCCESS)
+               return rc;
+       if (enable) {
+               reg_mask = SET_BITS(regAUDIO_RX_1, ARXINEN, 1);
+               reg_write = SET_BITS(regAUDIO_RX_1, ARXINEN, 1);
+       } else {
+               reg_mask = SET_BITS(regAUDIO_RX_1, ARXINEN, 1);
+               reg_write = SET_BITS(regAUDIO_RX_1, ARXINEN, 0);
+       }
+       rc = pmic_write_reg(REG_AUDIO_RX_1, reg_write, reg_mask);
+       return rc;
+}
+
+/*@}*/
+
+/**************************************************************************
+ * Module initialization and termination functions.
+ *
+ * Note that if this code is compiled into the kernel, then the
+ * module_init() function will be called within the device_initcall()
+ * group.
+ **************************************************************************
+ */
+
+/*!
+ * @name Audio Driver Loading/Unloading Functions
+ * These non-exported internal functions are used to support the audio
+ * device driver initialization and de-initialization operations.
+ */
+/*@{*/
+
+/*!
+ * @brief This is the audio device driver initialization function.
+ *
+ * This function is called by the kernel when this device driver is first
+ * loaded.
+ */
+static int __init mc13783_pmic_audio_init(void)
+{
+       printk(KERN_INFO "PMIC Audio driver loading...\n");
+
+       return 0;
+}
+
+/*!
+ * @brief This is the audio device driver de-initialization function.
+ *
+ * This function is called by the kernel when this device driver is about
+ * to be unloaded.
+ */
+static void __exit mc13783_pmic_audio_exit(void)
+{
+       printk(KERN_INFO "PMIC Audio driver unloading...\n");
+
+       /* Close all device handles that are still open. This will also
+        * deregister any callbacks that may still be active.
+        */
+       if (stDAC.handleState == HANDLE_IN_USE) {
+               pmic_audio_close(stDAC.handle);
+       }
+       if (vCodec.handleState == HANDLE_IN_USE) {
+               pmic_audio_close(vCodec.handle);
+       }
+       if (extStereoIn.handleState == HANDLE_IN_USE) {
+               pmic_audio_close(extStereoIn.handle);
+       }
+
+       /* Explicitly reset all of the audio registers so that there is no
+        * possibility of leaving the  audio hardware in a state
+        * where it can cause problems if there is no device driver loaded.
+        */
+       pmic_write_reg(REG_AUDIO_STEREO_DAC, RESET_ST_DAC, REG_FULLMASK);
+       pmic_write_reg(REG_AUDIO_CODEC, RESET_AUD_CODEC, REG_FULLMASK);
+       pmic_write_reg(REG_AUDIO_TX, RESET_AUDIO_TX, REG_FULLMASK);
+       pmic_write_reg(REG_AUDIO_SSI_NETWORK, RESET_SSI_NETWORK, REG_FULLMASK);
+       pmic_write_reg(REG_AUDIO_RX_0, RESET_AUDIO_RX_0, REG_FULLMASK);
+       pmic_write_reg(REG_AUDIO_RX_1, RESET_AUDIO_RX_1, REG_FULLMASK);
+}
+
+/*@}*/
+
+/*
+ * Module entry points and description information.
+ */
+
+module_init(mc13783_pmic_audio_init);
+module_exit(mc13783_pmic_audio_exit);
+
+MODULE_DESCRIPTION("PMIC - mc13783 ADC driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pmic/mc13783/pmic_battery.c b/drivers/mxc/pmic/mc13783/pmic_battery.c
new file mode 100644 (file)
index 0000000..3e9d141
--- /dev/null
@@ -0,0 +1,1220 @@
+/*
+ * 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 mc13783/pmic_battery.c
+ * @brief This is the main file of PMIC(mc13783) Battery driver.
+ *
+ * @ingroup PMIC_BATTERY
+ */
+
+/*
+ * Includes
+ */
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+
+#include <linux/pmic_battery.h>
+#include <linux/pmic_adc.h>
+#include <linux/pmic_status.h>
+
+#include "pmic_battery_defs.h"
+
+#include <mach/pmic_power.h>
+#ifdef CONFIG_MXC_HWEVENT
+#include <mach/hw_events.h>
+#endif
+
+static int pmic_battery_major;
+
+/*!
+ * Number of users waiting in suspendq
+ */
+static int swait;
+
+/*!
+ * To indicate whether any of the battery devices are suspending
+ */
+static int suspend_flag;
+
+/*!
+ * The suspendq is used to block application calls
+ */
+static wait_queue_head_t suspendq;
+
+static struct class *pmic_battery_class;
+
+/* EXPORTED FUNCTIONS */
+EXPORT_SYMBOL(pmic_batt_enable_charger);
+EXPORT_SYMBOL(pmic_batt_disable_charger);
+EXPORT_SYMBOL(pmic_batt_set_charger);
+EXPORT_SYMBOL(pmic_batt_get_charger_setting);
+EXPORT_SYMBOL(pmic_batt_get_charge_current);
+EXPORT_SYMBOL(pmic_batt_enable_eol);
+EXPORT_SYMBOL(pmic_batt_bp_enable_eol);
+EXPORT_SYMBOL(pmic_batt_disable_eol);
+EXPORT_SYMBOL(pmic_batt_set_out_control);
+EXPORT_SYMBOL(pmic_batt_set_threshold);
+EXPORT_SYMBOL(pmic_batt_led_control);
+EXPORT_SYMBOL(pmic_batt_set_reverse_supply);
+EXPORT_SYMBOL(pmic_batt_set_unregulated);
+EXPORT_SYMBOL(pmic_batt_set_5k_pull);
+EXPORT_SYMBOL(pmic_batt_event_subscribe);
+EXPORT_SYMBOL(pmic_batt_event_unsubscribe);
+
+static DECLARE_MUTEX(count_mutex);     /* open count mutex */
+static int open_count;         /* open count for device file */
+
+/*!
+ * Callback function for events, we want on MGN board
+ */
+static void callback_chg_detect(void)
+{
+#ifdef CONFIG_MXC_HWEVENT
+       t_sensor_bits sensor;
+       struct mxc_hw_event event = { HWE_BAT_CHARGER_PLUG, 0 };
+
+       pr_debug("In callback_chg_detect\n");
+
+       /* get sensor values */
+       pmic_get_sensors(&sensor);
+
+       pr_debug("Callback, charger detect:%d\n", sensor.sense_chgdets);
+
+       if (sensor.sense_chgdets)
+               event.args = 1;
+       else
+               event.args = 0;
+       /* send hardware event */
+       hw_event_send(HWE_DEF_PRIORITY, &event);
+#endif
+}
+
+static void callback_low_battery(void)
+{
+#ifdef CONFIG_MXC_HWEVENT
+       struct mxc_hw_event event = { HWE_BAT_BATTERY_LOW, 0 };
+
+       pr_debug("In callback_low_battery\n");
+       /* send hardware event */
+       hw_event_send(HWE_DEF_PRIORITY, &event);
+#endif
+}
+
+static void callback_power_fail(void)
+{
+#ifdef CONFIG_MXC_HWEVENT
+       struct mxc_hw_event event = { HWE_BAT_POWER_FAILED, 0 };
+
+       pr_debug("In callback_power_fail\n");
+       /* send hardware event */
+       hw_event_send(HWE_DEF_PRIORITY, &event);
+#endif
+}
+
+static void callback_chg_overvoltage(void)
+{
+#ifdef CONFIG_MXC_HWEVENT
+       struct mxc_hw_event event = { HWE_BAT_CHARGER_OVERVOLTAGE, 0 };
+
+       pr_debug("In callback_chg_overvoltage\n");
+       /* send hardware event */
+       hw_event_send(HWE_DEF_PRIORITY, &event);
+#endif
+}
+
+static void callback_chg_full(void)
+{
+#ifdef CONFIG_MXC_HWEVENT
+       t_sensor_bits sensor;
+       struct mxc_hw_event event = { HWE_BAT_CHARGER_FULL, 0 };
+
+       pr_debug("In callback_chg_full\n");
+
+       /* disable charge function */
+       pmic_batt_disable_charger(BATT_MAIN_CHGR);
+
+       /* get charger sensor */
+       pmic_get_sensors(&sensor);
+
+       /* if did not detect the charger */
+       if (sensor.sense_chgdets)
+               return;
+       /* send hardware event */
+       hw_event_send(HWE_DEF_PRIORITY, &event);
+#endif
+}
+
+/*!
+ * This is the suspend of power management for the pmic battery API.
+ * It suports SAVE and POWER_DOWN state.
+ *
+ * @param        pdev           the device
+ * @param        state          the state
+ *
+ * @return       This function returns 0 if successful.
+ */
+static int pmic_battery_suspend(struct platform_device *pdev,
+                               pm_message_t state)
+{
+       unsigned int reg_value = 0;
+
+       suspend_flag = 1;
+       CHECK_ERROR(pmic_write_reg(REG_CHARGER, reg_value, PMIC_ALL_BITS));
+
+       return 0;
+};
+
+/*!
+ * This is the resume of power management for the pmic battery API.
+ * It suports RESTORE state.
+ *
+ * @param        pdev           the device
+ *
+ * @return       This function returns 0 if successful.
+ */
+static int pmic_battery_resume(struct platform_device *pdev)
+{
+       suspend_flag = 0;
+       while (swait > 0) {
+               swait--;
+               wake_up_interruptible(&suspendq);
+       }
+
+       return 0;
+};
+
+/*!
+ * This function is used to start charging a battery. For different charger,
+ * different voltage and current range are supported. \n
+ *
+ *
+ * @param      chgr        Charger as defined in \b t_batt_charger.
+ * @param      c_voltage   Charging voltage.
+ * @param      c_current   Charging current.
+ *
+ * @return     This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_enable_charger(t_batt_charger chgr,
+                                    unsigned char c_voltage,
+                                    unsigned char c_current)
+{
+       unsigned int val, mask, reg;
+
+       val = 0;
+       mask = 0;
+       reg = 0;
+
+       if (suspend_flag == 1)
+               return PMIC_ERROR;
+
+       switch (chgr) {
+       case BATT_MAIN_CHGR:
+               val = BITFVAL(MC13783_BATT_DAC_DAC, c_current) |
+                   BITFVAL(MC13783_BATT_DAC_V_DAC, c_voltage);
+               mask = BITFMASK(MC13783_BATT_DAC_DAC) |
+                   BITFMASK(MC13783_BATT_DAC_V_DAC);
+               reg = REG_CHARGER;
+               break;
+
+       case BATT_CELL_CHGR:
+               val = BITFVAL(MC13783_BATT_DAC_V_COIN, c_voltage) |
+                   BITFVAL(MC13783_BATT_DAC_COIN_CH_EN,
+                           MC13783_BATT_DAC_COIN_CH_EN_ENABLED);
+               mask = BITFMASK(MC13783_BATT_DAC_V_COIN) |
+                   BITFMASK(MC13783_BATT_DAC_COIN_CH_EN);
+               reg = REG_POWER_CONTROL_0;
+               break;
+
+       case BATT_TRCKLE_CHGR:
+               val = BITFVAL(MC13783_BATT_DAC_TRCKLE, c_current);
+               mask = BITFMASK(MC13783_BATT_DAC_TRCKLE);
+               reg = REG_CHARGER;
+               break;
+
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_write_reg(reg, val, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function turns off a charger.
+ *
+ * @param      chgr        Charger as defined in \b t_batt_charger.
+ *
+ * @return     This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_disable_charger(t_batt_charger chgr)
+{
+       unsigned int val, mask, reg;
+
+       val = 0;
+       mask = 0;
+       reg = 0;
+
+       if (suspend_flag == 1)
+               return PMIC_ERROR;
+       switch (chgr) {
+       case BATT_MAIN_CHGR:
+               val = BITFVAL(MC13783_BATT_DAC_DAC, 0) |
+                   BITFVAL(MC13783_BATT_DAC_V_DAC, 0);
+               mask = BITFMASK(MC13783_BATT_DAC_DAC) |
+                   BITFMASK(MC13783_BATT_DAC_V_DAC);
+               reg = REG_CHARGER;
+               break;
+
+       case BATT_CELL_CHGR:
+               val = BITFVAL(MC13783_BATT_DAC_COIN_CH_EN,
+                             MC13783_BATT_DAC_COIN_CH_EN_DISABLED);
+               mask = BITFMASK(MC13783_BATT_DAC_COIN_CH_EN);
+               reg = REG_POWER_CONTROL_0;
+               break;
+
+       case BATT_TRCKLE_CHGR:
+               val = BITFVAL(MC13783_BATT_DAC_TRCKLE, 0);
+               mask = BITFMASK(MC13783_BATT_DAC_TRCKLE);
+               reg = REG_CHARGER;
+               break;
+
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_write_reg(reg, val, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function is used to change the charger setting.
+ *
+ * @param      chgr        Charger as defined in \b t_batt_charger.
+ * @param      c_voltage   Charging voltage.
+ * @param      c_current   Charging current.
+ *
+ * @return     This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_set_charger(t_batt_charger chgr,
+                                 unsigned char c_voltage,
+                                 unsigned char c_current)
+{
+       unsigned int val, mask, reg;
+
+       val = 0;
+       mask = 0;
+       reg = 0;
+
+       if (suspend_flag == 1)
+               return PMIC_ERROR;
+
+       switch (chgr) {
+       case BATT_MAIN_CHGR:
+               val = BITFVAL(MC13783_BATT_DAC_DAC, c_current) |
+                   BITFVAL(MC13783_BATT_DAC_V_DAC, c_voltage);
+               mask = BITFMASK(MC13783_BATT_DAC_DAC) |
+                   BITFMASK(MC13783_BATT_DAC_V_DAC);
+               reg = REG_CHARGER;
+               break;
+
+       case BATT_CELL_CHGR:
+               val = BITFVAL(MC13783_BATT_DAC_V_COIN, c_voltage);
+               mask = BITFMASK(MC13783_BATT_DAC_V_COIN);
+               reg = REG_POWER_CONTROL_0;
+               break;
+
+       case BATT_TRCKLE_CHGR:
+               val = BITFVAL(MC13783_BATT_DAC_TRCKLE, c_current);
+               mask = BITFMASK(MC13783_BATT_DAC_TRCKLE);
+               reg = REG_CHARGER;
+               break;
+
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_write_reg(reg, val, mask));
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function is used to retrive the charger setting.
+ *
+ * @param      chgr        Charger as defined in \b t_batt_charger.
+ * @param      c_voltage   Output parameter for charging voltage setting.
+ * @param      c_current   Output parameter for charging current setting.
+ *
+ * @return     This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_get_charger_setting(t_batt_charger chgr,
+                                         unsigned char *c_voltage,
+                                         unsigned char *c_current)
+{
+       unsigned int val, reg;
+
+       reg = 0;
+
+       if (suspend_flag == 1)
+               return PMIC_ERROR;
+
+       switch (chgr) {
+       case BATT_MAIN_CHGR:
+       case BATT_TRCKLE_CHGR:
+               reg = REG_CHARGER;
+               break;
+       case BATT_CELL_CHGR:
+               reg = REG_POWER_CONTROL_0;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_read_reg(reg, &val, PMIC_ALL_BITS));
+
+       switch (chgr) {
+       case BATT_MAIN_CHGR:
+               *c_voltage = BITFEXT(val, MC13783_BATT_DAC_V_DAC);;
+               *c_current = BITFEXT(val, MC13783_BATT_DAC_DAC);
+               break;
+
+       case BATT_CELL_CHGR:
+               *c_voltage = BITFEXT(val, MC13783_BATT_DAC_V_COIN);
+               *c_current = 0;
+               break;
+
+       case BATT_TRCKLE_CHGR:
+               *c_voltage = 0;
+               *c_current = BITFEXT(val, MC13783_BATT_DAC_TRCKLE);
+               break;
+
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function is retrives the main battery voltage.
+ *
+ * @param      b_voltage   Output parameter for voltage setting.
+ *
+ * @return     This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_get_batt_voltage(unsigned short *b_voltage)
+{
+       t_channel channel;
+       unsigned short result[8];
+
+       if (suspend_flag == 1)
+               return PMIC_ERROR;
+       channel = BATTERY_VOLTAGE;
+       CHECK_ERROR(pmic_adc_convert(channel, result));
+       *b_voltage = result[0];
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function is retrives the main battery current.
+ *
+ * @param      b_current   Output parameter for current setting.
+ *
+ * @return     This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_get_batt_current(unsigned short *b_current)
+{
+       t_channel channel;
+       unsigned short result[8];
+
+       if (suspend_flag == 1)
+               return PMIC_ERROR;
+
+       channel = BATTERY_CURRENT;
+       CHECK_ERROR(pmic_adc_convert(channel, result));
+       *b_current = result[0];
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function is retrives the main battery temperature.
+ *
+ * @param      b_temper   Output parameter for temperature setting.
+ *
+ * @return     This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_get_batt_temperature(unsigned short *b_temper)
+{
+       t_channel channel;
+       unsigned short result[8];
+
+       if (suspend_flag == 1)
+               return PMIC_ERROR;
+
+       channel = GEN_PURPOSE_AD5;
+       CHECK_ERROR(pmic_adc_convert(channel, result));
+       *b_temper = result[0];
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function is retrives the main battery charging voltage.
+ *
+ * @param      c_voltage   Output parameter for charging voltage setting.
+ *
+ * @return     This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_get_charge_voltage(unsigned short *c_voltage)
+{
+       t_channel channel;
+       unsigned short result[8];
+
+       if (suspend_flag == 1)
+               return PMIC_ERROR;
+
+       channel = CHARGE_VOLTAGE;
+       CHECK_ERROR(pmic_adc_convert(channel, result));
+       *c_voltage = result[0];
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function is retrives the main battery charging current.
+ *
+ * @param      c_current   Output parameter for charging current setting.
+ *
+ * @return     This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_get_charge_current(unsigned short *c_current)
+{
+       t_channel channel;
+       unsigned short result[8];
+
+       if (suspend_flag == 1)
+               return PMIC_ERROR;
+
+       channel = CHARGE_CURRENT;
+       CHECK_ERROR(pmic_adc_convert(channel, result));
+       *c_current = result[0];
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function enables End-of-Life comparator. Not supported on
+ * mc13783. Use pmic_batt_bp_enable_eol function.
+ *
+ * @param      threshold  End-of-Life threshold.
+ *
+ * @return     This function returns PMIC_UNSUPPORTED
+ */
+PMIC_STATUS pmic_batt_enable_eol(unsigned char threshold)
+{
+       return PMIC_NOT_SUPPORTED;
+}
+
+/*!
+ * This function enables End-of-Life comparator.
+ *
+ * @param      typical  Falling Edge Threshold threshold.
+ *                     @verbatim
+                       BPDET   UVDET   LOBATL
+                       ____    _____   ___________
+                       0       2.6     UVDET + 0.2
+                       1       2.6     UVDET + 0.3
+                       2       2.6     UVDET + 0.4
+                       3       2.6     UVDET + 0.5
+ *
+ * @return     This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_bp_enable_eol(t_bp_threshold typical)
+{
+       unsigned int val, mask;
+
+       if (suspend_flag == 1)
+               return PMIC_ERROR;
+
+       val = BITFVAL(MC13783_BATT_DAC_EOL_CMP_EN,
+                     MC13783_BATT_DAC_EOL_CMP_EN_ENABLE) |
+           BITFVAL(MC13783_BATT_DAC_EOL_SEL, typical);
+       mask = BITFMASK(MC13783_BATT_DAC_EOL_CMP_EN) |
+           BITFMASK(MC13783_BATT_DAC_EOL_SEL);
+
+       CHECK_ERROR(pmic_write_reg(REG_POWER_CONTROL_0, val, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function disables End-of-Life comparator.
+ *
+ * @return     This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_disable_eol(void)
+{
+       unsigned int val, mask;
+
+       if (suspend_flag == 1)
+               return PMIC_ERROR;
+
+       val = BITFVAL(MC13783_BATT_DAC_EOL_CMP_EN,
+                     MC13783_BATT_DAC_EOL_CMP_EN_DISABLE);
+       mask = BITFMASK(MC13783_BATT_DAC_EOL_CMP_EN);
+
+       CHECK_ERROR(pmic_write_reg(REG_POWER_CONTROL_0, val, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets the output controls.
+ * It sets the FETOVRD and FETCTRL bits of mc13783
+ *
+ * @param        control        type of control.
+ *
+ * @return       This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_batt_set_out_control(t_control control)
+{
+       unsigned int val, mask;
+       if (suspend_flag == 1)
+               return PMIC_ERROR;
+
+       switch (control) {
+       case CONTROL_HARDWARE:
+               val = BITFVAL(MC13783_BATT_DAC_FETOVRD_EN, 0) |
+                   BITFVAL(MC13783_BATT_DAC_FETCTRL_EN, 0);
+               mask = BITFMASK(MC13783_BATT_DAC_FETOVRD_EN) |
+                   BITFMASK(MC13783_BATT_DAC_FETCTRL_EN);
+               break;
+       case CONTROL_BPFET_LOW:
+               val = BITFVAL(MC13783_BATT_DAC_FETOVRD_EN, 1) |
+                   BITFVAL(MC13783_BATT_DAC_FETCTRL_EN, 0);
+               mask = BITFMASK(MC13783_BATT_DAC_FETOVRD_EN) |
+                   BITFMASK(MC13783_BATT_DAC_FETCTRL_EN);
+               break;
+       case CONTROL_BPFET_HIGH:
+               val = BITFVAL(MC13783_BATT_DAC_FETOVRD_EN, 1) |
+                   BITFVAL(MC13783_BATT_DAC_FETCTRL_EN, 1);
+               mask = BITFMASK(MC13783_BATT_DAC_FETOVRD_EN) |
+                   BITFMASK(MC13783_BATT_DAC_FETCTRL_EN);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_write_reg(REG_CHARGER, val, mask));
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets over voltage threshold.
+ *
+ * @param        threshold      value of over voltage threshold.
+ *
+ * @return       This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_batt_set_threshold(int threshold)
+{
+       unsigned int val, mask;
+
+       if (suspend_flag == 1)
+               return PMIC_ERROR;
+
+       if (threshold > BAT_THRESHOLD_MAX)
+               return PMIC_PARAMETER_ERROR;
+
+       val = BITFVAL(MC13783_BATT_DAC_OVCTRL, threshold);
+       mask = BITFMASK(MC13783_BATT_DAC_OVCTRL);
+       CHECK_ERROR(pmic_write_reg(REG_CHARGER, val, mask));
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function controls charge LED.
+ *
+ * @param      on   If on is ture, LED will be turned on,
+ *                  or otherwise, LED will be turned off.
+ *
+ * @return     This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_led_control(bool on)
+{
+       unsigned val, mask;
+
+       if (suspend_flag == 1)
+               return PMIC_ERROR;
+
+       val = BITFVAL(MC13783_BATT_DAC_LED_EN, on);
+       mask = BITFMASK(MC13783_BATT_DAC_LED_EN);
+
+       CHECK_ERROR(pmic_write_reg(REG_CHARGER, val, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets reverse supply mode.
+ *
+ * @param      enable     If enable is ture, reverse supply mode is enable,
+ *                        or otherwise, reverse supply mode is disabled.
+ *
+ * @return     This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_set_reverse_supply(bool enable)
+{
+       unsigned val, mask;
+
+       if (suspend_flag == 1)
+               return PMIC_ERROR;
+
+       val = BITFVAL(MC13783_BATT_DAC_REVERSE_SUPPLY, enable);
+       mask = BITFMASK(MC13783_BATT_DAC_REVERSE_SUPPLY);
+
+       CHECK_ERROR(pmic_write_reg(REG_CHARGER, val, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets unregulatored charging mode on main battery.
+ *
+ * @param      enable     If enable is ture, unregulated charging mode is
+ *                        enable, or otherwise, disabled.
+ *
+ * @return     This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_set_unregulated(bool enable)
+{
+       unsigned val, mask;
+
+       if (suspend_flag == 1)
+               return PMIC_ERROR;
+
+       val = BITFVAL(MC13783_BATT_DAC_UNREGULATED, enable);
+       mask = BITFMASK(MC13783_BATT_DAC_UNREGULATED);
+
+       CHECK_ERROR(pmic_write_reg(REG_CHARGER, val, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets a 5K pull down at CHRGRAW.
+ * To be used in the dual path charging configuration.
+ *
+ * @param      enable     If enable is true, 5k pull down is
+ *                        enable, or otherwise, disabled.
+ *
+ * @return     This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_set_5k_pull(bool enable)
+{
+       unsigned val, mask;
+
+       if (suspend_flag == 1)
+               return PMIC_ERROR;
+
+       val = BITFVAL(MC13783_BATT_DAC_5K, enable);
+       mask = BITFMASK(MC13783_BATT_DAC_5K);
+
+       CHECK_ERROR(pmic_write_reg(REG_CHARGER, val, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function is used to un/subscribe on battery event IT.
+ *
+ * @param        event          type of event.
+ * @param        callback       event callback function.
+ * @param        sub            define if Un/subscribe event.
+ *
+ * @return       This function returns 0 if successful.
+ */
+PMIC_STATUS mc13783_battery_event(t_batt_event event, void *callback, bool sub)
+{
+       pmic_event_callback_t bat_callback;
+       type_event bat_event;
+
+       bat_callback.func = callback;
+       bat_callback.param = NULL;
+       switch (event) {
+       case BAT_IT_CHG_DET:
+               bat_event = EVENT_CHGDETI;
+               break;
+       case BAT_IT_CHG_OVERVOLT:
+               bat_event = EVENT_CHGOVI;
+               break;
+       case BAT_IT_CHG_REVERSE:
+               bat_event = EVENT_CHGREVI;
+               break;
+       case BAT_IT_CHG_SHORT_CIRCUIT:
+               bat_event = EVENT_CHGSHORTI;
+               break;
+       case BAT_IT_CCCV:
+               bat_event = EVENT_CCCVI;
+               break;
+       case BAT_IT_BELOW_THRESHOLD:
+               bat_event = EVENT_CHRGCURRI;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+       if (sub == true) {
+               CHECK_ERROR(pmic_event_subscribe(bat_event, bat_callback));
+       } else {
+               CHECK_ERROR(pmic_event_unsubscribe(bat_event, bat_callback));
+       }
+       return 0;
+}
+
+/*!
+ * This function is used to subscribe on battery event IT.
+ *
+ * @param        event          type of event.
+ * @param        callback       event callback function.
+ *
+ * @return       This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_batt_event_subscribe(t_batt_event event, void *callback)
+{
+       if (suspend_flag == 1)
+               return PMIC_ERROR;
+
+       return mc13783_battery_event(event, callback, true);
+}
+
+/*!
+ * This function is used to un subscribe on battery event IT.
+ *
+ * @param        event          type of event.
+ * @param        callback       event callback function.
+ *
+ * @return       This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_batt_event_unsubscribe(t_batt_event event, void *callback)
+{
+       if (suspend_flag == 1)
+               return PMIC_ERROR;
+
+       return mc13783_battery_event(event, callback, false);
+}
+
+/*!
+ * This function implements IOCTL controls on a PMIC Battery device.
+ *
+ * @param        inode       pointer on the node
+ * @param        file        pointer on the file
+ * @param        cmd         the command
+ * @param        arg         the parameter
+ * @return       This function returns 0 if successful.
+ */
+static int pmic_battery_ioctl(struct inode *inode, struct file *file,
+                             unsigned int cmd, unsigned long arg)
+{
+       t_charger_setting *chgr_setting = NULL;
+       unsigned short c_current;
+       unsigned int bc_info;
+       t_eol_setting *eol_setting;
+
+       if (_IOC_TYPE(cmd) != 'p')
+               return -ENOTTY;
+
+       chgr_setting = kmalloc(sizeof(t_charger_setting), GFP_KERNEL);
+       eol_setting = kmalloc(sizeof(t_eol_setting), GFP_KERNEL);
+       switch (cmd) {
+       case PMIC_BATT_CHARGER_CONTROL:
+               if (chgr_setting == NULL)
+                       return -ENOMEM;
+
+               if (copy_from_user(chgr_setting, (t_charger_setting *) arg,
+                                  sizeof(t_charger_setting))) {
+                       kfree(chgr_setting);
+                       return -EFAULT;
+               }
+
+               if (chgr_setting->on != false) {
+                       CHECK_ERROR_KFREE(pmic_batt_enable_charger
+                                         (chgr_setting->chgr,
+                                          chgr_setting->c_voltage,
+                                          chgr_setting->c_current),
+                                         (kfree(chgr_setting)));
+               } else {
+                       CHECK_ERROR(pmic_batt_disable_charger
+                                   (chgr_setting->chgr));
+               }
+
+               kfree(chgr_setting);
+               break;
+
+       case PMIC_BATT_SET_CHARGER:
+               if (chgr_setting == NULL)
+                       return -ENOMEM;
+
+               if (copy_from_user(chgr_setting, (t_charger_setting *) arg,
+                                  sizeof(t_charger_setting))) {
+                       kfree(chgr_setting);
+                       return -EFAULT;
+               }
+
+               CHECK_ERROR_KFREE(pmic_batt_set_charger(chgr_setting->chgr,
+                                                       chgr_setting->c_voltage,
+                                                       chgr_setting->
+                                                       c_current),
+                                 (kfree(chgr_setting)));
+
+               kfree(chgr_setting);
+               break;
+
+       case PMIC_BATT_GET_CHARGER:
+               if (chgr_setting == NULL)
+                       return -ENOMEM;
+
+               if (copy_from_user(chgr_setting, (t_charger_setting *) arg,
+                                  sizeof(t_charger_setting))) {
+                       kfree(chgr_setting);
+                       return -EFAULT;
+               }
+
+               CHECK_ERROR_KFREE(pmic_batt_get_charger_setting
+                                 (chgr_setting->chgr, &chgr_setting->c_voltage,
+                                  &chgr_setting->c_current),
+                                 (kfree(chgr_setting)));
+               if (copy_to_user
+                   ((t_charger_setting *) arg, chgr_setting,
+                    sizeof(t_charger_setting))) {
+                       return -EFAULT;
+               }
+
+               kfree(chgr_setting);
+               break;
+
+       case PMIC_BATT_GET_CHARGER_SENSOR:
+               {
+                       t_sensor_bits sensor;
+                       pmic_get_sensors(&sensor);
+                       if (copy_to_user
+                           ((unsigned int *)arg, &sensor.sense_chgdets,
+                            sizeof(unsigned int)))
+                               return -EFAULT;
+
+                       break;
+               }
+       case PMIC_BATT_GET_BATTERY_VOLTAGE:
+               CHECK_ERROR(pmic_batt_get_batt_voltage(&c_current));
+               bc_info = (unsigned int)c_current * 2300 / 1023 + 2400;
+               if (copy_to_user((unsigned int *)arg, &bc_info,
+                                sizeof(unsigned int)))
+                       return -EFAULT;
+
+               break;
+
+       case PMIC_BATT_GET_BATTERY_CURRENT:
+               CHECK_ERROR(pmic_batt_get_batt_current(&c_current));
+               bc_info = (unsigned int)c_current * 5750 / 1023;
+               if (copy_to_user((unsigned int *)arg, &bc_info,
+                                sizeof(unsigned int)))
+                       return -EFAULT;
+               break;
+
+       case PMIC_BATT_GET_BATTERY_TEMPERATURE:
+               CHECK_ERROR(pmic_batt_get_batt_temperature(&c_current));
+               bc_info = (unsigned int)c_current;
+               if (copy_to_user((unsigned int *)arg, &bc_info,
+                                sizeof(unsigned int)))
+                       return -EFAULT;
+
+               break;
+
+       case PMIC_BATT_GET_CHARGER_VOLTAGE:
+               CHECK_ERROR(pmic_batt_get_charge_voltage(&c_current));
+               bc_info = (unsigned int)c_current * 23000 / 1023;
+               if (copy_to_user((unsigned int *)arg, &bc_info,
+                                sizeof(unsigned int)))
+                       return -EFAULT;
+
+               break;
+
+       case PMIC_BATT_GET_CHARGER_CURRENT:
+               CHECK_ERROR(pmic_batt_get_charge_current(&c_current));
+               bc_info = (unsigned int)c_current * 5750 / 1023;
+               if (copy_to_user((unsigned int *)arg, &bc_info,
+                                sizeof(unsigned int)))
+                       return -EFAULT;
+
+               break;
+
+       case PMIC_BATT_EOL_CONTROL:
+               if (eol_setting == NULL)
+                       return -ENOMEM;
+
+               if (copy_from_user(eol_setting, (t_eol_setting *) arg,
+                                  sizeof(t_eol_setting))) {
+                       kfree(eol_setting);
+                       return -EFAULT;
+               }
+
+               if (eol_setting->enable != false) {
+                       CHECK_ERROR_KFREE(pmic_batt_bp_enable_eol
+                                         (eol_setting->typical),
+                                         (kfree(chgr_setting)));
+               } else {
+                       CHECK_ERROR_KFREE(pmic_batt_disable_eol(),
+                                         (kfree(chgr_setting)));
+               }
+
+               kfree(eol_setting);
+               break;
+
+       case PMIC_BATT_SET_OUT_CONTROL:
+               CHECK_ERROR(pmic_batt_set_out_control((t_control) arg));
+               break;
+
+       case PMIC_BATT_SET_THRESHOLD:
+               CHECK_ERROR(pmic_batt_set_threshold((int)arg));
+               break;
+
+       case PMIC_BATT_LED_CONTROL:
+               CHECK_ERROR(pmic_batt_led_control((bool) arg));
+               break;
+
+       case PMIC_BATT_REV_SUPP_CONTROL:
+               CHECK_ERROR(pmic_batt_set_reverse_supply((bool) arg));
+               break;
+
+       case PMIC_BATT_UNREG_CONTROL:
+               CHECK_ERROR(pmic_batt_set_unregulated((bool) arg));
+               break;
+
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+/*!
+ * This function implements the open method on a Pmic battery device.
+ *
+ * @param        inode       pointer on the node
+ * @param        file        pointer on the file
+ * @return       This function returns 0.
+ */
+static int pmic_battery_open(struct inode *inode, struct file *file)
+{
+       while (suspend_flag == 1) {
+               swait++;
+               /* Block if the device is suspended */
+               if (wait_event_interruptible(suspendq, (suspend_flag == 0))) {
+                       return -ERESTARTSYS;
+               }
+       }
+
+       /* check open count, if open firstly, register callbacks */
+       down(&count_mutex);
+       if (open_count++ > 0) {
+               up(&count_mutex);
+               return 0;
+       }
+
+       pr_debug("Subscribe the callbacks\n");
+       /* register battery event callback */
+       if (pmic_batt_event_subscribe(BAT_IT_CHG_DET, callback_chg_detect)) {
+               pr_debug("Failed to subscribe the charger detect callback\n");
+               goto event_err1;
+       }
+       if (pmic_power_event_sub(PWR_IT_LOBATLI, callback_power_fail)) {
+               pr_debug("Failed to subscribe the power failed callback\n");
+               goto event_err2;
+       }
+       if (pmic_power_event_sub(PWR_IT_LOBATHI, callback_low_battery)) {
+               pr_debug("Failed to subscribe the low battery callback\n");
+               goto event_err3;
+       }
+       if (pmic_batt_event_subscribe
+           (BAT_IT_CHG_OVERVOLT, callback_chg_overvoltage)) {
+               pr_debug("Failed to subscribe the low battery callback\n");
+               goto event_err4;
+       }
+       if (pmic_batt_event_subscribe
+           (BAT_IT_BELOW_THRESHOLD, callback_chg_full)) {
+               pr_debug("Failed to subscribe the charge full callback\n");
+               goto event_err5;
+       }
+
+       up(&count_mutex);
+
+       return 0;
+
+       /* un-subscribe the event callbacks */
+event_err5:
+       pmic_batt_event_unsubscribe(BAT_IT_CHG_OVERVOLT,
+                                   callback_chg_overvoltage);
+event_err4:
+       pmic_power_event_unsub(PWR_IT_LOBATHI, callback_low_battery);
+event_err3:
+       pmic_power_event_unsub(PWR_IT_LOBATLI, callback_power_fail);
+event_err2:
+       pmic_batt_event_unsubscribe(BAT_IT_CHG_DET, callback_chg_detect);
+event_err1:
+
+       open_count--;
+       up(&count_mutex);
+
+       return -EFAULT;
+
+}
+
+/*!
+ * This function implements the release method on a Pmic battery device.
+ *
+ * @param        inode       pointer on the node
+ * @param        file        pointer on the file
+ * @return       This function returns 0.
+ */
+static int pmic_battery_release(struct inode *inode, struct file *file)
+{
+       while (suspend_flag == 1) {
+               swait++;
+               /* Block if the device is suspended */
+               if (wait_event_interruptible(suspendq, (suspend_flag == 0))) {
+                       return -ERESTARTSYS;
+               }
+       }
+
+       /* check open count, if open firstly, register callbacks */
+       down(&count_mutex);
+       if (--open_count == 0) {
+               /* unregister these event callback */
+               pr_debug("Unsubscribe the callbacks\n");
+               pmic_batt_event_unsubscribe(BAT_IT_BELOW_THRESHOLD,
+                                           callback_chg_full);
+               pmic_batt_event_unsubscribe(BAT_IT_CHG_OVERVOLT,
+                                           callback_chg_overvoltage);
+               pmic_power_event_unsub(PWR_IT_LOBATHI, callback_low_battery);
+               pmic_power_event_unsub(PWR_IT_LOBATLI, callback_power_fail);
+               pmic_batt_event_unsubscribe(BAT_IT_CHG_DET,
+                                           callback_chg_detect);
+       }
+       up(&count_mutex);
+
+       return 0;
+}
+
+static struct file_operations pmic_battery_fops = {
+       .owner = THIS_MODULE,
+       .ioctl = pmic_battery_ioctl,
+       .open = pmic_battery_open,
+       .release = pmic_battery_release,
+};
+
+static int pmic_battery_remove(struct platform_device *pdev)
+{
+       device_destroy(pmic_battery_class, MKDEV(pmic_battery_major, 0));
+       class_destroy(pmic_battery_class);
+       unregister_chrdev(pmic_battery_major, PMIC_BATTERY_STRING);
+       return 0;
+}
+
+static int pmic_battery_probe(struct platform_device *pdev)
+{
+       int ret = 0;
+       struct device *temp_class;
+
+       pmic_battery_major = register_chrdev(0, PMIC_BATTERY_STRING,
+                                            &pmic_battery_fops);
+
+       if (pmic_battery_major < 0) {
+               printk(KERN_ERR "Unable to get a major for pmic_battery\n");
+               return pmic_battery_major;
+       }
+       init_waitqueue_head(&suspendq);
+
+       pmic_battery_class = class_create(THIS_MODULE, PMIC_BATTERY_STRING);
+       if (IS_ERR(pmic_battery_class)) {
+               printk(KERN_ERR "Error creating PMIC battery class.\n");
+               ret = PTR_ERR(pmic_battery_class);
+               goto err_out1;
+       }
+
+       temp_class = device_create(pmic_battery_class, NULL,
+                                  MKDEV(pmic_battery_major, 0), NULL,
+                                  PMIC_BATTERY_STRING);
+       if (IS_ERR(temp_class)) {
+               printk(KERN_ERR "Error creating PMIC battery class device.\n");
+               ret = PTR_ERR(temp_class);
+               goto err_out2;
+       }
+
+       pmic_batt_led_control(true);
+       pmic_batt_set_5k_pull(true);
+
+       printk(KERN_INFO "PMIC Battery successfully probed\n");
+
+       return ret;
+
+      err_out2:
+       class_destroy(pmic_battery_class);
+      err_out1:
+       unregister_chrdev(pmic_battery_major, PMIC_BATTERY_STRING);
+       return ret;
+}
+
+static struct platform_driver pmic_battery_driver_ldm = {
+       .driver = {
+                  .name = "pmic_battery",
+                  .bus = &platform_bus_type,
+                  },
+       .suspend = pmic_battery_suspend,
+       .resume = pmic_battery_resume,
+       .probe = pmic_battery_probe,
+       .remove = pmic_battery_remove,
+};
+
+/*
+ * Init and Exit
+ */
+
+static int __init pmic_battery_init(void)
+{
+       pr_debug("PMIC Battery driver loading...\n");
+       return platform_driver_register(&pmic_battery_driver_ldm);
+}
+
+static void __exit pmic_battery_exit(void)
+{
+       platform_driver_unregister(&pmic_battery_driver_ldm);
+       pr_debug("PMIC Battery driver successfully unloaded\n");
+}
+
+/*
+ * Module entry points
+ */
+
+module_init(pmic_battery_init);
+module_exit(pmic_battery_exit);
+
+MODULE_DESCRIPTION("pmic_battery driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pmic/mc13783/pmic_battery_defs.h b/drivers/mxc/pmic/mc13783/pmic_battery_defs.h
new file mode 100644 (file)
index 0000000..93742c5
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * 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 mc13783/pmic_battery_defs.h
+ * @brief This is the internal header for PMIC(mc13783) Battery driver.
+ *
+ * @ingroup PMIC_BATTERY
+ */
+
+#ifndef __PMIC_BATTERY_DEFS_H__
+#define __PMIC_BATTERY_DEFS_H__
+
+#define         PMIC_BATTERY_STRING    "pmic_battery"
+
+/* REG_CHARGE */
+#define MC13783_BATT_DAC_V_DAC_LSH             0
+#define MC13783_BATT_DAC_V_DAC_WID             3
+#define MC13783_BATT_DAC_DAC_LSH                       3
+#define MC13783_BATT_DAC_DAC_WID                       4
+#define        MC13783_BATT_DAC_TRCKLE_LSH             7
+#define        MC13783_BATT_DAC_TRCKLE_WID             3
+#define MC13783_BATT_DAC_FETOVRD_EN_LSH                10
+#define MC13783_BATT_DAC_FETOVRD_EN_WID                1
+#define MC13783_BATT_DAC_FETCTRL_EN_LSH                11
+#define MC13783_BATT_DAC_FETCTRL_EN_WID                1
+#define MC13783_BATT_DAC_REVERSE_SUPPLY_LSH    13
+#define MC13783_BATT_DAC_REVERSE_SUPPLY_WID    1
+#define MC13783_BATT_DAC_OVCTRL_LSH            15
+#define MC13783_BATT_DAC_OVCTRL_WID            2
+#define MC13783_BATT_DAC_UNREGULATED_LSH               17
+#define MC13783_BATT_DAC_UNREGULATED_WID               1
+#define MC13783_BATT_DAC_LED_EN_LSH            18
+#define MC13783_BATT_DAC_LED_EN_WID            1
+#define MC13783_BATT_DAC_5K_LSH                        19
+#define MC13783_BATT_DAC_5K_WID                        1
+
+#define         BITS_OUT_VOLTAGE        0
+#define         LONG_OUT_VOLTAGE        3
+#define         BITS_CURRENT_MAIN       3
+#define         LONG_CURRENT_MAIN       4
+#define         BITS_CURRENT_TRICKLE    7
+#define         LONG_CURRENT_TRICKLE    3
+#define         BIT_FETOVRD             10
+#define         BIT_FETCTRL             11
+#define         BIT_RVRSMODE            13
+#define         BITS_OVERVOLTAGE        15
+#define         LONG_OVERVOLTAGE        2
+#define         BIT_UNREGULATED         17
+#define         BIT_CHRG_LED            18
+#define         BIT_CHRGRAWPDEN         19
+
+/* REG_POWXER_CONTROL_0 */
+#define MC13783_BATT_DAC_V_COIN_LSH            20
+#define MC13783_BATT_DAC_V_COIN_WID            3
+#define MC13783_BATT_DAC_COIN_CH_EN_LSH                23
+#define MC13783_BATT_DAC_COIN_CH_EN_WID                1
+#define MC13783_BATT_DAC_COIN_CH_EN_ENABLED    1
+#define MC13783_BATT_DAC_COIN_CH_EN_DISABLED   0
+#define MC13783_BATT_DAC_EOL_CMP_EN_LSH                18
+#define MC13783_BATT_DAC_EOL_CMP_EN_WID                1
+#define MC13783_BATT_DAC_EOL_CMP_EN_ENABLE     1
+#define MC13783_BATT_DAC_EOL_CMP_EN_DISABLE    0
+#define MC13783_BATT_DAC_EOL_SEL_LSH           16
+#define MC13783_BATT_DAC_EOL_SEL_WID           2
+
+#define         DEF_VALUE               0
+
+#define         BAT_THRESHOLD_MAX       3
+
+#endif                         /*  __PMIC_BATTERY_DEFS_H__ */
diff --git a/drivers/mxc/pmic/mc13783/pmic_convity.c b/drivers/mxc/pmic/mc13783/pmic_convity.c
new file mode 100644 (file)
index 0000000..5c963bd
--- /dev/null
@@ -0,0 +1,2468 @@
+/*
+ * 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 mc13783/pmic_convity.c
+ * @brief Implementation of the PMIC Connectivity driver APIs.
+ *
+ * The PMIC connectivity device driver and this API were developed to support
+ * the external connectivity capabilities of several power management ICs that
+ * are available from Freescale Semiconductor, Inc.
+ *
+ * The following operating modes, in terms of external connectivity, are
+ * supported:
+ *
+ * @verbatim
+       Operating Mode     mc13783
+       ---------------    -------
+       USB (incl. OTG)     Yes
+       RS-232              Yes
+       CEA-936             Yes
+
+   @endverbatim
+ *
+ * @ingroup PMIC_CONNECTIVITY
+ */
+
+#include <linux/interrupt.h>   /* For tasklet interface.                  */
+#include <linux/platform_device.h>     /* For kernel module interface.            */
+#include <linux/spinlock.h>    /* For spinlock interface.                 */
+#include <linux/pmic_adc.h>    /* For PMIC ADC driver interface.          */
+#include <linux/pmic_status.h>
+#include <mach/pmic_convity.h> /* For PMIC Connectivity driver interface. */
+
+/*
+ * mc13783 Connectivity API
+ */
+/* EXPORTED FUNCTIONS */
+EXPORT_SYMBOL(pmic_convity_open);
+EXPORT_SYMBOL(pmic_convity_close);
+EXPORT_SYMBOL(pmic_convity_set_mode);
+EXPORT_SYMBOL(pmic_convity_get_mode);
+EXPORT_SYMBOL(pmic_convity_reset);
+EXPORT_SYMBOL(pmic_convity_set_callback);
+EXPORT_SYMBOL(pmic_convity_clear_callback);
+EXPORT_SYMBOL(pmic_convity_get_callback);
+EXPORT_SYMBOL(pmic_convity_usb_set_speed);
+EXPORT_SYMBOL(pmic_convity_usb_get_speed);
+EXPORT_SYMBOL(pmic_convity_usb_set_power_source);
+EXPORT_SYMBOL(pmic_convity_usb_get_power_source);
+EXPORT_SYMBOL(pmic_convity_usb_set_xcvr);
+EXPORT_SYMBOL(pmic_convity_usb_get_xcvr);
+EXPORT_SYMBOL(pmic_convity_usb_otg_set_dlp_duration);
+EXPORT_SYMBOL(pmic_convity_usb_otg_get_dlp_duration);
+EXPORT_SYMBOL(pmic_convity_usb_otg_set_config);
+EXPORT_SYMBOL(pmic_convity_usb_otg_clear_config);
+EXPORT_SYMBOL(pmic_convity_usb_otg_get_config);
+EXPORT_SYMBOL(pmic_convity_set_output);
+EXPORT_SYMBOL(pmic_convity_rs232_set_config);
+EXPORT_SYMBOL(pmic_convity_rs232_get_config);
+EXPORT_SYMBOL(pmic_convity_cea936_exit_signal);
+
+/*! @def SET_BITS
+ * Set a register field to a given value.
+ */
+
+#define SET_BITS(reg, field, value)    (((value) << reg.field.offset) & \
+               reg.field.mask)
+
+/*! @def GET_BITS
+ * Get the current value of a given register field.
+ */
+#define GET_BITS(reg, value)    (((value) & reg.mask) >> \
+               reg.offset)
+
+/*!
+ * @brief Define the possible states for a device handle.
+ *
+ * This enumeration is used to track the current state of each device handle.
+ */
+typedef enum {
+       HANDLE_FREE,            /*!< Handle is available for use. */
+       HANDLE_IN_USE           /*!< Handle is currently in use.  */
+} HANDLE_STATE;
+
+/*
+ * This structure is used to define a specific hardware register field.
+ *
+ * All hardware register fields are defined using an offset to the LSB
+ * and a mask. The offset is used to right shift a register value before
+ * applying the mask to actually obtain the value of the field.
+ */
+typedef struct {
+       const unsigned char offset;     /* Offset of LSB of register field.           */
+       const unsigned int mask;        /* Mask value used to isolate register field. */
+} REGFIELD;
+
+/*!
+ * @brief This structure is used to identify the fields in the USBCNTRL_REG_0 hardware register.
+ *
+ * This structure lists all of the fields within the USBCNTRL_REG_0 hardware
+ * register.
+ */
+typedef struct {
+       REGFIELD FSENB;         /*!< USB Full Speed Enable                            */
+       REGFIELD USB_SUSPEND;   /*!< USB Suspend Mode Enable                          */
+       REGFIELD USB_PU;        /*!< USB Pullup Enable                                */
+       REGFIELD UDP_PD;        /*!< USB Data Plus Pulldown Enable                    */
+       REGFIELD UDM_PD;        /*!< USB 150K UDP Pullup Enable                       */
+       REGFIELD DP150K_PU;     /*!< USB Pullup/Pulldown Override Enable              */
+       REGFIELD VBUSPDENB;     /*!< USB VBUS Pulldown NMOS Switch Enable             */
+       REGFIELD CURRENT_LIMIT; /*!< USB Regulator Current Limit Setting-3 bits       */
+       REGFIELD DLP_SRP;       /*!< USB Data Line Pulsing Timer Enable               */
+       REGFIELD SE0_CONN;      /*!< USB Pullup Connect When SE0 Detected             */
+       REGFIELD USBXCVREN;     /*!< USB Transceiver Enabled When INTERFACE_MODE[2:0]=000 and RESETB=high */
+       REGFIELD PULLOVR;       /*!< 1K5 Pullup and UDP/UDM Pulldown Disable When UTXENB=Low             */
+       REGFIELD INTERFACE_MODE;        /*!< Connectivity Interface Mode Select-3 Bits        */
+       REGFIELD DATSE0;        /*!< USB Single or Differential Mode Select           */
+       REGFIELD BIDIR;         /*!< USB Unidirectional/Bidirectional Transmission    */
+       REGFIELD USBCNTRL;      /*!< USB Mode of Operation controlled By USBEN/SPI Pin */
+       REGFIELD IDPD;          /*!< USB UID Pulldown Enable                          */
+       REGFIELD IDPULSE;       /*!< USB Pulse to Gnd on UID Line Generated           */
+       REGFIELD IDPUCNTRL;     /*!< USB UID Pin pulled high By 5ua Curr Source       */
+       REGFIELD DMPULSE;       /*!< USB Positive pulse on the UDM Line Generated     */
+} USBCNTRL_REG_0;
+
+/*!
+ * @brief This variable is used to access the USBCNTRL_REG_0 hardware register.
+ *
+ * This variable defines how to access all of the fields within the
+ * USBCNTRL_REG_0  hardware register. The initial values consist of the offset
+ * and mask values needed to access each of the register fields.
+ */
+static const USBCNTRL_REG_0 regUSB0 = {
+       {0, 0x000001},          /*!< FSENB        */
+       {1, 0x000002},          /*!< USB_SUSPEND  */
+       {2, 0x000004},          /*!< USB_PU       */
+       {3, 0x000008},          /*!< UDP_PD       */
+       {4, 0x000010},          /*!< UDM_PD       */
+       {5, 0x000020},          /*!< DP150K_PU    */
+       {6, 0x000040},          /*!< VBUSPDENB    */
+       {7, 0x000380},          /*!< CURRENT_LIMIT */
+       {10, 0x000400},         /*!< DLP_SRP      */
+       {11, 0x000800},         /*!< SE0_CONN     */
+       {12, 0x001000},         /*!< USBXCVREN    */
+       {13, 0x002000},         /*!< PULLOVR      */
+       {14, 0x01c000},         /*!< INTERFACE_MODE */
+       {17, 0x020000},         /*!< DATSE0       */
+       {18, 0x040000},         /*!< BIDIR        */
+       {19, 0x080000},         /*!< USBCNTRL     */
+       {20, 0x100000},         /*!< IDPD         */
+       {21, 0x200000},         /*!< IDPULSE      */
+       {22, 0x400000},         /*!< IDPUCNTRL    */
+       {23, 0x800000}          /*!< DMPULSE      */
+
+};
+
+/*!
+ * @brief This structure is used to identify the fields in the USBCNTRL_REG_1 hardware register.
+ *
+ * This structure lists all of the fields within the USBCNTRL_REG_1 hardware
+ * register.
+ */
+typedef struct {
+       REGFIELD VUSBIN;        /*!< Controls The Input Source For VUSB             */
+       REGFIELD VUSB;          /*!< VUSB Output Voltage Select-High=3.3V Low=2.775V */
+       REGFIELD VUSBEN;        /*!< VUSB Output Enable-                            */
+       REGFIELD VBUSEN;        /*!< VBUS Output Enable-                            */
+       REGFIELD RSPOL;         /*!< Low=RS232 TX on UDM, RX on UDP
+                                  High= RS232 TX on UDP, RX on UDM               */
+       REGFIELD RSTRI;         /*!< TX Forced To Tristate in RS232 Mode Only       */
+       REGFIELD ID100kPU;      /*!< 100k UID Pullup Enabled                        */
+} USBCNTRL_REG_1;
+
+/*!
+ * @brief This variable is used to access the USBCNTRL_REG_1 hardware register.
+ *
+ * This variable defines how to access all of the fields within the
+ * USBCNTRL_REG_1  hardware register. The initial values consist of the offset
+ * and mask values needed to access each of the register fields.
+ */
+static const USBCNTRL_REG_1 regUSB1 = {
+       {0, 0x000003},          /*!< VUSBIN-2 Bits  */
+       {2, 0x000004},          /*!< VUSB           */
+       {3, 0x000008},          /*!< VUSBEN         */
+       /*{4, 0x000010} *//*!< Reserved       */
+       {5, 0x000020},          /*!< VBUSEN         */
+       {6, 0x000040},          /*!< RSPOL          */
+       {7, 0x000080},          /*!< RSTRI          */
+       {8, 0x000100}           /*!< ID100kPU       */
+       /*!< 9-23 Unused    */
+};
+
+/*! Define a mask to access the entire hardware register. */
+static const unsigned int REG_FULLMASK = 0xffffff;
+
+/*! Define the mc13783 USBCNTRL_REG_0 register power on reset state. */
+static const unsigned int RESET_USBCNTRL_REG_0 = 0x080060;
+
+/*! Define the mc13783 USBCNTRL_REG_1 register power on reset state. */
+static const unsigned int RESET_USBCNTRL_REG_1 = 0x000006;
+
+static pmic_event_callback_t eventNotify;
+
+/*!
+ * @brief This structure is used to maintain the current device driver state.
+ *
+ * This structure maintains the current state of the connectivity driver. This
+ * includes both the PMIC hardware state as well as the device handle and
+ * callback states.
+ */
+
+typedef struct {
+       PMIC_CONVITY_HANDLE handle;     /*!< Device handle.   */
+       HANDLE_STATE handle_state;      /*!< Device handle
+                                          state.           */
+       PMIC_CONVITY_MODE mode; /*!< Device mode.     */
+       PMIC_CONVITY_CALLBACK callback; /*!< Event callback function pointer. */
+       PMIC_CONVITY_EVENTS eventMask;  /*!< Event mask.      */
+       PMIC_CONVITY_USB_SPEED usbSpeed;        /*!< USB connection
+                                                  speed.           */
+       PMIC_CONVITY_USB_MODE usbMode;  /*!< USB connection
+                                          mode.            */
+       PMIC_CONVITY_USB_POWER_IN usbPowerIn;   /*!< USB transceiver
+                                                  power source.    */
+       PMIC_CONVITY_USB_POWER_OUT usbPowerOut; /*!< USB transceiver
+                                                  power output
+                                                  level.           */
+       PMIC_CONVITY_USB_TRANSCEIVER_MODE usbXcvrMode;  /*!< USB transceiver
+                                                          mode.            */
+       unsigned int usbDlpDuration;    /*!< USB Data Line
+                                          Pulsing duration. */
+       PMIC_CONVITY_USB_OTG_CONFIG usbOtgCfg;  /*!< USB OTG
+                                                  configuration
+                                                  options.         */
+       PMIC_CONVITY_RS232_INTERNAL rs232CfgInternal;   /*!< RS-232 internal
+                                                          connections.     */
+       PMIC_CONVITY_RS232_EXTERNAL rs232CfgExternal;   /*!< RS-232 external
+                                                          connections.     */
+} pmic_convity_state_struct;
+
+/*!
+ * @brief This structure is used to maintain the current device driver state.
+ *
+ * This structure maintains the current state of the driver in USB mode. This
+ * includes both the PMIC hardware state as well as the device handle and
+ * callback states.
+ */
+
+typedef struct {
+       PMIC_CONVITY_HANDLE handle;     /*!< Device handle.   */
+       HANDLE_STATE handle_state;      /*!< Device handle
+                                          state.           */
+       PMIC_CONVITY_MODE mode; /*!< Device mode.     */
+       PMIC_CONVITY_CALLBACK callback; /*!< Event callback function pointer. */
+       PMIC_CONVITY_EVENTS eventMask;  /*!< Event mask.      */
+       PMIC_CONVITY_USB_SPEED usbSpeed;        /*!< USB connection
+                                                  speed.           */
+       PMIC_CONVITY_USB_MODE usbMode;  /*!< USB connection
+                                          mode.            */
+       PMIC_CONVITY_USB_POWER_IN usbPowerIn;   /*!< USB transceiver
+                                                  power source.    */
+       PMIC_CONVITY_USB_POWER_OUT usbPowerOut; /*!< USB transceiver
+                                                  power output
+                                                  level.           */
+       PMIC_CONVITY_USB_TRANSCEIVER_MODE usbXcvrMode;  /*!< USB transceiver
+                                                          mode.            */
+       unsigned int usbDlpDuration;    /*!< USB Data Line
+                                          Pulsing duration. */
+       PMIC_CONVITY_USB_OTG_CONFIG usbOtgCfg;  /*!< USB OTG
+                                                  configuration
+                                                  options.         */
+} pmic_convity_usb_state;
+
+/*!
+ * @brief This structure is used to maintain the current device driver state.
+ *
+ * This structure maintains the current state of the driver in RS_232 mode. This
+ * includes both the PMIC hardware state as well as the device handle and
+ * callback states.
+ */
+
+typedef struct {
+       PMIC_CONVITY_HANDLE handle;     /*!< Device handle.   */
+       HANDLE_STATE handle_state;      /*!< Device handle
+                                          state.           */
+       PMIC_CONVITY_MODE mode; /*!< Device mode.     */
+       PMIC_CONVITY_CALLBACK callback; /*!< Event callback function pointer. */
+       PMIC_CONVITY_EVENTS eventMask;  /*!< Event mask.      */
+       PMIC_CONVITY_RS232_INTERNAL rs232CfgInternal;   /*!< RS-232 internal
+                                                          connections.     */
+       PMIC_CONVITY_RS232_EXTERNAL rs232CfgExternal;   /*!< RS-232 external
+                                                          connections.     */
+} pmic_convity_rs232_state;
+
+/*!
+ * @brief This structure is used to maintain the current device driver state.
+ *
+ * This structure maintains the current state of the driver in cea-936 mode. This
+ * includes both the PMIC hardware state as well as the device handle and
+ * callback states.
+ */
+
+typedef struct {
+       PMIC_CONVITY_HANDLE handle;     /*!< Device handle.   */
+       HANDLE_STATE handle_state;      /*!< Device handle
+                                          state.           */
+       PMIC_CONVITY_MODE mode; /*!< Device mode.     */
+       PMIC_CONVITY_CALLBACK callback; /*!< Event callback function pointer. */
+       PMIC_CONVITY_EVENTS eventMask;  /*!< Event mask.      */
+
+} pmic_convity_cea936_state;
+
+/*!
+ * @brief Identifies the hardware interrupt source.
+ *
+ * This enumeration identifies which of the possible hardware interrupt
+ * sources actually caused the current interrupt handler to be called.
+ */
+typedef enum {
+       CORE_EVENT_4V4 = 1,     /*!< Detected USB 4.4 V event.              */
+       CORE_EVENT_2V0 = 2,     /*!< Detected USB 2.0 V event.              */
+       CORE_EVENT_0V8 = 4,     /*!< Detected USB 0.8 V event.              */
+       CORE_EVENT_ABDET = 8    /*!< Detected USB mini A-B connector event. */
+} PMIC_CORE_EVENT;
+
+/*!
+ * @brief This structure defines the reset/power on state for the Connectivity driver.
+ */
+static const pmic_convity_state_struct reset = {
+       0,
+       HANDLE_FREE,
+       USB,
+       NULL,
+       0,
+       USB_FULL_SPEED,
+       USB_PERIPHERAL,
+       USB_POWER_INTERNAL,
+       USB_POWER_3V3,
+       USB_TRANSCEIVER_OFF,
+       0,
+       USB_PULL_OVERRIDE | USB_VBUS_CURRENT_LIMIT_HIGH,
+       RS232_TX_USE0VM_RX_UDATVP,
+       RS232_TX_UDM_RX_UDP
+};
+
+/*!
+ * @brief This structure maintains the current state of the Connectivity driver.
+ *
+ * The initial values must be identical to the reset state defined by the
+ * #reset variable.
+ */
+static pmic_convity_usb_state usb = {
+       0,
+       HANDLE_FREE,
+       USB,
+       NULL,
+       0,
+       USB_FULL_SPEED,
+       USB_PERIPHERAL,
+       USB_POWER_INTERNAL,
+       USB_POWER_3V3,
+       USB_TRANSCEIVER_OFF,
+       0,
+       USB_PULL_OVERRIDE | USB_VBUS_CURRENT_LIMIT_HIGH,
+};
+
+/*!
+ * @brief This structure maintains the current state of the Connectivity driver.
+ *
+ * The initial values must be identical to the reset state defined by the
+ * #reset variable.
+ */
+static pmic_convity_rs232_state rs_232 = {
+       0,
+       HANDLE_FREE,
+       RS232_1,
+       NULL,
+       0,
+       RS232_TX_USE0VM_RX_UDATVP,
+       RS232_TX_UDM_RX_UDP
+};
+
+/*!
+ * @brief This structure maintains the current state of the Connectivity driver.
+ *
+ * The initial values must be identical to the reset state defined by the
+ * #reset variable.
+ */
+static pmic_convity_cea936_state cea_936 = {
+       0,
+       HANDLE_FREE,
+       CEA936_MONO,
+       NULL,
+       0,
+};
+
+/*!
+ * @brief This spinlock is used to provide mutual exclusion.
+ *
+ * Create a spinlock that can be used to provide mutually exclusive
+ * read/write access to the globally accessible "convity" data structure
+ * that was defined above. Mutually exclusive access is required to
+ * ensure that the convity data structure is consistent at all times
+ * when possibly accessed by multiple threads of execution (for example,
+ * while simultaneously handling a user request and an interrupt event).
+ *
+ * We need to use a spinlock sometimes because we need to provide mutual
+ * exclusion while handling a hardware interrupt.
+ */
+static DEFINE_SPINLOCK(lock);
+
+/*!
+ * @brief This mutex is used to provide mutual exclusion.
+ *
+ * Create a mutex that can be used to provide mutually exclusive
+ * read/write access to the globally accessible data structures
+ * that were defined above. Mutually exclusive access is required to
+ * ensure that the Connectivity data structures are consistent at all
+ * times when possibly accessed by multiple threads of execution.
+ *
+ * Note that we use a mutex instead of the spinlock whenever disabling
+ * interrupts while in the critical section is not required. This helps
+ * to minimize kernel interrupt handling latency.
+ */
+static DECLARE_MUTEX(mutex);
+
+/* Prototype for the connectivity driver tasklet function. */
+static void pmic_convity_tasklet(struct work_struct *work);
+
+/*!
+ * @brief Tasklet handler for the connectivity driver.
+ *
+ * Declare a tasklet that will do most of the processing for all of the
+ * connectivity-related interrupt events (USB4.4VI, USB2.0VI, USB0.8VI,
+ * and AB_DETI). Note that we cannot do all of the required processing
+ * within the interrupt handler itself because we may need to call the
+ * ADC driver to measure voltages as well as calling any user-registered
+ * callback functions.
+ */
+DECLARE_WORK(convityTasklet, pmic_convity_tasklet);
+
+/*!
+ * @brief Global variable to track currently active interrupt events.
+ *
+ * This global variable is used to keep track of all of the currently
+ * active interrupt events for the connectivity driver. Note that access
+ * to this variable may occur while within an interrupt context and,
+ * therefore, must be guarded by using a spinlock.
+ */
+static PMIC_CORE_EVENT eventID;
+
+/* Prototypes for all static connectivity driver functions. */
+static PMIC_STATUS pmic_convity_set_mode_internal(const PMIC_CONVITY_MODE mode);
+static PMIC_STATUS pmic_convity_deregister_all(void);
+static void pmic_convity_event_handler(void *param);
+
+/**************************************************************************
+ * General setup and configuration functions.
+ **************************************************************************
+ */
+
+/*!
+ * @name General Setup and Configuration Connectivity APIs
+ * Functions for setting up and configuring the connectivity hardware.
+ */
+/*@{*/
+
+/*!
+ * Attempt to open and gain exclusive access to the PMIC connectivity
+ * hardware. An initial operating mode must also be specified.
+ *
+ * If the open request is successful, then a numeric handle is returned
+ * and this handle must be used in all subsequent function calls. The
+ * same handle must also be used in the pmic_convity_close() call when use
+ * of the PMIC connectivity hardware is no longer required.
+ *
+ * @param       handle          device handle from open() call
+ * @param       mode            initial connectivity operating mode
+ *
+ * @return      PMIC_SUCCESS    if the open request was successful
+ */
+PMIC_STATUS pmic_convity_open(PMIC_CONVITY_HANDLE * const handle,
+                             const PMIC_CONVITY_MODE mode)
+{
+       PMIC_STATUS rc = PMIC_ERROR;
+
+       if (handle == (PMIC_CONVITY_HANDLE *) NULL) {
+               /* Do not dereference a NULL pointer. */
+               return PMIC_ERROR;
+       }
+
+       /* We only need to acquire a mutex here because the interrupt handler
+        * never modifies the device handle or device handle state. Therefore,
+        * we don't need to worry about conflicts with the interrupt handler
+        * or the need to execute in an interrupt context.
+        *
+        * But we do need a critical section here to avoid problems in case
+        * multiple calls to pmic_convity_open() are made since we can only
+        * allow one of them to succeed.
+        */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       /* Check the current device handle state and acquire the handle if
+        * it is available.
+        */
+       if ((usb.handle_state != HANDLE_FREE)
+           && (rs_232.handle_state != HANDLE_FREE)
+           && (cea_936.handle_state != HANDLE_FREE)) {
+
+               /* Cannot open the PMIC connectivity hardware at this time or an invalid
+                * mode was requested.
+                */
+               *handle = reset.handle;
+       } else {
+
+               if (mode == USB) {
+                       usb.handle = (PMIC_CONVITY_HANDLE) (&usb);
+                       usb.handle_state = HANDLE_IN_USE;
+               } else if ((mode == RS232_1) || (mode == RS232_2)) {
+                       rs_232.handle = (PMIC_CONVITY_HANDLE) (&rs_232);
+                       rs_232.handle_state = HANDLE_IN_USE;
+               } else if ((mode == CEA936_STEREO) || (mode == CEA936_MONO)
+                          || (mode == CEA936_TEST_LEFT)
+                          || (mode == CEA936_TEST_RIGHT)) {
+                       cea_936.handle = (PMIC_CONVITY_HANDLE) (&cea_936);
+                       cea_936.handle_state = HANDLE_IN_USE;
+
+               }
+               /* Let's begin by acquiring the connectivity device handle. */
+               /* Then we can try to set the desired operating mode. */
+               rc = pmic_convity_set_mode_internal(mode);
+
+               if (rc == PMIC_SUCCESS) {
+                       /* Successfully set the desired operating mode, now return the
+                        * handle to the caller.
+                        */
+                       if (mode == USB) {
+                               *handle = usb.handle;
+                       } else if ((mode == RS232_1) || (mode == RS232_2)) {
+                               *handle = rs_232.handle;
+                       } else if ((mode == CEA936_STEREO)
+                                  || (mode == CEA936_MONO)
+                                  || (mode == CEA936_TEST_LEFT)
+                                  || (mode == CEA936_TEST_RIGHT)) {
+                               *handle = cea_936.handle;
+                       }
+               } else {
+                       /* Failed to set the desired mode, return the handle to an unused
+                        * state.
+                        */
+                       if (mode == USB) {
+                               usb.handle = reset.handle;
+                               usb.handle_state = reset.handle_state;
+                       } else if ((mode == RS232_1) || (mode == RS232_2)) {
+                               rs_232.handle = reset.handle;
+                               rs_232.handle_state = reset.handle_state;
+                       } else if ((mode == CEA936_STEREO)
+                                  || (mode == CEA936_MONO)
+                                  || (mode == CEA936_TEST_LEFT)
+                                  || (mode == CEA936_TEST_RIGHT)) {
+                               cea_936.handle = reset.handle;
+                               cea_936.handle_state = reset.handle_state;
+                       }
+
+                       *handle = reset.handle;
+               }
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * Terminate further access to the PMIC connectivity hardware. Also allows
+ * another process to call pmic_convity_open() to gain access.
+ *
+ * @param       handle          device handle from open() call
+ *
+ * @return      PMIC_SUCCESS    if the close request was successful
+ */
+PMIC_STATUS pmic_convity_close(const PMIC_CONVITY_HANDLE handle)
+{
+       PMIC_STATUS rc = PMIC_ERROR;
+
+       /* Begin a critical section here to avoid the possibility of race
+        * conditions if multiple threads happen to call this function and
+        * pmic_convity_open() at the same time.
+        */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       /* Confirm that the device handle matches the one assigned in the
+        * pmic_convity_open() call and then close the connection.
+        */
+       if (((handle == usb.handle) &&
+            (usb.handle_state == HANDLE_IN_USE)) || ((handle == rs_232.handle)
+                                                     && (rs_232.handle_state ==
+                                                         HANDLE_IN_USE))
+           || ((handle == cea_936.handle)
+               && (cea_936.handle_state == HANDLE_IN_USE))) {
+               rc = PMIC_SUCCESS;
+
+               /* Deregister for all existing callbacks if necessary and make sure
+                * that the event handling settings are consistent following the
+                * close operation.
+                */
+               if ((usb.callback != reset.callback)
+                   || (rs_232.callback != reset.callback)
+                   || (cea_936.callback != reset.callback)) {
+                       /* Deregister the existing callback function and all registered
+                        * events before we completely close the handle.
+                        */
+                       rc = pmic_convity_deregister_all();
+                       if (rc == PMIC_SUCCESS) {
+
+                       } else if (usb.eventMask != reset.eventMask) {
+                               /* Having a non-zero eventMask without a callback function being
+                                * defined should never occur but let's just make sure here that
+                                * we keep things consistent.
+                                */
+                               usb.eventMask = reset.eventMask;
+                               /* Mark the connectivity device handle as being closed. */
+                               usb.handle = reset.handle;
+                               usb.handle_state = reset.handle_state;
+
+                       } else if (rs_232.eventMask != reset.eventMask) {
+
+                               rs_232.eventMask = reset.eventMask;
+                               /* Mark the connectivity device handle as being closed. */
+                               rs_232.handle = reset.handle;
+                               rs_232.handle_state = reset.handle_state;
+
+                       } else if (cea_936.eventMask != reset.eventMask) {
+                               cea_936.eventMask = reset.eventMask;
+                               /* Mark the connectivity device handle as being closed. */
+                               cea_936.handle = reset.handle;
+                               cea_936.handle_state = reset.handle_state;
+
+                       }
+
+               }
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * Change the current operating mode of the PMIC connectivity hardware.
+ * The available connectivity operating modes is hardware dependent and
+ * consists of one or more of the following: USB (including USB On-the-Go),
+ * RS-232, and CEA-936. Requesting an operating mode that is not supported
+ * by the PMIC hardware will return PMIC_NOT_SUPPORTED.
+ *
+ * @param       handle          device handle from
+                                       open() call
+ * @param       mode            desired operating mode
+ *
+ * @return      PMIC_SUCCESS    if the requested mode was successfully set
+ */
+PMIC_STATUS pmic_convity_set_mode(const PMIC_CONVITY_HANDLE handle,
+                                 const PMIC_CONVITY_MODE mode)
+{
+       PMIC_STATUS rc = PMIC_ERROR;
+
+       /* Use a critical section to maintain a consistent state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if (((handle == usb.handle) &&
+            (usb.handle_state == HANDLE_IN_USE)) || ((handle == rs_232.handle)
+                                                     && (rs_232.handle_state ==
+                                                         HANDLE_IN_USE))
+           || ((handle == cea_936.handle)
+               && (cea_936.handle_state == HANDLE_IN_USE))) {
+               rc = pmic_convity_set_mode_internal(mode);
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * Get the current operating mode for the PMIC connectivity hardware.
+ *
+ * @param       handle          device handle from open() call
+ * @param       mode            the current PMIC connectivity operating mode
+ *
+ * @return      PMIC_SUCCESS    if the requested mode was successfully set
+ */
+PMIC_STATUS pmic_convity_get_mode(const PMIC_CONVITY_HANDLE handle,
+                                 PMIC_CONVITY_MODE * const mode)
+{
+       PMIC_STATUS rc = PMIC_ERROR;
+
+       /* Use a critical section to maintain a consistent state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((((handle == usb.handle) &&
+             (usb.handle_state == HANDLE_IN_USE)) || ((handle == rs_232.handle)
+                                                      && (rs_232.
+                                                          handle_state ==
+                                                          HANDLE_IN_USE))
+            || ((handle == cea_936.handle)
+                && (cea_936.handle_state == HANDLE_IN_USE)))
+           && (mode != (PMIC_CONVITY_MODE *) NULL)) {
+
+               *mode = usb.mode;
+
+               rc = PMIC_SUCCESS;
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * Restore all registers to the initial power-on/reset state.
+ *
+ * @param       handle          device handle from open() call
+ *
+ * @return      PMIC_SUCCESS    if the reset was successful
+ */
+PMIC_STATUS pmic_convity_reset(const PMIC_CONVITY_HANDLE handle)
+{
+       PMIC_STATUS rc = PMIC_ERROR;
+
+       /* Use a critical section to maintain a consistent state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+       if (((handle == usb.handle) &&
+            (usb.handle_state == HANDLE_IN_USE)) || ((handle == rs_232.handle)
+                                                     && (rs_232.handle_state ==
+                                                         HANDLE_IN_USE))
+           || ((handle == cea_936.handle)
+               && (cea_936.handle_state == HANDLE_IN_USE))) {
+
+               /* Reset the PMIC Connectivity register to it's power on state. */
+               rc = pmic_write_reg(REG_USB, RESET_USBCNTRL_REG_0,
+                                   REG_FULLMASK);
+
+               rc = pmic_write_reg(REG_CHARGE_USB_SPARE,
+                                   RESET_USBCNTRL_REG_1, REG_FULLMASK);
+
+               if (rc == PMIC_SUCCESS) {
+                       /* Also reset the device driver state data structure. */
+
+               }
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * Register a callback function that will be used to signal PMIC connectivity
+ * events. For example, the USB subsystem should register a callback function
+ * in order to be notified of device connect/disconnect events. Note, however,
+ * that non-USB events may also be signalled depending upon the PMIC hardware
+ * capabilities. Therefore, the callback function must be able to properly
+ * handle all of the possible events if support for non-USB peripherals is
+ * also to be included.
+ *
+ * @param       handle          device handle from open() call
+ * @param       func            a pointer to the callback function
+ * @param       eventMask       a mask selecting events to be notified
+ *
+ * @return      PMIC_SUCCESS    if the callback was successful registered
+ */
+PMIC_STATUS pmic_convity_set_callback(const PMIC_CONVITY_HANDLE handle,
+                                     const PMIC_CONVITY_CALLBACK func,
+                                     const PMIC_CONVITY_EVENTS eventMask)
+{
+       unsigned long flags;
+       PMIC_STATUS rc = PMIC_ERROR;
+
+       /* We need to start a critical section here to ensure a consistent state
+        * in case simultaneous calls to pmic_convity_set_callback() are made. In
+        * that case, we must serialize the calls to ensure that the "callback"
+        * and "eventMask" state variables are always consistent.
+        *
+        * Note that we don't actually need to acquire the spinlock until later
+        * when we are finally ready to update the "callback" and "eventMask"
+        * state variables which are shared with the interrupt handler.
+        */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle == usb.handle) && (usb.handle_state == HANDLE_IN_USE)) {
+
+               /* Return an error if either the callback function or event mask
+                * is not properly defined.
+                *
+                * It is also considered an error if a callback function has already
+                * been defined. If you wish to register for a new set of events,
+                * then you must first call pmic_convity_clear_callback() to
+                * deregister the existing callback function and list of events
+                * before trying to register a new callback function.
+                */
+               if ((func == NULL) || (eventMask == 0)
+                   || (usb.callback != NULL)) {
+                       rc = PMIC_ERROR;
+
+                       /* Register for PMIC events from the core protocol driver. */
+               } else {
+
+                       if ((eventMask & USB_DETECT_4V4_RISE) ||
+                           (eventMask & USB_DETECT_4V4_FALL)) {
+                               /* We need to register for the 4.4V interrupt.
+                                EVENT_USBI or EVENT_USB_44VI */
+                               eventNotify.func = pmic_convity_event_handler;
+                               eventNotify.param = (void *)(CORE_EVENT_4V4);
+                               rc = pmic_event_subscribe(EVENT_USBI,
+                                                         eventNotify);
+
+                               if (rc != PMIC_SUCCESS) {
+                                       return rc;
+                               }
+                       }
+
+                       if ((eventMask & USB_DETECT_2V0_RISE) ||
+                           (eventMask & USB_DETECT_2V0_FALL)) {
+                               /* We need to register for the 2.0V interrupt.
+                                EVENT_USB_20VI or EVENT_USBI */
+                               eventNotify.func = pmic_convity_event_handler;
+                               eventNotify.param = (void *)(CORE_EVENT_2V0);
+                               rc = pmic_event_subscribe(EVENT_USBI,
+                                                         eventNotify);
+
+                               if (rc != PMIC_SUCCESS) {
+                                       goto Cleanup_4V4;
+                               }
+                       }
+
+                       if ((eventMask & USB_DETECT_0V8_RISE) ||
+                           (eventMask & USB_DETECT_0V8_FALL)) {
+                               /* We need to register for the 0.8V interrupt.
+                                EVENT_USB_08VI or EVENT_USBI */
+                               eventNotify.func = pmic_convity_event_handler;
+                               eventNotify.param = (void *)(CORE_EVENT_0V8);
+                               rc = pmic_event_subscribe(EVENT_USBI,
+                                                         eventNotify);
+
+                               if (rc != PMIC_SUCCESS) {
+                                       goto Cleanup_2V0;
+                               }
+                       }
+
+                       if ((eventMask & USB_DETECT_MINI_A) ||
+                           (eventMask & USB_DETECT_MINI_B)
+                           || (eventMask & USB_DETECT_NON_USB_ACCESSORY)
+                           || (eventMask & USB_DETECT_FACTORY_MODE)) {
+                               /* We need to register for the AB_DET interrupt.
+                                EVENT_AB_DETI or EVENT_IDI */
+                               eventNotify.func = pmic_convity_event_handler;
+                               eventNotify.param = (void *)(CORE_EVENT_ABDET);
+                               rc = pmic_event_subscribe(EVENT_IDI,
+                                                         eventNotify);
+
+                               if (rc != PMIC_SUCCESS) {
+                                       goto Cleanup_0V8;
+                               }
+                       }
+
+                       /* Use a critical section to maintain a consistent state. */
+                       spin_lock_irqsave(&lock, flags);
+
+                       /* Successfully registered for all events. */
+                       usb.callback = func;
+                       usb.eventMask = eventMask;
+                       spin_unlock_irqrestore(&lock, flags);
+
+                       goto End;
+
+                       /* This section unregisters any already registered events if we should
+                        * encounter an error partway through the registration process. Note
+                        * that we don't check the return status here since it is already set
+                        * to PMIC_ERROR before we get here.
+                        */
+                     Cleanup_0V8:
+
+                       if ((eventMask & USB_DETECT_0V8_RISE) ||
+                           (eventMask & USB_DETECT_0V8_FALL)) {
+                               /* EVENT_USB_08VI or EVENT_USBI */
+                               eventNotify.func = pmic_convity_event_handler;
+                               eventNotify.param = (void *)(CORE_EVENT_0V8);
+                               pmic_event_unsubscribe(EVENT_USBI, eventNotify);
+                               goto End;
+                       }
+
+                     Cleanup_2V0:
+
+                       if ((eventMask & USB_DETECT_2V0_RISE) ||
+                           (eventMask & USB_DETECT_2V0_FALL)) {
+                               /* EVENT_USB_20VI or EVENT_USBI */
+                               eventNotify.func = pmic_convity_event_handler;
+                               eventNotify.param = (void *)(CORE_EVENT_2V0);
+                               pmic_event_unsubscribe(EVENT_USBI, eventNotify);
+                               goto End;
+                       }
+
+                     Cleanup_4V4:
+
+                       if ((eventMask & USB_DETECT_4V4_RISE) ||
+                           (eventMask & USB_DETECT_4V4_FALL)) {
+                               /* EVENT_USB_44VI or EVENT_USBI */
+                               eventNotify.func = pmic_convity_event_handler;
+                               eventNotify.param = (void *)(CORE_EVENT_4V4);
+                               pmic_event_unsubscribe(EVENT_USBI, eventNotify);
+                       }
+               }
+               /* Exit the critical section. */
+
+       }
+      End:up(&mutex);
+       return rc;
+
+}
+
+/*!
+ * Clears the current callback function. If this function returns successfully
+ * then all future Connectivity events will only be handled by the default
+ * handler within the Connectivity driver.
+ *
+ * @param       handle          device handle from open() call
+ *
+ * @return      PMIC_SUCCESS    if the callback was successful cleared
+ */
+PMIC_STATUS pmic_convity_clear_callback(const PMIC_CONVITY_HANDLE handle)
+{
+       PMIC_STATUS rc = PMIC_ERROR;
+
+       /* Use a critical section to maintain a consistent state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+       if (((handle == usb.handle) &&
+            (usb.handle_state == HANDLE_IN_USE)) || ((handle == rs_232.handle)
+                                                     && (rs_232.handle_state ==
+                                                         HANDLE_IN_USE))
+           || ((handle == cea_936.handle)
+               && (cea_936.handle_state == HANDLE_IN_USE))) {
+
+               rc = pmic_convity_deregister_all();
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * Get the current callback function and event mask.
+ *
+ * @param       handle          device handle from open() call
+ * @param       func            the current callback function
+ * @param       eventMask       the current event selection mask
+ *
+ * @return      PMIC_SUCCESS    if the callback information was successful
+ *                              retrieved
+ */
+PMIC_STATUS pmic_convity_get_callback(const PMIC_CONVITY_HANDLE handle,
+                                     PMIC_CONVITY_CALLBACK * const func,
+                                     PMIC_CONVITY_EVENTS * const eventMask)
+{
+       PMIC_STATUS rc = PMIC_ERROR;
+
+       /* Use a critical section to maintain a consistent state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+       if ((((handle == usb.handle) &&
+             (usb.handle_state == HANDLE_IN_USE)) || ((handle == rs_232.handle)
+                                                      && (rs_232.
+                                                          handle_state ==
+                                                          HANDLE_IN_USE))
+            || ((handle == cea_936.handle)
+                && (cea_936.handle_state == HANDLE_IN_USE)))
+           && (func != (PMIC_CONVITY_CALLBACK *) NULL)
+           && (eventMask != (PMIC_CONVITY_EVENTS *) NULL)) {
+               *func = usb.callback;
+               *eventMask = usb.eventMask;
+
+               rc = PMIC_SUCCESS;
+       }
+
+       /* Exit the critical section. */
+
+       up(&mutex);
+
+       return rc;
+}
+
+/*@*/
+
+/**************************************************************************
+ * USB-specific configuration and setup functions.
+ **************************************************************************
+ */
+
+/*!
+ * @name USB and USB-OTG Connectivity APIs
+ * Functions for controlling USB and USB-OTG connectivity.
+ */
+/*@{*/
+
+/*!
+ * Set the USB transceiver speed.
+ *
+ * @param       handle          device handle from open() call
+ * @param       speed           the desired USB transceiver speed
+ *
+ * @return      PMIC_SUCCESS    if the transceiver speed was successfully set
+ */
+PMIC_STATUS pmic_convity_usb_set_speed(const PMIC_CONVITY_HANDLE handle,
+                                      const PMIC_CONVITY_USB_SPEED speed)
+{
+       PMIC_STATUS rc = PMIC_ERROR;
+       unsigned int reg_value = 0;
+       unsigned int reg_mask = SET_BITS(regUSB0, FSENB, 1);
+
+       /* Use a critical section to maintain a consistent state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if (handle == (rs_232.handle || cea_936.handle)) {
+               return PMIC_PARAMETER_ERROR;
+       } else {
+               if ((handle == usb.handle) &&
+                   (usb.handle_state == HANDLE_IN_USE)) {
+                       /* Validate the function parameters and if they are valid, then
+                        * configure the pull-up and pull-down resistors as required for
+                        * the desired operating mode.
+                        */
+                       if ((speed == USB_HIGH_SPEED)) {
+                               /*
+                                * The USB transceiver also does not support the high speed mode
+                                * (which is also optional under the USB OTG specification).
+                                */
+                               rc = PMIC_NOT_SUPPORTED;
+                       } else if ((speed != USB_LOW_SPEED)
+                                  && (speed != USB_FULL_SPEED)) {
+                               /* Final validity check on the speed parameter. */
+                               rc = PMIC_ERROR;;
+                       } else {
+                               /* First configure the D+ and D- pull-up/pull-down resistors as
+                                * per the USB OTG specification.
+                                */
+                               if (speed == USB_FULL_SPEED) {
+                                       /* Activate pull-up on D+ and pull-down on D-. */
+                                       reg_value =
+                                           SET_BITS(regUSB0, UDM_PD, 1);
+                               } else if (speed == USB_LOW_SPEED) {
+                                       /* Activate pull-up on D+ and pull-down on D-. */
+                                       reg_value = SET_BITS(regUSB0, FSENB, 1);
+                               }
+
+                               /* Now set the desired USB transceiver speed. Note that
+                                * USB_FULL_SPEED simply requires FSENB=0 (which it
+                                * already is).
+                                */
+
+                               rc = pmic_write_reg(REG_USB, reg_value,
+                                                   reg_mask);
+
+                               if (rc == PMIC_SUCCESS) {
+                                       usb.usbSpeed = speed;
+                               }
+                       }
+               }
+       }
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * Get the USB transceiver speed.
+ *
+ * @param       handle          device handle from open() call
+ * @param       speed           the current USB transceiver speed
+ * @param       mode            the current USB transceiver mode
+ *
+ * @return      PMIC_SUCCESS    if the transceiver speed was successfully
+ *                              obtained
+ */
+PMIC_STATUS pmic_convity_usb_get_speed(const PMIC_CONVITY_HANDLE handle,
+                                      PMIC_CONVITY_USB_SPEED * const speed,
+                                      PMIC_CONVITY_USB_MODE * const mode)
+{
+       PMIC_STATUS rc = PMIC_ERROR;
+
+       /* Use a critical section to maintain a consistent state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle == usb.handle) &&
+           (usb.handle_state == HANDLE_IN_USE) &&
+           (speed != (PMIC_CONVITY_USB_SPEED *) NULL) &&
+           (mode != (PMIC_CONVITY_USB_MODE *) NULL)) {
+               *speed = usb.usbSpeed;
+               *mode = usb.usbMode;
+
+               rc = PMIC_SUCCESS;
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * This function enables/disables VUSB and VBUS output.
+ * This API configures the VUSBEN and VBUSEN bits of USB register
+ *
+ * @param       handle          device handle from open() call
+ * @param        out_type true, for VBUS
+ *                        false, for VUSB
+ * @param        out   if true, output is enabled
+ *                      if false, output is disabled
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_convity_set_output(const PMIC_CONVITY_HANDLE handle,
+                                   bool out_type, bool out)
+{
+
+       PMIC_STATUS rc = PMIC_ERROR;
+       unsigned int reg_value = 0;
+
+       unsigned int reg_mask = 0;
+
+       /* Use a critical section to maintain a consistent state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle == usb.handle) && (usb.handle_state == HANDLE_IN_USE)) {
+
+               if ((out_type == 0) && (out == 1)) {
+
+                       reg_value = SET_BITS(regUSB1, VUSBEN, 1);
+                       reg_mask = SET_BITS(regUSB1, VUSBEN, 1);
+
+                       rc = pmic_write_reg(REG_CHARGE_USB_SPARE,
+                                           reg_value, reg_mask);
+               } else if (out_type == 0 && out == 0) {
+                       reg_mask = SET_BITS(regUSB1, VBUSEN, 1);
+
+                       rc = pmic_write_reg(REG_CHARGE_USB_SPARE,
+                                           reg_value, reg_mask);
+               } else if (out_type == 1 && out == 1) {
+
+                       reg_value = SET_BITS(regUSB1, VBUSEN, 1);
+                       reg_mask = SET_BITS(regUSB1, VBUSEN, 1);
+
+                       rc = pmic_write_reg(REG_CHARGE_USB_SPARE,
+                                           reg_value, reg_mask);
+               }
+
+               else if (out_type == 1 && out == 0) {
+
+                       reg_mask = SET_BITS(regUSB1, VBUSEN, 1);
+                       rc = pmic_write_reg(REG_CHARGE_USB_SPARE,
+                                           reg_value, reg_mask);
+               }
+
+               /*else {
+
+                  rc = pmic_write_reg(REG_CHARGE_USB_SPARE,
+                  reg_value, reg_mask);
+                  } */
+       }
+
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * Set the USB transceiver's power supply configuration.
+ *
+ * @param       handle          device handle from open() call
+ * @param       pwrin           USB transceiver regulator input power source
+ * @param       pwrout          USB transceiver regulator output power level
+ *
+ * @return      PMIC_SUCCESS    if the USB transceiver's power supply
+ *                              configuration was successfully set
+ */
+PMIC_STATUS pmic_convity_usb_set_power_source(const PMIC_CONVITY_HANDLE handle,
+                                             const PMIC_CONVITY_USB_POWER_IN
+                                             pwrin,
+                                             const PMIC_CONVITY_USB_POWER_OUT
+                                             pwrout)
+{
+       PMIC_STATUS rc = PMIC_ERROR;
+       unsigned int reg_value = 0;
+       unsigned int reg_mask = 0;
+
+       /* Use a critical section to maintain a consistent state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle == usb.handle) && (usb.handle_state == HANDLE_IN_USE)) {
+
+               if (pwrin == USB_POWER_INTERNAL_BOOST) {
+                       reg_value |= SET_BITS(regUSB1, VUSBIN, 0);
+                       reg_mask = SET_BITS(regUSB1, VUSBIN, 1);
+               } else if (pwrin == USB_POWER_VBUS) {
+                       reg_value |= SET_BITS(regUSB1, VUSBIN, 1);
+                       reg_mask = SET_BITS(regUSB1, VUSBIN, 1);
+               }
+
+               else if (pwrin == USB_POWER_INTERNAL) {
+                       reg_value |= SET_BITS(regUSB1, VUSBIN, 2);
+                       reg_mask = SET_BITS(regUSB1, VUSBIN, 1);
+               }
+
+               if (pwrout == USB_POWER_3V3) {
+                       reg_value |= SET_BITS(regUSB1, VUSB, 1);
+                       reg_mask |= SET_BITS(regUSB1, VUSB, 1);
+               }
+
+               else if (pwrout == USB_POWER_2V775) {
+                       reg_value |= SET_BITS(regUSB1, VUSB, 0);
+                       reg_mask |= SET_BITS(regUSB1, VUSB, 1);
+               }
+               rc = pmic_write_reg(REG_CHARGE_USB_SPARE, reg_value, reg_mask);
+
+               if (rc == PMIC_SUCCESS) {
+                       usb.usbPowerIn = pwrin;
+                       usb.usbPowerOut = pwrout;
+               }
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * Get the USB transceiver's current power supply configuration.
+ *
+ * @param       handle          device handle from open() call
+ * @param       pwrin           USB transceiver regulator input power source
+ * @param       pwrout          USB transceiver regulator output power level
+ *
+ * @return      PMIC_SUCCESS    if the USB transceiver's power supply
+ *                              configuration was successfully retrieved
+ */
+PMIC_STATUS pmic_convity_usb_get_power_source(const PMIC_CONVITY_HANDLE handle,
+                                             PMIC_CONVITY_USB_POWER_IN *
+                                             const pwrin,
+                                             PMIC_CONVITY_USB_POWER_OUT *
+                                             const pwrout)
+{
+       PMIC_STATUS rc = PMIC_ERROR;
+
+       /* Use a critical section to maintain a consistent state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle == usb.handle) &&
+           (usb.handle_state == HANDLE_IN_USE) &&
+           (pwrin != (PMIC_CONVITY_USB_POWER_IN *) NULL) &&
+           (pwrout != (PMIC_CONVITY_USB_POWER_OUT *) NULL)) {
+               *pwrin = usb.usbPowerIn;
+               *pwrout = usb.usbPowerOut;
+
+               rc = PMIC_SUCCESS;
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * Set the USB transceiver's operating mode.
+ *
+ * @param       handle          device handle from open() call
+ * @param       mode            desired operating mode
+ *
+ * @return      PMIC_SUCCESS    if the USB transceiver's operating mode
+ *                              was successfully configured
+ */
+PMIC_STATUS pmic_convity_usb_set_xcvr(const PMIC_CONVITY_HANDLE handle,
+                                     const PMIC_CONVITY_USB_TRANSCEIVER_MODE
+                                     mode)
+{
+       PMIC_STATUS rc = PMIC_ERROR;
+       unsigned int reg_value = 0;
+       unsigned int reg_mask = 0;
+
+       /* Use a critical section to maintain a consistent state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle == usb.handle) && (usb.handle_state == HANDLE_IN_USE)) {
+
+               if (mode == USB_TRANSCEIVER_OFF) {
+                       reg_value = SET_BITS(regUSB0, USBXCVREN, 0);
+                       reg_mask |= SET_BITS(regUSB0, USB_SUSPEND, 1);
+
+                       rc = pmic_write_reg(REG_USB, reg_value, reg_mask);
+
+               }
+
+               if (mode == USB_SINGLE_ENDED_UNIDIR) {
+                       reg_value |=
+                           SET_BITS(regUSB0, DATSE0, 1) | SET_BITS(regUSB0,
+                                                                   BIDIR, 0);
+                       reg_mask |=
+                           SET_BITS(regUSB0, USB_SUSPEND,
+                                    1) | SET_BITS(regUSB0, DATSE0,
+                                                  1) | SET_BITS(regUSB0, BIDIR,
+                                                                1);
+               } else if (mode == USB_SINGLE_ENDED_BIDIR) {
+                       reg_value |=
+                           SET_BITS(regUSB0, DATSE0, 1) | SET_BITS(regUSB0,
+                                                                   BIDIR, 1);
+                       reg_mask |=
+                           SET_BITS(regUSB0, USB_SUSPEND,
+                                    1) | SET_BITS(regUSB0, DATSE0,
+                                                  1) | SET_BITS(regUSB0, BIDIR,
+                                                                1);
+               } else if (mode == USB_DIFFERENTIAL_UNIDIR) {
+                       reg_value |=
+                           SET_BITS(regUSB0, DATSE0, 0) | SET_BITS(regUSB0,
+                                                                   BIDIR, 0);
+                       reg_mask |=
+                           SET_BITS(regUSB0, USB_SUSPEND,
+                                    1) | SET_BITS(regUSB0, DATSE0,
+                                                  1) | SET_BITS(regUSB0, BIDIR,
+                                                                1);
+               } else if (mode == USB_DIFFERENTIAL_BIDIR) {
+                       reg_value |=
+                           SET_BITS(regUSB0, DATSE0, 0) | SET_BITS(regUSB0,
+                                                                   BIDIR, 1);
+                       reg_mask |=
+                           SET_BITS(regUSB0, USB_SUSPEND,
+                                    1) | SET_BITS(regUSB0, DATSE0,
+                                                  1) | SET_BITS(regUSB0, BIDIR,
+                                                                1);
+               }
+
+               if (mode == USB_SUSPEND_ON) {
+                       reg_value |= SET_BITS(regUSB0, USB_SUSPEND, 1);
+                       reg_mask |= SET_BITS(regUSB0, USB_SUSPEND, 1);
+               } else if (mode == USB_SUSPEND_OFF) {
+                       reg_value |= SET_BITS(regUSB0, USB_SUSPEND, 0);
+                       reg_mask |= SET_BITS(regUSB0, USB_SUSPEND, 1);
+               }
+
+               if (mode == USB_OTG_SRP_DLP_START) {
+                       reg_value |= SET_BITS(regUSB0, USB_PU, 0);
+                       reg_mask |= SET_BITS(regUSB0, USB_SUSPEND, 1);
+               } else if (mode == USB_OTG_SRP_DLP_STOP) {
+                       reg_value &= SET_BITS(regUSB0, USB_PU, 1);
+                       reg_mask |= SET_BITS(regUSB0, USB_PU, 1);
+               }
+
+               rc = pmic_write_reg(REG_USB, reg_value, reg_mask);
+
+               if (rc == PMIC_SUCCESS) {
+                       usb.usbXcvrMode = mode;
+               }
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * Get the USB transceiver's current operating mode.
+ *
+ * @param       handle          device handle from open() call
+ * @param       mode            current operating mode
+ *
+ * @return      PMIC_SUCCESS    if the USB transceiver's operating mode
+ *                              was successfully retrieved
+ */
+PMIC_STATUS pmic_convity_usb_get_xcvr(const PMIC_CONVITY_HANDLE handle,
+                                     PMIC_CONVITY_USB_TRANSCEIVER_MODE *
+                                     const mode)
+{
+       PMIC_STATUS rc = PMIC_ERROR;
+
+       /* Use a critical section to maintain a consistent state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle == usb.handle) &&
+           (usb.handle_state == HANDLE_IN_USE) &&
+           (mode != (PMIC_CONVITY_USB_TRANSCEIVER_MODE *) NULL)) {
+               *mode = usb.usbXcvrMode;
+
+               rc = PMIC_SUCCESS;
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * Set the Data Line Pulse duration (in milliseconds) for the USB OTG
+ * Session Request Protocol.
+ *
+ * For mc13783, this feature is not supported.So return PMIC_NOT_SUPPORTED
+ *
+ * @param       handle          device handle from open() call
+ * @param       duration        the data line pulse duration (ms)
+ *
+ * @return      PMIC_SUCCESS    if the pulse duration was successfully set
+ */
+PMIC_STATUS pmic_convity_usb_otg_set_dlp_duration(const PMIC_CONVITY_HANDLE
+                                                 handle,
+                                                 const unsigned int duration)
+{
+       PMIC_STATUS rc = PMIC_NOT_SUPPORTED;
+
+       /* The setting of the dlp duration is not supported by the mc13783 PMIC hardware. */
+
+       /* No critical section is required. */
+
+       if ((handle != usb.handle)
+           || (usb.handle_state != HANDLE_IN_USE)) {
+               /* Must return error indication for invalid handle parameter to be
+                * consistent with other APIs.
+                */
+               rc = PMIC_ERROR;
+       }
+
+       return rc;
+}
+
+/*!
+ * Get the current Data Line Pulse duration (in milliseconds) for the USB
+ * OTG Session Request Protocol.
+ *
+ * @param       handle          device handle from open() call
+ * @param       duration        the data line pulse duration (ms)
+ *
+ * @return      PMIC_SUCCESS    if the pulse duration was successfully obtained
+ */
+PMIC_STATUS pmic_convity_usb_otg_get_dlp_duration(const PMIC_CONVITY_HANDLE
+                                                 handle,
+                                                 unsigned int *const duration)
+{
+       PMIC_STATUS rc = PMIC_NOT_SUPPORTED;
+
+       /* The setting of dlp duration is not supported by the mc13783 PMIC hardware. */
+
+       /* No critical section is required. */
+
+       if ((handle != usb.handle)
+           || (usb.handle_state != HANDLE_IN_USE)) {
+               /* Must return error indication for invalid handle parameter to be
+                * consistent with other APIs.
+                */
+               rc = PMIC_ERROR;
+       }
+
+       return rc;
+}
+
+/*!
+ * Set the USB On-The-Go (OTG) configuration.
+ *
+ * @param       handle          device handle from open() call
+ * @param       cfg             desired USB OTG configuration
+ *
+ * @return      PMIC_SUCCESS    if the OTG configuration was successfully set
+ */
+PMIC_STATUS pmic_convity_usb_otg_set_config(const PMIC_CONVITY_HANDLE handle,
+                                           const PMIC_CONVITY_USB_OTG_CONFIG
+                                           cfg)
+{
+       PMIC_STATUS rc = PMIC_ERROR;
+       unsigned int reg_value = 0;
+       unsigned int reg_mask = 0;
+
+       /* Use a critical section to maintain a consistent state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle == usb.handle) && (usb.handle_state == HANDLE_IN_USE)) {
+               if (cfg & USB_OTG_SE0CONN) {
+                       reg_value = SET_BITS(regUSB0, SE0_CONN, 1);
+                       reg_mask = SET_BITS(regUSB0, SE0_CONN, 1);
+               }
+               if (cfg & USBXCVREN) {
+                       reg_value |= SET_BITS(regUSB0, USBXCVREN, 1);
+                       reg_mask |= SET_BITS(regUSB0, USBXCVREN, 1);
+               }
+
+               if (cfg & USB_OTG_DLP_SRP) {
+                       reg_value |= SET_BITS(regUSB0, DLP_SRP, 1);
+                       reg_mask |= SET_BITS(regUSB0, DLP_SRP, 1);
+               }
+
+               if (cfg & USB_PULL_OVERRIDE) {
+                       reg_value |= SET_BITS(regUSB0, PULLOVR, 1);
+                       reg_mask |= SET_BITS(regUSB0, PULLOVR, 1);
+               }
+
+               if (cfg & USB_PU) {
+                       reg_value |= SET_BITS(regUSB0, USB_PU, 1);
+                       reg_mask |= SET_BITS(regUSB0, USB_PU, 1);
+               }
+
+               if (cfg & USB_UDM_PD) {
+                       reg_value |= SET_BITS(regUSB0, UDM_PD, 1);
+                       reg_mask |= SET_BITS(regUSB0, UDM_PD, 1);
+               }
+
+               if (cfg & USB_UDP_PD) {
+                       reg_value |= SET_BITS(regUSB0, UDP_PD, 1);
+                       reg_mask |= SET_BITS(regUSB0, UDP_PD, 1);
+               }
+
+               if (cfg & USB_DP150K_PU) {
+                       reg_value |= SET_BITS(regUSB0, DP150K_PU, 1);
+                       reg_mask |= SET_BITS(regUSB0, DP150K_PU, 1);
+               }
+
+               if (cfg & USB_USBCNTRL) {
+                       reg_value |= SET_BITS(regUSB0, USBCNTRL, 1);
+                       reg_mask |= SET_BITS(regUSB0, USBCNTRL, 1);
+               }
+
+               if (cfg & USB_VBUS_CURRENT_LIMIT_HIGH) {
+                       reg_value |= SET_BITS(regUSB0, CURRENT_LIMIT, 0);
+               } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_10MS) {
+                       reg_value |= SET_BITS(regUSB0, CURRENT_LIMIT, 1);
+               } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_20MS) {
+                       reg_value |= SET_BITS(regUSB0, CURRENT_LIMIT, 2);
+               } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_30MS) {
+                       reg_value |= SET_BITS(regUSB0, CURRENT_LIMIT, 3);
+               } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_40MS) {
+                       reg_value |= SET_BITS(regUSB0, CURRENT_LIMIT, 4);
+               } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_50MS) {
+                       reg_value |= SET_BITS(regUSB0, CURRENT_LIMIT, 5);
+               } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_60MS) {
+                       reg_value |= SET_BITS(regUSB0, CURRENT_LIMIT, 6);
+               }
+               if (cfg & USB_VBUS_CURRENT_LIMIT_LOW) {
+                       reg_value |= SET_BITS(regUSB0, CURRENT_LIMIT, 7);
+                       reg_mask |= SET_BITS(regUSB0, CURRENT_LIMIT, 7);
+               }
+
+               if (cfg & USB_VBUS_PULLDOWN) {
+                       reg_value |= SET_BITS(regUSB0, VBUSPDENB, 1);
+                       reg_mask |= SET_BITS(regUSB0, VBUSPDENB, 1);
+               }
+
+               rc = pmic_write_reg(REG_USB, reg_value, reg_mask);
+
+               if (rc == PMIC_SUCCESS) {
+                       if ((cfg & USB_VBUS_CURRENT_LIMIT_HIGH) ||
+                           (cfg & USB_VBUS_CURRENT_LIMIT_LOW) ||
+                           (cfg & USB_VBUS_CURRENT_LIMIT_LOW_10MS) ||
+                           (cfg & USB_VBUS_CURRENT_LIMIT_LOW_20MS) ||
+                           (cfg & USB_VBUS_CURRENT_LIMIT_LOW_30MS) ||
+                           (cfg & USB_VBUS_CURRENT_LIMIT_LOW_40MS) ||
+                           (cfg & USB_VBUS_CURRENT_LIMIT_LOW_50MS) ||
+                           (cfg & USB_VBUS_CURRENT_LIMIT_LOW_60MS)) {
+                               /* Make sure that the VBUS current limit state is
+                                * correctly set to either USB_VBUS_CURRENT_LIMIT_HIGH
+                                * or USB_VBUS_CURRENT_LIMIT_LOW but never both at the
+                                * same time.
+                                *
+                                * We guarantee this by first clearing both of the
+                                * status bits and then resetting the correct one.
+                                */
+                               usb.usbOtgCfg &=
+                                   ~(USB_VBUS_CURRENT_LIMIT_HIGH |
+                                     USB_VBUS_CURRENT_LIMIT_LOW |
+                                     USB_VBUS_CURRENT_LIMIT_LOW_10MS |
+                                     USB_VBUS_CURRENT_LIMIT_LOW_20MS |
+                                     USB_VBUS_CURRENT_LIMIT_LOW_30MS |
+                                     USB_VBUS_CURRENT_LIMIT_LOW_40MS |
+                                     USB_VBUS_CURRENT_LIMIT_LOW_50MS |
+                                     USB_VBUS_CURRENT_LIMIT_LOW_60MS);
+                       }
+
+                       usb.usbOtgCfg |= cfg;
+               }
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * Clears the USB On-The-Go (OTG) configuration. Multiple configuration settings
+ * may be OR'd together in a single call. However, selecting conflicting
+ * settings (e.g., multiple VBUS current limits) will result in undefined
+ * behavior.
+ *
+ * @param   handle          Device handle from open() call.
+ * @param   cfg             USB OTG configuration settings to be cleared.
+ *
+ * @retval      PMIC_SUCCESS         If the OTG configuration was successfully
+ *                                   cleared.
+ * @retval      PMIC_PARAMETER_ERROR If the handle is invalid.
+ * @retval      PMIC_NOT_SUPPORTED   If the desired USB OTG configuration is
+ *                                   not supported by the PMIC hardware.
+ */
+PMIC_STATUS pmic_convity_usb_otg_clear_config(const PMIC_CONVITY_HANDLE handle,
+                                             const PMIC_CONVITY_USB_OTG_CONFIG
+                                             cfg)
+{
+       PMIC_STATUS rc = PMIC_ERROR;
+       unsigned int reg_value = 0;
+       unsigned int reg_mask = 0;
+
+       /* Use a critical section to maintain a consistent state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle == usb.handle) && (usb.handle_state == HANDLE_IN_USE)) {
+               /* if ((cfg & USB_VBUS_CURRENT_LIMIT_LOW_10MS) ||
+                  (cfg & USB_VBUS_CURRENT_LIMIT_LOW_20MS) ||
+                  (cfg & USB_VBUS_CURRENT_LIMIT_LOW_30MS) ||
+                  (cfg & USB_VBUS_CURRENT_LIMIT_LOW_40MS) ||
+                  (cfg & USB_VBUS_CURRENT_LIMIT_LOW_50MS) ||
+                  (cfg & USB_VBUS_CURRENT_LIMIT_LOW_60MS))
+                  {
+                  rc = PMIC_NOT_SUPPORTED;
+                  } */
+
+               if (cfg & USB_OTG_SE0CONN) {
+                       reg_mask = SET_BITS(regUSB0, SE0_CONN, 1);
+               }
+
+               if (cfg & USB_OTG_DLP_SRP) {
+                       reg_mask |= SET_BITS(regUSB0, DLP_SRP, 1);
+               }
+
+               if (cfg & USB_DP150K_PU) {
+                       reg_mask |= SET_BITS(regUSB0, DP150K_PU, 1);
+               }
+
+               if (cfg & USB_PULL_OVERRIDE) {
+                       reg_mask |= SET_BITS(regUSB0, PULLOVR, 1);
+               }
+
+               if (cfg & USB_PU) {
+
+                       reg_mask |= SET_BITS(regUSB0, USB_PU, 1);
+               }
+
+               if (cfg & USB_UDM_PD) {
+
+                       reg_mask |= SET_BITS(regUSB0, UDM_PD, 1);
+               }
+
+               if (cfg & USB_UDP_PD) {
+
+                       reg_mask |= SET_BITS(regUSB0, UDP_PD, 1);
+               }
+
+               if (cfg & USB_USBCNTRL) {
+                       reg_mask |= SET_BITS(regUSB0, USBCNTRL, 1);
+               }
+
+               if (cfg & USB_VBUS_CURRENT_LIMIT_HIGH) {
+                       reg_mask |= SET_BITS(regUSB0, CURRENT_LIMIT, 0);
+               } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_10MS) {
+                       reg_mask |= SET_BITS(regUSB0, CURRENT_LIMIT, 1);
+               } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_20MS) {
+                       reg_mask |= SET_BITS(regUSB0, CURRENT_LIMIT, 2);
+               } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_30MS) {
+                       reg_mask |= SET_BITS(regUSB0, CURRENT_LIMIT, 3);
+               } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_40MS) {
+                       reg_mask |= SET_BITS(regUSB0, CURRENT_LIMIT, 4);
+               } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_50MS) {
+                       reg_mask |= SET_BITS(regUSB0, CURRENT_LIMIT, 5);
+               } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_60MS) {
+                       reg_mask |= SET_BITS(regUSB0, CURRENT_LIMIT, 6);
+               }
+
+               if (cfg & USB_VBUS_PULLDOWN) {
+                       reg_mask |= SET_BITS(regUSB0, VBUSPDENB, 1);
+               }
+
+               rc = pmic_write_reg(REG_USB, reg_value, reg_mask);
+
+               if (rc == PMIC_SUCCESS) {
+                       usb.usbOtgCfg &= ~cfg;
+               }
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * Get the current USB On-The-Go (OTG) configuration.
+ *
+ * @param       handle          device handle from open() call
+ * @param       cfg             the current USB OTG configuration
+ *
+ * @return      PMIC_SUCCESS    if the OTG configuration was successfully
+ *                              retrieved
+ */
+PMIC_STATUS pmic_convity_usb_otg_get_config(const PMIC_CONVITY_HANDLE handle,
+                                           PMIC_CONVITY_USB_OTG_CONFIG *
+                                           const cfg)
+{
+       PMIC_STATUS rc = PMIC_ERROR;
+
+       /* Use a critical section to maintain a consistent state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle == usb.handle) &&
+           (usb.handle_state == HANDLE_IN_USE) &&
+           (cfg != (PMIC_CONVITY_USB_OTG_CONFIG *) NULL)) {
+               *cfg = usb.usbOtgCfg;
+
+               rc = PMIC_SUCCESS;
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*@}*/
+
+/**************************************************************************
+ * RS-232-specific configuration and setup functions.
+ **************************************************************************
+ */
+
+/*!
+ * @name RS-232 Serial Connectivity APIs
+ * Functions for controlling RS-232 serial connectivity.
+ */
+/*@{*/
+
+/*!
+ * Set the connectivity interface to the selected RS-232 operating
+ * configuration. Note that the RS-232 operating mode will be automatically
+ * overridden if the USB_EN is asserted at any time (e.g., when a USB device
+ * is attached). However, we will also automatically return to the RS-232
+ * mode once the USB device is detached.
+ *
+ * @param       handle          device handle from open() call
+ * @param       cfgInternal     RS-232 transceiver internal connections
+ * @param       cfgExternal     RS-232 transceiver external connections
+ *
+ * @return      PMIC_SUCCESS    if the requested mode was set
+ */
+PMIC_STATUS pmic_convity_rs232_set_config(const PMIC_CONVITY_HANDLE handle,
+                                         const PMIC_CONVITY_RS232_INTERNAL
+                                         cfgInternal,
+                                         const PMIC_CONVITY_RS232_EXTERNAL
+                                         cfgExternal)
+{
+       PMIC_STATUS rc = PMIC_ERROR;
+       unsigned int reg_value0 = 0, reg_value1 = 0;
+       unsigned int reg_mask = SET_BITS(regUSB1, RSPOL, 1);
+
+       /* Use a critical section to maintain a consistent state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle == rs_232.handle) && (rs_232.handle_state == HANDLE_IN_USE)) {
+               rc = PMIC_SUCCESS;
+
+               /* Validate the calling parameters. */
+               /*if ((cfgInternal !=  RS232_TX_USE0VM_RX_UDATVP) &&
+                  (cfgInternal != RS232_TX_RX_INTERNAL_DEFAULT) && (cfgInternal !=  RS232_TX_UDATVP_RX_URXVM))
+                  {
+
+                  rc = PMIC_NOT_SUPPORTED;
+                  } */
+               if (cfgInternal == RS232_TX_USE0VM_RX_UDATVP) {
+
+                       reg_value0 = SET_BITS(regUSB0, INTERFACE_MODE, 1);
+
+               } else if (cfgInternal == RS232_TX_RX_INTERNAL_DEFAULT) {
+
+                       reg_value0 = SET_BITS(regUSB0, INTERFACE_MODE, 1);
+                       reg_mask |= SET_BITS(regUSB1, RSPOL, 1);
+
+               } else if (cfgInternal == RS232_TX_UDATVP_RX_URXVM) {
+
+                       reg_value0 = SET_BITS(regUSB0, INTERFACE_MODE, 2);
+                       reg_value1 |= SET_BITS(regUSB1, RSPOL, 1);
+
+               } else if ((cfgExternal == RS232_TX_UDM_RX_UDP) ||
+                          (cfgExternal == RS232_TX_RX_EXTERNAL_DEFAULT)) {
+                       /* Configure for TX on D+ and RX on D-. */
+                       reg_value0 |= SET_BITS(regUSB0, INTERFACE_MODE, 1);
+                       reg_value1 |= SET_BITS(regUSB1, RSPOL, 0);
+               } else if (cfgExternal != RS232_TX_UDM_RX_UDP) {
+                       /* Any other RS-232 configuration is an error. */
+                       rc = PMIC_ERROR;
+               }
+
+               if (rc == PMIC_SUCCESS) {
+                       /* Configure for TX on D- and RX on D+. */
+                       rc = pmic_write_reg(REG_USB, reg_value0, reg_mask);
+
+                       rc = pmic_write_reg(REG_CHARGE_USB_SPARE,
+                                           reg_value1, reg_mask);
+
+                       if (rc == PMIC_SUCCESS) {
+                               rs_232.rs232CfgInternal = cfgInternal;
+                               rs_232.rs232CfgExternal = cfgExternal;
+                       }
+               }
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*!
+ * Get the connectivity interface's current RS-232 operating configuration.
+ *
+ * @param       handle          device handle from open() call
+ * @param       cfgInternal     RS-232 transceiver internal connections
+ * @param       cfgExternal     RS-232 transceiver external connections
+ *
+ * @return      PMIC_SUCCESS    if the requested mode was retrieved
+ */
+PMIC_STATUS pmic_convity_rs232_get_config(const PMIC_CONVITY_HANDLE handle,
+                                         PMIC_CONVITY_RS232_INTERNAL *
+                                         const cfgInternal,
+                                         PMIC_CONVITY_RS232_EXTERNAL *
+                                         const cfgExternal)
+{
+       PMIC_STATUS rc = PMIC_ERROR;
+
+       /* Use a critical section to maintain a consistent state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle == rs_232.handle) &&
+           (rs_232.handle_state == HANDLE_IN_USE) &&
+           (cfgInternal != (PMIC_CONVITY_RS232_INTERNAL *) NULL) &&
+           (cfgExternal != (PMIC_CONVITY_RS232_EXTERNAL *) NULL)) {
+               *cfgInternal = rs_232.rs232CfgInternal;
+               *cfgExternal = rs_232.rs232CfgExternal;
+
+               rc = PMIC_SUCCESS;
+       }
+
+       /* Exit the critical section. */
+       up(&mutex);
+
+       return rc;
+}
+
+/*@}*/
+
+/**************************************************************************
+ * CEA-936-specific configuration and setup functions.
+ **************************************************************************
+ */
+
+/*!
+ * @name CEA-936 Connectivity APIs
+ * Functions for controlling CEA-936 connectivity.
+ */
+/*@{*/
+
+/*!
+ * Signal the attached device to exit the current CEA-936 operating mode.
+ * Returns an error if the current operating mode is not CEA-936.
+ *
+ * @param       handle          device handle from open() call
+ * @param       signal          type of exit signal to be sent
+ *
+ * @return      PMIC_SUCCESS    if exit signal was sent
+ */
+PMIC_STATUS pmic_convity_cea936_exit_signal(const PMIC_CONVITY_HANDLE handle,
+                                           const
+                                           PMIC_CONVITY_CEA936_EXIT_SIGNAL
+                                           signal)
+{
+       PMIC_STATUS rc = PMIC_ERROR;
+       unsigned int reg_value = 0;
+       unsigned int reg_mask =
+           SET_BITS(regUSB0, IDPD, 1) | SET_BITS(regUSB0, IDPULSE, 1);
+
+       /* Use a critical section to maintain a consistent state. */
+       if (down_interruptible(&mutex))
+               return PMIC_SYSTEM_ERROR_EINTR;
+
+       if ((handle != cea_936.handle)
+           || (cea_936.handle_state != HANDLE_IN_USE)) {
+               /* Must return error indication for invalid handle parameter to be
+                * consistent with other APIs.
+                */
+               rc = PMIC_ERROR;
+       } else if (signal == CEA936_UID_PULLDOWN_6MS) {
+               reg_value =
+                   SET_BITS(regUSB0, IDPULSE, 0) | SET_BITS(regUSB0, IDPD, 0);
+       } else if (signal == CEA936_UID_PULLDOWN_6MS) {
+               reg_value = SET_BITS(regUSB0, IDPULSE, 1);
+       } else if (signal == CEA936_UID_PULLDOWN) {
+               reg_value = SET_BITS(regUSB0, IDPD, 1);
+       } else if (signal == CEA936_UDMPULSE) {
+               reg_value = SET_BITS(regUSB0, DMPULSE, 1);
+       }
+
+       rc = pmic_write_reg(REG_USB, reg_value, reg_mask);
+
+       up(&mutex);
+
+       return rc;
+}
+
+/*@}*/
+
+/**************************************************************************
+ * Static functions.
+ **************************************************************************
+ */
+
+/*!
+ * @name Connectivity Driver Internal Support Functions
+ * These non-exported internal functions are used to support the functionality
+ * of the exported connectivity APIs.
+ */
+/*@{*/
+
+/*!
+ * This internal helper function sets the desired operating mode (either USB
+ * OTG or RS-232). It must be called with the mutex already acquired.
+ *
+ * @param       mode               the desired operating mode (USB or RS232)
+ *
+ * @return      PMIC_SUCCESS       if the desired operating mode was set
+ * @return      PMIC_NOT_SUPPORTED if the desired operating mode is invalid
+ */
+static PMIC_STATUS pmic_convity_set_mode_internal(const PMIC_CONVITY_MODE mode)
+{
+       unsigned int reg_value0 = 0, reg_value1 = 0;
+       unsigned int reg_mask0 = 0, reg_mask1 = 0;
+
+       PMIC_STATUS rc = PMIC_SUCCESS;
+
+       switch (mode) {
+       case USB:
+               /* For the USB mode, we start by tri-stating the USB bus (by
+                * setting VBUSEN = 0) until a device is connected (i.e.,
+                * until we receive a 4.4V rising edge event). All pull-up
+                * and pull-down resistors are also disabled until a USB
+                * device is actually connected and we have determined which
+                * device is the host and the desired USB bus speed.
+                *
+                * Also tri-state the RS-232 buffers (by setting RSTRI = 1).
+                * This prevents the hardware from automatically returning to
+                * the RS-232 mode when the USB device is detached.
+                */
+
+               reg_value0 = SET_BITS(regUSB0, INTERFACE_MODE, 0);
+               reg_mask0 = SET_BITS(regUSB0, INTERFACE_MODE, 7);
+
+               /*reg_value1 = SET_BITS(regUSB1, RSTRI, 1); */
+
+               rc = pmic_write_reg(REG_USB, reg_value0, reg_mask0);
+               /*      if (rc == PMIC_SUCCESS) {
+                  CHECK_ERROR(pmic_write_reg
+                  (REG_CHARGE_USB_SPARE,
+                  reg_value1, reg_mask1));
+                  } */
+
+               break;
+
+       case RS232_1:
+               /* For the RS-232 mode, we tri-state the USB bus (by setting
+                * VBUSEN = 0) and enable the RS-232 transceiver (by setting
+                * RS232ENB = 0).
+                *
+                * Note that even in the RS-232 mode, if a USB device is
+                * plugged in, we will receive a 4.4V rising edge event which
+                * will automatically disable the RS-232 transceiver and
+                * tri-state the RS-232 buffers. This allows us to temporarily
+                * switch over to USB mode while the USB device is attached.
+                * The RS-232 transceiver and buffers will be automatically
+                * re-enabled when the USB device is detached.
+                */
+
+               /* Explicitly disconnect all of the USB pull-down resistors
+                * and the VUSB power regulator here just to be safe.
+                *
+                * But we do connect the internal pull-up resistor on USB_D+
+                * to avoid having an extra load on the USB_D+ line when in
+                * RS-232 mode.
+                */
+
+               reg_value0 |= SET_BITS(regUSB0, INTERFACE_MODE, 1) |
+                   SET_BITS(regUSB0, VBUSPDENB, 1) |
+                   SET_BITS(regUSB0, USB_PU, 1);
+               reg_mask0 =
+                   SET_BITS(regUSB0, INTERFACE_MODE, 7) | SET_BITS(regUSB0,
+                                                                   VBUSPDENB,
+                                                                   1) |
+                   SET_BITS(regUSB0, USB_PU, 1);
+
+               reg_value1 = SET_BITS(regUSB1, RSPOL, 0);
+
+               rc = pmic_write_reg(REG_USB, reg_value0, reg_mask0);
+
+               if (rc == PMIC_SUCCESS) {
+                       CHECK_ERROR(pmic_write_reg
+                                   (REG_CHARGE_USB_SPARE,
+                                    reg_value1, reg_mask1));
+               }
+               break;
+
+       case RS232_2:
+               reg_value0 |= SET_BITS(regUSB0, INTERFACE_MODE, 2) |
+                   SET_BITS(regUSB0, VBUSPDENB, 1) |
+                   SET_BITS(regUSB0, USB_PU, 1);
+               reg_mask0 =
+                   SET_BITS(regUSB0, INTERFACE_MODE, 7) | SET_BITS(regUSB0,
+                                                                   VBUSPDENB,
+                                                                   1) |
+                   SET_BITS(regUSB0, USB_PU, 1);
+
+               reg_value1 = SET_BITS(regUSB1, RSPOL, 1);
+
+               rc = pmic_write_reg(REG_USB, reg_value0, reg_mask0);
+
+               if (rc == PMIC_SUCCESS) {
+                       CHECK_ERROR(pmic_write_reg
+                                   (REG_CHARGE_USB_SPARE,
+                                    reg_value1, reg_mask1));
+               }
+               break;
+
+       case CEA936_MONO:
+               reg_value0 |= SET_BITS(regUSB0, INTERFACE_MODE, 4);
+
+               rc = pmic_write_reg(REG_USB, reg_value0, reg_mask0);
+               break;
+
+       case CEA936_STEREO:
+               reg_value0 |= SET_BITS(regUSB0, INTERFACE_MODE, 5);
+               reg_mask0 = SET_BITS(regUSB0, INTERFACE_MODE, 7);
+
+               rc = pmic_write_reg(REG_USB, reg_value0, reg_mask0);
+               break;
+
+       case CEA936_TEST_RIGHT:
+               reg_value0 |= SET_BITS(regUSB0, INTERFACE_MODE, 6);
+               reg_mask0 = SET_BITS(regUSB0, INTERFACE_MODE, 7);
+
+               rc = pmic_write_reg(REG_USB, reg_value0, reg_mask0);
+               break;
+
+       case CEA936_TEST_LEFT:
+               reg_value0 |= SET_BITS(regUSB0, INTERFACE_MODE, 7);
+               reg_mask0 = SET_BITS(regUSB0, INTERFACE_MODE, 7);
+
+               rc = pmic_write_reg(REG_USB, reg_value0, reg_mask0);
+               break;
+
+       default:
+               rc = PMIC_NOT_SUPPORTED;
+       }
+
+       if (rc == PMIC_SUCCESS) {
+               if (mode == USB) {
+                       usb.mode = mode;
+               } else if ((mode == RS232_1) || (mode == RS232_1)) {
+                       rs_232.mode = mode;
+               } else if ((mode == CEA936_MONO) || (mode == CEA936_STEREO) ||
+                          (mode == CEA936_TEST_RIGHT)
+                          || (mode == CEA936_TEST_LEFT)) {
+                       cea_936.mode = mode;
+               }
+       }
+
+       return rc;
+}
+
+/*!
+ * This internal helper function deregisters all of the currently registered
+ * callback events. It must be called with the mutual exclusion spinlock
+ * already acquired.
+ *
+ * We've defined the event and callback deregistration code here as a separate
+ * function because it can be called by either the pmic_convity_close() or the
+ * pmic_convity_clear_callback() APIs. We also wanted to avoid any possible
+ * issues with having the same thread calling spin_lock_irq() twice.
+ *
+ * Note that the mutex must have already been acquired. We will also acquire
+ * the spinlock here to avoid any possible race conditions with the interrupt
+ * handler.
+ *
+ * @return      PMIC_SUCCESS    if all of the callback events were cleared
+ */
+static PMIC_STATUS pmic_convity_deregister_all(void)
+{
+       unsigned long flags;
+       PMIC_STATUS rc = PMIC_SUCCESS;
+
+       /* Deregister each of the PMIC events that we had previously
+        * registered for by using pmic_event_subscribe().
+        */
+
+       if ((usb.eventMask & USB_DETECT_MINI_A) ||
+           (usb.eventMask & USB_DETECT_MINI_B) ||
+           (usb.eventMask & USB_DETECT_NON_USB_ACCESSORY) ||
+           (usb.eventMask & USB_DETECT_FACTORY_MODE)) {
+               /* EVENT_AB_DETI or EVENT_IDI */
+               eventNotify.func = pmic_convity_event_handler;
+               eventNotify.param = (void *)(CORE_EVENT_ABDET);
+
+               if (pmic_event_unsubscribe(EVENT_IDI, eventNotify) ==
+                   PMIC_SUCCESS) {
+                       /* Also acquire the spinlock here to avoid any possible race
+                        * conditions with the interrupt handler.
+                        */
+
+                       spin_lock_irqsave(&lock, flags);
+
+                       usb.eventMask &= ~(USB_DETECT_MINI_A |
+                                          USB_DETECT_MINI_B |
+                                          USB_DETECT_NON_USB_ACCESSORY |
+                                          USB_DETECT_FACTORY_MODE);
+
+                       spin_unlock_irqrestore(&lock, flags);
+               } else {
+                       pr_debug
+                           ("%s: pmic_event_unsubscribe() for EVENT_AB_DETI failed\n",
+                            __FILE__);
+                       rc = PMIC_ERROR;
+               }
+       }
+
+       else if ((usb.eventMask & USB_DETECT_0V8_RISE) ||
+                (usb.eventMask & USB_DETECT_0V8_FALL)) {
+               /* EVENT_USB_08VI or EVENT_USBI */
+               eventNotify.func = pmic_convity_event_handler;
+               eventNotify.param = (void *)(CORE_EVENT_0V8);
+               if (pmic_event_unsubscribe(EVENT_USBI, eventNotify) ==
+                   PMIC_SUCCESS) {
+                       /* Also acquire the spinlock here to avoid any possible race
+                        * conditions with the interrupt handler.
+                        */
+                       spin_lock_irqsave(&lock, flags);
+
+                       usb.eventMask &= ~(USB_DETECT_0V8_RISE |
+                                          USB_DETECT_0V8_FALL);
+
+                       spin_unlock_irqrestore(&lock, flags);
+               } else {
+                       pr_debug
+                           ("%s: pmic_event_unsubscribe() for EVENT_USB_08VI failed\n",
+                            __FILE__);
+                       rc = PMIC_ERROR;
+               }
+
+       }
+
+       else if ((usb.eventMask & USB_DETECT_2V0_RISE) ||
+                (usb.eventMask & USB_DETECT_2V0_FALL)) {
+               /* EVENT_USB_20VI or EVENT_USBI */
+               eventNotify.func = pmic_convity_event_handler;
+               eventNotify.param = (void *)(CORE_EVENT_2V0);
+               if (pmic_event_unsubscribe(EVENT_USBI, eventNotify) ==
+                   PMIC_SUCCESS) {
+                       /* Also acquire the spinlock here to avoid any possible race
+                        * conditions with the interrupt handler.
+                        */
+                       spin_lock_irqsave(&lock, flags);
+
+                       usb.eventMask &= ~(USB_DETECT_2V0_RISE |
+                                          USB_DETECT_2V0_FALL);
+
+                       spin_unlock_irqrestore(&lock, flags);
+               } else {
+                       pr_debug
+                           ("%s: pmic_event_unsubscribe() for EVENT_USB_20VI failed\n",
+                            __FILE__);
+                       rc = PMIC_ERROR;
+               }
+       }
+
+       else if ((usb.eventMask & USB_DETECT_4V4_RISE) ||
+                (usb.eventMask & USB_DETECT_4V4_FALL)) {
+
+               /* EVENT_USB_44VI or EVENT_USBI */
+               eventNotify.func = pmic_convity_event_handler;
+               eventNotify.param = (void *)(CORE_EVENT_4V4);
+
+               if (pmic_event_unsubscribe(EVENT_USBI, eventNotify) ==
+                   PMIC_SUCCESS) {
+
+                       /* Also acquire the spinlock here to avoid any possible race
+                        * conditions with the interrupt handler.
+                        */
+                       spin_lock_irqsave(&lock, flags);
+
+                       usb.eventMask &= ~(USB_DETECT_4V4_RISE |
+                                          USB_DETECT_4V4_FALL);
+
+                       spin_unlock_irqrestore(&lock, flags);
+               } else {
+                       pr_debug
+                           ("%s: pmic_event_unsubscribe() for EVENT_USB_44VI failed\n",
+                            __FILE__);
+                       rc = PMIC_ERROR;
+               }
+       }
+
+       if (rc == PMIC_SUCCESS) {
+               /* Also acquire the spinlock here to avoid any possible race
+                * conditions with the interrupt handler.
+                */
+               spin_lock_irqsave(&lock, flags);
+
+               /* Restore the initial reset values for the callback function
+                * and event mask parameters. This should be NULL and zero,
+                * respectively.
+                *
+                * Note that we wait until the end here to fully reset everything
+                * just in case some of the pmic_event_unsubscribe() calls above
+                * failed for some reason (which normally shouldn't happen).
+                */
+               usb.callback = reset.callback;
+               usb.eventMask = reset.eventMask;
+
+               spin_unlock_irqrestore(&lock, flags);
+       }
+       return rc;
+}
+
+/*!
+ * This is the default event handler for all connectivity-related events
+ * and hardware interrupts.
+ *
+ * @param       param           event ID
+ */
+static void pmic_convity_event_handler(void *param)
+{
+       unsigned long flags;
+
+       /* Update the global list of active interrupt events. */
+       spin_lock_irqsave(&lock, flags);
+       eventID |= (PMIC_CORE_EVENT) (param);
+       spin_unlock_irqrestore(&lock, flags);
+
+       /* Schedule the tasklet to be run as soon as it is convenient to do so. */
+       schedule_work(&convityTasklet);
+}
+
+/*!
+ * @brief This is the connectivity driver tasklet that handles interrupt events.
+ *
+ * This function is scheduled by the connectivity driver interrupt handler
+ * pmic_convity_event_handler() to complete the processing of all of the
+ * connectivity-related interrupt events.
+ *
+ * Since this tasklet runs with interrupts enabled, we can safely call
+ * the ADC driver, if necessary, to properly detect the type of USB connection
+ * that is being made and to call any user-registered callback functions.
+ *
+ * @param   arg                The parameter that was provided above in
+ *                                  the DECLARE_TASKLET() macro (unused).
+ */
+static void pmic_convity_tasklet(struct work_struct *work)
+{
+
+       PMIC_CONVITY_EVENTS activeEvents = 0;
+       unsigned long flags = 0;
+
+       /* Check the interrupt sense bits to determine exactly what
+        * event just occurred.
+        */
+       if (eventID & CORE_EVENT_4V4) {
+               spin_lock_irqsave(&lock, flags);
+               eventID &= ~CORE_EVENT_4V4;
+               spin_unlock_irqrestore(&lock, flags);
+
+               activeEvents |= pmic_check_sensor(SENSE_USB4V4S) ?
+                   USB_DETECT_4V4_RISE : USB_DETECT_4V4_FALL;
+
+               if (activeEvents & ~usb.eventMask) {
+                       /* The default handler for 4.4 V rising/falling edge detection
+                        * is to simply ignore the event.
+                        */
+                       ;
+               }
+       }
+       if (eventID & CORE_EVENT_2V0) {
+               spin_lock_irqsave(&lock, flags);
+               eventID &= ~CORE_EVENT_2V0;
+               spin_unlock_irqrestore(&lock, flags);
+
+               activeEvents |= pmic_check_sensor(SENSE_USB2V0S) ?
+                   USB_DETECT_2V0_RISE : USB_DETECT_2V0_FALL;
+               if (activeEvents & ~usb.eventMask) {
+                       /* The default handler for 2.0 V rising/falling edge detection
+                        * is to simply ignore the event.
+                        */
+                       ;
+               }
+       }
+       if (eventID & CORE_EVENT_0V8) {
+               spin_lock_irqsave(&lock, flags);
+               eventID &= ~CORE_EVENT_0V8;
+               spin_unlock_irqrestore(&lock, flags);
+
+               activeEvents |= pmic_check_sensor(SENSE_USB0V8S) ?
+                   USB_DETECT_0V8_RISE : USB_DETECT_0V8_FALL;
+
+               if (activeEvents & ~usb.eventMask) {
+                       /* The default handler for 0.8 V rising/falling edge detection
+                        * is to simply ignore the event.
+                        */
+                       ;
+               }
+       }
+       if (eventID & CORE_EVENT_ABDET) {
+               spin_lock_irqsave(&lock, flags);
+               eventID &= ~CORE_EVENT_ABDET;
+               spin_unlock_irqrestore(&lock, flags);
+
+               activeEvents |= pmic_check_sensor(SENSE_ID_GNDS) ?
+                   USB_DETECT_MINI_A : 0;
+
+               activeEvents |= pmic_check_sensor(SENSE_ID_FLOATS) ?
+                   USB_DETECT_MINI_B : 0;
+       }
+
+       /* Begin a critical section here so that we don't register/deregister
+        * for events or open/close the connectivity driver while the existing
+        * event handler (if it is currently defined) is in the middle of handling
+        * the current event.
+        */
+       spin_lock_irqsave(&lock, flags);
+
+       /* Finally, call the user-defined callback function if required. */
+       if ((usb.handle_state == HANDLE_IN_USE) &&
+           (usb.callback != NULL) && (activeEvents & usb.eventMask)) {
+               (*usb.callback) (activeEvents);
+       }
+
+       spin_unlock_irqrestore(&lock, flags);
+}
+
+/*@}*/
+
+/**************************************************************************
+ * Module initialization and termination functions.
+ *
+ * Note that if this code is compiled into the kernel, then the
+ * module_init() function will be called within the device_initcall()
+ * group.
+ **************************************************************************
+ */
+
+/*!
+ * @name Connectivity Driver Loading/Unloading Functions
+ * These non-exported internal functions are used to support the connectivity
+ * device driver initialization and de-initialization operations.
+ */
+/*@{*/
+
+/*!
+ * @brief This is the connectivity device driver initialization function.
+ *
+ * This function is called by the kernel when this device driver is first
+ * loaded.
+ */
+static int __init mc13783_pmic_convity_init(void)
+{
+       printk(KERN_INFO "PMIC Connectivity driver loading..\n");
+
+       return 0;
+}
+
+/*!
+ * @brief This is the Connectivity device driver de-initialization function.
+ *
+ * This function is called by the kernel when this device driver is about
+ * to be unloaded.
+ */
+static void __exit mc13783_pmic_convity_exit(void)
+{
+       printk(KERN_INFO "PMIC Connectivity driver unloading\n");
+
+       /* Close the device handle if it is still open. This will also
+        * deregister any callbacks that may still be active.
+        */
+       if (usb.handle_state == HANDLE_IN_USE) {
+               pmic_convity_close(usb.handle);
+       } else if (usb.handle_state == HANDLE_IN_USE) {
+               pmic_convity_close(rs_232.handle);
+       } else if (usb.handle_state == HANDLE_IN_USE) {
+               pmic_convity_close(cea_936.handle);
+       }
+
+       /* Reset the PMIC Connectivity register to it's power on state.
+        * We should do this when unloading the module so that we don't
+        * leave the hardware in a state which could cause problems when
+        * no device driver is loaded.
+        */
+       pmic_write_reg(REG_USB, RESET_USBCNTRL_REG_0, REG_FULLMASK);
+       pmic_write_reg(REG_CHARGE_USB_SPARE, RESET_USBCNTRL_REG_1,
+                      REG_FULLMASK);
+       /* Note that there is no need to reset the "convity" device driver
+        * state structure to the reset state since we are in the final
+        * stage of unloading the device driver. The device driver state
+        * structure will be automatically and properly reinitialized if
+        * this device driver is reloaded.
+        */
+}
+
+/*@}*/
+
+/*
+ * Module entry points and description information.
+ */
+
+module_init(mc13783_pmic_convity_init);
+module_exit(mc13783_pmic_convity_exit);
+
+MODULE_DESCRIPTION("mc13783 Connectivity device driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pmic/mc13783/pmic_light.c b/drivers/mxc/pmic/mc13783/pmic_light.c
new file mode 100644 (file)
index 0000000..0eb52be
--- /dev/null
@@ -0,0 +1,2764 @@
+/*
+ * 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 mc13783/pmic_light.c
+ * @brief This is the main file of PMIC(mc13783) Light and Backlight driver.
+ *
+ * @ingroup PMIC_LIGHT
+ */
+
+/*
+ * Includes
+ */
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/pmic_light.h>
+#include <linux/pmic_status.h>
+#include "pmic_light_defs.h"
+
+#define NB_LIGHT_REG      6
+
+static int pmic_light_major;
+
+/*!
+ * Number of users waiting in suspendq
+ */
+static int swait;
+
+/*!
+ * To indicate whether any of the light devices are suspending
+ */
+static int suspend_flag;
+
+/*!
+ * The suspendq is used to block application calls
+ */
+static wait_queue_head_t suspendq;
+
+static struct class *pmic_light_class;
+
+/* EXPORTED FUNCTIONS */
+EXPORT_SYMBOL(pmic_bklit_tcled_master_enable);
+EXPORT_SYMBOL(pmic_bklit_tcled_master_disable);
+EXPORT_SYMBOL(pmic_bklit_master_enable);
+EXPORT_SYMBOL(pmic_bklit_master_disable);
+EXPORT_SYMBOL(pmic_bklit_set_current);
+EXPORT_SYMBOL(pmic_bklit_get_current);
+EXPORT_SYMBOL(pmic_bklit_set_dutycycle);
+EXPORT_SYMBOL(pmic_bklit_get_dutycycle);
+EXPORT_SYMBOL(pmic_bklit_set_cycle_time);
+EXPORT_SYMBOL(pmic_bklit_get_cycle_time);
+EXPORT_SYMBOL(pmic_bklit_set_mode);
+EXPORT_SYMBOL(pmic_bklit_get_mode);
+EXPORT_SYMBOL(pmic_bklit_rampup);
+EXPORT_SYMBOL(pmic_bklit_off_rampup);
+EXPORT_SYMBOL(pmic_bklit_rampdown);
+EXPORT_SYMBOL(pmic_bklit_off_rampdown);
+EXPORT_SYMBOL(pmic_bklit_enable_edge_slow);
+EXPORT_SYMBOL(pmic_bklit_disable_edge_slow);
+EXPORT_SYMBOL(pmic_bklit_get_edge_slow);
+EXPORT_SYMBOL(pmic_bklit_set_strobemode);
+EXPORT_SYMBOL(pmic_tcled_enable);
+EXPORT_SYMBOL(pmic_tcled_disable);
+EXPORT_SYMBOL(pmic_tcled_get_mode);
+EXPORT_SYMBOL(pmic_tcled_ind_set_current);
+EXPORT_SYMBOL(pmic_tcled_ind_get_current);
+EXPORT_SYMBOL(pmic_tcled_ind_set_blink_pattern);
+EXPORT_SYMBOL(pmic_tcled_ind_get_blink_pattern);
+EXPORT_SYMBOL(pmic_tcled_fun_set_current);
+EXPORT_SYMBOL(pmic_tcled_fun_get_current);
+EXPORT_SYMBOL(pmic_tcled_fun_set_cycletime);
+EXPORT_SYMBOL(pmic_tcled_fun_get_cycletime);
+EXPORT_SYMBOL(pmic_tcled_fun_set_dutycycle);
+EXPORT_SYMBOL(pmic_tcled_fun_get_dutycycle);
+EXPORT_SYMBOL(pmic_tcled_fun_blendedramps);
+EXPORT_SYMBOL(pmic_tcled_fun_sawramps);
+EXPORT_SYMBOL(pmic_tcled_fun_blendedbowtie);
+EXPORT_SYMBOL(pmic_tcled_fun_chasinglightspattern);
+EXPORT_SYMBOL(pmic_tcled_fun_strobe);
+EXPORT_SYMBOL(pmic_tcled_fun_rampup);
+EXPORT_SYMBOL(pmic_tcled_get_fun_rampup);
+EXPORT_SYMBOL(pmic_tcled_fun_rampdown);
+EXPORT_SYMBOL(pmic_tcled_get_fun_rampdown);
+EXPORT_SYMBOL(pmic_tcled_fun_triode_on);
+EXPORT_SYMBOL(pmic_tcled_fun_triode_off);
+EXPORT_SYMBOL(pmic_tcled_enable_edge_slow);
+EXPORT_SYMBOL(pmic_tcled_disable_edge_slow);
+EXPORT_SYMBOL(pmic_tcled_enable_half_current);
+EXPORT_SYMBOL(pmic_tcled_disable_half_current);
+EXPORT_SYMBOL(pmic_tcled_enable_audio_modulation);
+EXPORT_SYMBOL(pmic_tcled_disable_audio_modulation);
+EXPORT_SYMBOL(pmic_bklit_set_boost_mode);
+EXPORT_SYMBOL(pmic_bklit_get_boost_mode);
+EXPORT_SYMBOL(pmic_bklit_config_boost_mode);
+EXPORT_SYMBOL(pmic_bklit_gets_boost_mode);
+
+/*!
+ * This is the suspend of power management for the pmic light API.
+ * It suports SAVE and POWER_DOWN state.
+ *
+ * @param        pdev           the device
+ * @param        state          the state
+ *
+ * @return       This function returns 0 if successful.
+ */
+static int pmic_light_suspend(struct platform_device *dev, pm_message_t state)
+{
+       suspend_flag = 1;
+       /* switch off all leds and backlights */
+       CHECK_ERROR(pmic_light_init_reg());
+
+       return 0;
+};
+
+/*!
+ * This is the resume of power management for the pmic light API.
+ * It suports RESTORE state.
+ *
+ * @param        dev            the device
+ *
+ * @return       This function returns 0 if successful.
+ */
+static int pmic_light_resume(struct platform_device *pdev)
+{
+       suspend_flag = 0;
+       while (swait > 0) {
+               swait--;
+               wake_up_interruptible(&suspendq);
+       }
+
+       return 0;
+};
+
+/*!
+ * This function enables backlight & tcled.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_tcled_master_enable(void)
+{
+       unsigned int reg_value = 0;
+       unsigned int mask = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       reg_value = BITFVAL(BIT_LEDEN, 1);
+       mask = BITFMASK(BIT_LEDEN);
+       CHECK_ERROR(pmic_write_reg(LREG_0, reg_value, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function disables backlight & tcled.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful
+ */
+PMIC_STATUS pmic_bklit_tcled_master_disable(void)
+{
+       unsigned int reg_value = 0;
+       unsigned int mask = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       reg_value = BITFVAL(BIT_LEDEN, 0);
+       mask = BITFMASK(BIT_LEDEN);
+       CHECK_ERROR(pmic_write_reg(LREG_0, reg_value, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function enables backlight. Not supported on mc13783
+ * Use pmic_bklit_tcled_master_enable.
+ *
+ * @return       This function returns PMIC_NOT_SUPPORTED
+ */
+PMIC_STATUS pmic_bklit_master_enable(void)
+{
+       return PMIC_NOT_SUPPORTED;
+}
+
+/*!
+ * This function disables backlight. Not supported on mc13783
+ * Use pmic_bklit_tcled_master_enable.
+ *
+ * @return       This function returns PMIC_NOT_SUPPORTED
+ */
+PMIC_STATUS pmic_bklit_master_disable(void)
+{
+       return PMIC_NOT_SUPPORTED;
+}
+
+/*!
+ * This function sets backlight current level.
+ *
+ * @param        channel   Backlight channel
+ * @param        level     Backlight current level, as the following table.
+ *                         @verbatim
+ *             level     main & aux   keyboard
+ *              ------    -----------  --------
+ *              0         0 mA         0 mA
+ *              1         3 mA         12 mA
+ *              2         6 mA         24 mA
+ *              3         9 mA         36 mA
+ *              4         12 mA        48 mA
+ *              5         15 mA        60 mA
+ *              6         18 mA        72 mA
+ *              7         21 mA        84 mA
+ *              @endverbatim
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_set_current(t_bklit_channel channel, unsigned char level)
+{
+       unsigned int mask;
+       unsigned int value;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       switch (channel) {
+       case BACKLIGHT_LED1:
+               value = BITFVAL(BIT_CL_MAIN, level);
+               mask = BITFMASK(BIT_CL_MAIN);
+               break;
+       case BACKLIGHT_LED2:
+               value = BITFVAL(BIT_CL_AUX, level);
+               mask = BITFMASK(BIT_CL_AUX);
+               break;
+       case BACKLIGHT_LED3:
+               value = BITFVAL(BIT_CL_KEY, level);
+               mask = BITFMASK(BIT_CL_KEY);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+       CHECK_ERROR(pmic_write_reg(LREG_2, value, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function retrives backlight current level.
+ * The channels are not individually adjustable, hence
+ * the channel parameter is ignored.
+ *
+ * @param        channel   Backlight channel (Ignored because the
+ *                         channels are not individually adjustable)
+ * @param        level     Pointer to store backlight current level result.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_get_current(t_bklit_channel channel,
+                                  unsigned char *level)
+{
+       unsigned int reg_value = 0;
+       unsigned int mask = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       switch (channel) {
+       case BACKLIGHT_LED1:
+               mask = BITFMASK(BIT_CL_MAIN);
+               break;
+       case BACKLIGHT_LED2:
+               mask = BITFMASK(BIT_CL_AUX);
+               break;
+       case BACKLIGHT_LED3:
+               mask = BITFMASK(BIT_CL_KEY);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_read_reg(LREG_2, &reg_value, mask));
+
+       switch (channel) {
+       case BACKLIGHT_LED1:
+               *level = BITFEXT(reg_value, BIT_CL_MAIN);
+               break;
+       case BACKLIGHT_LED2:
+               *level = BITFEXT(reg_value, BIT_CL_AUX);
+               break;
+       case BACKLIGHT_LED3:
+               *level = BITFEXT(reg_value, BIT_CL_KEY);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets a backlight channel duty cycle.
+ * LED perceived brightness for each zone may be individually set by setting
+ * duty cycle. The default setting is for 0% duty cycle; this keeps all zone
+ * drivers turned off even after the master enable command. Each LED current
+ * sink can be turned on and adjusted for brightness with an independent 4 bit
+ * word for a duty cycle ranging from 0% to 100% in approximately 6.7% steps.
+ *
+ * @param        channel   Backlight channel.
+ * @param        dc        Backlight duty cycle, as the following table.
+ *                         @verbatim
+ *              dc        Duty Cycle (% On-time over Cycle Time)
+ *              ------    ---------------------------------------
+ *              0        0%
+ *              1        6.7%
+ *              2        13.3%
+ *              3        20%
+ *              4        26.7%
+ *              5        33.3%
+ *              6        40%
+ *              7        46.7%
+ *              8        53.3%
+ *              9        60%
+ *              10        66.7%
+ *              11        73.3%
+ *              12        80%
+ *              13        86.7%
+ *              14        93.3%
+ *              15        100%
+ *              @endverbatim
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_set_dutycycle(t_bklit_channel channel, unsigned char dc)
+{
+       unsigned int reg_value = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       if (dc > 15) {
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_read_reg(LREG_2, &reg_value, PMIC_ALL_BITS));
+
+       switch (channel) {
+       case BACKLIGHT_LED1:
+               reg_value = reg_value & (~MASK_DUTY_CYCLE);
+               reg_value = reg_value | (dc << BIT_DUTY_CYCLE);
+               break;
+       case BACKLIGHT_LED2:
+               reg_value = reg_value & (~(MASK_DUTY_CYCLE << INDEX_AUX));
+               reg_value = reg_value | (dc << (BIT_DUTY_CYCLE + INDEX_AUX));
+               break;
+       case BACKLIGHT_LED3:
+               reg_value = reg_value & (~(MASK_DUTY_CYCLE << INDEX_KYD));
+               reg_value = reg_value | (dc << (BIT_DUTY_CYCLE + INDEX_KYD));
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+       CHECK_ERROR(pmic_write_reg(LREG_2, reg_value, PMIC_ALL_BITS));
+       return PMIC_SUCCESS;
+
+}
+
+/*!
+ * This function retrives a backlight channel duty cycle.
+ *
+ * @param        channel   Backlight channel.
+ * @param        dc        Pointer to backlight duty cycle.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_get_dutycycle(t_bklit_channel channel, unsigned char *dc)
+{
+       unsigned int reg_value = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+       CHECK_ERROR(pmic_read_reg(LREG_2, &reg_value, PMIC_ALL_BITS));
+
+       switch (channel) {
+       case BACKLIGHT_LED1:
+               *dc = (int)((reg_value & (MASK_DUTY_CYCLE))
+                           >> BIT_DUTY_CYCLE);
+
+               break;
+       case BACKLIGHT_LED2:
+               *dc = (int)((reg_value & (MASK_DUTY_CYCLE << INDEX_AUX))
+                           >> (BIT_DUTY_CYCLE + INDEX_AUX));
+               break;
+       case BACKLIGHT_LED3:
+               *dc = (int)((reg_value & (MASK_DUTY_CYCLE <<
+                                         INDEX_KYD)) >> (BIT_DUTY_CYCLE +
+                                                         INDEX_KYD));
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets a backlight channel cycle time.
+ * Cycle Time is defined as the period of a complete cycle of
+ * Time_on + Time_off. The default Cycle Time is set to 0.01 seconds such that
+ * the 100 Hz on-off cycling is averaged out by the eye to eliminate
+ * flickering. Additionally, the Cycle Time can be programmed to intentionally
+ * extend the period of on-off cycles for a visual pulsating or blinking effect.
+ *
+ * @param        period    Backlight cycle time, as the following table.
+ *                         @verbatim
+ *              period      Cycle Time
+ *              --------    ------------
+ *              0          0.01 seconds
+ *              1          0.1 seconds
+ *              2          0.5 seconds
+ *              3          2 seconds
+ *              @endverbatim
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_set_cycle_time(unsigned char period)
+{
+       unsigned int mask;
+       unsigned int value;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+       if (period > 3) {
+               return PMIC_PARAMETER_ERROR;
+       }
+       mask = BITFMASK(BIT_PERIOD);
+       value = BITFVAL(BIT_PERIOD, period);
+       CHECK_ERROR(pmic_write_reg(LREG_2, value, mask));
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function retrives a backlight channel cycle time setting.
+ *
+ * @param        period    Pointer to save backlight cycle time setting result.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_get_cycle_time(unsigned char *period)
+{
+       unsigned int mask;
+       unsigned int value;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+       mask = BITFMASK(BIT_PERIOD);
+       CHECK_ERROR(pmic_read_reg(LREG_2, &value, mask));
+       *period = BITFEXT(value, BIT_PERIOD);
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets backlight operation mode. There are two modes of
+ * operations: current control and triode mode.
+ * The Duty Cycle/Cycle Time control is retained in Triode Mode. Audio
+ * coupling is not available in Triode Mode.
+ *
+ * @param        channel   Backlight channel.
+ * @param        mode      Backlight operation mode.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_set_mode(t_bklit_channel channel, t_bklit_mode mode)
+{
+       unsigned int reg_value = 0;
+       unsigned int clear_val = 0;
+       unsigned int triode_val = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       CHECK_ERROR(pmic_read_reg(LREG_0, &reg_value, PMIC_ALL_BITS));
+
+       switch (channel) {
+       case BACKLIGHT_LED1:
+               clear_val = ~(MASK_TRIODE_MAIN_BL);
+               triode_val = MASK_TRIODE_MAIN_BL;
+               break;
+       case BACKLIGHT_LED2:
+               clear_val = ~(MASK_TRIODE_MAIN_BL << INDEX_AUXILIARY);
+               triode_val = (MASK_TRIODE_MAIN_BL << INDEX_AUXILIARY);
+               break;
+       case BACKLIGHT_LED3:
+               clear_val = ~(MASK_TRIODE_MAIN_BL << INDEX_KEYPAD);
+               triode_val = (MASK_TRIODE_MAIN_BL << INDEX_KEYPAD);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       reg_value = (reg_value & clear_val);
+
+       if (mode == BACKLIGHT_TRIODE_MODE) {
+               reg_value = (reg_value | triode_val);
+       }
+
+       CHECK_ERROR(pmic_write_reg(LREG_0, reg_value, PMIC_ALL_BITS));
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function gets backlight operation mode. There are two modes of
+ * operations: current control and triode mode.
+ * The Duty Cycle/Cycle Time control is retained in Triode Mode. Audio
+ * coupling is not available in Triode Mode.
+ *
+ * @param        channel   Backlight channel.
+ * @param        mode      Backlight operation mode.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_get_mode(t_bklit_channel channel, t_bklit_mode *mode)
+{
+       unsigned int reg_value = 0;
+       unsigned int mask = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       switch (channel) {
+       case BACKLIGHT_LED1:
+               mask = BITFMASK(BIT_TRIODE_MAIN_BL);
+               break;
+       case BACKLIGHT_LED2:
+               mask = BITFMASK(BIT_TRIODE_AUX_BL);
+               break;
+       case BACKLIGHT_LED3:
+               mask = BITFMASK(BIT_TRIODE_KEY_BL);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_read_reg(LREG_0, &reg_value, mask));
+
+       switch (channel) {
+       case BACKLIGHT_LED1:
+               *mode = BITFEXT(reg_value, BIT_TRIODE_MAIN_BL);
+               break;
+       case BACKLIGHT_LED2:
+               *mode = BITFEXT(reg_value, BIT_TRIODE_AUX_BL);
+               break;
+       case BACKLIGHT_LED3:
+               *mode = BITFEXT(reg_value, BIT_TRIODE_KEY_BL);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function starts backlight brightness ramp up function; ramp time is
+ * fixed at 0.5 seconds.
+ *
+ * @param        channel   Backlight channel.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_rampup(t_bklit_channel channel)
+{
+       unsigned int reg_value = 0;
+       unsigned int mask = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       switch (channel) {
+       case BACKLIGHT_LED1:
+               mask = BITFMASK(BIT_UP_MAIN_BL);
+               reg_value = BITFVAL(BIT_UP_MAIN_BL, 1);
+               break;
+       case BACKLIGHT_LED2:
+               mask = BITFMASK(BIT_UP_AUX_BL);
+               reg_value = BITFVAL(BIT_UP_AUX_BL, 1);
+               break;
+       case BACKLIGHT_LED3:
+               mask = BITFMASK(BIT_UP_KEY_BL);
+               reg_value = BITFVAL(BIT_UP_KEY_BL, 1);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_write_reg(LREG_0, reg_value, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function stops backlight brightness ramp up function;
+ *
+ * @param        channel   Backlight channel.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_off_rampup(t_bklit_channel channel)
+{
+       unsigned int reg_value = 0;
+       unsigned int mask = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       switch (channel) {
+       case BACKLIGHT_LED1:
+               mask = BITFMASK(BIT_UP_MAIN_BL);
+               break;
+       case BACKLIGHT_LED2:
+               mask = BITFMASK(BIT_UP_AUX_BL);
+               break;
+       case BACKLIGHT_LED3:
+               mask = BITFMASK(BIT_UP_KEY_BL);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_write_reg(LREG_0, reg_value, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function starts backlight brightness ramp down function; ramp time is
+ * fixed at 0.5 seconds.
+ *
+ * @param        channel   Backlight channel.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_rampdown(t_bklit_channel channel)
+{
+       unsigned int reg_value = 0;
+       unsigned int mask = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       switch (channel) {
+       case BACKLIGHT_LED1:
+               mask = BITFMASK(BIT_DOWN_MAIN_BL);
+               reg_value = BITFVAL(BIT_DOWN_MAIN_BL, 1);
+               break;
+       case BACKLIGHT_LED2:
+               mask = BITFMASK(BIT_DOWN_AUX_BL);
+               reg_value = BITFVAL(BIT_DOWN_AUX_BL, 1);
+               break;
+       case BACKLIGHT_LED3:
+               mask = BITFMASK(BIT_DOWN_KEY_BL);
+               reg_value = BITFVAL(BIT_DOWN_KEY_BL, 1);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_write_reg(LREG_0, reg_value, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function stops backlight brightness ramp down function.
+ *
+ * @param        channel   Backlight channel.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_off_rampdown(t_bklit_channel channel)
+{
+       unsigned int reg_value = 0;
+       unsigned int mask = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       switch (channel) {
+       case BACKLIGHT_LED1:
+               mask = BITFMASK(BIT_DOWN_MAIN_BL);
+               break;
+       case BACKLIGHT_LED2:
+               mask = BITFMASK(BIT_DOWN_AUX_BL);
+               break;
+       case BACKLIGHT_LED3:
+               mask = BITFMASK(BIT_DOWN_KEY_BL);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_write_reg(LREG_0, reg_value, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function enables backlight analog edge slowing mode. Analog Edge
+ * Slowing slows down the transient edges to reduce the chance of coupling LED
+ * modulation activity into other circuits. Rise and fall times will be targeted
+ * for approximately 50usec.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_enable_edge_slow(void)
+{
+       unsigned int mask;
+       unsigned int value;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+       mask = BITFMASK(BIT_SLEWLIMBL);
+       value = BITFVAL(BIT_SLEWLIMBL, 1);
+       CHECK_ERROR(pmic_write_reg(LREG_2, value, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function disables backlight analog edge slowing mode. The backlight
+ * drivers will default to an <93>Instant On<94> mode.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_disable_edge_slow(void)
+{
+       unsigned int mask;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+       mask = BITFMASK(BIT_SLEWLIMBL);
+       CHECK_ERROR(pmic_write_reg(LREG_2, 0, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function gets backlight analog edge slowing mode. DThe backlight
+ *
+ * @param        edge      Edge slowing mode.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_get_edge_slow(bool *edge)
+{
+       unsigned int mask;
+       unsigned int value;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+       mask = BITFMASK(BIT_SLEWLIMBL);
+       CHECK_ERROR(pmic_read_reg(LREG_2, &value, mask));
+       *edge = (bool) BITFEXT(value, BIT_SLEWLIMBL);
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets backlight Strobe Light Pulsing mode.
+ *
+ * @param        channel   Backlight channel.
+ * @param        mode      Strobe Light Pulsing mode.
+ *
+ * @return       This function returns PMIC_NOT_SUPPORTED.
+ */
+PMIC_STATUS pmic_bklit_set_strobemode(t_bklit_channel channel,
+                                     t_bklit_strobe_mode mode)
+{
+       return PMIC_NOT_SUPPORTED;
+}
+
+/*!
+ * This function enables tri-color LED.
+ *
+ * @param        mode      Tri-color LED operation mode.
+ * @param       bank      Selected tri-color bank
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_enable(t_tcled_mode mode, t_funlight_bank bank)
+{
+       unsigned int mask = 0;
+       unsigned int value = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       switch (mode) {
+       case TCLED_FUN_MODE:
+               switch (bank) {
+               case TCLED_FUN_BANK1:
+                       mask = MASK_BK1_FL;
+                       value = MASK_BK1_FL;
+                       break;
+               case TCLED_FUN_BANK2:
+                       mask = MASK_BK2_FL;
+                       value = MASK_BK2_FL;
+                       break;
+               case TCLED_FUN_BANK3:
+                       mask = MASK_BK3_FL;
+                       value = MASK_BK3_FL;
+                       break;
+               default:
+                       return PMIC_PARAMETER_ERROR;
+               }
+               break;
+       case TCLED_IND_MODE:
+               mask = MASK_BK1_FL | MASK_BK2_FL | MASK_BK3_FL;
+               break;
+       }
+
+       CHECK_ERROR(pmic_write_reg(LREG_0, value, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function disables tri-color LED.
+ *
+ * @param        bank      Selected tri-color bank
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ *
+ */
+PMIC_STATUS pmic_tcled_disable(t_funlight_bank bank)
+{
+       unsigned int mask = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       switch (bank) {
+       case TCLED_FUN_BANK1:
+               mask = MASK_BK1_FL;
+               break;
+       case TCLED_FUN_BANK2:
+               mask = MASK_BK2_FL;
+               break;
+       case TCLED_FUN_BANK3:
+               mask = MASK_BK3_FL;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_write_reg(LREG_0, 0, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function retrives tri-color LED operation mode.
+ *
+ * @param        mode      Pointer to Tri-color LED operation mode.
+ * @param        bank      Selected tri-color bank
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_get_mode(t_tcled_mode *mode, t_funlight_bank bank)
+{
+       unsigned int val;
+       unsigned int mask;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       switch (bank) {
+       case TCLED_FUN_BANK1:
+               mask = MASK_BK1_FL;
+               break;
+       case TCLED_FUN_BANK2:
+               mask = MASK_BK2_FL;
+               break;
+       case TCLED_FUN_BANK3:
+               mask = MASK_BK3_FL;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_read_reg(LREG_0, &val, mask));
+
+       if (val) {
+               *mode = TCLED_FUN_MODE;
+       } else {
+               *mode = TCLED_IND_MODE;
+       }
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets a tri-color LED channel current level in indicator mode.
+ *
+ * @param        channel      Tri-color LED channel.
+ * @param        level        Current level.
+ * @param        bank         Selected tri-color bank
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_ind_set_current(t_ind_channel channel,
+                                      t_tcled_cur_level level,
+                                      t_funlight_bank bank)
+{
+       unsigned int reg_conf = 0;
+       unsigned int mask;
+       unsigned int value;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       if (level > TCLED_CUR_LEVEL_4) {
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       switch (bank) {
+       case TCLED_FUN_BANK1:
+               reg_conf = LREG_3;
+               break;
+       case TCLED_FUN_BANK2:
+               reg_conf = LREG_4;
+               break;
+       case TCLED_FUN_BANK3:
+               reg_conf = LREG_5;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       switch (channel) {
+       case TCLED_IND_RED:
+               value = BITFVAL(BITS_CL_RED, level);
+               mask = BITFMASK(BITS_CL_RED);
+               break;
+       case TCLED_IND_GREEN:
+               value = BITFVAL(BITS_CL_GREEN, level);
+               mask = BITFMASK(BITS_CL_GREEN);
+               break;
+       case TCLED_IND_BLUE:
+               value = BITFVAL(BITS_CL_BLUE, level);
+               mask = BITFMASK(BITS_CL_BLUE);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_write_reg(reg_conf, value, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function retrives a tri-color LED channel current level
+ * in indicator mode.
+ *
+ * @param        channel      Tri-color LED channel.
+ * @param        level        Pointer to current level.
+ * @param        bank         Selected tri-color bank
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_ind_get_current(t_ind_channel channel,
+                                      t_tcled_cur_level *level,
+                                      t_funlight_bank bank)
+{
+       unsigned int reg_conf = 0;
+       unsigned int mask = 0;
+       unsigned int value = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       switch (bank) {
+       case TCLED_FUN_BANK1:
+               reg_conf = LREG_3;
+               break;
+       case TCLED_FUN_BANK2:
+               reg_conf = LREG_4;
+               break;
+       case TCLED_FUN_BANK3:
+               reg_conf = LREG_5;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       switch (channel) {
+       case TCLED_IND_RED:
+               mask = BITFMASK(BITS_CL_RED);
+               break;
+       case TCLED_IND_GREEN:
+               mask = BITFMASK(BITS_CL_GREEN);
+               break;
+       case TCLED_IND_BLUE:
+               mask = BITFMASK(BITS_CL_BLUE);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_read_reg(reg_conf, &value, mask));
+
+       switch (channel) {
+       case TCLED_IND_RED:
+               *level = BITFEXT(value, BITS_CL_RED);
+               break;
+       case TCLED_IND_GREEN:
+               *level = BITFEXT(value, BITS_CL_GREEN);
+               break;
+       case TCLED_IND_BLUE:
+               *level = BITFEXT(value, BITS_CL_BLUE);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets a tri-color LED channel blinking pattern in indication
+ * mode.
+ *
+ * @param        channel      Tri-color LED channel.
+ * @param        pattern      Blinking pattern.
+ * @param        skip         If true, skip a cycle after each cycle.
+ * @param        bank         Selected tri-color bank
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_ind_set_blink_pattern(t_ind_channel channel,
+                                            t_tcled_ind_blink_pattern pattern,
+                                            bool skip, t_funlight_bank bank)
+{
+       unsigned int reg_conf = 0;
+       unsigned int mask = 0;
+       unsigned int value = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       if (skip == true) {
+               return PMIC_NOT_SUPPORTED;
+       }
+
+       switch (bank) {
+       case TCLED_FUN_BANK1:
+               reg_conf = LREG_3;
+               break;
+       case TCLED_FUN_BANK2:
+               reg_conf = LREG_4;
+               break;
+       case TCLED_FUN_BANK3:
+               reg_conf = LREG_5;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       switch (channel) {
+       case TCLED_IND_RED:
+               value = BITFVAL(BITS_DC_RED, pattern);
+               mask = BITFMASK(BITS_DC_RED);
+               break;
+       case TCLED_IND_GREEN:
+               value = BITFVAL(BITS_DC_GREEN, pattern);
+               mask = BITFMASK(BITS_DC_GREEN);
+               break;
+       case TCLED_IND_BLUE:
+               value = BITFVAL(BITS_DC_BLUE, pattern);
+               mask = BITFMASK(BITS_DC_BLUE);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_write_reg(reg_conf, value, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function retrives a tri-color LED channel blinking pattern in
+ * indication mode.
+ *
+ * @param        channel      Tri-color LED channel.
+ * @param        pattern      Pointer to Blinking pattern.
+ * @param        skip         Pointer to a boolean varible indicating if skip
+ * @param        bank         Selected tri-color bank
+ *                            a cycle after each cycle.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_ind_get_blink_pattern(t_ind_channel channel,
+                                            t_tcled_ind_blink_pattern *
+                                            pattern, bool *skip,
+                                            t_funlight_bank bank)
+{
+       unsigned int reg_conf = 0;
+       unsigned int mask = 0;
+       unsigned int value = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       switch (bank) {
+       case TCLED_FUN_BANK1:
+               reg_conf = LREG_3;
+               break;
+       case TCLED_FUN_BANK2:
+               reg_conf = LREG_4;
+               break;
+       case TCLED_FUN_BANK3:
+               reg_conf = LREG_5;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       switch (channel) {
+       case TCLED_IND_RED:
+               mask = BITFMASK(BITS_DC_RED);
+               break;
+       case TCLED_IND_GREEN:
+               mask = BITFMASK(BITS_DC_GREEN);
+               break;
+       case TCLED_IND_BLUE:
+               mask = BITFMASK(BITS_DC_BLUE);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_read_reg(reg_conf, &value, mask));
+
+       switch (channel) {
+       case TCLED_IND_RED:
+               *pattern = BITFEXT(value, BITS_DC_RED);
+               break;
+       case TCLED_IND_GREEN:
+               *pattern = BITFEXT(value, BITS_DC_GREEN);
+               break;
+       case TCLED_IND_BLUE:
+               *pattern = BITFEXT(value, BITS_DC_BLUE);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets a tri-color LED channel current level in Fun Light mode.
+ *
+ * @param        bank         Tri-color LED bank
+ * @param        channel      Tri-color LED channel.
+ * @param        level        Current level.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_set_current(t_funlight_bank bank,
+                                      t_funlight_channel channel,
+                                      t_tcled_cur_level level)
+{
+       unsigned int reg_conf = 0;
+       unsigned int mask;
+       unsigned int value;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       if (level > TCLED_CUR_LEVEL_4) {
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       switch (bank) {
+       case TCLED_FUN_BANK1:
+               reg_conf = LREG_3;
+               break;
+       case TCLED_FUN_BANK2:
+               reg_conf = LREG_4;
+               break;
+       case TCLED_FUN_BANK3:
+               reg_conf = LREG_5;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       switch (channel) {
+       case TCLED_FUN_CHANNEL1:
+               value = BITFVAL(BITS_CL_RED, level);
+               mask = BITFMASK(BITS_CL_RED);
+               break;
+       case TCLED_FUN_CHANNEL2:
+               value = BITFVAL(BITS_CL_GREEN, level);
+               mask = BITFMASK(BITS_CL_GREEN);
+               break;
+       case TCLED_FUN_CHANNEL3:
+               value = BITFVAL(BITS_CL_BLUE, level);
+               mask = BITFMASK(BITS_CL_BLUE);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_write_reg(reg_conf, value, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function retrives a tri-color LED channel current level
+ * in Fun Light mode.
+ *
+ * @param        bank         Tri-color LED bank
+ * @param        channel      Tri-color LED channel.
+ * @param        level        Pointer to current level.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_get_current(t_funlight_bank bank,
+                                      t_funlight_channel channel,
+                                      t_tcled_cur_level *level)
+{
+       unsigned int reg_conf = 0;
+       unsigned int mask = 0;
+       unsigned int value = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       switch (bank) {
+       case TCLED_FUN_BANK1:
+               reg_conf = LREG_3;
+               break;
+       case TCLED_FUN_BANK2:
+               reg_conf = LREG_4;
+               break;
+       case TCLED_FUN_BANK3:
+               reg_conf = LREG_5;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       switch (channel) {
+       case TCLED_FUN_CHANNEL1:
+               mask = BITFMASK(BITS_CL_RED);
+               break;
+       case TCLED_FUN_CHANNEL2:
+               mask = BITFMASK(BITS_CL_GREEN);
+               break;
+       case TCLED_FUN_CHANNEL3:
+               mask = BITFMASK(BITS_CL_BLUE);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_read_reg(reg_conf, &value, mask));
+
+       switch (channel) {
+       case TCLED_FUN_CHANNEL1:
+               *level = BITFEXT(value, BITS_CL_RED);
+               break;
+       case TCLED_FUN_CHANNEL2:
+               *level = BITFEXT(value, BITS_CL_GREEN);
+               break;
+       case TCLED_FUN_CHANNEL3:
+               *level = BITFEXT(value, BITS_CL_BLUE);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets tri-color LED cycle time.
+ *
+ * @param        bank         Tri-color LED bank
+ * @param        ct           Cycle time.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_set_cycletime(t_funlight_bank bank,
+                                        t_tcled_fun_cycle_time ct)
+{
+       unsigned int reg_conf = 0;
+       unsigned int mask;
+       unsigned int value;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       if (ct > TC_CYCLE_TIME_4) {
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       switch (bank) {
+       case TCLED_FUN_BANK1:
+               reg_conf = LREG_3;
+               break;
+       case TCLED_FUN_BANK2:
+               reg_conf = LREG_4;
+               break;
+       case TCLED_FUN_BANK3:
+               reg_conf = LREG_5;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       value = BITFVAL(BIT_PERIOD, ct);
+       mask = BITFMASK(BIT_PERIOD);
+
+       CHECK_ERROR(pmic_write_reg(reg_conf, value, mask));
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function retrives tri-color LED cycle time in Fun Light mode.
+ *
+ * @param        bank         Tri-color LED bank
+ * @param        ct           Pointer to cycle time.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_get_cycletime(t_funlight_bank bank,
+                                        t_tcled_fun_cycle_time *ct)
+{
+       unsigned int reg_conf = 0;
+       unsigned int mask;
+       unsigned int value;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       if (*ct > TC_CYCLE_TIME_4) {
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       switch (bank) {
+       case TCLED_FUN_BANK1:
+               reg_conf = LREG_3;
+               break;
+       case TCLED_FUN_BANK2:
+               reg_conf = LREG_4;
+               break;
+       case TCLED_FUN_BANK3:
+               reg_conf = LREG_5;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       mask = BITFMASK(BIT_PERIOD);
+       CHECK_ERROR(pmic_read_reg(reg_conf, &value, mask));
+
+       *ct = BITFVAL(BIT_PERIOD, value);
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets a tri-color LED channel duty cycle in Fun Light mode.
+ *
+ * @param        bank         Tri-color LED bank
+ * @param        channel      Tri-color LED channel.
+ * @param        dc           Duty cycle.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_set_dutycycle(t_funlight_bank bank,
+                                        t_funlight_channel channel,
+                                        unsigned char dc)
+{
+       unsigned int reg_conf = 0;
+       unsigned int mask = 0;
+       unsigned int value = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       switch (bank) {
+       case TCLED_FUN_BANK1:
+               reg_conf = LREG_3;
+               break;
+       case TCLED_FUN_BANK2:
+               reg_conf = LREG_4;
+               break;
+       case TCLED_FUN_BANK3:
+               reg_conf = LREG_5;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       switch (channel) {
+       case TCLED_FUN_CHANNEL1:
+               value = BITFVAL(BITS_DC_RED, dc);
+               mask = BITFMASK(BITS_DC_RED);
+               break;
+       case TCLED_FUN_CHANNEL2:
+               value = BITFVAL(BITS_DC_GREEN, dc);
+               mask = BITFMASK(BITS_DC_GREEN);
+               break;
+       case TCLED_FUN_CHANNEL3:
+               value = BITFVAL(BITS_DC_BLUE, dc);
+               mask = BITFMASK(BITS_DC_BLUE);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_write_reg(reg_conf, value, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function retrives a tri-color LED channel duty cycle in Fun Light mode.
+ *
+ * @param        bank         Tri-color LED bank
+ * @param        channel      Tri-color LED channel.
+ * @param        dc           Pointer to duty cycle.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_get_dutycycle(t_funlight_bank bank,
+                                        t_funlight_channel channel,
+                                        unsigned char *dc)
+{
+       unsigned int reg_conf = 0;
+       unsigned int mask = 0;
+       unsigned int value = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       switch (bank) {
+       case TCLED_FUN_BANK1:
+               reg_conf = LREG_3;
+               break;
+       case TCLED_FUN_BANK2:
+               reg_conf = LREG_4;
+               break;
+       case TCLED_FUN_BANK3:
+               reg_conf = LREG_5;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       switch (channel) {
+       case TCLED_FUN_CHANNEL1:
+               mask = BITFMASK(BITS_DC_RED);
+               break;
+       case TCLED_FUN_CHANNEL2:
+               mask = BITFMASK(BITS_DC_GREEN);
+               break;
+       case TCLED_FUN_CHANNEL3:
+               mask = BITFMASK(BITS_DC_BLUE);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_read_reg(reg_conf, &value, mask));
+
+       switch (channel) {
+       case TCLED_FUN_CHANNEL1:
+               *dc = BITFEXT(value, BITS_DC_RED);
+               break;
+       case TCLED_FUN_CHANNEL2:
+               *dc = BITFEXT(value, BITS_DC_GREEN);
+               break;
+       case TCLED_FUN_CHANNEL3:
+               *dc = BITFEXT(value, BITS_DC_BLUE);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function initiates Blended Ramp fun light pattern.
+ *
+ * @param        bank         Tri-color LED bank
+ * @param        speed        Speed of pattern.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_blendedramps(t_funlight_bank bank,
+                                       t_tcled_fun_speed speed)
+{
+       unsigned int mask = 0;
+       unsigned int value = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       switch (speed) {
+       case TC_OFF:
+               value = BITFVAL(BITS_FUN_LIGHT, FUN_LIGHTS_OFF);
+               break;
+       case TC_SLOW:
+               value = BITFVAL(BITS_FUN_LIGHT, BLENDED_RAMPS_SLOW);
+               break;
+       case TC_FAST:
+               value = BITFVAL(BITS_FUN_LIGHT, BLENDED_RAMPS_FAST);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       mask = BITFMASK(BITS_FUN_LIGHT);
+       CHECK_ERROR(pmic_write_reg(LREG_0, value, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function initiates Saw Ramp fun light pattern.
+ *
+ * @param        bank         Tri-color LED bank
+ * @param        speed        Speed of pattern.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_sawramps(t_funlight_bank bank,
+                                   t_tcled_fun_speed speed)
+{
+       unsigned int mask = 0;
+       unsigned int value = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       switch (speed) {
+       case TC_OFF:
+               value = BITFVAL(BITS_FUN_LIGHT, FUN_LIGHTS_OFF);
+               break;
+       case TC_SLOW:
+               value = BITFVAL(BITS_FUN_LIGHT, SAW_RAMPS_SLOW);
+               break;
+       case TC_FAST:
+               value = BITFVAL(BITS_FUN_LIGHT, SAW_RAMPS_FAST);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       mask = BITFMASK(BITS_FUN_LIGHT);
+       CHECK_ERROR(pmic_write_reg(LREG_0, value, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function initiates Blended Bowtie fun light pattern.
+ *
+ * @param        bank         Tri-color LED bank
+ * @param        speed        Speed of pattern.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_blendedbowtie(t_funlight_bank bank,
+                                        t_tcled_fun_speed speed)
+{
+       unsigned int mask = 0;
+       unsigned int value = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       switch (speed) {
+       case TC_OFF:
+               value = BITFVAL(BITS_FUN_LIGHT, FUN_LIGHTS_OFF);
+               break;
+       case TC_SLOW:
+               value = BITFVAL(BITS_FUN_LIGHT, BLENDED_INVERSE_RAMPS_SLOW);
+               break;
+       case TC_FAST:
+               value = BITFVAL(BITS_FUN_LIGHT, BLENDED_INVERSE_RAMPS_FAST);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       mask = BITFMASK(BITS_FUN_LIGHT);
+       CHECK_ERROR(pmic_write_reg(LREG_0, value, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function initiates Chasing Lights fun light pattern.
+ *
+ * @param        bank         Tri-color LED bank
+ * @param        pattern      Chasing light pattern mode.
+ * @param        speed        Speed of pattern.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_chasinglightspattern(t_funlight_bank bank,
+                                               t_chaselight_pattern pattern,
+                                               t_tcled_fun_speed speed)
+{
+       unsigned int mask = 0;
+       unsigned int value = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       if (pattern > BGR) {
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       switch (speed) {
+       case TC_OFF:
+               value = BITFVAL(BITS_FUN_LIGHT, FUN_LIGHTS_OFF);
+               break;
+       case TC_SLOW:
+               if (pattern == PMIC_RGB) {
+                       value =
+                           BITFVAL(BITS_FUN_LIGHT, CHASING_LIGHTS_RGB_SLOW);
+               } else {
+                       value =
+                           BITFVAL(BITS_FUN_LIGHT, CHASING_LIGHTS_BGR_SLOW);
+               }
+               break;
+       case TC_FAST:
+               if (pattern == PMIC_RGB) {
+                       value =
+                           BITFVAL(BITS_FUN_LIGHT, CHASING_LIGHTS_RGB_FAST);
+               } else {
+                       value =
+                           BITFVAL(BITS_FUN_LIGHT, CHASING_LIGHTS_BGR_FAST);
+               }
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       mask = BITFMASK(BITS_FUN_LIGHT);
+       CHECK_ERROR(pmic_write_reg(LREG_0, value, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function initiates Strobe Mode fun light pattern.
+ *
+ * @param        bank         Tri-color LED bank
+ * @param        channel      Tri-color LED channel.
+ * @param        speed        Speed of pattern.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_strobe(t_funlight_bank bank,
+                                 t_funlight_channel channel,
+                                 t_tcled_fun_strobe_speed speed)
+{
+       /* not supported on mc13783 */
+
+       return PMIC_NOT_SUPPORTED;
+}
+
+/*!
+ * This function initiates Tri-color LED brightness Ramp Up function; Ramp time
+ * is fixed at 1 second.
+ *
+ * @param        bank         Tri-color LED bank
+ * @param        channel      Tri-color LED channel.
+ * @param        rampup       Ramp-up configuration.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_rampup(t_funlight_bank bank,
+                                 t_funlight_channel channel, bool rampup)
+{
+       unsigned int mask = 0;
+       unsigned int value = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       switch (bank) {
+       case TCLED_FUN_BANK1:
+               mask = LEDR1RAMPUP;
+               value = LEDR1RAMPUP;
+               break;
+       case TCLED_FUN_BANK2:
+               mask = LEDR2RAMPUP;
+               value = LEDR2RAMPUP;
+               break;
+       case TCLED_FUN_BANK3:
+               mask = LEDR3RAMPUP;
+               value = LEDR3RAMPUP;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       switch (channel) {
+       case TCLED_FUN_CHANNEL1:
+               mask = mask;
+               value = value;
+               break;
+       case TCLED_FUN_CHANNEL2:
+               mask = mask * 2;
+               value = value * 2;
+               break;
+       case TCLED_FUN_CHANNEL3:
+               mask = mask * 4;
+               value = value * 4;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       if (!rampup) {
+               value = 0;
+       }
+
+       CHECK_ERROR(pmic_write_reg(LREG_1, value, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function gets Tri-color LED brightness Ramp Up function; Ramp time
+ * is fixed at 1 second.
+ *
+ * @param        bank         Tri-color LED bank
+ * @param        channel      Tri-color LED channel.
+ * @param        rampup       Ramp-up configuration.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_get_fun_rampup(t_funlight_bank bank,
+                                     t_funlight_channel channel, bool *rampup)
+{
+       unsigned int mask = 0;
+       unsigned int value = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       switch (bank) {
+       case TCLED_FUN_BANK1:
+               mask = LEDR1RAMPUP;
+               break;
+       case TCLED_FUN_BANK2:
+               mask = LEDR2RAMPUP;
+               break;
+       case TCLED_FUN_BANK3:
+               mask = LEDR3RAMPUP;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       switch (channel) {
+       case TCLED_FUN_CHANNEL1:
+               mask = mask;
+               break;
+       case TCLED_FUN_CHANNEL2:
+               mask = mask * 2;
+               break;
+       case TCLED_FUN_CHANNEL3:
+               mask = mask * 4;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_read_reg(LREG_1, &value, mask));
+       if (value) {
+               *rampup = true;
+       } else {
+               *rampup = false;
+       }
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function initiates Tri-color LED brightness Ramp Down function; Ramp
+ * time is fixed at 1 second.
+ *
+ * @param        bank         Tri-color LED bank
+ * @param        channel      Tri-color LED channel.
+ * @param        rampdown     Ramp-down configuration.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_rampdown(t_funlight_bank bank,
+                                   t_funlight_channel channel, bool rampdown)
+{
+       unsigned int mask = 0;
+       unsigned int value = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       switch (bank) {
+       case TCLED_FUN_BANK1:
+               mask = LEDR1RAMPDOWN;
+               value = LEDR1RAMPDOWN;
+               break;
+       case TCLED_FUN_BANK2:
+               mask = LEDR2RAMPDOWN;
+               value = LEDR2RAMPDOWN;
+               break;
+       case TCLED_FUN_BANK3:
+               mask = LEDR3RAMPDOWN;
+               value = LEDR3RAMPDOWN;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       switch (channel) {
+       case TCLED_FUN_CHANNEL1:
+               mask = mask;
+               value = value;
+               break;
+       case TCLED_FUN_CHANNEL2:
+               mask = mask * 2;
+               value = value * 2;
+               break;
+       case TCLED_FUN_CHANNEL3:
+               mask = mask * 4;
+               value = value * 4;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       if (!rampdown) {
+               value = 0;
+       }
+
+       CHECK_ERROR(pmic_write_reg(LREG_1, value, mask));
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function initiates Tri-color LED brightness Ramp Down function; Ramp
+ * time is fixed at 1 second.
+ *
+ * @param        bank         Tri-color LED bank
+ * @param        channel      Tri-color LED channel.
+ * @param        rampdown     Ramp-down configuration.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_get_fun_rampdown(t_funlight_bank bank,
+                                       t_funlight_channel channel,
+                                       bool *rampdown)
+{
+       unsigned int mask = 0;
+       unsigned int value = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       switch (bank) {
+       case TCLED_FUN_BANK1:
+               mask = LEDR1RAMPDOWN;
+               break;
+       case TCLED_FUN_BANK2:
+               mask = LEDR2RAMPDOWN;
+               break;
+       case TCLED_FUN_BANK3:
+               mask = LEDR3RAMPDOWN;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       switch (channel) {
+       case TCLED_FUN_CHANNEL1:
+               mask = mask;
+               break;
+       case TCLED_FUN_CHANNEL2:
+               mask = mask * 2;
+               break;
+       case TCLED_FUN_CHANNEL3:
+               mask = mask * 4;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_read_reg(LREG_1, &value, mask));
+       if (value) {
+               *rampdown = true;
+       } else {
+               *rampdown = false;
+       }
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function enables a Tri-color channel triode mode.
+ *
+ * @param        bank         Tri-color LED bank
+ * @param        channel      Tri-color LED channel.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_triode_on(t_funlight_bank bank,
+                                    t_funlight_channel channel)
+{
+       unsigned int mask = 0;
+       unsigned int value = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       switch (bank) {
+       case TCLED_FUN_BANK1:
+               mask = MASK_BK1_FL;
+               value = ENABLE_BK1_FL;
+               break;
+       case TCLED_FUN_BANK2:
+               mask = MASK_BK2_FL;
+               value = ENABLE_BK2_FL;
+               break;
+       case TCLED_FUN_BANK3:
+               mask = MASK_BK3_FL;
+               value = ENABLE_BK2_FL;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_write_reg(LREG_0, value, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function disables a Tri-color LED channel triode mode.
+ *
+ * @param        bank         Tri-color LED bank
+ * @param        channel      Tri-color LED channel.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_triode_off(t_funlight_bank bank,
+                                     t_funlight_channel channel)
+{
+       unsigned int mask = 0;
+       unsigned int value = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       switch (bank) {
+       case TCLED_FUN_BANK1:
+               mask = MASK_BK1_FL;
+               break;
+       case TCLED_FUN_BANK2:
+               mask = MASK_BK2_FL;
+               break;
+       case TCLED_FUN_BANK3:
+               mask = MASK_BK3_FL;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_write_reg(LREG_0, value, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function enables Tri-color LED edge slowing.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_enable_edge_slow(void)
+{
+       unsigned int mask = 0;
+       unsigned int value = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       value = BITFVAL(BIT_SLEWLIMTC, 1);
+       mask = BITFMASK(BIT_SLEWLIMTC);
+
+       CHECK_ERROR(pmic_write_reg(LREG_1, value, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function disables Tri-color LED edge slowing.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_disable_edge_slow(void)
+{
+       unsigned int mask = 0;
+       unsigned int value = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       value = BITFVAL(BIT_SLEWLIMTC, 0);
+       mask = BITFMASK(BIT_SLEWLIMTC);
+
+       CHECK_ERROR(pmic_write_reg(LREG_1, value, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function enables Tri-color LED half current mode.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_enable_half_current(void)
+{
+       unsigned int mask = 0;
+       unsigned int value = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       value = BITFVAL(BIT_TC1HALF, 1);
+       mask = BITFMASK(BIT_TC1HALF);
+
+       CHECK_ERROR(pmic_write_reg(LREG_1, value, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function disables Tri-color LED half current mode.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_disable_half_current(void)
+{
+       unsigned int mask = 0;
+       unsigned int value = 0;
+
+       if (suspend_flag == 1) {
+               return -EBUSY;
+       }
+
+       value = BITFVAL(BIT_TC1HALF, 0);
+       mask = BITFMASK(BIT_TC1HALF);
+
+       CHECK_ERROR(pmic_write_reg(LREG_1, value, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function enables backlight or Tri-color LED audio modulation.
+ *
+ * @return       This function returns PMIC_NOT_SUPPORTED.
+ */
+PMIC_STATUS pmic_tcled_enable_audio_modulation(t_led_channel channel,
+                                              t_aud_path path,
+                                              t_aud_gain gain, bool lpf_bypass)
+{
+       return PMIC_NOT_SUPPORTED;
+}
+
+/*!
+ * This function disables backlight or Tri-color LED audio modulation.
+ *
+ * @return       This function returns PMIC_NOT_SUPPORTED.
+ */
+PMIC_STATUS pmic_tcled_disable_audio_modulation(void)
+{
+       return PMIC_NOT_SUPPORTED;
+}
+
+/*!
+ * This function enables the boost mode.
+ * Only on mc13783 2.0 or higher
+ *
+ * @param       en_dis   Enable or disable the boost mode
+ *
+ * @return      This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_bklit_set_boost_mode(bool en_dis)
+{
+
+       pmic_version_t mc13783_ver;
+       unsigned int mask;
+       unsigned int value;
+       mc13783_ver = pmic_get_version();
+       if (mc13783_ver.revision >= 20) {
+
+               if (suspend_flag == 1) {
+                       return -EBUSY;
+               }
+
+               value = BITFVAL(BIT_BOOSTEN, en_dis);
+               mask = BITFMASK(BIT_BOOSTEN);
+               CHECK_ERROR(pmic_write_reg(LREG_0, value, mask));
+               return PMIC_SUCCESS;
+       } else {
+               return PMIC_NOT_SUPPORTED;
+       }
+}
+
+/*!
+ * This function gets the boost mode.
+ * Only on mc13783 2.0 or higher
+ *
+ * @param       en_dis   Enable or disable the boost mode
+ *
+ * @return      This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_bklit_get_boost_mode(bool *en_dis)
+{
+       pmic_version_t mc13783_ver;
+       unsigned int mask;
+       unsigned int value;
+       mc13783_ver = pmic_get_version();
+       if (mc13783_ver.revision >= 20) {
+
+               if (suspend_flag == 1) {
+                       return -EBUSY;
+               }
+               mask = BITFMASK(BIT_BOOSTEN);
+               CHECK_ERROR(pmic_read_reg(LREG_0, &value, mask));
+               *en_dis = BITFEXT(value, BIT_BOOSTEN);
+               return PMIC_SUCCESS;
+       } else {
+               return PMIC_NOT_SUPPORTED;
+       }
+}
+
+/*!
+ * This function sets boost mode configuration
+ * Only on mc13783 2.0 or higher
+ *
+ * @param    abms      Define adaptive boost mode selection
+ * @param    abr       Define adaptive boost reference
+ *
+ * @return       This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_bklit_config_boost_mode(unsigned int abms, unsigned int abr)
+{
+       unsigned int conf_boost = 0;
+       unsigned int mask;
+       unsigned int value;
+       pmic_version_t mc13783_ver;
+
+       mc13783_ver = pmic_get_version();
+       if (mc13783_ver.revision >= 20) {
+               if (suspend_flag == 1) {
+                       return -EBUSY;
+               }
+
+               if (abms > MAX_BOOST_ABMS) {
+                       return PMIC_PARAMETER_ERROR;
+               }
+
+               if (abr > MAX_BOOST_ABR) {
+                       return PMIC_PARAMETER_ERROR;
+               }
+
+               conf_boost = abms | (abr << 3);
+
+               value = BITFVAL(BITS_BOOST, conf_boost);
+               mask = BITFMASK(BITS_BOOST);
+               CHECK_ERROR(pmic_write_reg(LREG_0, value, mask));
+
+               return PMIC_SUCCESS;
+       } else {
+               return PMIC_NOT_SUPPORTED;
+       }
+}
+
+/*!
+ * This function gets boost mode configuration
+ * Only on mc13783 2.0 or higher
+ *
+ * @param    abms      Define adaptive boost mode selection
+ * @param    abr       Define adaptive boost reference
+ *
+ * @return       This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_bklit_gets_boost_mode(unsigned int *abms, unsigned int *abr)
+{
+       unsigned int mask;
+       unsigned int value;
+       pmic_version_t mc13783_ver;
+       mc13783_ver = pmic_get_version();
+       if (mc13783_ver.revision >= 20) {
+               if (suspend_flag == 1) {
+                       return -EBUSY;
+               }
+
+               mask = BITFMASK(BITS_BOOST_ABMS);
+               CHECK_ERROR(pmic_read_reg(LREG_0, &value, mask));
+               *abms = BITFEXT(value, BITS_BOOST_ABMS);
+
+               mask = BITFMASK(BITS_BOOST_ABR);
+               CHECK_ERROR(pmic_read_reg(LREG_0, &value, mask));
+               *abr = BITFEXT(value, BITS_BOOST_ABR);
+               return PMIC_SUCCESS;
+       } else {
+               return PMIC_NOT_SUPPORTED;
+       }
+}
+
+/*!
+ * This function implements IOCTL controls on a PMIC Light device.
+ *
+
+ * @param        inode       pointer on the node
+ * @param        file        pointer on the file
+ * @param        cmd         the command
+ * @param        arg         the parameter
+ * @return       This function returns 0 if successful.
+ */
+static int pmic_light_ioctl(struct inode *inode, struct file *file,
+                           unsigned int cmd, unsigned long arg)
+{
+       t_bklit_setting_param *bklit_setting = NULL;
+       t_tcled_enable_param *tcled_setting;
+       t_fun_param *fun_param;
+       t_tcled_ind_param *tcled_ind;
+
+       if (_IOC_TYPE(cmd) != 'p')
+               return -ENOTTY;
+
+       bklit_setting = kmalloc(sizeof(t_bklit_setting_param), GFP_KERNEL);
+       tcled_setting = kmalloc(sizeof(t_tcled_enable_param), GFP_KERNEL);
+       fun_param = kmalloc(sizeof(t_fun_param), GFP_KERNEL);
+       tcled_ind = kmalloc(sizeof(t_tcled_ind_param), GFP_KERNEL);
+       switch (cmd) {
+       case PMIC_BKLIT_TCLED_ENABLE:
+               pmic_bklit_tcled_master_enable();
+               break;
+
+       case PMIC_BKLIT_TCLED_DISABLE:
+               pmic_bklit_tcled_master_disable();
+               break;
+
+       case PMIC_BKLIT_ENABLE:
+               pmic_bklit_master_enable();
+               break;
+
+       case PMIC_BKLIT_DISABLE:
+               pmic_bklit_master_disable();
+               break;
+
+       case PMIC_SET_BKLIT:
+               if (bklit_setting == NULL)
+                       return -ENOMEM;
+
+               if (copy_from_user(bklit_setting, (t_bklit_setting_param *) arg,
+                                  sizeof(t_bklit_setting_param))) {
+                       kfree(bklit_setting);
+                       return -EFAULT;
+               }
+
+               CHECK_ERROR_KFREE(pmic_bklit_set_mode(bklit_setting->channel,
+                                                     bklit_setting->mode),
+                                 (kfree(bklit_setting)));
+
+               CHECK_ERROR_KFREE(pmic_bklit_set_current(bklit_setting->channel,
+                                                        bklit_setting->
+                                                        current_level),
+                                 (kfree(bklit_setting)));
+               CHECK_ERROR_KFREE(pmic_bklit_set_dutycycle
+                                 (bklit_setting->channel,
+                                  bklit_setting->duty_cycle),
+                                 (kfree(bklit_setting)));
+               CHECK_ERROR_KFREE(pmic_bklit_set_cycle_time
+                                 (bklit_setting->cycle_time),
+                                 (kfree(bklit_setting)));
+               CHECK_ERROR_KFREE(pmic_bklit_set_boost_mode
+                                 (bklit_setting->en_dis),
+                                 (kfree(bklit_setting)));
+               CHECK_ERROR_KFREE(pmic_bklit_config_boost_mode
+                                 (bklit_setting->abms, bklit_setting->abr),
+                                 (kfree(bklit_setting)));
+               if (bklit_setting->edge_slow != false) {
+                       CHECK_ERROR_KFREE(pmic_bklit_enable_edge_slow(),
+                                         (kfree(bklit_setting)));
+               } else {
+                       CHECK_ERROR_KFREE(pmic_bklit_disable_edge_slow(),
+                                         (kfree(bklit_setting)));
+               }
+
+               kfree(bklit_setting);
+               break;
+
+       case PMIC_GET_BKLIT:
+               if (bklit_setting == NULL)
+                       return -ENOMEM;
+
+               if (copy_from_user(bklit_setting, (t_bklit_setting_param *) arg,
+                                  sizeof(t_bklit_setting_param))) {
+                       kfree(bklit_setting);
+                       return -EFAULT;
+               }
+
+               CHECK_ERROR_KFREE(pmic_bklit_get_current(bklit_setting->channel,
+                                                        &bklit_setting->
+                                                        current_level),
+                                 (kfree(bklit_setting)));
+               CHECK_ERROR_KFREE(pmic_bklit_get_cycle_time
+                                 (&bklit_setting->cycle_time),
+                                 (kfree(bklit_setting)));
+               CHECK_ERROR_KFREE(pmic_bklit_get_dutycycle
+                                 (bklit_setting->channel,
+                                  &bklit_setting->duty_cycle),
+                                 (kfree(bklit_setting)));
+               bklit_setting->strobe = BACKLIGHT_STROBE_NONE;
+               CHECK_ERROR_KFREE(pmic_bklit_get_mode(bklit_setting->channel,
+                                                     &bklit_setting->mode),
+                                 (kfree(bklit_setting)));
+               CHECK_ERROR_KFREE(pmic_bklit_get_edge_slow
+                                 (&bklit_setting->edge_slow),
+                                 (kfree(bklit_setting)));
+               CHECK_ERROR_KFREE(pmic_bklit_get_boost_mode
+                                 (&bklit_setting->en_dis),
+                                 (kfree(bklit_setting)));
+               CHECK_ERROR_KFREE(pmic_bklit_gets_boost_mode
+                                 (&bklit_setting->abms, &bklit_setting->abr),
+                                 (kfree(bklit_setting)));
+
+               if (copy_to_user((t_bklit_setting_param *) arg, bklit_setting,
+                                sizeof(t_bklit_setting_param))) {
+                       kfree(bklit_setting);
+                       return -EFAULT;
+               }
+               kfree(bklit_setting);
+               break;
+
+       case PMIC_RAMPUP_BKLIT:
+               CHECK_ERROR(pmic_bklit_rampup((t_bklit_channel) arg));
+               break;
+
+       case PMIC_RAMPDOWN_BKLIT:
+               CHECK_ERROR(pmic_bklit_rampdown((t_bklit_channel) arg));
+               break;
+
+       case PMIC_OFF_RAMPUP_BKLIT:
+               CHECK_ERROR(pmic_bklit_off_rampup((t_bklit_channel) arg));
+               break;
+
+       case PMIC_OFF_RAMPDOWN_BKLIT:
+               CHECK_ERROR(pmic_bklit_off_rampdown((t_bklit_channel) arg));
+               break;
+
+       case PMIC_TCLED_ENABLE:
+               if (tcled_setting == NULL)
+                       return -ENOMEM;
+
+               if (copy_from_user(tcled_setting, (t_tcled_enable_param *) arg,
+                                  sizeof(t_tcled_enable_param))) {
+                       kfree(tcled_setting);
+                       return -EFAULT;
+               }
+               CHECK_ERROR_KFREE(pmic_tcled_enable(tcled_setting->mode,
+                                                   tcled_setting->bank),
+                                 (kfree(bklit_setting)));
+               break;
+
+       case PMIC_TCLED_DISABLE:
+               CHECK_ERROR(pmic_tcled_disable((t_funlight_bank) arg));
+               break;
+
+       case PMIC_TCLED_PATTERN:
+               if (fun_param == NULL)
+                       return -ENOMEM;
+
+               if (copy_from_user(fun_param,
+                                  (t_fun_param *) arg, sizeof(t_fun_param))) {
+                       kfree(fun_param);
+                       return -EFAULT;
+               }
+
+               switch (fun_param->pattern) {
+               case BLENDED_RAMPS_SLOW:
+                       CHECK_ERROR_KFREE(pmic_tcled_fun_blendedramps
+                                         (fun_param->bank, TC_SLOW),
+                                         (kfree(fun_param)));
+                       break;
+
+               case BLENDED_RAMPS_FAST:
+                       CHECK_ERROR_KFREE(pmic_tcled_fun_blendedramps
+                                         (fun_param->bank, TC_FAST),
+                                         (kfree(fun_param)));
+                       break;
+
+               case SAW_RAMPS_SLOW:
+                       CHECK_ERROR_KFREE(pmic_tcled_fun_sawramps
+                                         (fun_param->bank, TC_SLOW),
+                                         (kfree(fun_param)));
+                       break;
+
+               case SAW_RAMPS_FAST:
+                       CHECK_ERROR_KFREE(pmic_tcled_fun_sawramps
+                                         (fun_param->bank, TC_FAST),
+                                         (kfree(fun_param)));
+                       break;
+
+               case BLENDED_BOWTIE_SLOW:
+                       CHECK_ERROR_KFREE(pmic_tcled_fun_blendedbowtie
+                                         (fun_param->bank, TC_SLOW),
+                                         (kfree(fun_param)));
+                       break;
+
+               case BLENDED_BOWTIE_FAST:
+                       CHECK_ERROR_KFREE(pmic_tcled_fun_blendedbowtie
+                                         (fun_param->bank, TC_FAST),
+                                         (kfree(fun_param)));
+                       break;
+
+               case STROBE_SLOW:
+                       CHECK_ERROR_KFREE(pmic_tcled_fun_strobe
+                                         (fun_param->bank, fun_param->channel,
+                                          TC_STROBE_SLOW), (kfree(fun_param)));
+                       break;
+
+               case STROBE_FAST:
+                       CHECK_ERROR_KFREE(pmic_tcled_fun_strobe
+                                         (fun_param->bank,
+                                          fun_param->channel, TC_STROBE_SLOW),
+                                         (kfree(fun_param)));
+                       break;
+
+               case CHASING_LIGHT_RGB_SLOW:
+                       CHECK_ERROR_KFREE(pmic_tcled_fun_chasinglightspattern
+                                         (fun_param->bank, PMIC_RGB, TC_SLOW),
+                                         (kfree(fun_param)));
+                       break;
+
+               case CHASING_LIGHT_RGB_FAST:
+                       CHECK_ERROR_KFREE(pmic_tcled_fun_chasinglightspattern
+                                         (fun_param->bank, PMIC_RGB, TC_FAST),
+                                         (kfree(fun_param)));
+                       break;
+
+               case CHASING_LIGHT_BGR_SLOW:
+                       CHECK_ERROR_KFREE(pmic_tcled_fun_chasinglightspattern
+                                         (fun_param->bank, BGR, TC_SLOW),
+                                         (kfree(fun_param)));
+                       break;
+
+               case CHASING_LIGHT_BGR_FAST:
+                       CHECK_ERROR_KFREE(pmic_tcled_fun_chasinglightspattern
+                                         (fun_param->bank, BGR, TC_FAST),
+                                         (kfree(fun_param)));
+                       break;
+               }
+
+               kfree(fun_param);
+               break;
+
+       case PMIC_SET_TCLED:
+               if (tcled_ind == NULL)
+                       return -ENOMEM;
+
+               if (copy_from_user(tcled_ind, (t_tcled_ind_param *) arg,
+                                  sizeof(t_tcled_ind_param))) {
+                       kfree(tcled_ind);
+                       return -EFAULT;
+               }
+               CHECK_ERROR_KFREE(pmic_tcled_ind_set_current(tcled_ind->channel,
+                                                            tcled_ind->level,
+                                                            tcled_ind->bank),
+                                 (kfree(tcled_ind)));
+               CHECK_ERROR_KFREE(pmic_tcled_ind_set_blink_pattern
+                                 (tcled_ind->channel, tcled_ind->pattern,
+                                  tcled_ind->skip, tcled_ind->bank),
+                                 (kfree(tcled_ind)));
+               CHECK_ERROR_KFREE(pmic_tcled_fun_rampup
+                                 (tcled_ind->bank, tcled_ind->channel,
+                                  tcled_ind->rampup), (kfree(tcled_ind)));
+               CHECK_ERROR_KFREE(pmic_tcled_fun_rampdown
+                                 (tcled_ind->bank, tcled_ind->channel,
+                                  tcled_ind->rampdown), (kfree(tcled_ind)));
+               if (tcled_ind->half_current) {
+                       CHECK_ERROR_KFREE(pmic_tcled_enable_half_current(),
+                                         (kfree(tcled_ind)));
+               } else {
+                       CHECK_ERROR_KFREE(pmic_tcled_disable_half_current(),
+                                         (kfree(tcled_ind)));
+               }
+
+               kfree(tcled_ind);
+               break;
+
+       case PMIC_GET_TCLED:
+               if (tcled_ind == NULL)
+                       return -ENOMEM;
+
+               if (copy_from_user(tcled_ind, (t_tcled_ind_param *) arg,
+                                  sizeof(t_tcled_ind_param))) {
+                       kfree(tcled_ind);
+                       return -EFAULT;
+               }
+               CHECK_ERROR_KFREE(pmic_tcled_ind_get_current(tcled_ind->channel,
+                                                            &tcled_ind->level,
+                                                            tcled_ind->bank),
+                                 (kfree(tcled_ind)));
+               CHECK_ERROR_KFREE(pmic_tcled_ind_get_blink_pattern
+                                 (tcled_ind->channel, &tcled_ind->pattern,
+                                  &tcled_ind->skip, tcled_ind->bank),
+                                 (kfree(tcled_ind)));
+               CHECK_ERROR_KFREE(pmic_tcled_get_fun_rampup
+                                 (tcled_ind->bank, tcled_ind->channel,
+                                  &tcled_ind->rampup), (kfree(tcled_ind)));
+               CHECK_ERROR_KFREE(pmic_tcled_get_fun_rampdown
+                                 (tcled_ind->bank, tcled_ind->channel,
+                                  &tcled_ind->rampdown), (kfree(tcled_ind)));
+               if (copy_to_user
+                   ((t_tcled_ind_param *) arg, tcled_ind,
+                    sizeof(t_tcled_ind_param))) {
+                       return -EFAULT;
+               }
+               kfree(tcled_ind);
+
+               break;
+
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+/*!
+ * This function initialize Light registers of mc13783 with 0.
+ *
+ * @return       This function returns 0 if successful.
+ */
+int pmic_light_init_reg(void)
+{
+       CHECK_ERROR(pmic_write_reg(LREG_0, 0, PMIC_ALL_BITS));
+       CHECK_ERROR(pmic_write_reg(LREG_1, 0, PMIC_ALL_BITS));
+       CHECK_ERROR(pmic_write_reg(LREG_2, 0, PMIC_ALL_BITS));
+       CHECK_ERROR(pmic_write_reg(LREG_3, 0, PMIC_ALL_BITS));
+       CHECK_ERROR(pmic_write_reg(LREG_4, 0, PMIC_ALL_BITS));
+       CHECK_ERROR(pmic_write_reg(LREG_5, 0, PMIC_ALL_BITS));
+       return 0;
+}
+
+/*!
+ * This function implements the open method on a mc13783 light device.
+ *
+ * @param        inode       pointer on the node
+ * @param        file        pointer on the file
+ * @return       This function returns 0.
+ */
+static int pmic_light_open(struct inode *inode, struct file *file)
+{
+       while (suspend_flag == 1) {
+               swait++;
+               /* Block if the device is suspended */
+               if (wait_event_interruptible(suspendq, (suspend_flag == 0))) {
+                       return -ERESTARTSYS;
+               }
+       }
+       return 0;
+}
+
+/*!
+ * This function implements the release method on a mc13783 light device.
+ *
+ * @param        inode       pointer on the node
+ * @param        file        pointer on the file
+ * @return       This function returns 0.
+ */
+static int pmic_light_release(struct inode *inode, struct file *file)
+{
+       while (suspend_flag == 1) {
+               swait++;
+               /* Block if the device is suspended */
+               if (wait_event_interruptible(suspendq, (suspend_flag == 0))) {
+                       return -ERESTARTSYS;
+               }
+       }
+       return 0;
+}
+
+static struct file_operations pmic_light_fops = {
+       .owner = THIS_MODULE,
+       .ioctl = pmic_light_ioctl,
+       .open = pmic_light_open,
+       .release = pmic_light_release,
+};
+
+static int pmic_light_remove(struct platform_device *pdev)
+{
+       device_destroy(pmic_light_class, MKDEV(pmic_light_major, 0));
+       class_destroy(pmic_light_class);
+       unregister_chrdev(pmic_light_major, "pmic_light");
+       return 0;
+}
+
+static int pmic_light_probe(struct platform_device *pdev)
+{
+       int ret = 0;
+       struct device *temp_class;
+
+       while (suspend_flag == 1) {
+               swait++;
+               /* Block if the device is suspended */
+               if (wait_event_interruptible(suspendq, (suspend_flag == 0))) {
+                       return -ERESTARTSYS;
+               }
+       }
+       pmic_light_major = register_chrdev(0, "pmic_light", &pmic_light_fops);
+
+       if (pmic_light_major < 0) {
+               printk(KERN_ERR "Unable to get a major for pmic_light\n");
+               return pmic_light_major;
+       }
+       init_waitqueue_head(&suspendq);
+
+       pmic_light_class = class_create(THIS_MODULE, "pmic_light");
+       if (IS_ERR(pmic_light_class)) {
+               printk(KERN_ERR "Error creating pmic_light class.\n");
+               ret = PTR_ERR(pmic_light_class);
+               goto err_out1;
+       }
+
+       temp_class = device_create(pmic_light_class, NULL,
+                                  MKDEV(pmic_light_major, 0), NULL,
+                                  "pmic_light");
+       if (IS_ERR(temp_class)) {
+               printk(KERN_ERR "Error creating pmic_light class device.\n");
+               ret = PTR_ERR(temp_class);
+               goto err_out2;
+       }
+
+       ret = pmic_light_init_reg();
+       if (ret != PMIC_SUCCESS) {
+               goto err_out3;
+       }
+
+       printk(KERN_INFO "PMIC Light successfully loaded\n");
+       return ret;
+
+      err_out3:
+       device_destroy(pmic_light_class, MKDEV(pmic_light_major, 0));
+      err_out2:
+       class_destroy(pmic_light_class);
+      err_out1:
+       unregister_chrdev(pmic_light_major, "pmic_light");
+       return ret;
+}
+
+static struct platform_driver pmic_light_driver_ldm = {
+       .driver = {
+                  .name = "pmic_light",
+                  },
+       .suspend = pmic_light_suspend,
+       .resume = pmic_light_resume,
+       .probe = pmic_light_probe,
+       .remove = pmic_light_remove,
+};
+
+/*
+ * Initialization and Exit
+ */
+
+static int __init pmic_light_init(void)
+{
+       pr_debug("PMIC Light driver loading...\n");
+       return platform_driver_register(&pmic_light_driver_ldm);
+}
+static void __exit pmic_light_exit(void)
+{
+       platform_driver_unregister(&pmic_light_driver_ldm);
+       pr_debug("PMIC Light driver successfully unloaded\n");
+}
+
+/*
+ * Module entry points
+ */
+
+subsys_initcall(pmic_light_init);
+module_exit(pmic_light_exit);
+
+MODULE_DESCRIPTION("PMIC_LIGHT");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pmic/mc13783/pmic_light_defs.h b/drivers/mxc/pmic/mc13783/pmic_light_defs.h
new file mode 100644 (file)
index 0000000..2c76843
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * 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 mc13783/pmic_light_defs.h
+ * @brief This is the internal header PMIC(mc13783) Light and Backlight driver.
+ *
+ * @ingroup PMIC_LIGHT
+ */
+
+#ifndef __MC13783_LIGHT_DEFS_H__
+#define __MC13783_LIGHT_DEFS_H__
+
+#define LREG_0   REG_LED_CONTROL_0
+#define LREG_1   REG_LED_CONTROL_1
+#define LREG_2   REG_LED_CONTROL_2
+#define LREG_3   REG_LED_CONTROL_3
+#define LREG_4   REG_LED_CONTROL_4
+#define LREG_5   REG_LED_CONTROL_5
+
+/* REG_LED_CONTROL_0 */
+
+#define         BIT_LEDEN_LSH           0
+#define         BIT_LEDEN_WID           1
+#define         MASK_TRIODE_MAIN_BL     0x080
+#define         INDEX_AUXILIARY         1
+#define         INDEX_KEYPAD            2
+#define         BITS_FUN_LIGHT_LSH      17
+#define         BITS_FUN_LIGHT_WID      4
+#define         MASK_FUN_LIGHT          0x1E0000
+#define         MASK_BK1_FL             0x200000
+#define         ENABLE_BK1_FL           0x200000
+#define         MASK_BK2_FL             0x400000
+#define         ENABLE_BK2_FL           0x400000
+#define         MASK_BK3_FL             0x800000
+#define         ENABLE_BK3_FL           0x800000
+#define        BIT_UP_MAIN_BL_LSH      1
+#define        BIT_UP_MAIN_BL_WID      1
+#define        BIT_UP_AUX_BL_LSH       2
+#define        BIT_UP_AUX_BL_WID       1
+#define        BIT_UP_KEY_BL_LSH       3
+#define        BIT_UP_KEY_BL_WID       1
+#define        BIT_DOWN_MAIN_BL_LSH    4
+#define        BIT_DOWN_MAIN_BL_WID    1
+#define        BIT_DOWN_AUX_BL_LSH     5
+#define        BIT_DOWN_AUX_BL_WID     1
+#define        BIT_DOWN_KEY_BL_LSH     6
+#define        BIT_DOWN_KEY_BL_WID     1
+#define        BIT_TRIODE_MAIN_BL_LSH  7
+#define        BIT_TRIODE_MAIN_BL_WID  1
+#define        BIT_TRIODE_AUX_BL_LSH   8
+#define        BIT_TRIODE_AUX_BL_WID   1
+#define        BIT_TRIODE_KEY_BL_LSH   9
+#define        BIT_TRIODE_KEY_BL_WID   1
+
+#define         BIT_BOOSTEN_LSH         10
+#define         BIT_BOOSTEN_WID         1
+#define         BITS_BOOST_LSH          11
+#define         BITS_BOOST_WID          5
+#define         BITS_BOOST_ABMS_LSH     11
+#define         BITS_BOOST_ABMS_WID     3
+#define         BITS_BOOST_ABR_LSH      14
+#define         BITS_BOOST_ABR_WID      2
+
+#define         MAX_BOOST_ABMS          7
+#define         MAX_BOOST_ABR           3
+
+/* REG_LED_CONTROL_1 */
+
+#define         BIT_SLEWLIMTC_LSH       23
+#define         BIT_SLEWLIMTC_WID       1
+#define         BIT_TC1HALF_LSH         18
+#define         BIT_TC1HALF_WID         1
+#define         LEDR1RAMPUP             0x000001
+#define         LEDR2RAMPUP             0x000040
+#define         LEDR3RAMPUP             0x001000
+#define         LEDR1RAMPDOWN           0x000008
+#define         LEDR2RAMPDOWN           0x000200
+#define         LEDR3RAMPDOWN           0x008000
+
+/* REG_LED_CONTROL_2 */
+
+#define         BIT_SLEWLIMBL_LSH       23
+#define         BIT_SLEWLIMBL_WID       1
+#define         BIT_DUTY_CYCLE          9
+#define         MASK_DUTY_CYCLE         0x001E00
+#define         INDEX_AUX               4
+#define         INDEX_KYD               8
+#define         BIT_CL_MAIN_LSH                0
+#define         BIT_CL_MAIN_WID                3
+#define         BIT_CL_AUX_LSH         3
+#define         BIT_CL_AUX_WID         3
+#define         BIT_CL_KEY_LSH         6
+#define         BIT_CL_KEY_WID         3
+
+/* REG_LED_CONTROL_3 4 5 */
+#define         BITS_CL_RED_LSH         0
+#define         BITS_CL_RED_WID         2
+#define         BITS_CL_GREEN_LSH       2
+#define         BITS_CL_GREEN_WID       2
+#define         BITS_CL_BLUE_LSH        4
+#define         BITS_CL_BLUE_WID        2
+#define         BITS_DC_RED_LSH         6
+#define         BITS_DC_RED_WID         5
+#define         BITS_DC_GREEN_LSH       11
+#define         BITS_DC_GREEN_WID       5
+#define         BITS_DC_BLUE_LSH        16
+#define         BITS_DC_BLUE_WID        5
+#define         BIT_PERIOD_LSH          21
+#define         BIT_PERIOD_WID          2
+
+#define         DUTY_CYCLE_MAX          31
+
+/* Fun light pattern */
+#define                BLENDED_RAMPS_SLOW              0
+#define                BLENDED_RAMPS_FAST              1
+#define                SAW_RAMPS_SLOW                  2
+#define                SAW_RAMPS_FAST                  3
+#define                BLENDED_INVERSE_RAMPS_SLOW      4
+#define                BLENDED_INVERSE_RAMPS_FAST      5
+#define                CHASING_LIGHTS_RGB_SLOW         6
+#define                CHASING_LIGHTS_RGB_FAST         7
+#define                CHASING_LIGHTS_BGR_SLOW         8
+#define                CHASING_LIGHTS_BGR_FAST         9
+#define                FUN_LIGHTS_OFF                  15
+
+/*!
+ * This function initialize Light registers of mc13783 with 0.
+ *
+ * @return       This function returns 0 if successful.
+ */
+int pmic_light_init_reg(void);
+
+#endif                         /*  __MC13783_LIGHT_DEFS_H__ */
diff --git a/drivers/mxc/pmic/mc13783/pmic_power.c b/drivers/mxc/pmic/mc13783/pmic_power.c
new file mode 100644 (file)
index 0000000..7a17d2a
--- /dev/null
@@ -0,0 +1,3146 @@
+/*
+ * 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 mc13783/pmic_power.c
+ * @brief This is the main file of PMIC(mc13783) Power driver.
+ *
+ * @ingroup PMIC_POWER
+ */
+
+/*
+ * Includes
+ */
+
+#include <linux/platform_device.h>
+#include <linux/ioctl.h>
+#include <linux/pmic_status.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <mach/pmic_power.h>
+
+#include "pmic_power_defs.h"
+
+#ifdef CONFIG_MXC_HWEVENT
+#include <mach/hw_events.h>
+#endif
+
+#include <asm/mach-types.h>
+
+#define MC13783_REGCTRL_GPOx_MASK 0x18000
+
+static bool VBKUP1_EN;
+static bool VBKUP2_EN;
+
+/*
+ * Power Pmic API
+ */
+
+/* EXPORTED FUNCTIONS */
+EXPORT_SYMBOL(pmic_power_off);
+EXPORT_SYMBOL(pmic_power_set_pc_config);
+EXPORT_SYMBOL(pmic_power_get_pc_config);
+EXPORT_SYMBOL(pmic_power_regulator_on);
+EXPORT_SYMBOL(pmic_power_regulator_off);
+EXPORT_SYMBOL(pmic_power_regulator_set_voltage);
+EXPORT_SYMBOL(pmic_power_regulator_get_voltage);
+EXPORT_SYMBOL(pmic_power_regulator_set_config);
+EXPORT_SYMBOL(pmic_power_regulator_get_config);
+EXPORT_SYMBOL(pmic_power_vbkup2_auto_en);
+EXPORT_SYMBOL(pmic_power_get_vbkup2_auto_state);
+EXPORT_SYMBOL(pmic_power_bat_det_en);
+EXPORT_SYMBOL(pmic_power_get_bat_det_state);
+EXPORT_SYMBOL(pmic_power_vib_pin_en);
+EXPORT_SYMBOL(pmic_power_gets_vib_pin_state);
+EXPORT_SYMBOL(pmic_power_get_power_mode_sense);
+EXPORT_SYMBOL(pmic_power_set_regen_assig);
+EXPORT_SYMBOL(pmic_power_get_regen_assig);
+EXPORT_SYMBOL(pmic_power_set_regen_inv);
+EXPORT_SYMBOL(pmic_power_get_regen_inv);
+EXPORT_SYMBOL(pmic_power_esim_v_en);
+EXPORT_SYMBOL(pmic_power_gets_esim_v_state);
+EXPORT_SYMBOL(pmic_power_set_auto_reset_en);
+EXPORT_SYMBOL(pmic_power_get_auto_reset_en);
+EXPORT_SYMBOL(pmic_power_set_conf_button);
+EXPORT_SYMBOL(pmic_power_get_conf_button);
+EXPORT_SYMBOL(pmic_power_event_sub);
+EXPORT_SYMBOL(pmic_power_event_unsub);
+
+/*!
+ * This function is called to put the power in a low power state.
+ * Switching off the platform cannot be decided by
+ * the power module. It has to be handled by the
+ * client application.
+ *
+ * @param   pdev  the device structure used to give information on which power
+ *                device (0 through 3 channels) to suspend
+ * @param   state the power state the device is entering
+ *
+ * @return  The function always returns 0.
+ */
+static int pmic_power_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       return 0;
+};
+
+/*!
+ * This function is called to resume the power from a low power state.
+ *
+ * @param   pdev  the device structure used to give information on which power
+ *                device (0 through 3 channels) to suspend
+ *
+ * @return  The function always returns 0.
+ */
+static int pmic_power_resume(struct platform_device *pdev)
+{
+       return 0;
+};
+
+/*!
+ * This function sets user power off in power control register and thus powers
+ * off the phone.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+void pmic_power_off(void)
+{
+       unsigned int mask, value;
+
+       mask = BITFMASK(MC13783_PWRCTRL_USER_OFF_SPI);
+       value = BITFVAL(MC13783_PWRCTRL_USER_OFF_SPI,
+                       MC13783_PWRCTRL_USER_OFF_SPI_ENABLE);
+
+       pmic_write_reg(REG_POWER_CONTROL_0, value, mask);
+}
+
+/*!
+ * This function sets the power control configuration.
+ *
+ * @param        pc_config   power control configuration.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_set_pc_config(t_pc_config *pc_config)
+{
+       unsigned int pwrctrl_val_reg0 = 0;
+       unsigned int pwrctrl_val_reg1 = 0;
+       unsigned int pwrctrl_mask_reg0 = 0;
+       unsigned int pwrctrl_mask_reg1 = 0;
+
+       if (pc_config == NULL) {
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       if (pc_config->pc_enable != false) {
+               pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_PCEN,
+                                           MC13783_PWRCTRL_PCEN_ENABLE);
+               pwrctrl_val_reg1 |= BITFVAL(MC13783_PWRCTRL_PCT,
+                                           pc_config->pc_timer);
+       } else {
+               pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_PCEN,
+                                           MC13783_PWRCTRL_PCEN_DISABLE);
+       }
+       pwrctrl_mask_reg0 |= BITFMASK(MC13783_PWRCTRL_PCEN);
+       pwrctrl_mask_reg1 |= BITFMASK(MC13783_PWRCTRL_PCT);
+
+       if (pc_config->pc_count_enable != false) {
+
+               pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_PC_COUNT_EN,
+                                           MC13783_PWRCTRL_PC_COUNT_EN_ENABLE);
+               pwrctrl_val_reg1 |= BITFVAL(MC13783_PWRCTRL_PC_COUNT,
+                                           pc_config->pc_count);
+               pwrctrl_val_reg1 |= BITFVAL(MC13783_PWRCTRL_PC_MAX_CNT,
+                                           pc_config->pc_max_count);
+       } else {
+               pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_PC_COUNT_EN,
+                                           MC13783_PWRCTRL_PC_COUNT_EN_DISABLE);
+       }
+       pwrctrl_mask_reg0 |= BITFMASK(MC13783_PWRCTRL_PC_COUNT_EN);
+       pwrctrl_mask_reg1 |= BITFMASK(MC13783_PWRCTRL_PC_MAX_CNT) |
+           BITFMASK(MC13783_PWRCTRL_PC_COUNT);
+
+       if (pc_config->warm_enable != false) {
+               pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_WARM_EN,
+                                           MC13783_PWRCTRL_WARM_EN_ENABLE);
+       } else {
+               pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_WARM_EN,
+                                           MC13783_PWRCTRL_WARM_EN_DISABLE);
+       }
+       pwrctrl_mask_reg0 |= BITFMASK(MC13783_PWRCTRL_WARM_EN);
+
+       if (pc_config->user_off_pc != false) {
+               pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_USER_OFF_PC,
+                                           MC13783_PWRCTRL_USER_OFF_PC_ENABLE);
+       } else {
+               pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_WARM_EN,
+                                           MC13783_PWRCTRL_USER_OFF_PC_DISABLE);
+       }
+       pwrctrl_mask_reg0 |= BITFMASK(MC13783_PWRCTRL_USER_OFF_PC);
+
+       if (pc_config->clk_32k_user_off != false) {
+               pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_32OUT_USER_OFF,
+                                           MC13783_PWRCTRL_32OUT_USER_OFF_ENABLE);
+       } else {
+               pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_32OUT_USER_OFF,
+                                           MC13783_PWRCTRL_32OUT_USER_OFF_DISABLE);
+       }
+       pwrctrl_mask_reg0 |= BITFMASK(MC13783_PWRCTRL_32OUT_USER_OFF);
+
+       if (pc_config->clk_32k_enable != false) {
+               pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_32OUT_EN,
+                                           MC13783_PWRCTRL_32OUT_EN_ENABLE);
+       } else {
+               pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_32OUT_EN,
+                                           MC13783_PWRCTRL_32OUT_EN_DISABLE);
+       }
+       pwrctrl_mask_reg0 |= BITFMASK(MC13783_PWRCTRL_32OUT_EN);
+
+       if (pc_config->en_vbkup1 != false) {
+               pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_VBKUP1_EN,
+                                           MC13783_PWRCTRL_VBKUP_ENABLE);
+               VBKUP1_EN = true;
+       } else {
+               pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_VBKUP1_EN,
+                                           MC13783_PWRCTRL_VBKUP_DISABLE);
+               VBKUP1_EN = false;
+       }
+       pwrctrl_mask_reg0 |= BITFMASK(MC13783_PWRCTRL_VBKUP1_EN);
+
+       if (pc_config->en_vbkup2 != false) {
+               pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_VBKUP2_EN,
+                                           MC13783_PWRCTRL_VBKUP_ENABLE);
+               VBKUP2_EN = true;
+       } else {
+               pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_VBKUP2_EN,
+                                           MC13783_PWRCTRL_VBKUP_DISABLE);
+               VBKUP2_EN = false;
+       }
+       pwrctrl_mask_reg0 |= BITFMASK(MC13783_PWRCTRL_VBKUP2_EN);
+
+       if (pc_config->auto_en_vbkup1 != false) {
+               pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_VBKUP1_AUTO_EN,
+                                           MC13783_PWRCTRL_VBKUP_ENABLE);
+       } else {
+               pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_VBKUP1_AUTO_EN,
+                                           MC13783_PWRCTRL_VBKUP_DISABLE);
+       }
+       pwrctrl_mask_reg0 |= BITFMASK(MC13783_PWRCTRL_VBKUP1_AUTO_EN);
+
+       if (pc_config->auto_en_vbkup2 != false) {
+               pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_VBKUP2_AUTO_EN,
+                                           MC13783_PWRCTRL_VBKUP_ENABLE);
+       } else {
+               pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_VBKUP2_AUTO_EN,
+                                           MC13783_PWRCTRL_VBKUP_DISABLE);
+       }
+       pwrctrl_mask_reg0 |= BITFMASK(MC13783_PWRCTRL_VBKUP2_AUTO_EN);
+
+       if (VBKUP1_EN != false) {
+               if (pc_config->vhold_voltage > 3
+                   || pc_config->vhold_voltage < 0) {
+                       return PMIC_PARAMETER_ERROR;
+               } else {
+
+                       pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_VBKUP1,
+                                                   pc_config->vhold_voltage);
+               }
+       }
+       if (VBKUP2_EN != false) {
+               if (pc_config->vhold_voltage > 3
+                   || pc_config->vhold_voltage < 0) {
+                       return PMIC_PARAMETER_ERROR;
+               } else {
+                       pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_VBKUP2,
+                                                   pc_config->vhold_voltage2);
+               }
+       }
+       pwrctrl_mask_reg0 |= BITFMASK(MC13783_PWRCTRL_VBKUP1) |
+           BITFMASK(MC13783_PWRCTRL_VBKUP2);
+
+       if (pc_config->mem_allon != false) {
+               pwrctrl_val_reg1 |= BITFVAL(MC13783_PWRCTRL_MEM_ALLON,
+                                           MC13783_PWRCTRL_MEM_ALLON_ENABLE);
+               pwrctrl_val_reg1 |= BITFVAL(MC13783_PWRCTRL_MEM_TMR,
+                                           pc_config->mem_timer);
+       } else {
+               pwrctrl_val_reg1 |= BITFVAL(MC13783_PWRCTRL_MEM_ALLON,
+                                           MC13783_PWRCTRL_MEM_ALLON_DISABLE);
+       }
+       pwrctrl_mask_reg1 |= BITFMASK(MC13783_PWRCTRL_MEM_ALLON) |
+           BITFMASK(MC13783_PWRCTRL_MEM_TMR);
+
+       CHECK_ERROR(pmic_write_reg(REG_POWER_CONTROL_0,
+                                  pwrctrl_val_reg0, pwrctrl_mask_reg0));
+       CHECK_ERROR(pmic_write_reg(REG_POWER_CONTROL_1,
+                                  pwrctrl_val_reg1, pwrctrl_mask_reg1));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function retrives the power control configuration.
+ *
+ * @param        pc_config   pointer to power control configuration.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_get_pc_config(t_pc_config *pc_config)
+{
+       unsigned int pwrctrl_val_reg0 = 0;
+       unsigned int pwrctrl_val_reg1 = 0;
+
+       if (pc_config == NULL) {
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_read_reg(REG_POWER_CONTROL_0,
+                                 &pwrctrl_val_reg0, PMIC_ALL_BITS));
+       CHECK_ERROR(pmic_read_reg(REG_POWER_CONTROL_1,
+                                 &pwrctrl_val_reg1, PMIC_ALL_BITS));
+
+       if (BITFEXT(pwrctrl_val_reg0, MC13783_PWRCTRL_PCEN)
+           == MC13783_PWRCTRL_PCEN_ENABLE) {
+               pc_config->pc_enable = true;
+               pc_config->pc_timer = BITFEXT(pwrctrl_val_reg1,
+                                             MC13783_PWRCTRL_PCT);
+
+       } else {
+               pc_config->pc_enable = false;
+               pc_config->pc_timer = 0;
+       }
+
+       if (BITFEXT(pwrctrl_val_reg0, MC13783_PWRCTRL_PC_COUNT_EN)
+           == MC13783_PWRCTRL_PCEN_ENABLE) {
+               pc_config->pc_count_enable = true;
+               pc_config->pc_count = BITFEXT(pwrctrl_val_reg1,
+                                             MC13783_PWRCTRL_PC_COUNT);
+               pc_config->pc_max_count = BITFEXT(pwrctrl_val_reg1,
+                                                 MC13783_PWRCTRL_PC_MAX_CNT);
+       } else {
+               pc_config->pc_count_enable = false;
+               pc_config->pc_count = 0;
+               pc_config->pc_max_count = 0;
+       }
+
+       if (BITFEXT(pwrctrl_val_reg0, MC13783_PWRCTRL_WARM_EN)
+           == MC13783_PWRCTRL_WARM_EN_ENABLE) {
+               pc_config->warm_enable = true;
+       } else {
+               pc_config->warm_enable = false;
+       }
+
+       if (BITFEXT(pwrctrl_val_reg0, MC13783_PWRCTRL_USER_OFF_PC)
+           == MC13783_PWRCTRL_USER_OFF_PC_ENABLE) {
+               pc_config->user_off_pc = true;
+       } else {
+               pc_config->user_off_pc = false;
+       }
+
+       if (BITFEXT(pwrctrl_val_reg0, MC13783_PWRCTRL_32OUT_USER_OFF)
+           == MC13783_PWRCTRL_32OUT_USER_OFF_ENABLE) {
+               pc_config->clk_32k_user_off = true;
+       } else {
+               pc_config->clk_32k_user_off = false;
+       }
+
+       if (BITFEXT(pwrctrl_val_reg0, MC13783_PWRCTRL_32OUT_EN)
+           == MC13783_PWRCTRL_32OUT_EN_ENABLE) {
+               pc_config->clk_32k_enable = true;
+       } else {
+               pc_config->clk_32k_enable = false;
+       }
+
+       if (BITFEXT(pwrctrl_val_reg0, MC13783_PWRCTRL_VBKUP1_AUTO_EN)
+           == MC13783_PWRCTRL_VBKUP_ENABLE) {
+               pc_config->auto_en_vbkup1 = true;
+       } else {
+               pc_config->auto_en_vbkup1 = false;
+       }
+
+       if (BITFEXT(pwrctrl_val_reg0, MC13783_PWRCTRL_VBKUP2_AUTO_EN)
+           == MC13783_PWRCTRL_VBKUP_ENABLE) {
+               pc_config->auto_en_vbkup2 = true;
+       } else {
+               pc_config->auto_en_vbkup2 = false;
+       }
+
+       if (BITFEXT(pwrctrl_val_reg0, MC13783_PWRCTRL_VBKUP1_EN)
+           == MC13783_PWRCTRL_VBKUP_ENABLE) {
+               pc_config->en_vbkup1 = true;
+               pc_config->vhold_voltage = BITFEXT(pwrctrl_val_reg0,
+                                                  MC13783_PWRCTRL_VBKUP1);
+       } else {
+               pc_config->en_vbkup1 = false;
+               pc_config->vhold_voltage = 0;
+       }
+
+       if (BITFEXT(pwrctrl_val_reg0, MC13783_PWRCTRL_VBKUP2_EN)
+           == MC13783_PWRCTRL_VBKUP_ENABLE) {
+               pc_config->en_vbkup2 = true;
+               pc_config->vhold_voltage2 = BITFEXT(pwrctrl_val_reg0,
+                                                   MC13783_PWRCTRL_VBKUP2);
+       } else {
+               pc_config->en_vbkup2 = false;
+               pc_config->vhold_voltage2 = 0;
+       }
+
+       if (BITFEXT(pwrctrl_val_reg1, MC13783_PWRCTRL_MEM_ALLON) ==
+           MC13783_PWRCTRL_MEM_ALLON_ENABLE) {
+               pc_config->mem_allon = true;
+               pc_config->mem_timer = BITFEXT(pwrctrl_val_reg1,
+                                              MC13783_PWRCTRL_MEM_TMR);
+       } else {
+               pc_config->mem_allon = false;
+               pc_config->mem_timer = 0;
+       }
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function turns on a regulator.
+ *
+ * @param        regulator    The regulator to be truned on.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_regulator_on(t_pmic_regulator regulator)
+{
+       unsigned int reg_val = 0, reg_mask = 0;
+       unsigned int reg;
+
+       switch (regulator) {
+       case SW_PLL:
+               reg_val = BITFVAL(MC13783_SWCTRL_PLL_EN,
+                                 MC13783_SWCTRL_PLL_EN_ENABLE);
+               reg_mask = BITFMASK(MC13783_SWCTRL_PLL_EN);
+               reg = REG_SWITCHERS_4;
+               break;
+       case SW_SW3:
+               reg_val = BITFVAL(MC13783_SWCTRL_SW3_EN,
+                                 MC13783_SWCTRL_SW3_EN_ENABLE);
+               reg_mask = BITFMASK(MC13783_SWCTRL_SW3_EN);
+               reg = REG_SWITCHERS_5;
+               break;
+       case REGU_VAUDIO:
+               reg_val = BITFVAL(MC13783_REGCTRL_VAUDIO_EN,
+                                 MC13783_REGCTRL_VAUDIO_EN_ENABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VAUDIO_EN);
+               reg = REG_REGULATOR_MODE_0;
+               break;
+       case REGU_VIOHI:
+               reg_val = BITFVAL(MC13783_REGCTRL_VIOHI_EN,
+                                 MC13783_REGCTRL_VIOHI_EN_ENABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VIOHI_EN);
+               reg = REG_REGULATOR_MODE_0;
+               break;
+       case REGU_VIOLO:
+               reg_val = BITFVAL(MC13783_REGCTRL_VIOLO_EN,
+                                 MC13783_REGCTRL_VIOLO_EN_ENABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VIOLO_EN);
+               reg = REG_REGULATOR_MODE_0;
+               break;
+       case REGU_VDIG:
+               reg_val = BITFVAL(MC13783_REGCTRL_VDIG_EN,
+                                 MC13783_REGCTRL_VDIG_EN_ENABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VDIG_EN);
+               reg = REG_REGULATOR_MODE_0;
+               break;
+       case REGU_VGEN:
+               reg_val = BITFVAL(MC13783_REGCTRL_VGEN_EN,
+                                 MC13783_REGCTRL_VGEN_EN_ENABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VGEN_EN);
+               reg = REG_REGULATOR_MODE_0;
+               break;
+       case REGU_VRFDIG:
+               reg_val = BITFVAL(MC13783_REGCTRL_VRFDIG_EN,
+                                 MC13783_REGCTRL_VRFDIG_EN_ENABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VRFDIG_EN);
+               reg = REG_REGULATOR_MODE_0;
+               break;
+       case REGU_VRFREF:
+               reg_val = BITFVAL(MC13783_REGCTRL_VRFREF_EN,
+                                 MC13783_REGCTRL_VRFREF_EN_ENABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VRFREF_EN);
+               reg = REG_REGULATOR_MODE_0;
+               break;
+       case REGU_VRFCP:
+               reg_val = BITFVAL(MC13783_REGCTRL_VRFCP_EN,
+                                 MC13783_REGCTRL_VRFCP_EN_ENABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VRFCP_EN);
+               reg = REG_REGULATOR_MODE_0;
+               break;
+       case REGU_VSIM:
+               reg_val = BITFVAL(MC13783_REGCTRL_VSIM_EN,
+                                 MC13783_REGCTRL_VSIM_EN_ENABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VSIM_EN);
+               reg = REG_REGULATOR_MODE_1;
+               break;
+       case REGU_VESIM:
+               reg_val = BITFVAL(MC13783_REGCTRL_VESIM_EN,
+                                 MC13783_REGCTRL_VESIM_EN_ENABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VESIM_EN);
+               reg = REG_REGULATOR_MODE_1;
+               break;
+       case REGU_VCAM:
+               reg_val = BITFVAL(MC13783_REGCTRL_VCAM_EN,
+                                 MC13783_REGCTRL_VCAM_EN_ENABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VCAM_EN);
+               reg = REG_REGULATOR_MODE_1;
+               break;
+       case REGU_VRFBG:
+               reg_val = BITFVAL(MC13783_REGCTRL_VRFBG_EN,
+                                 MC13783_REGCTRL_VRFBG_EN_ENABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VRFBG_EN);
+               reg = REG_REGULATOR_MODE_1;
+               break;
+       case REGU_VVIB:
+               reg_val = BITFVAL(MC13783_REGCTRL_VVIB_EN,
+                                 MC13783_REGCTRL_VVIB_EN_ENABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VVIB_EN);
+               reg = REG_REGULATOR_MODE_1;
+               break;
+       case REGU_VRF1:
+               reg_val = BITFVAL(MC13783_REGCTRL_VRF1_EN,
+                                 MC13783_REGCTRL_VRF1_EN_ENABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VRF1_EN);
+               reg = REG_REGULATOR_MODE_1;
+               break;
+       case REGU_VRF2:
+               reg_val = BITFVAL(MC13783_REGCTRL_VRF2_EN,
+                                 MC13783_REGCTRL_VRF2_EN_ENABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VRF2_EN);
+               reg = REG_REGULATOR_MODE_1;
+               break;
+       case REGU_VMMC1:
+               reg_val = BITFVAL(MC13783_REGCTRL_VMMC1_EN,
+                                 MC13783_REGCTRL_VMMC1_EN_ENABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VMMC1_EN);
+               reg = REG_REGULATOR_MODE_1;
+               break;
+       case REGU_VMMC2:
+               reg_val = BITFVAL(MC13783_REGCTRL_VMMC2_EN,
+                                 MC13783_REGCTRL_VMMC2_EN_ENABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VMMC2_EN);
+               reg = REG_REGULATOR_MODE_1;
+               break;
+       case REGU_GPO1:
+               reg_val = BITFVAL(MC13783_REGCTRL_GPO1_EN,
+                                 MC13783_REGCTRL_GPO1_EN_ENABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_GPO1_EN);
+               reg = REG_POWER_MISCELLANEOUS;
+               break;
+       case REGU_GPO2:
+               reg_val = BITFVAL(MC13783_REGCTRL_GPO2_EN,
+                                 MC13783_REGCTRL_GPO2_EN_ENABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_GPO2_EN);
+               reg = REG_POWER_MISCELLANEOUS;
+               break;
+       case REGU_GPO3:
+               reg_val = BITFVAL(MC13783_REGCTRL_GPO3_EN,
+                                 MC13783_REGCTRL_GPO3_EN_ENABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_GPO3_EN);
+               reg = REG_POWER_MISCELLANEOUS;
+               break;
+       case REGU_GPO4:
+               reg_val = BITFVAL(MC13783_REGCTRL_GPO4_EN,
+                                 MC13783_REGCTRL_GPO4_EN_ENABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_GPO4_EN);
+               reg = REG_POWER_MISCELLANEOUS;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_write_reg(reg, reg_val, reg_mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function turns off a regulator.
+ *
+ * @param        regulator    The regulator to be truned off.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_regulator_off(t_pmic_regulator regulator)
+{
+       unsigned int reg_val = 0, reg_mask = 0;
+       unsigned int reg;
+
+       switch (regulator) {
+       case SW_PLL:
+               reg_val = BITFVAL(MC13783_SWCTRL_PLL_EN,
+                                 MC13783_SWCTRL_PLL_EN_DISABLE);
+               reg_mask = BITFMASK(MC13783_SWCTRL_PLL_EN);
+               reg = REG_SWITCHERS_4;
+               break;
+       case SW_SW3:
+               reg_val = BITFVAL(MC13783_SWCTRL_SW3_EN,
+                                 MC13783_SWCTRL_SW3_EN_DISABLE);
+               reg_mask = BITFMASK(MC13783_SWCTRL_SW3_EN);
+               reg = REG_SWITCHERS_5;
+               break;
+       case REGU_VAUDIO:
+               reg_val = BITFVAL(MC13783_REGCTRL_VAUDIO_EN,
+                                 MC13783_REGCTRL_VAUDIO_EN_DISABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VAUDIO_EN);
+               reg = REG_REGULATOR_MODE_0;
+               break;
+       case REGU_VIOHI:
+               reg_val = BITFVAL(MC13783_REGCTRL_VIOHI_EN,
+                                 MC13783_REGCTRL_VIOHI_EN_DISABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VIOHI_EN);
+               reg = REG_REGULATOR_MODE_0;
+               break;
+       case REGU_VIOLO:
+               reg_val = BITFVAL(MC13783_REGCTRL_VIOLO_EN,
+                                 MC13783_REGCTRL_VIOLO_EN_DISABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VIOLO_EN);
+               reg = REG_REGULATOR_MODE_0;
+               break;
+       case REGU_VDIG:
+               reg_val = BITFVAL(MC13783_REGCTRL_VDIG_EN,
+                                 MC13783_REGCTRL_VDIG_EN_DISABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VDIG_EN);
+               reg = REG_REGULATOR_MODE_0;
+               break;
+       case REGU_VGEN:
+               reg_val = BITFVAL(MC13783_REGCTRL_VGEN_EN,
+                                 MC13783_REGCTRL_VGEN_EN_DISABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VGEN_EN);
+               reg = REG_REGULATOR_MODE_0;
+               break;
+       case REGU_VRFDIG:
+               reg_val = BITFVAL(MC13783_REGCTRL_VRFDIG_EN,
+                                 MC13783_REGCTRL_VRFDIG_EN_DISABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VRFDIG_EN);
+               reg = REG_REGULATOR_MODE_0;
+               break;
+       case REGU_VRFREF:
+               reg_val = BITFVAL(MC13783_REGCTRL_VRFREF_EN,
+                                 MC13783_REGCTRL_VRFREF_EN_DISABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VRFREF_EN);
+               reg = REG_REGULATOR_MODE_0;
+               break;
+       case REGU_VRFCP:
+               reg_val = BITFVAL(MC13783_REGCTRL_VRFCP_EN,
+                                 MC13783_REGCTRL_VRFCP_EN_DISABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VRFCP_EN);
+               reg = REG_REGULATOR_MODE_0;
+               break;
+       case REGU_VSIM:
+               reg_val = BITFVAL(MC13783_REGCTRL_VSIM_EN,
+                                 MC13783_REGCTRL_VSIM_EN_DISABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VSIM_EN);
+               reg = REG_REGULATOR_MODE_1;
+               break;
+       case REGU_VESIM:
+               reg_val = BITFVAL(MC13783_REGCTRL_VESIM_EN,
+                                 MC13783_REGCTRL_VESIM_EN_DISABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VESIM_EN);
+               reg = REG_REGULATOR_MODE_1;
+               break;
+       case REGU_VCAM:
+               reg_val = BITFVAL(MC13783_REGCTRL_VCAM_EN,
+                                 MC13783_REGCTRL_VCAM_EN_DISABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VCAM_EN);
+               reg = REG_REGULATOR_MODE_1;
+               break;
+       case REGU_VRFBG:
+               reg_val = BITFVAL(MC13783_REGCTRL_VRFBG_EN,
+                                 MC13783_REGCTRL_VRFBG_EN_DISABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VRFBG_EN);
+               reg = REG_REGULATOR_MODE_1;
+               break;
+       case REGU_VVIB:
+               reg_val = BITFVAL(MC13783_REGCTRL_VVIB_EN,
+                                 MC13783_REGCTRL_VVIB_EN_DISABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VVIB_EN);
+               reg = REG_REGULATOR_MODE_1;
+               break;
+       case REGU_VRF1:
+               reg_val = BITFVAL(MC13783_REGCTRL_VRF1_EN,
+                                 MC13783_REGCTRL_VRF1_EN_DISABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VRF1_EN);
+               reg = REG_REGULATOR_MODE_1;
+               break;
+       case REGU_VRF2:
+               reg_val = BITFVAL(MC13783_REGCTRL_VRF2_EN,
+                                 MC13783_REGCTRL_VRF2_EN_DISABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VRF2_EN);
+               reg = REG_REGULATOR_MODE_1;
+               break;
+       case REGU_VMMC1:
+               reg_val = BITFVAL(MC13783_REGCTRL_VMMC1_EN,
+                                 MC13783_REGCTRL_VMMC1_EN_DISABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VMMC1_EN);
+               reg = REG_REGULATOR_MODE_1;
+               break;
+       case REGU_VMMC2:
+               reg_val = BITFVAL(MC13783_REGCTRL_VMMC2_EN,
+                                 MC13783_REGCTRL_VMMC2_EN_DISABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VMMC2_EN);
+               reg = REG_REGULATOR_MODE_1;
+               break;
+       case REGU_GPO1:
+               reg_val = BITFVAL(MC13783_REGCTRL_GPO1_EN,
+                                 MC13783_REGCTRL_GPO1_EN_DISABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_GPO1_EN);
+               reg = REG_POWER_MISCELLANEOUS;
+               break;
+       case REGU_GPO2:
+               reg_val = BITFVAL(MC13783_REGCTRL_GPO2_EN,
+                                 MC13783_REGCTRL_GPO2_EN_DISABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_GPO2_EN);
+               reg = REG_POWER_MISCELLANEOUS;
+               break;
+       case REGU_GPO3:
+               reg_val = BITFVAL(MC13783_REGCTRL_GPO3_EN,
+                                 MC13783_REGCTRL_GPO3_EN_DISABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_GPO3_EN);
+               reg = REG_POWER_MISCELLANEOUS;
+               break;
+       case REGU_GPO4:
+               reg_val = BITFVAL(MC13783_REGCTRL_GPO4_EN,
+                                 MC13783_REGCTRL_GPO4_EN_DISABLE);
+               reg_mask = BITFMASK(MC13783_REGCTRL_GPO4_EN);
+               reg = REG_POWER_MISCELLANEOUS;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_write_reg(reg, reg_val, reg_mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets the regulator output voltage.
+ *
+ * @param        regulator    The regulator to be configured.
+ * @param        voltage      The regulator output voltage.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_regulator_set_voltage(t_pmic_regulator regulator,
+                                            t_regulator_voltage voltage)
+{
+       unsigned int reg_val = 0, reg_mask = 0;
+       unsigned int reg;
+
+       switch (regulator) {
+       case SW_SW1A:
+               if ((voltage.sw1a < SW1A_0_9V) || (voltage.sw1a > SW1A_2_2V)) {
+                       return PMIC_PARAMETER_ERROR;
+               }
+               reg_val = BITFVAL(MC13783_SWSET_SW1A, voltage.sw1a);
+               reg_mask = BITFMASK(MC13783_SWSET_SW1A);
+               reg = REG_SWITCHERS_0;
+               break;
+       case SW_SW1B:
+               if ((voltage.sw1b < SW1B_0_9V) || (voltage.sw1b > SW1B_2_2V)) {
+                       return PMIC_PARAMETER_ERROR;
+               }
+               reg_val = BITFVAL(MC13783_SWSET_SW1B, voltage.sw1b);
+               reg_mask = BITFMASK(MC13783_SWSET_SW1B);
+               reg = REG_SWITCHERS_1;
+               break;
+       case SW_SW2A:
+               if ((voltage.sw2a < SW2A_0_9V) || (voltage.sw2a > SW2A_2_2V)) {
+                       return PMIC_PARAMETER_ERROR;
+               }
+               reg_val = BITFVAL(MC13783_SWSET_SW2A, voltage.sw1a);
+               reg_mask = BITFMASK(MC13783_SWSET_SW2A);
+               reg = REG_SWITCHERS_2;
+               break;
+       case SW_SW2B:
+               if ((voltage.sw2b < SW2B_0_9V) || (voltage.sw2b > SW2B_2_2V)) {
+                       return PMIC_PARAMETER_ERROR;
+               }
+               reg_val = BITFVAL(MC13783_SWSET_SW2B, voltage.sw2b);
+               reg_mask = BITFMASK(MC13783_SWSET_SW1A);
+               reg = REG_SWITCHERS_3;
+               break;
+       case SW_SW3:
+               if (voltage.sw3 != SW3_5V) {
+                       return PMIC_PARAMETER_ERROR;
+               }
+               reg_val = BITFVAL(MC13783_SWSET_SW3, voltage.sw3);
+               reg_mask = BITFMASK(MC13783_SWSET_SW3);
+               reg = REG_SWITCHERS_5;
+               break;
+       case REGU_VIOLO:
+               if ((voltage.violo < VIOLO_1_2V) ||
+                   (voltage.violo > VIOLO_1_8V)) {
+                       return PMIC_PARAMETER_ERROR;
+               }
+               reg_val = BITFVAL(MC13783_REGSET_VIOLO, voltage.violo);
+               reg_mask = BITFMASK(MC13783_REGSET_VIOLO);
+               reg = REG_REGULATOR_SETTING_0;
+               break;
+       case REGU_VDIG:
+               if ((voltage.vdig < VDIG_1_2V) || (voltage.vdig > VDIG_1_8V)) {
+                       return PMIC_PARAMETER_ERROR;
+               }
+               reg_val = BITFVAL(MC13783_REGSET_VDIG, voltage.vdig);
+               reg_mask = BITFMASK(MC13783_REGSET_VDIG);
+               reg = REG_REGULATOR_SETTING_0;
+               break;
+       case REGU_VGEN:
+               if ((voltage.vgen < VGEN_1_2V) || (voltage.vgen > VGEN_2_4V)) {
+                       return PMIC_PARAMETER_ERROR;
+               }
+               reg_val = BITFVAL(MC13783_REGSET_VGEN, voltage.vgen);
+               reg_mask = BITFMASK(MC13783_REGSET_VGEN);
+               reg = REG_REGULATOR_SETTING_0;
+               break;
+       case REGU_VRFDIG:
+               if ((voltage.vrfdig < VRFDIG_1_2V) ||
+                   (voltage.vrfdig > VRFDIG_1_875V)) {
+                       return PMIC_PARAMETER_ERROR;
+               }
+               reg_val = BITFVAL(MC13783_REGSET_VRFDIG, voltage.vrfdig);
+               reg_mask = BITFMASK(MC13783_REGSET_VRFDIG);
+               reg = REG_REGULATOR_SETTING_0;
+               break;
+       case REGU_VRFREF:
+               if ((voltage.vrfref < VRFREF_2_475V) ||
+                   (voltage.vrfref > VRFREF_2_775V)) {
+                       return PMIC_PARAMETER_ERROR;
+               }
+               reg_val = BITFVAL(MC13783_REGSET_VRFREF, voltage.vrfref);
+               reg_mask = BITFMASK(MC13783_REGSET_VRFREF);
+               reg = REG_REGULATOR_SETTING_0;
+               break;
+       case REGU_VRFCP:
+               if ((voltage.vrfcp < VRFCP_2_7V) ||
+                   (voltage.vrfcp > VRFCP_2_775V)) {
+                       return PMIC_PARAMETER_ERROR;
+               }
+               reg_val = BITFVAL(MC13783_REGSET_VRFCP, voltage.vrfcp);
+               reg_mask = BITFMASK(MC13783_REGSET_VRFCP);
+               reg = REG_REGULATOR_SETTING_0;
+               break;
+       case REGU_VSIM:
+               if ((voltage.vsim < VSIM_1_8V) || (voltage.vsim > VSIM_2_9V)) {
+                       return PMIC_PARAMETER_ERROR;
+               }
+               reg_val = BITFVAL(MC13783_REGSET_VSIM, voltage.vsim);
+               reg_mask = BITFMASK(MC13783_REGSET_VSIM);
+               reg = REG_REGULATOR_SETTING_0;
+               break;
+       case REGU_VESIM:
+               if ((voltage.vesim < VESIM_1_8V) ||
+                   (voltage.vesim > VESIM_2_9V)) {
+                       return PMIC_PARAMETER_ERROR;
+               }
+               reg_val = BITFVAL(MC13783_REGSET_VESIM, voltage.vesim);
+               reg_mask = BITFMASK(MC13783_REGSET_VESIM);
+               reg = REG_REGULATOR_SETTING_0;
+               break;
+       case REGU_VCAM:
+               if ((voltage.vcam < VCAM_1_5V) || (voltage.vcam > VCAM_3V)) {
+                       return PMIC_PARAMETER_ERROR;
+               }
+               reg_val = BITFVAL(MC13783_REGSET_VCAM, voltage.vcam);
+               reg_mask = BITFMASK(MC13783_REGSET_VCAM);
+               reg = REG_REGULATOR_SETTING_0;
+               break;
+       case REGU_VVIB:
+               if ((voltage.vvib < VVIB_1_3V) || (voltage.vvib > VVIB_3V)) {
+                       return PMIC_PARAMETER_ERROR;
+               }
+               reg_val = BITFVAL(MC13783_REGSET_VVIB, voltage.vvib);
+               reg_mask = BITFMASK(MC13783_REGSET_VVIB);
+               reg = REG_REGULATOR_SETTING_1;
+               break;
+       case REGU_VRF1:
+               if ((voltage.vrf1 < VRF1_1_5V) || (voltage.vrf1 > VRF1_2_775V)) {
+                       return PMIC_PARAMETER_ERROR;
+               }
+               reg_val = BITFVAL(MC13783_REGSET_VRF1, voltage.vrf1);
+               reg_mask = BITFMASK(MC13783_REGSET_VRF1);
+               reg = REG_REGULATOR_SETTING_1;
+               break;
+       case REGU_VRF2:
+               if ((voltage.vrf2 < VRF2_1_5V) || (voltage.vrf2 > VRF2_2_775V)) {
+                       return PMIC_PARAMETER_ERROR;
+               }
+               reg_val = BITFVAL(MC13783_REGSET_VRF2, voltage.vrf2);
+               reg_mask = BITFMASK(MC13783_REGSET_VRF2);
+               reg = REG_REGULATOR_SETTING_1;
+               break;
+       case REGU_VMMC1:
+               if ((voltage.vmmc1 < VMMC1_1_6V) || (voltage.vmmc1 > VMMC1_3V)) {
+                       return PMIC_PARAMETER_ERROR;
+               }
+               reg_val = BITFVAL(MC13783_REGSET_VMMC1, voltage.vmmc1);
+               reg_mask = BITFMASK(MC13783_REGSET_VMMC1);
+               reg = REG_REGULATOR_SETTING_1;
+               break;
+       case REGU_VMMC2:
+               if ((voltage.vmmc2 < VMMC2_1_6V) || (voltage.vmmc2 > VMMC2_3V)) {
+                       return PMIC_PARAMETER_ERROR;
+               }
+               reg_val = BITFVAL(MC13783_REGSET_VMMC2, voltage.vmmc2);
+               reg_mask = BITFMASK(MC13783_REGSET_VMMC2);
+               reg = REG_REGULATOR_SETTING_1;
+               break;
+       case REGU_VAUDIO:
+       case REGU_VIOHI:
+       case REGU_VRFBG:
+       case REGU_GPO1:
+       case REGU_GPO2:
+       case REGU_GPO3:
+       case REGU_GPO4:
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_write_reg(reg, reg_val, reg_mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function retrives the regulator output voltage.
+ *
+ * @param        regulator    The regulator to be truned off.
+ * @param        voltage      Pointer to regulator output voltage.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_regulator_get_voltage(t_pmic_regulator regulator,
+                                            t_regulator_voltage *voltage)
+{
+       unsigned int reg_val = 0;
+
+       if (regulator == SW_SW1A) {
+               CHECK_ERROR(pmic_read_reg(REG_SWITCHERS_0,
+                                         &reg_val, PMIC_ALL_BITS));
+       } else if (regulator == SW_SW1B) {
+               CHECK_ERROR(pmic_read_reg(REG_SWITCHERS_1,
+                                         &reg_val, PMIC_ALL_BITS));
+       } else if (regulator == SW_SW2A) {
+               CHECK_ERROR(pmic_read_reg(REG_SWITCHERS_2,
+                                         &reg_val, PMIC_ALL_BITS));
+       } else if (regulator == SW_SW2B) {
+               CHECK_ERROR(pmic_read_reg(REG_SWITCHERS_3,
+                                         &reg_val, PMIC_ALL_BITS));
+       } else if (regulator == SW_SW3) {
+               CHECK_ERROR(pmic_read_reg(REG_SWITCHERS_5,
+                                         &reg_val, PMIC_ALL_BITS));
+       } else if ((regulator == REGU_VIOLO) || (regulator == REGU_VDIG) ||
+                  (regulator == REGU_VGEN) ||
+                  (regulator == REGU_VRFDIG) ||
+                  (regulator == REGU_VRFREF) ||
+                  (regulator == REGU_VRFCP) ||
+                  (regulator == REGU_VSIM) ||
+                  (regulator == REGU_VESIM) || (regulator == REGU_VCAM)) {
+               CHECK_ERROR(pmic_read_reg(REG_REGULATOR_SETTING_0,
+                                         &reg_val, PMIC_ALL_BITS));
+       } else if ((regulator == REGU_VVIB) || (regulator == REGU_VRF1) ||
+                  (regulator == REGU_VRF2) ||
+                  (regulator == REGU_VMMC1) || (regulator == REGU_VMMC2)) {
+               CHECK_ERROR(pmic_read_reg(REG_REGULATOR_SETTING_1,
+                                         &reg_val, PMIC_ALL_BITS));
+       }
+
+       switch (regulator) {
+       case SW_SW1A:
+               voltage->sw1a = BITFEXT(reg_val, MC13783_SWSET_SW1A);
+               break;
+       case SW_SW1B:
+               voltage->sw1b = BITFEXT(reg_val, MC13783_SWSET_SW1B);
+               break;
+       case SW_SW2A:
+               voltage->sw2a = BITFEXT(reg_val, MC13783_SWSET_SW2A);
+               break;
+       case SW_SW2B:
+               voltage->sw2b = BITFEXT(reg_val, MC13783_SWSET_SW2B);
+               break;
+       case SW_SW3:
+               voltage->sw3 = BITFEXT(reg_val, MC13783_SWSET_SW3);
+               break;
+       case REGU_VIOLO:
+               voltage->violo = BITFEXT(reg_val, MC13783_REGSET_VIOLO);
+               break;
+       case REGU_VDIG:
+               voltage->vdig = BITFEXT(reg_val, MC13783_REGSET_VDIG);
+               break;
+       case REGU_VGEN:
+               voltage->vgen = BITFEXT(reg_val, MC13783_REGSET_VGEN);
+               break;
+       case REGU_VRFDIG:
+               voltage->vrfdig = BITFEXT(reg_val, MC13783_REGSET_VRFDIG);
+               break;
+       case REGU_VRFREF:
+               voltage->vrfref = BITFEXT(reg_val, MC13783_REGSET_VRFREF);
+               break;
+       case REGU_VRFCP:
+               voltage->vrfcp = BITFEXT(reg_val, MC13783_REGSET_VRFCP);
+               break;
+       case REGU_VSIM:
+               voltage->vsim = BITFEXT(reg_val, MC13783_REGSET_VSIM);
+               break;
+       case REGU_VESIM:
+               voltage->vesim = BITFEXT(reg_val, MC13783_REGSET_VESIM);
+               break;
+       case REGU_VCAM:
+               voltage->vcam = BITFEXT(reg_val, MC13783_REGSET_VCAM);
+               break;
+       case REGU_VVIB:
+               voltage->vvib = BITFEXT(reg_val, MC13783_REGSET_VVIB);
+               break;
+       case REGU_VRF1:
+               voltage->vrf1 = BITFEXT(reg_val, MC13783_REGSET_VRF1);
+               break;
+       case REGU_VRF2:
+               voltage->vrf2 = BITFEXT(reg_val, MC13783_REGSET_VRF2);
+               break;
+       case REGU_VMMC1:
+               voltage->vmmc1 = BITFEXT(reg_val, MC13783_REGSET_VMMC1);
+               break;
+       case REGU_VMMC2:
+               voltage->vmmc2 = BITFEXT(reg_val, MC13783_REGSET_VMMC2);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets the DVS voltage
+ *
+ * @param        regulator    The regulator to be configured.
+ * @param        dvs          The switch Dynamic Voltage Scaling
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_set_dvs(t_pmic_regulator regulator,
+                                       t_regulator_voltage dvs)
+{
+       unsigned int reg_val = 0, reg_mask = 0;
+       unsigned int reg;
+
+       switch (regulator) {
+       case SW_SW1A:
+               if ((dvs.sw1a < SW1A_0_9V) || (dvs.sw1a > SW1A_2_2V)) {
+                       return PMIC_PARAMETER_ERROR;
+               }
+               reg_val = BITFVAL(MC13783_SWSET_SW1A_DVS, dvs.sw1a);
+               reg_mask = BITFMASK(MC13783_SWSET_SW1A_DVS);
+               reg = REG_SWITCHERS_0;
+               break;
+       case SW_SW1B:
+               if ((dvs.sw1b < SW1B_0_9V) || (dvs.sw1b > SW1B_2_2V)) {
+                       return PMIC_PARAMETER_ERROR;
+               }
+               reg_val = BITFVAL(MC13783_SWSET_SW1B_DVS, dvs.sw1b);
+               reg_mask = BITFMASK(MC13783_SWSET_SW1B_DVS);
+               reg = REG_SWITCHERS_1;
+               break;
+       case SW_SW2A:
+               if ((dvs.sw2a < SW2A_0_9V) || (dvs.sw2a > SW2A_2_2V)) {
+                       return PMIC_PARAMETER_ERROR;
+               }
+               reg_val = BITFVAL(MC13783_SWSET_SW2A_DVS, dvs.sw2a);
+               reg_mask = BITFMASK(MC13783_SWSET_SW2A_DVS);
+               reg = REG_SWITCHERS_2;
+               break;
+       case SW_SW2B:
+               if ((dvs.sw2b < SW2B_0_9V) || (dvs.sw2b > SW2B_2_2V)) {
+                       return PMIC_PARAMETER_ERROR;
+               }
+               reg_val = BITFVAL(MC13783_SWSET_SW2B_DVS, dvs.sw2b);
+               reg_mask = BITFMASK(MC13783_SWSET_SW2B_DVS);
+               reg = REG_SWITCHERS_3;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_write_reg(reg, reg_val, reg_mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function gets the DVS voltage
+ *
+ * @param        regulator    The regulator to be handled.
+ * @param        dvs          The switch Dynamic Voltage Scaling
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_get_dvs(t_pmic_regulator regulator,
+                                       t_regulator_voltage *dvs)
+{
+       unsigned int reg_val = 0, reg_mask = 0;
+       unsigned int reg;
+
+       switch (regulator) {
+       case SW_SW1A:
+               reg_mask = BITFMASK(MC13783_SWSET_SW1A_DVS);
+               reg = REG_SWITCHERS_0;
+               break;
+       case SW_SW1B:
+               reg_mask = BITFMASK(MC13783_SWSET_SW1B_DVS);
+               reg = REG_SWITCHERS_1;
+               break;
+       case SW_SW2A:
+               reg_mask = BITFMASK(MC13783_SWSET_SW2A_DVS);
+               reg = REG_SWITCHERS_2;
+               break;
+       case SW_SW2B:
+               reg_mask = BITFMASK(MC13783_SWSET_SW2B_DVS);
+               reg = REG_SWITCHERS_3;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_read_reg(reg, &reg_val, reg_mask));
+
+       switch (regulator) {
+       case SW_SW1A:
+               *dvs = (t_regulator_voltage) BITFEXT(reg_val,
+                                                    MC13783_SWSET_SW1A_DVS);
+               break;
+       case SW_SW1B:
+               *dvs = (t_regulator_voltage) BITFEXT(reg_val,
+                                                    MC13783_SWSET_SW1B_DVS);
+               break;
+       case SW_SW2A:
+               *dvs = (t_regulator_voltage) BITFEXT(reg_val,
+                                                    MC13783_SWSET_SW2A_DVS);
+               break;
+       case SW_SW2B:
+               *dvs = (t_regulator_voltage) BITFEXT(reg_val,
+                                                    MC13783_SWSET_SW2B_DVS);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets the standiby voltage
+ *
+ * @param        regulator    The regulator to be configured.
+ * @param        stby         The switch standby voltage
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_set_stby(t_pmic_regulator regulator,
+                                        t_regulator_voltage stby)
+{
+       unsigned int reg_val = 0, reg_mask = 0;
+       unsigned int reg;
+
+       switch (regulator) {
+       case SW_SW1A:
+               if ((stby.sw1a < SW1A_0_9V) || (stby.sw1a > SW1A_2_2V)) {
+                       return PMIC_PARAMETER_ERROR;
+               }
+               reg_val = BITFVAL(MC13783_SWSET_SW1A_STDBY, stby.sw1a);
+               reg_mask = BITFMASK(MC13783_SWSET_SW1A_STDBY);
+               reg = REG_SWITCHERS_0;
+               break;
+       case SW_SW1B:
+               if ((stby.sw1b < SW1B_0_9V) || (stby.sw1b > SW1B_2_2V)) {
+                       return PMIC_PARAMETER_ERROR;
+               }
+               reg_val = BITFVAL(MC13783_SWSET_SW1B_STDBY, stby.sw1b);
+               reg_mask = BITFMASK(MC13783_SWSET_SW1B_STDBY);
+               reg = REG_SWITCHERS_1;
+               break;
+       case SW_SW2A:
+               if ((stby.sw2a < SW2A_0_9V) || (stby.sw2a > SW2A_2_2V)) {
+                       return PMIC_PARAMETER_ERROR;
+               }
+               reg_val = BITFVAL(MC13783_SWSET_SW2A_STDBY, stby.sw2a);
+               reg_mask = BITFMASK(MC13783_SWSET_SW2A_STDBY);
+               reg = REG_SWITCHERS_2;
+               break;
+       case SW_SW2B:
+               if ((stby.sw2b < SW2B_0_9V) || (stby.sw2b > SW2B_2_2V)) {
+                       return PMIC_PARAMETER_ERROR;
+               }
+               reg_val = BITFVAL(MC13783_SWSET_SW2B_STDBY, stby.sw2b);
+               reg_mask = BITFMASK(MC13783_SWSET_SW2B_STDBY);
+               reg = REG_SWITCHERS_3;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_write_reg(reg, reg_val, reg_mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function gets the standiby voltage
+ *
+ * @param        regulator    The regulator to be handled.
+ * @param        stby         The switch standby voltage
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_get_stby(t_pmic_regulator regulator,
+                                        t_regulator_voltage *stby)
+{
+       unsigned int reg_val = 0, reg_mask = 0;
+       unsigned int reg;
+
+       switch (regulator) {
+       case SW_SW1A:
+               reg_mask = BITFMASK(MC13783_SWSET_SW1A_STDBY);
+               reg = REG_SWITCHERS_0;
+               break;
+       case SW_SW1B:
+               reg_mask = BITFMASK(MC13783_SWSET_SW1B_STDBY);
+               reg = REG_SWITCHERS_1;
+               break;
+       case SW_SW2A:
+               reg_mask = BITFMASK(MC13783_SWSET_SW2A_STDBY);
+               reg = REG_SWITCHERS_2;
+               break;
+       case SW_SW2B:
+               reg_mask = BITFMASK(MC13783_SWSET_SW2B_STDBY);
+               reg = REG_SWITCHERS_3;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_read_reg(reg, &reg_val, reg_mask));
+
+       switch (regulator) {
+       case SW_SW1A:
+               *stby = (t_regulator_voltage) BITFEXT(reg_val,
+                                                     MC13783_SWSET_SW1A_STDBY);
+               break;
+       case SW_SW1B:
+               *stby = (t_regulator_voltage) BITFEXT(reg_val,
+                                                     MC13783_SWSET_SW1B_STDBY);
+               break;
+       case SW_SW2A:
+               *stby = (t_regulator_voltage) BITFEXT(reg_val,
+                                                     MC13783_SWSET_SW2A_STDBY);
+               break;
+       case SW_SW2B:
+               *stby = (t_regulator_voltage) BITFEXT(reg_val,
+                                                     MC13783_SWSET_SW2B_STDBY);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets the switchers mode.
+ *
+ * @param        regulator    The regulator to be configured.
+ * @param        mode        The switcher mode
+ * @param        stby        Switch between main and standby.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_set_mode(t_pmic_regulator regulator,
+                                        t_regulator_sw_mode mode, bool stby)
+{
+       unsigned int reg_val = 0, reg_mask = 0;
+       unsigned int reg;
+       unsigned int l_mode;
+
+       if (mode == SYNC_RECT) {
+               l_mode = MC13783_SWCTRL_SW_MODE_SYNC_RECT_EN;
+       } else if (mode == NO_PULSE_SKIP) {
+               l_mode = MC13783_SWCTRL_SW_MODE_PULSE_NO_SKIP_EN;
+       } else if (mode == PULSE_SKIP) {
+               l_mode = MC13783_SWCTRL_SW_MODE_PULSE_SKIP_EN;
+       } else if (mode == LOW_POWER) {
+               l_mode = MC13783_SWCTRL_SW_MODE_LOW_POWER_EN;
+       } else {
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       switch (regulator) {
+       case SW_SW1A:
+               if (stby) {
+                       reg_val =
+                           BITFVAL(MC13783_SWCTRL_SW1A_STBY_MODE, l_mode);
+                       reg_mask = BITFMASK(MC13783_SWCTRL_SW1A_STBY_MODE);
+               } else {
+                       reg_val = BITFVAL(MC13783_SWCTRL_SW1A_MODE, l_mode);
+                       reg_mask = BITFMASK(MC13783_SWCTRL_SW1A_MODE);
+               }
+               reg = REG_SWITCHERS_4;
+               break;
+       case SW_SW1B:
+               if (stby) {
+                       reg_val =
+                           BITFVAL(MC13783_SWCTRL_SW1B_STBY_MODE, l_mode);
+                       reg_mask = BITFMASK(MC13783_SWCTRL_SW1B_STBY_MODE);
+               } else {
+                       reg_val = BITFVAL(MC13783_SWCTRL_SW1B_MODE, l_mode);
+                       reg_mask = BITFMASK(MC13783_SWCTRL_SW1B_MODE);
+               }
+               reg = REG_SWITCHERS_4;
+               break;
+       case SW_SW2A:
+               if (stby) {
+                       reg_val =
+                           BITFVAL(MC13783_SWCTRL_SW2A_STBY_MODE, l_mode);
+                       reg_mask = BITFMASK(MC13783_SWCTRL_SW2A_STBY_MODE);
+               } else {
+                       reg_val = BITFVAL(MC13783_SWCTRL_SW2A_MODE, l_mode);
+                       reg_mask = BITFMASK(MC13783_SWCTRL_SW2A_MODE);
+               }
+               reg = REG_SWITCHERS_5;
+               break;
+       case SW_SW2B:
+               if (stby) {
+                       reg_val =
+                           BITFVAL(MC13783_SWCTRL_SW2B_STBY_MODE, l_mode);
+                       reg_mask = BITFMASK(MC13783_SWCTRL_SW2B_STBY_MODE);
+               } else {
+                       reg_val = BITFVAL(MC13783_SWCTRL_SW2B_MODE, l_mode);
+                       reg_mask = BITFMASK(MC13783_SWCTRL_SW2B_MODE);
+               }
+               reg = REG_SWITCHERS_5;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_write_reg(reg, reg_val, reg_mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function gets the switchers mode.
+ *
+ * @param        regulator    The regulator to be handled.
+ * @param        mode         The switcher mode.
+ * @param        stby         Switch between main and standby.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_get_mode(t_pmic_regulator regulator,
+                                        t_regulator_sw_mode *mode, bool stby)
+{
+       unsigned int reg_val = 0, reg_mask = 0;
+       unsigned int reg = 0;
+       unsigned int l_mode = 0;
+
+       switch (regulator) {
+       case SW_SW1A:
+               if (stby) {
+                       reg_mask = BITFMASK(MC13783_SWCTRL_SW1A_STBY_MODE);
+               } else {
+                       reg_mask = BITFMASK(MC13783_SWCTRL_SW1A_MODE);
+               }
+               reg = REG_SWITCHERS_4;
+               break;
+       case SW_SW1B:
+               if (stby) {
+                       reg_mask = BITFMASK(MC13783_SWCTRL_SW1B_STBY_MODE);
+               } else {
+                       reg_mask = BITFMASK(MC13783_SWCTRL_SW1B_MODE);
+               }
+               reg = REG_SWITCHERS_4;
+               break;
+       case SW_SW2A:
+               if (stby) {
+                       reg_mask = BITFMASK(MC13783_SWCTRL_SW2A_STBY_MODE);
+               } else {
+                       reg_mask = BITFMASK(MC13783_SWCTRL_SW2A_MODE);
+               }
+               reg = REG_SWITCHERS_5;
+               break;
+       case SW_SW2B:
+               if (stby) {
+                       reg_mask = BITFMASK(MC13783_SWCTRL_SW2B_STBY_MODE);
+               } else {
+                       reg_mask = BITFMASK(MC13783_SWCTRL_SW2B_MODE);
+               }
+               reg = REG_SWITCHERS_5;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_read_reg(reg, &reg_val, reg_mask));
+
+       switch (regulator) {
+       case SW_SW1A:
+               if (stby) {
+                       l_mode =
+                           BITFEXT(reg_val, MC13783_SWCTRL_SW1A_STBY_MODE);
+               } else {
+                       l_mode = BITFEXT(reg_val, MC13783_SWCTRL_SW1A_MODE);
+               }
+               break;
+       case SW_SW1B:
+               if (stby) {
+                       l_mode =
+                           BITFEXT(reg_val, MC13783_SWCTRL_SW1B_STBY_MODE);
+               } else {
+                       l_mode = BITFEXT(reg_val, MC13783_SWCTRL_SW1B_MODE);
+               }
+               break;
+       case SW_SW2A:
+               if (stby) {
+                       l_mode =
+                           BITFEXT(reg_val, MC13783_SWCTRL_SW2A_STBY_MODE);
+               } else {
+                       l_mode = BITFEXT(reg_val, MC13783_SWCTRL_SW2A_MODE);
+               }
+               break;
+       case SW_SW2B:
+               if (stby) {
+                       l_mode =
+                           BITFEXT(reg_val, MC13783_SWCTRL_SW2B_STBY_MODE);
+               } else {
+                       l_mode = BITFEXT(reg_val, MC13783_SWCTRL_SW2B_MODE);
+               }
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       if (l_mode == MC13783_SWCTRL_SW_MODE_SYNC_RECT_EN) {
+               *mode = SYNC_RECT;
+       } else if (l_mode == MC13783_SWCTRL_SW_MODE_PULSE_NO_SKIP_EN) {
+               *mode = NO_PULSE_SKIP;
+       } else if (l_mode == MC13783_SWCTRL_SW_MODE_PULSE_SKIP_EN) {
+               *mode = PULSE_SKIP;
+       } else if (l_mode == MC13783_SWCTRL_SW_MODE_LOW_POWER_EN) {
+               *mode = LOW_POWER;
+       } else {
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets the switch dvs speed
+ *
+ * @param        regulator    The regulator to be configured.
+ * @param        speed       The dvs speed.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_set_dvs_speed(t_pmic_regulator regulator,
+                                             t_switcher_dvs_speed speed)
+{
+       unsigned int reg_val = 0, reg_mask = 0;
+       unsigned int reg;
+       if (speed > 3 || speed < 0) {
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       switch (regulator) {
+       case SW_SW1A:
+               reg_val = BITFVAL(MC13783_SWCTRL_SW1A_DVS_SPEED, speed);
+               reg_mask = BITFMASK(MC13783_SWCTRL_SW1A_DVS_SPEED);
+               reg = REG_SWITCHERS_4;
+               break;
+       case SW_SW1B:
+               reg_val = BITFVAL(MC13783_SWCTRL_SW2B_DVS_SPEED, speed);
+               reg_mask = BITFMASK(MC13783_SWCTRL_SW1B_DVS_SPEED);
+               reg = REG_SWITCHERS_4;
+               break;
+       case SW_SW2A:
+               reg_val = BITFVAL(MC13783_SWCTRL_SW2A_DVS_SPEED, speed);
+               reg_mask = BITFMASK(MC13783_SWCTRL_SW2A_DVS_SPEED);
+               reg = REG_SWITCHERS_5;
+               break;
+       case SW_SW2B:
+               reg_val = BITFVAL(MC13783_SWCTRL_SW2B_DVS_SPEED, speed);
+               reg_mask = BITFMASK(MC13783_SWCTRL_SW2B_DVS_SPEED);
+               reg = REG_SWITCHERS_5;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_write_reg(reg, reg_val, reg_mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function gets the switch dvs speed
+ *
+ * @param        regulator    The regulator to be handled.
+ * @param        speed        The dvs speed.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_get_dvs_speed(t_pmic_regulator regulator,
+                                             t_switcher_dvs_speed *speed)
+{
+       unsigned int reg_val = 0, reg_mask = 0;
+       unsigned int reg;
+
+       switch (regulator) {
+       case SW_SW1A:
+               reg_mask = BITFMASK(MC13783_SWCTRL_SW1A_DVS_SPEED);
+               reg = REG_SWITCHERS_4;
+               break;
+       case SW_SW1B:
+               reg_mask = BITFMASK(MC13783_SWCTRL_SW1B_DVS_SPEED);
+               reg = REG_SWITCHERS_4;
+               break;
+       case SW_SW2A:
+               reg_mask = BITFMASK(MC13783_SWCTRL_SW2A_DVS_SPEED);
+               reg = REG_SWITCHERS_5;
+               break;
+       case SW_SW2B:
+               reg_mask = BITFMASK(MC13783_SWCTRL_SW2B_DVS_SPEED);
+               reg = REG_SWITCHERS_5;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_read_reg(reg, &reg_val, reg_mask));
+
+       switch (regulator) {
+       case SW_SW1A:
+               *speed = BITFEXT(reg_val, MC13783_SWCTRL_SW1A_DVS_SPEED);
+               break;
+       case SW_SW1B:
+               *speed = BITFEXT(reg_val, MC13783_SWCTRL_SW1B_DVS_SPEED);
+               break;
+       case SW_SW2A:
+               *speed = BITFEXT(reg_val, MC13783_SWCTRL_SW2A_DVS_SPEED);
+               break;
+       case SW_SW2B:
+               *speed = BITFEXT(reg_val, MC13783_SWCTRL_SW2B_DVS_SPEED);
+               break;
+       default:
+               break;
+       }
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets the switch panic mode
+ *
+ * @param        regulator    The regulator to be configured.
+ * @param        panic_mode   Enable or disable panic mode
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_set_panic_mode(t_pmic_regulator regulator,
+                                              bool panic_mode)
+{
+       unsigned int reg_val = 0, reg_mask = 0;
+       unsigned int reg;
+
+       switch (regulator) {
+       case SW_SW1A:
+               reg_val = BITFVAL(MC13783_SWCTRL_SW1A_PANIC_MODE, panic_mode);
+               reg_mask = BITFMASK(MC13783_SWCTRL_SW1A_PANIC_MODE);
+               reg = REG_SWITCHERS_4;
+               break;
+       case SW_SW1B:
+               reg_val = BITFVAL(MC13783_SWCTRL_SW2B_PANIC_MODE, panic_mode);
+               reg_mask = BITFMASK(MC13783_SWCTRL_SW1B_PANIC_MODE);
+               reg = REG_SWITCHERS_4;
+               break;
+       case SW_SW2A:
+               reg_val = BITFVAL(MC13783_SWCTRL_SW2A_PANIC_MODE, panic_mode);
+               reg_mask = BITFMASK(MC13783_SWCTRL_SW2A_PANIC_MODE);
+               reg = REG_SWITCHERS_5;
+               break;
+       case SW_SW2B:
+               reg_val = BITFVAL(MC13783_SWCTRL_SW2B_PANIC_MODE, panic_mode);
+               reg_mask = BITFMASK(MC13783_SWCTRL_SW2B_PANIC_MODE);
+               reg = REG_SWITCHERS_5;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_write_reg(reg, reg_val, reg_mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function gets the switch panic mode
+ *
+ * @param        regulator    The regulator to be handled
+ * @param        panic_mode   Enable or disable panic mode
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_get_panic_mode(t_pmic_regulator regulator,
+                                              bool *panic_mode)
+{
+       unsigned int reg_val = 0, reg_mask = 0;
+       unsigned int reg;
+
+       switch (regulator) {
+       case SW_SW1A:
+               reg_mask = BITFMASK(MC13783_SWCTRL_SW1A_PANIC_MODE);
+               reg = REG_SWITCHERS_4;
+               break;
+       case SW_SW1B:
+               reg_mask = BITFMASK(MC13783_SWCTRL_SW1B_PANIC_MODE);
+               reg = REG_SWITCHERS_4;
+               break;
+       case SW_SW2A:
+               reg_mask = BITFMASK(MC13783_SWCTRL_SW2A_PANIC_MODE);
+               reg = REG_SWITCHERS_5;
+               break;
+       case SW_SW2B:
+               reg_mask = BITFMASK(MC13783_SWCTRL_SW2B_PANIC_MODE);
+               reg = REG_SWITCHERS_5;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_read_reg(reg, &reg_val, reg_mask));
+
+       switch (regulator) {
+       case SW_SW1A:
+               *panic_mode = BITFEXT(reg_val, MC13783_SWCTRL_SW1A_PANIC_MODE);
+               break;
+       case SW_SW1B:
+               *panic_mode = BITFEXT(reg_val, MC13783_SWCTRL_SW1B_PANIC_MODE);
+               break;
+       case SW_SW2A:
+               *panic_mode = BITFEXT(reg_val, MC13783_SWCTRL_SW2A_PANIC_MODE);
+               break;
+       case SW_SW2B:
+               *panic_mode = BITFEXT(reg_val, MC13783_SWCTRL_SW2B_PANIC_MODE);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets the switch softstart mode
+ *
+ * @param        regulator    The regulator to be configured.
+ * @param        softstart    Enable or disable softstart.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_set_softstart(t_pmic_regulator regulator,
+                                             bool softstart)
+{
+       unsigned int reg_val = 0, reg_mask = 0;
+       unsigned int reg;
+
+       switch (regulator) {
+       case SW_SW1A:
+               reg_val = BITFVAL(MC13783_SWCTRL_SW1A_SOFTSTART, softstart);
+               reg_mask = BITFMASK(MC13783_SWCTRL_SW1A_SOFTSTART);
+               reg = REG_SWITCHERS_4;
+               break;
+       case SW_SW1B:
+               reg_val = BITFVAL(MC13783_SWCTRL_SW2B_SOFTSTART, softstart);
+               reg_mask = BITFMASK(MC13783_SWCTRL_SW1B_SOFTSTART);
+               reg = REG_SWITCHERS_4;
+               break;
+       case SW_SW2A:
+               reg_val = BITFVAL(MC13783_SWCTRL_SW2A_SOFTSTART, softstart);
+               reg_mask = BITFMASK(MC13783_SWCTRL_SW2A_SOFTSTART);
+               reg = REG_SWITCHERS_5;
+               break;
+       case SW_SW2B:
+               reg_val = BITFVAL(MC13783_SWCTRL_SW2B_SOFTSTART, softstart);
+               reg_mask = BITFMASK(MC13783_SWCTRL_SW2B_SOFTSTART);
+               reg = REG_SWITCHERS_5;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_write_reg(reg, reg_val, reg_mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function gets the switch softstart mode
+ *
+ * @param        regulator    The regulator to be handled
+ * @param        softstart    Enable or disable softstart.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_get_softstart(t_pmic_regulator regulator,
+                                             bool *softstart)
+{
+       unsigned int reg_val = 0, reg_mask = 0;
+       unsigned int reg;
+
+       switch (regulator) {
+       case SW_SW1A:
+               reg_mask = BITFMASK(MC13783_SWCTRL_SW1A_SOFTSTART);
+               reg = REG_SWITCHERS_4;
+               break;
+       case SW_SW1B:
+               reg_mask = BITFMASK(MC13783_SWCTRL_SW1B_SOFTSTART);
+               reg = REG_SWITCHERS_4;
+               break;
+       case SW_SW2A:
+               reg_mask = BITFMASK(MC13783_SWCTRL_SW2A_SOFTSTART);
+               reg = REG_SWITCHERS_5;
+               break;
+       case SW_SW2B:
+               reg_mask = BITFMASK(MC13783_SWCTRL_SW2B_SOFTSTART);
+               reg = REG_SWITCHERS_5;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_read_reg(reg, &reg_val, reg_mask));
+
+       switch (regulator) {
+       case SW_SW1A:
+               *softstart = BITFEXT(reg_val, MC13783_SWCTRL_SW1A_SOFTSTART);
+               break;
+       case SW_SW1B:
+               *softstart = BITFEXT(reg_val, MC13783_SWCTRL_SW2B_SOFTSTART);
+               break;
+       case SW_SW2A:
+               *softstart = BITFEXT(reg_val, MC13783_SWCTRL_SW2A_SOFTSTART);
+               break;
+       case SW_SW2B:
+               *softstart = BITFEXT(reg_val, MC13783_SWCTRL_SW2B_SOFTSTART);
+               break;
+       default:
+               break;
+       }
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets the PLL multiplication factor
+ *
+ * @param        regulator    The regulator to be configured.
+ * @param        factor       The multiplication factor.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_set_factor(t_pmic_regulator regulator,
+                                          t_switcher_factor factor)
+{
+       unsigned int reg_val = 0, reg_mask = 0;
+
+       if (regulator != SW_PLL) {
+               return PMIC_PARAMETER_ERROR;
+       }
+       if (factor < FACTOR_28 || factor > FACTOR_35) {
+               return PMIC_PARAMETER_ERROR;
+       }
+       reg_val = BITFVAL(MC13783_SWCTRL_PLL_FACTOR, factor);
+       reg_mask = BITFMASK(MC13783_SWCTRL_PLL_FACTOR);
+
+       CHECK_ERROR(pmic_write_reg(REG_SWITCHERS_4, reg_val, reg_mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function gets the PLL multiplication factor
+ *
+ * @param        regulator    The regulator to be handled
+ * @param        factor       The multiplication factor.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_get_factor(t_pmic_regulator regulator,
+                                          t_switcher_factor *factor)
+{
+       unsigned int reg_val = 0, reg_mask = 0;
+
+       if (regulator != SW_PLL) {
+               return PMIC_PARAMETER_ERROR;
+       }
+       reg_mask = BITFMASK(MC13783_SWCTRL_PLL_FACTOR);
+
+       CHECK_ERROR(pmic_read_reg(REG_SWITCHERS_4, &reg_val, reg_mask));
+
+       *factor = BITFEXT(reg_val, MC13783_SWCTRL_PLL_FACTOR);
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function enables or disables low power mode.
+ *
+ * @param        regulator    The regulator to be configured.
+ * @param        lp_mode      Select nominal or low power mode.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_regulator_set_lp_mode(t_pmic_regulator regulator,
+                                            t_regulator_lp_mode lp_mode)
+{
+       unsigned int reg_val = 0, reg_mask = 0;
+       unsigned int reg;
+       unsigned int l_mode, l_stby;
+
+       if (lp_mode == LOW_POWER_DISABLED) {
+               l_mode = MC13783_REGTRL_LP_MODE_DISABLE;
+               l_stby = MC13783_REGTRL_STBY_MODE_DISABLE;
+       } else if (lp_mode == LOW_POWER_CTRL_BY_PIN) {
+               l_mode = MC13783_REGTRL_LP_MODE_DISABLE;
+               l_stby = MC13783_REGTRL_STBY_MODE_ENABLE;
+       } else if (lp_mode == LOW_POWER_EN) {
+               l_mode = MC13783_REGTRL_LP_MODE_ENABLE;
+               l_stby = MC13783_REGTRL_STBY_MODE_DISABLE;
+       } else if (lp_mode == LOW_POWER_AND_LOW_POWER_CTRL_BY_PIN) {
+               l_mode = MC13783_REGTRL_LP_MODE_ENABLE;
+               l_stby = MC13783_REGTRL_STBY_MODE_ENABLE;
+       } else {
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       switch (regulator) {
+       case SW_SW3:
+               reg_val = BITFVAL(MC13783_SWCTRL_SW3_MODE, l_mode) |
+                   BITFVAL(MC13783_SWCTRL_SW3_STBY, l_stby);
+               reg_mask = BITFMASK(MC13783_SWCTRL_SW3_MODE) |
+                   BITFMASK(MC13783_SWCTRL_SW3_STBY);
+               reg = REG_SWITCHERS_5;
+               break;
+       case REGU_VAUDIO:
+               reg_val = BITFVAL(MC13783_REGCTRL_VAUDIO_MODE, l_mode) |
+                   BITFVAL(MC13783_REGCTRL_VAUDIO_STBY, l_stby);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VAUDIO_MODE) |
+                   BITFMASK(MC13783_REGCTRL_VAUDIO_STBY);
+               reg = REG_REGULATOR_MODE_0;
+               break;
+       case REGU_VIOHI:
+               reg_val = BITFVAL(MC13783_REGCTRL_VIOHI_MODE, l_mode) |
+                   BITFVAL(MC13783_REGCTRL_VIOHI_STBY, l_stby);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VIOHI_MODE) |
+                   BITFMASK(MC13783_REGCTRL_VIOHI_STBY);
+               reg = REG_REGULATOR_MODE_0;
+               break;
+       case REGU_VIOLO:
+               reg_val = BITFVAL(MC13783_REGCTRL_VIOLO_MODE, l_mode) |
+                   BITFVAL(MC13783_REGCTRL_VIOLO_STBY, l_stby);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VIOLO_MODE) |
+                   BITFMASK(MC13783_REGCTRL_VIOLO_STBY);
+               reg = REG_REGULATOR_MODE_0;
+               break;
+       case REGU_VDIG:
+               reg_val = BITFVAL(MC13783_REGCTRL_VDIG_MODE, l_mode) |
+                   BITFVAL(MC13783_REGCTRL_VDIG_STBY, l_stby);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VDIG_MODE) |
+                   BITFMASK(MC13783_REGCTRL_VDIG_STBY);
+               reg = REG_REGULATOR_MODE_0;
+               break;
+       case REGU_VGEN:
+               reg_val = BITFVAL(MC13783_REGCTRL_VGEN_MODE, l_mode) |
+                   BITFVAL(MC13783_REGCTRL_VGEN_STBY, l_stby);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VGEN_MODE) |
+                   BITFMASK(MC13783_REGCTRL_VGEN_STBY);
+               reg = REG_REGULATOR_MODE_0;
+               break;
+       case REGU_VRFDIG:
+               reg_val = BITFVAL(MC13783_REGCTRL_VRFDIG_MODE, l_mode) |
+                   BITFVAL(MC13783_REGCTRL_VRFDIG_STBY, l_stby);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VRFDIG_MODE) |
+                   BITFMASK(MC13783_REGCTRL_VRFDIG_STBY);
+               reg = REG_REGULATOR_MODE_0;
+               break;
+       case REGU_VRFREF:
+               reg_val = BITFVAL(MC13783_REGCTRL_VRFREF_MODE, l_mode) |
+                   BITFVAL(MC13783_REGCTRL_VRFREF_STBY, l_stby);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VRFREF_MODE) |
+                   BITFMASK(MC13783_REGCTRL_VRFREF_STBY);
+               reg = REG_REGULATOR_MODE_0;
+               break;
+       case REGU_VRFCP:
+               reg_val = BITFVAL(MC13783_REGCTRL_VRFCP_MODE, l_mode) |
+                   BITFVAL(MC13783_REGCTRL_VRFCP_STBY, l_stby);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VRFCP_MODE) |
+                   BITFMASK(MC13783_REGCTRL_VRFCP_STBY);
+               reg = REG_REGULATOR_MODE_0;
+               break;
+       case REGU_VSIM:
+               reg_val = BITFVAL(MC13783_REGCTRL_VSIM_MODE, l_mode) |
+                   BITFVAL(MC13783_REGCTRL_VSIM_STBY, l_stby);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VSIM_MODE) |
+                   BITFMASK(MC13783_REGCTRL_VSIM_STBY);
+               reg = REG_REGULATOR_MODE_1;
+               break;
+       case REGU_VESIM:
+               reg_val = BITFVAL(MC13783_REGCTRL_VESIM_MODE, l_mode) |
+                   BITFVAL(MC13783_REGCTRL_VESIM_STBY, l_stby);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VESIM_MODE) |
+                   BITFMASK(MC13783_REGCTRL_VESIM_STBY);
+               reg = REG_REGULATOR_MODE_1;
+               break;
+       case REGU_VCAM:
+               reg_val = BITFVAL(MC13783_REGCTRL_VCAM_MODE, l_mode) |
+                   BITFVAL(MC13783_REGCTRL_VCAM_STBY, l_stby);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VCAM_MODE) |
+                   BITFMASK(MC13783_REGCTRL_VCAM_STBY);
+               reg = REG_REGULATOR_MODE_1;
+               break;
+       case REGU_VRFBG:
+               if ((lp_mode == LOW_POWER) ||
+                   (lp_mode == LOW_POWER_AND_LOW_POWER_CTRL_BY_PIN)) {
+                       return PMIC_PARAMETER_ERROR;
+               }
+               reg_val = BITFVAL(MC13783_REGCTRL_VRFBG_STBY, l_mode);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VRFBG_STBY);
+               reg = REG_REGULATOR_MODE_1;
+               break;
+       case REGU_VRF1:
+               reg_val = BITFVAL(MC13783_REGCTRL_VRF1_MODE, l_mode) |
+                   BITFVAL(MC13783_REGCTRL_VRF1_STBY, l_stby);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VRF1_MODE) |
+                   BITFMASK(MC13783_REGCTRL_VRF1_STBY);
+               reg = REG_REGULATOR_MODE_1;
+               break;
+       case REGU_VRF2:
+               reg_val = BITFVAL(MC13783_REGCTRL_VRF2_MODE, l_mode) |
+                   BITFVAL(MC13783_REGCTRL_VRF2_STBY, l_stby);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VRF2_MODE) |
+                   BITFMASK(MC13783_REGCTRL_VRF2_STBY);
+               reg = REG_REGULATOR_MODE_1;
+               break;
+       case REGU_VMMC1:
+               reg_val = BITFVAL(MC13783_REGCTRL_VMMC1_MODE, l_mode) |
+                   BITFVAL(MC13783_REGCTRL_VMMC1_STBY, l_stby);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VMMC1_MODE) |
+                   BITFMASK(MC13783_REGCTRL_VMMC1_STBY);
+               reg = REG_REGULATOR_MODE_1;
+               break;
+       case REGU_VMMC2:
+               reg_val = BITFVAL(MC13783_REGCTRL_VMMC2_MODE, l_mode) |
+                   BITFVAL(MC13783_REGCTRL_VMMC2_STBY, l_stby);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VMMC2_MODE) |
+                   BITFMASK(MC13783_REGCTRL_VMMC2_STBY);
+               reg = REG_REGULATOR_MODE_1;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_write_reg(reg, reg_val, reg_mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function gets low power mode.
+ *
+ * @param        regulator    The regulator to be handled
+ * @param        lp_mode      Select nominal or low power mode.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_regulator_get_lp_mode(t_pmic_regulator regulator,
+                                            t_regulator_lp_mode *lp_mode)
+{
+       unsigned int reg_val = 0, reg_mask = 0;
+       unsigned int reg;
+       unsigned int l_mode, l_stby;
+
+       switch (regulator) {
+       case SW_SW3:
+               reg_mask = BITFMASK(MC13783_SWCTRL_SW3_MODE) |
+                   BITFMASK(MC13783_SWCTRL_SW3_STBY);
+               reg = REG_SWITCHERS_5;
+               break;
+       case REGU_VAUDIO:
+               reg_mask = BITFMASK(MC13783_REGCTRL_VAUDIO_MODE) |
+                   BITFMASK(MC13783_REGCTRL_VAUDIO_STBY);
+               reg = REG_REGULATOR_MODE_0;
+               break;
+       case REGU_VIOHI:
+               reg_mask = BITFMASK(MC13783_REGCTRL_VIOHI_MODE) |
+                   BITFMASK(MC13783_REGCTRL_VIOHI_STBY);
+               reg = REG_REGULATOR_MODE_0;
+               break;
+       case REGU_VIOLO:
+               reg_mask = BITFMASK(MC13783_REGCTRL_VIOLO_MODE) |
+                   BITFMASK(MC13783_REGCTRL_VIOLO_STBY);
+               reg = REG_REGULATOR_MODE_0;
+               break;
+       case REGU_VDIG:
+               reg_mask = BITFMASK(MC13783_REGCTRL_VDIG_MODE) |
+                   BITFMASK(MC13783_REGCTRL_VDIG_STBY);
+               reg = REG_REGULATOR_MODE_0;
+               break;
+       case REGU_VGEN:
+               reg_mask = BITFMASK(MC13783_REGCTRL_VGEN_MODE) |
+                   BITFMASK(MC13783_REGCTRL_VGEN_STBY);
+               reg = REG_REGULATOR_MODE_0;
+               break;
+       case REGU_VRFDIG:
+               reg_mask = BITFMASK(MC13783_REGCTRL_VRFDIG_MODE) |
+                   BITFMASK(MC13783_REGCTRL_VRFDIG_STBY);
+               reg = REG_REGULATOR_MODE_0;
+               break;
+       case REGU_VRFREF:
+               reg_mask = BITFMASK(MC13783_REGCTRL_VRFREF_MODE) |
+                   BITFMASK(MC13783_REGCTRL_VRFREF_STBY);
+               reg = REG_REGULATOR_MODE_0;
+               break;
+       case REGU_VRFCP:
+               reg_mask = BITFMASK(MC13783_REGCTRL_VRFCP_MODE) |
+                   BITFMASK(MC13783_REGCTRL_VRFCP_STBY);
+               reg = REG_REGULATOR_MODE_0;
+               break;
+       case REGU_VSIM:
+               reg_mask = BITFMASK(MC13783_REGCTRL_VSIM_MODE) |
+                   BITFMASK(MC13783_REGCTRL_VSIM_STBY);
+               reg = REG_REGULATOR_MODE_1;
+               break;
+       case REGU_VESIM:
+               reg_mask = BITFMASK(MC13783_REGCTRL_VESIM_MODE) |
+                   BITFMASK(MC13783_REGCTRL_VESIM_STBY);
+               reg = REG_REGULATOR_MODE_1;
+               break;
+       case REGU_VCAM:
+               reg_mask = BITFMASK(MC13783_REGCTRL_VCAM_MODE) |
+                   BITFMASK(MC13783_REGCTRL_VCAM_STBY);
+               reg = REG_REGULATOR_MODE_1;
+               break;
+       case REGU_VRFBG:
+               reg_mask = BITFMASK(MC13783_REGCTRL_VRFBG_STBY);
+               reg = REG_REGULATOR_MODE_1;
+               break;
+       case REGU_VRF1:
+               reg_mask = BITFMASK(MC13783_REGCTRL_VRF1_MODE) |
+                   BITFMASK(MC13783_REGCTRL_VRF1_STBY);
+               reg = REG_REGULATOR_MODE_1;
+               break;
+       case REGU_VRF2:
+               reg_mask = BITFMASK(MC13783_REGCTRL_VRF2_MODE) |
+                   BITFMASK(MC13783_REGCTRL_VRF2_STBY);
+               reg = REG_REGULATOR_MODE_1;
+               break;
+       case REGU_VMMC1:
+               reg_mask = BITFMASK(MC13783_REGCTRL_VMMC1_MODE) |
+                   BITFMASK(MC13783_REGCTRL_VMMC1_STBY);
+               reg = REG_REGULATOR_MODE_1;
+               break;
+       case REGU_VMMC2:
+               reg_mask = BITFMASK(MC13783_REGCTRL_VMMC2_MODE) |
+                   BITFMASK(MC13783_REGCTRL_VMMC2_STBY);
+               reg = REG_REGULATOR_MODE_1;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_read_reg(reg, &reg_val, reg_mask));
+
+       switch (regulator) {
+       case SW_SW3:
+               l_mode = BITFEXT(reg_val, MC13783_SWCTRL_SW3_MODE);
+               l_stby = BITFEXT(reg_val, MC13783_SWCTRL_SW3_STBY);
+               break;
+       case REGU_VAUDIO:
+               l_mode = BITFEXT(reg_val, MC13783_REGCTRL_VAUDIO_MODE);
+               l_stby = BITFEXT(reg_val, MC13783_REGCTRL_VAUDIO_STBY);
+               break;
+       case REGU_VIOHI:
+               l_mode = BITFEXT(reg_val, MC13783_REGCTRL_VIOHI_MODE);
+               l_stby = BITFEXT(reg_val, MC13783_REGCTRL_VIOHI_STBY);
+               break;
+       case REGU_VIOLO:
+               l_mode = BITFEXT(reg_val, MC13783_REGCTRL_VIOLO_MODE);
+               l_stby = BITFEXT(reg_val, MC13783_REGCTRL_VIOLO_STBY);
+               break;
+       case REGU_VDIG:
+               l_mode = BITFEXT(reg_val, MC13783_REGCTRL_VDIG_MODE);
+               l_stby = BITFEXT(reg_val, MC13783_REGCTRL_VDIG_STBY);
+               break;
+       case REGU_VGEN:
+               l_mode = BITFEXT(reg_val, MC13783_REGCTRL_VGEN_MODE);
+               l_stby = BITFEXT(reg_val, MC13783_REGCTRL_VGEN_STBY);
+               break;
+       case REGU_VRFDIG:
+               l_mode = BITFEXT(reg_val, MC13783_REGCTRL_VRFDIG_MODE);
+               l_stby = BITFEXT(reg_val, MC13783_REGCTRL_VRFDIG_STBY);
+               break;
+       case REGU_VRFREF:
+               l_mode = BITFEXT(reg_val, MC13783_REGCTRL_VRFREF_MODE);
+               l_stby = BITFEXT(reg_val, MC13783_REGCTRL_VRFREF_STBY);
+               break;
+       case REGU_VRFCP:
+               l_mode = BITFEXT(reg_val, MC13783_REGCTRL_VRFCP_MODE);
+               l_stby = BITFEXT(reg_val, MC13783_REGCTRL_VRFCP_STBY);
+               break;
+       case REGU_VSIM:
+               l_mode = BITFEXT(reg_val, MC13783_REGCTRL_VSIM_MODE);
+               l_stby = BITFEXT(reg_val, MC13783_REGCTRL_VSIM_STBY);
+               break;
+       case REGU_VESIM:
+               l_mode = BITFEXT(reg_val, MC13783_REGCTRL_VESIM_MODE);
+               l_stby = BITFEXT(reg_val, MC13783_REGCTRL_VESIM_STBY);
+               break;
+       case REGU_VCAM:
+               l_mode = BITFEXT(reg_val, MC13783_REGCTRL_VCAM_MODE);
+               l_stby = BITFEXT(reg_val, MC13783_REGCTRL_VCAM_STBY);
+               break;
+       case REGU_VRFBG:
+               l_mode = MC13783_REGTRL_LP_MODE_DISABLE;
+               l_stby = BITFEXT(reg_val, MC13783_REGCTRL_VRFBG_STBY);
+               break;
+       case REGU_VRF1:
+               l_mode = BITFEXT(reg_val, MC13783_REGCTRL_VRF1_MODE);
+               l_stby = BITFEXT(reg_val, MC13783_REGCTRL_VRF1_STBY);
+               break;
+       case REGU_VRF2:
+               l_mode = BITFEXT(reg_val, MC13783_REGCTRL_VRF2_MODE);
+               l_stby = BITFEXT(reg_val, MC13783_REGCTRL_VRF2_STBY);
+               break;
+       case REGU_VMMC1:
+               l_mode = BITFEXT(reg_val, MC13783_REGCTRL_VMMC1_MODE);
+               l_stby = BITFEXT(reg_val, MC13783_REGCTRL_VMMC1_STBY);
+               break;
+       case REGU_VMMC2:
+               l_mode = BITFEXT(reg_val, MC13783_REGCTRL_VMMC2_MODE);
+               l_stby = BITFEXT(reg_val, MC13783_REGCTRL_VMMC2_STBY);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       if ((l_mode == MC13783_REGTRL_LP_MODE_DISABLE) &&
+           (l_stby == MC13783_REGTRL_STBY_MODE_DISABLE)) {
+               *lp_mode = LOW_POWER_DISABLED;
+       } else if ((l_mode == MC13783_REGTRL_LP_MODE_DISABLE) &&
+                  (l_stby == MC13783_REGTRL_STBY_MODE_ENABLE)) {
+               *lp_mode = LOW_POWER_CTRL_BY_PIN;
+       } else if ((l_mode == MC13783_REGTRL_LP_MODE_ENABLE) &&
+                  (l_stby == MC13783_REGTRL_STBY_MODE_DISABLE)) {
+               *lp_mode = LOW_POWER_EN;
+       } else {
+               *lp_mode = LOW_POWER_AND_LOW_POWER_CTRL_BY_PIN;
+       }
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets the regulator configuration.
+ *
+ * @param        regulator    The regulator to be configured.
+ * @param        config       The regulator output configuration.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_regulator_set_config(t_pmic_regulator regulator,
+                                           t_regulator_config *config)
+{
+       if (config == NULL) {
+               return PMIC_ERROR;
+       }
+
+       switch (regulator) {
+       case SW_SW1A:
+       case SW_SW1B:
+       case SW_SW2A:
+       case SW_SW2B:
+               CHECK_ERROR(pmic_power_regulator_set_voltage
+                           (regulator, config->voltage));
+               CHECK_ERROR(pmic_power_switcher_set_dvs
+                           (regulator, config->voltage_lvs));
+               CHECK_ERROR(pmic_power_switcher_set_stby
+                           (regulator, config->voltage_stby));
+               CHECK_ERROR(pmic_power_switcher_set_mode
+                           (regulator, config->mode, false));
+               CHECK_ERROR(pmic_power_switcher_set_mode
+                           (regulator, config->stby_mode, true));
+               CHECK_ERROR(pmic_power_switcher_set_dvs_speed
+                           (regulator, config->dvs_speed));
+               CHECK_ERROR(pmic_power_switcher_set_panic_mode
+                           (regulator, config->panic_mode));
+               CHECK_ERROR(pmic_power_switcher_set_softstart
+                           (regulator, config->softstart));
+               break;
+       case SW_PLL:
+               CHECK_ERROR(pmic_power_switcher_set_factor
+                           (regulator, config->factor));
+               break;
+       case SW_SW3:
+       case REGU_VIOLO:
+       case REGU_VDIG:
+       case REGU_VGEN:
+       case REGU_VRFDIG:
+       case REGU_VRFREF:
+       case REGU_VRFCP:
+       case REGU_VSIM:
+       case REGU_VESIM:
+       case REGU_VCAM:
+       case REGU_VRF1:
+       case REGU_VRF2:
+       case REGU_VMMC1:
+       case REGU_VMMC2:
+               CHECK_ERROR(pmic_power_regulator_set_voltage
+                           (regulator, config->voltage));
+               CHECK_ERROR(pmic_power_regulator_set_lp_mode
+                           (regulator, config->lp_mode));
+               break;
+       case REGU_VVIB:
+               CHECK_ERROR(pmic_power_regulator_set_voltage
+                           (regulator, config->voltage));
+               break;
+       case REGU_VAUDIO:
+       case REGU_VIOHI:
+       case REGU_VRFBG:
+               CHECK_ERROR(pmic_power_regulator_set_lp_mode
+                           (regulator, config->lp_mode));
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function retrives the regulator output configuration.
+ *
+ * @param        regulator    The regulator to be truned off.
+ * @param        config       Pointer to regulator configuration.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_regulator_get_config(t_pmic_regulator regulator,
+                                           t_regulator_config *config)
+{
+       if (config == NULL) {
+               return PMIC_ERROR;
+       }
+
+       switch (regulator) {
+       case SW_SW1A:
+       case SW_SW1B:
+       case SW_SW2A:
+       case SW_SW2B:
+               CHECK_ERROR(pmic_power_regulator_get_voltage
+                           (regulator, &config->voltage));
+               CHECK_ERROR(pmic_power_switcher_get_dvs
+                           (regulator, &config->voltage_lvs));
+               CHECK_ERROR(pmic_power_switcher_get_stby
+                           (regulator, &config->voltage_stby));
+               CHECK_ERROR(pmic_power_switcher_get_mode
+                           (regulator, &config->mode, false));
+               CHECK_ERROR(pmic_power_switcher_get_mode
+                           (regulator, &config->stby_mode, true));
+               CHECK_ERROR(pmic_power_switcher_get_dvs_speed
+                           (regulator, &config->dvs_speed));
+               CHECK_ERROR(pmic_power_switcher_get_panic_mode
+                           (regulator, &config->panic_mode));
+               CHECK_ERROR(pmic_power_switcher_get_softstart
+                           (regulator, &config->softstart));
+               break;
+       case SW_PLL:
+               CHECK_ERROR(pmic_power_switcher_get_factor
+                           (regulator, &config->factor));
+               break;
+       case SW_SW3:
+       case REGU_VIOLO:
+       case REGU_VDIG:
+       case REGU_VGEN:
+       case REGU_VRFDIG:
+       case REGU_VRFREF:
+       case REGU_VRFCP:
+       case REGU_VSIM:
+       case REGU_VESIM:
+       case REGU_VCAM:
+       case REGU_VRF1:
+       case REGU_VRF2:
+       case REGU_VMMC1:
+       case REGU_VMMC2:
+               CHECK_ERROR(pmic_power_regulator_get_voltage
+                           (regulator, &config->voltage));
+               CHECK_ERROR(pmic_power_regulator_get_lp_mode
+                           (regulator, &config->lp_mode));
+               break;
+       case REGU_VVIB:
+               CHECK_ERROR(pmic_power_regulator_get_voltage
+                           (regulator, &config->voltage));
+               break;
+       case REGU_VAUDIO:
+       case REGU_VIOHI:
+       case REGU_VRFBG:
+               CHECK_ERROR(pmic_power_regulator_get_lp_mode
+                           (regulator, &config->lp_mode));
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function enables automatically VBKUP2 in the memory hold modes.
+ * Only on mc13783 2.0 or higher
+ *
+ * @param        en           if true, enable VBKUP2AUTOMH
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_vbkup2_auto_en(bool en)
+{
+       unsigned int reg_val = 0, reg_mask = 0;
+       pmic_version_t mc13783_ver;
+       mc13783_ver = pmic_get_version();
+       if (mc13783_ver.revision >= 20) {
+               reg_val = BITFVAL(MC13783_REGCTRL_VBKUP2AUTOMH, en);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VBKUP2AUTOMH);
+
+               CHECK_ERROR(pmic_write_reg(REG_POWER_CONTROL_0,
+                                          reg_val, reg_mask));
+               return PMIC_SUCCESS;
+       } else {
+               return PMIC_NOT_SUPPORTED;
+       }
+}
+
+/*!
+ * This function gets state of automatically VBKUP2.
+ * Only on mc13783 2.0 or higher
+ *
+ * @param        en           if true, VBKUP2AUTOMH is enabled
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_get_vbkup2_auto_state(bool *en)
+{
+       unsigned int reg_val = 0, reg_mask = 0;
+       pmic_version_t mc13783_ver;
+       mc13783_ver = pmic_get_version();
+       if (mc13783_ver.revision >= 20) {
+               reg_mask = BITFMASK(MC13783_REGCTRL_VBKUP2AUTOMH);
+               CHECK_ERROR(pmic_read_reg(REG_POWER_CONTROL_0,
+                                         &reg_val, reg_mask));
+               *en = BITFEXT(reg_val, MC13783_REGCTRL_VBKUP2AUTOMH);
+
+               return PMIC_SUCCESS;
+       } else {
+               return PMIC_NOT_SUPPORTED;
+       }
+}
+
+/*!
+ * This function enables battery detect function.
+ * Only on mc13783 2.0 or higher
+ *
+ * @param        en           if true, enable BATTDETEN
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_bat_det_en(bool en)
+{
+       unsigned int reg_val = 0, reg_mask = 0;
+       pmic_version_t mc13783_ver;
+       mc13783_ver = pmic_get_version();
+       if (mc13783_ver.revision >= 20) {
+               reg_val = BITFVAL(MC13783_REGCTRL_BATTDETEN, en);
+               reg_mask = BITFMASK(MC13783_REGCTRL_BATTDETEN);
+
+               CHECK_ERROR(pmic_write_reg(REG_POWER_CONTROL_0,
+                                          reg_val, reg_mask));
+               return PMIC_SUCCESS;
+       } else {
+               return PMIC_NOT_SUPPORTED;
+       }
+}
+
+/*!
+ * This function gets state of battery detect function.
+ * Only on mc13783 2.0 or higher
+ *
+ * @param        en           if true, BATTDETEN is enabled
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_get_bat_det_state(bool *en)
+{
+       unsigned int reg_val = 0, reg_mask = 0;
+       pmic_version_t mc13783_ver;
+       mc13783_ver = pmic_get_version();
+       if (mc13783_ver.revision >= 20) {
+               reg_mask = BITFMASK(MC13783_REGCTRL_BATTDETEN);
+
+               CHECK_ERROR(pmic_read_reg(REG_POWER_CONTROL_0,
+                                         &reg_val, reg_mask));
+               *en = BITFEXT(reg_val, MC13783_REGCTRL_BATTDETEN);
+               return PMIC_SUCCESS;
+       } else {
+               return PMIC_NOT_SUPPORTED;
+       }
+}
+
+/*!
+ * This function enables control of VVIB by VIBEN pin.
+ * Only on mc13783 2.0 or higher
+ *
+ * @param        en           if true, enable VIBPINCTRL
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_vib_pin_en(bool en)
+{
+       unsigned int reg_val = 0, reg_mask = 0;
+       pmic_version_t mc13783_ver;
+       mc13783_ver = pmic_get_version();
+       if (mc13783_ver.revision >= 20) {
+               reg_val = BITFVAL(MC13783_REGCTRL_VIBPINCTRL, en);
+               reg_mask = BITFMASK(MC13783_REGCTRL_VIBPINCTRL);
+
+               CHECK_ERROR(pmic_write_reg(REG_POWER_MISCELLANEOUS,
+                                          reg_val, reg_mask));
+               return PMIC_SUCCESS;
+       } else {
+               return PMIC_NOT_SUPPORTED;
+       }
+}
+
+/*!
+ * This function gets state of control of VVIB by VIBEN pin.
+ * Only on mc13783 2.0 or higher
+ * @param        en           if true, VIBPINCTRL is enabled
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_gets_vib_pin_state(bool *en)
+{
+       unsigned int reg_val = 0, reg_mask = 0;
+       pmic_version_t mc13783_ver;
+       mc13783_ver = pmic_get_version();
+       if (mc13783_ver.revision >= 20) {
+               reg_mask = BITFMASK(MC13783_REGCTRL_VIBPINCTRL);
+               CHECK_ERROR(pmic_read_reg(REG_POWER_MISCELLANEOUS,
+                                         &reg_val, reg_mask));
+               *en = BITFEXT(reg_val, MC13783_REGCTRL_VIBPINCTRL);
+               return PMIC_SUCCESS;
+       } else {
+               return PMIC_NOT_SUPPORTED;
+       }
+}
+
+/*!
+ * This function returns power up sense value
+ *
+ * @param        p_up_sense     value of power up sense
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_get_power_mode_sense(struct t_p_up_sense *p_up_sense)
+{
+       unsigned int reg_value = 0;
+       CHECK_ERROR(pmic_read_reg(REG_POWER_UP_MODE_SENSE,
+                                 &reg_value, PMIC_ALL_BITS));
+       p_up_sense->state_ictest = (STATE_ICTEST_MASK & reg_value);
+       p_up_sense->state_clksel = ((STATE_CLKSEL_MASK & reg_value)
+                                   >> STATE_CLKSEL_BIT);
+       p_up_sense->state_pums1 = ((STATE_PUMS1_MASK & reg_value)
+                                  >> STATE_PUMS1_BITS);
+       p_up_sense->state_pums2 = ((STATE_PUMS2_MASK & reg_value)
+                                  >> STATE_PUMS2_BITS);
+       p_up_sense->state_pums3 = ((STATE_PUMS3_MASK & reg_value)
+                                  >> STATE_PUMS3_BITS);
+       p_up_sense->state_chrgmode0 = ((STATE_CHRGM1_MASK & reg_value)
+                                      >> STATE_CHRGM1_BITS);
+       p_up_sense->state_chrgmode1 = ((STATE_CHRGM2_MASK & reg_value)
+                                      >> STATE_CHRGM2_BITS);
+       p_up_sense->state_umod = ((STATE_UMOD_MASK & reg_value)
+                                 >> STATE_UMOD_BITS);
+       p_up_sense->state_usben = ((STATE_USBEN_MASK & reg_value)
+                                  >> STATE_USBEN_BIT);
+       p_up_sense->state_sw_1a1b_joined = ((STATE_SW1A_J_B_MASK & reg_value)
+                                           >> STATE_SW1A_J_B_BIT);
+       p_up_sense->state_sw_2a2b_joined = ((STATE_SW2A_J_B_MASK & reg_value)
+                                           >> STATE_SW2A_J_B_BIT);
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function configures the Regen assignment for all regulator
+ *
+ * @param        regulator      type of regulator
+ * @param        en_dis         if true, the regulator is enabled by regen.
+ *
+ * @return       This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_set_regen_assig(t_pmic_regulator regulator, bool en_dis)
+{
+       unsigned int reg_val = 0, reg_mask = 0;
+
+       switch (regulator) {
+       case REGU_VAUDIO:
+               reg_val = BITFVAL(MC13783_REGGEN_VAUDIO, en_dis);
+               reg_mask = BITFMASK(MC13783_REGGEN_VAUDIO);
+               break;
+       case REGU_VIOHI:
+               reg_val = BITFVAL(MC13783_REGGEN_VIOHI, en_dis);
+               reg_mask = BITFMASK(MC13783_REGGEN_VIOHI);
+               break;
+       case REGU_VIOLO:
+               reg_val = BITFVAL(MC13783_REGGEN_VIOLO, en_dis);
+               reg_mask = BITFMASK(MC13783_REGGEN_VIOLO);
+               break;
+       case REGU_VDIG:
+               reg_val = BITFVAL(MC13783_REGGEN_VDIG, en_dis);
+               reg_mask = BITFMASK(MC13783_REGGEN_VDIG);
+               break;
+       case REGU_VGEN:
+               reg_val = BITFVAL(MC13783_REGGEN_VGEN, en_dis);
+               reg_mask = BITFMASK(MC13783_REGGEN_VGEN);
+               break;
+       case REGU_VRFDIG:
+               reg_val = BITFVAL(MC13783_REGGEN_VRFDIG, en_dis);
+               reg_mask = BITFMASK(MC13783_REGGEN_VRFDIG);
+               break;
+       case REGU_VRFREF:
+               reg_val = BITFVAL(MC13783_REGGEN_VRFREF, en_dis);
+               reg_mask = BITFMASK(MC13783_REGGEN_VRFREF);
+               break;
+       case REGU_VRFCP:
+               reg_val = BITFVAL(MC13783_REGGEN_VRFCP, en_dis);
+               reg_mask = BITFMASK(MC13783_REGGEN_VRFCP);
+               break;
+       case REGU_VCAM:
+               reg_val = BITFVAL(MC13783_REGGEN_VCAM, en_dis);
+               reg_mask = BITFMASK(MC13783_REGGEN_VCAM);
+               break;
+       case REGU_VRFBG:
+               reg_val = BITFVAL(MC13783_REGGEN_VRFBG, en_dis);
+               reg_mask = BITFMASK(MC13783_REGGEN_VRFBG);
+               break;
+       case REGU_VRF1:
+               reg_val = BITFVAL(MC13783_REGGEN_VRF1, en_dis);
+               reg_mask = BITFMASK(MC13783_REGGEN_VRF1);
+               break;
+       case REGU_VRF2:
+               reg_val = BITFVAL(MC13783_REGGEN_VRF2, en_dis);
+               reg_mask = BITFMASK(MC13783_REGGEN_VRF2);
+               break;
+       case REGU_VMMC1:
+               reg_val = BITFVAL(MC13783_REGGEN_VMMC1, en_dis);
+               reg_mask = BITFMASK(MC13783_REGGEN_VMMC1);
+               break;
+       case REGU_VMMC2:
+               reg_val = BITFVAL(MC13783_REGGEN_VMMC2, en_dis);
+               reg_mask = BITFMASK(MC13783_REGGEN_VMMC2);
+               break;
+       case REGU_GPO1:
+               reg_val = BITFVAL(MC13783_REGGEN_GPO1, en_dis);
+               reg_mask = BITFMASK(MC13783_REGGEN_GPO1);
+               break;
+       case REGU_GPO2:
+               reg_val = BITFVAL(MC13783_REGGEN_GPO2, en_dis);
+               reg_mask = BITFMASK(MC13783_REGGEN_GPO2);
+               break;
+       case REGU_GPO3:
+               reg_val = BITFVAL(MC13783_REGGEN_GPO3, en_dis);
+               reg_mask = BITFMASK(MC13783_REGGEN_GPO3);
+               break;
+       case REGU_GPO4:
+               reg_val = BITFVAL(MC13783_REGGEN_GPO4, en_dis);
+               reg_mask = BITFMASK(MC13783_REGGEN_GPO4);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_write_reg(REG_REGEN_ASSIGNMENT, reg_val, reg_mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function gets the Regen assignment for all regulator
+ *
+ * @param        regulator      type of regulator
+ * @param        en_dis         return value, if true :
+ *                              the regulator is enabled by regen.
+ * @return       This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_get_regen_assig(t_pmic_regulator regulator,
+                                      bool *en_dis)
+{
+       unsigned int reg_val = 0, reg_mask = 0;
+
+       switch (regulator) {
+       case REGU_VAUDIO:
+               reg_mask = BITFMASK(MC13783_REGGEN_VAUDIO);
+               break;
+       case REGU_VIOHI:
+               reg_mask = BITFMASK(MC13783_REGGEN_VIOHI);
+               break;
+       case REGU_VIOLO:
+               reg_mask = BITFMASK(MC13783_REGGEN_VIOLO);
+               break;
+       case REGU_VDIG:
+               reg_mask = BITFMASK(MC13783_REGGEN_VDIG);
+               break;
+       case REGU_VGEN:
+               reg_mask = BITFMASK(MC13783_REGGEN_VGEN);
+               break;
+       case REGU_VRFDIG:
+               reg_mask = BITFMASK(MC13783_REGGEN_VRFDIG);
+               break;
+       case REGU_VRFREF:
+               reg_mask = BITFMASK(MC13783_REGGEN_VRFREF);
+               break;
+       case REGU_VRFCP:
+               reg_mask = BITFMASK(MC13783_REGGEN_VRFCP);
+               break;
+       case REGU_VCAM:
+               reg_mask = BITFMASK(MC13783_REGGEN_VCAM);
+               break;
+       case REGU_VRFBG:
+               reg_mask = BITFMASK(MC13783_REGGEN_VRFBG);
+               break;
+       case REGU_VRF1:
+               reg_mask = BITFMASK(MC13783_REGGEN_VRF1);
+               break;
+       case REGU_VRF2:
+               reg_mask = BITFMASK(MC13783_REGGEN_VRF2);
+               break;
+       case REGU_VMMC1:
+               reg_mask = BITFMASK(MC13783_REGGEN_VMMC1);
+               break;
+       case REGU_VMMC2:
+               reg_mask = BITFMASK(MC13783_REGGEN_VMMC2);
+               break;
+       case REGU_GPO1:
+               reg_mask = BITFMASK(MC13783_REGGEN_GPO1);
+               break;
+       case REGU_GPO2:
+               reg_mask = BITFMASK(MC13783_REGGEN_GPO2);
+               break;
+       case REGU_GPO3:
+               reg_mask = BITFMASK(MC13783_REGGEN_GPO3);
+               break;
+       case REGU_GPO4:
+               reg_mask = BITFMASK(MC13783_REGGEN_GPO4);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_read_reg(REG_REGEN_ASSIGNMENT, &reg_val, reg_mask));
+
+       switch (regulator) {
+       case REGU_VAUDIO:
+               *en_dis = BITFEXT(reg_val, MC13783_REGGEN_VAUDIO);
+               break;
+       case REGU_VIOHI:
+               *en_dis = BITFEXT(reg_val, MC13783_REGGEN_VIOHI);
+               break;
+       case REGU_VIOLO:
+               *en_dis = BITFEXT(reg_val, MC13783_REGGEN_VIOLO);
+               break;
+       case REGU_VDIG:
+               *en_dis = BITFEXT(reg_val, MC13783_REGGEN_VDIG);
+               break;
+       case REGU_VGEN:
+               *en_dis = BITFEXT(reg_val, MC13783_REGGEN_VGEN);
+               break;
+       case REGU_VRFDIG:
+               *en_dis = BITFEXT(reg_val, MC13783_REGGEN_VRFDIG);
+               break;
+       case REGU_VRFREF:
+               *en_dis = BITFEXT(reg_val, MC13783_REGGEN_VRFREF);
+               break;
+       case REGU_VRFCP:
+               *en_dis = BITFEXT(reg_val, MC13783_REGGEN_VRFCP);
+               break;
+       case REGU_VCAM:
+               *en_dis = BITFEXT(reg_val, MC13783_REGGEN_VCAM);
+               break;
+       case REGU_VRFBG:
+               *en_dis = BITFEXT(reg_val, MC13783_REGGEN_VRFBG);
+               break;
+       case REGU_VRF1:
+               *en_dis = BITFEXT(reg_val, MC13783_REGGEN_VRF1);
+               break;
+       case REGU_VRF2:
+               *en_dis = BITFEXT(reg_val, MC13783_REGGEN_VRF2);
+               break;
+       case REGU_VMMC1:
+               *en_dis = BITFEXT(reg_val, MC13783_REGGEN_VMMC1);
+               break;
+       case REGU_VMMC2:
+               *en_dis = BITFEXT(reg_val, MC13783_REGGEN_VMMC2);
+               break;
+       case REGU_GPO1:
+               *en_dis = BITFEXT(reg_val, MC13783_REGGEN_GPO1);
+               break;
+       case REGU_GPO2:
+               *en_dis = BITFEXT(reg_val, MC13783_REGGEN_GPO2);
+               break;
+       case REGU_GPO3:
+               *en_dis = BITFEXT(reg_val, MC13783_REGGEN_GPO3);
+               break;
+       case REGU_GPO4:
+               *en_dis = BITFEXT(reg_val, MC13783_REGGEN_GPO4);
+               break;
+       default:
+               break;
+       }
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets the Regen polarity.
+ *
+ * @param        en_dis         If true regen is inverted.
+ *
+ * @return       This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_set_regen_inv(bool en_dis)
+{
+       unsigned int reg_val = 0, reg_mask = 0;
+
+       reg_val = BITFVAL(MC13783_REGGEN_INV, en_dis);
+       reg_mask = BITFMASK(MC13783_REGGEN_INV);
+
+       CHECK_ERROR(pmic_write_reg(REG_REGEN_ASSIGNMENT, reg_val, reg_mask));
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function gets the Regen polarity.
+ *
+ * @param        en_dis         If true regen is inverted.
+ *
+ * @return       This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_get_regen_inv(bool *en_dis)
+{
+       unsigned int reg_val = 0, reg_mask = 0;
+
+       reg_mask = BITFMASK(MC13783_REGGEN_INV);
+       CHECK_ERROR(pmic_read_reg(REG_REGEN_ASSIGNMENT, &reg_val, reg_mask));
+       *en_dis = BITFEXT(reg_val, MC13783_REGGEN_INV);
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function enables esim control voltage.
+ * Only on mc13783 2.0 or higher
+ *
+ * @param        vesim          if true, enable VESIMESIMEN
+ * @param        vmmc1          if true, enable VMMC1ESIMEN
+ * @param        vmmc2          if true, enable VMMC2ESIMEN
+ *
+ * @return       This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_esim_v_en(bool vesim, bool vmmc1, bool vmmc2)
+{
+       unsigned int reg_val = 0, reg_mask = 0;
+       pmic_version_t mc13783_ver;
+       mc13783_ver = pmic_get_version();
+       if (mc13783_ver.revision >= 20) {
+               reg_val = BITFVAL(MC13783_REGGEN_VESIMESIM, vesim) |
+                   BITFVAL(MC13783_REGGEN_VMMC1ESIM, vesim) |
+                   BITFVAL(MC13783_REGGEN_VMMC2ESIM, vesim);
+               reg_mask = BITFMASK(MC13783_REGGEN_VESIMESIM) |
+                   BITFMASK(MC13783_REGGEN_VMMC1ESIM) |
+                   BITFMASK(MC13783_REGGEN_VMMC2ESIM);
+               CHECK_ERROR(pmic_write_reg(REG_REGEN_ASSIGNMENT,
+                                          reg_val, reg_mask));
+               return PMIC_SUCCESS;
+       } else {
+               return PMIC_NOT_SUPPORTED;
+       }
+}
+
+/*!
+ * This function gets esim control voltage values.
+ * Only on mc13783 2.0 or higher
+ *
+ * @param        vesim          if true, enable VESIMESIMEN
+ * @param        vmmc1          if true, enable VMMC1ESIMEN
+ * @param        vmmc2          if true, enable VMMC2ESIMEN
+ *
+ * @return       This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_gets_esim_v_state(bool *vesim, bool *vmmc1,
+                                        bool *vmmc2)
+{
+       unsigned int reg_val = 0, reg_mask = 0;
+       pmic_version_t mc13783_ver;
+       mc13783_ver = pmic_get_version();
+       if (mc13783_ver.revision >= 20) {
+               reg_mask = BITFMASK(MC13783_REGGEN_VESIMESIM) |
+                   BITFMASK(MC13783_REGGEN_VMMC1ESIM) |
+                   BITFMASK(MC13783_REGGEN_VMMC2ESIM);
+               CHECK_ERROR(pmic_read_reg(REG_REGEN_ASSIGNMENT,
+                                         &reg_val, reg_mask));
+               *vesim = BITFEXT(reg_val, MC13783_REGGEN_VESIMESIM);
+               *vmmc1 = BITFEXT(reg_val, MC13783_REGGEN_VMMC1ESIM);
+               *vmmc2 = BITFEXT(reg_val, MC13783_REGGEN_VMMC2ESIM);
+               return PMIC_SUCCESS;
+       } else {
+               return PMIC_NOT_SUPPORTED;
+       }
+}
+
+/*!
+ * This function enables auto reset after a system reset.
+ *
+ * @param        en         if true, the auto reset is enabled
+ *
+ * @return       This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_set_auto_reset_en(bool en)
+{
+       unsigned int reg_val = 0, reg_mask = 0;
+
+       reg_val = BITFVAL(MC13783_AUTO_RESTART, en);
+       reg_mask = BITFMASK(MC13783_AUTO_RESTART);
+
+       CHECK_ERROR(pmic_write_reg(REG_POWER_CONTROL_2, reg_val, reg_mask));
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function gets auto reset configuration.
+ *
+ * @param        en         if true, the auto reset is enabled
+ *
+ * @return       This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_get_auto_reset_en(bool *en)
+{
+       unsigned int reg_val = 0, reg_mask = 0;
+
+       reg_mask = BITFMASK(MC13783_AUTO_RESTART);
+       CHECK_ERROR(pmic_read_reg(REG_POWER_CONTROL_2, &reg_val, reg_mask));
+       *en = BITFEXT(reg_val, MC13783_AUTO_RESTART);
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function configures a system reset on a button.
+ *
+ * @param       bt         type of button.
+ * @param       sys_rst    if true, enable the system reset on this button
+ * @param       deb_time   sets the debounce time on this button pin
+ *
+ * @return       This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_set_conf_button(t_button bt, bool sys_rst, int deb_time)
+{
+       int max_val = 0;
+       unsigned int reg_val = 0, reg_mask = 0;
+
+       max_val = (1 << MC13783_DEB_BT_ON1B_WID) - 1;
+       if (deb_time > max_val) {
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       switch (bt) {
+       case BT_ON1B:
+               reg_val = BITFVAL(MC13783_EN_BT_ON1B, sys_rst) |
+                   BITFVAL(MC13783_DEB_BT_ON1B, deb_time);
+               reg_mask = BITFMASK(MC13783_EN_BT_ON1B) |
+                   BITFMASK(MC13783_DEB_BT_ON1B);
+               break;
+       case BT_ON2B:
+               reg_val = BITFVAL(MC13783_EN_BT_ON2B, sys_rst) |
+                   BITFVAL(MC13783_DEB_BT_ON2B, deb_time);
+               reg_mask = BITFMASK(MC13783_EN_BT_ON2B) |
+                   BITFMASK(MC13783_DEB_BT_ON2B);
+               break;
+       case BT_ON3B:
+               reg_val = BITFVAL(MC13783_EN_BT_ON3B, sys_rst) |
+                   BITFVAL(MC13783_DEB_BT_ON3B, deb_time);
+               reg_mask = BITFMASK(MC13783_EN_BT_ON3B) |
+                   BITFMASK(MC13783_DEB_BT_ON3B);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_write_reg(REG_POWER_CONTROL_2, reg_val, reg_mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function gets configuration of a button.
+ *
+ * @param       bt         type of button.
+ * @param       sys_rst    if true, the system reset is enabled on this button
+ * @param       deb_time   gets the debounce time on this button pin
+ *
+ * @return       This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_get_conf_button(t_button bt,
+                                      bool *sys_rst, int *deb_time)
+{
+       unsigned int reg_val = 0, reg_mask = 0;
+
+       switch (bt) {
+       case BT_ON1B:
+               reg_mask = BITFMASK(MC13783_EN_BT_ON1B) |
+                   BITFMASK(MC13783_DEB_BT_ON1B);
+               break;
+       case BT_ON2B:
+               reg_mask = BITFMASK(MC13783_EN_BT_ON2B) |
+                   BITFMASK(MC13783_DEB_BT_ON2B);
+               break;
+       case BT_ON3B:
+               reg_mask = BITFMASK(MC13783_EN_BT_ON3B) |
+                   BITFMASK(MC13783_DEB_BT_ON3B);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_read_reg(REG_POWER_CONTROL_2, &reg_val, reg_mask));
+
+       switch (bt) {
+       case BT_ON1B:
+               *sys_rst = BITFEXT(reg_val, MC13783_EN_BT_ON1B);
+               *deb_time = BITFEXT(reg_val, MC13783_DEB_BT_ON1B);
+               break;
+       case BT_ON2B:
+               *sys_rst = BITFEXT(reg_val, MC13783_EN_BT_ON2B);
+               *deb_time = BITFEXT(reg_val, MC13783_DEB_BT_ON2B);
+               break;
+       case BT_ON3B:
+               *sys_rst = BITFEXT(reg_val, MC13783_EN_BT_ON3B);
+               *deb_time = BITFEXT(reg_val, MC13783_DEB_BT_ON3B);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function is used to un/subscribe on power event IT.
+ *
+ * @param        event          type of event.
+ * @param        callback       event callback function.
+ * @param        sub            define if Un/subscribe event.
+ *
+ * @return       This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_event(t_pwr_int event, void *callback, bool sub)
+{
+       pmic_event_callback_t power_callback;
+       type_event power_event;
+
+       power_callback.func = callback;
+       power_callback.param = NULL;
+       switch (event) {
+       case PWR_IT_BPONI:
+               power_event = EVENT_BPONI;
+               break;
+       case PWR_IT_LOBATLI:
+               power_event = EVENT_LOBATLI;
+               break;
+       case PWR_IT_LOBATHI:
+               power_event = EVENT_LOBATHI;
+               break;
+       case PWR_IT_ONOFD1I:
+               power_event = EVENT_ONOFD1I;
+               break;
+       case PWR_IT_ONOFD2I:
+               power_event = EVENT_ONOFD2I;
+               break;
+       case PWR_IT_ONOFD3I:
+               power_event = EVENT_ONOFD3I;
+               break;
+       case PWR_IT_SYSRSTI:
+               power_event = EVENT_SYSRSTI;
+               break;
+       case PWR_IT_PWRRDYI:
+               power_event = EVENT_PWRRDYI;
+               break;
+       case PWR_IT_PCI:
+               power_event = EVENT_PCI;
+               break;
+       case PWR_IT_WARMI:
+               power_event = EVENT_WARMI;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+       if (sub == true) {
+               CHECK_ERROR(pmic_event_subscribe(power_event, power_callback));
+       } else {
+               CHECK_ERROR(pmic_event_unsubscribe
+                           (power_event, power_callback));
+       }
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function is used to subscribe on power event IT.
+ *
+ * @param        event          type of event.
+ * @param        callback       event callback function.
+ *
+ * @return       This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_event_sub(t_pwr_int event, void *callback)
+{
+       return pmic_power_event(event, callback, true);
+}
+
+/*!
+ * This function is used to un subscribe on power event IT.
+ *
+ * @param        event          type of event.
+ * @param        callback       event callback function.
+ *
+ * @return       This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_event_unsub(t_pwr_int event, void *callback)
+{
+       return pmic_power_event(event, callback, false);
+}
+
+void pmic_power_key_callback(void)
+{
+#ifdef CONFIG_MXC_HWEVENT
+       /*read the power key is pressed or up */
+       t_sensor_bits sense;
+       struct mxc_hw_event event = { HWE_POWER_KEY, 0 };
+
+       pmic_get_sensors(&sense);
+       if (sense.sense_onofd1s) {
+               pr_debug("PMIC Power key up\n");
+               event.args = PWRK_UNPRESS;
+       } else {
+               pr_debug("PMIC Power key pressed\n");
+               event.args = PWRK_PRESS;
+       }
+       /* send hw event */
+       hw_event_send(HWE_DEF_PRIORITY, &event);
+#endif
+}
+
+static irqreturn_t power_key_int(int irq, void *dev_id)
+{
+       pr_info(KERN_INFO "on-off key pressed\n");
+
+       return 0;
+}
+
+extern void gpio_power_key_active(void);
+
+/*
+ * Init and Exit
+ */
+
+static int pmic_power_probe(struct platform_device *pdev)
+{
+       int irq, ret;
+       struct pmic_platform_data *ppd;
+
+       /* configure on/off button */
+       gpio_power_key_active();
+
+       ppd = pdev->dev.platform_data;
+       if (ppd)
+               irq = ppd->power_key_irq;
+       else
+               goto done;
+
+       if (irq == 0) {
+               pr_info(KERN_INFO "PMIC Power has no platform data\n");
+               goto done;
+       }
+       set_irq_type(irq, IRQF_TRIGGER_RISING);
+
+       ret = request_irq(irq, power_key_int, 0, "power_key", 0);
+       if (ret)
+               pr_info(KERN_ERR "register on-off key interrupt failed\n");
+
+       set_irq_wake(irq, 1);
+
+      done:
+       pr_info(KERN_INFO "PMIC Power successfully probed\n");
+       return 0;
+}
+
+static struct platform_driver pmic_power_driver_ldm = {
+       .driver = {
+                  .name = "pmic_power",
+                  },
+       .suspend = pmic_power_suspend,
+       .resume = pmic_power_resume,
+       .probe = pmic_power_probe,
+       .remove = NULL,
+};
+
+static int __init pmic_power_init(void)
+{
+       pr_debug("PMIC Power driver loading..\n");
+       pmic_power_event_sub(PWR_IT_ONOFD1I, pmic_power_key_callback);
+       /* set power off hook to mc13783 power off */
+       pm_power_off = pmic_power_off;
+       return platform_driver_register(&pmic_power_driver_ldm);
+}
+static void __exit pmic_power_exit(void)
+{
+       pmic_power_event_unsub(PWR_IT_ONOFD1I, pmic_power_key_callback);
+       platform_driver_unregister(&pmic_power_driver_ldm);
+       pr_debug("PMIC Power driver successfully unloaded\n");
+}
+
+/*
+ * Module entry points
+ */
+
+subsys_initcall_sync(pmic_power_init);
+module_exit(pmic_power_exit);
+
+MODULE_DESCRIPTION("pmic_power driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pmic/mc13783/pmic_power_defs.h b/drivers/mxc/pmic/mc13783/pmic_power_defs.h
new file mode 100644 (file)
index 0000000..bdca675
--- /dev/null
@@ -0,0 +1,509 @@
+/*
+ * 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 mc13783/pmic_power_defs.h
+ * @brief This is the internal header define of PMIC(mc13783) Power driver.
+ *
+ * @ingroup PMIC_POWER
+ */
+
+/*
+ * Includes
+ */
+
+#ifndef __MC13783_POWER_DEFS_H__
+#define __MC13783_POWER_DEFS_H__
+
+/*
+ * Power Up Mode Sense bits
+ */
+
+#define         STATE_ICTEST_MASK       0x000001
+
+#define         STATE_CLKSEL_BIT        1
+#define         STATE_CLKSEL_MASK       0x000002
+
+#define         STATE_PUMS1_BITS        2
+#define         STATE_PUMS1_MASK        0x00000C
+
+#define         STATE_PUMS2_BITS        4
+#define         STATE_PUMS2_MASK        0x000030
+
+#define         STATE_PUMS3_BITS        6
+#define         STATE_PUMS3_MASK        0x0000C0
+
+#define         STATE_CHRGM1_BITS       8
+#define         STATE_CHRGM1_MASK       0x000300
+
+#define         STATE_CHRGM2_BITS       10
+#define         STATE_CHRGM2_MASK       0x000C00
+
+#define         STATE_UMOD_BITS         12
+#define         STATE_UMOD_MASK         0x003000
+
+#define         STATE_USBEN_BIT         14
+#define         STATE_USBEN_MASK        0x004000
+
+#define         STATE_SW1A_J_B_BIT      15
+#define         STATE_SW1A_J_B_MASK     0x008000
+
+#define         STATE_SW2A_J_B_BIT      16
+#define         STATE_SW2A_J_B_MASK     0x010000
+
+#define         PC_COUNT_MAX            3
+#define         PC_COUNT_MIN            0
+/*
+ * Reg Regen
+ */
+#define MC13783_REGGEN_VAUDIO_LSH                      0
+#define MC13783_REGGEN_VAUDIO_WID                      1
+#define MC13783_REGGEN_VIOHI_LSH                       1
+#define MC13783_REGGEN_VIOHI_WID                       1
+#define MC13783_REGGEN_VIOLO_LSH                       2
+#define MC13783_REGGEN_VIOLO_WID                       1
+#define MC13783_REGGEN_VDIG_LSH                        3
+#define MC13783_REGGEN_VDIG_WID                        1
+#define MC13783_REGGEN_VGEN_LSH                        4
+#define MC13783_REGGEN_VGEN_WID                        1
+#define MC13783_REGGEN_VRFDIG_LSH                      5
+#define MC13783_REGGEN_VRFDIG_WID                      1
+#define MC13783_REGGEN_VRFREF_LSH                      6
+#define MC13783_REGGEN_VRFREF_WID                      1
+#define MC13783_REGGEN_VRFCP_LSH                       7
+#define MC13783_REGGEN_VRFCP_WID                       1
+#define MC13783_REGGEN_VCAM_LSH                        8
+#define MC13783_REGGEN_VCAM_WID                        1
+#define MC13783_REGGEN_VRFBG_LSH                       9
+#define MC13783_REGGEN_VRFBG_WID                       1
+#define MC13783_REGGEN_VRF1_LSH                        10
+#define MC13783_REGGEN_VRF1_WID                        1
+#define MC13783_REGGEN_VRF2_LSH                        11
+#define MC13783_REGGEN_VRF2_WID                        1
+#define MC13783_REGGEN_VMMC1_LSH                       12
+#define MC13783_REGGEN_VMMC1_WID                       1
+#define MC13783_REGGEN_VMMC2_LSH                       13
+#define MC13783_REGGEN_VMMC2_WID                       1
+#define MC13783_REGGEN_GPO1_LSH                        16
+#define MC13783_REGGEN_GPO1_WID                        1
+#define MC13783_REGGEN_GPO2_LSH                        17
+#define MC13783_REGGEN_GPO2_WID                        1
+#define MC13783_REGGEN_GPO3_LSH                        18
+#define MC13783_REGGEN_GPO3_WID                        1
+#define MC13783_REGGEN_GPO4_LSH                        19
+#define MC13783_REGGEN_GPO4_WID                        1
+#define MC13783_REGGEN_INV_LSH                 20
+#define MC13783_REGGEN_INV_WID                 1
+#define MC13783_REGGEN_VESIMESIM_LSH           21
+#define MC13783_REGGEN_VESIMESIM_WID           1
+#define MC13783_REGGEN_VMMC1ESIM_LSH           22
+#define MC13783_REGGEN_VMMC1ESIM_WID           1
+#define MC13783_REGGEN_VMMC2ESIM_LSH           23
+#define MC13783_REGGEN_VMMC2ESIM_WID           1
+
+/*
+ * Reg Power Control 0
+ */
+#define MC13783_PWRCTRL_PCEN_LSH                       0
+#define MC13783_PWRCTRL_PCEN_WID                       1
+#define MC13783_PWRCTRL_PCEN_ENABLE            1
+#define MC13783_PWRCTRL_PCEN_DISABLE           0
+#define MC13783_PWRCTRL_PC_COUNT_EN_LSH                1
+#define MC13783_PWRCTRL_PC_COUNT_EN_WID                1
+#define MC13783_PWRCTRL_PC_COUNT_EN_ENABLE     1
+#define MC13783_PWRCTRL_PC_COUNT_EN_DISABLE    0
+#define MC13783_PWRCTRL_WARM_EN_LSH            2
+#define MC13783_PWRCTRL_WARM_EN_WID            1
+#define MC13783_PWRCTRL_WARM_EN_ENABLE         1
+#define MC13783_PWRCTRL_WARM_EN_DISABLE                0
+#define MC13783_PWRCTRL_USER_OFF_SPI_LSH               3
+#define MC13783_PWRCTRL_USER_OFF_SPI_WID               1
+#define MC13783_PWRCTRL_USER_OFF_SPI_ENABLE    1
+#define MC13783_PWRCTRL_USER_OFF_PC_LSH                4
+#define MC13783_PWRCTRL_USER_OFF_PC_WID                1
+#define MC13783_PWRCTRL_USER_OFF_PC_ENABLE     1
+#define MC13783_PWRCTRL_USER_OFF_PC_DISABLE    0
+#define MC13783_PWRCTRL_32OUT_USER_OFF_LSH     5
+#define MC13783_PWRCTRL_32OUT_USER_OFF_WID     1
+#define MC13783_PWRCTRL_32OUT_USER_OFF_ENABLE  1
+#define MC13783_PWRCTRL_32OUT_USER_OFF_DISABLE 0
+#define MC13783_PWRCTRL_32OUT_EN_LSH           6
+#define MC13783_PWRCTRL_32OUT_EN_WID           1
+#define MC13783_PWRCTRL_32OUT_EN_ENABLE                1
+#define MC13783_PWRCTRL_32OUT_EN_DISABLE               0
+#define MC13783_REGCTRL_VBKUP2AUTOMH_LSH               7
+#define MC13783_REGCTRL_VBKUP2AUTOMH_WID               1
+#define MC13783_PWRCTRL_VBKUP1_EN_LSH          8
+#define MC13783_PWRCTRL_VBKUP1_EN_WID          1
+#define MC13783_PWRCTRL_VBKUP_ENABLE           1
+#define MC13783_PWRCTRL_VBKUP_DISABLE          0
+#define MC13783_PWRCTRL_VBKUP1_AUTO_EN_LSH     9
+#define MC13783_PWRCTRL_VBKUP1_AUTO_EN_WID     1
+#define MC13783_PWRCTRL_VBKUP1_LSH             10
+#define MC13783_PWRCTRL_VBKUP1_WID             2
+#define MC13783_PWRCTRL_VBKUP2_EN_LSH          12
+#define MC13783_PWRCTRL_VBKUP2_EN_WID          1
+#define MC13783_PWRCTRL_VBKUP2_AUTO_EN_LSH     13
+#define MC13783_PWRCTRL_VBKUP2_AUTO_EN_WID     1
+#define MC13783_PWRCTRL_VBKUP2_LSH             14
+#define MC13783_PWRCTRL_VBKUP2_WID             2
+#define MC13783_REGCTRL_BATTDETEN_LSH          19
+#define MC13783_REGCTRL_BATTDETEN_WID          1
+
+/*
+ * Reg Power Control 1
+ */
+#define MC13783_PWRCTRL_PCT_LSH                0
+#define MC13783_PWRCTRL_PCT_WID                8
+#define MC13783_PWRCTRL_PC_COUNT_LSH   8
+#define MC13783_PWRCTRL_PC_COUNT_WID   4
+#define MC13783_PWRCTRL_PC_MAX_CNT_LSH 12
+#define MC13783_PWRCTRL_PC_MAX_CNT_WID 4
+#define MC13783_PWRCTRL_MEM_TMR_LSH    16
+#define MC13783_PWRCTRL_MEM_TMR_WID    4
+#define MC13783_PWRCTRL_MEM_ALLON_LSH  20
+#define MC13783_PWRCTRL_MEM_ALLON_WID  1
+#define MC13783_PWRCTRL_MEM_ALLON_ENABLE       1
+#define MC13783_PWRCTRL_MEM_ALLON_DISABLE      0
+
+/*
+ * Reg Power Control 2
+ */
+#define MC13783_AUTO_RESTART_LSH       0
+#define MC13783_AUTO_RESTART_WID       1
+#define MC13783_EN_BT_ON1B_LSH         1
+#define MC13783_EN_BT_ON1B_WID         1
+#define MC13783_EN_BT_ON2B_LSH         2
+#define MC13783_EN_BT_ON2B_WID         1
+#define MC13783_EN_BT_ON3B_LSH         3
+#define MC13783_EN_BT_ON3B_WID         1
+#define MC13783_DEB_BT_ON1B_LSH                4
+#define MC13783_DEB_BT_ON1B_WID                2
+#define MC13783_DEB_BT_ON2B_LSH                6
+#define MC13783_DEB_BT_ON2B_WID                2
+#define MC13783_DEB_BT_ON3B_LSH                8
+#define MC13783_DEB_BT_ON3B_WID                2
+
+/*
+ * Reg Regulator Mode 0
+ */
+#define MC13783_REGCTRL_VAUDIO_EN_LSH  0
+#define MC13783_REGCTRL_VAUDIO_EN_WID  1
+#define MC13783_REGCTRL_VAUDIO_EN_ENABLE       1
+#define MC13783_REGCTRL_VAUDIO_EN_DISABLE      0
+#define MC13783_REGCTRL_VAUDIO_STBY_LSH        1
+#define MC13783_REGCTRL_VAUDIO_STBY_WID        1
+#define MC13783_REGCTRL_VAUDIO_MODE_LSH        2
+#define MC13783_REGCTRL_VAUDIO_MODE_WID        1
+#define MC13783_REGCTRL_VIOHI_EN_LSH   3
+#define MC13783_REGCTRL_VIOHI_EN_WID   1
+#define MC13783_REGCTRL_VIOHI_EN_ENABLE        1
+#define MC13783_REGCTRL_VIOHI_EN_DISABLE       0
+#define MC13783_REGCTRL_VIOHI_STBY_LSH 4
+#define MC13783_REGCTRL_VIOHI_STBY_WID 1
+#define MC13783_REGCTRL_VIOHI_MODE_LSH 5
+#define MC13783_REGCTRL_VIOHI_MODE_WID 1
+#define MC13783_REGCTRL_VIOLO_EN_LSH   6
+#define MC13783_REGCTRL_VIOLO_EN_WID   1
+#define MC13783_REGCTRL_VIOLO_EN_ENABLE        1
+#define MC13783_REGCTRL_VIOLO_EN_DISABLE       0
+#define MC13783_REGCTRL_VIOLO_STBY_LSH 7
+#define MC13783_REGCTRL_VIOLO_STBY_WID 1
+#define MC13783_REGCTRL_VIOLO_MODE_LSH 8
+#define MC13783_REGCTRL_VIOLO_MODE_WID 1
+#define MC13783_REGCTRL_VDIG_EN_LSH    9
+#define MC13783_REGCTRL_VDIG_EN_WID    1
+#define MC13783_REGCTRL_VDIG_EN_ENABLE 1
+#define MC13783_REGCTRL_VDIG_EN_DISABLE        0
+#define MC13783_REGCTRL_VDIG_STBY_LSH  10
+#define MC13783_REGCTRL_VDIG_STBY_WID  1
+#define MC13783_REGCTRL_VDIG_MODE_LSH  11
+#define MC13783_REGCTRL_VDIG_MODE_WID  1
+#define MC13783_REGCTRL_VGEN_EN_LSH    12
+#define MC13783_REGCTRL_VGEN_EN_WID    1
+#define MC13783_REGCTRL_VGEN_EN_ENABLE 1
+#define MC13783_REGCTRL_VGEN_EN_DISABLE        0
+#define MC13783_REGCTRL_VGEN_STBY_LSH  13
+#define MC13783_REGCTRL_VGEN_STBY_WID  1
+#define MC13783_REGCTRL_VGEN_MODE_LSH  14
+#define MC13783_REGCTRL_VGEN_MODE_WID  1
+#define MC13783_REGCTRL_VRFDIG_EN_LSH  15
+#define MC13783_REGCTRL_VRFDIG_EN_WID  1
+#define MC13783_REGCTRL_VRFDIG_EN_ENABLE       1
+#define MC13783_REGCTRL_VRFDIG_EN_DISABLE      0
+#define MC13783_REGCTRL_VRFDIG_STBY_LSH        16
+#define MC13783_REGCTRL_VRFDIG_STBY_WID        1
+#define MC13783_REGCTRL_VRFDIG_MODE_LSH        17
+#define MC13783_REGCTRL_VRFDIG_MODE_WID        1
+#define MC13783_REGCTRL_VRFREF_EN_LSH  18
+#define MC13783_REGCTRL_VRFREF_EN_WID  1
+#define MC13783_REGCTRL_VRFREF_EN_ENABLE       1
+#define MC13783_REGCTRL_VRFREF_EN_DISABLE      0
+#define MC13783_REGCTRL_VRFREF_STBY_LSH        19
+#define MC13783_REGCTRL_VRFREF_STBY_WID        1
+#define MC13783_REGCTRL_VRFREF_MODE_LSH        20
+#define MC13783_REGCTRL_VRFREF_MODE_WID        1
+#define MC13783_REGCTRL_VRFCP_EN_LSH   21
+#define MC13783_REGCTRL_VRFCP_EN_WID   1
+#define MC13783_REGCTRL_VRFCP_EN_ENABLE        1
+#define MC13783_REGCTRL_VRFCP_EN_DISABLE       0
+#define MC13783_REGCTRL_VRFCP_STBY_LSH 22
+#define MC13783_REGCTRL_VRFCP_STBY_WID 1
+#define MC13783_REGCTRL_VRFCP_MODE_LSH 23
+#define MC13783_REGCTRL_VRFCP_MODE_WID 1
+
+/*
+ * Reg Regulator Mode 1
+ */
+#define MC13783_REGCTRL_VSIM_EN_LSH    0
+#define MC13783_REGCTRL_VSIM_EN_WID    1
+#define MC13783_REGCTRL_VSIM_EN_ENABLE 1
+#define MC13783_REGCTRL_VSIM_EN_DISABLE        0
+#define MC13783_REGCTRL_VSIM_STBY_LSH  1
+#define MC13783_REGCTRL_VSIM_STBY_WID  1
+#define MC13783_REGCTRL_VSIM_MODE_LSH  2
+#define MC13783_REGCTRL_VSIM_MODE_WID  1
+#define MC13783_REGCTRL_VESIM_EN_LSH   3
+#define MC13783_REGCTRL_VESIM_EN_WID   1
+#define MC13783_REGCTRL_VESIM_EN_ENABLE        1
+#define MC13783_REGCTRL_VESIM_EN_DISABLE       0
+#define MC13783_REGCTRL_VESIM_STBY_LSH 4
+#define MC13783_REGCTRL_VESIM_STBY_WID 1
+#define MC13783_REGCTRL_VESIM_MODE_LSH 5
+#define MC13783_REGCTRL_VESIM_MODE_WID 1
+#define MC13783_REGCTRL_VCAM_EN_LSH    6
+#define MC13783_REGCTRL_VCAM_EN_WID    1
+#define MC13783_REGCTRL_VCAM_EN_ENABLE 1
+#define MC13783_REGCTRL_VCAM_EN_DISABLE        0
+#define MC13783_REGCTRL_VCAM_STBY_LSH  7
+#define MC13783_REGCTRL_VCAM_STBY_WID  1
+#define MC13783_REGCTRL_VCAM_MODE_LSH  8
+#define MC13783_REGCTRL_VCAM_MODE_WID  1
+#define        MC13783_REGCTRL_VRFBG_EN_LSH    9
+#define        MC13783_REGCTRL_VRFBG_EN_WID    1
+#define MC13783_REGCTRL_VRFBG_EN_ENABLE        1
+#define MC13783_REGCTRL_VRFBG_EN_DISABLE       0
+#define MC13783_REGCTRL_VRFBG_STBY_LSH 10
+#define MC13783_REGCTRL_VRFBG_STBY_WID 1
+#define MC13783_REGCTRL_VVIB_EN_LSH    11
+#define MC13783_REGCTRL_VVIB_EN_WID    1
+#define MC13783_REGCTRL_VVIB_EN_ENABLE 1
+#define MC13783_REGCTRL_VVIB_EN_DISABLE        0
+#define MC13783_REGCTRL_VRF1_EN_LSH    12
+#define MC13783_REGCTRL_VRF1_EN_WID    1
+#define MC13783_REGCTRL_VRF1_EN_ENABLE 1
+#define MC13783_REGCTRL_VRF1_EN_DISABLE        0
+#define MC13783_REGCTRL_VRF1_STBY_LSH  13
+#define MC13783_REGCTRL_VRF1_STBY_WID  1
+#define MC13783_REGCTRL_VRF1_MODE_LSH  14
+#define MC13783_REGCTRL_VRF1_MODE_WID  1
+#define MC13783_REGCTRL_VRF2_EN_LSH    15
+#define MC13783_REGCTRL_VRF2_EN_WID    1
+#define MC13783_REGCTRL_VRF2_EN_ENABLE 1
+#define MC13783_REGCTRL_VRF2_EN_DISABLE        0
+#define MC13783_REGCTRL_VRF2_STBY_LSH  16
+#define MC13783_REGCTRL_VRF2_STBY_WID  1
+#define MC13783_REGCTRL_VRF2_MODE_LSH  17
+#define MC13783_REGCTRL_VRF2_MODE_WID  1
+#define MC13783_REGCTRL_VMMC1_EN_LSH   18
+#define MC13783_REGCTRL_VMMC1_EN_WID   1
+#define MC13783_REGCTRL_VMMC1_EN_ENABLE        1
+#define MC13783_REGCTRL_VMMC1_EN_DISABLE       0
+#define MC13783_REGCTRL_VMMC1_STBY_LSH 19
+#define MC13783_REGCTRL_VMMC1_STBY_WID 1
+#define MC13783_REGCTRL_VMMC1_MODE_LSH 20
+#define MC13783_REGCTRL_VMMC1_MODE_WID 1
+#define MC13783_REGCTRL_VMMC2_EN_LSH   21
+#define MC13783_REGCTRL_VMMC2_EN_WID   1
+#define MC13783_REGCTRL_VMMC2_EN_ENABLE        1
+#define MC13783_REGCTRL_VMMC2_EN_DISABLE       0
+#define MC13783_REGCTRL_VMMC2_STBY_LSH 22
+#define MC13783_REGCTRL_VMMC2_STBY_WID 1
+#define MC13783_REGCTRL_VMMC2_MODE_LSH 23
+#define MC13783_REGCTRL_VMMC2_MODE_WID 1
+
+/*
+ * Reg Regulator Misc.
+ */
+#define MC13783_REGCTRL_GPO1_EN_LSH    6
+#define MC13783_REGCTRL_GPO1_EN_WID    1
+#define MC13783_REGCTRL_GPO1_EN_ENABLE 1
+#define MC13783_REGCTRL_GPO1_EN_DISABLE        0
+#define MC13783_REGCTRL_GPO2_EN_LSH    8
+#define MC13783_REGCTRL_GPO2_EN_WID    1
+#define MC13783_REGCTRL_GPO2_EN_ENABLE 1
+#define MC13783_REGCTRL_GPO2_EN_DISABLE        0
+#define MC13783_REGCTRL_GPO3_EN_LSH    10
+#define MC13783_REGCTRL_GPO3_EN_WID    1
+#define MC13783_REGCTRL_GPO3_EN_ENABLE 1
+#define MC13783_REGCTRL_GPO3_EN_DISABLE        0
+#define MC13783_REGCTRL_GPO4_EN_LSH    12
+#define MC13783_REGCTRL_GPO4_EN_WID    1
+#define MC13783_REGCTRL_GPO4_EN_ENABLE 1
+#define MC13783_REGCTRL_GPO4_EN_DISABLE        0
+#define MC13783_REGCTRL_VIBPINCTRL_LSH 14
+#define MC13783_REGCTRL_VIBPINCTRL_WID 1
+
+/*
+ * Reg Regulator Setting 0
+ */
+#define MC13783_REGSET_VIOLO_LSH               2
+#define MC13783_REGSET_VIOLO_WID               2
+#define MC13783_REGSET_VDIG_LSH                4
+#define MC13783_REGSET_VDIG_WID                2
+#define MC13783_REGSET_VGEN_LSH                6
+#define MC13783_REGSET_VGEN_WID                3
+#define MC13783_REGSET_VRFDIG_LSH              9
+#define MC13783_REGSET_VRFDIG_WID              2
+#define MC13783_REGSET_VRFREF_LSH              11
+#define MC13783_REGSET_VRFREF_WID              2
+#define MC13783_REGSET_VRFCP_LSH               13
+#define MC13783_REGSET_VRFCP_WID               1
+#define MC13783_REGSET_VSIM_LSH                14
+#define MC13783_REGSET_VSIM_WID                1
+#define MC13783_REGSET_VESIM_LSH               15
+#define MC13783_REGSET_VESIM_WID               1
+#define MC13783_REGSET_VCAM_LSH                16
+#define MC13783_REGSET_VCAM_WID                3
+
+/*
+ * Reg Regulator Setting 1
+ */
+#define MC13783_REGSET_VVIB_LSH                0
+#define MC13783_REGSET_VVIB_WID                2
+#define MC13783_REGSET_VRF1_LSH                2
+#define MC13783_REGSET_VRF1_WID                2
+#define MC13783_REGSET_VRF2_LSH                4
+#define MC13783_REGSET_VRF2_WID                2
+#define MC13783_REGSET_VMMC1_LSH               6
+#define MC13783_REGSET_VMMC1_WID               3
+#define MC13783_REGSET_VMMC2_LSH               9
+#define MC13783_REGSET_VMMC2_WID               3
+
+/*
+ * Reg Switcher 0
+ */
+#define MC13783_SWSET_SW1A_LSH         0
+#define MC13783_SWSET_SW1A_WID         6
+#define MC13783_SWSET_SW1A_DVS_LSH     6
+#define MC13783_SWSET_SW1A_DVS_WID     6
+#define MC13783_SWSET_SW1A_STDBY_LSH   12
+#define MC13783_SWSET_SW1A_STDBY_WID   6
+
+/*
+ * Reg Switcher 1
+ */
+#define MC13783_SWSET_SW1B_LSH         0
+#define MC13783_SWSET_SW1B_WID         6
+#define MC13783_SWSET_SW1B_DVS_LSH     6
+#define MC13783_SWSET_SW1B_DVS_WID     6
+#define MC13783_SWSET_SW1B_STDBY_LSH   12
+#define MC13783_SWSET_SW1B_STDBY_WID   6
+
+/*
+ * Reg Switcher 2
+ */
+#define MC13783_SWSET_SW2A_LSH         0
+#define MC13783_SWSET_SW2A_WID         6
+#define MC13783_SWSET_SW2A_DVS_LSH     6
+#define MC13783_SWSET_SW2A_DVS_WID     6
+#define MC13783_SWSET_SW2A_STDBY_LSH   12
+#define MC13783_SWSET_SW2A_STDBY_WID   6
+
+/*
+ * Reg Switcher 3
+ */
+#define MC13783_SWSET_SW2B_LSH         0
+#define MC13783_SWSET_SW2B_WID         6
+#define MC13783_SWSET_SW2B_DVS_LSH     6
+#define MC13783_SWSET_SW2B_DVS_WID     6
+#define MC13783_SWSET_SW2B_STDBY_LSH   12
+#define MC13783_SWSET_SW2B_STDBY_WID   6
+
+/*
+ * Reg Switcher 4
+ */
+#define MC13783_SWCTRL_SW1A_MODE_LSH           0
+#define MC13783_SWCTRL_SW1A_MODE_WID           2
+#define MC13783_SWCTRL_SW1A_STBY_MODE_LSH              2
+#define MC13783_SWCTRL_SW1A_STBY_MODE_WID              2
+#define MC13783_SWCTRL_SW1A_DVS_SPEED_LSH              6
+#define MC13783_SWCTRL_SW1A_DVS_SPEED_WID              2
+#define MC13783_SWCTRL_SW1A_PANIC_MODE_LSH     8
+#define MC13783_SWCTRL_SW1A_PANIC_MODE_WID     1
+#define MC13783_SWCTRL_SW1A_SOFTSTART_LSH              9
+#define MC13783_SWCTRL_SW1A_SOFTSTART_WID              1
+#define MC13783_SWCTRL_SW1B_MODE_LSH           10
+#define MC13783_SWCTRL_SW1B_MODE_WID           2
+#define MC13783_SWCTRL_SW1B_STBY_MODE_LSH              12
+#define MC13783_SWCTRL_SW1B_STBY_MODE_WID              2
+#define MC13783_SWCTRL_SW1B_DVS_SPEED_LSH              14
+#define MC13783_SWCTRL_SW1B_DVS_SPEED_WID              2
+#define MC13783_SWCTRL_SW1B_PANIC_MODE_LSH     16
+#define MC13783_SWCTRL_SW1B_PANIC_MODE_WID     1
+#define MC13783_SWCTRL_SW1B_SOFTSTART_LSH              17
+#define MC13783_SWCTRL_SW1B_SOFTSTART_WID              1
+#define MC13783_SWCTRL_PLL_EN_LSH                      18
+#define MC13783_SWCTRL_PLL_EN_WID                      1
+#define MC13783_SWCTRL_PLL_EN_ENABLE           1
+#define MC13783_SWCTRL_PLL_EN_DISABLE          0
+#define MC13783_SWCTRL_PLL_FACTOR_LSH          19
+#define MC13783_SWCTRL_PLL_FACTOR_WID          3
+
+/*
+ * Reg Switcher 5
+ */
+#define MC13783_SWCTRL_SW2A_MODE_LSH           0
+#define MC13783_SWCTRL_SW2A_MODE_WID           2
+#define MC13783_SWCTRL_SW2A_STBY_MODE_LSH              2
+#define MC13783_SWCTRL_SW2A_STBY_MODE_WID              2
+#define MC13783_SWCTRL_SW2A_DVS_SPEED_LSH              6
+#define MC13783_SWCTRL_SW2A_DVS_SPEED_WID              2
+#define MC13783_SWCTRL_SW2A_PANIC_MODE_LSH     8
+#define MC13783_SWCTRL_SW2A_PANIC_MODE_WID     1
+#define MC13783_SWCTRL_SW2A_SOFTSTART_LSH              9
+#define MC13783_SWCTRL_SW2A_SOFTSTART_WID              1
+#define MC13783_SWCTRL_SW2B_MODE_LSH           10
+#define MC13783_SWCTRL_SW2B_MODE_WID           2
+#define MC13783_SWCTRL_SW2B_STBY_MODE_LSH              12
+#define MC13783_SWCTRL_SW2B_STBY_MODE_WID              2
+#define MC13783_SWCTRL_SW2B_DVS_SPEED_LSH              14
+#define MC13783_SWCTRL_SW2B_DVS_SPEED_WID              2
+#define MC13783_SWCTRL_SW2B_PANIC_MODE_LSH     16
+#define MC13783_SWCTRL_SW2B_PANIC_MODE_WID     1
+#define MC13783_SWCTRL_SW2B_SOFTSTART_LSH              17
+#define MC13783_SWCTRL_SW2B_SOFTSTART_WID              1
+#define MC13783_SWSET_SW3_LSH                  18
+#define MC13783_SWSET_SW3_WID                  2
+#define MC13783_SWCTRL_SW3_EN_LSH                      20
+#define MC13783_SWCTRL_SW3_EN_WID                      2
+#define MC13783_SWCTRL_SW3_EN_ENABLE           1
+#define MC13783_SWCTRL_SW3_EN_DISABLE          0
+#define MC13783_SWCTRL_SW3_STBY_LSH            21
+#define MC13783_SWCTRL_SW3_STBY_WID            1
+#define MC13783_SWCTRL_SW3_MODE_LSH            22
+#define MC13783_SWCTRL_SW3_MODE_WID            1
+
+/*
+ * Switcher configuration
+ */
+#define MC13783_SWCTRL_SW_MODE_SYNC_RECT_EN    0
+#define MC13783_SWCTRL_SW_MODE_PULSE_NO_SKIP_EN        1
+#define MC13783_SWCTRL_SW_MODE_PULSE_SKIP_EN   2
+#define MC13783_SWCTRL_SW_MODE_LOW_POWER_EN    3
+#define MC13783_REGTRL_LP_MODE_ENABLE          1
+#define MC13783_REGTRL_LP_MODE_DISABLE         0
+#define MC13783_REGTRL_STBY_MODE_ENABLE                1
+#define MC13783_REGTRL_STBY_MODE_DISABLE               0
+
+#endif                         /*  __MC13783_POWER_DEFS_H__ */
diff --git a/drivers/mxc/pmic/mc13783/pmic_rtc.c b/drivers/mxc/pmic/mc13783/pmic_rtc.c
new file mode 100644 (file)
index 0000000..dd6dd51
--- /dev/null
@@ -0,0 +1,544 @@
+/*
+ * 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 mc13783/pmic_rtc.c
+ * @brief This is the main file of PMIC(mc13783) RTC driver.
+ *
+ * @ingroup PMIC_RTC
+ */
+
+/*
+ * Includes
+ */
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/poll.h>
+#include <linux/platform_device.h>
+#include <linux/pmic_rtc.h>
+#include <linux/pmic_status.h>
+
+#include "pmic_rtc_defs.h"
+
+#define PMIC_LOAD_ERROR_MSG            \
+"PMIC card was not correctly detected. Stop loading PMIC RTC driver\n"
+
+/*
+ * Global variables
+ */
+static int pmic_rtc_major;
+static void callback_alarm_asynchronous(void *);
+static void callback_alarm_synchronous(void *);
+static unsigned int pmic_rtc_poll(struct file *file, poll_table * wait);
+static DECLARE_WAIT_QUEUE_HEAD(queue_alarm);
+static DECLARE_WAIT_QUEUE_HEAD(pmic_rtc_wait);
+static pmic_event_callback_t alarm_callback;
+static pmic_event_callback_t rtc_callback;
+static bool pmic_rtc_done;
+static struct class *pmic_rtc_class;
+
+static DECLARE_MUTEX(mutex);
+
+/* EXPORTED FUNCTIONS */
+EXPORT_SYMBOL(pmic_rtc_set_time);
+EXPORT_SYMBOL(pmic_rtc_get_time);
+EXPORT_SYMBOL(pmic_rtc_set_time_alarm);
+EXPORT_SYMBOL(pmic_rtc_get_time_alarm);
+EXPORT_SYMBOL(pmic_rtc_wait_alarm);
+EXPORT_SYMBOL(pmic_rtc_event_sub);
+EXPORT_SYMBOL(pmic_rtc_event_unsub);
+
+/*
+ * Real Time Clock Pmic API
+ */
+
+/*!
+ * This is the callback function called on TSI Pmic event, used in asynchronous
+ * call.
+ */
+static void callback_alarm_asynchronous(void *unused)
+{
+       pmic_rtc_done = true;
+}
+
+/*!
+ * This is the callback function is used in test code for (un)sub.
+ */
+static void callback_test_sub(void)
+{
+       printk(KERN_INFO "*****************************************\n");
+       printk(KERN_INFO "***** PMIC RTC 'Alarm IT CallBack' ******\n");
+       printk(KERN_INFO "*****************************************\n");
+}
+
+/*!
+ * This is the callback function called on TSI Pmic event, used in synchronous
+ * call.
+ */
+static void callback_alarm_synchronous(void *unused)
+{
+       printk(KERN_INFO "*** Alarm IT Pmic ***\n");
+       wake_up(&queue_alarm);
+}
+
+/*!
+ * This function wait the Alarm event
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_rtc_wait_alarm(void)
+{
+       DEFINE_WAIT(wait);
+       alarm_callback.func = callback_alarm_synchronous;
+       alarm_callback.param = NULL;
+       CHECK_ERROR(pmic_event_subscribe(EVENT_TODAI, alarm_callback));
+       prepare_to_wait(&queue_alarm, &wait, TASK_UNINTERRUPTIBLE);
+       schedule();
+       finish_wait(&queue_alarm, &wait);
+       CHECK_ERROR(pmic_event_unsubscribe(EVENT_TODAI, alarm_callback));
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function set the real time clock of PMIC
+ *
+ * @param        pmic_time     value of date and time
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_rtc_set_time(struct timeval *pmic_time)
+{
+       unsigned int tod_reg_val = 0;
+       unsigned int day_reg_val = 0;
+       unsigned int mask, value;
+
+       tod_reg_val = pmic_time->tv_sec % 86400;
+       day_reg_val = pmic_time->tv_sec / 86400;
+
+       mask = BITFMASK(MC13783_RTCTIME_TIME);
+       value = BITFVAL(MC13783_RTCTIME_TIME, tod_reg_val);
+       CHECK_ERROR(pmic_write_reg(REG_RTC_TIME, value, mask));
+
+       mask = BITFMASK(MC13783_RTCDAY_DAY);
+       value = BITFVAL(MC13783_RTCDAY_DAY, day_reg_val);
+       CHECK_ERROR(pmic_write_reg(REG_RTC_DAY, value, mask));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function get the real time clock of PMIC
+ *
+ * @param        pmic_time     return value of date and time
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_rtc_get_time(struct timeval *pmic_time)
+{
+       unsigned int tod_reg_val = 0;
+       unsigned int day_reg_val = 0;
+       unsigned int mask, value;
+
+       mask = BITFMASK(MC13783_RTCTIME_TIME);
+       CHECK_ERROR(pmic_read_reg(REG_RTC_TIME, &value, mask));
+       tod_reg_val = BITFEXT(value, MC13783_RTCTIME_TIME);
+
+       mask = BITFMASK(MC13783_RTCDAY_DAY);
+       CHECK_ERROR(pmic_read_reg(REG_RTC_DAY, &value, mask));
+       day_reg_val = BITFEXT(value, MC13783_RTCDAY_DAY);
+
+       pmic_time->tv_sec = (unsigned long)((unsigned long)(tod_reg_val &
+                                                           0x0001FFFF) +
+                                           (unsigned long)(day_reg_val *
+                                                           86400));
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function set the real time clock alarm of PMIC
+ *
+ * @param        pmic_time     value of date and time
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_rtc_set_time_alarm(struct timeval *pmic_time)
+{
+       unsigned int tod_reg_val = 0;
+       unsigned int day_reg_val = 0;
+       unsigned int mask, value;
+
+       if (down_interruptible(&mutex) < 0)
+               return ret;
+
+       tod_reg_val = pmic_time->tv_sec % 86400;
+       day_reg_val = pmic_time->tv_sec / 86400;
+
+       mask = BITFMASK(MC13783_RTCALARM_TIME);
+       value = BITFVAL(MC13783_RTCALARM_TIME, tod_reg_val);
+       CHECK_ERROR(pmic_write_reg(REG_RTC_ALARM, value, mask));
+
+       mask = BITFMASK(MC13783_RTCALARM_DAY);
+       value = BITFVAL(MC13783_RTCALARM_DAY, day_reg_val);
+       CHECK_ERROR(pmic_write_reg(REG_RTC_DAY_ALARM, value, mask));
+       up(&mutex);
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function get the real time clock alarm of PMIC
+ *
+ * @param        pmic_time     return value of date and time
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_rtc_get_time_alarm(struct timeval *pmic_time)
+{
+       unsigned int tod_reg_val = 0;
+       unsigned int day_reg_val = 0;
+       unsigned int mask, value;
+
+       mask = BITFMASK(MC13783_RTCALARM_TIME);
+       CHECK_ERROR(pmic_read_reg(REG_RTC_ALARM, &value, mask));
+       tod_reg_val = BITFEXT(value, MC13783_RTCALARM_TIME);
+
+       mask = BITFMASK(MC13783_RTCALARM_DAY);
+       CHECK_ERROR(pmic_read_reg(REG_RTC_DAY_ALARM, &value, mask));
+       day_reg_val = BITFEXT(value, MC13783_RTCALARM_DAY);
+
+       pmic_time->tv_sec = (unsigned long)((unsigned long)(tod_reg_val &
+                                                           0x0001FFFF) +
+                                           (unsigned long)(day_reg_val *
+                                                           86400));
+
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function is used to un/subscribe on RTC event IT.
+ *
+ * @param        event         type of event.
+ * @param        callback      event callback function.
+ * @param        sub           define if Un/subscribe event.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_rtc_event(t_rtc_int event, void *callback, bool sub)
+{
+       type_event rtc_event;
+       if (callback == NULL) {
+               return PMIC_ERROR;
+       } else {
+               rtc_callback.func = callback;
+               rtc_callback.param = NULL;
+       }
+       switch (event) {
+       case RTC_IT_ALARM:
+               rtc_event = EVENT_TODAI;
+               break;
+       case RTC_IT_1HZ:
+               rtc_event = EVENT_E1HZI;
+               break;
+       case RTC_IT_RST:
+               rtc_event = EVENT_RTCRSTI;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+       if (sub == true) {
+               CHECK_ERROR(pmic_event_subscribe(rtc_event, rtc_callback));
+       } else {
+               CHECK_ERROR(pmic_event_unsubscribe(rtc_event, rtc_callback));
+       }
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function is used to subscribe on RTC event IT.
+ *
+ * @param        event         type of event.
+ * @param        callback      event callback function.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_rtc_event_sub(t_rtc_int event, void *callback)
+{
+       CHECK_ERROR(pmic_rtc_event(event, callback, true));
+       return PMIC_SUCCESS;
+}
+
+/*!
+ * This function is used to un subscribe on RTC event IT.
+ *
+ * @param        event         type of event.
+ * @param        callback      event callback function.
+ *
+ * @return       This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_rtc_event_unsub(t_rtc_int event, void *callback)
+{
+       CHECK_ERROR(pmic_rtc_event(event, callback, false));
+       return PMIC_SUCCESS;
+}
+
+/* Called without the kernel lock - fine */
+static unsigned int pmic_rtc_poll(struct file *file, poll_table * wait)
+{
+       /*poll_wait(file, &pmic_rtc_wait, wait); */
+
+       if (pmic_rtc_done)
+               return POLLIN | POLLRDNORM;
+       return 0;
+}
+
+/*!
+ * This function implements IOCTL controls on a PMIC RTC device.
+ *
+ * @param        inode       pointer on the node
+ * @param        file        pointer on the file
+ * @param        cmd         the command
+ * @param        arg         the parameter
+ * @return       This function returns 0 if successful.
+ */
+static int pmic_rtc_ioctl(struct inode *inode, struct file *file,
+                         unsigned int cmd, unsigned long arg)
+{
+       struct timeval *pmic_time = NULL;
+
+       if (_IOC_TYPE(cmd) != 'p')
+               return -ENOTTY;
+
+       if (arg) {
+               pmic_time = kmalloc(sizeof(struct timeval), GFP_KERNEL);
+               if (pmic_time == NULL)
+                       return -ENOMEM;
+
+               /*      if (copy_from_user(pmic_time, (struct timeval *)arg,
+                  sizeof(struct timeval))) {
+                  return -EFAULT;
+                  } */
+       }
+
+       switch (cmd) {
+       case PMIC_RTC_SET_TIME:
+               if (copy_from_user(pmic_time, (struct timeval *)arg,
+                                  sizeof(struct timeval))) {
+                       return -EFAULT;
+               }
+               pr_debug("SET RTC\n");
+               CHECK_ERROR(pmic_rtc_set_time(pmic_time));
+               break;
+       case PMIC_RTC_GET_TIME:
+               if (copy_to_user((struct timeval *)arg, pmic_time,
+                                sizeof(struct timeval))) {
+                       return -EFAULT;
+               }
+               pr_debug("GET RTC\n");
+               CHECK_ERROR(pmic_rtc_get_time(pmic_time));
+               break;
+       case PMIC_RTC_SET_ALARM:
+               if (copy_from_user(pmic_time, (struct timeval *)arg,
+                                  sizeof(struct timeval))) {
+                       return -EFAULT;
+               }
+               pr_debug("SET RTC ALARM\n");
+               CHECK_ERROR(pmic_rtc_set_time_alarm(pmic_time));
+               break;
+       case PMIC_RTC_GET_ALARM:
+               if (copy_to_user((struct timeval *)arg, pmic_time,
+                                sizeof(struct timeval))) {
+                       return -EFAULT;
+               }
+               pr_debug("GET RTC ALARM\n");
+               CHECK_ERROR(pmic_rtc_get_time_alarm(pmic_time));
+               break;
+       case PMIC_RTC_WAIT_ALARM:
+               printk(KERN_INFO "WAIT ALARM...\n");
+               CHECK_ERROR(pmic_rtc_event_sub(RTC_IT_ALARM,
+                                              callback_test_sub));
+               CHECK_ERROR(pmic_rtc_wait_alarm());
+               printk(KERN_INFO "ALARM DONE\n");
+               CHECK_ERROR(pmic_rtc_event_unsub(RTC_IT_ALARM,
+                                                callback_test_sub));
+               break;
+       case PMIC_RTC_ALARM_REGISTER:
+               printk(KERN_INFO "PMIC RTC ALARM REGISTER\n");
+               alarm_callback.func = callback_alarm_asynchronous;
+               alarm_callback.param = NULL;
+               CHECK_ERROR(pmic_event_subscribe(EVENT_TODAI, alarm_callback));
+               break;
+       case PMIC_RTC_ALARM_UNREGISTER:
+               printk(KERN_INFO "PMIC RTC ALARM UNREGISTER\n");
+               alarm_callback.func = callback_alarm_asynchronous;
+               alarm_callback.param = NULL;
+               CHECK_ERROR(pmic_event_unsubscribe
+                           (EVENT_TODAI, alarm_callback));
+               pmic_rtc_done = false;
+               break;
+       default:
+               pr_debug("%d unsupported ioctl command\n", (int)cmd);
+               return -EINVAL;
+       }
+
+       if (arg) {
+               if (copy_to_user((struct timeval *)arg, pmic_time,
+                                sizeof(struct timeval))) {
+                       return -EFAULT;
+               }
+               kfree(pmic_time);
+       }
+
+       return 0;
+}
+
+/*!
+ * This function implements the open method on a PMIC RTC device.
+ *
+ * @param        inode       pointer on the node
+ * @param        file        pointer on the file
+ * @return       This function returns 0.
+ */
+static int pmic_rtc_open(struct inode *inode, struct file *file)
+{
+       return 0;
+}
+
+/*!
+ * This function implements the release method on a PMIC RTC device.
+ *
+ * @param        inode       pointer on the node
+ * @param        file        pointer on the file
+ * @return       This function returns 0.
+ */
+static int pmic_rtc_release(struct inode *inode, struct file *file)
+{
+       return 0;
+}
+
+/*!
+ * This function is called to put the RTC in a low power state.
+ * There is no need for power handlers for the RTC device.
+ * The RTC cannot be suspended.
+ *
+ * @param   pdev  the device structure used to give information on which RTC
+ *                device (0 through 3 channels) to suspend
+ * @param   state the power state the device is entering
+ *
+ * @return  The function always returns 0.
+ */
+static int pmic_rtc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       return 0;
+}
+
+/*!
+ * This function is called to resume the RTC from a low power state.
+ *
+ * @param   pdev  the device structure used to give information on which RTC
+ *                device (0 through 3 channels) to suspend
+ *
+ * @return  The function always returns 0.
+ */
+static int pmic_rtc_resume(struct platform_device *pdev)
+{
+       return 0;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+
+static struct file_operations pmic_rtc_fops = {
+       .owner = THIS_MODULE,
+       .ioctl = pmic_rtc_ioctl,
+       .poll = pmic_rtc_poll,
+       .open = pmic_rtc_open,
+       .release = pmic_rtc_release,
+};
+
+static int pmic_rtc_remove(struct platform_device *pdev)
+{
+       device_destroy(pmic_rtc_class, MKDEV(pmic_rtc_major, 0));
+       class_destroy(pmic_rtc_class);
+       unregister_chrdev(pmic_rtc_major, "pmic_rtc");
+       return 0;
+}
+
+static int pmic_rtc_probe(struct platform_device *pdev)
+{
+       int ret = 0;
+       struct device *temp_class;
+
+       pmic_rtc_major = register_chrdev(0, "pmic_rtc", &pmic_rtc_fops);
+       if (pmic_rtc_major < 0) {
+               printk(KERN_ERR "Unable to get a major for pmic_rtc\n");
+               return pmic_rtc_major;
+       }
+
+       pmic_rtc_class = class_create(THIS_MODULE, "pmic_rtc");
+       if (IS_ERR(pmic_rtc_class)) {
+               printk(KERN_ERR "Error creating pmic rtc class.\n");
+               ret = PTR_ERR(pmic_rtc_class);
+               goto err_out1;
+       }
+
+       temp_class = device_create(pmic_rtc_class, NULL,
+                                  MKDEV(pmic_rtc_major, 0), NULL,
+                                  "pmic_rtc");
+       if (IS_ERR(temp_class)) {
+               printk(KERN_ERR "Error creating pmic rtc class device.\n");
+               ret = PTR_ERR(temp_class);
+               goto err_out2;
+       }
+
+       printk(KERN_INFO "PMIC RTC successfully probed\n");
+       return ret;
+
+      err_out2:
+       class_destroy(pmic_rtc_class);
+      err_out1:
+       unregister_chrdev(pmic_rtc_major, "pmic_rtc");
+       return ret;
+}
+
+static struct platform_driver pmic_rtc_driver_ldm = {
+       .driver = {
+                  .name = "pmic_rtc",
+                  .owner = THIS_MODULE,
+                  },
+       .suspend = pmic_rtc_suspend,
+       .resume = pmic_rtc_resume,
+       .probe = pmic_rtc_probe,
+       .remove = pmic_rtc_remove,
+};
+
+static int __init pmic_rtc_init(void)
+{
+       pr_debug("PMIC RTC driver loading...\n");
+       return platform_driver_register(&pmic_rtc_driver_ldm);
+}
+static void __exit pmic_rtc_exit(void)
+{
+       platform_driver_unregister(&pmic_rtc_driver_ldm);
+       pr_debug("PMIC RTC driver successfully unloaded\n");
+}
+
+/*
+ * Module entry points
+ */
+
+subsys_initcall(pmic_rtc_init);
+module_exit(pmic_rtc_exit);
+
+MODULE_DESCRIPTION("Pmic_rtc driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pmic/mc13783/pmic_rtc_defs.h b/drivers/mxc/pmic/mc13783/pmic_rtc_defs.h
new file mode 100644 (file)
index 0000000..0164a51
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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
+ */
+#ifndef __MC13783_RTC_DEFS_H__
+#define __MC13783_RTC_DEFS_H__
+
+/*!
+ * @file mc13783/pmic_rtc_defs.h
+ * @brief This is the internal header of PMIC(mc13783) RTC driver.
+ *
+ * @ingroup PMIC_RTC
+ */
+
+/*
+ * RTC Time
+ */
+#define MC13783_RTCTIME_TIME_LSH       0
+#define MC13783_RTCTIME_TIME_WID       17
+
+/*
+ * RTC Alarm
+ */
+#define MC13783_RTCALARM_TIME_LSH      0
+#define MC13783_RTCALARM_TIME_WID      17
+
+/*
+ * RTC Day
+ */
+#define MC13783_RTCDAY_DAY_LSH         0
+#define MC13783_RTCDAY_DAY_WID         15
+
+/*
+ * RTC Day alarm
+ */
+#define MC13783_RTCALARM_DAY_LSH       0
+#define MC13783_RTCALARM_DAY_WID       15
+
+#endif                         /* __MC13783_RTC_DEFS_H__ */
diff --git a/drivers/mxc/pmic/mc13892/Kconfig b/drivers/mxc/pmic/mc13892/Kconfig
new file mode 100644 (file)
index 0000000..930e06a
--- /dev/null
@@ -0,0 +1,48 @@
+#
+# PMIC Modules configuration
+#
+
+config MXC_MC13892_ADC
+       tristate "MC13892 ADC support"
+       depends on MXC_PMIC_MC13892
+       ---help---
+       This is the MC13892 ADC module driver. This module provides kernel API
+       for the ADC system of MC13892.
+       It controls also the touch screen interface.
+       If you want MC13892 ADC support, you should say Y here
+
+config MXC_MC13892_RTC
+       tristate "MC13892 Real Time Clock (RTC) support"
+       depends on MXC_PMIC_MC13892
+       ---help---
+       This is the MC13892 RTC module driver. This module provides kernel API
+       for RTC part of MC13892.
+       If you want MC13892 RTC support, you should say Y here
+config MXC_MC13892_LIGHT
+       tristate "MC13892 Light and Backlight support"
+       depends on MXC_PMIC_MC13892
+       ---help---
+       This is the MC13892 Light module driver. This module provides kernel API
+       for led and backlight control part of MC13892.
+       If you want MC13892 Light support, you should say Y here
+config MXC_MC13892_BATTERY
+       tristate "MC13892 Battery API support"
+       depends on MXC_PMIC_MC13892
+       ---help---
+       This is the MC13892 battery module driver. This module provides kernel API
+       for battery control part of MC13892.
+       If you want MC13892 battery support, you should say Y here
+config MXC_MC13892_CONNECTIVITY
+       tristate "MC13892 Connectivity API support"
+       depends on MXC_PMIC_MC13892
+       ---help---
+       This is the MC13892 connectivity module driver. This module provides kernel API
+       for USB/RS232 connectivity control part of MC13892.
+       If you want MC13892 connectivity support, you should say Y here
+config MXC_MC13892_POWER
+       tristate "MC13892 Power API support"
+       depends on MXC_PMIC_MC13892
+       ---help---
+       This is the MC13892 power and supplies module driver. This module provides kernel API
+       for power and regulator control part of MC13892.
+       If you want MC13892 power support, you should say Y here
diff --git a/drivers/mxc/pmic/mc13892/Makefile b/drivers/mxc/pmic/mc13892/Makefile
new file mode 100644 (file)
index 0000000..0ed2b7e
--- /dev/null
@@ -0,0 +1,10 @@
+#
+# Makefile for the mc13783 pmic drivers.
+#
+
+obj-$(CONFIG_MXC_MC13892_ADC) += pmic_adc.o
+#obj-$(CONFIG_MXC_MC13892_RTC) += pmic_rtc.o
+obj-$(CONFIG_MXC_MC13892_LIGHT) += pmic_light.o
+obj-$(CONFIG_MXC_MC13892_BATTERY) += pmic_battery.o
+#obj-$(CONFIG_MXC_MC13892_CONNECTIVITY) += pmic_convity.o
+#obj-$(CONFIG_MXC_MC13892_POWER) += pmic_power.o
diff --git a/drivers/mxc/pmic/mc13892/pmic_adc.c b/drivers/mxc/pmic/mc13892/pmic_adc.c
new file mode 100644 (file)
index 0000000..cc74078
--- /dev/null
@@ -0,0 +1,1073 @@
+/*
+ * 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
+ */
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/device.h>
+
+#include <linux/pmic_adc.h>
+#include <linux/pmic_status.h>
+
+#include "../core/pmic.h"
+
+#define         DEF_ADC_0     0x008000
+#define         DEF_ADC_3     0x0001c0
+
+#define ADC_NB_AVAILABLE        2
+
+#define MAX_CHANNEL             7
+
+#define MC13892_ADC0_TS_M_LSH  14
+#define MC13892_ADC0_TS_M_WID  3
+
+/*
+ * Maximun allowed variation in the three X/Y co-ordinates acquired from
+ * touch-screen
+ */
+#define DELTA_Y_MAX             50
+#define DELTA_X_MAX             50
+
+/*
+ * ADC 0
+ */
+#define ADC_CHRGICON           0x000002
+#define ADC_WAIT_TSI_0         0x001400
+
+#define ADC_INC                 0x030000
+#define ADC_BIS                 0x800000
+#define ADC_CHRGRAW_D5          0x008000
+
+/*
+ * ADC 1
+ */
+
+#define ADC_EN                  0x000001
+#define ADC_SGL_CH              0x000002
+#define ADC_ADCCAL             0x000004
+#define ADC_ADSEL               0x000008
+#define ADC_TRIGMASK           0x000010
+#define ADC_CH_0_POS            5
+#define ADC_CH_0_MASK           0x0000E0
+#define ADC_CH_1_POS            8
+#define ADC_CH_1_MASK           0x000700
+#define ADC_DELAY_POS           11
+#define ADC_DELAY_MASK          0x07F800
+#define ADC_ATO                 0x080000
+#define ASC_ADC                 0x100000
+#define ADC_WAIT_TSI_1         0x200001
+#define ADC_NO_ADTRIG           0x200000
+
+/*
+ * ADC 2 - 4
+ */
+#define ADD1_RESULT_MASK        0x00000FFC
+#define ADD2_RESULT_MASK        0x00FFC000
+#define ADC_TS_MASK             0x00FFCFFC
+
+#define ADC_WCOMP               0x040000
+#define ADC_WCOMP_H_POS         0
+#define ADC_WCOMP_L_POS         9
+#define ADC_WCOMP_H_MASK        0x00003F
+#define ADC_WCOMP_L_MASK        0x007E00
+
+#define ADC_MODE_MASK           0x00003F
+
+#define ADC_INT_BISDONEI        0x02
+#define ADC_TSMODE_MASK 0x007000
+
+typedef enum adc_state {
+       ADC_FREE,
+       ADC_USED,
+       ADC_MONITORING,
+} t_adc_state;
+
+typedef enum reading_mode {
+       /*!
+        * Enables lithium cell reading
+        */
+       M_LITHIUM_CELL = 0x000001,
+       /*!
+        * Enables charge current reading
+        */
+       M_CHARGE_CURRENT = 0x000002,
+       /*!
+        * Enables battery current reading
+        */
+       M_BATTERY_CURRENT = 0x000004,
+} t_reading_mode;
+
+typedef struct {
+       /*!
+        * Delay before first conversion
+        */
+       unsigned int delay;
+       /*!
+        * sets the ATX bit for delay on all conversion
+        */
+       bool conv_delay;
+       /*!
+        * Sets the single channel mode
+        */
+       bool single_channel;
+       /*!
+        * Channel selection 1
+        */
+       t_channel channel_0;
+       /*!
+        * Channel selection 2
+        */
+       t_channel channel_1;
+       /*!
+        * Used to configure ADC mode with t_reading_mode
+        */
+       t_reading_mode read_mode;
+       /*!
+        * Sets the Touch screen mode
+        */
+       bool read_ts;
+       /*!
+        * Wait TSI event before touch screen reading
+        */
+       bool wait_tsi;
+       /*!
+        * Sets CHRGRAW scaling to divide by 5
+        * Only supported on 2.0 and higher
+        */
+       bool chrgraw_devide_5;
+       /*!
+        * Return ADC values
+        */
+       unsigned int value[8];
+       /*!
+        * Return touch screen values
+        */
+       t_touch_screen ts_value;
+} t_adc_param;
+
+static int pmic_adc_filter(t_touch_screen *ts_curr);
+int mc13892_adc_request(bool read_ts);
+int mc13892_adc_release(int adc_index);
+t_reading_mode mc13892_set_read_mode(t_channel channel);
+PMIC_STATUS mc13892_adc_read_ts(t_touch_screen *touch_sample, int wait_tsi);
+
+/* internal function */
+static void callback_tsi(void *);
+static void callback_adcdone(void *);
+static void callback_adcbisdone(void *);
+
+static int swait;
+
+static int suspend_flag;
+
+static wait_queue_head_t suspendq;
+
+/* EXPORTED FUNCTIONS */
+EXPORT_SYMBOL(pmic_adc_init);
+EXPORT_SYMBOL(pmic_adc_deinit);
+EXPORT_SYMBOL(pmic_adc_convert);
+EXPORT_SYMBOL(pmic_adc_convert_8x);
+EXPORT_SYMBOL(pmic_adc_set_touch_mode);
+EXPORT_SYMBOL(pmic_adc_get_touch_mode);
+EXPORT_SYMBOL(pmic_adc_get_touch_sample);
+
+static DECLARE_COMPLETION(adcdone_it);
+static DECLARE_COMPLETION(adcbisdone_it);
+static DECLARE_COMPLETION(adc_tsi);
+static pmic_event_callback_t tsi_event;
+static pmic_event_callback_t event_adc;
+static pmic_event_callback_t event_adc_bis;
+static bool data_ready_adc_1;
+static bool data_ready_adc_2;
+static bool adc_ts;
+static bool wait_ts;
+static bool monitor_en;
+static bool monitor_adc;
+static DECLARE_MUTEX(convert_mutex);
+
+static DECLARE_WAIT_QUEUE_HEAD(queue_adc_busy);
+static t_adc_state adc_dev[2];
+
+static unsigned channel_num[] = {
+       0,
+       1,
+       3,
+       4,
+       2,
+       0,
+       1,
+       3,
+       4,
+       -1,
+       5,
+       6,
+       7,
+       -1,
+       -1,
+       -1,
+       -1,
+       -1,
+       -1,
+       -1,
+       -1,
+       -1,
+       -1,
+       -1,
+       -1
+};
+
+static bool pmic_adc_ready;
+
+int is_pmic_adc_ready()
+{
+       return pmic_adc_ready;
+}
+EXPORT_SYMBOL(is_pmic_adc_ready);
+
+
+static int pmic_adc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       suspend_flag = 1;
+       CHECK_ERROR(pmic_write_reg(REG_ADC0, DEF_ADC_0, PMIC_ALL_BITS));
+       CHECK_ERROR(pmic_write_reg(REG_ADC1, 0, PMIC_ALL_BITS));
+       CHECK_ERROR(pmic_write_reg(REG_ADC2, 0, PMIC_ALL_BITS));
+       CHECK_ERROR(pmic_write_reg(REG_ADC3, DEF_ADC_3, PMIC_ALL_BITS));
+       CHECK_ERROR(pmic_write_reg(REG_ADC4, 0, PMIC_ALL_BITS));
+
+       return 0;
+};
+
+static int pmic_adc_resume(struct platform_device *pdev)
+{
+       /* nothing for mc13892 adc */
+       unsigned int adc_0_reg, adc_1_reg, reg_mask;
+       suspend_flag = 0;
+
+       /* let interrupt of TSI again */
+       adc_0_reg = ADC_WAIT_TSI_0;
+       reg_mask = ADC_WAIT_TSI_0;
+       CHECK_ERROR(pmic_write_reg(REG_ADC0, adc_0_reg, reg_mask));
+       adc_1_reg = ADC_WAIT_TSI_1 | (ADC_BIS * adc_ts);
+       CHECK_ERROR(pmic_write_reg(REG_ADC1, adc_1_reg, PMIC_ALL_BITS));
+
+       while (swait > 0) {
+               swait--;
+               wake_up_interruptible(&suspendq);
+       }
+
+       return 0;
+};
+
+static void callback_tsi(void *unused)
+{
+       pr_debug("*** TSI IT mc13892 PMIC_ADC_GET_TOUCH_SAMPLE ***\n");
+       if (wait_ts) {
+               complete(&adc_tsi);
+               pmic_event_mask(EVENT_TSI);
+       }
+}
+
+static void callback_adcdone(void *unused)
+{
+       if (data_ready_adc_1)
+               complete(&adcdone_it);
+}
+
+static void callback_adcbisdone(void *unused)
+{
+       pr_debug("* adcdone bis it callback *\n");
+       if (data_ready_adc_2)
+               complete(&adcbisdone_it);
+}
+
+static int pmic_adc_filter(t_touch_screen *ts_curr)
+{
+       unsigned int ydiff, xdiff;
+       unsigned int sample_sumx, sample_sumy;
+
+       if (ts_curr->contact_resistance == 0) {
+               ts_curr->x_position = 0;
+               ts_curr->y_position = 0;
+               return 0;
+       }
+
+       ydiff = abs(ts_curr->y_position1 - ts_curr->y_position2);
+       if (ydiff > DELTA_Y_MAX) {
+               pr_debug("pmic_adc_filter: Ret pos y\n");
+               return -1;
+       }
+
+       xdiff = abs(ts_curr->x_position1 - ts_curr->x_position2);
+       if (xdiff > DELTA_X_MAX) {
+               pr_debug("mc13892_adc_filter: Ret pos x\n");
+               return -1;
+       }
+
+       sample_sumx = ts_curr->x_position1 + ts_curr->x_position2;
+       sample_sumy = ts_curr->y_position1 + ts_curr->y_position2;
+
+       ts_curr->y_position = sample_sumy / 2;
+       ts_curr->x_position = sample_sumx / 2;
+
+       return 0;
+}
+
+int pmic_adc_init(void)
+{
+       unsigned int reg_value = 0, i = 0;
+
+       if (suspend_flag == 1)
+               return -EBUSY;
+
+       for (i = 0; i < ADC_NB_AVAILABLE; i++)
+               adc_dev[i] = ADC_FREE;
+
+       CHECK_ERROR(pmic_write_reg(REG_ADC0, DEF_ADC_0, PMIC_ALL_BITS));
+       CHECK_ERROR(pmic_write_reg(REG_ADC1, 0, PMIC_ALL_BITS));
+       CHECK_ERROR(pmic_write_reg(REG_ADC2, 0, PMIC_ALL_BITS));
+       CHECK_ERROR(pmic_write_reg(REG_ADC3, DEF_ADC_3, PMIC_ALL_BITS));
+       CHECK_ERROR(pmic_write_reg(REG_ADC4, 0, PMIC_ALL_BITS));
+       reg_value = 0x001000;
+
+       data_ready_adc_1 = false;
+       data_ready_adc_2 = false;
+       adc_ts = false;
+       wait_ts = false;
+       monitor_en = false;
+       monitor_adc = false;
+
+       /* sub to ADCDone IT */
+       event_adc.param = NULL;
+       event_adc.func = callback_adcdone;
+       CHECK_ERROR(pmic_event_subscribe(EVENT_ADCDONEI, event_adc));
+
+       /* sub to ADCDoneBis IT */
+       event_adc_bis.param = NULL;
+       event_adc_bis.func = callback_adcbisdone;
+       CHECK_ERROR(pmic_event_subscribe(EVENT_ADCBISDONEI, event_adc_bis));
+
+       /* sub to Touch Screen IT */
+       tsi_event.param = NULL;
+       tsi_event.func = callback_tsi;
+       CHECK_ERROR(pmic_event_subscribe(EVENT_TSI, tsi_event));
+
+       return PMIC_SUCCESS;
+}
+
+PMIC_STATUS pmic_adc_deinit(void)
+{
+       CHECK_ERROR(pmic_event_unsubscribe(EVENT_ADCDONEI, event_adc));
+       CHECK_ERROR(pmic_event_unsubscribe(EVENT_ADCBISDONEI, event_adc_bis));
+       CHECK_ERROR(pmic_event_unsubscribe(EVENT_TSI, tsi_event));
+
+       return PMIC_SUCCESS;
+}
+
+int mc13892_adc_init_param(t_adc_param *adc_param)
+{
+       int i = 0;
+
+       if (suspend_flag == 1)
+               return -EBUSY;
+
+       adc_param->delay = 0;
+       adc_param->conv_delay = false;
+       adc_param->single_channel = false;
+       adc_param->channel_0 = BATTERY_VOLTAGE;
+       adc_param->channel_1 = BATTERY_VOLTAGE;
+       adc_param->read_mode = 0;
+       adc_param->wait_tsi = 0;
+       adc_param->chrgraw_devide_5 = true;
+       adc_param->read_ts = false;
+       adc_param->ts_value.x_position = 0;
+       adc_param->ts_value.y_position = 0;
+       adc_param->ts_value.contact_resistance = 0;
+       for (i = 0; i <= MAX_CHANNEL; i++)
+               adc_param->value[i] = 0;
+
+       return 0;
+}
+
+PMIC_STATUS mc13892_adc_convert(t_adc_param *adc_param)
+{
+       bool use_bis = false;
+       unsigned int adc_0_reg = 0, adc_1_reg = 0, result_reg = 0, i = 0;
+       unsigned int result = 0, temp = 0;
+       pmic_version_t mc13892_ver;
+       int ret;
+
+       pr_debug("mc13892 ADC - mc13892_adc_convert ....\n");
+       if (suspend_flag == 1)
+               return -EBUSY;
+
+       if (adc_param->wait_tsi) {
+               /* configure adc to wait tsi interrupt */
+               INIT_COMPLETION(adc_tsi);
+
+               /*for ts don't use bis */
+               /*put ts in interrupt mode */
+               /* still kep reference? */
+               adc_0_reg = 0x001400 | (ADC_BIS * 0);
+               pmic_event_unmask(EVENT_TSI);
+               CHECK_ERROR(pmic_write_reg(REG_ADC0, adc_0_reg, PMIC_ALL_BITS));
+               /*for ts don't use bis */
+               adc_1_reg = 0x200001 | (ADC_BIS * 0);
+               CHECK_ERROR(pmic_write_reg(REG_ADC1, adc_1_reg, PMIC_ALL_BITS));
+               pr_debug("wait tsi ....\n");
+               wait_ts = true;
+               wait_for_completion_interruptible(&adc_tsi);
+               wait_ts = false;
+       }
+       down(&convert_mutex);
+       use_bis = mc13892_adc_request(adc_param->read_ts);
+       if (use_bis < 0) {
+               pr_debug("process has received a signal and got interrupted\n");
+               ret = -EINTR;
+               goto out_up_convert_mutex;
+       }
+
+       /* CONFIGURE ADC REG 0 */
+       adc_0_reg = 0;
+       adc_1_reg = 0;
+       if (adc_param->read_ts == false) {
+               adc_0_reg = adc_param->read_mode & 0x00003F;
+               /* add auto inc */
+               adc_0_reg |= ADC_INC;
+               if (use_bis) {
+                       /* add adc bis */
+                       adc_0_reg |= ADC_BIS;
+               }
+               mc13892_ver = pmic_get_version();
+               if (mc13892_ver.revision >= 20)
+                       if (adc_param->chrgraw_devide_5)
+                               adc_0_reg |= ADC_CHRGRAW_D5;
+
+               if (adc_param->single_channel)
+                       adc_1_reg |= ADC_SGL_CH;
+
+               if (adc_param->conv_delay)
+                       adc_1_reg |= ADC_ATO;
+
+               if (adc_param->single_channel)
+                       adc_1_reg |= ADC_SGL_CH;
+
+               adc_1_reg |= (adc_param->channel_0 << ADC_CH_0_POS) &
+                   ADC_CH_0_MASK;
+               adc_1_reg |= (adc_param->channel_1 << ADC_CH_1_POS) &
+                   ADC_CH_1_MASK;
+       } else {
+               adc_0_reg = 0x002400 | (ADC_BIS * use_bis) | ADC_INC;
+       }
+
+       if (adc_param->channel_0 == channel_num[CHARGE_CURRENT])
+               adc_0_reg |= ADC_CHRGICON;
+
+       /*Change has been made here */
+       ret = pmic_write_reg(REG_ADC0, adc_0_reg,
+               ADC_INC | ADC_BIS | ADC_CHRGRAW_D5 | 0xfff00ff);
+       if (ret != PMIC_SUCCESS) {
+               pr_debug("pmic_write_reg");
+               goto out_mc13892_adc_release;
+       }
+
+       /* CONFIGURE ADC REG 1 */
+       if (adc_param->read_ts == false) {
+               adc_1_reg |= ADC_NO_ADTRIG;
+               adc_1_reg |= ADC_EN;
+               adc_1_reg |= (adc_param->delay << ADC_DELAY_POS) &
+                   ADC_DELAY_MASK;
+               if (use_bis)
+                       adc_1_reg |= ADC_BIS;
+       } else {
+               /* configure and start convert to read x and y position */
+               /* configure to read 2 value in channel selection 1 & 2 */
+               adc_1_reg = 0x100409 | (ADC_BIS * use_bis) | ADC_NO_ADTRIG;
+               /* set ATOx = 5, it could be better for ts ADC */
+               adc_1_reg |= 0x002800;
+       }
+
+       if (adc_param->channel_0 == channel_num[CHARGE_CURRENT]) {
+               adc_param->channel_1 = channel_num[CHARGE_CURRENT];
+               adc_1_reg &= ~(ADC_CH_0_MASK | ADC_CH_1_MASK | ADC_NO_ADTRIG |
+                              ADC_TRIGMASK | ADC_EN | ADC_SGL_CH | ADC_ADCCAL);
+               adc_1_reg |= ((adc_param->channel_0 << ADC_CH_0_POS) |
+                             (adc_param->channel_1 << ADC_CH_1_POS));
+               adc_1_reg |= (ADC_EN | ADC_SGL_CH | ADC_ADCCAL);
+
+               if (use_bis == 0) {
+                       CHECK_ERROR(pmic_write_reg(REG_ADC1, adc_1_reg,
+                                                  0xFFFFFF));
+               } else {
+                       CHECK_ERROR(pmic_write_reg(REG_ADC1, adc_1_reg,
+                                                  0xFFFFFF));
+                       temp = 0x800000;
+                       CHECK_ERROR(pmic_write_reg(REG_ADC3, temp, 0xFFFFFF));
+               }
+
+               adc_1_reg &= ~(ADC_NO_ADTRIG | ASC_ADC | ADC_ADCCAL);
+               adc_1_reg |= (ADC_NO_ADTRIG | ASC_ADC);
+               if (use_bis == 0) {
+                       data_ready_adc_1 = true;
+                       INIT_COMPLETION(adcdone_it);
+                       CHECK_ERROR(pmic_write_reg(REG_ADC1, adc_1_reg,
+                                                  0xFFFFFF));
+                       pr_debug("wait adc done\n");
+                       wait_for_completion_interruptible(&adcdone_it);
+                       data_ready_adc_1 = false;
+               } else {
+                       data_ready_adc_2 = true;
+                       INIT_COMPLETION(adcbisdone_it);
+                       CHECK_ERROR(pmic_write_reg(REG_ADC1, adc_1_reg,
+                                                  0xFFFFFF));
+                       temp = 0x800000;
+                       CHECK_ERROR(pmic_write_reg(REG_ADC3, temp, 0xFFFFFF));
+                       pr_debug("wait adc done bis\n");
+                       wait_for_completion_interruptible(&adcbisdone_it);
+                       data_ready_adc_2 = false;
+               }
+       } else {
+       if (use_bis == 0) {
+               data_ready_adc_1 = false;
+               adc_1_reg |= ASC_ADC;
+               data_ready_adc_1 = true;
+               pr_debug("Write Reg %i = %x\n", REG_ADC1, adc_1_reg);
+               INIT_COMPLETION(adcdone_it);
+               ret = pmic_write_reg(REG_ADC1, adc_1_reg,
+                                                  ADC_SGL_CH | ADC_ATO |
+                                                  ADC_ADSEL | ADC_CH_0_MASK |
+                                                  ADC_CH_1_MASK |
+                                          ADC_NO_ADTRIG | ADC_EN |
+                                                  ADC_DELAY_MASK | ASC_ADC |
+                                                  ADC_BIS);
+               if (ret != PMIC_SUCCESS) {
+                       pr_debug("pmic_write_reg");
+                       goto out_mc13892_adc_release;
+               }
+
+               pr_debug("wait adc done\n");
+               wait_for_completion_interruptible(&adcdone_it);
+               data_ready_adc_1 = false;
+       } else {
+               data_ready_adc_2 = false;
+               adc_1_reg |= ASC_ADC;
+               data_ready_adc_2 = true;
+               INIT_COMPLETION(adcbisdone_it);
+               ret = pmic_write_reg(REG_ADC1, adc_1_reg, 0xFFFFFF);
+               if (ret != PMIC_SUCCESS) {
+                       pr_debug("pmic_write_reg");
+                       goto out_mc13892_adc_release;
+               }
+
+               temp = 0x800000;
+               ret = pmic_write_reg(REG_ADC3, temp, 0xFFFFFF);
+               if (ret != PMIC_SUCCESS) {
+                       pr_info("pmic_write_reg");
+                       goto out_mc13892_adc_release;
+               }
+
+               pr_debug("wait adc done bis\n");
+               wait_for_completion_interruptible(&adcbisdone_it);
+               data_ready_adc_2 = false;
+       }
+       }
+
+       /* read result and store in adc_param */
+       result = 0;
+       if (use_bis == 0)
+               result_reg = REG_ADC2;
+       else
+               result_reg = REG_ADC4;
+
+       ret = pmic_write_reg(REG_ADC1, 4 << ADC_CH_1_POS,
+               ADC_CH_0_MASK | ADC_CH_1_MASK);
+       if (ret != PMIC_SUCCESS) {
+               pr_debug("pmic_write_reg");
+               goto out_mc13892_adc_release;
+       }
+
+       for (i = 0; i <= 3; i++) {
+               ret = pmic_read_reg(result_reg, &result, PMIC_ALL_BITS);
+               if (ret != PMIC_SUCCESS) {
+                       pr_debug("pmic_write_reg");
+                       goto out_mc13892_adc_release;
+               }
+
+               adc_param->value[i] = ((result & ADD1_RESULT_MASK) >> 2);
+               adc_param->value[i + 4] = ((result & ADD2_RESULT_MASK) >> 14);
+               pr_debug("value[%d] = %d, value[%d] = %d\n",
+                        i, adc_param->value[i],
+                        i + 4, adc_param->value[i + 4]);
+       }
+       if (adc_param->read_ts) {
+               adc_param->ts_value.x_position = adc_param->value[0];
+               adc_param->ts_value.x_position1 = adc_param->value[0];
+               adc_param->ts_value.x_position2 = adc_param->value[1];
+               adc_param->ts_value.y_position = adc_param->value[3];
+               adc_param->ts_value.y_position1 = adc_param->value[3];
+               adc_param->ts_value.y_position2 = adc_param->value[4];
+               adc_param->ts_value.contact_resistance = adc_param->value[6];
+               ret = pmic_write_reg(REG_ADC0, 0x0, ADC_TSMODE_MASK);
+               if (ret != PMIC_SUCCESS) {
+                       pr_debug("pmic_write_reg");
+                       goto out_mc13892_adc_release;
+               }
+       }
+
+       /*if (adc_param->read_ts) {
+          adc_param->ts_value.x_position = adc_param->value[2];
+          adc_param->ts_value.y_position = adc_param->value[5];
+          adc_param->ts_value.contact_resistance = adc_param->value[6];
+          } */
+       ret = PMIC_SUCCESS;
+out_mc13892_adc_release:
+       mc13892_adc_release(use_bis);
+out_up_convert_mutex:
+       up(&convert_mutex);
+
+       return ret;
+}
+
+t_reading_mode mc13892_set_read_mode(t_channel channel)
+{
+       t_reading_mode read_mode = 0;
+
+       switch (channel) {
+       case CHARGE_CURRENT:
+               read_mode = M_CHARGE_CURRENT;
+               break;
+       case BATTERY_CURRENT:
+               read_mode = M_BATTERY_CURRENT;
+               break;
+       default:
+               read_mode = 0;
+       }
+
+       return read_mode;
+}
+
+PMIC_STATUS pmic_adc_convert(t_channel channel, unsigned short *result)
+{
+       t_adc_param adc_param;
+       PMIC_STATUS ret;
+       unsigned int i;
+
+       if (suspend_flag == 1)
+               return -EBUSY;
+
+       channel = channel_num[channel];
+       if (channel == -1) {
+               pr_debug("Wrong channel ID\n");
+               return PMIC_PARAMETER_ERROR;
+       }
+       mc13892_adc_init_param(&adc_param);
+       pr_debug("pmic_adc_convert\n");
+       adc_param.read_ts = false;
+       adc_param.single_channel = true;
+       adc_param.read_mode = mc13892_set_read_mode(channel);
+
+       /* Find the group */
+       if (channel <= 7)
+               adc_param.channel_0 = channel;
+       else
+               return PMIC_PARAMETER_ERROR;
+
+       ret = mc13892_adc_convert(&adc_param);
+       for (i = 0; i <= 7; i++)
+               result[i] = adc_param.value[i];
+
+       return ret;
+}
+
+PMIC_STATUS pmic_adc_convert_8x(t_channel channel, unsigned short *result)
+{
+       t_adc_param adc_param;
+       int i;
+       PMIC_STATUS ret;
+       if (suspend_flag == 1)
+               return -EBUSY;
+
+       channel = channel_num[channel];
+
+       if (channel == -1) {
+               pr_debug("Wrong channel ID\n");
+               return PMIC_PARAMETER_ERROR;
+       }
+       mc13892_adc_init_param(&adc_param);
+       pr_debug("pmic_adc_convert_8x\n");
+       adc_param.read_ts = false;
+       adc_param.single_channel = true;
+       adc_param.read_mode = mc13892_set_read_mode(channel);
+
+       if (channel <= 7) {
+               adc_param.channel_0 = channel;
+               adc_param.channel_1 = channel;
+       } else
+               return PMIC_PARAMETER_ERROR;
+
+       ret = mc13892_adc_convert(&adc_param);
+       for (i = 0; i <= 7; i++)
+               result[i] = adc_param.value[i];
+
+       return ret;
+}
+
+PMIC_STATUS pmic_adc_set_touch_mode(t_touch_mode touch_mode)
+{
+       if (suspend_flag == 1)
+               return -EBUSY;
+
+       CHECK_ERROR(pmic_write_reg(REG_ADC0,
+                                  BITFVAL(MC13892_ADC0_TS_M, touch_mode),
+                                  BITFMASK(MC13892_ADC0_TS_M)));
+       return PMIC_SUCCESS;
+}
+
+PMIC_STATUS pmic_adc_get_touch_mode(t_touch_mode *touch_mode)
+{
+       unsigned int value;
+       if (suspend_flag == 1)
+               return -EBUSY;
+
+       CHECK_ERROR(pmic_read_reg(REG_ADC0, &value, PMIC_ALL_BITS));
+
+       *touch_mode = BITFEXT(value, MC13892_ADC0_TS_M);
+
+       return PMIC_SUCCESS;
+}
+
+PMIC_STATUS pmic_adc_get_touch_sample(t_touch_screen *touch_sample, int wait)
+{
+       if (mc13892_adc_read_ts(touch_sample, wait) != 0)
+               return PMIC_ERROR;
+       if (0 == pmic_adc_filter(touch_sample))
+               return PMIC_SUCCESS;
+       else
+               return PMIC_ERROR;
+}
+
+PMIC_STATUS mc13892_adc_read_ts(t_touch_screen *ts_value, int wait_tsi)
+{
+       t_adc_param param;
+       pr_debug("mc13892_adc : mc13892_adc_read_ts\n");
+       if (suspend_flag == 1)
+               return -EBUSY;
+
+       if (wait_ts) {
+               pr_debug("mc13892_adc : error TS busy \n");
+               return PMIC_ERROR;
+       }
+       mc13892_adc_init_param(&param);
+       param.wait_tsi = wait_tsi;
+       param.read_ts = true;
+       if (mc13892_adc_convert(&param) != 0)
+               return PMIC_ERROR;
+       /* check if x-y is ok */
+       if (param.ts_value.contact_resistance < 1000) {
+               ts_value->x_position = param.ts_value.x_position;
+               ts_value->x_position1 = param.ts_value.x_position1;
+               ts_value->x_position2 = param.ts_value.x_position2;
+
+               ts_value->y_position = param.ts_value.y_position;
+               ts_value->y_position1 = param.ts_value.y_position1;
+               ts_value->y_position2 = param.ts_value.y_position2;
+
+               ts_value->contact_resistance =
+                   param.ts_value.contact_resistance + 1;
+
+       } else {
+               ts_value->x_position = 0;
+               ts_value->y_position = 0;
+               ts_value->contact_resistance = 0;
+
+       }
+       return PMIC_SUCCESS;
+}
+
+int mc13892_adc_request(bool read_ts)
+{
+       int adc_index = -1;
+       if (read_ts != 0) {
+               /*for ts we use bis=0 */
+               if (adc_dev[0] == ADC_USED)
+                       return -1;
+               /*no wait here */
+               adc_dev[0] = ADC_USED;
+               adc_index = 0;
+       } else {
+               /*for other adc use bis = 1 */
+               if (adc_dev[1] == ADC_USED) {
+                       return -1;
+                       /*no wait here */
+               }
+               adc_dev[1] = ADC_USED;
+               adc_index = 1;
+       }
+       pr_debug("mc13892_adc : request ADC %d\n", adc_index);
+       return adc_index;
+}
+
+int mc13892_adc_release(int adc_index)
+{
+       while (suspend_flag == 1) {
+               swait++;
+               /* Block if the device is suspended */
+               if (wait_event_interruptible(suspendq, (suspend_flag == 0)))
+                       return -ERESTARTSYS;
+       }
+
+       pr_debug("mc13892_adc : release ADC %d\n", adc_index);
+       if ((adc_dev[adc_index] == ADC_MONITORING) ||
+           (adc_dev[adc_index] == ADC_USED)) {
+               adc_dev[adc_index] = ADC_FREE;
+               wake_up(&queue_adc_busy);
+               return 0;
+       }
+       return -1;
+}
+
+#ifdef DEBUG
+static t_adc_param adc_param_db;
+
+static ssize_t adc_info(struct device *dev, struct device_attribute *attr,
+                       char *buf)
+{
+       int *value = adc_param_db.value;
+
+       pr_debug("adc_info\n");
+
+       pr_debug("ch0\t\t%d\n", adc_param_db.channel_0);
+       pr_debug("ch1\t\t%d\n", adc_param_db.channel_1);
+       pr_debug("d5\t\t%d\n", adc_param_db.chrgraw_devide_5);
+       pr_debug("conv delay\t%d\n", adc_param_db.conv_delay);
+       pr_debug("delay\t\t%d\n", adc_param_db.delay);
+       pr_debug("read mode\t%d\n", adc_param_db.read_mode);
+       pr_debug("read ts\t\t%d\n", adc_param_db.read_ts);
+       pr_debug("single ch\t%d\n", adc_param_db.single_channel);
+       pr_debug("wait ts int\t%d\n", adc_param_db.wait_tsi);
+       pr_debug("value0-3:\t%d\t%d\t%d\t%d\n", value[0], value[1],
+                value[2], value[3]);
+       pr_debug("value4-7:\t%d\t%d\t%d\t%d\n", value[4], value[5],
+                value[6], value[7]);
+
+       return 0;
+}
+
+enum {
+       ADC_SET_CH0 = 0,
+       ADC_SET_CH1,
+       ADC_SET_DV5,
+       ADC_SET_CON_DELAY,
+       ADC_SET_DELAY,
+       ADC_SET_RM,
+       ADC_SET_RT,
+       ADC_SET_S_CH,
+       ADC_SET_WAIT_TS,
+       ADC_INIT_P,
+       ADC_START,
+       ADC_TS,
+       ADC_TS_READ,
+       ADC_TS_CAL,
+       ADC_CMD_MAX
+};
+
+static const char *const adc_cmd[ADC_CMD_MAX] = {
+       [ADC_SET_CH0] = "ch0",
+       [ADC_SET_CH1] = "ch1",
+       [ADC_SET_DV5] = "dv5",
+       [ADC_SET_CON_DELAY] = "cd",
+       [ADC_SET_DELAY] = "dl",
+       [ADC_SET_RM] = "rm",
+       [ADC_SET_RT] = "rt",
+       [ADC_SET_S_CH] = "sch",
+       [ADC_SET_WAIT_TS] = "wt",
+       [ADC_INIT_P] = "init",
+       [ADC_START] = "start",
+       [ADC_TS] = "touch",
+       [ADC_TS_READ] = "touchr",
+       [ADC_TS_CAL] = "cal"
+};
+
+static int cmd(unsigned int index, int value)
+{
+       t_touch_screen ts;
+
+       switch (index) {
+       case ADC_SET_CH0:
+               adc_param_db.channel_0 = value;
+               break;
+       case ADC_SET_CH1:
+               adc_param_db.channel_1 = value;
+               break;
+       case ADC_SET_DV5:
+               adc_param_db.chrgraw_devide_5 = value;
+               break;
+       case ADC_SET_CON_DELAY:
+               adc_param_db.conv_delay = value;
+               break;
+       case ADC_SET_RM:
+               adc_param_db.read_mode = value;
+               break;
+       case ADC_SET_RT:
+               adc_param_db.read_ts = value;
+               break;
+       case ADC_SET_S_CH:
+               adc_param_db.single_channel = value;
+               break;
+       case ADC_SET_WAIT_TS:
+               adc_param_db.wait_tsi = value;
+               break;
+       case ADC_INIT_P:
+               mc13892_adc_init_param(&adc_param_db);
+               break;
+       case ADC_START:
+               mc13892_adc_convert(&adc_param_db);
+               break;
+       case ADC_TS:
+               pmic_adc_get_touch_sample(&ts, 1);
+               pr_debug("x = %d\n", ts.x_position);
+               pr_debug("y = %d\n", ts.y_position);
+               pr_debug("p = %d\n", ts.contact_resistance);
+               break;
+       case ADC_TS_READ:
+               pmic_adc_get_touch_sample(&ts, 0);
+               pr_debug("x = %d\n", ts.x_position);
+               pr_debug("y = %d\n", ts.y_position);
+               pr_debug("p = %d\n", ts.contact_resistance);
+               break;
+       case ADC_TS_CAL:
+               break;
+       default:
+               pr_debug("error command\n");
+               break;
+       }
+       return 0;
+}
+
+static ssize_t adc_ctl(struct device *dev, struct device_attribute *attr,
+                      const char *buf, size_t count)
+{
+       int state = 0;
+       const char *const *s;
+       char *p, *q;
+       int error;
+       int len, value = 0;
+
+       pr_debug("adc_ctl\n");
+
+       q = NULL;
+       q = memchr(buf, ' ', count);
+
+       if (q != NULL) {
+               len = q - buf;
+               q += 1;
+               value = simple_strtoul(q, NULL, 10);
+       } else {
+               p = memchr(buf, '\n', count);
+               len = p ? p - buf : count;
+       }
+
+       for (s = &adc_cmd[state]; state < ADC_CMD_MAX; s++, state++) {
+               if (*s && !strncmp(buf, *s, len))
+                       break;
+       }
+       if (state < ADC_CMD_MAX && *s)
+               error = cmd(state, value);
+       else
+               error = -EINVAL;
+
+       return count;
+}
+
+#else
+static ssize_t adc_info(struct device *dev, struct device_attribute *attr,
+                       char *buf)
+{
+       return 0;
+}
+
+static ssize_t adc_ctl(struct device *dev, struct device_attribute *attr,
+                      const char *buf, size_t count)
+{
+       return count;
+}
+
+#endif
+
+static DEVICE_ATTR(adc, 0644, adc_info, adc_ctl);
+
+static int pmic_adc_module_probe(struct platform_device *pdev)
+{
+       int ret = 0;
+
+       pr_debug("PMIC ADC start probe\n");
+       ret = device_create_file(&(pdev->dev), &dev_attr_adc);
+       if (ret) {
+               pr_debug("Can't create device file!\n");
+               return -ENODEV;
+       }
+
+       init_waitqueue_head(&suspendq);
+
+       ret = pmic_adc_init();
+       if (ret != PMIC_SUCCESS) {
+               pr_debug("Error in pmic_adc_init.\n");
+               goto rm_dev_file;
+       }
+
+       pmic_adc_ready = 1;
+       pr_debug("PMIC ADC successfully probed\n");
+       return 0;
+
+rm_dev_file:
+       device_remove_file(&(pdev->dev), &dev_attr_adc);
+       return ret;
+}
+
+static int pmic_adc_module_remove(struct platform_device *pdev)
+{
+       pmic_adc_deinit();
+       pmic_adc_ready = 0;
+       pr_debug("PMIC ADC successfully removed\n");
+       return 0;
+}
+
+static struct platform_driver pmic_adc_driver_ldm = {
+       .driver = {
+                  .name = "pmic_adc",
+                  },
+       .suspend = pmic_adc_suspend,
+       .resume = pmic_adc_resume,
+       .probe = pmic_adc_module_probe,
+       .remove = pmic_adc_module_remove,
+};
+
+static int __init pmic_adc_module_init(void)
+{
+       pr_debug("PMIC ADC driver loading...\n");
+       return platform_driver_register(&pmic_adc_driver_ldm);
+}
+
+static void __exit pmic_adc_module_exit(void)
+{
+       platform_driver_unregister(&pmic_adc_driver_ldm);
+       pr_debug("PMIC ADC driver successfully unloaded\n");
+}
+
+module_init(pmic_adc_module_init);
+module_exit(pmic_adc_module_exit);
+
+MODULE_DESCRIPTION("PMIC ADC device driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pmic/mc13892/pmic_battery.c b/drivers/mxc/pmic/mc13892/pmic_battery.c
new file mode 100644 (file)
index 0000000..6061e6e
--- /dev/null
@@ -0,0 +1,797 @@
+/*
+ * 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
+ */
+
+/*
+ * Includes
+ */
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <asm/mach-types.h>
+#include <linux/pmic_battery.h>
+#include <linux/pmic_adc.h>
+#include <linux/pmic_status.h>
+
+#define BIT_CHG_VOL_LSH                0
+#define BIT_CHG_VOL_WID                3
+
+#define BIT_CHG_CURR_LSH               3
+#define BIT_CHG_CURR_WID               4
+
+#define BIT_CHG_PLIM_LSH               15
+#define BIT_CHG_PLIM_WID               2
+
+#define BIT_CHG_DETS_LSH 6
+#define BIT_CHG_DETS_WID 1
+#define BIT_CHG_CURRS_LSH 11
+#define BIT_CHG_CURRS_WID 1
+
+#define TRICKLE_CHG_EN_LSH     7
+#define        LOW_POWER_BOOT_ACK_LSH  8
+#define BAT_TH_CHECK_DIS_LSH   9
+#define        BATTFET_CTL_EN_LSH      10
+#define BATTFET_CTL_LSH                11
+#define        REV_MOD_EN_LSH          13
+#define PLIM_DIS_LSH           17
+#define        CHG_LED_EN_LSH          18
+#define RESTART_CHG_STAT_LSH   20
+#define        AUTO_CHG_DIS_LSH        21
+#define CYCLING_DIS_LSH                22
+#define        VI_PROGRAM_EN_LSH       23
+
+#define TRICKLE_CHG_EN_WID     1
+#define        LOW_POWER_BOOT_ACK_WID  1
+#define BAT_TH_CHECK_DIS_WID   1
+#define        BATTFET_CTL_EN_WID      1
+#define BATTFET_CTL_WID                1
+#define        REV_MOD_EN_WID          1
+#define PLIM_DIS_WID           1
+#define        CHG_LED_EN_WID          1
+#define RESTART_CHG_STAT_WID   1
+#define        AUTO_CHG_DIS_WID        1
+#define CYCLING_DIS_WID                1
+#define        VI_PROGRAM_EN_WID       1
+
+#define ACC_STARTCC_LSH                0
+#define ACC_STARTCC_WID                1
+#define ACC_RSTCC_LSH          1
+#define ACC_RSTCC_WID          1
+#define ACC_CCFAULT_LSH                7
+#define ACC_CCFAULT_WID                7
+#define ACC_CCOUT_LSH          8
+#define ACC_CCOUT_WID          16
+#define ACC1_ONEC_LSH          0
+#define ACC1_ONEC_WID          15
+
+#define ACC_CALIBRATION 0x17
+#define ACC_START_COUNTER 0x07
+#define ACC_STOP_COUNTER 0x2
+#define ACC_CONTROL_BIT_MASK 0x1f
+#define ACC_ONEC_VALUE 2621
+#define ACC_COULOMB_PER_LSB 1
+#define ACC_CALIBRATION_DURATION_MSECS 20
+
+#define BAT_VOLTAGE_UNIT_UV 4692
+#define BAT_CURRENT_UNIT_UA 5870
+#define CHG_VOLTAGE_UINT_UV 23474
+#define CHG_MIN_CURRENT_UA 3500
+
+#define COULOMB_TO_UAH(c) (10000 * c / 36)
+
+enum chg_setting {
+       TRICKLE_CHG_EN,
+       LOW_POWER_BOOT_ACK,
+       BAT_TH_CHECK_DIS,
+       BATTFET_CTL_EN,
+       BATTFET_CTL,
+       REV_MOD_EN,
+       PLIM_DIS,
+       CHG_LED_EN,
+       RESTART_CHG_STAT,
+       AUTO_CHG_DIS,
+       CYCLING_DIS,
+       VI_PROGRAM_EN
+};
+
+/* Flag used to indicate if Charger workaround is active. */
+int chg_wa_is_active;
+/* Flag used to indicate if Charger workaround timer is on. */
+int chg_wa_timer;
+int disable_chg_timer;
+struct workqueue_struct *chg_wq;
+struct delayed_work chg_work;
+
+static int pmic_set_chg_current(unsigned short curr)
+{
+       unsigned int mask;
+       unsigned int value;
+
+       value = BITFVAL(BIT_CHG_CURR, curr);
+       mask = BITFMASK(BIT_CHG_CURR);
+       CHECK_ERROR(pmic_write_reg(REG_CHARGE, value, mask));
+
+       return 0;
+}
+
+static int pmic_set_chg_misc(enum chg_setting type, unsigned short flag)
+{
+
+       unsigned int reg_value = 0;
+       unsigned int mask = 0;
+
+       switch (type) {
+       case TRICKLE_CHG_EN:
+               reg_value = BITFVAL(TRICKLE_CHG_EN, flag);
+               mask = BITFMASK(TRICKLE_CHG_EN);
+               break;
+       case LOW_POWER_BOOT_ACK:
+               reg_value = BITFVAL(LOW_POWER_BOOT_ACK, flag);
+               mask = BITFMASK(LOW_POWER_BOOT_ACK);
+               break;
+       case BAT_TH_CHECK_DIS:
+               reg_value = BITFVAL(BAT_TH_CHECK_DIS, flag);
+               mask = BITFMASK(BAT_TH_CHECK_DIS);
+               break;
+       case BATTFET_CTL_EN:
+               reg_value = BITFVAL(BATTFET_CTL_EN, flag);
+               mask = BITFMASK(BATTFET_CTL_EN);
+               break;
+       case BATTFET_CTL:
+               reg_value = BITFVAL(BATTFET_CTL, flag);
+               mask = BITFMASK(BATTFET_CTL);
+               break;
+       case REV_MOD_EN:
+               reg_value = BITFVAL(REV_MOD_EN, flag);
+               mask = BITFMASK(REV_MOD_EN);
+               break;
+       case PLIM_DIS:
+               reg_value = BITFVAL(PLIM_DIS, flag);
+               mask = BITFMASK(PLIM_DIS);
+               break;
+       case CHG_LED_EN:
+               reg_value = BITFVAL(CHG_LED_EN, flag);
+               mask = BITFMASK(CHG_LED_EN);
+               break;
+       case RESTART_CHG_STAT:
+               reg_value = BITFVAL(RESTART_CHG_STAT, flag);
+               mask = BITFMASK(RESTART_CHG_STAT);
+               break;
+       case AUTO_CHG_DIS:
+               reg_value = BITFVAL(AUTO_CHG_DIS, flag);
+               mask = BITFMASK(AUTO_CHG_DIS);
+               break;
+       case CYCLING_DIS:
+               reg_value = BITFVAL(CYCLING_DIS, flag);
+               mask = BITFMASK(CYCLING_DIS);
+               break;
+       case VI_PROGRAM_EN:
+               reg_value = BITFVAL(VI_PROGRAM_EN, flag);
+               mask = BITFMASK(VI_PROGRAM_EN);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_write_reg(REG_CHARGE, reg_value, mask));
+
+       return 0;
+}
+
+static int pmic_get_batt_voltage(unsigned short *voltage)
+{
+       t_channel channel;
+       unsigned short result[8];
+
+       channel = BATTERY_VOLTAGE;
+       CHECK_ERROR(pmic_adc_convert(channel, result));
+       *voltage = result[0];
+
+       return 0;
+}
+
+static int pmic_get_batt_current(unsigned short *curr)
+{
+       t_channel channel;
+       unsigned short result[8];
+
+       channel = BATTERY_CURRENT;
+       CHECK_ERROR(pmic_adc_convert(channel, result));
+       *curr = result[0];
+
+       return 0;
+}
+
+static int coulomb_counter_calibration;
+static unsigned int coulomb_counter_start_time_msecs;
+
+static int pmic_start_coulomb_counter(void)
+{
+       /* set scaler */
+       CHECK_ERROR(pmic_write_reg(REG_ACC1,
+               ACC_COULOMB_PER_LSB * ACC_ONEC_VALUE, BITFMASK(ACC1_ONEC)));
+
+       CHECK_ERROR(pmic_write_reg(
+               REG_ACC0, ACC_START_COUNTER, ACC_CONTROL_BIT_MASK));
+       coulomb_counter_start_time_msecs = jiffies_to_msecs(jiffies);
+       pr_debug("coulomb counter start time %u\n",
+               coulomb_counter_start_time_msecs);
+       return 0;
+}
+
+static int pmic_stop_coulomb_counter(void)
+{
+       CHECK_ERROR(pmic_write_reg(
+               REG_ACC0, ACC_STOP_COUNTER, ACC_CONTROL_BIT_MASK));
+       return 0;
+}
+
+static int pmic_calibrate_coulomb_counter(void)
+{
+       int ret;
+       unsigned int value;
+
+       /* set scaler */
+       CHECK_ERROR(pmic_write_reg(REG_ACC1,
+               0x1, BITFMASK(ACC1_ONEC)));
+
+       CHECK_ERROR(pmic_write_reg(
+               REG_ACC0, ACC_CALIBRATION, ACC_CONTROL_BIT_MASK));
+       msleep(ACC_CALIBRATION_DURATION_MSECS);
+
+       ret = pmic_read_reg(REG_ACC0, &value, BITFMASK(ACC_CCOUT));
+       if (ret != 0)
+               return -1;
+       value = BITFEXT(value, ACC_CCOUT);
+       pr_debug("calibrate value = %x\n", value);
+       coulomb_counter_calibration = (int)((s16)((u16) value));
+       pr_debug("coulomb_counter_calibration = %d\n",
+               coulomb_counter_calibration);
+
+       return 0;
+
+}
+
+static int pmic_get_charger_coulomb(int *coulomb)
+{
+       int ret;
+       unsigned int value;
+       int calibration;
+       unsigned int time_diff_msec;
+
+       ret = pmic_read_reg(REG_ACC0, &value, BITFMASK(ACC_CCOUT));
+       if (ret != 0)
+               return -1;
+       value = BITFEXT(value, ACC_CCOUT);
+       pr_debug("counter value = %x\n", value);
+       *coulomb = ((s16)((u16)value)) * ACC_COULOMB_PER_LSB;
+
+       if (abs(*coulomb) >= ACC_COULOMB_PER_LSB) {
+                       /* calibrate */
+               time_diff_msec = jiffies_to_msecs(jiffies);
+               time_diff_msec =
+                       (time_diff_msec > coulomb_counter_start_time_msecs) ?
+                       (time_diff_msec - coulomb_counter_start_time_msecs) :
+                       (0xffffffff - coulomb_counter_start_time_msecs
+                       + time_diff_msec);
+               calibration = coulomb_counter_calibration * (int)time_diff_msec
+                       / (ACC_ONEC_VALUE * ACC_CALIBRATION_DURATION_MSECS);
+               *coulomb -= calibration;
+       }
+
+       return 0;
+}
+
+static int pmic_restart_charging(void)
+{
+       pmic_set_chg_misc(BAT_TH_CHECK_DIS, 1);
+       pmic_set_chg_misc(AUTO_CHG_DIS, 0);
+       pmic_set_chg_misc(VI_PROGRAM_EN, 1);
+       pmic_set_chg_current(0x8);
+       pmic_set_chg_misc(RESTART_CHG_STAT, 1);
+       pmic_set_chg_misc(PLIM_DIS, 3);
+       return 0;
+}
+
+struct mc13892_dev_info {
+       struct device *dev;
+
+       unsigned short voltage_raw;
+       int voltage_uV;
+       unsigned short current_raw;
+       int current_uA;
+       int battery_status;
+       int full_counter;
+       int charger_online;
+       int charger_voltage_uV;
+       int accum_current_uAh;
+
+       struct power_supply bat;
+       struct power_supply charger;
+
+       struct workqueue_struct *monitor_wqueue;
+       struct delayed_work monitor_work;
+};
+
+#define mc13892_SENSER 25
+#define to_mc13892_dev_info(x) container_of((x), struct mc13892_dev_info, \
+                                             bat);
+
+static enum power_supply_property mc13892_battery_props[] = {
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_CHARGE_NOW,
+       POWER_SUPPLY_PROP_STATUS,
+};
+
+static enum power_supply_property mc13892_charger_props[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+};
+
+static int pmic_get_chg_value(unsigned int *value)
+{
+       t_channel channel;
+       unsigned short result[8], max1 = 0, min1 = 0, max2 = 0, min2 = 0, i;
+       unsigned int average = 0, average1 = 0, average2 = 0;
+
+       channel = CHARGE_CURRENT;
+       CHECK_ERROR(pmic_adc_convert(channel, result));
+
+
+       for (i = 0; i < 8; i++) {
+               if ((result[i] & 0x200) != 0) {
+                       result[i] = 0x400 - result[i];
+                       average2 += result[i];
+                       if ((max2 == 0) || (max2 < result[i]))
+                               max2 = result[i];
+                       if ((min2 == 0) || (min2 > result[i]))
+                               min2 = result[i];
+               } else {
+                       average1 += result[i];
+                       if ((max1 == 0) || (max1 < result[i]))
+                               max1 = result[i];
+                       if ((min1 == 0) || (min1 > result[i]))
+                               min1 = result[i];
+               }
+       }
+
+       if (max1 != 0) {
+               average1 -= max1;
+               if (max2 != 0)
+                       average2 -= max2;
+               else
+                       average1 -= min1;
+       } else
+               average2 -= max2 + min2;
+
+       if (average1 >= average2) {
+               average = (average1 - average2) / 6;
+               *value = average;
+       } else {
+               average = (average2 - average1) / 6;
+               *value = ((~average) + 1) & 0x3FF;
+       }
+
+       return 0;
+}
+
+static void chg_thread(struct work_struct *work)
+{
+       int ret;
+       unsigned int value = 0;
+       int dets;
+
+       if (disable_chg_timer) {
+               disable_chg_timer = 0;
+               pmic_set_chg_current(0x8);
+               queue_delayed_work(chg_wq, &chg_work, 100);
+               chg_wa_timer = 1;
+               return;
+       }
+
+       ret = pmic_read_reg(REG_INT_SENSE0, &value, BITFMASK(BIT_CHG_DETS));
+
+       if (ret == 0) {
+               dets = BITFEXT(value, BIT_CHG_DETS);
+               pr_debug("dets=%d\n", dets);
+
+               if (dets == 1) {
+                       pmic_get_chg_value(&value);
+                       pr_debug("average value=%d\n", value);
+                       if ((value <= 3) | ((value & 0x200) != 0)) {
+                               pr_debug("%s: Disable the charger\n", __func__);
+                               pmic_set_chg_current(0);
+                               disable_chg_timer = 1;
+                       }
+
+                       queue_delayed_work(chg_wq, &chg_work, 100);
+                       chg_wa_timer = 1;
+               }
+       }
+}
+
+static int mc13892_charger_update_status(struct mc13892_dev_info *di)
+{
+       int ret;
+       unsigned int value;
+       int online;
+
+       ret = pmic_read_reg(REG_INT_SENSE0, &value, BITFMASK(BIT_CHG_DETS));
+
+       if (ret == 0) {
+               online = BITFEXT(value, BIT_CHG_DETS);
+               if (online != di->charger_online) {
+                       di->charger_online = online;
+                       dev_info(di->charger.dev, "charger status: %s\n",
+                               online ? "online" : "offline");
+                       power_supply_changed(&di->charger);
+
+                       cancel_delayed_work(&di->monitor_work);
+                       queue_delayed_work(di->monitor_wqueue,
+                               &di->monitor_work, HZ / 10);
+                       if (online) {
+                               pmic_start_coulomb_counter();
+                               pmic_restart_charging();
+                               queue_delayed_work(chg_wq, &chg_work, 100);
+                               chg_wa_timer = 1;
+                       } else {
+                               cancel_delayed_work(&chg_work);
+                               chg_wa_timer = 0;
+                               pmic_stop_coulomb_counter();
+               }
+       }
+       }
+
+       return ret;
+}
+
+static int mc13892_charger_get_property(struct power_supply *psy,
+                                      enum power_supply_property psp,
+                                      union power_supply_propval *val)
+{
+       struct mc13892_dev_info *di =
+               container_of((psy), struct mc13892_dev_info, charger);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = di->charger_online;
+               return 0;
+       default:
+               break;
+       }
+       return -EINVAL;
+}
+
+static int mc13892_battery_read_status(struct mc13892_dev_info *di)
+{
+       int retval;
+       int coulomb;
+       retval = pmic_get_batt_voltage(&(di->voltage_raw));
+       if (retval == 0)
+               di->voltage_uV = di->voltage_raw * BAT_VOLTAGE_UNIT_UV;
+
+       retval = pmic_get_batt_current(&(di->current_raw));
+       if (retval == 0) {
+               if (di->current_raw & 0x200)
+                       di->current_uA =
+                               (0x1FF - (di->current_raw & 0x1FF)) *
+                               BAT_CURRENT_UNIT_UA * (-1);
+               else
+                       di->current_uA =
+                               (di->current_raw & 0x1FF) * BAT_CURRENT_UNIT_UA;
+       }
+       retval = pmic_get_charger_coulomb(&coulomb);
+       if (retval == 0)
+               di->accum_current_uAh = COULOMB_TO_UAH(coulomb);
+
+       return retval;
+}
+
+static void mc13892_battery_update_status(struct mc13892_dev_info *di)
+{
+       unsigned int value;
+       int retval;
+       int old_battery_status = di->battery_status;
+
+       if (di->battery_status == POWER_SUPPLY_STATUS_UNKNOWN)
+               di->full_counter = 0;
+
+       if (di->charger_online) {
+               retval = pmic_read_reg(REG_INT_SENSE0,
+                                       &value, BITFMASK(BIT_CHG_CURRS));
+
+               if (retval == 0) {
+                       value = BITFEXT(value, BIT_CHG_CURRS);
+                       if (value)
+                               di->battery_status =
+                                       POWER_SUPPLY_STATUS_CHARGING;
+                       else
+                               di->battery_status =
+                                       POWER_SUPPLY_STATUS_NOT_CHARGING;
+               }
+
+               if (di->battery_status == POWER_SUPPLY_STATUS_NOT_CHARGING)
+                       di->full_counter++;
+               else
+                       di->full_counter = 0;
+       } else {
+               di->battery_status = POWER_SUPPLY_STATUS_DISCHARGING;
+               di->full_counter = 0;
+       }
+
+       dev_dbg(di->bat.dev, "bat status: %d\n",
+               di->battery_status);
+
+       if (old_battery_status != POWER_SUPPLY_STATUS_UNKNOWN &&
+               di->battery_status != old_battery_status)
+               power_supply_changed(&di->bat);
+}
+
+static void mc13892_battery_work(struct work_struct *work)
+{
+       struct mc13892_dev_info *di = container_of(work,
+                                                    struct mc13892_dev_info,
+                                                    monitor_work.work);
+       const int interval = HZ * 60;
+
+       dev_dbg(di->dev, "%s\n", __func__);
+
+       mc13892_battery_update_status(di);
+       queue_delayed_work(di->monitor_wqueue, &di->monitor_work, interval);
+}
+
+static void charger_online_event_callback(void *para)
+{
+       struct mc13892_dev_info *di = (struct mc13892_dev_info *) para;
+       pr_info("\n\n DETECTED charger plug/unplug event\n");
+       mc13892_charger_update_status(di);
+}
+
+
+static int mc13892_battery_get_property(struct power_supply *psy,
+                                      enum power_supply_property psp,
+                                      union power_supply_propval *val)
+{
+       struct mc13892_dev_info *di = to_mc13892_dev_info(psy);
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               if (di->battery_status == POWER_SUPPLY_STATUS_UNKNOWN) {
+                       mc13892_charger_update_status(di);
+                       mc13892_battery_update_status(di);
+               }
+               val->intval = di->battery_status;
+               return 0;
+       default:
+               break;
+       }
+
+       mc13892_battery_read_status(di);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               val->intval = di->voltage_uV;
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_NOW:
+               val->intval = di->current_uA;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_NOW:
+               val->intval = di->accum_current_uAh;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+               val->intval = 3800000;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+               val->intval = 3300000;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static ssize_t chg_wa_enable_show(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       if (chg_wa_is_active & chg_wa_timer)
+               return sprintf(buf, "Charger LED workaround timer is on\n");
+       else
+               return sprintf(buf, "Charger LED workaround timer is off\n");
+}
+
+static ssize_t chg_wa_enable_store(struct device *dev,
+                                struct device_attribute *attr,
+                                const char *buf, size_t size)
+{
+       if (strstr(buf, "1") != NULL) {
+               if (chg_wa_is_active) {
+                       if (chg_wa_timer)
+                               printk(KERN_INFO "Charger timer is already on\n");
+                       else {
+                               queue_delayed_work(chg_wq, &chg_work, 100);
+                               chg_wa_timer = 1;
+                               printk(KERN_INFO "Turned on the timer\n");
+                       }
+               }
+       } else if (strstr(buf, "0") != NULL) {
+               if (chg_wa_is_active) {
+                       if (chg_wa_timer) {
+                               cancel_delayed_work(&chg_work);
+                               chg_wa_timer = 0;
+                               printk(KERN_INFO "Turned off charger timer\n");
+                        } else {
+                               printk(KERN_INFO "The Charger workaround timer is off\n");
+                       }
+               }
+       }
+
+       return size;
+}
+
+static DEVICE_ATTR(enable, 0644, chg_wa_enable_show, chg_wa_enable_store);
+
+static int pmic_battery_remove(struct platform_device *pdev)
+{
+       pmic_event_callback_t bat_event_callback;
+       struct mc13892_dev_info *di = platform_get_drvdata(pdev);
+
+       bat_event_callback.func = charger_online_event_callback;
+       bat_event_callback.param = (void *) di;
+       pmic_event_unsubscribe(EVENT_CHGDETI, bat_event_callback);
+
+       cancel_rearming_delayed_workqueue(di->monitor_wqueue,
+                                         &di->monitor_work);
+       cancel_rearming_delayed_workqueue(chg_wq,
+                                         &chg_work);
+       destroy_workqueue(di->monitor_wqueue);
+       destroy_workqueue(chg_wq);
+       chg_wa_timer = 0;
+       chg_wa_is_active = 0;
+       disable_chg_timer = 0;
+       power_supply_unregister(&di->bat);
+       power_supply_unregister(&di->charger);
+
+       kfree(di);
+
+       return 0;
+}
+
+static int pmic_battery_probe(struct platform_device *pdev)
+{
+       int retval = 0;
+       struct mc13892_dev_info *di;
+       pmic_event_callback_t bat_event_callback;
+       pmic_version_t pmic_version;
+
+       /* Only apply battery driver for MC13892 V2.0 due to ENGR108085 */
+       pmic_version = pmic_get_version();
+       if (pmic_version.revision < 20) {
+               pr_debug("Battery driver is only applied for MC13892 V2.0\n");
+               return -1;
+       }
+       if (machine_is_mx50_arm2()) {
+               pr_debug("mc13892 charger is not used for this platform\n");
+               return -1;
+       }
+
+       di = kzalloc(sizeof(*di), GFP_KERNEL);
+       if (!di) {
+               retval = -ENOMEM;
+               goto di_alloc_failed;
+       }
+
+       platform_set_drvdata(pdev, di);
+
+       di->charger.name        = "mc13892_charger";
+       di->charger.type = POWER_SUPPLY_TYPE_MAINS;
+       di->charger.properties = mc13892_charger_props;
+       di->charger.num_properties = ARRAY_SIZE(mc13892_charger_props);
+       di->charger.get_property = mc13892_charger_get_property;
+       retval = power_supply_register(&pdev->dev, &di->charger);
+       if (retval) {
+               dev_err(di->dev, "failed to register charger\n");
+               goto charger_failed;
+       }
+
+       INIT_DELAYED_WORK(&chg_work, chg_thread);
+       chg_wq = create_singlethread_workqueue("mxc_chg");
+       if (!chg_wq) {
+               retval = -ESRCH;
+               goto workqueue_failed;
+       }
+
+       INIT_DELAYED_WORK(&di->monitor_work, mc13892_battery_work);
+       di->monitor_wqueue = create_singlethread_workqueue(dev_name(&pdev->dev));
+       if (!di->monitor_wqueue) {
+               retval = -ESRCH;
+               goto workqueue_failed;
+       }
+       queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ * 10);
+
+       di->dev = &pdev->dev;
+       di->bat.name    = "mc13892_bat";
+       di->bat.type = POWER_SUPPLY_TYPE_BATTERY;
+       di->bat.properties = mc13892_battery_props;
+       di->bat.num_properties = ARRAY_SIZE(mc13892_battery_props);
+       di->bat.get_property = mc13892_battery_get_property;
+       di->bat.use_for_apm = 1;
+
+       di->battery_status = POWER_SUPPLY_STATUS_UNKNOWN;
+
+       retval = power_supply_register(&pdev->dev, &di->bat);
+       if (retval) {
+               dev_err(di->dev, "failed to register battery\n");
+               goto batt_failed;
+       }
+
+       bat_event_callback.func = charger_online_event_callback;
+       bat_event_callback.param = (void *) di;
+       pmic_event_subscribe(EVENT_CHGDETI, bat_event_callback);
+       retval = sysfs_create_file(&pdev->dev.kobj, &dev_attr_enable.attr);
+
+       if (retval) {
+               printk(KERN_ERR
+                      "Battery: Unable to register sysdev entry for Battery");
+               goto workqueue_failed;
+       }
+       chg_wa_is_active = 1;
+       chg_wa_timer = 0;
+       disable_chg_timer = 0;
+
+       pmic_stop_coulomb_counter();
+       pmic_calibrate_coulomb_counter();
+       goto success;
+
+workqueue_failed:
+       power_supply_unregister(&di->charger);
+charger_failed:
+       power_supply_unregister(&di->bat);
+batt_failed:
+       kfree(di);
+di_alloc_failed:
+success:
+       dev_dbg(di->dev, "%s battery probed!\n", __func__);
+       return retval;
+
+
+       return 0;
+}
+
+static struct platform_driver pmic_battery_driver_ldm = {
+       .driver = {
+                  .name = "pmic_battery",
+                  .bus = &platform_bus_type,
+                  },
+       .probe = pmic_battery_probe,
+       .remove = pmic_battery_remove,
+};
+
+static int __init pmic_battery_init(void)
+{
+       pr_debug("PMIC Battery driver loading...\n");
+       return platform_driver_register(&pmic_battery_driver_ldm);
+}
+
+static void __exit pmic_battery_exit(void)
+{
+       platform_driver_unregister(&pmic_battery_driver_ldm);
+       pr_debug("PMIC Battery driver successfully unloaded\n");
+}
+
+module_init(pmic_battery_init);
+module_exit(pmic_battery_exit);
+
+MODULE_DESCRIPTION("pmic_battery driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pmic/mc13892/pmic_light.c b/drivers/mxc/pmic/mc13892/pmic_light.c
new file mode 100644 (file)
index 0000000..1745933
--- /dev/null
@@ -0,0 +1,685 @@
+/*
+ * 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 mc13892/pmic_light.c
+ * @brief This is the main file of PMIC(mc13783) Light and Backlight driver.
+ *
+ * @ingroup PMIC_LIGHT
+ */
+
+/*
+ * Includes
+ */
+#define DEBUG
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/pmic_light.h>
+#include <linux/pmic_status.h>
+
+#define BIT_CL_MAIN_LSH                9
+#define BIT_CL_AUX_LSH         21
+#define BIT_CL_KEY_LSH         9
+#define BIT_CL_RED_LSH         9
+#define BIT_CL_GREEN_LSH       21
+#define BIT_CL_BLUE_LSH                9
+
+#define BIT_CL_MAIN_WID                3
+#define BIT_CL_AUX_WID         3
+#define BIT_CL_KEY_WID         3
+#define BIT_CL_RED_WID         3
+#define BIT_CL_GREEN_WID       3
+#define BIT_CL_BLUE_WID                3
+
+#define BIT_DC_MAIN_LSH                3
+#define BIT_DC_AUX_LSH         15
+#define BIT_DC_KEY_LSH         3
+#define BIT_DC_RED_LSH         3
+#define BIT_DC_GREEN_LSH       15
+#define BIT_DC_BLUE_LSH                3
+
+#define BIT_DC_MAIN_WID                6
+#define BIT_DC_AUX_WID         6
+#define BIT_DC_KEY_WID         6
+#define BIT_DC_RED_WID         6
+#define BIT_DC_GREEN_WID       6
+#define BIT_DC_BLUE_WID                6
+
+#define BIT_RP_MAIN_LSH                2
+#define BIT_RP_AUX_LSH         14
+#define BIT_RP_KEY_LSH         2
+#define BIT_RP_RED_LSH         2
+#define BIT_RP_GREEN_LSH       14
+#define BIT_RP_BLUE_LSH                2
+
+#define BIT_RP_MAIN_WID                1
+#define BIT_RP_AUX_WID         1
+#define BIT_RP_KEY_WID         1
+#define BIT_RP_RED_WID         1
+#define BIT_RP_GREEN_WID       1
+#define BIT_RP_BLUE_WID                1
+
+#define BIT_HC_MAIN_LSH                1
+#define BIT_HC_AUX_LSH         13
+#define BIT_HC_KEY_LSH         1
+
+#define BIT_HC_MAIN_WID                1
+#define BIT_HC_AUX_WID         1
+#define BIT_HC_KEY_WID         1
+
+#define BIT_BP_RED_LSH         0
+#define BIT_BP_GREEN_LSH       12
+#define BIT_BP_BLUE_LSH                0
+
+#define BIT_BP_RED_WID         2
+#define BIT_BP_GREEN_WID       2
+#define BIT_BP_BLUE_WID                2
+
+int pmic_light_init_reg(void)
+{
+       CHECK_ERROR(pmic_write_reg(REG_LED_CTL0, 0, PMIC_ALL_BITS));
+       CHECK_ERROR(pmic_write_reg(REG_LED_CTL1, 0, PMIC_ALL_BITS));
+       CHECK_ERROR(pmic_write_reg(REG_LED_CTL2, 0, PMIC_ALL_BITS));
+       CHECK_ERROR(pmic_write_reg(REG_LED_CTL3, 0, PMIC_ALL_BITS));
+
+       return 0;
+}
+
+static int pmic_light_suspend(struct platform_device *dev, pm_message_t state)
+{
+       return 0;
+};
+
+static int pmic_light_resume(struct platform_device *pdev)
+{
+       return 0;
+};
+
+PMIC_STATUS mc13892_bklit_set_hi_current(enum lit_channel channel, int mode)
+{
+       unsigned int mask;
+       unsigned int value;
+       int reg;
+
+       switch (channel) {
+       case LIT_MAIN:
+               value = BITFVAL(BIT_HC_MAIN, mode);
+               mask = BITFMASK(BIT_HC_MAIN);
+               reg = REG_LED_CTL0;
+               break;
+       case LIT_AUX:
+               value = BITFVAL(BIT_HC_AUX, mode);
+               mask = BITFMASK(BIT_HC_AUX);
+               reg = REG_LED_CTL0;
+               break;
+       case LIT_KEY:
+               value = BITFVAL(BIT_HC_KEY, mode);
+               mask = BITFMASK(BIT_HC_KEY);
+               reg = REG_LED_CTL1;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+       CHECK_ERROR(pmic_write_reg(reg, value, mask));
+       return PMIC_SUCCESS;
+}
+
+PMIC_STATUS mc13892_bklit_get_hi_current(enum lit_channel channel, int *mode)
+{
+       unsigned int mask;
+       int reg;
+
+       switch (channel) {
+       case LIT_MAIN:
+               mask = BITFMASK(BIT_HC_MAIN);
+               reg = REG_LED_CTL0;
+               break;
+       case LIT_AUX:
+               mask = BITFMASK(BIT_HC_AUX);
+               reg = REG_LED_CTL0;
+               break;
+       case LIT_KEY:
+               mask = BITFMASK(BIT_HC_KEY);
+               reg = REG_LED_CTL1;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_read_reg(reg, mode, mask));
+       return PMIC_SUCCESS;
+}
+
+PMIC_STATUS mc13892_bklit_set_current(enum lit_channel channel,
+                                     unsigned char level)
+{
+       unsigned int mask;
+       unsigned int value;
+       int reg;
+
+       if (level > LIT_CURR_HI_42)
+               return PMIC_PARAMETER_ERROR;
+       else if (level >= LIT_CURR_HI_0) {
+               CHECK_ERROR(mc13892_bklit_set_hi_current(channel, 1));
+               level -= LIT_CURR_HI_0;
+       }
+
+       switch (channel) {
+       case LIT_MAIN:
+               value = BITFVAL(BIT_CL_MAIN, level);
+               mask = BITFMASK(BIT_CL_MAIN);
+               reg = REG_LED_CTL0;
+               break;
+       case LIT_AUX:
+               value = BITFVAL(BIT_CL_AUX, level);
+               mask = BITFMASK(BIT_CL_AUX);
+               reg = REG_LED_CTL0;
+               break;
+       case LIT_KEY:
+               value = BITFVAL(BIT_CL_KEY, level);
+               mask = BITFMASK(BIT_CL_KEY);
+               reg = REG_LED_CTL1;
+               break;
+       case LIT_RED:
+               value = BITFVAL(BIT_CL_RED, level);
+               mask = BITFMASK(BIT_CL_RED);
+               reg = REG_LED_CTL2;
+               break;
+       case LIT_GREEN:
+               value = BITFVAL(BIT_CL_GREEN, level);
+               mask = BITFMASK(BIT_CL_GREEN);
+               reg = REG_LED_CTL2;
+               break;
+       case LIT_BLUE:
+               value = BITFVAL(BIT_CL_BLUE, level);
+               mask = BITFMASK(BIT_CL_BLUE);
+               reg = REG_LED_CTL3;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+       CHECK_ERROR(pmic_write_reg(reg, value, mask));
+
+       return PMIC_SUCCESS;
+}
+
+PMIC_STATUS mc13892_bklit_get_current(enum lit_channel channel,
+                                     unsigned char *level)
+{
+       unsigned int reg_value = 0;
+       unsigned int mask = 0;
+       int reg, mode;
+
+       CHECK_ERROR(mc13892_bklit_get_hi_current(channel, &mode));
+
+       switch (channel) {
+       case LIT_MAIN:
+               mask = BITFMASK(BIT_CL_MAIN);
+               reg = REG_LED_CTL0;
+               break;
+       case LIT_AUX:
+               mask = BITFMASK(BIT_CL_AUX);
+               reg = REG_LED_CTL0;
+               break;
+       case LIT_KEY:
+               mask = BITFMASK(BIT_CL_KEY);
+               reg = REG_LED_CTL1;
+               break;
+       case LIT_RED:
+               mask = BITFMASK(BIT_CL_RED);
+               reg = REG_LED_CTL2;
+               break;
+       case LIT_GREEN:
+               mask = BITFMASK(BIT_CL_GREEN);
+               reg = REG_LED_CTL2;
+               break;
+       case LIT_BLUE:
+               mask = BITFMASK(BIT_CL_BLUE);
+               reg = REG_LED_CTL3;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_read_reg(reg, &reg_value, mask));
+
+       switch (channel) {
+       case LIT_MAIN:
+               *level = BITFEXT(reg_value, BIT_CL_MAIN);
+               break;
+       case LIT_AUX:
+               *level = BITFEXT(reg_value, BIT_CL_AUX);
+               break;
+       case LIT_KEY:
+               *level = BITFEXT(reg_value, BIT_CL_KEY);
+               break;
+       case LIT_RED:
+               *level = BITFEXT(reg_value, BIT_CL_RED);
+               break;
+       case LIT_GREEN:
+               *level = BITFEXT(reg_value, BIT_CL_GREEN);
+               break;
+       case LIT_BLUE:
+               *level = BITFEXT(reg_value, BIT_CL_BLUE);
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       if (mode == 1)
+               *level += LIT_CURR_HI_0;
+
+       return PMIC_SUCCESS;
+}
+
+PMIC_STATUS mc13892_bklit_set_dutycycle(enum lit_channel channel,
+                                       unsigned char dc)
+{
+       unsigned int mask;
+       unsigned int value;
+       int reg;
+
+       switch (channel) {
+       case LIT_MAIN:
+               value = BITFVAL(BIT_DC_MAIN, dc);
+               mask = BITFMASK(BIT_DC_MAIN);
+               reg = REG_LED_CTL0;
+               break;
+       case LIT_AUX:
+               value = BITFVAL(BIT_DC_AUX, dc);
+               mask = BITFMASK(BIT_DC_AUX);
+               reg = REG_LED_CTL0;
+               break;
+       case LIT_KEY:
+               value = BITFVAL(BIT_DC_KEY, dc);
+               mask = BITFMASK(BIT_DC_KEY);
+               reg = REG_LED_CTL1;
+               break;
+       case LIT_RED:
+               value = BITFVAL(BIT_DC_RED, dc);
+               mask = BITFMASK(BIT_DC_RED);
+               reg = REG_LED_CTL2;
+               break;
+       case LIT_GREEN:
+               value = BITFVAL(BIT_DC_GREEN, dc);
+               mask = BITFMASK(BIT_DC_GREEN);
+               reg = REG_LED_CTL2;
+               break;
+       case LIT_BLUE:
+               value = BITFVAL(BIT_DC_BLUE, dc);
+               mask = BITFMASK(BIT_DC_BLUE);
+               reg = REG_LED_CTL3;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+       CHECK_ERROR(pmic_write_reg(reg, value, mask));
+       return PMIC_SUCCESS;
+}
+
+PMIC_STATUS mc13892_bklit_get_dutycycle(enum lit_channel channel,
+                                       unsigned char *dc)
+{
+       unsigned int mask;
+       int reg;
+       unsigned int reg_value = 0;
+
+       switch (channel) {
+       case LIT_MAIN:
+               mask = BITFMASK(BIT_DC_MAIN);
+               reg = REG_LED_CTL0;
+               break;
+       case LIT_AUX:
+               mask = BITFMASK(BIT_DC_AUX);
+               reg = REG_LED_CTL0;
+               break;
+       case LIT_KEY:
+               mask = BITFMASK(BIT_DC_KEY);
+               reg = REG_LED_CTL1;
+               break;
+       case LIT_RED:
+               mask = BITFMASK(BIT_DC_RED);
+               reg = REG_LED_CTL2;
+               break;
+       case LIT_GREEN:
+               mask = BITFMASK(BIT_DC_GREEN);
+               reg = REG_LED_CTL2;
+               break;
+       case LIT_BLUE:
+               mask = BITFMASK(BIT_DC_BLUE);
+               reg = REG_LED_CTL3;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_read_reg(reg, &reg_value, mask));
+       return PMIC_SUCCESS;
+}
+
+PMIC_STATUS mc13892_bklit_set_ramp(enum lit_channel channel, int flag)
+{
+       unsigned int mask;
+       unsigned int value;
+       int reg;
+
+       switch (channel) {
+       case LIT_MAIN:
+               value = BITFVAL(BIT_RP_MAIN, flag);
+               mask = BITFMASK(BIT_RP_MAIN);
+               reg = REG_LED_CTL0;
+               break;
+       case LIT_AUX:
+               value = BITFVAL(BIT_RP_AUX, flag);
+               mask = BITFMASK(BIT_RP_AUX);
+               reg = REG_LED_CTL0;
+               break;
+       case LIT_KEY:
+               value = BITFVAL(BIT_RP_KEY, flag);
+               mask = BITFMASK(BIT_RP_KEY);
+               reg = REG_LED_CTL1;
+               break;
+       case LIT_RED:
+               value = BITFVAL(BIT_RP_RED, flag);
+               mask = BITFMASK(BIT_RP_RED);
+               reg = REG_LED_CTL2;
+               break;
+       case LIT_GREEN:
+               value = BITFVAL(BIT_RP_GREEN, flag);
+               mask = BITFMASK(BIT_RP_GREEN);
+               reg = REG_LED_CTL2;
+               break;
+       case LIT_BLUE:
+               value = BITFVAL(BIT_RP_BLUE, flag);
+               mask = BITFMASK(BIT_RP_BLUE);
+               reg = REG_LED_CTL3;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+       CHECK_ERROR(pmic_write_reg(reg, value, mask));
+       return PMIC_SUCCESS;
+}
+
+PMIC_STATUS mc13892_bklit_get_ramp(enum lit_channel channel, int *flag)
+{
+       unsigned int mask;
+       int reg;
+
+       switch (channel) {
+       case LIT_MAIN:
+               mask = BITFMASK(BIT_RP_MAIN);
+               reg = REG_LED_CTL0;
+               break;
+       case LIT_AUX:
+               mask = BITFMASK(BIT_RP_AUX);
+               reg = REG_LED_CTL0;
+               break;
+       case LIT_KEY:
+               mask = BITFMASK(BIT_RP_KEY);
+               reg = REG_LED_CTL1;
+               break;
+       case LIT_RED:
+               mask = BITFMASK(BIT_RP_RED);
+               reg = REG_LED_CTL2;
+               break;
+       case LIT_GREEN:
+               mask = BITFMASK(BIT_RP_GREEN);
+               reg = REG_LED_CTL2;
+               break;
+       case LIT_BLUE:
+               mask = BITFMASK(BIT_RP_BLUE);
+               reg = REG_LED_CTL3;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_read_reg(reg, flag, mask));
+       return PMIC_SUCCESS;
+}
+
+PMIC_STATUS mc13892_bklit_set_blink_p(enum lit_channel channel, int period)
+{
+       unsigned int mask;
+       unsigned int value;
+       int reg;
+
+       switch (channel) {
+       case LIT_RED:
+               value = BITFVAL(BIT_BP_RED, period);
+               mask = BITFMASK(BIT_BP_RED);
+               reg = REG_LED_CTL2;
+               break;
+       case LIT_GREEN:
+               value = BITFVAL(BIT_BP_GREEN, period);
+               mask = BITFMASK(BIT_BP_GREEN);
+               reg = REG_LED_CTL2;
+               break;
+       case LIT_BLUE:
+               value = BITFVAL(BIT_BP_BLUE, period);
+               mask = BITFMASK(BIT_BP_BLUE);
+               reg = REG_LED_CTL3;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+       CHECK_ERROR(pmic_write_reg(reg, value, mask));
+       return PMIC_SUCCESS;
+}
+
+PMIC_STATUS mc13892_bklit_get_blink_p(enum lit_channel channel, int *period)
+{
+       unsigned int mask;
+       int reg;
+
+       switch (channel) {
+       case LIT_RED:
+               mask = BITFMASK(BIT_BP_RED);
+               reg = REG_LED_CTL2;
+               break;
+       case LIT_GREEN:
+               mask = BITFMASK(BIT_BP_GREEN);
+               reg = REG_LED_CTL2;
+               break;
+       case LIT_BLUE:
+               mask = BITFMASK(BIT_BP_BLUE);
+               reg = REG_LED_CTL3;
+               break;
+       default:
+               return PMIC_PARAMETER_ERROR;
+       }
+
+       CHECK_ERROR(pmic_read_reg(reg, period, mask));
+       return PMIC_SUCCESS;
+}
+
+EXPORT_SYMBOL(mc13892_bklit_set_current);
+EXPORT_SYMBOL(mc13892_bklit_get_current);
+EXPORT_SYMBOL(mc13892_bklit_set_dutycycle);
+EXPORT_SYMBOL(mc13892_bklit_get_dutycycle);
+EXPORT_SYMBOL(mc13892_bklit_set_ramp);
+EXPORT_SYMBOL(mc13892_bklit_get_ramp);
+EXPORT_SYMBOL(mc13892_bklit_set_blink_p);
+EXPORT_SYMBOL(mc13892_bklit_get_blink_p);
+
+static int pmic_light_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+
+#ifdef DEBUG
+static ssize_t lit_info(struct device *dev, struct device_attribute *attr,
+                       char *buf)
+{
+       return 0;
+}
+
+enum {
+       SET_CURR = 0,
+       SET_DC,
+       SET_RAMP,
+       SET_BP,
+       SET_CH,
+       LIT_CMD_MAX
+};
+
+static const char *const lit_cmd[LIT_CMD_MAX] = {
+       [SET_CURR] = "cur",
+       [SET_DC] = "dc",
+       [SET_RAMP] = "ra",
+       [SET_BP] = "bp",
+       [SET_CH] = "ch"
+};
+
+static int cmd(unsigned int index, int value)
+{
+       static int ch = LIT_MAIN;
+       int ret = 0;
+
+       switch (index) {
+       case SET_CH:
+               ch = value;
+               break;
+       case SET_CURR:
+               pr_debug("set %d cur %d\n", ch, value);
+               ret = mc13892_bklit_set_current(ch, value);
+               break;
+       case SET_DC:
+               pr_debug("set %d dc %d\n", ch, value);
+               ret = mc13892_bklit_set_dutycycle(ch, value);
+               break;
+       case SET_RAMP:
+               pr_debug("set %d ramp %d\n", ch, value);
+               ret = mc13892_bklit_set_ramp(ch, value);
+               break;
+       case SET_BP:
+               pr_debug("set %d bp %d\n", ch, value);
+               ret = mc13892_bklit_set_blink_p(ch, value);
+               break;
+       default:
+               pr_debug("error command\n");
+               break;
+       }
+
+       if (ret == PMIC_SUCCESS)
+               pr_debug("command exec successfully!\n");
+
+       return 0;
+}
+
+static ssize_t lit_ctl(struct device *dev, struct device_attribute *attr,
+                      const char *buf, size_t count)
+{
+       int state = 0;
+       const char *const *s;
+       char *p, *q;
+       int error;
+       int len, value = 0;
+
+       pr_debug("lit_ctl\n");
+
+       q = NULL;
+       q = memchr(buf, ' ', count);
+
+       if (q != NULL) {
+               len = q - buf;
+               q += 1;
+               value = simple_strtoul(q, NULL, 10);
+       } else {
+               p = memchr(buf, '\n', count);
+               len = p ? p - buf : count;
+       }
+
+       for (s = &lit_cmd[state]; state < LIT_CMD_MAX; s++, state++) {
+               if (*s && !strncmp(buf, *s, len))
+                       break;
+       }
+       if (state < LIT_CMD_MAX && *s)
+               error = cmd(state, value);
+       else
+               error = -EINVAL;
+
+       return count;
+}
+
+#else
+static ssize_t lit_info(struct device *dev, struct device_attribute *attr,
+                       char *buf)
+{
+       return 0;
+}
+
+static ssize_t lit_ctl(struct device *dev, struct device_attribute *attr,
+                      const char *buf, size_t count)
+{
+       return count;
+}
+
+#endif
+
+static DEVICE_ATTR(lit, 0644, lit_info, lit_ctl);
+
+static int pmic_light_probe(struct platform_device *pdev)
+{
+       int ret = 0;
+
+       pr_debug("PMIC ADC start probe\n");
+       ret = device_create_file(&(pdev->dev), &dev_attr_lit);
+       if (ret) {
+               pr_debug("Can't create device file!\n");
+               return -ENODEV;
+       }
+
+       pmic_light_init_reg();
+
+       pr_debug("PMIC Light successfully loaded\n");
+       return 0;
+}
+
+static struct platform_driver pmic_light_driver_ldm = {
+       .driver = {
+                  .name = "pmic_light",
+                  },
+       .suspend = pmic_light_suspend,
+       .resume = pmic_light_resume,
+       .probe = pmic_light_probe,
+       .remove = pmic_light_remove,
+};
+
+/*
+ * Initialization and Exit
+ */
+
+static int __init pmic_light_init(void)
+{
+       pr_debug("PMIC Light driver loading...\n");
+       return platform_driver_register(&pmic_light_driver_ldm);
+}
+static void __exit pmic_light_exit(void)
+{
+       platform_driver_unregister(&pmic_light_driver_ldm);
+       pr_debug("PMIC Light driver successfully unloaded\n");
+}
+
+/*
+ * Module entry points
+ */
+
+subsys_initcall(pmic_light_init);
+module_exit(pmic_light_exit);
+
+MODULE_DESCRIPTION("PMIC_LIGHT");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/ssi/Kconfig b/drivers/mxc/ssi/Kconfig
new file mode 100644 (file)
index 0000000..4cb581c
--- /dev/null
@@ -0,0 +1,12 @@
+#
+# SPI device configuration
+#
+
+menu "MXC SSI support"
+
+config MXC_SSI
+       tristate "SSI support"
+       ---help---
+         Say Y to get the SSI services API available on MXC platform.
+
+endmenu
diff --git a/drivers/mxc/ssi/Makefile b/drivers/mxc/ssi/Makefile
new file mode 100644 (file)
index 0000000..f9bb441
--- /dev/null
@@ -0,0 +1,7 @@
+#
+# Makefile for the kernel SSI device drivers.
+#
+
+obj-$(CONFIG_MXC_SSI)          += ssimod.o
+
+ssimod-objs := ssi.o
diff --git a/drivers/mxc/ssi/registers.h b/drivers/mxc/ssi/registers.h
new file mode 100644 (file)
index 0000000..dd62365
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * 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 ../ssi/registers.h
+  * @brief This header file contains SSI driver low level definition to access module registers.
+  *
+  * @ingroup SSI
+  */
+
+#ifndef __MXC_SSI_REGISTERS_H__
+#define __MXC_SSI_REGISTERS_H__
+
+/*!
+ * This include to define bool type, false and true definitions.
+ */
+#include <mach/hardware.h>
+
+#define SPBA_CPU_SSI            0x07
+
+#define    MXC_SSISTX0         0x00
+#define    MXC_SSISTX1         0x04
+#define    MXC_SSISRX0         0x08
+#define    MXC_SSISRX1         0x0C
+#define    MXC_SSISCR           0x10
+#define    MXC_SSISISR          0x14
+#define    MXC_SSISIER         0x18
+#define    MXC_SSISTCR         0x1C
+#define    MXC_SSISRCR         0x20
+#define    MXC_SSISTCCR        0x24
+#define    MXC_SSISRCCR        0x28
+#define    MXC_SSISFCSR        0x2C
+#define    MXC_SSISTR           0x30
+#define    MXC_SSISOR           0x34
+#define    MXC_SSISACNT        0x38
+#define    MXC_SSISACADD       0x3C
+#define    MXC_SSISACDAT       0x40
+#define    MXC_SSISATAG        0x44
+#define    MXC_SSISTMSK        0x48
+#define    MXC_SSISRMSK        0x4C
+
+/* MXC 91221 only */
+#define    MXC_SSISACCST       0x50
+#define    MXC_SSISACCEN       0x54
+#define    MXC_SSISACCDIS      0x58
+
+/*! SSI1 registers offset*/
+#define    MXC_SSI1STX0                0x00
+#define    MXC_SSI1STX1                0x04
+#define    MXC_SSI1SRX0                0x08
+#define    MXC_SSI1SRX1        0x0C
+#define    MXC_SSI1SCR          0x10
+#define    MXC_SSI1SISR         0x14
+#define    MXC_SSI1SIER        0x18
+#define    MXC_SSI1STCR        0x1C
+#define    MXC_SSI1SRCR        0x20
+#define    MXC_SSI1STCCR       0x24
+#define    MXC_SSI1SRCCR       0x28
+#define    MXC_SSI1SFCSR       0x2C
+#define    MXC_SSI1STR          0x30
+#define    MXC_SSI1SOR          0x34
+#define    MXC_SSI1SACNT       0x38
+#define    MXC_SSI1SACADD      0x3C
+#define    MXC_SSI1SACDAT      0x40
+#define    MXC_SSI1SATAG       0x44
+#define    MXC_SSI1STMSK       0x48
+#define    MXC_SSI1SRMSK       0x4C
+
+/* MXC91221 only */
+
+#define    MXC_SSISACCST       0x50
+#define    MXC_SSISACCEN       0x54
+#define    MXC_SSISACCDIS      0x58
+
+/* Not on MXC91221 */
+/*! SSI2 registers offset*/
+#define    MXC_SSI2STX0        0x00
+#define    MXC_SSI2STX1         0x04
+#define    MXC_SSI2SRX0        0x08
+#define    MXC_SSI2SRX1         0x0C
+#define    MXC_SSI2SCR          0x10
+#define    MXC_SSI2SISR         0x14
+#define    MXC_SSI2SIER         0x18
+#define    MXC_SSI2STCR         0x1C
+#define    MXC_SSI2SRCR         0x20
+#define    MXC_SSI2STCCR       0x24
+#define    MXC_SSI2SRCCR       0x28
+#define    MXC_SSI2SFCSR        0x2C
+#define    MXC_SSI2STR         0x30
+#define    MXC_SSI2SOR         0x34
+#define    MXC_SSI2SACNT       0x38
+#define    MXC_SSI2SACADD      0x3C
+#define    MXC_SSI2SACDAT      0x40
+#define    MXC_SSI2SATAG       0x44
+#define    MXC_SSI2STMSK       0x48
+#define    MXC_SSI2SRMSK       0x4C
+
+/*!
+ * SCR Register bit shift definitions
+ */
+#define SSI_ENABLE_SHIFT            0
+#define SSI_TRANSMIT_ENABLE_SHIFT   1
+#define SSI_RECEIVE_ENABLE_SHIFT    2
+#define SSI_NETWORK_MODE_SHIFT      3
+#define SSI_SYNCHRONOUS_MODE_SHIFT  4
+#define SSI_I2S_MODE_SHIFT          5
+#define SSI_SYSTEM_CLOCK_SHIFT      7
+#define SSI_TWO_CHANNEL_SHIFT       8
+#define SSI_CLOCK_IDLE_SHIFT        9
+
+/* MXC91221 only*/
+#define SSI_TX_FRAME_CLOCK_DISABLE_SHIFT        10
+#define SSI_RX_FRAME_CLOCK_DISABLE_SHIFT        11
+
+/*!
+ * STCR & SRCR Registers bit shift definitions
+ */
+#define SSI_EARLY_FRAME_SYNC_SHIFT    0
+#define SSI_FRAME_SYNC_LENGTH_SHIFT   1
+#define SSI_FRAME_SYNC_INVERT_SHIFT   2
+#define SSI_CLOCK_POLARITY_SHIFT      3
+#define SSI_SHIFT_DIRECTION_SHIFT     4
+#define SSI_CLOCK_DIRECTION_SHIFT     5
+#define SSI_FRAME_DIRECTION_SHIFT     6
+#define SSI_FIFO_ENABLE_0_SHIFT       7
+#define SSI_FIFO_ENABLE_1_SHIFT       8
+#define SSI_BIT_0_SHIFT               9
+
+/* MXC91221 only*/
+#define SSI_TX_FRAME_CLOCK_DISABLE_SHIFT        10
+#define SSI_RX_DATA_EXTENSION_SHIFT   10       /*SRCR only */
+/*!
+ * STCCR & SRCCR Registers bit shift definitions
+ */
+#define SSI_PRESCALER_MODULUS_SHIFT          0
+#define SSI_FRAME_RATE_DIVIDER_SHIFT         8
+#define SSI_WORD_LENGTH_SHIFT               13
+#define SSI_PRESCALER_RANGE_SHIFT           17
+#define SSI_DIVIDE_BY_TWO_SHIFT             18
+#define SSI_FRAME_DIVIDER_MASK              31
+#define SSI_MIN_FRAME_DIVIDER_RATIO          1
+#define SSI_MAX_FRAME_DIVIDER_RATIO         32
+#define SSI_PRESCALER_MODULUS_MASK         255
+#define SSI_MIN_PRESCALER_MODULUS_RATIO      1
+#define SSI_MAX_PRESCALER_MODULUS_RATIO    256
+#define SSI_WORD_LENGTH_MASK                15
+
+#define SSI_IRQ_STATUS_NUMBER        25
+
+/*!
+ * SFCSR Register bit shift definitions
+ */
+#define SSI_RX_FIFO_1_COUNT_SHIFT       28
+#define SSI_TX_FIFO_1_COUNT_SHIFT       24
+#define SSI_RX_FIFO_1_WATERMARK_SHIFT   20
+#define SSI_TX_FIFO_1_WATERMARK_SHIFT   16
+#define SSI_RX_FIFO_0_COUNT_SHIFT       12
+#define SSI_TX_FIFO_0_COUNT_SHIFT        8
+#define SSI_RX_FIFO_0_WATERMARK_SHIFT    4
+#define SSI_TX_FIFO_0_WATERMARK_SHIFT    0
+#define SSI_MIN_FIFO_WATERMARK           0
+#define SSI_MAX_FIFO_WATERMARK           8
+
+/*!
+ * SSI Option Register (SOR) bit shift definitions
+ */
+#define SSI_FRAME_SYN_RESET_SHIFT        0
+#define SSI_WAIT_SHIFT                   1
+#define SSI_INIT_SHIFT                   3
+#define SSI_TRANSMITTER_CLEAR_SHIFT      4
+#define SSI_RECEIVER_CLEAR_SHIFT         5
+#define SSI_CLOCK_OFF_SHIFT              6
+#define SSI_WAIT_STATE_MASK            0x3
+
+/*!
+ * SSI AC97 Control Register (SACNT) bit shift definitions
+ */
+#define AC97_MODE_ENABLE_SHIFT           0
+#define AC97_VARIABLE_OPERATION_SHIFT    1
+#define AC97_TAG_IN_FIFO_SHIFT           2
+#define AC97_READ_COMMAND_SHIFT          3
+#define AC97_WRITE_COMMAND_SHIFT         4
+#define AC97_FRAME_RATE_DIVIDER_SHIFT    5
+#define AC97_FRAME_RATE_MASK          0x3F
+
+/*!
+ * SSI Test Register (STR) bit shift definitions
+ */
+#define SSI_TEST_MODE_SHIFT               15
+#define SSI_RCK2TCK_SHIFT                 14
+#define SSI_RFS2TFS_SHIFT                 13
+#define SSI_RXSTATE_SHIFT                 8
+#define SSI_TXD2RXD_SHIFT                 7
+#define SSI_TCK2RCK_SHIFT                 6
+#define SSI_TFS2RFS_SHIFT                 5
+#define SSI_TXSTATE_SHIFT                 0
+
+#endif                         /* __MXC_SSI_REGISTERS_H__ */
diff --git a/drivers/mxc/ssi/ssi.c b/drivers/mxc/ssi/ssi.c
new file mode 100644 (file)
index 0000000..da09631
--- /dev/null
@@ -0,0 +1,1221 @@
+/*
+ * 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 ssi.c
+ * @brief This file contains the implementation of the SSI driver main services
+ *
+ *
+ * @ingroup SSI
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include <mach/clock.h>
+
+#include "registers.h"
+#include "ssi.h"
+
+static spinlock_t ssi_lock;
+struct mxc_audio_platform_data *ssi_platform_data;
+
+EXPORT_SYMBOL(ssi_ac97_frame_rate_divider);
+EXPORT_SYMBOL(ssi_ac97_get_command_address_register);
+EXPORT_SYMBOL(ssi_ac97_get_command_data_register);
+EXPORT_SYMBOL(ssi_ac97_get_tag_register);
+EXPORT_SYMBOL(ssi_ac97_mode_enable);
+EXPORT_SYMBOL(ssi_ac97_tag_in_fifo);
+EXPORT_SYMBOL(ssi_ac97_read_command);
+EXPORT_SYMBOL(ssi_ac97_set_command_address_register);
+EXPORT_SYMBOL(ssi_ac97_set_command_data_register);
+EXPORT_SYMBOL(ssi_ac97_set_tag_register);
+EXPORT_SYMBOL(ssi_ac97_variable_mode);
+EXPORT_SYMBOL(ssi_ac97_write_command);
+EXPORT_SYMBOL(ssi_clock_idle_state);
+EXPORT_SYMBOL(ssi_clock_off);
+EXPORT_SYMBOL(ssi_enable);
+EXPORT_SYMBOL(ssi_get_data);
+EXPORT_SYMBOL(ssi_get_status);
+EXPORT_SYMBOL(ssi_i2s_mode);
+EXPORT_SYMBOL(ssi_interrupt_disable);
+EXPORT_SYMBOL(ssi_interrupt_enable);
+EXPORT_SYMBOL(ssi_network_mode);
+EXPORT_SYMBOL(ssi_receive_enable);
+EXPORT_SYMBOL(ssi_rx_bit0);
+EXPORT_SYMBOL(ssi_rx_clock_direction);
+EXPORT_SYMBOL(ssi_rx_clock_divide_by_two);
+EXPORT_SYMBOL(ssi_rx_clock_polarity);
+EXPORT_SYMBOL(ssi_rx_clock_prescaler);
+EXPORT_SYMBOL(ssi_rx_early_frame_sync);
+EXPORT_SYMBOL(ssi_rx_fifo_counter);
+EXPORT_SYMBOL(ssi_rx_fifo_enable);
+EXPORT_SYMBOL(ssi_rx_fifo_full_watermark);
+EXPORT_SYMBOL(ssi_rx_flush_fifo);
+EXPORT_SYMBOL(ssi_rx_frame_direction);
+EXPORT_SYMBOL(ssi_rx_frame_rate);
+EXPORT_SYMBOL(ssi_rx_frame_sync_active);
+EXPORT_SYMBOL(ssi_rx_frame_sync_length);
+EXPORT_SYMBOL(ssi_rx_mask_time_slot);
+EXPORT_SYMBOL(ssi_rx_prescaler_modulus);
+EXPORT_SYMBOL(ssi_rx_shift_direction);
+EXPORT_SYMBOL(ssi_rx_word_length);
+EXPORT_SYMBOL(ssi_set_data);
+EXPORT_SYMBOL(ssi_set_wait_states);
+EXPORT_SYMBOL(ssi_synchronous_mode);
+EXPORT_SYMBOL(ssi_system_clock);
+EXPORT_SYMBOL(ssi_transmit_enable);
+EXPORT_SYMBOL(ssi_two_channel_mode);
+EXPORT_SYMBOL(ssi_tx_bit0);
+EXPORT_SYMBOL(ssi_tx_clock_direction);
+EXPORT_SYMBOL(ssi_tx_clock_divide_by_two);
+EXPORT_SYMBOL(ssi_tx_clock_polarity);
+EXPORT_SYMBOL(ssi_tx_clock_prescaler);
+EXPORT_SYMBOL(ssi_tx_early_frame_sync);
+EXPORT_SYMBOL(ssi_tx_fifo_counter);
+EXPORT_SYMBOL(ssi_tx_fifo_empty_watermark);
+EXPORT_SYMBOL(ssi_tx_fifo_enable);
+EXPORT_SYMBOL(ssi_tx_flush_fifo);
+EXPORT_SYMBOL(ssi_tx_frame_direction);
+EXPORT_SYMBOL(ssi_tx_frame_rate);
+EXPORT_SYMBOL(ssi_tx_frame_sync_active);
+EXPORT_SYMBOL(ssi_tx_frame_sync_length);
+EXPORT_SYMBOL(ssi_tx_mask_time_slot);
+EXPORT_SYMBOL(ssi_tx_prescaler_modulus);
+EXPORT_SYMBOL(ssi_tx_shift_direction);
+EXPORT_SYMBOL(ssi_tx_word_length);
+EXPORT_SYMBOL(get_ssi_fifo_addr);
+
+struct resource *res;
+unsigned long base_addr_1;
+unsigned long base_addr_2;
+
+unsigned int get_ssi_fifo_addr(unsigned int ssi, int direction)
+{
+       unsigned int fifo_addr;
+       if (direction == 1) {
+               if (ssi_platform_data->ssi_num == 2) {
+                       fifo_addr =
+                           (ssi ==
+                            SSI1) ? (int)(base_addr_1 +
+                                          MXC_SSI1STX0) : (int)(base_addr_2 +
+                                                                MXC_SSI2STX0);
+               } else {
+                       fifo_addr = (int)(base_addr_1 + MXC_SSI1STX0);
+               }
+       } else {
+               fifo_addr = (int)(base_addr_1 + MXC_SSI1SRX0);
+       }
+       return fifo_addr;
+}
+
+void *get_ssi_base_addr(unsigned int ssi)
+{
+       if (ssi_platform_data->ssi_num == 2) {
+               if (ssi == SSI1)
+                       return IO_ADDRESS(base_addr_1);
+               else
+                       return IO_ADDRESS(base_addr_2);
+       }
+       return IO_ADDRESS(base_addr_1);
+}
+
+void set_register_bits(unsigned int mask, unsigned int data,
+                      unsigned int offset, unsigned int ssi)
+{
+       volatile unsigned long reg = 0;
+       void *base_addr = get_ssi_base_addr(ssi);
+       unsigned long flags = 0;
+
+       spin_lock_irqsave(&ssi_lock, flags);
+       reg = __raw_readl(base_addr + offset);
+       reg = (reg & (~mask)) | data;
+       __raw_writel(reg, base_addr + offset);
+       spin_unlock_irqrestore(&ssi_lock, flags);
+}
+
+unsigned long getreg_value(unsigned int offset, unsigned int ssi)
+{
+       void *base_addr = get_ssi_base_addr(ssi);
+       return __raw_readl(base_addr + offset);
+}
+
+void set_register(unsigned int data, unsigned int offset, unsigned int ssi)
+{
+       void *base_addr = get_ssi_base_addr(ssi);
+       __raw_writel(data, base_addr + offset);
+}
+
+/*!
+ * This function controls the AC97 frame rate divider.
+ *
+ * @param        module               the module number
+ * @param        frame_rate_divider   the AC97 frame rate divider
+ */
+void ssi_ac97_frame_rate_divider(ssi_mod module,
+                                unsigned char frame_rate_divider)
+{
+       unsigned int reg = 0;
+
+       reg = getreg_value(MXC_SSISACNT, module);
+       reg |= ((frame_rate_divider & AC97_FRAME_RATE_MASK)
+               << AC97_FRAME_RATE_DIVIDER_SHIFT);
+       set_register(reg, MXC_SSISACNT, module);
+}
+
+/*!
+ * This function gets the AC97 command address register.
+ *
+ * @param        module        the module number
+ * @return       This function returns the command address slot information.
+ */
+unsigned int ssi_ac97_get_command_address_register(ssi_mod module)
+{
+       return getreg_value(MXC_SSISACADD, module);
+}
+
+/*!
+ * This function gets the AC97 command data register.
+ *
+ * @param        module        the module number
+ * @return       This function returns the command data slot information.
+ */
+unsigned int ssi_ac97_get_command_data_register(ssi_mod module)
+{
+       return getreg_value(MXC_SSISACDAT, module);
+}
+
+/*!
+ * This function gets the AC97 tag register.
+ *
+ * @param        module        the module number
+ * @return       This function returns the tag information.
+ */
+unsigned int ssi_ac97_get_tag_register(ssi_mod module)
+{
+       return getreg_value(MXC_SSISATAG, module);
+}
+
+/*!
+ * This function controls the AC97 mode.
+ *
+ * @param        module        the module number
+ * @param        state         the AC97 mode state (enabled or disabled)
+ */
+void ssi_ac97_mode_enable(ssi_mod module, bool state)
+{
+       unsigned int reg = 0;
+
+       reg = getreg_value(MXC_SSISACNT, module);
+       if (state == true) {
+               reg |= (1 << AC97_MODE_ENABLE_SHIFT);
+       } else {
+               reg &= ~(1 << AC97_MODE_ENABLE_SHIFT);
+       }
+
+       set_register(reg, MXC_SSISACNT, module);
+}
+
+/*!
+ * This function controls the AC97 tag in FIFO behavior.
+ *
+ * @param        module        the module number
+ * @param        state         the tag in fifo behavior (Tag info stored in Rx FIFO 0 if true,
+ * Tag info stored in SATAG register otherwise)
+ */
+void ssi_ac97_tag_in_fifo(ssi_mod module, bool state)
+{
+       unsigned int reg = 0;
+
+       reg = getreg_value(MXC_SSISACNT, module);
+       if (state == true) {
+               reg |= (1 << AC97_TAG_IN_FIFO_SHIFT);
+       } else {
+               reg &= ~(1 << AC97_TAG_IN_FIFO_SHIFT);
+       }
+
+       set_register(reg, MXC_SSISACNT, module);
+}
+
+/*!
+ * This function controls the AC97 read command.
+ *
+ * @param        module        the module number
+ * @param        state         the next AC97 command is a read command or not
+ */
+void ssi_ac97_read_command(ssi_mod module, bool state)
+{
+       unsigned int reg = 0;
+
+       reg = getreg_value(MXC_SSISACNT, module);
+       if (state == true) {
+               reg |= (1 << AC97_READ_COMMAND_SHIFT);
+       } else {
+               reg &= ~(1 << AC97_READ_COMMAND_SHIFT);
+       }
+
+       set_register(reg, MXC_SSISACNT, module);
+}
+
+/*!
+ * This function sets the AC97 command address register.
+ *
+ * @param        module        the module number
+ * @param        address       the command address slot information
+ */
+void ssi_ac97_set_command_address_register(ssi_mod module, unsigned int address)
+{
+       set_register(address, MXC_SSISACADD, module);
+}
+
+/*!
+ * This function sets the AC97 command data register.
+ *
+ * @param        module        the module number
+ * @param        data          the command data slot information
+ */
+void ssi_ac97_set_command_data_register(ssi_mod module, unsigned int data)
+{
+       set_register(data, MXC_SSISACDAT, module);
+}
+
+/*!
+ * This function sets the AC97 tag register.
+ *
+ * @param        module        the module number
+ * @param        tag           the tag information
+ */
+void ssi_ac97_set_tag_register(ssi_mod module, unsigned int tag)
+{
+       set_register(tag, MXC_SSISATAG, module);
+}
+
+/*!
+ * This function controls the AC97 variable mode.
+ *
+ * @param        module        the module number
+ * @param        state         the AC97 variable mode state (enabled or disabled)
+ */ void ssi_ac97_variable_mode(ssi_mod module, bool state)
+{
+       unsigned int reg = 0;
+
+       reg = getreg_value(MXC_SSISACNT, module);
+       if (state == true) {
+               reg |= (1 << AC97_VARIABLE_OPERATION_SHIFT);
+       } else {
+               reg &= ~(1 << AC97_VARIABLE_OPERATION_SHIFT);
+       }
+
+       set_register(reg, MXC_SSISACNT, module);
+}
+
+/*!
+ * This function controls the AC97 write command.
+ *
+ * @param        module        the module number
+ * @param        state         the next AC97 command is a write command or not
+ */
+void ssi_ac97_write_command(ssi_mod module, bool state)
+{
+       unsigned int reg = 0;
+
+       reg = getreg_value(MXC_SSISACNT, module);
+       if (state == true) {
+               reg |= (1 << AC97_WRITE_COMMAND_SHIFT);
+       } else {
+               reg &= ~(1 << AC97_WRITE_COMMAND_SHIFT);
+       }
+
+       set_register(reg, MXC_SSISACNT, module);
+}
+
+/*!
+ * This function controls the idle state of the transmit clock port during SSI internal gated mode.
+ *
+ * @param        module        the module number
+ * @param        state         the clock idle state
+ */
+void ssi_clock_idle_state(ssi_mod module, idle_state state)
+{
+       set_register_bits(1 << SSI_CLOCK_IDLE_SHIFT,
+                         state << SSI_CLOCK_IDLE_SHIFT, MXC_SSISCR, module);
+}
+
+/*!
+ * This function turns off/on the ccm_ssi_clk to reduce power consumption.
+ *
+ * @param        module        the module number
+ * @param        state         the state for ccm_ssi_clk (true: turn off, else:turn on)
+ */
+void ssi_clock_off(ssi_mod module, bool state)
+{
+       set_register_bits(1 << SSI_CLOCK_OFF_SHIFT,
+                         state << SSI_CLOCK_OFF_SHIFT, MXC_SSISOR, module);
+}
+
+/*!
+ * This function enables/disables the SSI module.
+ *
+ * @param        module        the module number
+ * @param        state         the state for SSI module
+ */
+void ssi_enable(ssi_mod module, bool state)
+{
+       set_register_bits(1 << SSI_ENABLE_SHIFT, state << SSI_ENABLE_SHIFT,
+                         MXC_SSISCR, module);
+}
+
+/*!
+ * This function gets the data word in the Receive FIFO of the SSI module.
+ *
+ * @param        module        the module number
+ * @param        fifo          the Receive FIFO to read
+ * @return       This function returns the read data.
+ */
+unsigned int ssi_get_data(ssi_mod module, fifo_nb fifo)
+{
+       unsigned int result = 0;
+
+       if (ssi_fifo_0 == fifo) {
+               result = getreg_value(MXC_SSISRX0, module);
+       } else {
+               result = getreg_value(MXC_SSISRX1, module);
+       }
+
+       return result;
+}
+
+/*!
+ * This function returns the status of the SSI module (SISR register) as a combination of status.
+ *
+ * @param        module        the module number
+ * @return       This function returns the status of the SSI module
+ */
+ssi_status_enable_mask ssi_get_status(ssi_mod module)
+{
+       unsigned int result;
+
+       result = getreg_value(MXC_SSISISR, module);
+       result &= ((1 << SSI_IRQ_STATUS_NUMBER) - 1);
+
+       return (ssi_status_enable_mask) result;
+}
+
+/*!
+ * This function selects the I2S mode of the SSI module.
+ *
+ * @param        module        the module number
+ * @param        mode          the I2S mode
+ */
+void ssi_i2s_mode(ssi_mod module, mode_i2s mode)
+{
+       set_register_bits(3 << SSI_I2S_MODE_SHIFT, mode << SSI_I2S_MODE_SHIFT,
+                         MXC_SSISCR, module);
+}
+
+/*!
+ * This function disables the interrupts of the SSI module.
+ *
+ * @param        module        the module number
+ * @param        mask          the mask of the interrupts to disable
+ */
+void ssi_interrupt_disable(ssi_mod module, ssi_status_enable_mask mask)
+{
+       set_register_bits(mask, 0, MXC_SSISIER, module);
+}
+
+/*!
+ * This function enables the interrupts of the SSI module.
+ *
+ * @param        module        the module number
+ * @param        mask          the mask of the interrupts to enable
+ */
+void ssi_interrupt_enable(ssi_mod module, ssi_status_enable_mask mask)
+{
+       set_register_bits(0, mask, MXC_SSISIER, module);
+}
+
+/*!
+ * This function enables/disables the network mode of the SSI module.
+ *
+ * @param        module        the module number
+ * @param        state         the network mode state
+ */
+void ssi_network_mode(ssi_mod module, bool state)
+{
+       set_register_bits(1 << SSI_NETWORK_MODE_SHIFT,
+                         state << SSI_NETWORK_MODE_SHIFT, MXC_SSISCR, module);
+}
+
+/*!
+ * This function enables/disables the receive section of the SSI module.
+ *
+ * @param        module        the module number
+ * @param        state         the receive section state
+ */
+void ssi_receive_enable(ssi_mod module, bool state)
+{
+       set_register_bits(1 << SSI_RECEIVE_ENABLE_SHIFT,
+                         state << SSI_RECEIVE_ENABLE_SHIFT, MXC_SSISCR,
+                         module);
+}
+
+/*!
+ * This function configures the SSI module to receive data word at bit position 0 or 23 in the Receive shift register.
+ *
+ * @param        module        the module number
+ * @param        state         the state to receive at bit 0
+ */
+void ssi_rx_bit0(ssi_mod module, bool state)
+{
+       set_register_bits(1 << SSI_BIT_0_SHIFT, state << SSI_BIT_0_SHIFT,
+                         MXC_SSISRCR, module);
+}
+
+/*!
+ * This function controls the source of the clock signal used to clock the Receive shift register.
+ *
+ * @param        module        the module number
+ * @param        direction     the clock signal direction
+ */
+void ssi_rx_clock_direction(ssi_mod module, ssi_tx_rx_direction direction)
+{
+       set_register_bits(1 << SSI_CLOCK_DIRECTION_SHIFT,
+                         direction << SSI_CLOCK_DIRECTION_SHIFT, MXC_SSISRCR,
+                         module);
+}
+
+/*!
+ * This function configures the divide-by-two divider of the SSI module for the receive section.
+ *
+ * @param        module        the module number
+ * @param        state         the divider state
+ */
+void ssi_rx_clock_divide_by_two(ssi_mod module, bool state)
+{
+       set_register_bits(1 << SSI_DIVIDE_BY_TWO_SHIFT,
+                         state << SSI_DIVIDE_BY_TWO_SHIFT, MXC_SSISRCCR,
+                         module);
+}
+
+/*!
+ * This function controls which bit clock edge is used to clock in data.
+ *
+ * @param        module        the module number
+ * @param        polarity      the clock polarity
+ */
+void ssi_rx_clock_polarity(ssi_mod module, ssi_tx_rx_clock_polarity polarity)
+{
+       set_register_bits(1 << SSI_CLOCK_POLARITY_SHIFT,
+                         polarity << SSI_CLOCK_POLARITY_SHIFT, MXC_SSISRCR,
+                         module);
+}
+
+/*!
+ * This function configures a fixed divide-by-eight clock prescaler divider of the SSI module in series with the variable prescaler for the receive section.
+ *
+ * @param        module        the module number
+ * @param        state         the prescaler state
+ */
+void ssi_rx_clock_prescaler(ssi_mod module, bool state)
+{
+       set_register_bits(1 << SSI_PRESCALER_RANGE_SHIFT,
+                         state << SSI_PRESCALER_RANGE_SHIFT,
+                         MXC_SSISRCCR, module);
+}
+
+/*!
+ * This function controls the early frame sync configuration.
+ *
+ * @param        module        the module number
+ * @param        early         the early frame sync configuration
+ */
+void ssi_rx_early_frame_sync(ssi_mod module, ssi_tx_rx_early_frame_sync early)
+{
+       set_register_bits(1 << SSI_EARLY_FRAME_SYNC_SHIFT,
+                         early << SSI_EARLY_FRAME_SYNC_SHIFT,
+                         MXC_SSISRCR, module);
+}
+
+/*!
+ * This function gets the number of data words in the Receive FIFO.
+ *
+ * @param        module        the module number
+ * @param        fifo          the fifo
+ * @return       This function returns the number of words in the Rx FIFO.
+ */
+unsigned char ssi_rx_fifo_counter(ssi_mod module, fifo_nb fifo)
+{
+       unsigned char result;
+       result = 0;
+
+       if (ssi_fifo_0 == fifo) {
+               result = getreg_value(MXC_SSISFCSR, module);
+               result &= (0xF << SSI_RX_FIFO_0_COUNT_SHIFT);
+               result = result >> SSI_RX_FIFO_0_COUNT_SHIFT;
+       } else {
+               result = getreg_value(MXC_SSISFCSR, module);
+               result &= (0xF << SSI_RX_FIFO_1_COUNT_SHIFT);
+               result = result >> SSI_RX_FIFO_1_COUNT_SHIFT;
+       }
+
+       return result;
+}
+
+/*!
+ * This function enables the Receive FIFO.
+ *
+ * @param        module        the module number
+ * @param        fifo          the fifo to enable
+ * @param        enable        the state of the fifo, enabled or disabled
+ */
+
+void ssi_rx_fifo_enable(ssi_mod module, fifo_nb fifo, bool enable)
+{
+       volatile unsigned int reg;
+
+       reg = getreg_value(MXC_SSISRCR, module);
+       if (enable == true) {
+               reg |= ((1 + fifo) << SSI_FIFO_ENABLE_0_SHIFT);
+       } else {
+               reg &= ~((1 + fifo) << SSI_FIFO_ENABLE_0_SHIFT);
+       }
+
+       set_register(reg, MXC_SSISRCR, module);
+}
+
+/*!
+ * This function controls the threshold at which the RFFx flag will be set.
+ *
+ * @param        module        the module number
+ * @param        fifo          the fifo to enable
+ * @param        watermark     the watermark
+ * @return       This function returns the result of the operation (0 if successful, -1 otherwise).
+ */
+int ssi_rx_fifo_full_watermark(ssi_mod module,
+                              fifo_nb fifo, unsigned char watermark)
+{
+       int result = -1;
+       result = -1;
+
+       if ((watermark > SSI_MIN_FIFO_WATERMARK) &&
+           (watermark <= SSI_MAX_FIFO_WATERMARK)) {
+               if (ssi_fifo_0 == fifo) {
+                       set_register_bits(0xf << SSI_RX_FIFO_0_WATERMARK_SHIFT,
+                                         watermark <<
+                                         SSI_RX_FIFO_0_WATERMARK_SHIFT,
+                                         MXC_SSISFCSR, module);
+               } else {
+                       set_register_bits(0xf << SSI_RX_FIFO_1_WATERMARK_SHIFT,
+                                         watermark <<
+                                         SSI_RX_FIFO_1_WATERMARK_SHIFT,
+                                         MXC_SSISFCSR, module);
+               }
+
+               result = 0;
+       }
+
+       return result;
+}
+
+/*!
+ * This function flushes the Receive FIFOs.
+ *
+ * @param        module        the module number
+ */
+void ssi_rx_flush_fifo(ssi_mod module)
+{
+       set_register_bits(0, 1 << SSI_RECEIVER_CLEAR_SHIFT, MXC_SSISOR, module);
+}
+
+/*!
+ * This function controls the direction of the Frame Sync signal for the receive section.
+ *
+ * @param        module        the module number
+ * @param        direction     the Frame Sync signal direction
+ */
+void ssi_rx_frame_direction(ssi_mod module, ssi_tx_rx_direction direction)
+{
+       set_register_bits(1 << SSI_FRAME_DIRECTION_SHIFT,
+                         direction << SSI_FRAME_DIRECTION_SHIFT,
+                         MXC_SSISRCR, module);
+}
+
+/*!
+ * This function configures the Receive frame rate divider for the receive section.
+ *
+ * @param        module        the module number
+ * @param        ratio         the divide ratio from 1 to 32
+ * @return       This function returns the result of the operation (0 if successful, -1 otherwise).
+ */
+int ssi_rx_frame_rate(ssi_mod module, unsigned char ratio)
+{
+       int result = -1;
+
+       if ((ratio >= SSI_MIN_FRAME_DIVIDER_RATIO) &&
+           (ratio <= SSI_MAX_FRAME_DIVIDER_RATIO)) {
+               set_register_bits(SSI_FRAME_DIVIDER_MASK <<
+                                 SSI_FRAME_RATE_DIVIDER_SHIFT,
+                                 (ratio - 1) << SSI_FRAME_RATE_DIVIDER_SHIFT,
+                                 MXC_SSISRCCR, module);
+               result = 0;
+       }
+
+       return result;
+}
+
+/*!
+ * This function controls the Frame Sync active polarity for the receive section.
+ *
+ * @param        module        the module number
+ * @param        active        the Frame Sync active polarity
+ */
+void ssi_rx_frame_sync_active(ssi_mod module,
+                             ssi_tx_rx_frame_sync_active active)
+{
+       set_register_bits(1 << SSI_FRAME_SYNC_INVERT_SHIFT,
+                         active << SSI_FRAME_SYNC_INVERT_SHIFT,
+                         MXC_SSISRCR, module);
+}
+
+/*!
+ * This function controls the Frame Sync length (one word or one bit long) for the receive section.
+ *
+ * @param        module        the module number
+ * @param        length        the Frame Sync length
+ */
+void ssi_rx_frame_sync_length(ssi_mod module,
+                             ssi_tx_rx_frame_sync_length length)
+{
+       set_register_bits(1 << SSI_FRAME_SYNC_LENGTH_SHIFT,
+                         length << SSI_FRAME_SYNC_LENGTH_SHIFT,
+                         MXC_SSISRCR, module);
+}
+
+/*!
+ * This function configures the time slot(s) to mask for the receive section.
+ *
+ * @param        module        the module number
+ * @param        mask          the mask to indicate the time slot(s) masked
+ */
+void ssi_rx_mask_time_slot(ssi_mod module, unsigned int mask)
+{
+       set_register_bits(0xFFFFFFFF, mask, MXC_SSISRMSK, module);
+}
+
+/*!
+ * This function configures the Prescale divider for the receive section.
+ *
+ * @param        module        the module number
+ * @param        divider       the divide ratio from 1 to  256
+ * @return       This function returns the result of the operation (0 if successful, -1 otherwise).
+ */
+int ssi_rx_prescaler_modulus(ssi_mod module, unsigned int divider)
+{
+       int result = -1;
+
+       if ((divider >= SSI_MIN_PRESCALER_MODULUS_RATIO) &&
+           (divider <= SSI_MAX_PRESCALER_MODULUS_RATIO)) {
+
+               set_register_bits(SSI_PRESCALER_MODULUS_MASK <<
+                                 SSI_PRESCALER_MODULUS_SHIFT,
+                                 (divider - 1) << SSI_PRESCALER_MODULUS_SHIFT,
+                                 MXC_SSISRCCR, module);
+               result = 0;
+       }
+
+       return result;
+}
+
+/*!
+ * This function controls whether the MSB or LSB will be received first in a sample.
+ *
+ * @param        module        the module number
+ * @param        direction     the shift direction
+ */
+void ssi_rx_shift_direction(ssi_mod module, ssi_tx_rx_shift_direction direction)
+{
+       set_register_bits(1 << SSI_SHIFT_DIRECTION_SHIFT,
+                         direction << SSI_SHIFT_DIRECTION_SHIFT,
+                         MXC_SSISRCR, module);
+}
+
+/*!
+ * This function configures the Receive word length.
+ *
+ * @param        module        the module number
+ * @param        length        the word length
+ */
+void ssi_rx_word_length(ssi_mod module, ssi_word_length length)
+{
+       set_register_bits(SSI_WORD_LENGTH_MASK << SSI_WORD_LENGTH_SHIFT,
+                         length << SSI_WORD_LENGTH_SHIFT,
+                         MXC_SSISRCCR, module);
+}
+
+/*!
+ * This function sets the data word in the Transmit FIFO of the SSI module.
+ *
+ * @param        module        the module number
+ * @param        fifo          the FIFO number
+ * @param        data          the data to load in the FIFO
+ */
+
+void ssi_set_data(ssi_mod module, fifo_nb fifo, unsigned int data)
+{
+       if (ssi_fifo_0 == fifo) {
+               set_register(data, MXC_SSISTX0, module);
+       } else {
+               set_register(data, MXC_SSISTX1, module);
+       }
+}
+
+/*!
+ * This function controls the number of wait states between the core and SSI.
+ *
+ * @param        module        the module number
+ * @param        wait          the number of wait state(s)
+ */
+void ssi_set_wait_states(ssi_mod module, ssi_wait_states wait)
+{
+       set_register_bits(SSI_WAIT_STATE_MASK << SSI_WAIT_SHIFT,
+                         wait << SSI_WAIT_SHIFT, MXC_SSISOR, module);
+}
+
+/*!
+ * This function enables/disables the synchronous mode of the SSI module.
+ *
+ * @param        module        the module number
+ * @param        state         the synchronous mode state
+ */
+void ssi_synchronous_mode(ssi_mod module, bool state)
+{
+       set_register_bits(1 << SSI_SYNCHRONOUS_MODE_SHIFT,
+                         state << SSI_SYNCHRONOUS_MODE_SHIFT,
+                         MXC_SSISCR, module);
+}
+
+/*!
+ * This function allows the SSI module to output the SYS_CLK at the SRCK port.
+ *
+ * @param        module        the module number
+ * @param        state         the system clock state
+ */
+void ssi_system_clock(ssi_mod module, bool state)
+{
+       set_register_bits(1 << SSI_SYSTEM_CLOCK_SHIFT,
+                         state << SSI_SYSTEM_CLOCK_SHIFT, MXC_SSISCR, module);
+}
+
+/*!
+ * This function enables/disables the transmit section of the SSI module.
+ *
+ * @param        module        the module number
+ * @param        state         the transmit section state
+ */
+void ssi_transmit_enable(ssi_mod module, bool state)
+{
+       set_register_bits(1 << SSI_TRANSMIT_ENABLE_SHIFT,
+                         state << SSI_TRANSMIT_ENABLE_SHIFT,
+                         MXC_SSISCR, module);
+}
+
+/*!
+ * This function allows the SSI module to operate in the two channel mode.
+ *
+ * @param        module        the module number
+ * @param        state         the two channel mode state
+ */
+void ssi_two_channel_mode(ssi_mod module, bool state)
+{
+       set_register_bits(1 << SSI_TWO_CHANNEL_SHIFT,
+                         state << SSI_TWO_CHANNEL_SHIFT, MXC_SSISCR, module);
+}
+
+/*!
+ * This function configures the SSI module to transmit data word from bit position 0 or 23 in the Transmit shift register.
+ *
+ * @param        module        the module number
+ * @param        state         the transmit from bit 0 state
+ */
+void ssi_tx_bit0(ssi_mod module, bool state)
+{
+       set_register_bits(1 << SSI_BIT_0_SHIFT,
+                         state << SSI_BIT_0_SHIFT, MXC_SSISTCR, module);
+}
+
+/*!
+ * This function controls the direction of the clock signal used to clock the Transmit shift register.
+ *
+ * @param        module        the module number
+ * @param        direction     the clock signal direction
+ */
+void ssi_tx_clock_direction(ssi_mod module, ssi_tx_rx_direction direction)
+{
+       set_register_bits(1 << SSI_CLOCK_DIRECTION_SHIFT,
+                         direction << SSI_CLOCK_DIRECTION_SHIFT,
+                         MXC_SSISTCR, module);
+}
+
+/*!
+ * This function configures the divide-by-two divider of the SSI module for the transmit section.
+ *
+ * @param        module        the module number
+ * @param        state         the divider state
+ */
+void ssi_tx_clock_divide_by_two(ssi_mod module, bool state)
+{
+       set_register_bits(1 << SSI_DIVIDE_BY_TWO_SHIFT,
+                         state << SSI_DIVIDE_BY_TWO_SHIFT,
+                         MXC_SSISTCCR, module);
+}
+
+/*!
+ * This function controls which bit clock edge is used to clock out data.
+ *
+ * @param        module        the module number
+ * @param        polarity      the clock polarity
+ */
+void ssi_tx_clock_polarity(ssi_mod module, ssi_tx_rx_clock_polarity polarity)
+{
+       set_register_bits(1 << SSI_CLOCK_POLARITY_SHIFT,
+                         polarity << SSI_CLOCK_POLARITY_SHIFT,
+                         MXC_SSISTCR, module);
+}
+
+/*!
+ * This function configures a fixed divide-by-eight clock prescaler divider of the SSI module in series with the variable prescaler for the transmit section.
+ *
+ * @param        module        the module number
+ * @param        state         the prescaler state
+ */
+void ssi_tx_clock_prescaler(ssi_mod module, bool state)
+{
+       set_register_bits(1 << SSI_PRESCALER_RANGE_SHIFT,
+                         state << SSI_PRESCALER_RANGE_SHIFT,
+                         MXC_SSISTCCR, module);
+}
+
+/*!
+ * This function controls the early frame sync configuration for the transmit section.
+ *
+ * @param        module        the module number
+ * @param        early         the early frame sync configuration
+ */
+void ssi_tx_early_frame_sync(ssi_mod module, ssi_tx_rx_early_frame_sync early)
+{
+       set_register_bits(1 << SSI_EARLY_FRAME_SYNC_SHIFT,
+                         early << SSI_EARLY_FRAME_SYNC_SHIFT,
+                         MXC_SSISTCR, module);
+}
+
+/*!
+ * This function gets the number of data words in the Transmit FIFO.
+ *
+ * @param        module        the module number
+ * @param        fifo          the fifo
+ * @return       This function returns the number of words in the Tx FIFO.
+ */
+unsigned char ssi_tx_fifo_counter(ssi_mod module, fifo_nb fifo)
+{
+       unsigned char result = 0;
+
+       if (ssi_fifo_0 == fifo) {
+               result = getreg_value(MXC_SSISFCSR, module);
+               result &= (0xF << SSI_TX_FIFO_0_COUNT_SHIFT);
+               result >>= SSI_TX_FIFO_0_COUNT_SHIFT;
+       } else {
+               result = getreg_value(MXC_SSISFCSR, module);
+               result &= (0xF << SSI_TX_FIFO_1_COUNT_SHIFT);
+               result >>= SSI_TX_FIFO_1_COUNT_SHIFT;
+       }
+
+       return result;
+}
+
+/*!
+ * This function controls the threshold at which the TFEx flag will be set.
+ *
+ * @param        module        the module number
+ * @param        fifo          the fifo to enable
+ * @param        watermark     the watermark
+ * @return       This function returns the result of the operation (0 if successful, -1 otherwise).
+ */
+int ssi_tx_fifo_empty_watermark(ssi_mod module,
+                               fifo_nb fifo, unsigned char watermark)
+{
+       int result = -1;
+
+       if ((watermark > SSI_MIN_FIFO_WATERMARK) &&
+           (watermark <= SSI_MAX_FIFO_WATERMARK)) {
+               if (ssi_fifo_0 == fifo) {
+                       set_register_bits(0xf << SSI_TX_FIFO_0_WATERMARK_SHIFT,
+                                         watermark <<
+                                         SSI_TX_FIFO_0_WATERMARK_SHIFT,
+                                         MXC_SSISFCSR, module);
+               } else {
+                       set_register_bits(0xf << SSI_TX_FIFO_1_WATERMARK_SHIFT,
+                                         watermark <<
+                                         SSI_TX_FIFO_1_WATERMARK_SHIFT,
+                                         MXC_SSISFCSR, module);
+               }
+
+               result = 0;
+       }
+
+       return result;
+}
+
+/*!
+ * This function enables the Transmit FIFO.
+ *
+ * @param        module        the module number
+ * @param        fifo          the fifo to enable
+ * @param        enable        the state of the fifo, enabled or disabled
+ */
+
+void ssi_tx_fifo_enable(ssi_mod module, fifo_nb fifo, bool enable)
+{
+       unsigned int reg;
+
+       reg = getreg_value(MXC_SSISTCR, module);
+       if (enable == true) {
+               reg |= ((1 + fifo) << SSI_FIFO_ENABLE_0_SHIFT);
+       } else {
+               reg &= ~((1 + fifo) << SSI_FIFO_ENABLE_0_SHIFT);
+       }
+
+       set_register(reg, MXC_SSISTCR, module);
+}
+
+/*!
+ * This function flushes the Transmit FIFOs.
+ *
+ * @param        module        the module number
+ */
+void ssi_tx_flush_fifo(ssi_mod module)
+{
+       set_register_bits(0, 1 << SSI_TRANSMITTER_CLEAR_SHIFT,
+                         MXC_SSISOR, module);
+}
+
+/*!
+ * This function controls the direction of the Frame Sync signal for the transmit section.
+ *
+ * @param        module        the module number
+ * @param        direction     the Frame Sync signal direction
+ */
+void ssi_tx_frame_direction(ssi_mod module, ssi_tx_rx_direction direction)
+{
+       set_register_bits(1 << SSI_FRAME_DIRECTION_SHIFT,
+                         direction << SSI_FRAME_DIRECTION_SHIFT,
+                         MXC_SSISTCR, module);
+}
+
+/*!
+ * This function configures the Transmit frame rate divider.
+ *
+ * @param        module        the module number
+ * @param        ratio         the divide ratio from 1 to 32
+ * @return       This function returns the result of the operation (0 if successful, -1 otherwise).
+ */
+int ssi_tx_frame_rate(ssi_mod module, unsigned char ratio)
+{
+       int result = -1;
+
+       if ((ratio >= SSI_MIN_FRAME_DIVIDER_RATIO) &&
+           (ratio <= SSI_MAX_FRAME_DIVIDER_RATIO)) {
+
+               set_register_bits(SSI_FRAME_DIVIDER_MASK <<
+                                 SSI_FRAME_RATE_DIVIDER_SHIFT,
+                                 (ratio - 1) << SSI_FRAME_RATE_DIVIDER_SHIFT,
+                                 MXC_SSISTCCR, module);
+               result = 0;
+       }
+
+       return result;
+}
+
+/*!
+ * This function controls the Frame Sync active polarity for the transmit section.
+ *
+ * @param        module        the module number
+ * @param        active        the Frame Sync active polarity
+ */
+void ssi_tx_frame_sync_active(ssi_mod module,
+                             ssi_tx_rx_frame_sync_active active)
+{
+       set_register_bits(1 << SSI_FRAME_SYNC_INVERT_SHIFT,
+                         active << SSI_FRAME_SYNC_INVERT_SHIFT,
+                         MXC_SSISTCR, module);
+}
+
+/*!
+ * This function controls the Frame Sync length (one word or one bit long) for the transmit section.
+ *
+ * @param        module        the module number
+ * @param        length        the Frame Sync length
+ */
+void ssi_tx_frame_sync_length(ssi_mod module,
+                             ssi_tx_rx_frame_sync_length length)
+{
+       set_register_bits(1 << SSI_FRAME_SYNC_LENGTH_SHIFT,
+                         length << SSI_FRAME_SYNC_LENGTH_SHIFT,
+                         MXC_SSISTCR, module);
+}
+
+/*!
+ * This function configures the time slot(s) to mask for the transmit section.
+ *
+ * @param        module        the module number
+ * @param        mask          the mask to indicate the time slot(s) masked
+ */
+void ssi_tx_mask_time_slot(ssi_mod module, unsigned int mask)
+{
+       set_register_bits(0xFFFFFFFF, mask, MXC_SSISTMSK, module);
+}
+
+/*!
+ * This function configures the Prescale divider for the transmit section.
+ *
+ * @param        module        the module number
+ * @param        divider       the divide ratio from 1 to  256
+ * @return       This function returns the result of the operation (0 if successful, -1 otherwise).
+ */
+int ssi_tx_prescaler_modulus(ssi_mod module, unsigned int divider)
+{
+       int result = -1;
+
+       if ((divider >= SSI_MIN_PRESCALER_MODULUS_RATIO) &&
+           (divider <= SSI_MAX_PRESCALER_MODULUS_RATIO)) {
+
+               set_register_bits(SSI_PRESCALER_MODULUS_MASK <<
+                                 SSI_PRESCALER_MODULUS_SHIFT,
+                                 (divider - 1) << SSI_PRESCALER_MODULUS_SHIFT,
+                                 MXC_SSISTCCR, module);
+               result = 0;
+       }
+
+       return result;
+}
+
+/*!
+ * This function controls whether the MSB or LSB will be transmitted first in a sample.
+ *
+ * @param        module        the module number
+ * @param        direction     the shift direction
+ */
+void ssi_tx_shift_direction(ssi_mod module, ssi_tx_rx_shift_direction direction)
+{
+       set_register_bits(1 << SSI_SHIFT_DIRECTION_SHIFT,
+                         direction << SSI_SHIFT_DIRECTION_SHIFT,
+                         MXC_SSISTCR, module);
+}
+
+/*!
+ * This function configures the Transmit word length.
+ *
+ * @param        module        the module number
+ * @param        length        the word length
+ */
+void ssi_tx_word_length(ssi_mod module, ssi_word_length length)
+{
+       set_register_bits(SSI_WORD_LENGTH_MASK << SSI_WORD_LENGTH_SHIFT,
+                         length << SSI_WORD_LENGTH_SHIFT,
+                         MXC_SSISTCCR, module);
+}
+
+/*!
+ * This function initializes the driver in terms of memory of the soundcard
+ * and some basic HW clock settings.
+ *
+ * @return              0 on success, -1 otherwise.
+ */
+static int __init ssi_probe(struct platform_device *pdev)
+{
+       int ret = -1;
+       ssi_platform_data =
+           (struct mxc_audio_platform_data *)pdev->dev.platform_data;
+       if (!ssi_platform_data) {
+               dev_err(&pdev->dev, "can't get the platform data for SSI\n");
+               return -EINVAL;
+       }
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       if (!res) {
+               dev_err(&pdev->dev, "can't get platform resource -  SSI\n");
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       if (pdev->id == 0) {
+               base_addr_1 = res->start;
+       } else if (pdev->id == 1) {
+               base_addr_2 = res->start;
+       }
+
+       printk(KERN_INFO "SSI %d module loaded successfully \n", pdev->id + 1);
+
+       return 0;
+      err:
+       return -1;
+
+}
+
+static int ssi_remove(struct platform_device *dev)
+{
+       return 0;
+}
+
+static struct platform_driver mxc_ssi_driver = {
+       .probe = ssi_probe,
+       .remove = ssi_remove,
+       .driver = {
+                  .name = "mxc_ssi",
+                  },
+};
+
+/*!
+ * This function implements the init function of the SSI device.
+ * This function is called when the module is loaded.
+ *
+ * @return       This function returns 0.
+ */
+static int __init ssi_init(void)
+{
+       spin_lock_init(&ssi_lock);
+       return platform_driver_register(&mxc_ssi_driver);
+
+}
+
+/*!
+ * This function implements the exit function of the SPI device.
+ * This function is called when the module is unloaded.
+ *
+ */
+static void __exit ssi_exit(void)
+{
+       platform_driver_unregister(&mxc_ssi_driver);
+       printk(KERN_INFO "SSI module unloaded successfully\n");
+}
+
+module_init(ssi_init);
+module_exit(ssi_exit);
+
+MODULE_DESCRIPTION("SSI char device driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/ssi/ssi.h b/drivers/mxc/ssi/ssi.h
new file mode 100644 (file)
index 0000000..f31edbe
--- /dev/null
@@ -0,0 +1,574 @@
+/*
+ * 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 SSI Synchronous Serial Interface (SSI) Driver
+  */
+
+ /*!
+  * @file ssi.h
+  * @brief This header file contains SSI driver functions prototypes.
+  *
+  * @ingroup SSI
+  */
+
+#ifndef __MXC_SSI_H__
+#define __MXC_SSI_H__
+
+#include "ssi_types.h"
+
+/*!
+ * This function gets the SSI fifo address.
+ *
+ * @param        ssi                 ssi number
+ * @param        direction           To indicate playback / recording
+ * @return       This function returns the SSI fifo address.
+ */
+unsigned int get_ssi_fifo_addr(unsigned int ssi, int direction);
+
+/*!
+ * This function controls the AC97 frame rate divider.
+ *
+ * @param        module               the module number
+ * @param        frame_rate_divider   the AC97 frame rate divider
+ */
+void ssi_ac97_frame_rate_divider(ssi_mod module,
+                                unsigned char frame_rate_divider);
+
+/*!
+ * This function gets the AC97 command address register.
+ *
+ * @param        module        the module number
+ * @return       This function returns the command address slot information.
+ */
+unsigned int ssi_ac97_get_command_address_register(ssi_mod module);
+
+/*!
+ * This function gets the AC97 command data register.
+ *
+ * @param        module        the module number
+ * @return       This function returns the command data slot information.
+ */
+unsigned int ssi_ac97_get_command_data_register(ssi_mod module);
+
+/*!
+ * This function gets the AC97 tag register.
+ *
+ * @param        module        the module number
+ * @return       This function returns the tag information.
+ */
+unsigned int ssi_ac97_get_tag_register(ssi_mod module);
+
+/*!
+ * This function controls the AC97 mode.
+ *
+ * @param        module        the module number
+ * @param        state         the AC97 mode state (enabled or disabled)
+ */
+void ssi_ac97_mode_enable(ssi_mod module, bool state);
+
+/*!
+ * This function controls the AC97 tag in FIFO behavior.
+ *
+ * @param        module        the module number
+ * @param        state         the tag in fifo behavior (Tag info stored in Rx FIFO 0 if TRUE,
+ * Tag info stored in SATAG register otherwise)
+ */
+void ssi_ac97_tag_in_fifo(ssi_mod module, bool state);
+
+/*!
+ * This function controls the AC97 read command.
+ *
+ * @param        module        the module number
+ * @param        state         the next AC97 command is a read command or not
+ */
+void ssi_ac97_read_command(ssi_mod module, bool state);
+
+/*!
+ * This function sets the AC97 command address register.
+ *
+ * @param        module        the module number
+ * @param        address       the command address slot information
+ */
+void ssi_ac97_set_command_address_register(ssi_mod module,
+                                          unsigned int address);
+
+/*!
+ * This function sets the AC97 command data register.
+ *
+ * @param        module        the module number
+ * @param        data          the command data slot information
+ */
+void ssi_ac97_set_command_data_register(ssi_mod module, unsigned int data);
+
+/*!
+ * This function sets the AC97 tag register.
+ *
+ * @param        module        the module number
+ * @param        tag           the tag information
+ */
+void ssi_ac97_set_tag_register(ssi_mod module, unsigned int tag);
+
+/*!
+ * This function controls the AC97 variable mode.
+ *
+ * @param        module        the module number
+ * @param        state         the AC97 variable mode state (enabled or disabled)
+ */
+void ssi_ac97_variable_mode(ssi_mod module, bool state);
+
+/*!
+ * This function controls the AC97 write command.
+ *
+ * @param        module        the module number
+ * @param        state         the next AC97 command is a write command or not
+ */
+void ssi_ac97_write_command(ssi_mod module, bool state);
+
+/*!
+ * This function controls the idle state of the transmit clock port during SSI internal gated mode.
+ *
+ * @param        module        the module number
+ * @param        state         the clock idle state
+ */
+void ssi_clock_idle_state(ssi_mod module, idle_state state);
+
+/*!
+ * This function turns off/on the ccm_ssi_clk to reduce power consumption.
+ *
+ * @param        module        the module number
+ * @param        state         the state for ccm_ssi_clk (true: turn off, else:turn on)
+ */
+void ssi_clock_off(ssi_mod module, bool state);
+
+/*!
+ * This function enables/disables the SSI module.
+ *
+ * @param        module        the module number
+ * @param        state         the state for SSI module
+ */
+void ssi_enable(ssi_mod module, bool state);
+
+/*!
+ * This function gets the data word in the Receive FIFO of the SSI module.
+ *
+ * @param        module        the module number
+ * @param        fifo          the Receive FIFO to read
+ * @return       This function returns the read data.
+ */
+unsigned int ssi_get_data(ssi_mod module, fifo_nb fifo);
+
+/*!
+ * This function returns the status of the SSI module (SISR register) as a combination of status.
+ *
+ * @param        module        the module number
+ * @return       This function returns the status of the SSI module.
+ */
+ssi_status_enable_mask ssi_get_status(ssi_mod module);
+
+/*!
+ * This function selects the I2S mode of the SSI module.
+ *
+ * @param        module        the module number
+ * @param        mode          the I2S mode
+ */
+void ssi_i2s_mode(ssi_mod module, mode_i2s mode);
+
+/*!
+ * This function disables the interrupts of the SSI module.
+ *
+ * @param        module        the module number
+ * @param        mask          the mask of the interrupts to disable
+ */
+void ssi_interrupt_disable(ssi_mod module, ssi_status_enable_mask mask);
+
+/*!
+ * This function enables the interrupts of the SSI module.
+ *
+ * @param        module        the module number
+ * @param        mask          the mask of the interrupts to enable
+ */
+void ssi_interrupt_enable(ssi_mod module, ssi_status_enable_mask mask);
+
+/*!
+ * This function enables/disables the network mode of the SSI module.
+ *
+ * @param        module        the module number
+ * @param        state         the network mode state
+ */
+void ssi_network_mode(ssi_mod module, bool state);
+
+/*!
+ * This function enables/disables the receive section of the SSI module.
+ *
+ * @param        module        the module number
+ * @param        state         the receive section state
+ */
+void ssi_receive_enable(ssi_mod module, bool state);
+
+/*!
+ * This function configures the SSI module to receive data word at bit position 0 or 23 in the Receive shift register.
+ *
+ * @param        module        the module number
+ * @param        state         the state to receive at bit 0
+ */
+void ssi_rx_bit0(ssi_mod module, bool state);
+
+/*!
+ * This function controls the source of the clock signal used to clock the Receive shift register.
+ *
+ * @param        module        the module number
+ * @param        direction     the clock signal direction
+ */
+void ssi_rx_clock_direction(ssi_mod module, ssi_tx_rx_direction direction);
+
+/*!
+ * This function configures the divide-by-two divider of the SSI module for the receive section.
+ *
+ * @param        module        the module number
+ * @param        state         the divider state
+ */
+void ssi_rx_clock_divide_by_two(ssi_mod module, bool state);
+
+/*!
+ * This function controls which bit clock edge is used to clock in data.
+ *
+ * @param        module        the module number
+ * @param        polarity      the clock polarity
+ */
+void ssi_rx_clock_polarity(ssi_mod module, ssi_tx_rx_clock_polarity polarity);
+
+/*!
+ * This function configures a fixed divide-by-eight clock prescaler divider of the SSI module in series with the variable prescaler for the receive section.
+ *
+ * @param        module        the module number
+ * @param        state         the prescaler state
+ */
+void ssi_rx_clock_prescaler(ssi_mod module, bool state);
+
+/*!
+ * This function controls the early frame sync configuration.
+ *
+ * @param        module        the module number
+ * @param        early         the early frame sync configuration
+ */
+void ssi_rx_early_frame_sync(ssi_mod module, ssi_tx_rx_early_frame_sync early);
+
+/*!
+ * This function gets the number of data words in the Receive FIFO.
+ *
+ * @param        module        the module number
+ * @param        fifo          the fifo
+ * @return       This function returns the number of words in the Rx FIFO.
+ */
+unsigned char ssi_rx_fifo_counter(ssi_mod module, fifo_nb fifo);
+
+/*!
+ * This function enables the Receive FIFO.
+ *
+ * @param        module        the module number
+ * @param        fifo          the fifo to enable
+ * @param        enabled       the state of the fifo, enabled or disabled
+ */
+void ssi_rx_fifo_enable(ssi_mod module, fifo_nb fifo, bool enabled);
+
+/*!
+ * This function controls the threshold at which the RFFx flag will be set.
+ *
+ * @param        module        the module number
+ * @param        fifo          the fifo to enable
+ * @param        watermark     the watermark
+ * @return       This function returns the result of the operation (0 if successful, -1 otherwise).
+ */
+int ssi_rx_fifo_full_watermark(ssi_mod module,
+                              fifo_nb fifo, unsigned char watermark);
+
+/*!
+ * This function flushes the Receive FIFOs.
+ *
+ * @param        module        the module number
+ */
+void ssi_rx_flush_fifo(ssi_mod module);
+
+/*!
+ * This function controls the direction of the Frame Sync signal for the receive section.
+ *
+ * @param        module        the module number
+ * @param        direction     the Frame Sync signal direction
+ */
+void ssi_rx_frame_direction(ssi_mod module, ssi_tx_rx_direction direction);
+
+/*!
+ * This function configures the Receive frame rate divider for the receive section.
+ *
+ * @param        module        the module number
+ * @param        ratio         the divide ratio from 1 to 32
+ * @return       This function returns the result of the operation (0 if successful, -1 otherwise).
+ */
+int ssi_rx_frame_rate(ssi_mod module, unsigned char ratio);
+
+/*!
+ * This function controls the Frame Sync active polarity for the receive section.
+ *
+ * @param        module        the module number
+ * @param        active        the Frame Sync active polarity
+ */
+void ssi_rx_frame_sync_active(ssi_mod module,
+                             ssi_tx_rx_frame_sync_active active);
+
+/*!
+ * This function controls the Frame Sync length (one word or one bit long) for the receive section.
+ *
+ * @param        module        the module number
+ * @param        length        the Frame Sync length
+ */
+void ssi_rx_frame_sync_length(ssi_mod module,
+                             ssi_tx_rx_frame_sync_length length);
+
+/*!
+ * This function configures the time slot(s) to mask for the receive section.
+ *
+ * @param        module        the module number
+ * @param        mask          the mask to indicate the time slot(s) masked
+ */
+void ssi_rx_mask_time_slot(ssi_mod module, unsigned int mask);
+
+/*!
+ * This function configures the Prescale divider for the receive section.
+ *
+ * @param        module        the module number
+ * @param        divider       the divide ratio from 1 to  256
+ * @return       This function returns the result of the operation (0 if successful, -1 otherwise).
+ */
+int ssi_rx_prescaler_modulus(ssi_mod module, unsigned int divider);
+
+/*!
+ * This function controls whether the MSB or LSB will be received first in a sample.
+ *
+ * @param        module        the module number
+ * @param        direction     the shift direction
+ */
+void ssi_rx_shift_direction(ssi_mod module,
+                           ssi_tx_rx_shift_direction direction);
+
+/*!
+ * This function configures the Receive word length.
+ *
+ * @param        module        the module number
+ * @param        length        the word length
+ */
+void ssi_rx_word_length(ssi_mod module, ssi_word_length length);
+
+/*!
+ * This function sets the data word in the Transmit FIFO of the SSI module.
+ *
+ * @param        module        the module number
+ * @param        fifo          the FIFO number
+ * @param        data          the data to load in the FIFO
+ */
+void ssi_set_data(ssi_mod module, fifo_nb fifo, unsigned int data);
+
+/*!
+ * This function controls the number of wait states between the core and SSI.
+ *
+ * @param        module        the module number
+ * @param        wait          the number of wait state(s)
+ */
+void ssi_set_wait_states(ssi_mod module, ssi_wait_states wait);
+
+/*!
+ * This function enables/disables the synchronous mode of the SSI module.
+ *
+ * @param        module        the module number
+ * @param        state         the synchronous mode state
+ */
+void ssi_synchronous_mode(ssi_mod module, bool state);
+
+/*!
+ * This function allows the SSI module to output the SYS_CLK at the SRCK port.
+ *
+ * @param        module        the module number
+ * @param        state         the system clock state
+ */
+void ssi_system_clock(ssi_mod module, bool state);
+
+/*!
+ * This function enables/disables the transmit section of the SSI module.
+ *
+ * @param        module        the module number
+ * @param        state         the transmit section state
+ */
+void ssi_transmit_enable(ssi_mod module, bool state);
+
+/*!
+ * This function allows the SSI module to operate in the two channel mode.
+ *
+ * @param        module        the module number
+ * @param        state         the two channel mode state
+ */
+void ssi_two_channel_mode(ssi_mod module, bool state);
+
+/*!
+ * This function configures the SSI module to transmit data word from bit position 0 or 23 in the Transmit shift register.
+ *
+ * @param        module        the module number
+ * @param        state         the transmit from bit 0 state
+ */
+void ssi_tx_bit0(ssi_mod module, bool state);
+
+/*!
+ * This function controls the direction of the clock signal used to clock the Transmit shift register.
+ *
+ * @param        module        the module number
+ * @param        direction     the clock signal direction
+ */
+void ssi_tx_clock_direction(ssi_mod module, ssi_tx_rx_direction direction);
+
+/*!
+ * This function configures the divide-by-two divider of the SSI module for the transmit section.
+ *
+ * @param        module        the module number
+ * @param        state         the divider state
+ */
+void ssi_tx_clock_divide_by_two(ssi_mod module, bool state);
+
+/*!
+ * This function controls which bit clock edge is used to clock out data.
+ *
+ * @param        module        the module number
+ * @param        polarity      the clock polarity
+ */
+void ssi_tx_clock_polarity(ssi_mod module, ssi_tx_rx_clock_polarity polarity);
+
+/*!
+ * This function configures a fixed divide-by-eight clock prescaler divider of the SSI module in series with the variable prescaler for the transmit section.
+ *
+ * @param        module        the module number
+ * @param        state         the prescaler state
+ */
+void ssi_tx_clock_prescaler(ssi_mod module, bool state);
+
+/*!
+ * This function controls the early frame sync configuration for the transmit section.
+ *
+ * @param        module        the module number
+ * @param        early         the early frame sync configuration
+ */
+void ssi_tx_early_frame_sync(ssi_mod module, ssi_tx_rx_early_frame_sync early);
+
+/*!
+ * This function gets the number of data words in the Transmit FIFO.
+ *
+ * @param        module        the module number
+ * @param        fifo          the fifo
+ * @return       This function returns the number of words in the Tx FIFO.
+ */
+unsigned char ssi_tx_fifo_counter(ssi_mod module, fifo_nb fifo);
+
+/*!
+ * This function controls the threshold at which the TFEx flag will be set.
+ *
+ * @param        module        the module number
+ * @param        fifo          the fifo to enable
+ * @param        watermark     the watermark
+ * @return       This function returns the result of the operation (0 if successful, -1 otherwise).
+ */
+int ssi_tx_fifo_empty_watermark(ssi_mod module, fifo_nb fifo,
+                               unsigned char watermark);
+
+/*!
+ * This function enables the Transmit FIFO.
+ *
+ * @param        module        the module number
+ * @param        fifo          the fifo to enable
+ * @param        enable        the state of the FIFO, enabled or disabled
+ */
+void ssi_tx_fifo_enable(ssi_mod module, fifo_nb fifo, bool enable);
+
+/*!
+ * This function flushes the Transmit FIFOs.
+ *
+ * @param        module        the module number
+ */
+void ssi_tx_flush_fifo(ssi_mod module);
+
+/*!
+ * This function controls the direction of the Frame Sync signal for the transmit section.
+ *
+ * @param        module        the module number
+ * @param        direction     the Frame Sync signal direction
+ */
+void ssi_tx_frame_direction(ssi_mod module, ssi_tx_rx_direction direction);
+
+/*!
+ * This function configures the Transmit frame rate divider.
+ *
+ * @param        module        the module number
+ * @param        ratio         the divide ratio from 1 to 32
+ * @return       This function returns the result of the operation (0 if successful, -1 otherwise).
+ */
+int ssi_tx_frame_rate(ssi_mod module, unsigned char ratio);
+
+/*!
+ * This function controls the Frame Sync active polarity for the transmit section.
+ *
+ * @param        module        the module number
+ * @param        active        the Frame Sync active polarity
+ */
+void ssi_tx_frame_sync_active(ssi_mod module,
+                             ssi_tx_rx_frame_sync_active active);
+
+/*!
+ * This function controls the Frame Sync length (one word or one bit long) for the transmit section.
+ *
+ * @param        module        the module number
+ * @param        length        the Frame Sync length
+ */
+void ssi_tx_frame_sync_length(ssi_mod module,
+                             ssi_tx_rx_frame_sync_length length);
+
+/*!
+ * This function configures the time slot(s) to mask for the transmit section.
+ *
+ * @param        module        the module number
+ * @param        mask          the mask to indicate the time slot(s) masked
+ */
+void ssi_tx_mask_time_slot(ssi_mod module, unsigned int mask);
+
+/*!
+ * This function configures the Prescale divider for the transmit section.
+ *
+ * @param        module        the module number
+ * @param        divider       the divide ratio from 1 to  256
+ * @return       This function returns the result of the operation (0 if successful, -1 otherwise).
+ */
+int ssi_tx_prescaler_modulus(ssi_mod module, unsigned int divider);
+
+/*!
+ * This function controls whether the MSB or LSB will be transmited first in a sample.
+ *
+ * @param        module        the module number
+ * @param        direction     the shift direction
+ */
+void ssi_tx_shift_direction(ssi_mod module,
+                           ssi_tx_rx_shift_direction direction);
+
+/*!
+ * This function configures the Transmit word length.
+ *
+ * @param        module        the module number
+ * @param        length        the word length
+ */
+void ssi_tx_word_length(ssi_mod module, ssi_word_length length);
+
+#endif                         /* __MXC_SSI_H__ */
diff --git a/drivers/mxc/ssi/ssi_types.h b/drivers/mxc/ssi/ssi_types.h
new file mode 100644 (file)
index 0000000..eb8024d
--- /dev/null
@@ -0,0 +1,367 @@
+/*
+ * 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 ssi_types.h
+  * @brief This header file contains SSI types.
+  *
+  * @ingroup SSI
+  */
+
+#ifndef __MXC_SSI_TYPES_H__
+#define __MXC_SSI_TYPES_H__
+
+/*!
+ * This enumeration describes the FIFO number.
+ */
+typedef enum {
+       /*!
+        * FIFO 0
+        */
+       ssi_fifo_0 = 0,
+       /*!
+        * FIFO 1
+        */
+       ssi_fifo_1 = 1
+} fifo_nb;
+
+/*!
+ * This enumeration describes the clock idle state.
+ */
+typedef enum {
+       /*!
+        * Clock idle state is 1
+        */
+       clock_idle_state_1 = 0,
+       /*!
+        * Clock idle state is 0
+        */
+       clock_idle_state_0 = 1
+} idle_state;
+
+/*!
+ * This enumeration describes I2S mode.
+ */
+typedef enum {
+       /*!
+        * Normal mode
+        */
+       i2s_normal = 0,
+       /*!
+        * Master mode
+        */
+       i2s_master = 1,
+       /*!
+        * Slave mode
+        */
+       i2s_slave = 2
+} mode_i2s;
+
+/*!
+ * This enumeration describes index for both SSI1 and SSI2 modules.
+ */
+typedef enum {
+       /*!
+        * SSI1 index
+        */
+       SSI1 = 0,
+       /*!
+        * SSI2 index not present on MXC 91221 and MXC91311
+        */
+       SSI2 = 1
+} ssi_mod;
+
+/*!
+ * This enumeration describes the status/enable bits for interrupt source of the SSI module.
+ */
+typedef enum {
+       /*!
+        * SSI Transmit FIFO 0 empty bit
+        */
+       ssi_tx_fifo_0_empty = 0x00000001,
+       /*!
+        * SSI Transmit FIFO 1 empty bit
+        */
+       ssi_tx_fifo_1_empty = 0x00000002,
+       /*!
+        * SSI Receive FIFO 0 full bit
+        */
+       ssi_rx_fifo_0_full = 0x00000004,
+       /*!
+        * SSI Receive FIFO 1 full bit
+        */
+       ssi_rx_fifo_1_full = 0x00000008,
+       /*!
+        * SSI Receive Last Time Slot bit
+        */
+       ssi_rls = 0x00000010,
+       /*!
+        * SSI Transmit Last Time Slot bit
+        */
+       ssi_tls = 0x00000020,
+       /*!
+        * SSI Receive Frame Sync bit
+        */
+       ssi_rfs = 0x00000040,
+       /*!
+        * SSI Transmit Frame Sync bit
+        */
+       ssi_tfs = 0x00000080,
+       /*!
+        * SSI Transmitter underrun 0 bit
+        */
+       ssi_transmitter_underrun_0 = 0x00000100,
+       /*!
+        * SSI Transmitter underrun 1 bit
+        */
+       ssi_transmitter_underrun_1 = 0x00000200,
+       /*!
+        * SSI Receiver overrun 0 bit
+        */
+       ssi_receiver_overrun_0 = 0x00000400,
+       /*!
+        * SSI Receiver overrun 1 bit
+        */
+       ssi_receiver_overrun_1 = 0x00000800,
+       /*!
+        * SSI Transmit Data register empty 0 bit
+        */
+       ssi_tx_data_reg_empty_0 = 0x00001000,
+       /*!
+        * SSI Transmit Data register empty 1 bit
+        */
+       ssi_tx_data_reg_empty_1 = 0x00002000,
+
+       /*!
+        * SSI Receive Data Ready 0 bit
+        */
+       ssi_rx_data_ready_0 = 0x00004000,
+       /*!
+        * SSI Receive Data Ready 1 bit
+        */
+       ssi_rx_data_ready_1 = 0x00008000,
+       /*!
+        * SSI Receive tag updated bit
+        */
+       ssi_rx_tag_updated = 0x00010000,
+       /*!
+        * SSI Command data register updated bit
+        */
+       ssi_cmd_data_reg_updated = 0x00020000,
+       /*!
+        * SSI Command address register updated bit
+        */
+       ssi_cmd_address_reg_updated = 0x00040000,
+       /*!
+        * SSI Transmit interrupt enable bit
+        */
+       ssi_tx_interrupt_enable = 0x00080000,
+       /*!
+        * SSI Transmit DMA enable bit
+        */
+       ssi_tx_dma_interrupt_enable = 0x00100000,
+       /*!
+        * SSI Receive interrupt enable bit
+        */
+       ssi_rx_interrupt_enable = 0x00200000,
+       /*!
+        * SSI Receive DMA enable bit
+        */
+       ssi_rx_dma_interrupt_enable = 0x00400000,
+       /*!
+        * SSI Tx frame complete enable bit on MXC91221 & MXC91311 only
+        */
+       ssi_tx_frame_complete = 0x00800000,
+       /*!
+        * SSI Rx frame complete on MXC91221 & MXC91311 only
+        */
+       ssi_rx_frame_complete = 0x001000000
+} ssi_status_enable_mask;
+
+/*!
+ * This enumeration describes the clock edge to clock in or clock out data.
+ */
+typedef enum {
+       /*!
+        * Clock on rising edge
+        */
+       ssi_clock_on_rising_edge = 0,
+       /*!
+        * Clock on falling edge
+        */
+       ssi_clock_on_falling_edge = 1
+} ssi_tx_rx_clock_polarity;
+
+/*!
+ * This enumeration describes the clock direction.
+ */
+typedef enum {
+       /*!
+        * Clock is external
+        */
+       ssi_tx_rx_externally = 0,
+       /*!
+        * Clock is generated internally
+        */
+       ssi_tx_rx_internally = 1
+} ssi_tx_rx_direction;
+
+/*!
+ * This enumeration describes the early frame sync behavior.
+ */
+typedef enum {
+       /*!
+        * Frame Sync starts on the first data bit
+        */
+       ssi_frame_sync_first_bit = 0,
+       /*!
+        * Frame Sync starts one bit before the first data bit
+        */
+       ssi_frame_sync_one_bit_before = 1
+} ssi_tx_rx_early_frame_sync;
+
+/*!
+ * This enumeration describes the Frame Sync active value.
+ */
+typedef enum {
+       /*!
+        * Frame Sync is active when high
+        */
+       ssi_frame_sync_active_high = 0,
+       /*!
+        * Frame Sync is active when low
+        */
+       ssi_frame_sync_active_low = 1
+} ssi_tx_rx_frame_sync_active;
+
+/*!
+ * This enumeration describes the Frame Sync active length.
+ */
+typedef enum {
+       /*!
+        * Frame Sync is active when high
+        */
+       ssi_frame_sync_one_word = 0,
+       /*!
+        * Frame Sync is active when low
+        */
+       ssi_frame_sync_one_bit = 1
+} ssi_tx_rx_frame_sync_length;
+
+/*!
+ * This enumeration describes the Tx/Rx frame shift direction.
+ */
+typedef enum {
+       /*!
+        * MSB first
+        */
+       ssi_msb_first = 0,
+       /*!
+        * LSB first
+        */
+       ssi_lsb_first = 1
+} ssi_tx_rx_shift_direction;
+
+/*!
+ * This enumeration describes the wait state number.
+ */
+typedef enum {
+       /*!
+        * 0 wait state
+        */
+       ssi_waitstates0 = 0x0,
+       /*!
+        * 1 wait state
+        */
+       ssi_waitstates1 = 0x1,
+       /*!
+        * 2 wait states
+        */
+       ssi_waitstates2 = 0x2,
+       /*!
+        * 3 wait states
+        */
+       ssi_waitstates3 = 0x3
+} ssi_wait_states;
+
+/*!
+ * This enumeration describes the word length.
+ */
+typedef enum {
+       /*!
+        * 2 bits long
+        */
+       ssi_2_bits = 0x0,
+       /*!
+        * 4 bits long
+        */
+       ssi_4_bits = 0x1,
+       /*!
+        * 6 bits long
+        */
+       ssi_6_bits = 0x2,
+       /*!
+        * 8 bits long
+        */
+       ssi_8_bits = 0x3,
+       /*!
+        * 10 bits long
+        */
+       ssi_10_bits = 0x4,
+       /*!
+        * 12 bits long
+        */
+       ssi_12_bits = 0x5,
+       /*!
+        * 14 bits long
+        */
+       ssi_14_bits = 0x6,
+       /*!
+        * 16 bits long
+        */
+       ssi_16_bits = 0x7,
+       /*!
+        * 18 bits long
+        */
+       ssi_18_bits = 0x8,
+       /*!
+        * 20 bits long
+        */
+       ssi_20_bits = 0x9,
+       /*!
+        * 22 bits long
+        */
+       ssi_22_bits = 0xA,
+       /*!
+        * 24 bits long
+        */
+       ssi_24_bits = 0xB,
+       /*!
+        * 26 bits long
+        */
+       ssi_26_bits = 0xC,
+       /*!
+        * 28 bits long
+        */
+       ssi_28_bits = 0xD,
+       /*!
+        * 30 bits long
+        */
+       ssi_30_bits = 0xE,
+       /*!
+        * 32 bits long
+        */
+       ssi_32_bits = 0xF
+} ssi_word_length;
+
+#endif                         /* __MXC_SSI_TYPES_H__ */
diff --git a/drivers/mxc/vpu/Kconfig b/drivers/mxc/vpu/Kconfig
new file mode 100644 (file)
index 0000000..37e55e0
--- /dev/null
@@ -0,0 +1,30 @@
+#
+# Codec configuration
+#
+
+menu "MXC VPU(Video Processing Unit) support"
+
+config MXC_VPU
+         tristate "Support for MXC VPU(Video Processing Unit)"
+         depends on (ARCH_MX3 || ARCH_MX27 || ARCH_MX37 || ARCH_MX5)
+         default y
+       ---help---
+         The VPU codec device provides codec function for H.264/MPEG4/H.263,
+         as well as MPEG2/VC-1/DivX on some platforms.
+
+config MXC_VPU_IRAM
+         tristate "Use IRAM as temporary buffer for VPU to enhance performace"
+         depends on (ARCH_MX37 || ARCH_MX5)
+         default y
+       ---help---
+         The VPU can use internal RAM as temporary buffer to save external
+         memroy bandwith, thus to enhance video performance.
+
+config MXC_VPU_DEBUG
+       bool "MXC VPU debugging"
+       depends on MXC_VPU != n
+       help
+         This is an option for the developers; most people should
+         say N here.  This enables MXC VPU driver debugging.
+
+endmenu
diff --git a/drivers/mxc/vpu/Makefile b/drivers/mxc/vpu/Makefile
new file mode 100644 (file)
index 0000000..88e8f2c
--- /dev/null
@@ -0,0 +1,10 @@
+#
+# Makefile for the VPU drivers.
+#
+
+obj-$(CONFIG_MXC_VPU)                  += vpu.o
+vpu-objs       := mxc_vpu.o mxc_vl2cc.o
+
+ifeq ($(CONFIG_MXC_VPU_DEBUG),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
diff --git a/drivers/mxc/vpu/mxc_vl2cc.c b/drivers/mxc/vpu/mxc_vl2cc.c
new file mode 100644 (file)
index 0000000..f30efea
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * 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 mxc_vl2cc.c
+ *
+ * @brief VL2CC initialization and flush operation implementation
+ *
+ * @ingroup VL2CC
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+
+#define VL2CC_CTRL_OFFSET      (0x100)
+#define VL2CC_AUXCTRL_OFFSET   (0x104)
+#define VL2CC_INVWAY_OFFSET    (0x77C)
+#define VL2CC_CLEANWAY_OFFSET  (0x7BC)
+
+/*! VL2CC clock handle. */
+static struct clk *vl2cc_clk;
+static u32 *vl2cc_base;
+
+/*!
+ * Initialization function of VL2CC. Remap the VL2CC base address.
+ *
+ * @return status  0 success.
+ */
+int vl2cc_init(u32 vl2cc_hw_base)
+{
+       vl2cc_base = ioremap(vl2cc_hw_base, SZ_8K - 1);
+       if (vl2cc_base == NULL) {
+               printk(KERN_INFO "vl2cc: Unable to ioremap\n");
+               return -ENOMEM;
+       }
+
+       vl2cc_clk = clk_get(NULL, "vl2cc_clk");
+       if (IS_ERR(vl2cc_clk)) {
+               printk(KERN_INFO "vl2cc: Unable to get clock\n");
+               iounmap(vl2cc_base);
+               return -EIO;
+       }
+
+       printk(KERN_INFO "VL2CC initialized\n");
+       return 0;
+}
+
+/*!
+ * Enable VL2CC hardware
+ */
+void vl2cc_enable(void)
+{
+       volatile u32 reg;
+
+       clk_enable(vl2cc_clk);
+
+       /* Disable VL2CC */
+       reg = __raw_readl(vl2cc_base + VL2CC_CTRL_OFFSET);
+       reg &= 0xFFFFFFFE;
+       __raw_writel(reg, vl2cc_base + VL2CC_CTRL_OFFSET);
+
+       /* Set the latency for data RAM reads, data RAM writes, tag RAM and
+        * dirty RAM to 1 cycle - write 0x0 to AUX CTRL [11:0] and also
+        * configure the number of ways to 8 - write 8 to AUX CTRL [16:13]
+        */
+       reg = __raw_readl(vl2cc_base + VL2CC_AUXCTRL_OFFSET);
+       reg &= 0xFFFE1000;      /* Clear [16:13] too */
+       reg |= (0x8 << 13);     /* [16:13] = 8; */
+       __raw_writel(reg, vl2cc_base + VL2CC_AUXCTRL_OFFSET);
+
+       /* Invalidate the VL2CC ways - write 0xff to INV BY WAY and poll the
+        * register until its value is 0x0
+        */
+       __raw_writel(0xff, vl2cc_base + VL2CC_INVWAY_OFFSET);
+       while (__raw_readl(vl2cc_base + VL2CC_INVWAY_OFFSET) != 0x0)
+               ;
+
+       /* Enable VL2CC */
+       reg = __raw_readl(vl2cc_base + VL2CC_CTRL_OFFSET);
+       reg |= 0x1;
+       __raw_writel(reg, vl2cc_base + VL2CC_CTRL_OFFSET);
+}
+
+/*!
+ * Flush VL2CC
+ */
+void vl2cc_flush(void)
+{
+       __raw_writel(0xff, vl2cc_base + VL2CC_CLEANWAY_OFFSET);
+       while (__raw_readl(vl2cc_base + VL2CC_CLEANWAY_OFFSET) != 0x0)
+               ;
+}
+
+/*!
+ * Disable VL2CC
+ */
+void vl2cc_disable(void)
+{
+       __raw_writel(0, vl2cc_base + VL2CC_CTRL_OFFSET);
+       clk_disable(vl2cc_clk);
+}
+
+/*!
+ * Cleanup VL2CC
+ */
+void vl2cc_cleanup(void)
+{
+       clk_put(vl2cc_clk);
+       iounmap(vl2cc_base);
+}
diff --git a/drivers/mxc/vpu/mxc_vpu.c b/drivers/mxc/vpu/mxc_vpu.c
new file mode 100644 (file)
index 0000000..afac6d9
--- /dev/null
@@ -0,0 +1,884 @@
+/*
+ * Copyright 2006-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 mxc_vpu.c
+ *
+ * @brief VPU system initialization and file operation implementation
+ *
+ * @ingroup VPU
+ */
+
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/stat.h>
+#include <linux/platform_device.h>
+#include <linux/kdev_t.h>
+#include <linux/dma-mapping.h>
+#include <linux/iram_alloc.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/fsl_devices.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/sched.h>
+
+#include <asm/sizes.h>
+#include <mach/clock.h>
+#include <mach/hardware.h>
+
+#include <mach/mxc_vpu.h>
+
+struct vpu_priv {
+       struct fasync_struct *async_queue;
+       struct work_struct work;
+       struct workqueue_struct *workqueue;
+};
+
+/* To track the allocated memory buffer */
+typedef struct memalloc_record {
+       struct list_head list;
+       struct vpu_mem_desc mem;
+} memalloc_record;
+
+struct iram_setting {
+       u32 start;
+       u32 end;
+};
+
+static DEFINE_SPINLOCK(vpu_lock);
+static LIST_HEAD(head);
+
+static int vpu_major;
+static int vpu_clk_usercount;
+static struct class *vpu_class;
+static struct vpu_priv vpu_data;
+static u8 open_count;
+static struct clk *vpu_clk;
+static struct vpu_mem_desc bitwork_mem = { 0 };
+static struct vpu_mem_desc pic_para_mem = { 0 };
+static struct vpu_mem_desc user_data_mem = { 0 };
+static struct vpu_mem_desc share_mem = { 0 };
+
+static void __iomem *vpu_base;
+static int vpu_irq;
+static u32 phy_vpu_base_addr;
+static struct mxc_vpu_platform_data *vpu_plat;
+
+/* IRAM setting */
+static struct iram_setting iram;
+
+/* implement the blocking ioctl */
+static int codec_done;
+static wait_queue_head_t vpu_queue;
+
+static u32 workctrl_regsave[6];
+static u32 rd_ptr_regsave[4];
+static u32 wr_ptr_regsave[4];
+static u32 dis_flag_regsave[4];
+
+#define        READ_REG(x)             __raw_readl(vpu_base + x)
+#define        WRITE_REG(val, x)       __raw_writel(val, vpu_base + x)
+#define        SAVE_WORK_REGS  do {                                    \
+       int i;                                                  \
+       for (i = 0; i < ARRAY_SIZE(workctrl_regsave)/2; i++)    \
+               workctrl_regsave[i] = READ_REG(BIT_WORK_CTRL_BUF_REG(i));\
+} while (0)
+#define        RESTORE_WORK_REGS       do {                            \
+       int i;                                                  \
+       for (i = 0; i < ARRAY_SIZE(workctrl_regsave)/2; i++)    \
+               WRITE_REG(workctrl_regsave[i], BIT_WORK_CTRL_BUF_REG(i));\
+} while (0)
+#define        SAVE_CTRL_REGS  do {                                    \
+       int i;                                                  \
+       for (i = ARRAY_SIZE(workctrl_regsave)/2;                \
+                       i < ARRAY_SIZE(workctrl_regsave); i++)          \
+               workctrl_regsave[i] = READ_REG(BIT_WORK_CTRL_BUF_REG(i));\
+} while (0)
+#define        RESTORE_CTRL_REGS       do {                            \
+       int i;                                                  \
+       for (i = ARRAY_SIZE(workctrl_regsave)/2;                \
+                       i < ARRAY_SIZE(workctrl_regsave); i++)          \
+               WRITE_REG(workctrl_regsave[i], BIT_WORK_CTRL_BUF_REG(i));\
+} while (0)
+#define        SAVE_RDWR_PTR_REGS      do {                                    \
+       int i;                                                          \
+       for (i = 0; i < ARRAY_SIZE(rd_ptr_regsave); i++)                \
+               rd_ptr_regsave[i] = READ_REG(BIT_RD_PTR_REG(i));        \
+       for (i = 0; i < ARRAY_SIZE(wr_ptr_regsave); i++)                \
+               wr_ptr_regsave[i] = READ_REG(BIT_WR_PTR_REG(i));        \
+} while (0)
+#define        RESTORE_RDWR_PTR_REGS   do {                                    \
+       int i;                                                          \
+       for (i = 0; i < ARRAY_SIZE(rd_ptr_regsave); i++)                \
+               WRITE_REG(rd_ptr_regsave[i], BIT_RD_PTR_REG(i));        \
+       for (i = 0; i < ARRAY_SIZE(wr_ptr_regsave); i++)                \
+               WRITE_REG(wr_ptr_regsave[i], BIT_WR_PTR_REG(i));        \
+} while (0)
+#define        SAVE_DIS_FLAG_REGS      do {                                    \
+       int i;                                                          \
+       for (i = 0; i < ARRAY_SIZE(dis_flag_regsave); i++)              \
+               dis_flag_regsave[i] = READ_REG(BIT_FRM_DIS_FLG_REG(i)); \
+} while (0)
+#define        RESTORE_DIS_FLAG_REGS   do {                                    \
+       int i;                                                          \
+       for (i = 0; i < ARRAY_SIZE(dis_flag_regsave); i++)              \
+               WRITE_REG(dis_flag_regsave[i], BIT_FRM_DIS_FLG_REG(i)); \
+} while (0)
+
+/*!
+ * Private function to alloc dma buffer
+ * @return status  0 success.
+ */
+static int vpu_alloc_dma_buffer(struct vpu_mem_desc *mem)
+{
+       mem->cpu_addr = (unsigned long)
+           dma_alloc_coherent(NULL, PAGE_ALIGN(mem->size),
+                              (dma_addr_t *) (&mem->phy_addr),
+                              GFP_DMA | GFP_KERNEL);
+       pr_debug("[ALLOC] mem alloc cpu_addr = 0x%x\n", mem->cpu_addr);
+       if ((void *)(mem->cpu_addr) == NULL) {
+               printk(KERN_ERR "Physical memory allocation error!\n");
+               return -1;
+       }
+       return 0;
+}
+
+/*!
+ * Private function to free dma buffer
+ */
+static void vpu_free_dma_buffer(struct vpu_mem_desc *mem)
+{
+       if (mem->cpu_addr != 0) {
+               dma_free_coherent(0, PAGE_ALIGN(mem->size),
+                                 (void *)mem->cpu_addr, mem->phy_addr);
+       }
+}
+
+/*!
+ * Private function to free buffers
+ * @return status  0 success.
+ */
+static int vpu_free_buffers(void)
+{
+       struct memalloc_record *rec, *n;
+       struct vpu_mem_desc mem;
+
+       list_for_each_entry_safe(rec, n, &head, list) {
+               mem = rec->mem;
+               if (mem.cpu_addr != 0) {
+                       vpu_free_dma_buffer(&mem);
+                       pr_debug("[FREE] freed paddr=0x%08X\n", mem.phy_addr);
+                       /* delete from list */
+                       list_del(&rec->list);
+                       kfree(rec);
+               }
+       }
+
+       return 0;
+}
+
+static inline void vpu_worker_callback(struct work_struct *w)
+{
+       struct vpu_priv *dev = container_of(w, struct vpu_priv,
+                               work);
+
+       if (dev->async_queue)
+               kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
+
+       codec_done = 1;
+       wake_up_interruptible(&vpu_queue);
+
+       /*
+        * Clock is gated on when dec/enc started, gate it off when
+        * interrupt is received.
+        */
+       clk_disable(vpu_clk);
+}
+
+/*!
+ * @brief vpu interrupt handler
+ */
+static irqreturn_t vpu_irq_handler(int irq, void *dev_id)
+{
+       struct vpu_priv *dev = dev_id;
+
+       READ_REG(BIT_INT_STATUS);
+       WRITE_REG(0x1, BIT_INT_CLEAR);
+
+       queue_work(dev->workqueue, &dev->work);
+
+       return IRQ_HANDLED;
+}
+
+/*!
+ * @brief open function for vpu file operation
+ *
+ * @return  0 on success or negative error code on error
+ */
+static int vpu_open(struct inode *inode, struct file *filp)
+{
+       spin_lock(&vpu_lock);
+       if ((open_count++ == 0) && cpu_is_mx32())
+               vl2cc_enable();
+       filp->private_data = (void *)(&vpu_data);
+       spin_unlock(&vpu_lock);
+       return 0;
+}
+
+/*!
+ * @brief IO ctrl function for vpu file operation
+ * @param cmd IO ctrl command
+ * @return  0 on success or negative error code on error
+ */
+static long vpu_ioctl(struct file *filp, u_int cmd,
+                    u_long arg)
+{
+       int ret = 0;
+
+       switch (cmd) {
+       case VPU_IOC_PHYMEM_ALLOC:
+               {
+                       struct memalloc_record *rec;
+
+                       rec = kzalloc(sizeof(*rec), GFP_KERNEL);
+                       if (!rec)
+                               return -ENOMEM;
+
+                       ret = copy_from_user(&(rec->mem),
+                                            (struct vpu_mem_desc *)arg,
+                                            sizeof(struct vpu_mem_desc));
+                       if (ret) {
+                               kfree(rec);
+                               return -EFAULT;
+                       }
+
+                       pr_debug("[ALLOC] mem alloc size = 0x%x\n",
+                                rec->mem.size);
+
+                       ret = vpu_alloc_dma_buffer(&(rec->mem));
+                       if (ret == -1) {
+                               kfree(rec);
+                               printk(KERN_ERR
+                                      "Physical memory allocation error!\n");
+                               break;
+                       }
+                       ret = copy_to_user((void __user *)arg, &(rec->mem),
+                                          sizeof(struct vpu_mem_desc));
+                       if (ret) {
+                               kfree(rec);
+                               ret = -EFAULT;
+                               break;
+                       }
+
+                       spin_lock(&vpu_lock);
+                       list_add(&rec->list, &head);
+                       spin_unlock(&vpu_lock);
+
+                       break;
+               }
+       case VPU_IOC_PHYMEM_FREE:
+               {
+                       struct memalloc_record *rec, *n;
+                       struct vpu_mem_desc vpu_mem;
+
+                       ret = copy_from_user(&vpu_mem,
+                                            (struct vpu_mem_desc *)arg,
+                                            sizeof(struct vpu_mem_desc));
+                       if (ret)
+                               return -EACCES;
+
+                       pr_debug("[FREE] mem freed cpu_addr = 0x%x\n",
+                                vpu_mem.cpu_addr);
+                       if ((void *)vpu_mem.cpu_addr != NULL) {
+                               vpu_free_dma_buffer(&vpu_mem);
+                       }
+
+                       spin_lock(&vpu_lock);
+                       list_for_each_entry_safe(rec, n, &head, list) {
+                               if (rec->mem.cpu_addr == vpu_mem.cpu_addr) {
+                                       /* delete from list */
+                                       list_del(&rec->list);
+                                       kfree(rec);
+                                       break;
+                               }
+                       }
+                       spin_unlock(&vpu_lock);
+
+                       break;
+               }
+       case VPU_IOC_WAIT4INT:
+               {
+                       u_long timeout = (u_long) arg;
+                       if (!wait_event_interruptible_timeout
+                           (vpu_queue, codec_done != 0,
+                            msecs_to_jiffies(timeout))) {
+                               printk(KERN_WARNING "VPU blocking: timeout.\n");
+                               ret = -ETIME;
+                       } else if (signal_pending(current)) {
+                               printk(KERN_WARNING
+                                      "VPU interrupt received.\n");
+                               ret = -ERESTARTSYS;
+                       } else
+                               codec_done = 0;
+                       break;
+               }
+       case VPU_IOC_VL2CC_FLUSH:
+               if (cpu_is_mx32()) {
+                       vl2cc_flush();
+               }
+               break;
+       case VPU_IOC_IRAM_SETTING:
+               {
+                       ret = copy_to_user((void __user *)arg, &iram,
+                                          sizeof(struct iram_setting));
+                       if (ret)
+                               ret = -EFAULT;
+
+                       break;
+               }
+       case VPU_IOC_CLKGATE_SETTING:
+               {
+                       u32 clkgate_en;
+
+                       if (get_user(clkgate_en, (u32 __user *) arg))
+                               return -EFAULT;
+
+                       if (clkgate_en) {
+                               clk_enable(vpu_clk);
+                       } else {
+                               clk_disable(vpu_clk);
+                       }
+
+                       break;
+               }
+       case VPU_IOC_GET_SHARE_MEM:
+               {
+                       spin_lock(&vpu_lock);
+                       if (share_mem.cpu_addr != 0) {
+                               ret = copy_to_user((void __user *)arg,
+                                                  &share_mem,
+                                                  sizeof(struct vpu_mem_desc));
+                               spin_unlock(&vpu_lock);
+                               break;
+                       } else {
+                               if (copy_from_user(&share_mem,
+                                                  (struct vpu_mem_desc *)arg,
+                                                sizeof(struct vpu_mem_desc))) {
+                                       spin_unlock(&vpu_lock);
+                                       return -EFAULT;
+                               }
+                               if (vpu_alloc_dma_buffer(&share_mem) == -1)
+                                       ret = -EFAULT;
+                               else {
+                                       if (copy_to_user((void __user *)arg,
+                                                        &share_mem,
+                                                        sizeof(struct
+                                                               vpu_mem_desc)))
+                                               ret = -EFAULT;
+                               }
+                       }
+                       spin_unlock(&vpu_lock);
+                       break;
+               }
+       case VPU_IOC_GET_WORK_ADDR:
+               {
+                       if (bitwork_mem.cpu_addr != 0) {
+                               ret =
+                                   copy_to_user((void __user *)arg,
+                                                &bitwork_mem,
+                                                sizeof(struct vpu_mem_desc));
+                               break;
+                       } else {
+                               if (copy_from_user(&bitwork_mem,
+                                                  (struct vpu_mem_desc *)arg,
+                                                  sizeof(struct vpu_mem_desc)))
+                                       return -EFAULT;
+
+                               if (vpu_alloc_dma_buffer(&bitwork_mem) == -1)
+                                       ret = -EFAULT;
+                               else if (copy_to_user((void __user *)arg,
+                                                     &bitwork_mem,
+                                                     sizeof(struct
+                                                            vpu_mem_desc)))
+                                       ret = -EFAULT;
+                       }
+                       break;
+               }
+       case VPU_IOC_GET_PIC_PARA_ADDR:
+               {
+                       if (pic_para_mem.cpu_addr != 0) {
+                               ret =
+                                   copy_to_user((void __user *)arg,
+                                                &pic_para_mem,
+                                                sizeof(struct vpu_mem_desc));
+                               break;
+                       } else {
+                               if (copy_from_user(&pic_para_mem,
+                                                  (struct vpu_mem_desc *)arg,
+                                                  sizeof(struct vpu_mem_desc)))
+                                       return -EFAULT;
+
+                               if (vpu_alloc_dma_buffer(&pic_para_mem) == -1)
+                                       ret = -EFAULT;
+                               else if (copy_to_user((void __user *)arg,
+                                                     &pic_para_mem,
+                                                     sizeof(struct
+                                                            vpu_mem_desc)))
+                                       ret = -EFAULT;
+                       }
+                       break;
+               }
+       case VPU_IOC_GET_USER_DATA_ADDR:
+               {
+                       if (user_data_mem.cpu_addr != 0) {
+                               ret =
+                                   copy_to_user((void __user *)arg,
+                                                &user_data_mem,
+                                                sizeof(struct vpu_mem_desc));
+                               break;
+                       } else {
+                               if (copy_from_user(&user_data_mem,
+                                                  (struct vpu_mem_desc *)arg,
+                                                  sizeof(struct vpu_mem_desc)))
+                                       return -EFAULT;
+
+                               if (vpu_alloc_dma_buffer(&user_data_mem) == -1)
+                                       ret = -EFAULT;
+                               else if (copy_to_user((void __user *)arg,
+                                                     &user_data_mem,
+                                                     sizeof(struct
+                                                            vpu_mem_desc)))
+                                       ret = -EFAULT;
+                       }
+                       break;
+               }
+       case VPU_IOC_SYS_SW_RESET:
+               {
+                       if (vpu_plat->reset)
+                               vpu_plat->reset();
+
+                       break;
+               }
+       case VPU_IOC_REG_DUMP:
+               break;
+       case VPU_IOC_PHYMEM_DUMP:
+               break;
+       default:
+               {
+                       printk(KERN_ERR "No such IOCTL, cmd is %d\n", cmd);
+                       break;
+               }
+       }
+       return ret;
+}
+
+/*!
+ * @brief Release function for vpu file operation
+ * @return  0 on success or negative error code on error
+ */
+static int vpu_release(struct inode *inode, struct file *filp)
+{
+       spin_lock(&vpu_lock);
+       if (open_count > 0 && !(--open_count)) {
+               vpu_free_buffers();
+
+               if (cpu_is_mx32())
+                       vl2cc_disable();
+
+               /* Free shared memory when vpu device is idle */
+               vpu_free_dma_buffer(&share_mem);
+               share_mem.cpu_addr = 0;
+       }
+       spin_unlock(&vpu_lock);
+
+       return 0;
+}
+
+/*!
+ * @brief fasync function for vpu file operation
+ * @return  0 on success or negative error code on error
+ */
+static int vpu_fasync(int fd, struct file *filp, int mode)
+{
+       struct vpu_priv *dev = (struct vpu_priv *)filp->private_data;
+       return fasync_helper(fd, filp, mode, &dev->async_queue);
+}
+
+/*!
+ * @brief memory map function of harware registers for vpu file operation
+ * @return  0 on success or negative error code on error
+ */
+static int vpu_map_hwregs(struct file *fp, struct vm_area_struct *vm)
+{
+       unsigned long pfn;
+
+       vm->vm_flags |= VM_IO | VM_RESERVED;
+       vm->vm_page_prot = pgprot_noncached(vm->vm_page_prot);
+       pfn = phy_vpu_base_addr >> PAGE_SHIFT;
+       pr_debug("size=0x%x,  page no.=0x%x\n",
+                (int)(vm->vm_end - vm->vm_start), (int)pfn);
+       return remap_pfn_range(vm, vm->vm_start, pfn, vm->vm_end - vm->vm_start,
+                              vm->vm_page_prot) ? -EAGAIN : 0;
+}
+
+/*!
+ * @brief memory map function of memory for vpu file operation
+ * @return  0 on success or negative error code on error
+ */
+static int vpu_map_mem(struct file *fp, struct vm_area_struct *vm)
+{
+       int request_size;
+       request_size = vm->vm_end - vm->vm_start;
+
+       pr_debug(" start=0x%x, pgoff=0x%x, size=0x%x\n",
+                (unsigned int)(vm->vm_start), (unsigned int)(vm->vm_pgoff),
+                request_size);
+
+       vm->vm_flags |= VM_IO | VM_RESERVED;
+       vm->vm_page_prot = pgprot_writecombine(vm->vm_page_prot);
+
+       return remap_pfn_range(vm, vm->vm_start, vm->vm_pgoff,
+                              request_size, vm->vm_page_prot) ? -EAGAIN : 0;
+
+}
+
+/*!
+ * @brief memory map interface for vpu file operation
+ * @return  0 on success or negative error code on error
+ */
+static int vpu_mmap(struct file *fp, struct vm_area_struct *vm)
+{
+       if (vm->vm_pgoff)
+               return vpu_map_mem(fp, vm);
+       else
+               return vpu_map_hwregs(fp, vm);
+}
+
+struct file_operations vpu_fops = {
+       .owner = THIS_MODULE,
+       .open = vpu_open,
+       .unlocked_ioctl = vpu_ioctl,
+       .release = vpu_release,
+       .fasync = vpu_fasync,
+       .mmap = vpu_mmap,
+};
+
+/*!
+ * This function is called by the driver framework to initialize the vpu device.
+ * @param   dev The device structure for the vpu passed in by the framework.
+ * @return   0 on success or negative error code on error
+ */
+static int vpu_dev_probe(struct platform_device *pdev)
+{
+       int err = 0;
+       struct device *temp_class;
+       struct resource *res;
+       unsigned long addr = 0;
+
+       vpu_plat = pdev->dev.platform_data;
+
+       if (VPU_IRAM_SIZE)
+               iram_alloc(VPU_IRAM_SIZE, &addr);
+       if (addr == 0)
+               iram.start = iram.end = 0;
+       else {
+               iram.start = addr;
+               iram.end = addr + VPU_IRAM_SIZE - 1;
+       }
+
+       if (cpu_is_mx32()) {
+               err = vl2cc_init(iram.start);
+               if (err != 0)
+                       return err;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               printk(KERN_ERR "vpu: unable to get vpu base addr\n");
+               return -ENODEV;
+       }
+       phy_vpu_base_addr = res->start;
+       vpu_base = ioremap(res->start, res->end - res->start);
+
+       vpu_major = register_chrdev(vpu_major, "mxc_vpu", &vpu_fops);
+       if (vpu_major < 0) {
+               printk(KERN_ERR "vpu: unable to get a major for VPU\n");
+               err = -EBUSY;
+               goto error;
+       }
+
+       vpu_class = class_create(THIS_MODULE, "mxc_vpu");
+       if (IS_ERR(vpu_class)) {
+               err = PTR_ERR(vpu_class);
+               goto err_out_chrdev;
+       }
+
+       temp_class = device_create(vpu_class, NULL, MKDEV(vpu_major, 0),
+                                  NULL, "mxc_vpu");
+       if (IS_ERR(temp_class)) {
+               err = PTR_ERR(temp_class);
+               goto err_out_class;
+       }
+
+       vpu_clk = clk_get(&pdev->dev, "vpu_clk");
+       if (IS_ERR(vpu_clk)) {
+               err = -ENOENT;
+               goto err_out_class;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (!res) {
+               printk(KERN_ERR "vpu: unable to get vpu interrupt\n");
+               err = -ENXIO;
+               goto err_out_class;
+       }
+       vpu_irq = res->start;
+
+       err = request_irq(vpu_irq, vpu_irq_handler, 0, "VPU_CODEC_IRQ",
+                         (void *)(&vpu_data));
+       if (err)
+               goto err_out_class;
+
+       vpu_data.workqueue = create_workqueue("vpu_wq");
+       INIT_WORK(&vpu_data.work, vpu_worker_callback);
+       printk(KERN_INFO "VPU initialized\n");
+       goto out;
+
+      err_out_class:
+       device_destroy(vpu_class, MKDEV(vpu_major, 0));
+       class_destroy(vpu_class);
+      err_out_chrdev:
+       unregister_chrdev(vpu_major, "mxc_vpu");
+      error:
+       iounmap(vpu_base);
+       if (cpu_is_mx32()) {
+               vl2cc_cleanup();
+       }
+      out:
+       return err;
+}
+
+static int vpu_dev_remove(struct platform_device *pdev)
+{
+       free_irq(vpu_irq, &vpu_data);
+       cancel_work_sync(&vpu_data.work);
+       flush_workqueue(vpu_data.workqueue);
+       destroy_workqueue(vpu_data.workqueue);
+
+       iounmap(vpu_base);
+       if (VPU_IRAM_SIZE)
+               iram_free(iram.start, VPU_IRAM_SIZE);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int vpu_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       int i;
+       unsigned long timeout;
+
+       /* Wait for vpu go to idle state, suspect vpu cannot be changed
+          to idle state after about 1 sec */
+       if (open_count > 0) {
+               timeout = jiffies + HZ;
+               clk_enable(vpu_clk);
+               while (READ_REG(BIT_BUSY_FLAG)) {
+                       msleep(1);
+                       if (time_after(jiffies, timeout))
+                               goto out;
+               }
+               clk_disable(vpu_clk);
+       }
+
+       /* Make sure clock is disabled before suspend */
+       vpu_clk_usercount = clk_get_usecount(vpu_clk);
+       for (i = 0; i < vpu_clk_usercount; i++)
+               clk_disable(vpu_clk);
+
+       if (!cpu_is_mx53()) {
+               clk_enable(vpu_clk);
+               if (bitwork_mem.cpu_addr != 0) {
+                       SAVE_WORK_REGS;
+                       SAVE_CTRL_REGS;
+                       SAVE_RDWR_PTR_REGS;
+                       SAVE_DIS_FLAG_REGS;
+
+                       WRITE_REG(0x1, BIT_BUSY_FLAG);
+                       WRITE_REG(VPU_SLEEP_REG_VALUE, BIT_RUN_COMMAND);
+                       while (READ_REG(BIT_BUSY_FLAG))
+                               ;
+               }
+               clk_disable(vpu_clk);
+       }
+
+       if (cpu_is_mx37() || cpu_is_mx51() || cpu_is_mx53())
+               if (vpu_plat->pg)
+                       vpu_plat->pg(1);
+
+       return 0;
+
+out:
+       clk_disable(vpu_clk);
+       return -EAGAIN;
+
+}
+
+static int vpu_resume(struct platform_device *pdev)
+{
+       int i;
+
+       if (cpu_is_mx37() || cpu_is_mx51() || cpu_is_mx53())
+               if (vpu_plat->pg)
+                       vpu_plat->pg(0);
+
+       if (cpu_is_mx53())
+               goto recover_clk;
+
+       clk_enable(vpu_clk);
+       if (bitwork_mem.cpu_addr != 0) {
+               u32 *p = (u32 *) bitwork_mem.cpu_addr;
+               u32 data;
+               u16 data_hi;
+               u16 data_lo;
+
+               RESTORE_WORK_REGS;
+
+               WRITE_REG(0x0, BIT_RESET_CTRL);
+               WRITE_REG(0x0, BIT_CODE_RUN);
+
+               /*
+                * Re-load boot code, from the codebuffer in external RAM.
+                * Thankfully, we only need 4096 bytes, same for all platforms.
+                */
+               if (cpu_is_mx51()) {
+                       for (i = 0; i < 2048; i += 4) {
+                               data = p[(i / 2) + 1];
+                               data_hi = (data >> 16) & 0xFFFF;
+                               data_lo = data & 0xFFFF;
+                               WRITE_REG((i << 16) | data_hi, BIT_CODE_DOWN);
+                               WRITE_REG(((i + 1) << 16) | data_lo,
+                                         BIT_CODE_DOWN);
+
+                               data = p[i / 2];
+                               data_hi = (data >> 16) & 0xFFFF;
+                               data_lo = data & 0xFFFF;
+                               WRITE_REG(((i + 2) << 16) | data_hi,
+                                         BIT_CODE_DOWN);
+                               WRITE_REG(((i + 3) << 16) | data_lo,
+                                         BIT_CODE_DOWN);
+                       }
+               } else {
+                       for (i = 0; i < 2048; i += 2) {
+                               if (cpu_is_mx37())
+                                       data = swab32(p[i / 2]);
+                               else
+                                       data = p[i / 2];
+                               data_hi = (data >> 16) & 0xFFFF;
+                               data_lo = data & 0xFFFF;
+
+                               WRITE_REG((i << 16) | data_hi, BIT_CODE_DOWN);
+                               WRITE_REG(((i + 1) << 16) | data_lo,
+                                         BIT_CODE_DOWN);
+                       }
+               }
+
+               RESTORE_CTRL_REGS;
+
+               WRITE_REG(BITVAL_PIC_RUN, BIT_INT_ENABLE);
+
+               WRITE_REG(0x1, BIT_BUSY_FLAG);
+               WRITE_REG(0x1, BIT_CODE_RUN);
+               while (READ_REG(BIT_BUSY_FLAG))
+                       ;
+
+               RESTORE_RDWR_PTR_REGS;
+               RESTORE_DIS_FLAG_REGS;
+
+               WRITE_REG(0x1, BIT_BUSY_FLAG);
+               WRITE_REG(VPU_WAKE_REG_VALUE, BIT_RUN_COMMAND);
+               while (READ_REG(BIT_BUSY_FLAG))
+                       ;
+       }
+       clk_disable(vpu_clk);
+
+recover_clk:
+       /* Recover vpu clock */
+       for (i = 0; i < vpu_clk_usercount; i++)
+               clk_enable(vpu_clk);
+
+       return 0;
+}
+#else
+#define        vpu_suspend     NULL
+#define        vpu_resume      NULL
+#endif                         /* !CONFIG_PM */
+
+/*! Driver definition
+ *
+ */
+static struct platform_driver mxcvpu_driver = {
+       .driver = {
+                  .name = "mxc_vpu",
+                  },
+       .probe = vpu_dev_probe,
+       .remove = vpu_dev_remove,
+       .suspend = vpu_suspend,
+       .resume = vpu_resume,
+};
+
+static int __init vpu_init(void)
+{
+       int ret = platform_driver_register(&mxcvpu_driver);
+
+       init_waitqueue_head(&vpu_queue);
+
+       return ret;
+}
+
+static void __exit vpu_exit(void)
+{
+       if (vpu_major > 0) {
+               device_destroy(vpu_class, MKDEV(vpu_major, 0));
+               class_destroy(vpu_class);
+               unregister_chrdev(vpu_major, "mxc_vpu");
+               vpu_major = 0;
+       }
+
+       if (cpu_is_mx32()) {
+               vl2cc_cleanup();
+       }
+
+       vpu_free_dma_buffer(&bitwork_mem);
+       vpu_free_dma_buffer(&pic_para_mem);
+       vpu_free_dma_buffer(&user_data_mem);
+
+       clk_put(vpu_clk);
+
+       platform_driver_unregister(&mxcvpu_driver);
+       return;
+}
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Linux VPU driver for Freescale i.MX/MXC");
+MODULE_LICENSE("GPL");
+
+module_init(vpu_init);
+module_exit(vpu_exit);
index 549b960667c81b5bcbff1145550a28630038c374..9afd6e8b4f216928b5df4d7287232ad06bf46ee9 100644 (file)
@@ -2389,6 +2389,10 @@ source "drivers/video/omap2/Kconfig"
 source "drivers/video/backlight/Kconfig"
 source "drivers/video/display/Kconfig"
 
+if ARCH_MXC
+source "drivers/video/mxc/Kconfig"
+endif
+
 if VT
        source "drivers/video/console/Kconfig"
 endif
index 8b83129e209ca0f943a3d624e29930a679aec110..a3ae7aafe5b6af15e08311d8686cd7caefcd7697 100644 (file)
@@ -48,6 +48,7 @@ obj-$(CONFIG_FB_KYRO)             += kyro/
 obj-$(CONFIG_FB_SAVAGE)                  += savage/
 obj-$(CONFIG_FB_GEODE)           += geode/
 obj-$(CONFIG_FB_MBX)             += mbx/
+obj-$(CONFIG_FB_MXC)              += mxc/
 obj-$(CONFIG_FB_NEOMAGIC)         += neofb.o
 obj-$(CONFIG_FB_3DFX)             += tdfxfb.o
 obj-$(CONFIG_FB_CONTROL)          += controlfb.o
diff --git a/drivers/video/mxc/Kconfig b/drivers/video/mxc/Kconfig
new file mode 100644 (file)
index 0000000..e2b79fa
--- /dev/null
@@ -0,0 +1,111 @@
+config FB_MXC
+       tristate "MXC Framebuffer support"
+       depends on FB && (MXC_IPU || ARCH_MX21 || ARCH_MX27 || ARCH_MX25)
+       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_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
+
+config FB_MXC_LDB
+       tristate "MXC LDB"
+       depends on FB_MXC_SYNC_PANEL
+       depends on MXC_IPU_V3
+
+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
+       tristate "Si Image SII9022 DVI/HDMI Interface Chip"
+
+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_AUTO_UPDATE_MODE
+    bool "E-Ink Auto-update Mode Support"
+    default n
+    depends on FB_MXC_EINK_PANEL
+
+config FB_MXC_ELCDIF_FB
+       depends on FB && ARCH_MXC
+       tristate "Support MXC ELCDIF framebuffer"
+
+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 (file)
index 0000000..723001a
--- /dev/null
@@ -0,0 +1,26 @@
+obj-$(CONFIG_FB_MXC_TVOUT_TVE)                 += tve.o
+obj-$(CONFIG_FB_MXC_SII902X)                    += mxcfb_sii902x.o
+obj-$(CONFIG_FB_MXC_LDB)                       += ldb.o
+obj-$(CONFIG_FB_MODE_HELPERS)                  += 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
+else
+       obj-$(CONFIG_FB_MXC_SYNC_PANEL)         += mxc_ipuv3_fb.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
+obj-$(CONFIG_FB_MXC_ELCDIF_FB)             += mxc_elcdif_fb.o
diff --git a/drivers/video/mxc/ch7024.c b/drivers/video/mxc/ch7024.c
new file mode 100644 (file)
index 0000000..32ca332
--- /dev/null
@@ -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/elcdif_regs.h b/drivers/video/mxc/elcdif_regs.h
new file mode 100644 (file)
index 0000000..42e377d
--- /dev/null
@@ -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 (file)
index 0000000..9830717
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+ * 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., 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_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_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_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_NEXTLUT            (epdc_base + 0x460)
+#define EPDC_STATUS_COL                (epdc_base + 0x480)
+#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_DEBUG                     (epdc_base + 0x500)
+#define EPDC_DEBUG_LUT0                (epdc_base + 0x540)
+#define EPDC_DEBUG_LUT1                (epdc_base + 0x550)
+#define EPDC_DEBUG_LUT2                (epdc_base + 0x560)
+#define EPDC_DEBUG_LUT3                (epdc_base + 0x570)
+#define EPDC_DEBUG_LUT4                (epdc_base + 0x580)
+#define EPDC_DEBUG_LUT5                (epdc_base + 0x590)
+#define EPDC_DEBUG_LUT6                (epdc_base + 0x5A0)
+#define EPDC_DEBUG_LUT7                (epdc_base + 0x5B0)
+#define EPDC_DEBUG_LUT8                (epdc_base + 0x5C0)
+#define EPDC_DEBUG_LUT9                (epdc_base + 0x5D0)
+#define EPDC_DEBUG_LUT10               (epdc_base + 0x5E0)
+#define EPDC_DEBUG_LUT11               (epdc_base + 0x5F0)
+#define EPDC_DEBUG_LUT12               (epdc_base + 0x600)
+#define EPDC_DEBUG_LUT13               (epdc_base + 0x610)
+#define EPDC_DEBUG_LUT14               (epdc_base + 0x620)
+#define EPDC_DEBUG_LUT15               (epdc_base + 0x630)
+#define EPDC_GPIO                      (epdc_base + 0x700)
+#define EPDC_VERSION                   (epdc_base + 0x7F0)
+
+/*
+ * 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_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 = 0xF0000,
+       EPDC_UPD_CTRL_LUT_SEL_OFFSET = 16,
+       EPDC_UPD_CTRL_WAVEFORM_MODE_MASK = 0xFF00,
+       EPDC_UPD_CTRL_WAVEFORM_MODE_OFFSET = 8,
+       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_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_STATUS_NEXTLUT field values */
+       EPDC_STATUS_NEXTLUT_NEXT_LUT_VALID = 0x100,
+       EPDC_STATUS_NEXTLUT_NEXT_LUT_MASK = 0xF,
+       EPDC_STATUS_NEXTLUT_NEXT_LUT_OFFSET = 0,
+
+/* EPDC_STATUS field values */
+       EPDC_STATUS_LUTS_UNDERRUN = 0x4,
+       EPDC_STATUS_LUTS_BUSY = 0x2,
+       EPDC_STATUS_WB_BUSY = 0x1,
+
+/* EPDC_DEBUG field values */
+       EPDC_DEBUG_UNDERRUN_RECOVER = 0x2,
+       EPDC_DEBUG_COLLISION_OFF = 0x1,
+
+/* 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,
+};
+
+#endif /* __EPDC_REGS_INCLUDED__ */
diff --git a/drivers/video/mxc/ldb.c b/drivers/video/mxc/ldb.c
new file mode 100644 (file)
index 0000000..0fbdc5f
--- /dev/null
@@ -0,0 +1,1525 @@
+/*
+ * 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.
+ */
+
+/*!
+ * @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/ldb.h>
+#include <linux/mxcfb.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/fsl_devices.h>
+#include <mach/hardware.h>
+#include <mach/clock.h>
+
+#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
+
+enum ldb_chan_mode_opt {
+       LDB_SIN_DI0 = 0,
+       LDB_SIN_DI1 = 1,
+       LDB_SEP = 2,
+       LDB_DUL_DI0 = 3,
+       LDB_DUL_DI1 = 4,
+       LDB_SPL_DI0 = 5,
+       LDB_SPL_DI1 = 6,
+       LDB_NO_MODE = 7,
+};
+
+static struct ldb_data {
+       struct fb_info *fbi[2];
+       bool ch_working[2];
+       int blank[2];
+       uint32_t chan_mode_opt;
+       uint32_t chan_bit_map[2];
+       uint32_t bgref_rmode;
+       uint32_t base_addr;
+       uint32_t *control_reg;
+       struct clk *ldb_di_clk[2];
+       struct regulator *lvds_bg_reg;
+} ldb;
+
+static struct device *g_ldb_dev;
+static u32 *ldb_reg;
+static int g_chan_mode_opt = LDB_NO_MODE;
+static int g_chan_bit_map[2];
+static bool g_enable_ldb;
+static bool g_di0_used;
+static bool g_di1_used;
+
+DEFINE_SPINLOCK(ldb_lock);
+
+/*fake clock api*/
+int clk_get_usecount(struct clk *clk)
+{
+       return 1;
+}
+
+struct fb_videomode mxcfb_ldb_modedb[] = {
+       {
+        "1080P60", 60, 1920, 1080, 7692,
+        100, 40,
+        30, 3,
+        10, 2,
+        0,
+        FB_VMODE_NONINTERLACED,
+        FB_MODE_IS_DETAILED,},
+       {
+        "XGA", 60, 1024, 768, 15385,
+        220, 40,
+        21, 7,
+        60, 10,
+        0,
+        FB_VMODE_NONINTERLACED,
+        FB_MODE_IS_DETAILED,},
+};
+int mxcfb_ldb_modedb_sz = ARRAY_SIZE(mxcfb_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));
+}
+
+static void ldb_disable(int ipu_di)
+{
+       uint32_t reg;
+       int i = 0;
+
+       spin_lock(&ldb_lock);
+
+       switch (ldb.chan_mode_opt) {
+       case LDB_SIN_DI0:
+               if (ipu_di != 0 || !ldb.ch_working[0]) {
+                       spin_unlock(&ldb_lock);
+                       return;
+               }
+
+               reg = __raw_readl(ldb.control_reg);
+               __raw_writel((reg & ~LDB_CH0_MODE_MASK) |
+                            LDB_CH0_MODE_DISABLE,
+                            ldb.control_reg);
+
+               ldb.ldb_di_clk[0] = clk_get(g_ldb_dev, "ldb_di0_clk");
+               if (clk_get_usecount(ldb.ldb_di_clk[0]) != 0)
+                       clk_disable(ldb.ldb_di_clk[0]);
+               clk_put(ldb.ldb_di_clk[0]);
+
+               ldb.ch_working[0] = false;
+               break;
+       case LDB_SIN_DI1:
+               if (ipu_di != 1 || !ldb.ch_working[1]) {
+                       spin_unlock(&ldb_lock);
+                       return;
+               }
+
+               reg = __raw_readl(ldb.control_reg);
+               __raw_writel((reg & ~LDB_CH1_MODE_MASK) |
+                            LDB_CH1_MODE_DISABLE,
+                            ldb.control_reg);
+
+               ldb.ldb_di_clk[1] = clk_get(g_ldb_dev, "ldb_di1_clk");
+               if (clk_get_usecount(ldb.ldb_di_clk[1]) != 0)
+                       clk_disable(ldb.ldb_di_clk[1]);
+               clk_put(ldb.ldb_di_clk[1]);
+
+               ldb.ch_working[1] = false;
+               break;
+       case LDB_SPL_DI0:
+       case LDB_DUL_DI0:
+               if (ipu_di != 0) {
+                       spin_unlock(&ldb_lock);
+                       return;
+               }
+
+               for (i = 0; i < 2; i++) {
+                       if (ldb.ch_working[i]) {
+                               reg = __raw_readl(ldb.control_reg);
+                               if (i == 0)
+                                       __raw_writel((reg & ~LDB_CH0_MODE_MASK) |
+                                                    LDB_CH0_MODE_DISABLE,
+                                                    ldb.control_reg);
+                               else
+                                       __raw_writel((reg & ~LDB_CH0_MODE_MASK) |
+                                                    LDB_CH1_MODE_DISABLE,
+                                                    ldb.control_reg);
+
+                               if (ldb.chan_mode_opt == LDB_SPL_DI0) {
+                                       reg = __raw_readl(ldb.control_reg);
+                                       __raw_writel(reg & ~LDB_SPLIT_MODE_EN,
+                                                    ldb.control_reg);
+                               }
+
+                               ldb.ldb_di_clk[i] = clk_get(g_ldb_dev, i ?
+                                                       "ldb_di1_clk" :
+                                                       "ldb_di0_clk");
+                               if (clk_get_usecount(ldb.ldb_di_clk[i]) != 0)
+                                       clk_disable(ldb.ldb_di_clk[i]);
+                               clk_put(ldb.ldb_di_clk[i]);
+
+                               ldb.ch_working[i] = false;
+                       }
+               }
+               break;
+       case LDB_SPL_DI1:
+       case LDB_DUL_DI1:
+               if (ipu_di != 1) {
+                       spin_unlock(&ldb_lock);
+                       return;
+               }
+
+               for (i = 0; i < 2; i++) {
+                       if (ldb.ch_working[i]) {
+                               reg = __raw_readl(ldb.control_reg);
+                               if (i == 0)
+                                       __raw_writel((reg & ~LDB_CH0_MODE_MASK) |
+                                                    LDB_CH0_MODE_DISABLE,
+                                                    ldb.control_reg);
+                               else
+                                       __raw_writel((reg & ~LDB_CH0_MODE_MASK) |
+                                                    LDB_CH1_MODE_DISABLE,
+                                                    ldb.control_reg);
+
+                               if (ldb.chan_mode_opt == LDB_SPL_DI1) {
+                                       reg = __raw_readl(ldb.control_reg);
+                                       __raw_writel(reg & ~LDB_SPLIT_MODE_EN,
+                                                    ldb.control_reg);
+                               }
+
+                               ldb.ldb_di_clk[i] = clk_get(g_ldb_dev, i ?
+                                                       "ldb_di1_clk" :
+                                                       "ldb_di0_clk");
+                               if (clk_get_usecount(ldb.ldb_di_clk[i]) != 0)
+                                       clk_disable(ldb.ldb_di_clk[i]);
+                               clk_put(ldb.ldb_di_clk[i]);
+
+                               ldb.ch_working[i] = false;
+                       }
+               }
+               break;
+       case LDB_SEP:
+               if (ldb.ch_working[ipu_di]) {
+                       reg = __raw_readl(ldb.control_reg);
+                       if (ipu_di == 0)
+                               __raw_writel((reg & ~LDB_CH0_MODE_MASK) |
+                                            LDB_CH0_MODE_DISABLE,
+                                            ldb.control_reg);
+                       else
+                               __raw_writel((reg & ~LDB_CH1_MODE_MASK) |
+                                            LDB_CH1_MODE_DISABLE,
+                                            ldb.control_reg);
+
+                       ldb.ldb_di_clk[ipu_di] = clk_get(g_ldb_dev, ipu_di ?
+                                                              "ldb_di1_clk" :
+                                                              "ldb_di0_clk");
+                       if (clk_get_usecount(ldb.ldb_di_clk[ipu_di]) != 0)
+                               clk_disable(ldb.ldb_di_clk[ipu_di]);
+                       clk_put(ldb.ldb_di_clk[ipu_di]);
+
+                       ldb.ch_working[ipu_di] = false;
+               }
+               break;
+       default:
+               break;
+       }
+
+       spin_unlock(&ldb_lock);
+       return;
+}
+
+static void ldb_enable(int ipu_di)
+{
+       uint32_t reg;
+
+       spin_lock(&ldb_lock);
+
+       reg = __raw_readl(ldb.control_reg);
+       switch (ldb.chan_mode_opt) {
+       case LDB_SIN_DI0:
+               if (ldb.ch_working[0] || ipu_di != 0) {
+                       spin_unlock(&ldb_lock);
+                       return;
+               }
+
+               ldb.ldb_di_clk[0] = clk_get(g_ldb_dev, "ldb_di0_clk");
+               if (clk_get_usecount(ldb.ldb_di_clk[0]) == 0)
+                       clk_enable(ldb.ldb_di_clk[0]);
+               clk_put(ldb.ldb_di_clk[0]);
+               __raw_writel((reg & ~LDB_CH0_MODE_MASK) |
+                             LDB_CH0_MODE_EN_TO_DI0, ldb.control_reg);
+               ldb.ch_working[0] = true;
+               break;
+       case LDB_SIN_DI1:
+               if (ldb.ch_working[1] || ipu_di != 1) {
+                       spin_unlock(&ldb_lock);
+                       return;
+               }
+
+               ldb.ldb_di_clk[1] = clk_get(g_ldb_dev, "ldb_di1_clk");
+               if (clk_get_usecount(ldb.ldb_di_clk[1]) == 0)
+                       clk_enable(ldb.ldb_di_clk[1]);
+               clk_put(ldb.ldb_di_clk[1]);
+               __raw_writel((reg & ~LDB_CH1_MODE_MASK) |
+                             LDB_CH1_MODE_EN_TO_DI1, ldb.control_reg);
+               ldb.ch_working[1] = true;
+               break;
+       case LDB_SEP:
+               if (ldb.ch_working[ipu_di]) {
+                       spin_unlock(&ldb_lock);
+                       return;
+               }
+
+               if (ipu_di == 0) {
+                       ldb.ldb_di_clk[0] = clk_get(g_ldb_dev, "ldb_di0_clk");
+                       if (clk_get_usecount(ldb.ldb_di_clk[0]) == 0)
+                               clk_enable(ldb.ldb_di_clk[0]);
+                       clk_put(ldb.ldb_di_clk[0]);
+                       __raw_writel((reg & ~LDB_CH0_MODE_MASK) |
+                                     LDB_CH0_MODE_EN_TO_DI0,
+                                     ldb.control_reg);
+                       ldb.ch_working[0] = true;
+               } else {
+                       ldb.ldb_di_clk[1] = clk_get(g_ldb_dev, "ldb_di1_clk");
+                       if (clk_get_usecount(ldb.ldb_di_clk[1]) == 0)
+                               clk_enable(ldb.ldb_di_clk[1]);
+                       clk_put(ldb.ldb_di_clk[1]);
+                       __raw_writel((reg & ~LDB_CH1_MODE_MASK) |
+                                     LDB_CH1_MODE_EN_TO_DI1,
+                                     ldb.control_reg);
+                       ldb.ch_working[1] = true;
+               }
+               break;
+       case LDB_DUL_DI0:
+       case LDB_SPL_DI0:
+               if (ipu_di != 0)
+                       return;
+               else
+                       goto proc;
+       case LDB_DUL_DI1:
+       case LDB_SPL_DI1:
+               if (ipu_di != 1)
+                       return;
+proc:
+               if (ldb.ch_working[0] || ldb.ch_working[1]) {
+                       spin_unlock(&ldb_lock);
+                       return;
+               }
+
+               ldb.ldb_di_clk[0] = clk_get(g_ldb_dev, "ldb_di0_clk");
+               ldb.ldb_di_clk[1] = clk_get(g_ldb_dev, "ldb_di1_clk");
+               if (clk_get_usecount(ldb.ldb_di_clk[0]) == 0)
+                       clk_enable(ldb.ldb_di_clk[0]);
+               if (clk_get_usecount(ldb.ldb_di_clk[1]) == 0)
+                       clk_enable(ldb.ldb_di_clk[1]);
+               clk_put(ldb.ldb_di_clk[0]);
+               clk_put(ldb.ldb_di_clk[1]);
+
+               if (ldb.chan_mode_opt == LDB_DUL_DI0 ||
+                   ldb.chan_mode_opt == LDB_SPL_DI0) {
+                       __raw_writel((reg & ~LDB_CH0_MODE_MASK) |
+                                     LDB_CH0_MODE_EN_TO_DI0,
+                                     ldb.control_reg);
+                       reg = __raw_readl(ldb.control_reg);
+                       __raw_writel((reg & ~LDB_CH1_MODE_MASK) |
+                                     LDB_CH1_MODE_EN_TO_DI0,
+                                     ldb.control_reg);
+               } else if (ldb.chan_mode_opt == LDB_DUL_DI1 ||
+                          ldb.chan_mode_opt == LDB_SPL_DI1) {
+                       __raw_writel((reg & ~LDB_CH0_MODE_MASK) |
+                                     LDB_CH0_MODE_EN_TO_DI1,
+                                     ldb.control_reg);
+                       reg = __raw_readl(ldb.control_reg);
+                       __raw_writel((reg & ~LDB_CH1_MODE_MASK) |
+                                     LDB_CH1_MODE_EN_TO_DI1,
+                                     ldb.control_reg);
+               }
+               if (ldb.chan_mode_opt == LDB_SPL_DI0 ||
+                   ldb.chan_mode_opt == LDB_SPL_DI1) {
+                       reg = __raw_readl(ldb.control_reg);
+                       __raw_writel(reg | LDB_SPLIT_MODE_EN,
+                                     ldb.control_reg);
+               }
+               ldb.ch_working[0] = true;
+               ldb.ch_working[1] = true;
+               break;
+       default:
+               break;
+       }
+       spin_unlock(&ldb_lock);
+       return;
+}
+
+static int ldb_fb_pre_setup(struct fb_info *fbi)
+{
+       int ipu_di = 0;
+       struct clk *di_clk, *ldb_clk_parent;
+       unsigned long ldb_clk_prate = 455000000;
+
+       fbi->mode = (struct fb_videomode *)fb_match_mode(&fbi->var,
+                       &fbi->modelist);
+       if (!fbi->mode) {
+               dev_warn(g_ldb_dev, "can not find mode for xres=%d, yres=%d\n",
+                               fbi->var.xres, fbi->var.yres);
+               return 0;
+       }
+
+       if (fbi->fbops->fb_ioctl) {
+               mm_segment_t old_fs;
+
+               old_fs = get_fs();
+               set_fs(KERNEL_DS);
+               fbi->fbops->fb_ioctl(fbi,
+                               MXCFB_GET_FB_IPU_DI,
+                               (unsigned long)&ipu_di);
+               fbi->fbops->fb_ioctl(fbi,
+                               MXCFB_GET_FB_BLANK,
+                               (unsigned int)(&ldb.blank[ipu_di]));
+               set_fs(old_fs);
+
+               /*
+                * Default ldb mode:
+                * 1080p: DI0 split, SPWG or DI1 split, SPWG
+                * others: single, SPWG
+                */
+               if (ldb.chan_mode_opt == LDB_NO_MODE) {
+                       if (fb_mode_is_equal(fbi->mode, &mxcfb_ldb_modedb[0])) {
+                               if (ipu_di == 0) {
+                                       ldb.chan_mode_opt = LDB_SPL_DI0;
+                                       dev_warn(g_ldb_dev,
+                                               "default di0 split mode\n");
+                               } else {
+                                       ldb.chan_mode_opt = LDB_SPL_DI1;
+                                       dev_warn(g_ldb_dev,
+                                               "default di1 split mode\n");
+                               }
+                               ldb.chan_bit_map[0] = LDB_BIT_MAP_SPWG;
+                               ldb.chan_bit_map[1] = LDB_BIT_MAP_SPWG;
+                       } else if (fb_mode_is_equal(fbi->mode, &mxcfb_ldb_modedb[1])) {
+                               if (ipu_di == 0) {
+                                       ldb.chan_mode_opt = LDB_SIN_DI0;
+                                       ldb.chan_bit_map[0] = LDB_BIT_MAP_SPWG;
+                                       dev_warn(g_ldb_dev,
+                                                "default di0 single mode\n");
+                               } else {
+                                       ldb.chan_mode_opt = LDB_SIN_DI1;
+                                       ldb.chan_bit_map[1] = LDB_BIT_MAP_SPWG;
+                                       dev_warn(g_ldb_dev,
+                                                "default di1 single mode\n");
+                               }
+                       }
+               }
+
+               /* TODO:Set the correct pll4 rate for all situations */
+               if (ipu_di == 1) {
+                       ldb.ldb_di_clk[1] =
+                               clk_get(g_ldb_dev, "ldb_di1_clk");
+                       di_clk = clk_get(g_ldb_dev, "ipu_di1_clk");
+                       ldb_clk_parent =
+                               clk_get_parent(ldb.ldb_di_clk[1]);
+                       clk_set_rate(ldb_clk_parent, ldb_clk_prate);
+                       clk_set_parent(di_clk, ldb.ldb_di_clk[1]);
+                       clk_put(di_clk);
+                       clk_put(ldb.ldb_di_clk[1]);
+               } else {
+                       ldb.ldb_di_clk[0] =
+                               clk_get(g_ldb_dev, "ldb_di0_clk");
+                       di_clk = clk_get(g_ldb_dev, "ipu_di0_clk");
+                       ldb_clk_parent =
+                               clk_get_parent(ldb.ldb_di_clk[0]);
+                       clk_set_rate(ldb_clk_parent, ldb_clk_prate);
+                       clk_set_parent(di_clk, ldb.ldb_di_clk[0]);
+                       clk_put(di_clk);
+                       clk_put(ldb.ldb_di_clk[0]);
+               }
+
+               switch (ldb.chan_mode_opt) {
+               case LDB_SIN_DI0:
+                       ldb.ldb_di_clk[0] = clk_get(g_ldb_dev, "ldb_di0_clk");
+                       clk_set_rate(ldb.ldb_di_clk[0], ldb_clk_prate/7);
+                       if (ldb.blank[0] == FB_BLANK_UNBLANK &&
+                           clk_get_usecount(ldb.ldb_di_clk[0]) == 0)
+                               clk_enable(ldb.ldb_di_clk[0]);
+                       clk_put(ldb.ldb_di_clk[0]);
+                       break;
+               case LDB_SIN_DI1:
+                       ldb.ldb_di_clk[1] = clk_get(g_ldb_dev, "ldb_di1_clk");
+                       clk_set_rate(ldb.ldb_di_clk[1], ldb_clk_prate/7);
+                       if (ldb.blank[1] == FB_BLANK_UNBLANK &&
+                           clk_get_usecount(ldb.ldb_di_clk[1]) == 0)
+                               clk_enable(ldb.ldb_di_clk[1]);
+                       clk_put(ldb.ldb_di_clk[1]);
+                       break;
+               case LDB_SEP:
+                       if (ipu_di == 0) {
+                               ldb.ldb_di_clk[0] = clk_get(g_ldb_dev, "ldb_di0_clk");
+                               clk_set_rate(ldb.ldb_di_clk[0], ldb_clk_prate/7);
+                               if (ldb.blank[0] == FB_BLANK_UNBLANK &&
+                                   clk_get_usecount(ldb.ldb_di_clk[0]) == 0)
+                                       clk_enable(ldb.ldb_di_clk[0]);
+                               clk_put(ldb.ldb_di_clk[0]);
+                       } else {
+                               ldb.ldb_di_clk[1] = clk_get(g_ldb_dev, "ldb_di1_clk");
+                               clk_set_rate(ldb.ldb_di_clk[1], ldb_clk_prate/7);
+                               if (ldb.blank[1] == FB_BLANK_UNBLANK &&
+                                   clk_get_usecount(ldb.ldb_di_clk[1]) == 0)
+                                       clk_enable(ldb.ldb_di_clk[1]);
+                               clk_put(ldb.ldb_di_clk[1]);
+                       }
+                       break;
+               case LDB_DUL_DI0:
+               case LDB_SPL_DI0:
+                       ldb.ldb_di_clk[0] = clk_get(g_ldb_dev, "ldb_di0_clk");
+                       ldb.ldb_di_clk[1] = clk_get(g_ldb_dev, "ldb_di1_clk");
+                       if (ldb.chan_mode_opt == LDB_DUL_DI0) {
+                               clk_set_rate(ldb.ldb_di_clk[0], ldb_clk_prate/7);
+                       } else {
+                               clk_set_rate(ldb.ldb_di_clk[0], 2*ldb_clk_prate/7);
+                               clk_set_rate(ldb.ldb_di_clk[1], 2*ldb_clk_prate/7);
+                       }
+                       if (ldb.blank[0] == FB_BLANK_UNBLANK) {
+                               if (clk_get_usecount(ldb.ldb_di_clk[0]) == 0)
+                                       clk_enable(ldb.ldb_di_clk[0]);
+                               if (clk_get_usecount(ldb.ldb_di_clk[1]) == 0)
+                                       clk_enable(ldb.ldb_di_clk[1]);
+                       }
+                       clk_put(ldb.ldb_di_clk[0]);
+                       clk_put(ldb.ldb_di_clk[1]);
+                       break;
+               case LDB_DUL_DI1:
+               case LDB_SPL_DI1:
+                       ldb.ldb_di_clk[0] = clk_get(g_ldb_dev, "ldb_di0_clk");
+                       ldb.ldb_di_clk[1] = clk_get(g_ldb_dev, "ldb_di1_clk");
+                       if (ldb.chan_mode_opt == LDB_DUL_DI1) {
+                               clk_set_rate(ldb.ldb_di_clk[1], ldb_clk_prate/7);
+                       } else {
+                               clk_set_rate(ldb.ldb_di_clk[0], 2*ldb_clk_prate/7);
+                               clk_set_rate(ldb.ldb_di_clk[1], 2*ldb_clk_prate/7);
+                       }
+                       if (ldb.blank[1] == FB_BLANK_UNBLANK) {
+                               if (clk_get_usecount(ldb.ldb_di_clk[0]) == 0)
+                                       clk_enable(ldb.ldb_di_clk[0]);
+                               if (clk_get_usecount(ldb.ldb_di_clk[1]) == 0)
+                                       clk_enable(ldb.ldb_di_clk[1]);
+                       }
+                       clk_put(ldb.ldb_di_clk[0]);
+                       clk_put(ldb.ldb_di_clk[1]);
+                       break;
+               default:
+                       break;
+               }
+
+               if (ldb.blank[ipu_di] == FB_BLANK_UNBLANK)
+                       ldb_enable(ipu_di);
+       }
+
+       return 0;
+}
+
+int ldb_fb_event(struct notifier_block *nb, unsigned long val, void *v)
+{
+       struct fb_event *event = v;
+       struct fb_info *fbi = event->info;
+       mm_segment_t old_fs;
+       int ipu_di = 0;
+
+       /* Get rid of impact from FG fb */
+       if (strcmp(fbi->fix.id, "DISP3 FG") == 0)
+               return 0;
+
+       if (fbi->fbops->fb_ioctl) {
+               old_fs = get_fs();
+               set_fs(KERNEL_DS);
+               fbi->fbops->fb_ioctl(fbi,
+                               MXCFB_GET_FB_IPU_DI,
+                               (unsigned long)&ipu_di);
+               set_fs(old_fs);
+       } else
+               return 0;
+
+       if ((ipu_di == 0 && !g_di0_used) ||
+           (ipu_di == 1 && !g_di1_used) ||
+           ipu_di > 1)
+               return 0;
+
+       fbi->mode = (struct fb_videomode *)fb_match_mode(&fbi->var,
+                       &fbi->modelist);
+
+       if (!fbi->mode) {
+               dev_warn(g_ldb_dev, "can not find mode for xres=%d, yres=%d\n",
+                               fbi->var.xres, fbi->var.yres);
+               return 0;
+       }
+
+       switch (val) {
+       case FB_EVENT_MODE_CHANGE: {
+               int ipu_di_pix_fmt;
+               uint32_t reg;
+
+               if ((ldb.fbi[0] != NULL && ldb.chan_mode_opt != LDB_SEP) ||
+                   ldb.fbi[1] != NULL)
+                       return 0;
+
+               /*
+                * We cannot support two LVDS panels with different
+                * pixel clock rates except that one's pixel clock rate
+                * is two times of the others'.
+                */
+               if (ldb.fbi[0]) {
+                       if (ldb.fbi[0]->var.pixclock == fbi->var.pixclock ||
+                           ldb.fbi[0]->var.pixclock ==
+                                               2 * fbi->var.pixclock ||
+                           fbi->var.pixclock == 2 * ldb.fbi[0]->var.pixclock)
+                               ldb.fbi[1] = fbi;
+                       else
+                               return 0;
+               } else
+                       ldb.fbi[0] = fbi;
+
+               old_fs = get_fs();
+               set_fs(KERNEL_DS);
+               fbi->fbops->fb_ioctl(fbi, MXCFB_GET_DIFMT,
+                               (unsigned long)&ipu_di_pix_fmt);
+               set_fs(old_fs);
+
+               if (!valid_mode(ipu_di_pix_fmt)) {
+                       dev_err(g_ldb_dev, "Unsupport pixel format "
+                                          "for ldb input\n");
+                       return 0;
+               }
+
+               reg = __raw_readl(ldb.control_reg);
+               if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT) {
+                       if (ipu_di == 0)
+                               __raw_writel((reg &
+                                       ~LDB_DI0_VS_POL_MASK) |
+                                       LDB_DI0_VS_POL_ACT_HIGH,
+                                       ldb.control_reg);
+                       else
+                               __raw_writel((reg &
+                                       ~LDB_DI1_VS_POL_MASK) |
+                                       LDB_DI1_VS_POL_ACT_HIGH,
+                                       ldb.control_reg);
+               } else {
+                       if (ipu_di == 0)
+                               __raw_writel((reg &
+                                       ~LDB_DI0_VS_POL_MASK) |
+                                       LDB_DI0_VS_POL_ACT_LOW,
+                                       ldb.control_reg);
+                       else
+                               __raw_writel((reg &
+                                       ~LDB_DI1_VS_POL_MASK) |
+                                       LDB_DI1_VS_POL_ACT_LOW,
+                                       ldb.control_reg);
+               }
+
+               switch (ldb.chan_mode_opt) {
+               case LDB_SIN_DI0:
+                       reg = __raw_readl(ldb.control_reg);
+                       if (bits_per_pixel(ipu_di_pix_fmt) == 24)
+                               __raw_writel((reg & ~LDB_DATA_WIDTH_CH0_MASK) |
+                                             LDB_DATA_WIDTH_CH0_24,
+                                             ldb.control_reg);
+                       else if (bits_per_pixel(ipu_di_pix_fmt) == 18)
+                               __raw_writel((reg & ~LDB_DATA_WIDTH_CH0_MASK) |
+                                             LDB_DATA_WIDTH_CH0_18,
+                                             ldb.control_reg);
+
+                       reg = __raw_readl(ldb.control_reg);
+                       if (ldb.chan_bit_map[0] == LDB_BIT_MAP_SPWG)
+                               __raw_writel((reg & ~LDB_BIT_MAP_CH0_MASK) |
+                                             LDB_BIT_MAP_CH0_SPWG,
+                                             ldb.control_reg);
+                       else
+                               __raw_writel((reg & ~LDB_BIT_MAP_CH0_MASK) |
+                                             LDB_BIT_MAP_CH0_JEIDA,
+                                             ldb.control_reg);
+
+                       reg = __raw_readl(ldb.control_reg);
+                       __raw_writel((reg & ~LDB_CH0_MODE_MASK) |
+                                     LDB_CH0_MODE_EN_TO_DI0, ldb.control_reg);
+                       if (ldb.blank[0] == FB_BLANK_UNBLANK)
+                               ldb.ch_working[0] = true;
+                       break;
+               case LDB_SIN_DI1:
+                       reg = __raw_readl(ldb.control_reg);
+                       if (bits_per_pixel(ipu_di_pix_fmt) == 24)
+                               __raw_writel((reg & ~LDB_DATA_WIDTH_CH1_MASK) |
+                                             LDB_DATA_WIDTH_CH1_24,
+                                             ldb.control_reg);
+                       else if (bits_per_pixel(ipu_di_pix_fmt) == 18)
+                               __raw_writel((reg & ~LDB_DATA_WIDTH_CH1_MASK) |
+                                             LDB_DATA_WIDTH_CH1_18,
+                                             ldb.control_reg);
+
+                       reg = __raw_readl(ldb.control_reg);
+                       if (ldb.chan_bit_map[1] == LDB_BIT_MAP_SPWG)
+                               __raw_writel((reg & ~LDB_BIT_MAP_CH1_MASK) |
+                                             LDB_BIT_MAP_CH1_SPWG,
+                                             ldb.control_reg);
+                       else
+                               __raw_writel((reg & ~LDB_BIT_MAP_CH1_MASK) |
+                                             LDB_BIT_MAP_CH1_JEIDA,
+                                             ldb.control_reg);
+
+                       reg = __raw_readl(ldb.control_reg);
+                       __raw_writel((reg & ~LDB_CH1_MODE_MASK) |
+                                     LDB_CH1_MODE_EN_TO_DI1, ldb.control_reg);
+                       if (ldb.blank[1] == FB_BLANK_UNBLANK)
+                               ldb.ch_working[1] = true;
+                       break;
+               case LDB_SEP:
+                       reg = __raw_readl(ldb.control_reg);
+                       if (ipu_di == 0) {
+                               if (bits_per_pixel(ipu_di_pix_fmt) == 24)
+                                       __raw_writel((reg & ~LDB_DATA_WIDTH_CH0_MASK) |
+                                                     LDB_DATA_WIDTH_CH0_24,
+                                                     ldb.control_reg);
+                               else if (bits_per_pixel(ipu_di_pix_fmt) == 18)
+                                       __raw_writel((reg & ~LDB_DATA_WIDTH_CH0_MASK) |
+                                                     LDB_DATA_WIDTH_CH0_18,
+                                                     ldb.control_reg);
+                       } else {
+                               if (bits_per_pixel(ipu_di_pix_fmt) == 24)
+                                       __raw_writel((reg & ~LDB_DATA_WIDTH_CH1_MASK) |
+                                                     LDB_DATA_WIDTH_CH1_24,
+                                                     ldb.control_reg);
+                               else if (bits_per_pixel(ipu_di_pix_fmt) == 18)
+                                       __raw_writel((reg & ~LDB_DATA_WIDTH_CH1_MASK) |
+                                                     LDB_DATA_WIDTH_CH1_18,
+                                                     ldb.control_reg);
+                       }
+
+                       reg = __raw_readl(ldb.control_reg);
+                       if (ldb.chan_bit_map[0] == LDB_BIT_MAP_SPWG)
+                               __raw_writel((reg & ~LDB_BIT_MAP_CH0_MASK) |
+                                             LDB_BIT_MAP_CH0_SPWG,
+                                             ldb.control_reg);
+                       else
+                               __raw_writel((reg & ~LDB_BIT_MAP_CH0_MASK) |
+                                             LDB_BIT_MAP_CH0_JEIDA,
+                                             ldb.control_reg);
+                       reg = __raw_readl(ldb.control_reg);
+                       if (ldb.chan_bit_map[1] == LDB_BIT_MAP_SPWG)
+                               __raw_writel((reg & ~LDB_BIT_MAP_CH1_MASK) |
+                                             LDB_BIT_MAP_CH1_SPWG,
+                                             ldb.control_reg);
+                       else
+                               __raw_writel((reg & ~LDB_BIT_MAP_CH1_MASK) |
+                                             LDB_BIT_MAP_CH1_JEIDA,
+                                             ldb.control_reg);
+
+                       reg = __raw_readl(ldb.control_reg);
+                       __raw_writel((reg & ~(LDB_CH0_MODE_MASK |
+                                             LDB_CH1_MODE_MASK)) |
+                                     LDB_CH0_MODE_EN_TO_DI0 |
+                                     LDB_CH1_MODE_EN_TO_DI1, ldb.control_reg);
+                       if (ldb.blank[0] == FB_BLANK_UNBLANK)
+                               ldb.ch_working[0] = true;
+                       if (ldb.blank[1] == FB_BLANK_UNBLANK)
+                               ldb.ch_working[1] = true;
+                       break;
+               case LDB_DUL_DI0:
+               case LDB_SPL_DI0:
+                       reg = __raw_readl(ldb.control_reg);
+                       if (bits_per_pixel(ipu_di_pix_fmt) == 24)
+                               __raw_writel((reg & ~(LDB_DATA_WIDTH_CH0_MASK |
+                                                     LDB_DATA_WIDTH_CH1_MASK)) |
+                                             LDB_DATA_WIDTH_CH0_24 |
+                                             LDB_DATA_WIDTH_CH1_24,
+                                             ldb.control_reg);
+                       else if (bits_per_pixel(ipu_di_pix_fmt) == 18)
+                               __raw_writel((reg & ~(LDB_DATA_WIDTH_CH0_MASK |
+                                                     LDB_DATA_WIDTH_CH1_MASK)) |
+                                             LDB_DATA_WIDTH_CH0_18 |
+                                             LDB_DATA_WIDTH_CH1_18,
+                                             ldb.control_reg);
+
+                       reg = __raw_readl(ldb.control_reg);
+                       if (ldb.chan_bit_map[0] == LDB_BIT_MAP_SPWG)
+                               __raw_writel((reg & ~LDB_BIT_MAP_CH0_MASK) |
+                                             LDB_BIT_MAP_CH0_SPWG,
+                                             ldb.control_reg);
+                       else
+                               __raw_writel((reg & ~LDB_BIT_MAP_CH0_MASK) |
+                                             LDB_BIT_MAP_CH0_JEIDA,
+                                             ldb.control_reg);
+                       reg = __raw_readl(ldb.control_reg);
+                       if (ldb.chan_bit_map[1] == LDB_BIT_MAP_SPWG)
+                               __raw_writel((reg & ~LDB_BIT_MAP_CH1_MASK) |
+                                             LDB_BIT_MAP_CH1_SPWG,
+                                             ldb.control_reg);
+                       else
+                               __raw_writel((reg & ~LDB_BIT_MAP_CH1_MASK) |
+                                             LDB_BIT_MAP_CH1_JEIDA,
+                                             ldb.control_reg);
+
+                       reg = __raw_readl(ldb.control_reg);
+                       if (ldb.chan_mode_opt == LDB_SPL_DI0)
+                               __raw_writel(reg | LDB_SPLIT_MODE_EN,
+                                             ldb.control_reg);
+
+                       reg = __raw_readl(ldb.control_reg);
+                       __raw_writel((reg & ~(LDB_CH0_MODE_MASK |
+                                             LDB_CH1_MODE_MASK)) |
+                                     LDB_CH0_MODE_EN_TO_DI0 |
+                                     LDB_CH1_MODE_EN_TO_DI0, ldb.control_reg);
+                       if (ldb.blank[0] == FB_BLANK_UNBLANK) {
+                               ldb.ch_working[0] = true;
+                               ldb.ch_working[1] = true;
+                       }
+                       break;
+               case LDB_DUL_DI1:
+               case LDB_SPL_DI1:
+                       reg = __raw_readl(ldb.control_reg);
+                       if (bits_per_pixel(ipu_di_pix_fmt) == 24)
+                               __raw_writel((reg & ~(LDB_DATA_WIDTH_CH0_MASK |
+                                                     LDB_DATA_WIDTH_CH1_MASK)) |
+                                             LDB_DATA_WIDTH_CH0_24 |
+                                             LDB_DATA_WIDTH_CH1_24,
+                                             ldb.control_reg);
+                       else if (bits_per_pixel(ipu_di_pix_fmt) == 18)
+                               __raw_writel((reg & ~(LDB_DATA_WIDTH_CH0_MASK |
+                                                     LDB_DATA_WIDTH_CH1_MASK)) |
+                                             LDB_DATA_WIDTH_CH0_18 |
+                                             LDB_DATA_WIDTH_CH1_18,
+                                             ldb.control_reg);
+
+                       reg = __raw_readl(ldb.control_reg);
+                       if (ldb.chan_bit_map[0] == LDB_BIT_MAP_SPWG)
+                               __raw_writel((reg & ~LDB_BIT_MAP_CH0_MASK) |
+                                             LDB_BIT_MAP_CH0_SPWG,
+                                             ldb.control_reg);
+                       else
+                               __raw_writel((reg & ~LDB_BIT_MAP_CH0_MASK) |
+                                             LDB_BIT_MAP_CH0_JEIDA,
+                                             ldb.control_reg);
+                       reg = __raw_readl(ldb.control_reg);
+                       if (ldb.chan_bit_map[1] == LDB_BIT_MAP_SPWG)
+                               __raw_writel((reg & ~LDB_BIT_MAP_CH1_MASK) |
+                                             LDB_BIT_MAP_CH1_SPWG,
+                                             ldb.control_reg);
+                       else
+                               __raw_writel((reg & ~LDB_BIT_MAP_CH1_MASK) |
+                                             LDB_BIT_MAP_CH1_JEIDA,
+                                             ldb.control_reg);
+
+                       reg = __raw_readl(ldb.control_reg);
+                       if (ldb.chan_mode_opt == LDB_SPL_DI1)
+                               __raw_writel(reg | LDB_SPLIT_MODE_EN,
+                                             ldb.control_reg);
+
+                       reg = __raw_readl(ldb.control_reg);
+                       __raw_writel((reg & ~(LDB_CH0_MODE_MASK |
+                                             LDB_CH1_MODE_MASK)) |
+                                     LDB_CH0_MODE_EN_TO_DI1 |
+                                     LDB_CH1_MODE_EN_TO_DI1, ldb.control_reg);
+                       if (ldb.blank[1] == FB_BLANK_UNBLANK) {
+                               ldb.ch_working[0] = true;
+                               ldb.ch_working[1] = true;
+                       }
+                       break;
+               default:
+                       break;
+               }
+               break;
+       }
+       case FB_EVENT_BLANK: {
+               if (ldb.fbi[0] != fbi && ldb.fbi[1] != fbi)
+                       return 0;
+
+               if (*((int *)event->data) == ldb.blank[ipu_di])
+                       return 0;
+
+               if (*((int *)event->data) == FB_BLANK_UNBLANK)
+                       ldb_enable(ipu_di);
+               else
+                       ldb_disable(ipu_di);
+
+               ldb.blank[ipu_di] = *((int *)event->data);
+               break;
+       }
+       default:
+               break;
+       }
+       return 0;
+}
+
+static struct notifier_block nb = {
+       .notifier_call = ldb_fb_event,
+};
+
+static long mxc_ldb_ioctl(struct file *file,
+               unsigned int cmd, unsigned long arg)
+{
+       int ret = 0;
+       uint32_t reg;
+
+       switch (cmd) {
+       case LDB_BGREF_RMODE:
+               {
+               ldb_bgref_parm parm;
+
+               if (copy_from_user(&parm, (ldb_bgref_parm *) arg,
+                                  sizeof(ldb_bgref_parm)))
+                       return -EFAULT;
+
+               spin_lock(&ldb_lock);
+               reg = __raw_readl(ldb.control_reg);
+               if (parm.bgref_mode == LDB_EXT_REF)
+                       __raw_writel((reg & ~LDB_BGREF_RMODE_MASK) |
+                                     LDB_BGREF_RMODE_EXT, ldb.control_reg);
+               else if (parm.bgref_mode == LDB_INT_REF)
+                       __raw_writel((reg & ~LDB_BGREF_RMODE_MASK) |
+                                     LDB_BGREF_RMODE_INT, ldb.control_reg);
+               spin_unlock(&ldb_lock);
+               break;
+               }
+       case LDB_VSYNC_POL:
+               {
+               ldb_vsync_parm parm;
+
+               if (copy_from_user(&parm, (ldb_vsync_parm *) arg,
+                                  sizeof(ldb_vsync_parm)))
+                       return -EFAULT;
+
+               spin_lock(&ldb_lock);
+               reg = __raw_readl(ldb.control_reg);
+               if (parm.vsync_mode == LDB_VS_ACT_H) {
+                       if (parm.di == 0)
+                               __raw_writel((reg &
+                                       ~LDB_DI0_VS_POL_MASK) |
+                                       LDB_DI0_VS_POL_ACT_HIGH,
+                                       ldb.control_reg);
+                       else
+                               __raw_writel((reg &
+                                       ~LDB_DI1_VS_POL_MASK) |
+                                       LDB_DI1_VS_POL_ACT_HIGH,
+                                       ldb.control_reg);
+               } else if (parm.vsync_mode == LDB_VS_ACT_L) {
+                       if (parm.di == 0)
+                               __raw_writel((reg &
+                                       ~LDB_DI0_VS_POL_MASK) |
+                                       LDB_DI0_VS_POL_ACT_LOW,
+                                       ldb.control_reg);
+                       else
+                               __raw_writel((reg &
+                                       ~LDB_DI1_VS_POL_MASK) |
+                                       LDB_DI1_VS_POL_ACT_LOW,
+                                       ldb.control_reg);
+
+               }
+               spin_unlock(&ldb_lock);
+               break;
+               }
+       case LDB_BIT_MAP:
+               {
+               ldb_bitmap_parm parm;
+
+               if (copy_from_user(&parm, (ldb_bitmap_parm *) arg,
+                                  sizeof(ldb_bitmap_parm)))
+                       return -EFAULT;
+
+               spin_lock(&ldb_lock);
+               reg = __raw_readl(ldb.control_reg);
+               if (parm.bitmap_mode == LDB_BIT_MAP_SPWG) {
+                       if (parm.channel == 0)
+                               __raw_writel((reg & ~LDB_BIT_MAP_CH0_MASK) |
+                                             LDB_BIT_MAP_CH0_SPWG,
+                                             ldb.control_reg);
+                       else
+                               __raw_writel((reg & ~LDB_BIT_MAP_CH0_MASK) |
+                                             LDB_BIT_MAP_CH1_SPWG,
+                                             ldb.control_reg);
+               } else if (parm.bitmap_mode == LDB_BIT_MAP_JEIDA) {
+                       if (parm.channel == 0)
+                               __raw_writel((reg & ~LDB_BIT_MAP_CH0_MASK) |
+                                             LDB_BIT_MAP_CH0_JEIDA,
+                                             ldb.control_reg);
+                       else
+                               __raw_writel((reg & ~LDB_BIT_MAP_CH0_MASK) |
+                                             LDB_BIT_MAP_CH1_JEIDA,
+                                             ldb.control_reg);
+               }
+               spin_unlock(&ldb_lock);
+               break;
+               }
+       case LDB_DATA_WIDTH:
+               {
+               ldb_data_width_parm parm;
+
+               if (copy_from_user(&parm, (ldb_data_width_parm *) arg,
+                                  sizeof(ldb_data_width_parm)))
+                       return -EFAULT;
+
+               spin_lock(&ldb_lock);
+               reg = __raw_readl(ldb.control_reg);
+               if (parm.data_width == 24) {
+                       if (parm.channel == 0)
+                               __raw_writel((reg & ~LDB_DATA_WIDTH_CH0_MASK) |
+                                             LDB_DATA_WIDTH_CH0_24,
+                                             ldb.control_reg);
+                       else
+                               __raw_writel((reg & ~LDB_DATA_WIDTH_CH0_MASK) |
+                                             LDB_DATA_WIDTH_CH1_24,
+                                             ldb.control_reg);
+               } else if (parm.data_width == 18) {
+                       if (parm.channel == 0)
+                               __raw_writel((reg & ~LDB_DATA_WIDTH_CH0_MASK) |
+                                             LDB_DATA_WIDTH_CH0_18,
+                                             ldb.control_reg);
+                       else
+                               __raw_writel((reg & ~LDB_DATA_WIDTH_CH0_MASK) |
+                                             LDB_DATA_WIDTH_CH1_18,
+                                             ldb.control_reg);
+               }
+               spin_unlock(&ldb_lock);
+               break;
+               }
+       case LDB_CHAN_MODE:
+               {
+               ldb_chan_mode_parm parm;
+               struct clk *pll4_clk;
+               unsigned long pll4_rate = 0;
+
+               if (copy_from_user(&parm, (ldb_chan_mode_parm *) arg,
+                                  sizeof(ldb_chan_mode_parm)))
+                       return -EFAULT;
+
+               spin_lock(&ldb_lock);
+
+               /* TODO:Set the correct pll4 rate for all situations */
+               pll4_clk = clk_get(g_ldb_dev, "pll4");
+               pll4_rate = clk_get_rate(pll4_clk);
+               pll4_rate = 455000000;
+               clk_set_rate(pll4_clk, pll4_rate);
+               clk_put(pll4_clk);
+
+               reg = __raw_readl(ldb.control_reg);
+               switch (parm.channel_mode) {
+               case LDB_CHAN_MODE_SIN:
+                       if (parm.di == 0) {
+                               ldb.chan_mode_opt = LDB_SIN_DI0;
+
+                               ldb.ldb_di_clk[0] = clk_get(g_ldb_dev,
+                                                           "ldb_di0_clk");
+                               clk_set_rate(ldb.ldb_di_clk[0], pll4_rate/7);
+                               clk_put(ldb.ldb_di_clk[0]);
+
+                               __raw_writel((reg & ~LDB_CH0_MODE_MASK) |
+                                             LDB_CH0_MODE_EN_TO_DI0,
+                                             ldb.control_reg);
+                       } else {
+                               ldb.chan_mode_opt = LDB_SIN_DI1;
+
+                               ldb.ldb_di_clk[1] = clk_get(g_ldb_dev,
+                                                           "ldb_di1_clk");
+                               clk_set_rate(ldb.ldb_di_clk[1], pll4_rate/7);
+                               clk_put(ldb.ldb_di_clk[1]);
+
+                               __raw_writel((reg & ~LDB_CH1_MODE_MASK) |
+                                             LDB_CH1_MODE_EN_TO_DI1,
+                                             ldb.control_reg);
+                       }
+                       break;
+               case LDB_CHAN_MODE_SEP:
+                       ldb.chan_mode_opt = LDB_SEP;
+
+                       ldb.ldb_di_clk[0] = clk_get(g_ldb_dev, "ldb_di0_clk");
+                       clk_set_rate(ldb.ldb_di_clk[0], pll4_rate/7);
+                       clk_put(ldb.ldb_di_clk[0]);
+                       ldb.ldb_di_clk[1] = clk_get(g_ldb_dev, "ldb_di1_clk");
+                       clk_set_rate(ldb.ldb_di_clk[1], pll4_rate/7);
+                       clk_put(ldb.ldb_di_clk[1]);
+
+                       __raw_writel((reg & ~(LDB_CH0_MODE_MASK |
+                                             LDB_CH1_MODE_MASK)) |
+                                     LDB_CH0_MODE_EN_TO_DI0 |
+                                     LDB_CH1_MODE_EN_TO_DI1,
+                                     ldb.control_reg);
+                       break;
+               case LDB_CHAN_MODE_DUL:
+               case LDB_CHAN_MODE_SPL:
+                       ldb.ldb_di_clk[0] = clk_get(g_ldb_dev, "ldb_di0_clk");
+                       ldb.ldb_di_clk[1] = clk_get(g_ldb_dev, "ldb_di1_clk");
+                       if (parm.di == 0) {
+                               if (parm.channel_mode == LDB_CHAN_MODE_DUL) {
+                                       ldb.chan_mode_opt = LDB_DUL_DI0;
+                                       clk_set_rate(ldb.ldb_di_clk[0],
+                                                    pll4_rate/7);
+                               } else {
+                                       ldb.chan_mode_opt = LDB_SPL_DI0;
+                                       clk_set_rate(ldb.ldb_di_clk[0],
+                                                    2*pll4_rate/7);
+                                       clk_set_rate(ldb.ldb_di_clk[1],
+                                                    2*pll4_rate/7);
+                                       reg = __raw_readl(ldb.control_reg);
+                                       __raw_writel(reg | LDB_SPLIT_MODE_EN,
+                                                     ldb.control_reg);
+                               }
+
+                               reg = __raw_readl(ldb.control_reg);
+                               __raw_writel((reg & ~(LDB_CH0_MODE_MASK |
+                                                     LDB_CH1_MODE_MASK)) |
+                                             LDB_CH0_MODE_EN_TO_DI0 |
+                                             LDB_CH1_MODE_EN_TO_DI0,
+                                             ldb.control_reg);
+                       } else {
+                               if (parm.channel_mode == LDB_CHAN_MODE_DUL) {
+                                       ldb.chan_mode_opt = LDB_DUL_DI1;
+                                       clk_set_rate(ldb.ldb_di_clk[1],
+                                                    pll4_rate/7);
+                               } else {
+                                       ldb.chan_mode_opt = LDB_SPL_DI1;
+                                       clk_set_rate(ldb.ldb_di_clk[0],
+                                                    2*pll4_rate/7);
+                                       clk_set_rate(ldb.ldb_di_clk[1],
+                                                    2*pll4_rate/7);
+                                       reg = __raw_readl(ldb.control_reg);
+                                       __raw_writel(reg | LDB_SPLIT_MODE_EN,
+                                                     ldb.control_reg);
+                               }
+
+                               reg = __raw_readl(ldb.control_reg);
+                               __raw_writel((reg & ~(LDB_CH0_MODE_MASK |
+                                                     LDB_CH1_MODE_MASK)) |
+                                             LDB_CH0_MODE_EN_TO_DI1 |
+                                             LDB_CH1_MODE_EN_TO_DI1,
+                                             ldb.control_reg);
+                       }
+                       clk_put(ldb.ldb_di_clk[0]);
+                       clk_put(ldb.ldb_di_clk[1]);
+                       break;
+               default:
+                       ret = -EINVAL;
+                       break;
+               }
+               spin_unlock(&ldb_lock);
+               break;
+               }
+       case LDB_ENABLE:
+               {
+               int ipu_di;
+
+               if (copy_from_user(&ipu_di, (int *) arg, sizeof(int)))
+                       return -EFAULT;
+
+               ldb_enable(ipu_di);
+               break;
+               }
+       case LDB_DISABLE:
+               {
+               int ipu_di;
+
+               if (copy_from_user(&ipu_di, (int *) arg, sizeof(int)))
+                       return -EFAULT;
+
+               ldb_disable(ipu_di);
+               break;
+               }
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static int mxc_ldb_open(struct inode *inode, struct file *file)
+{
+       return 0;
+}
+
+static int mxc_ldb_release(struct inode *inode, struct file *file)
+{
+       return 0;
+}
+
+static int mxc_ldb_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       return 0;
+}
+
+static const struct file_operations mxc_ldb_fops = {
+       .owner = THIS_MODULE,
+       .open = mxc_ldb_open,
+       .mmap = mxc_ldb_mmap,
+       .release = mxc_ldb_release,
+       .unlocked_ioctl = mxc_ldb_ioctl
+};
+
+/*!
+ * 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 resource *res;
+       struct ldb_platform_data *plat_data = pdev->dev.platform_data;
+       uint32_t reg;
+       struct device *temp;
+       int mxc_ldb_major;
+       struct class *mxc_ldb_class;
+
+       if (g_enable_ldb == false)
+               return -ENODEV;
+
+       spin_lock_init(&ldb_lock);
+
+       g_ldb_dev = &pdev->dev;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (IS_ERR(res))
+               return -ENODEV;
+
+       memset(&ldb, 0, sizeof(struct ldb_data));
+       ldb.chan_mode_opt = g_chan_mode_opt;
+       ldb.chan_bit_map[0] = g_chan_bit_map[0];
+       ldb.chan_bit_map[1] = g_chan_bit_map[1];
+
+       ldb.base_addr = res->start;
+       ldb_reg = ioremap(ldb.base_addr, res->end - res->start + 1);
+       ldb.control_reg = ldb_reg + 2;
+
+       ldb.bgref_rmode = plat_data->ext_ref;
+       ldb.lvds_bg_reg = regulator_get(&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);
+       }
+
+       reg = __raw_readl(ldb.control_reg);
+       if (ldb.bgref_rmode == LDB_EXT_REF)
+               __raw_writel((reg & ~LDB_BGREF_RMODE_MASK) |
+                             LDB_BGREF_RMODE_EXT, ldb.control_reg);
+       else
+               __raw_writel((reg & ~LDB_BGREF_RMODE_MASK) |
+                             LDB_BGREF_RMODE_INT, ldb.control_reg);
+
+       mxc_ldb_major = register_chrdev(0, "mxc_ldb", &mxc_ldb_fops);
+       if (mxc_ldb_major < 0) {
+               dev_err(g_ldb_dev, "Unable to register MXC LDB as a char "
+                                  "device\n");
+               ret = mxc_ldb_major;
+               goto err0;
+       }
+
+       mxc_ldb_class = class_create(THIS_MODULE, "mxc_ldb");
+       if (IS_ERR(mxc_ldb_class)) {
+               dev_err(g_ldb_dev, "Unable to create class for MXC LDB\n");
+               ret = PTR_ERR(mxc_ldb_class);
+               goto err1;
+       }
+
+       temp = device_create(mxc_ldb_class, NULL, MKDEV(mxc_ldb_major, 0),
+                       NULL, "mxc_ldb");
+       if (IS_ERR(temp)) {
+               dev_err(g_ldb_dev, "Unable to create class device for "
+                                  "MXC LDB\n");
+               ret = PTR_ERR(temp);
+               goto err2;
+       }
+
+       if (g_di0_used) {
+               mxcfb_register_mode(0, mxcfb_ldb_modedb,
+                               mxcfb_ldb_modedb_sz,
+                               MXC_DISP_SPEC_DEV);
+               mxcfb_register_presetup(0, ldb_fb_pre_setup);
+       }
+       if (g_di1_used) {
+               mxcfb_register_mode(1, mxcfb_ldb_modedb,
+                               mxcfb_ldb_modedb_sz,
+                               MXC_DISP_SPEC_DEV);
+               mxcfb_register_presetup(1, ldb_fb_pre_setup);
+       }
+
+       ret = fb_register_client(&nb);
+       if (ret < 0)
+               goto err2;
+
+       ldb.blank[0] = ldb.blank[1] = -1;
+
+       return ret;
+err2:
+       class_destroy(mxc_ldb_class);
+err1:
+       unregister_chrdev(mxc_ldb_major, "mxc_ldb");
+err0:
+       iounmap(ldb_reg);
+       return ret;
+}
+
+static int ldb_remove(struct platform_device *pdev)
+{
+       int i;
+
+       __raw_writel(0, ldb.control_reg);
+
+       for (i = 0; i < 2; i++) {
+               if (ldb.ch_working[i]) {
+                       ldb.ldb_di_clk[i] = clk_get(g_ldb_dev,
+                                           i ? "ldb_di1_clk" : "ldb_di0_clk");
+                       clk_disable(ldb.ldb_di_clk[i]);
+                       clk_put(ldb.ldb_di_clk[i]);
+                       ldb.ch_working[i] = false;
+               }
+       }
+
+       fb_unregister_client(&nb);
+       return 0;
+}
+
+static int ldb_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       switch (ldb.chan_mode_opt) {
+       case LDB_SIN_DI0:
+       case LDB_DUL_DI0:
+       case LDB_SPL_DI0:
+               if (ldb.blank[0] != FB_BLANK_UNBLANK)
+                       ldb_disable(0);
+               break;
+       case LDB_SIN_DI1:
+       case LDB_DUL_DI1:
+       case LDB_SPL_DI1:
+               if (ldb.blank[1] != FB_BLANK_UNBLANK)
+                       ldb_disable(1);
+               break;
+       case LDB_SEP:
+               if (ldb.blank[0] != FB_BLANK_UNBLANK)
+                       ldb_disable(0);
+               if (ldb.blank[1] != FB_BLANK_UNBLANK)
+                       ldb_disable(1);
+               break;
+       default:
+               break;
+       }
+       return 0;
+}
+
+static int ldb_resume(struct platform_device *pdev)
+{
+       switch (ldb.chan_mode_opt) {
+       case LDB_SIN_DI0:
+       case LDB_DUL_DI0:
+       case LDB_SPL_DI0:
+               if (ldb.blank[0] == FB_BLANK_UNBLANK)
+                       ldb_enable(0);
+               break;
+       case LDB_SIN_DI1:
+       case LDB_DUL_DI1:
+       case LDB_SPL_DI1:
+               if (ldb.blank[1] == FB_BLANK_UNBLANK)
+                       ldb_enable(1);
+               break;
+       case LDB_SEP:
+               if (ldb.blank[0] == FB_BLANK_UNBLANK)
+                       ldb_enable(0);
+               if (ldb.blank[1] == FB_BLANK_UNBLANK)
+                       ldb_enable(1);
+               break;
+       default:
+               break;
+       }
+       return 0;
+}
+
+static struct platform_driver mxcldb_driver = {
+       .driver = {
+                  .name = "mxc_ldb",
+                  },
+       .probe = ldb_probe,
+       .remove = ldb_remove,
+       .suspend = ldb_suspend,
+       .resume = ldb_resume,
+};
+
+/*
+ * Parse user specified options (`ldb=')
+ * example:
+ * ldb=single(separate, dual or split),(di=0 or di=1),
+ *     ch0_map=SPWG or JEIDA,ch1_map=SPWG or JEIDA
+ *
+ */
+static int __init ldb_setup(char *options)
+{
+       g_enable_ldb = true;
+
+       if (!strlen(options))
+               return 1;
+       else if (!strsep(&options, "="))
+               return 1;
+
+       if (!strncmp(options, "di0", 3))
+               g_di0_used = true;
+
+       if (!strncmp(options, "di1", 3))
+               g_di1_used = true;
+
+       if (!strncmp(options, "single", 6)) {
+               strsep(&options, ",");
+               if (!strncmp(options, "di=0", 4)) {
+                       g_chan_mode_opt = LDB_SIN_DI0;
+                       g_di0_used = true;
+               } else {
+                       g_chan_mode_opt = LDB_SIN_DI1;
+                       g_di1_used = true;
+               }
+       } else if (!strncmp(options, "separate", 8)) {
+               g_chan_mode_opt = LDB_SEP;
+               g_di0_used = true;
+               g_di1_used = true;
+       } else if (!strncmp(options, "dual", 4)) {
+               strsep(&options, ",");
+               if (!strncmp(options, "di=", 3)) {
+                       if (simple_strtoul(options + 3, NULL, 0) == 0) {
+                               g_chan_mode_opt = LDB_DUL_DI0;
+                               g_di0_used = true;
+                       } else {
+                               g_chan_mode_opt = LDB_DUL_DI1;
+                               g_di1_used = true;
+                       }
+               }
+       } else if (!strncmp(options, "split", 5)) {
+               strsep(&options, ",");
+               if (!strncmp(options, "di=", 3)) {
+                       if (simple_strtoul(options + 3, NULL, 0) == 0) {
+                               g_chan_mode_opt = LDB_SPL_DI0;
+                               g_di0_used = true;
+                       } else {
+                               g_chan_mode_opt = LDB_SPL_DI1;
+                               g_di1_used = true;
+                       }
+               }
+       } else
+               return 1;
+
+       if ((strsep(&options, ",") != NULL) &&
+           !strncmp(options, "ch0_map=", 8)) {
+               if (!strncmp(options + 8, "SPWG", 4))
+                       g_chan_bit_map[0] = LDB_BIT_MAP_SPWG;
+               else
+                       g_chan_bit_map[0] = LDB_BIT_MAP_JEIDA;
+       }
+
+       if (!(g_chan_mode_opt == LDB_SIN_DI0 ||
+             g_chan_mode_opt == LDB_SIN_DI1) &&
+           (strsep(&options, ",") != NULL) &&
+           !strncmp(options, "ch1_map=", 8)) {
+               if (!strncmp(options + 8, "SPWG", 4))
+                       g_chan_bit_map[1] = LDB_BIT_MAP_SPWG;
+               else
+                       g_chan_bit_map[1] = LDB_BIT_MAP_JEIDA;
+       }
+
+       return 1;
+}
+__setup("ldb", ldb_setup);
+
+static int __init ldb_init(void)
+{
+       int ret;
+
+       ret = platform_driver_register(&mxcldb_driver);
+       return 0;
+}
+
+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/mx2fb.c b/drivers/video/mxc/mx2fb.c
new file mode 100644 (file)
index 0000000..0b96771
--- /dev/null
@@ -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 (file)
index 0000000..86c7abf
--- /dev/null
@@ -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_edid.c b/drivers/video/mxc/mxc_edid.c
new file mode 100644 (file)
index 0000000..1102a64
--- /dev/null
@@ -0,0 +1,395 @@
+/*
+ * 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 mxc_edid.c
+ *
+ * @brief MXC EDID 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/mxcfb.h>
+#include <linux/fsl_devices.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <mach/mxc_edid.h>
+#include "../edid.h"
+
+#define MXC_EDID_LENGTH (EDID_LENGTH*2)
+
+#undef DEBUG  /* define this for verbose EDID parsing output */
+
+#ifdef DEBUG
+#define DPRINTK(fmt, args...) printk(fmt, ## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+struct mxc_ddc_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[MXC_EDID_LENGTH];
+
+       u32 di;
+       void (*init)(void);
+       int (*update)(void);
+} mxc_ddc;
+
+static bool g_enable_ddc;
+
+void mxc_edid_parse_ext_blk(unsigned char *edid,
+               struct mxc_edid_cfg *cfg,
+               struct fb_monspecs *specs)
+{
+       unsigned char index = 0x0;
+
+       if (edid[index++] != 0x2) /* only support cea ext block now */
+               return;
+       if (edid[index++] != 0x3) /* only support version 3*/
+               return;
+
+       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;
+
+       return fb_edid_add_monspecs(edid, specs);
+}
+
+/* make sure edid has 256 bytes*/
+int mxc_edid_read(struct i2c_adapter *adp, unsigned short addr,
+       unsigned char *edid, struct mxc_edid_cfg *cfg, struct fb_info *fbi)
+{
+       u8 buf0[2] = {0, 0};
+       int dat = 0;
+       struct i2c_msg msg[2] = {
+               {
+               .addr   = addr,
+               .flags  = 0,
+               .len    = 1,
+               .buf    = buf0,
+               }, {
+               .addr   = addr,
+               .flags  = I2C_M_RD,
+               .len    = EDID_LENGTH,
+               .buf    = edid,
+               },
+       };
+
+       if (adp == NULL)
+               return -EINVAL;
+
+       memset(edid, 0, 256);
+       memset(cfg, 0, sizeof(struct mxc_edid_cfg));
+
+       buf0[0] = 0x00;
+       dat = i2c_transfer(adp, msg, 2);
+
+       /* If 0x50 fails, try 0x37. */
+       if (edid[1] == 0x00) {
+               msg[0].addr = msg[1].addr = 0x37;
+               dat = i2c_transfer(adp, msg, 2);
+               if (dat < 0)
+                       return dat;
+       }
+
+       if (edid[1] == 0x00)
+               return -ENOENT;
+
+       /* edid first block parsing */
+       memset(&fbi->monspecs, 0, sizeof(fbi->monspecs));
+       fb_edid_to_monspecs(edid, &fbi->monspecs);
+
+       /* need read ext block? Only support one more blk now*/
+       if (edid[0x7E]) {
+               if (edid[0x7E] > 1)
+                       DPRINTK("Edid has %d ext block, \
+                               but now only support 1 ext blk\n", edid[0x7E]);
+               buf0[0] = 0x80;
+               msg[1].buf = edid + EDID_LENGTH;
+               dat = i2c_transfer(adp, msg, 2);
+               if (dat < 0)
+                       return dat;
+
+               /* edid ext block parsing */
+               mxc_edid_parse_ext_blk(edid + 128, cfg, &fbi->monspecs);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(mxc_edid_read);
+
+static ssize_t mxc_ddc_show_state(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       if (mxc_ddc.cable_plugin == 0)
+               strcpy(buf, "plugout\n");
+       else
+               strcpy(buf, "plugin\n");
+
+       return strlen(buf);
+}
+
+static DEVICE_ATTR(cable_state, S_IRUGO, mxc_ddc_show_state, NULL);
+
+static ssize_t mxc_ddc_show_edid(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       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 ",
+                                       mxc_ddc.edid[j*16 + i]);
+               len += sprintf(buf+len, "\n");
+       }
+
+       return len;
+}
+
+static DEVICE_ATTR(edid, S_IRUGO, mxc_ddc_show_edid, NULL);
+
+static void det_worker(struct work_struct *work)
+{
+       char event_string[16];
+       char *envp[] = { event_string, NULL };
+
+       /* cable connection changes */
+       if (mxc_ddc.update()) {
+               mxc_ddc.cable_plugin = 1;
+               sprintf(event_string, "EVENT=plugin");
+
+               /* make sure fb is powerdown */
+               console_lock();
+               fb_blank(mxc_ddc.fbi, FB_BLANK_POWERDOWN);
+               console_unlock();
+
+               if (mxc_edid_read(mxc_ddc.client->adapter, mxc_ddc.client->addr,
+                               mxc_ddc.edid, &mxc_ddc.edid_cfg, mxc_ddc.fbi) < 0)
+                       dev_err(&mxc_ddc.client->dev,
+                                       "MXC ddc: read edid fail\n");
+               else {
+                       if (mxc_ddc.fbi->monspecs.modedb_len > 0) {
+                               int i;
+                               const struct fb_videomode *mode;
+                               struct fb_videomode m;
+
+                               fb_destroy_modelist(&mxc_ddc.fbi->modelist);
+
+                               for (i = 0; i < mxc_ddc.fbi->monspecs.modedb_len; i++)
+                                       fb_add_videomode(&mxc_ddc.fbi->monspecs.modedb[i],
+                                                       &mxc_ddc.fbi->modelist);
+
+                               fb_var_to_videomode(&m, &mxc_ddc.fbi->var);
+                               mode = fb_find_nearest_mode(&m,
+                                               &mxc_ddc.fbi->modelist);
+
+                               fb_videomode_to_var(&mxc_ddc.fbi->var, mode);
+
+                               mxc_ddc.fbi->var.activate |= FB_ACTIVATE_FORCE;
+                               console_lock();
+                               mxc_ddc.fbi->flags |= FBINFO_MISC_USEREVENT;
+                               fb_set_var(mxc_ddc.fbi, &mxc_ddc.fbi->var);
+                               mxc_ddc.fbi->flags &= ~FBINFO_MISC_USEREVENT;
+                               console_unlock();
+                       }
+
+                       console_lock();
+                       fb_blank(mxc_ddc.fbi, FB_BLANK_UNBLANK);
+                       console_unlock();
+               }
+       } else {
+               mxc_ddc.cable_plugin = 0;
+               sprintf(event_string, "EVENT=plugout");
+               console_lock();
+               fb_blank(mxc_ddc.fbi, FB_BLANK_POWERDOWN);
+               console_unlock();
+       }
+
+       kobject_uevent_env(&mxc_ddc.pdev->dev.kobj, KOBJ_CHANGE, envp);
+}
+
+static irqreturn_t mxc_ddc_detect_handler(int irq, void *data)
+{
+       if (mxc_ddc.fbi)
+               schedule_delayed_work(&(mxc_ddc.det_work), msecs_to_jiffies(300));
+       return IRQ_HANDLED;
+}
+
+static int mxc_ddc_fb_event(struct notifier_block *nb, unsigned long val, void *v)
+{
+       struct fb_event *event = v;
+       struct fb_info *fbi = event->info;
+
+       if ((mxc_ddc.di)) {
+               if (strcmp(event->info->fix.id, "DISP3 BG - DI1"))
+                       return 0;
+       } else {
+               if (strcmp(event->info->fix.id, "DISP3 BG"))
+                       return 0;
+       }
+
+       switch (val) {
+       case FB_EVENT_FB_REGISTERED:
+               if (mxc_ddc.fbi != NULL)
+                       break;
+               mxc_ddc.fbi = fbi;
+               break;
+       }
+       return 0;
+}
+
+static struct notifier_block nb = {
+       .notifier_call = mxc_ddc_fb_event,
+};
+
+static int __devinit mxc_ddc_probe(struct i2c_client *client,
+               const struct i2c_device_id *id)
+{
+       int ret = 0;
+       struct fb_info edid_fbi;
+       struct fsl_mxc_ddc_platform_data *plat = client->dev.platform_data;
+
+       if (g_enable_ddc == false)
+               return -EPERM;
+
+       mxc_ddc.client = client;
+       mxc_ddc.di = plat->di;
+       mxc_ddc.init = plat->init;
+       mxc_ddc.update = plat->update;
+
+       if (!mxc_ddc.update)
+               return -EINVAL;
+
+       if (mxc_ddc.init)
+               mxc_ddc.init();
+
+       if (mxc_ddc.update()) {
+               mxc_ddc.cable_plugin = 1;
+               /* try to read edid */
+               if (mxc_edid_read(client->adapter, client->addr,
+                                       mxc_ddc.edid, &mxc_ddc.edid_cfg, &edid_fbi) < 0)
+                       dev_warn(&client->dev, "Can not read edid\n");
+#if defined(CONFIG_MXC_IPU_V3) && defined(CONFIG_FB_MXC_SYNC_PANEL)
+               else
+                       mxcfb_register_mode(mxc_ddc.di, edid_fbi.monspecs.modedb,
+                                       edid_fbi.monspecs.modedb_len, MXC_DISP_DDC_DEV);
+#endif
+       } else
+               mxc_ddc.cable_plugin = 0;
+
+       if (client->irq) {
+               ret = request_irq(client->irq, mxc_ddc_detect_handler,
+                               IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+                               "ddc_det", &mxc_ddc);
+               if (ret < 0) {
+                       dev_warn(&client->dev,
+                               "MXC ddc: cound not request det irq %d\n",
+                               client->irq);
+                       goto err;
+               } else {
+                       INIT_DELAYED_WORK(&(mxc_ddc.det_work), det_worker);
+                       ret = device_create_file(&mxc_ddc.pdev->dev, &dev_attr_cable_state);
+                       if (ret < 0)
+                               dev_warn(&client->dev,
+                                       "MXC ddc: cound not create sys node for cable state\n");
+                       ret = device_create_file(&mxc_ddc.pdev->dev, &dev_attr_edid);
+                       if (ret < 0)
+                               dev_warn(&client->dev,
+                                       "MXC ddc: cound not create sys node for edid\n");
+               }
+       }
+
+       fb_register_client(&nb);
+
+err:
+       return ret;
+}
+
+static int __devexit mxc_ddc_remove(struct i2c_client *client)
+{
+       fb_unregister_client(&nb);
+       return 0;
+}
+
+static int __init enable_ddc_setup(char *options)
+{
+       g_enable_ddc = true;
+
+       return 1;
+}
+__setup("ddc", enable_ddc_setup);
+
+static const struct i2c_device_id mxc_ddc_id[] = {
+       { "mxc_ddc", 0 },
+       {},
+};
+MODULE_DEVICE_TABLE(i2c, mxc_ddc_id);
+
+static struct i2c_driver mxc_ddc_i2c_driver = {
+       .driver = {
+                  .name = "mxc_ddc",
+                  },
+       .probe = mxc_ddc_probe,
+       .remove = mxc_ddc_remove,
+       .id_table = mxc_ddc_id,
+};
+
+static int __init mxc_ddc_init(void)
+{
+       int ret;
+
+       memset(&mxc_ddc, 0, sizeof(mxc_ddc));
+
+       mxc_ddc.pdev = platform_device_register_simple("mxc_ddc", 0, NULL, 0);
+       if (IS_ERR(mxc_ddc.pdev)) {
+               printk(KERN_ERR
+                               "Unable to register MXC DDC as a platform device\n");
+               ret = PTR_ERR(mxc_ddc.pdev);
+               goto err;
+       }
+
+       return i2c_add_driver(&mxc_ddc_i2c_driver);
+err:
+       return ret;
+}
+
+static void __exit mxc_ddc_exit(void)
+{
+       i2c_del_driver(&mxc_ddc_i2c_driver);
+       platform_device_unregister(mxc_ddc.pdev);
+}
+
+module_init(mxc_ddc_init);
+module_exit(mxc_ddc_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC DDC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/mxc/mxc_elcdif_fb.c b/drivers/video/mxc/mxc_elcdif_fb.c
new file mode 100644 (file)
index 0000000..7a5fa6d
--- /dev/null
@@ -0,0 +1,1466 @@
+/*
+ * Copyright (C) 2010-2011 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"
+
+/*  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;
+       struct completion vsync_complete;
+       struct completion frame_done_complete;
+       struct semaphore flip_sem;
+       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 */
+};
+
+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 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;
+
+       if (!g_elcdif_axi_clk_enable) {
+               clk_enable(g_elcdif_axi_clk);
+               g_elcdif_axi_clk_enable = true;
+       }
+
+       dev_dbg(g_elcdif_dev, "pixel clk = %d\n", pixel_clk);
+       rounded_pixel_clk = clk_round_rate(g_elcdif_pix_clk, pixel_clk);
+       clk_set_rate(g_elcdif_pix_clk, rounded_pixel_clk);
+
+       __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(BF_ELCDIF_CTRL2_OUTSTANDING_REQS
+                   (BV_ELCDIF_CTRL2_OUTSTANDING_REQS__REQ_8),
+                    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;
+}
+
+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(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_SET);
+       __raw_writel(BM_ELCDIF_CTRL_RUN,
+                    elcdif_base + HW_ELCDIF_CTRL_SET);
+       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 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;
+
+       dev_dbg(fbi->device, "Reconfiguring framebuffer\n");
+
+       sema_init(&data->flip_sem, 1);
+
+       /* release prev panel */
+       if (!g_elcdif_pix_clk_enable) {
+               clk_enable(g_elcdif_pix_clk);
+               g_elcdif_pix_clk_enable = true;
+       }
+       mxc_elcdif_blank_panel(FB_BLANK_POWERDOWN);
+       mxc_elcdif_stop();
+       release_dotclk_panel();
+       mxc_elcdif_dma_release();
+       mxc_elcdif_fb_set_fix(fbi);
+       if (g_elcdif_pix_clk_enable) {
+               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)) {
+               if (fbi->fix.smem_start)
+                       mxc_elcdif_fb_unmap_video_memory(fbi);
+
+               if (mxc_elcdif_fb_map_video_memory(fbi) < 0)
+                       return -ENOMEM;
+       }
+
+       if (data->next_blank != FB_BLANK_UNBLANK)
+               return 0;
+
+       /* init next panel */
+       if (!g_elcdif_pix_clk_enable) {
+               clk_enable(g_elcdif_pix_clk);
+               g_elcdif_pix_clk_enable = true;
+       }
+       mxc_init_elcdif();
+       mxc_elcdif_init_panel();
+
+       dev_dbg(fbi->device, "pixclock = %ul Hz\n",
+               (u32) (PICOS2KHZ(fbi->var.pixclock) * 1000UL));
+
+       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_OE_LOW_ACT))
+               sig_cfg.enable_pol = true;
+
+       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.upper_margin,
+                          fbi->var.yres,
+                          fbi->var.hsync_len,
+                          fbi->var.left_margin +
+                          fbi->var.xres + fbi->var.right_margin,
+                          fbi->var.left_margin,
+                          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();
+       mxc_elcdif_blank_panel(FB_BLANK_UNBLANK);
+
+       fbi->mode = (struct fb_videomode *)fb_match_mode(&fbi->var,
+                                                        &fbi->modelist);
+       return 0;
+}
+
+static int mxc_elcdif_fb_check_var(struct fb_var_screeninfo *var,
+                                struct fb_info *info)
+{
+       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;
+
+       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) {
+               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;
+
+       down(&data->flip_sem);
+
+       __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 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;
+
+       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;
+
+       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;
+
+       if (pdata && pdata->mode && pdata->num_modes)
+               fb_videomode_to_modelist(pdata->mode, pdata->num_modes,
+                               &fbi->modelist);
+
+       if (!fb_mode && pdata && pdata->mode_str)
+               fb_mode = pdata->mode_str;
+
+       if (fb_mode) {
+               ret = fb_find_mode(&fbi->var, fbi, fb_mode, NULL, 0, NULL,
+                                  default_bpp);
+               if ((!ret || (ret > 2)) && pdata && pdata->mode &&
+                   pdata->num_modes)
+                       fb_find_mode(&fbi->var, fbi, fb_mode, pdata->mode,
+                                       pdata->num_modes, NULL, default_bpp);
+       }
+
+       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);
+
+       ret = register_framebuffer(fbi);
+       if (ret)
+               goto err3;
+
+       platform_set_drvdata(pdev, fbi);
+
+       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:
+       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;
+}
+
+#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;
+
+       acquire_console_sem();
+       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;
+       }
+       release_console_sem();
+       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;
+
+       acquire_console_sem();
+       mxc_elcdif_fb_blank(data->next_blank, fbi);
+       fb_set_suspend(fbi, 0);
+       release_console_sem();
+
+       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_fb.c b/drivers/video/mxc/mxc_epdc_fb.c
new file mode 100644 (file)
index 0000000..bf91d41
--- /dev/null
@@ -0,0 +1,4036 @@
+/*
+ * Copyright (C) 2010-2011 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/time.h>
+
+#include "epdc_regs.h"
+
+/*
+ * Enable this define to have a default panel
+ * loaded during driver initialization
+ */
+/*#define DEFAULT_PANEL_HW_INIT*/
+
+#define NUM_SCREENS_MIN        2
+#define EPDC_NUM_LUTS 16
+#define EPDC_MAX_NUM_UPDATES 20
+#define INVALID_LUT -1
+
+#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
+
+static unsigned long default_bpp = 16;
+
+struct update_marker_data {
+       u32 update_marker;
+       struct completion update_completion;
+       int lut_num;
+};
+
+/* 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;
+       struct mxcfb_update_data upd_data;/* Update parameters */
+       dma_addr_t phys_addr;           /* Pointer to phys address of processed Y buf */
+       void *virt_addr;
+       u32 epdc_offs;                  /* Add to buffer pointer to resolve alignment */
+       u32 size;
+       int lut_num;                    /* Assigned before update is processed into working buffer */
+       int collision_mask;             /* Set when update results in collision */
+                                       /* Represents other LUTs that we collide with */
+       struct update_marker_data *upd_marker_data;
+       u32 update_order;               /* Numeric ordering value for update */
+       dma_addr_t phys_addr_copybuf;   /* Phys address of copied update data */
+       void *virt_addr_copybuf;        /* Used for PxP SW workaround */
+};
+
+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[24];
+       struct list_head list;
+       struct mxc_epdc_fb_mode *cur_mode;
+       struct mxc_epdc_fb_platform_data *pdata;
+       int blank;
+       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;
+       struct clk *epdc_clk_axi;
+       struct clk *epdc_clk_pix;
+       struct regulator *display_regulator;
+       struct regulator *vcom_regulator;
+       bool fw_default_load;
+
+       /* FB elements related to EPDC updates */
+       bool in_init;
+       bool hw_ready;
+       bool waiting_for_idle;
+       u32 auto_mode;
+       u32 upd_scheme;
+       struct update_data_list *upd_buf_queue;
+       struct update_data_list *upd_buf_free_list;
+       struct update_data_list *upd_buf_collision_list;
+       struct update_data_list *cur_update;
+       spinlock_t queue_lock;
+       int trt_entries;
+       int temp_index;
+       u8 *temp_range_bounds;
+       struct mxcfb_waveform_modes wv_modes;
+       u32 *waveform_buffer_virt;
+       u32 waveform_buffer_phys;
+       u32 waveform_buffer_size;
+       u32 *working_buffer_virt;
+       u32 working_buffer_phys;
+       u32 working_buffer_size;
+       u32 order_cnt;
+       struct update_marker_data update_marker_array[EPDC_MAX_NUM_UPDATES];
+       u32 lut_update_order[EPDC_NUM_LUTS];
+       struct completion updates_done;
+       struct delayed_work epdc_done_work;
+       struct workqueue_struct *epdc_submit_workqueue;
+       struct work_struct epdc_submit_work;
+       bool waiting_for_wb;
+       bool waiting_for_lut;
+       struct completion update_res_free;
+       struct mutex power_mutex;
+       bool powering_down;
+       int pwrdown_delay;
+
+       /* 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 reserved0_0: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;
+};
+
+struct mxcfb_waveform_data_file {
+       struct waveform_data_header wdh;
+       u32 *data;      /* Temperature Range Table + Waveform Data */
+};
+
+void __iomem *epdc_base;
+
+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);
+
+
+#ifdef DEBUG
+static void dump_pxp_config(struct mxc_epdc_fb_data *fb_data,
+                           struct pxp_config_data *pxp_conf)
+{
+       dev_err(fb_data->dev, "S0 fmt 0x%x",
+               pxp_conf->s0_param.pixel_fmt);
+       dev_err(fb_data->dev, "S0 width 0x%x",
+               pxp_conf->s0_param.width);
+       dev_err(fb_data->dev, "S0 height 0x%x",
+               pxp_conf->s0_param.height);
+       dev_err(fb_data->dev, "S0 ckey 0x%x",
+               pxp_conf->s0_param.color_key);
+       dev_err(fb_data->dev, "S0 ckey en 0x%x",
+               pxp_conf->s0_param.color_key_enable);
+
+       dev_err(fb_data->dev, "OL0 combine en 0x%x",
+               pxp_conf->ol_param[0].combine_enable);
+       dev_err(fb_data->dev, "OL0 fmt 0x%x",
+               pxp_conf->ol_param[0].pixel_fmt);
+       dev_err(fb_data->dev, "OL0 width 0x%x",
+               pxp_conf->ol_param[0].width);
+       dev_err(fb_data->dev, "OL0 height 0x%x",
+               pxp_conf->ol_param[0].height);
+       dev_err(fb_data->dev, "OL0 ckey 0x%x",
+               pxp_conf->ol_param[0].color_key);
+       dev_err(fb_data->dev, "OL0 ckey en 0x%x",
+               pxp_conf->ol_param[0].color_key_enable);
+       dev_err(fb_data->dev, "OL0 alpha 0x%x",
+               pxp_conf->ol_param[0].global_alpha);
+       dev_err(fb_data->dev, "OL0 alpha en 0x%x",
+               pxp_conf->ol_param[0].global_alpha_enable);
+       dev_err(fb_data->dev, "OL0 local alpha en 0x%x",
+               pxp_conf->ol_param[0].local_alpha_enable);
+
+       dev_err(fb_data->dev, "Out fmt 0x%x",
+               pxp_conf->out_param.pixel_fmt);
+       dev_err(fb_data->dev, "Out width 0x%x",
+               pxp_conf->out_param.width);
+       dev_err(fb_data->dev, "Out height 0x%x",
+               pxp_conf->out_param.height);
+
+       dev_err(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_err(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_err(fb_data->dev, "Scaling en 0x%x", pxp_conf->proc_data.scaling);
+       dev_err(fb_data->dev, "HFlip en 0x%x", pxp_conf->proc_data.hflip);
+       dev_err(fb_data->dev, "VFlip en 0x%x", pxp_conf->proc_data.vflip);
+       dev_err(fb_data->dev, "Rotation 0x%x", pxp_conf->proc_data.rotate);
+       dev_err(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_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_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_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_NEXTLUT 0x%x\n", __raw_readl(EPDC_STATUS_NEXTLUT));
+       printk(KERN_DEBUG "EPDC_STATUS_COL 0x%x\n", __raw_readl(EPDC_STATUS_COL));
+       printk(KERN_DEBUG "EPDC_STATUS 0x%x\n", __raw_readl(EPDC_STATUS));
+       printk(KERN_DEBUG "EPDC_DEBUG 0x%x\n", __raw_readl(EPDC_DEBUG));
+       printk(KERN_DEBUG "EPDC_DEBUG_LUT0 0x%x\n", __raw_readl(EPDC_DEBUG_LUT0));
+       printk(KERN_DEBUG "EPDC_DEBUG_LUT1 0x%x\n", __raw_readl(EPDC_DEBUG_LUT1));
+       printk(KERN_DEBUG "EPDC_DEBUG_LUT2 0x%x\n", __raw_readl(EPDC_DEBUG_LUT2));
+       printk(KERN_DEBUG "EPDC_DEBUG_LUT3 0x%x\n", __raw_readl(EPDC_DEBUG_LUT3));
+       printk(KERN_DEBUG "EPDC_DEBUG_LUT4 0x%x\n", __raw_readl(EPDC_DEBUG_LUT4));
+       printk(KERN_DEBUG "EPDC_DEBUG_LUT5 0x%x\n", __raw_readl(EPDC_DEBUG_LUT5));
+       printk(KERN_DEBUG "EPDC_DEBUG_LUT6 0x%x\n", __raw_readl(EPDC_DEBUG_LUT6));
+       printk(KERN_DEBUG "EPDC_DEBUG_LUT7 0x%x\n", __raw_readl(EPDC_DEBUG_LUT7));
+       printk(KERN_DEBUG "EPDC_DEBUG_LUT8 0x%x\n", __raw_readl(EPDC_DEBUG_LUT8));
+       printk(KERN_DEBUG "EPDC_DEBUG_LUT9 0x%x\n", __raw_readl(EPDC_DEBUG_LUT9));
+       printk(KERN_DEBUG "EPDC_DEBUG_LUT10 0x%x\n", __raw_readl(EPDC_DEBUG_LUT10));
+       printk(KERN_DEBUG "EPDC_DEBUG_LUT11 0x%x\n", __raw_readl(EPDC_DEBUG_LUT11));
+       printk(KERN_DEBUG "EPDC_DEBUG_LUT12 0x%x\n", __raw_readl(EPDC_DEBUG_LUT12));
+       printk(KERN_DEBUG "EPDC_DEBUG_LUT13 0x%x\n", __raw_readl(EPDC_DEBUG_LUT13));
+       printk(KERN_DEBUG "EPDC_DEBUG_LUT14 0x%x\n", __raw_readl(EPDC_DEBUG_LUT14));
+       printk(KERN_DEBUG "EPDC_DEBUG_LUT15 0x%x\n", __raw_readl(EPDC_DEBUG_LUT15));
+       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_err(dev,
+               "X = %d, Y = %d, Width = %d, Height = %d, WaveMode = %d, "
+               "LUT = %d, Coll Mask = 0x%x, order = %d\n",
+               upd_data_list->upd_data.update_region.left,
+               upd_data_list->upd_data.update_region.top,
+               upd_data_list->upd_data.update_region.width,
+               upd_data_list->upd_data.update_region.height,
+               upd_data_list->upd_data.waveform_mode, upd_data_list->lut_num,
+               upd_data_list->collision_mask,
+               upd_data_list->update_order);
+}
+
+static void dump_collision_list(struct mxc_epdc_fb_data *fb_data)
+{
+       struct update_data_list *plist;
+
+       dev_err(fb_data->dev, "Collision List:\n");
+       if (list_empty(&fb_data->upd_buf_collision_list->list))
+               dev_err(fb_data->dev, "Empty");
+       list_for_each_entry(plist, &fb_data->upd_buf_collision_list->list, list) {
+               dev_err(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_err(fb_data->dev, "Free List:\n");
+       if (list_empty(&fb_data->upd_buf_free_list->list))
+               dev_err(fb_data->dev, "Empty");
+       list_for_each_entry(plist, &fb_data->upd_buf_free_list->list, list) {
+               dev_err(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_queue(struct mxc_epdc_fb_data *fb_data)
+{
+       struct update_data_list *plist;
+
+       dev_err(fb_data->dev, "Queue:\n");
+       if (list_empty(&fb_data->upd_buf_queue->list))
+               dev_err(fb_data->dev, "Empty");
+       list_for_each_entry(plist, &fb_data->upd_buf_queue->list, list) {
+               dev_err(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_all_updates(struct mxc_epdc_fb_data *fb_data)
+{
+       dump_free_list(fb_data);
+       dump_queue(fb_data);
+       dump_collision_list(fb_data);
+       dev_err(fb_data->dev, "Current update being processed:\n");
+       if (fb_data->cur_update == NULL)
+               dev_err(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
+
+
+/********************************************************
+ * Start Low-Level EPDC Functions
+ ********************************************************/
+
+static inline void epdc_lut_complete_intr(u32 lut_num, bool enable)
+{
+       if (enable)
+               __raw_writel(1 << lut_num, EPDC_IRQ_MASK_SET);
+       else
+               __raw_writel(1 << lut_num, EPDC_IRQ_MASK_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_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)
+{
+       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)
+{
+       u32 val = (height << EPDC_UPD_SIZE_HEIGHT_OFFSET) | width;
+       __raw_writel(val, EPDC_UPD_SIZE);
+}
+
+static void epdc_submit_update(u32 lut_num, u32 waveform_mode, u32 update_mode,
+                              bool use_test_mode, u32 np_val)
+{
+       u32 reg_val = 0;
+
+       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);
+       }
+
+       reg_val |=
+           ((lut_num << EPDC_UPD_CTRL_LUT_SEL_OFFSET) &
+            EPDC_UPD_CTRL_LUT_SEL_MASK) |
+           ((waveform_mode << EPDC_UPD_CTRL_WAVEFORM_MODE_OFFSET) &
+            EPDC_UPD_CTRL_WAVEFORM_MODE_MASK) |
+           update_mode;
+
+       __raw_writel(reg_val, EPDC_UPD_CTRL);
+}
+
+static inline bool epdc_is_lut_complete(u32 lut_num)
+{
+       u32 val = __raw_readl(EPDC_IRQ);
+       bool is_compl = val & (1 << lut_num) ? true : false;
+
+       return is_compl;
+}
+
+static inline void epdc_clear_lut_complete_irq(u32 lut_num)
+{
+       __raw_writel(1 << lut_num, EPDC_IRQ_CLEAR);
+}
+
+static inline bool epdc_is_lut_active(u32 lut_num)
+{
+       u32 val = __raw_readl(EPDC_STATUS_LUTS);
+       bool is_active = val & (1 << lut_num) ? true : false;
+
+       return is_active;
+}
+
+static inline bool epdc_any_luts_active(void)
+{
+       bool any_active = __raw_readl(EPDC_STATUS_LUTS) ? 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 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_collision(void)
+{
+       u32 val = __raw_readl(EPDC_IRQ);
+       return (val & EPDC_IRQ_LUT_COL_IRQ) ? true : false;
+}
+
+static inline int epdc_get_colliding_luts(void)
+{
+       u32 val = __raw_readl(EPDC_STATUS_COL);
+       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);
+}
+
+void epdc_init_settings(struct mxc_epdc_fb_data *fb_data)
+{
+       struct mxc_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;
+
+       /* 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 4bit Buf pixel format */
+       reg_val = EPDC_FORMAT_TFT_PIXEL_FORMAT_2BIT
+           | EPDC_FORMAT_BUF_PIXEL_FORMAT_P4N
+           | ((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(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_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;
+       __raw_writel(reg_val, EPDC_TCE_CTRL);
+
+       /* EPDC_TCE_HSCAN */
+       epdc_set_horizontal_timing(screeninfo->left_margin,
+                                  screeninfo->right_margin,
+                                  screeninfo->hsync_len,
+                                  screeninfo->hsync_len);
+
+       /* EPDC_TCE_VSCAN */
+       epdc_set_vertical_timing(screeninfo->upper_margin,
+                                screeninfo->lower_margin,
+                                screeninfo->vsync_len);
+
+       /* 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);
+}
+
+static void epdc_powerup(struct mxc_epdc_fb_data *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, "EPDC Powerup\n");
+
+       /* Enable pins used by EPDC */
+       if (fb_data->pdata->enable_pins)
+               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);
+
+       /* 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;
+
+       mutex_unlock(&fb_data->power_mutex);
+}
+
+static void epdc_powerdown(struct mxc_epdc_fb_data *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, "EPDC Powerdown\n");
+
+       /* Disable power to the EPD panel */
+       regulator_disable(fb_data->vcom_regulator);
+       regulator_disable(fb_data->display_regulator);
+
+       /* 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)
+               fb_data->pdata->disable_pins();
+
+       fb_data->power_state = POWER_STATE_OFF;
+       fb_data->powering_down = false;
+
+       mutex_unlock(&fb_data->power_mutex);
+}
+
+static void epdc_init_sequence(struct mxc_epdc_fb_data *fb_data)
+{
+       /* Initialize EPDC, passing pointer to EPDC registers */
+       epdc_init_settings(fb_data);
+       __raw_writel(fb_data->waveform_buffer_phys, EPDC_WVADDR);
+       __raw_writel(fb_data->working_buffer_phys, EPDC_WB_ADDR);
+       epdc_powerup(fb_data);
+       draw_mode0(fb_data);
+       epdc_powerdown(fb_data);
+}
+
+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;
+
+       if (offset < info->fix.smem_len) {
+               /* mapping framebuffer memory */
+               len = info->fix.smem_len - offset;
+               vma->vm_pgoff = (info->fix.smem_start + offset) >> PAGE_SHIFT;
+       } else
+               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(info->device, "mmap remap_pfn_range failed\n");
+               return -ENOBUFS;
+       }
+
+       return 0;
+}
+
+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)
+{
+       if (regno >= 256)       /* no. of hw registers */
+               return 1;
+       /*
+        * Program hardware... do anything you want with transp
+        */
+
+       /* 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;
+}
+
+static void adjust_coordinates(struct mxc_epdc_fb_data *fb_data,
+       struct mxcfb_rect *update_region, struct mxcfb_rect *adj_update_region)
+{
+       struct fb_var_screeninfo *screeninfo = &fb_data->epdc_fb_var;
+       u32 rotation = fb_data->epdc_fb_var.rotate;
+       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 = screeninfo->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 = screeninfo->yres -
+                               (update_region->top + update_region->height);
+                       adj_update_region->left = screeninfo->xres -
+                               (update_region->left + update_region->width);
+                       break;
+               case FB_ROTATE_CCW:
+                       adj_update_region->left = update_region->top;
+                       adj_update_region->top = screeninfo->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 = screeninfo->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 = screeninfo->yres -
+                               (update_region->top + update_region->height);
+                       update_region->left = screeninfo->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 = screeninfo->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 mxc_epdc_fb_mode *epdc_modes = fb_data->pdata->epdc_mode;
+       int i;
+       int ret;
+       unsigned long flags;
+       __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_epdc_fb_flush_updates(fb_data);
+
+       spin_lock_irqsave(&fb_data->queue_lock, flags);
+       /*
+        * 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;
+       spin_unlock_irqrestore(&fb_data->queue_lock, flags);
+
+       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_GREY;
+       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_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;
+               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 epdc modes */
+               for (i = 0; i < fb_data->pdata->num_modes; i++) {
+                       if (!fb_mode_is_equal(epdc_modes[i].vmode, &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");
+                       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;
+
+               /* 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");
+                       return ret;
+               }
+       }
+
+       mxc_epdc_fb_set_fix(info);
+
+       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;
+
+       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 = default_bpp;
+
+       switch (var->bits_per_pixel) {
+       case 8:
+               if (var->grayscale != 0) {
+                       /*
+                        * 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 {
+                       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_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;
+
+       memcpy(&fb_data->wv_modes, modes, sizeof(modes));
+}
+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;
+
+       if (fb_data->trt_entries == 0) {
+               dev_err(fb_data->dev,
+                       "No TRT exists...using default temp index\n");
+               return DEFAULT_TEMP_INDEX;
+       }
+
+       /* Search temperature ranges for a match */
+       for (i = 0; i < fb_data->trt_entries - 1; i++) {
+               if ((temp >= fb_data->temp_range_bounds[i])
+                       && (temp < fb_data->temp_range_bounds[i+1])) {
+                       index = i;
+                       break;
+               }
+       }
+
+       if (index < 0) {
+               dev_err(fb_data->dev,
+                       "No TRT index match...using default temp index\n");
+               return DEFAULT_TEMP_INDEX;
+       }
+
+       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;
+       unsigned long flags;
+
+       /* Store temp index. Used later when configuring updates. */
+       spin_lock_irqsave(&fb_data->queue_lock, flags);
+       fb_data->temp_index = mxc_epdc_fb_get_temp_index(fb_data, temperature);
+       spin_unlock_irqrestore(&fb_data->queue_lock, flags);
+
+       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;
+
+       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_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;
+
+       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))
+               fb_data->upd_scheme = upd_scheme;
+       else {
+               dev_err(fb_data->dev, "Invalid update scheme specified.\n");
+               return -EINVAL;
+       }
+
+       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)
+{
+       int i;
+       unsigned char *temp_buf_ptr = upd_data_list->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;
+
+       /* Set source buf pointer based on input source, panning, etc. */
+       if (upd_data_list->upd_data.flags & EPDC_FLAG_USE_ALT_BUFFER) {
+               src_upd_region = &upd_data_list->upd_data.alt_buffer_data.alt_update_region;
+               src_stride =
+                       upd_data_list->upd_data.alt_buffer_data.width * bpp/8;
+               src_ptr = upd_data_list->upd_data.alt_buffer_data.virt_addr
+                       + src_upd_region->top * src_stride;
+       } else {
+               src_upd_region = &upd_data_list->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;
+
+       int ret;
+
+       /*
+        * Gotta do a whole bunch of buffer ptr manipulation to
+        * work around HW restrictions for PxP & EPDC
+        */
+
+       /*
+        * Are we using FB or an alternate (overlay)
+        * buffer for source of update?
+        */
+       if (upd_data_list->upd_data.flags & EPDC_FLAG_USE_ALT_BUFFER) {
+               src_width = upd_data_list->upd_data.alt_buffer_data.width;
+               src_height = upd_data_list->upd_data.alt_buffer_data.height;
+               src_upd_region = &upd_data_list->upd_data.alt_buffer_data.alt_update_region;
+       } else {
+               src_width = fb_data->epdc_fb_var.xres_virtual;
+               src_height = fb_data->epdc_fb_var.yres;
+               src_upd_region = &upd_data_list->upd_data.update_region;
+       }
+
+       bytes_per_pixel = fb_data->epdc_fb_var.bits_per_pixel/8;
+
+       /*
+        * SW workaround for PxP limitation
+        *
+        * 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;
+       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)))
+               line_overflow = true;
+
+       if (((width_unaligned || height_unaligned || input_unaligned) &&
+               (upd_data_list->upd_data.waveform_mode == WAVEFORM_MODE_AUTO))
+               || line_overflow) {
+
+               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;
+       }
+
+       /*
+        * Compute buffer offset to account for
+        * PxP limitation (input must be 32-bit aligned)
+        */
+       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 should change to reflect relative position to input ptr */
+               pxp_upd_region.top = 0;
+               pxp_upd_region.left = offset_from_4 / bytes_per_pixel;
+       } else {
+               pxp_input_offs =
+                       (src_upd_region->top * src_width + src_upd_region->left)
+                       * bytes_per_pixel;
+               /* Update region should change to reflect relative position to input ptr */
+               pxp_upd_region.top = 0;
+               pxp_upd_region.left = 0;
+       }
+
+       /* 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:
+               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_data_list->epdc_offs = ALIGN(pxp_output_offs, 8);
+
+       mutex_lock(&fb_data->pxp_mutex);
+
+       /* Source address either comes from alternate buffer
+          provided in update data, or from the framebuffer. */
+       if (use_temp_buf)
+               sg_dma_address(&fb_data->sg[0]) =
+                       upd_data_list->phys_addr_copybuf;
+       else if (upd_data_list->upd_data.flags & EPDC_FLAG_USE_ALT_BUFFER)
+               sg_dma_address(&fb_data->sg[0]) =
+                       upd_data_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 */
+       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),
+                   upd_data_list->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_data_list->upd_data.flags & EPDC_FLAG_ENABLE_INVERSION)
+               fb_data->pxp_conf.proc_data.lut_transform |= PXP_LUT_INVERT;
+       if (upd_data_list->upd_data.flags & EPDC_FLAG_FORCE_MONOCHROME)
+               fb_data->pxp_conf.proc_data.lut_transform |=
+                       PXP_LUT_BLACK_WHITE;
+
+       /*
+        * Toggle inversion processing if 8-bit
+        * inverted is the current pixel format.
+        */
+       if (fb_data->epdc_fb_var.grayscale == GRAYSCALE_8BIT_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 EPDC HW while ePxP is processing */
+       if ((fb_data->power_state == POWER_STATE_OFF)
+               || fb_data->powering_down) {
+               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);
+               return ret;
+       }
+
+       mutex_unlock(&fb_data->pxp_mutex);
+
+       /* Update waveform mode from PxP histogram results */
+       if (upd_data_list->upd_data.waveform_mode == WAVEFORM_MODE_AUTO) {
+               if (hist_stat & 0x1)
+                       upd_data_list->upd_data.waveform_mode =
+                               fb_data->wv_modes.mode_du;
+               else if (hist_stat & 0x2)
+                       upd_data_list->upd_data.waveform_mode =
+                               fb_data->wv_modes.mode_gc4;
+               else if (hist_stat & 0x4)
+                       upd_data_list->upd_data.waveform_mode =
+                               fb_data->wv_modes.mode_gc8;
+               else if (hist_stat & 0x8)
+                       upd_data_list->upd_data.waveform_mode =
+                               fb_data->wv_modes.mode_gc16;
+               else
+                       upd_data_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_data_list->upd_data.waveform_mode);
+       }
+
+       return 0;
+
+}
+
+static int epdc_submit_merge(struct update_data_list *upd_data_list,
+                               struct update_data_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_data_list->upd_data;
+       b = &update_to_merge->upd_data;
+       arect = &upd_data_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) ||
+               a->update_mode != b->update_mode ||
+               arect->left > (brect->left + brect->width) ||
+               brect->left > (arect->left + arect->width) ||
+               arect->top > (brect->top + brect->height) ||
+               brect->top > (arect->top + arect->height) ||
+               (b->update_marker != 0 && a->update_marker != 0))
+               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;
+
+       /* Preserve marker value for merged update */
+       if (b->update_marker != 0) {
+               a->update_marker = b->update_marker;
+               upd_data_list->upd_marker_data =
+                       update_to_merge->upd_marker_data;
+       }
+
+       /* Merged update should take on the earliest order */
+       upd_data_list->update_order =
+               (upd_data_list->update_order > update_to_merge->update_order) ?
+               upd_data_list->update_order : update_to_merge->update_order;
+
+       return MERGE_OK;
+}
+
+static void epdc_submit_work_func(struct work_struct *work)
+{
+       int temp_index;
+       struct update_data_list *next_update;
+       struct update_data_list *temp;
+       unsigned long flags;
+       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;
+       bool end_merge = false;
+
+       /* Protect access to buffer queues and to update HW */
+       spin_lock_irqsave(&fb_data->queue_lock, flags);
+
+       /*
+        * 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,
+                               &fb_data->upd_buf_collision_list->list, list) {
+
+               if (next_update->collision_mask != 0)
+                       continue;
+
+               dev_dbg(fb_data->dev, "A collision update is ready to go!\n");
+
+               /*
+                * We have a collision cleared, so select it for resubmission.
+                * If an update is already selected, attempt to merge.
+                */
+               if (!upd_data_list) {
+                       upd_data_list = next_update;
+                       list_del_init(&next_update->list);
+                       if (fb_data->upd_scheme == UPDATE_SCHEME_QUEUE)
+                               /* If not merging, we have our update */
+                               break;
+               } else {
+                       switch (epdc_submit_merge(upd_data_list,
+                                                       next_update)) {
+                       case MERGE_OK:
+                               dev_dbg(fb_data->dev,
+                                       "Update merged [collision]\n");
+                               list_del_init(&next_update->list);
+                               /* Add to free buffer list */
+                               list_add_tail(&next_update->list,
+                                        &fb_data->upd_buf_free_list->list);
+                               break;
+                       case MERGE_FAIL:
+                               dev_dbg(fb_data->dev,
+                                       "Update not merged [collision]\n");
+                               break;
+                       case MERGE_BLOCK:
+                               dev_dbg(fb_data->dev,
+                                       "Merge blocked [collision]\n");
+                               end_merge = true;
+                               break;
+                       }
+
+                       if (end_merge) {
+                               end_merge = false;
+                               break;
+                       }
+               }
+       }
+
+       /*
+        * Skip update queue only if we found a collision
+        * update and we are not merging
+        */
+       if (!((fb_data->upd_scheme == UPDATE_SCHEME_QUEUE) &&
+               upd_data_list)) {
+               /*
+                * If we didn't find a collision update ready to go,
+                * we try to grab one from the update queue
+                */
+               list_for_each_entry_safe(next_update, temp,
+                                       &fb_data->upd_buf_queue->list, list) {
+
+                       dev_dbg(fb_data->dev, "Found a pending update!\n");
+
+                       if (!upd_data_list) {
+                               upd_data_list = next_update;
+                               list_del_init(&next_update->list);
+                               if (fb_data->upd_scheme == UPDATE_SCHEME_QUEUE)
+                                       /* If not merging, we have an update */
+                                       break;
+                       } else {
+                               switch (epdc_submit_merge(upd_data_list,
+                                                               next_update)) {
+                               case MERGE_OK:
+                                       dev_dbg(fb_data->dev,
+                                               "Update merged [queue]\n");
+                                       list_del_init(&next_update->list);
+                                       /* Add to free buffer list */
+                                       list_add_tail(&next_update->list,
+                                                &fb_data->upd_buf_free_list->list);
+                                       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;
+                       }
+               }
+       }
+
+       /* Release buffer queues */
+       spin_unlock_irqrestore(&fb_data->queue_lock, flags);
+
+       /* Is update list empty? */
+       if (!upd_data_list)
+               return;
+
+       /* 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 */
+               spin_lock_irqsave(&fb_data->queue_lock, flags);
+               /* Add to free buffer list */
+               list_add_tail(&upd_data_list->list,
+                        &fb_data->upd_buf_free_list->list);
+               /* Release buffer queues */
+               spin_unlock_irqrestore(&fb_data->queue_lock, flags);
+               return;
+       }
+
+       /* Get rotation-adjusted coordinates */
+       adjust_coordinates(fb_data, &upd_data_list->upd_data.update_region,
+               &adj_update_region);
+
+       /* Protect access to buffer queues and to update HW */
+       spin_lock_irqsave(&fb_data->queue_lock, flags);
+
+       /*
+        * 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) {
+               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 */
+               spin_unlock_irqrestore(&fb_data->queue_lock, flags);
+               wait_for_completion(&fb_data->update_res_free);
+               spin_lock_irqsave(&fb_data->queue_lock, flags);
+       }
+
+       /*
+        * 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()) {
+               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 */
+               spin_unlock_irqrestore(&fb_data->queue_lock, flags);
+               wait_for_completion(&fb_data->update_res_free);
+               spin_lock_irqsave(&fb_data->queue_lock, flags);
+       }
+
+
+       /* LUTs are available, so we get one here */
+       fb_data->cur_update = upd_data_list;
+       fb_data->cur_update->lut_num = epdc_get_next_lut();
+
+       /* Associate LUT with update marker */
+       if ((fb_data->cur_update->upd_marker_data)
+               && (fb_data->cur_update->upd_marker_data->update_marker != 0))
+               fb_data->cur_update->upd_marker_data->lut_num =
+                                               fb_data->cur_update->lut_num;
+
+       /* Mark LUT with order */
+       fb_data->lut_update_order[fb_data->cur_update->lut_num] =
+               fb_data->cur_update->update_order;
+
+       /* Enable Collision and WB complete IRQs */
+       epdc_working_buf_intr(true);
+       epdc_lut_complete_intr(fb_data->cur_update->lut_num, true);
+
+       /* Program EPDC update to process buffer */
+       if (fb_data->cur_update->upd_data.temp != TEMP_USE_AMBIENT) {
+               temp_index = mxc_epdc_fb_get_temp_index(fb_data,
+                               fb_data->cur_update->upd_data.temp);
+               epdc_set_temp(temp_index);
+       }
+       epdc_set_update_addr(fb_data->cur_update->phys_addr
+                               + fb_data->cur_update->epdc_offs);
+       epdc_set_update_coord(adj_update_region.left, adj_update_region.top);
+       epdc_set_update_dimensions(adj_update_region.width,
+                                  adj_update_region.height);
+       epdc_submit_update(fb_data->cur_update->lut_num,
+                          fb_data->cur_update->upd_data.waveform_mode,
+                          fb_data->cur_update->upd_data.update_mode, false, 0);
+
+       /* Release buffer queues */
+       spin_unlock_irqrestore(&fb_data->queue_lock, flags);
+}
+
+
+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;
+       struct update_data_list *upd_data_list = NULL;
+       unsigned long flags;
+       int i;
+       struct mxcfb_rect *screen_upd_region; /* Region on screen to update */
+       int temp_index;
+       int ret;
+
+       /* Has EPDC HW been initialized? */
+       if (!fb_data->hw_ready) {
+               dev_err(fb_data->dev, "Display HW not properly initialized."
+                       "  Aborting update.\n");
+               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);
+               return -EINVAL;
+       }
+       if ((upd_data->waveform_mode > 255) &&
+               (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->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");
+               return -EINVAL;
+       }
+       if ((upd_data->flags & EPDC_FLAG_USE_ALT_BUFFER) &&
+               ((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;
+       }
+
+       spin_lock_irqsave(&fb_data->queue_lock, flags);
+
+       /*
+        * 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");
+               spin_unlock_irqrestore(&fb_data->queue_lock, flags);
+               return -EPERM;
+       }
+
+       /*
+        * Get available intermediate (PxP output) buffer to hold
+        * processed update region
+        */
+       if (list_empty(&fb_data->upd_buf_free_list->list)) {
+               dev_err(fb_data->dev,
+                       "No free intermediate buffers available.\n");
+               spin_unlock_irqrestore(&fb_data->queue_lock, flags);
+               return -ENOMEM;
+       }
+
+       /* Grab first available buffer and delete it from the free list */
+       upd_data_list =
+           list_entry(fb_data->upd_buf_free_list->list.next,
+                      struct update_data_list, list);
+
+       list_del_init(&upd_data_list->list);
+
+       /* copy update parameters to the current update data object */
+       memcpy(&upd_data_list->upd_data, upd_data,
+              sizeof(struct mxcfb_update_data));
+       memcpy(&upd_data_list->upd_data.update_region, &upd_data->update_region,
+              sizeof(struct mxcfb_rect));
+
+       /* If marker specified, associate it with a completion */
+       if (upd_data->update_marker != 0) {
+               /* Find available update marker and set it up */
+               for (i = 0; i < EPDC_MAX_NUM_UPDATES; i++) {
+                       /* Marker value set to 0 signifies it is not currently in use */
+                       if (fb_data->update_marker_array[i].update_marker == 0) {
+                               fb_data->update_marker_array[i].update_marker = upd_data->update_marker;
+                               init_completion(&fb_data->update_marker_array[i].update_completion);
+                               upd_data_list->upd_marker_data = &fb_data->update_marker_array[i];
+                               break;
+                       }
+               }
+       } else {
+               if (upd_data_list->upd_marker_data)
+                       upd_data_list->upd_marker_data->update_marker = 0;
+       }
+
+       upd_data_list->update_order = fb_data->order_cnt++;
+
+       if (fb_data->upd_scheme != UPDATE_SCHEME_SNAPSHOT) {
+               /* Queued update scheme processing */
+
+               /* Add processed Y buffer to update list */
+               list_add_tail(&upd_data_list->list,
+                                 &fb_data->upd_buf_queue->list);
+
+               spin_unlock_irqrestore(&fb_data->queue_lock, flags);
+
+               /* Signal workqueue to handle new update */
+               queue_work(fb_data->epdc_submit_workqueue,
+                       &fb_data->epdc_submit_work);
+
+               return 0;
+       }
+
+       /* Snapshot update scheme processing */
+
+       spin_unlock_irqrestore(&fb_data->queue_lock, flags);
+
+       /*
+        * Hold on to original screen update region, which we
+        * will ultimately use when telling EPDC where to update on panel
+        */
+       screen_upd_region = &upd_data_list->upd_data.update_region;
+
+       ret = epdc_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_data_list->upd_data.waveform_mode;
+
+       /* Get rotation-adjusted coordinates */
+       adjust_coordinates(fb_data, &upd_data_list->upd_data.update_region,
+               NULL);
+
+       /* Grab lock for queue manipulation and update submission */
+       spin_lock_irqsave(&fb_data->queue_lock, flags);
+
+       /*
+        * 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->list);
+
+               /* Return and allow the update to be submitted by the ISR. */
+               spin_unlock_irqrestore(&fb_data->queue_lock, flags);
+               return 0;
+       }
+
+       /* Save current update */
+       fb_data->cur_update = upd_data_list;
+
+       /* LUTs are available, so we get one here */
+       upd_data_list->lut_num = epdc_get_next_lut();
+
+       /* Associate LUT with update marker */
+       if (upd_data_list->upd_marker_data)
+               if (upd_data_list->upd_marker_data->update_marker != 0)
+                       upd_data_list->upd_marker_data->lut_num = upd_data_list->lut_num;
+
+       /* Mark LUT as containing new update */
+       fb_data->lut_update_order[upd_data_list->lut_num] =
+               upd_data_list->update_order;
+
+       /* Clear status and Enable LUT complete and WB complete IRQs */
+       epdc_working_buf_intr(true);
+       epdc_lut_complete_intr(fb_data->cur_update->lut_num, true);
+
+       /* Program EPDC update to process buffer */
+       epdc_set_update_addr(upd_data_list->phys_addr + upd_data_list->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 (upd_data_list->upd_data.temp != TEMP_USE_AMBIENT) {
+               temp_index = mxc_epdc_fb_get_temp_index(fb_data,
+                       upd_data_list->upd_data.temp);
+               epdc_set_temp(temp_index);
+       } else
+               epdc_set_temp(fb_data->temp_index);
+
+       epdc_submit_update(upd_data_list->lut_num,
+                          upd_data_list->upd_data.waveform_mode,
+                          upd_data_list->upd_data.update_mode, false, 0);
+
+       spin_unlock_irqrestore(&fb_data->queue_lock, flags);
+       return 0;
+}
+EXPORT_SYMBOL(mxc_epdc_fb_send_update);
+
+int mxc_epdc_fb_wait_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;
+       int ret;
+       int i;
+
+       /* 0 is an invalid update_marker value */
+       if (update_marker == 0)
+               return -EINVAL;
+
+       /*
+        * Wait for completion associated with update_marker requested.
+        * Note: If update completed already, marker will have been
+        * cleared and we will just return
+        */
+       for (i = 0; i < EPDC_MAX_NUM_UPDATES; i++) {
+               if (fb_data->update_marker_array[i].update_marker == update_marker) {
+                       dev_dbg(fb_data->dev, "Waiting for marker %d\n", update_marker);
+                       ret = wait_for_completion_timeout(&fb_data->update_marker_array[i].update_completion, msecs_to_jiffies(5000));
+                       if (!ret)
+                               dev_err(fb_data->dev, "Timed out waiting for update completion\n");
+                       dev_dbg(fb_data->dev, "marker %d signalled!\n", update_marker);
+                       break;
+               }
+       }
+
+       return 0;
+}
+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_ioctl(struct fb_info *info, unsigned int cmd,
+                            unsigned long arg)
+{
+       void __user *argp = (void __user *)arg;
+       int ret = -EINVAL;
+
+       switch (cmd) {
+       case MXCFB_SET_WAVEFORM_MODES:
+               {
+                       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:
+               {
+                       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:
+               {
+                       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:
+               {
+                       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:
+               {
+                       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;
+               }
+       case MXCFB_WAIT_FOR_UPDATE_COMPLETE:
+               {
+                       u32 update_marker = 0;
+                       if (!get_user(update_marker, (__u32 __user *) arg))
+                               ret =
+                                   mxc_epdc_fb_wait_update_complete(update_marker,
+                                       info);
+                       break;
+               }
+
+       case MXCFB_SET_PWRDOWN_DELAY:
+               {
+                       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:
+               {
+                       int pwrdown_delay = mxc_epdc_get_pwrdown_delay(info);
+                       if (put_user(pwrdown_delay,
+                               (int __user *)argp))
+                               ret = -EFAULT;
+                       ret = 0;
+                       break;
+               }
+       default:
+               break;
+       }
+       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;
+
+       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);
+}
+
+void mxc_epdc_fb_flush_updates(struct mxc_epdc_fb_data *fb_data)
+{
+       unsigned long flags;
+       /* Grab queue lock to prevent any new updates from being submitted */
+       spin_lock_irqsave(&fb_data->queue_lock, flags);
+
+       if (!is_free_list_full(fb_data)) {
+               /* Initialize event signalling updates are done */
+               init_completion(&fb_data->updates_done);
+               fb_data->waiting_for_idle = true;
+
+               spin_unlock_irqrestore(&fb_data->queue_lock, flags);
+               /* Wait for any currently active updates to complete */
+               wait_for_completion_timeout(&fb_data->updates_done, msecs_to_jiffies(2000));
+               spin_lock_irqsave(&fb_data->queue_lock, flags);
+               fb_data->waiting_for_idle = false;
+       }
+
+       spin_unlock_irqrestore(&fb_data->queue_lock, flags);
+}
+
+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;
+
+       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:
+       case FB_BLANK_VSYNC_SUSPEND:
+       case FB_BLANK_HSYNC_SUSPEND:
+       case FB_BLANK_NORMAL:
+               mxc_epdc_fb_flush_updates(fb_data);
+               break;
+       }
+       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;
+       unsigned long flags;
+
+       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;
+       }
+
+       spin_lock_irqsave(&fb_data->queue_lock, flags);
+
+       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;
+
+       spin_unlock_irqrestore(&fb_data->queue_lock, flags);
+
+       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_setcolreg = mxc_epdc_fb_setcolreg,
+       .fb_pan_display = mxc_epdc_fb_pan_display,
+       .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 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, list)
+               count++;
+
+       /* Check to see if all buffers are in this list */
+       if (count == EPDC_MAX_NUM_UPDATES)
+               return true;
+       else
+               return false;
+}
+
+static bool do_updates_overlap(struct update_data_list *update1,
+                                       struct update_data_list *update2)
+{
+       struct mxcfb_rect *rect1 = &update1->upd_data.update_region;
+       struct mxcfb_rect *rect2 = &update2->upd_data.update_region;
+       __u32 bottom1, bottom2, right1, right2;
+       bottom1 = rect1->top + rect1->height;
+       bottom2 = rect2->top + rect2->height;
+       right1 = rect1->left + rect1->width;
+       right2 = rect2->left + rect2->width;
+
+       if ((rect1->top < bottom2) &&
+               (bottom1 > rect2->top) &&
+               (rect1->left < right2) &&
+               (right1 > rect2->left)) {
+               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;
+       struct update_data_list *collision_update;
+       struct mxcfb_rect *next_upd_region;
+       unsigned long flags;
+       int temp_index;
+       u32 luts_completed_mask;
+       u32 temp_mask;
+       u32 missed_coll_mask = 0;
+       u32 lut;
+       bool ignore_collision = false;
+       int i, j;
+
+       /*
+        * If we just completed one-time panel init, bypass
+        * queue handling, clear interrupt and return
+        */
+       if (fb_data->in_init) {
+               if (epdc_is_working_buffer_complete()) {
+                       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(0)) {
+                       epdc_lut_complete_intr(0, false);
+                       epdc_clear_lut_complete_irq(0);
+                       fb_data->in_init = false;
+                       dev_dbg(fb_data->dev, "Cleared LUT complete for init update\n");
+               }
+
+               return IRQ_HANDLED;
+       }
+
+       if (!(__raw_readl(EPDC_IRQ_MASK) & __raw_readl(EPDC_IRQ)))
+               return IRQ_HANDLED;
+
+       if (__raw_readl(EPDC_IRQ) & EPDC_IRQ_TCE_UNDERRUN_IRQ) {
+               dev_err(fb_data->dev, "TCE underrun!  Panel may lock up.\n");
+               return IRQ_HANDLED;
+       }
+
+       /* Protect access to buffer queues and to update HW */
+       spin_lock_irqsave(&fb_data->queue_lock, flags);
+
+       /* Free any LUTs that have completed */
+       luts_completed_mask = 0;
+       for (i = 0; i < EPDC_NUM_LUTS; i++) {
+               if (!epdc_is_lut_complete(i))
+                       continue;
+
+               dev_dbg(fb_data->dev, "\nLUT %d completed\n", i);
+
+               /* Disable IRQ for completed LUT */
+               epdc_lut_complete_intr(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, list) {
+                       collision_update->collision_mask =
+                           collision_update->collision_mask & ~(1 << i);
+               }
+
+               epdc_clear_lut_complete_irq(i);
+
+               luts_completed_mask |= 1 << i;
+
+               fb_data->lut_update_order[i] = 0;
+
+               /* Signal completion if submit workqueue needs a LUT */
+               if (fb_data->waiting_for_lut) {
+                       complete(&fb_data->update_res_free);
+                       fb_data->waiting_for_lut = false;
+               }
+
+               /* Signal completion if anyone waiting on this LUT */
+               for (j = 0; j < EPDC_MAX_NUM_UPDATES; j++) {
+                       if (fb_data->update_marker_array[j].lut_num != i)
+                               continue;
+
+                       /* Signal completion of update */
+                       dev_dbg(fb_data->dev,
+                               "Signaling marker %d\n",
+                               fb_data->update_marker_array[j].update_marker);
+                       complete(&fb_data->update_marker_array[j].update_completion);
+                       /* Ensure this doesn't get signaled again inadvertently */
+                       fb_data->update_marker_array[j].lut_num = INVALID_LUT;
+                       /*
+                        * Setting marker to 0 is OK - any wait call will
+                        * return when marker doesn't match any in array
+                        */
+                       fb_data->update_marker_array[j].update_marker = 0;
+               }
+       }
+
+       /* Check to see if all updates have completed */
+       if (is_free_list_full(fb_data) &&
+               (fb_data->cur_update == NULL) &&
+               !epdc_any_luts_active()) {
+
+               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 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)
+                       complete(&fb_data->updates_done);
+       }
+
+       /* Is Working Buffer busy? */
+       if (epdc_is_working_buffer_busy()) {
+               /* Can't submit another update until WB is done */
+               spin_unlock_irqrestore(&fb_data->queue_lock, flags);
+               return IRQ_HANDLED;
+       }
+
+       /*
+        * Were we waiting on working buffer?
+        * If so, update queues and check for collisions
+        */
+       if (fb_data->cur_update != NULL) {
+               dev_dbg(fb_data->dev, "\nWorking buffer completed\n");
+
+               /* Signal completion if submit workqueue was waiting on WB */
+               if (fb_data->waiting_for_wb) {
+                       complete(&fb_data->update_res_free);
+                       fb_data->waiting_for_lut = false;
+               }
+
+               /*
+                * Check for "missed collision" conditions:
+                *  - Current update overlaps one or more updates
+                *    in collision list
+                *  - No collision reported with current active updates
+                */
+               list_for_each_entry(collision_update,
+                                   &fb_data->upd_buf_collision_list->list,
+                                   list)
+                       if (do_updates_overlap(collision_update,
+                               fb_data->cur_update))
+                               missed_coll_mask |=
+                                       collision_update->collision_mask;
+
+               /* Was there a collision? */
+               if (epdc_is_collision() || missed_coll_mask) {
+                       /* Check list of colliding LUTs, and add to our collision mask */
+                       fb_data->cur_update->collision_mask =
+                           epdc_get_colliding_luts();
+
+                       if (!fb_data->cur_update->collision_mask) {
+                               fb_data->cur_update->collision_mask =
+                                       missed_coll_mask;
+                               dev_dbg(fb_data->dev, "Missed collision "
+                                       "possible. Mask = 0x%x\n",
+                                       missed_coll_mask);
+                       }
+
+                       /* Clear collisions that just completed */
+                       fb_data->cur_update->collision_mask &= ~luts_completed_mask;
+
+                       dev_dbg(fb_data->dev, "\nCollision mask = 0x%x\n",
+                              fb_data->cur_update->collision_mask);
+
+                       /*
+                        * 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) {
+                               if (!(temp_mask & 0x1))
+                                       continue;
+
+                               if (fb_data->lut_update_order[lut] >=
+                                       fb_data->cur_update->update_order) {
+                                       dev_dbg(fb_data->dev, "Ignoring collision with newer update.\n");
+                                       ignore_collision = true;
+                                       break;
+                               }
+                       }
+
+                       if (ignore_collision) {
+                               /* Add to free buffer list */
+                               list_add_tail(&fb_data->cur_update->list,
+                                        &fb_data->upd_buf_free_list->list);
+                       } else {
+                               /*
+                                * If update has a marker, clear the LUT, since we
+                                * don't want to signal that it is complete.
+                                */
+                               if (fb_data->cur_update->upd_marker_data)
+                                       if (fb_data->cur_update->upd_marker_data->update_marker != 0)
+                                               fb_data->cur_update->upd_marker_data->lut_num = INVALID_LUT;
+
+                               /* Move to collision list */
+                               list_add_tail(&fb_data->cur_update->list,
+                                        &fb_data->upd_buf_collision_list->list);
+                       }
+               } else {
+                       /* Add to free buffer list */
+                       list_add_tail(&fb_data->cur_update->list,
+                                &fb_data->upd_buf_free_list->list);
+               }
+               /* 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) {
+               /* Queued update scheme processing */
+
+               /* Schedule task to submit collision and pending update */
+               if (!fb_data->powering_down)
+                       queue_work(fb_data->epdc_submit_workqueue,
+                               &fb_data->epdc_submit_work);
+
+               /* Release buffer queues */
+               spin_unlock_irqrestore(&fb_data->queue_lock, flags);
+
+               return IRQ_HANDLED;
+       }
+
+       /* Snapshot update scheme processing */
+
+       /* Check to see if any LUTs are free */
+       if (!epdc_any_luts_available()) {
+               dev_dbg(fb_data->dev, "No luts available.\n");
+               spin_unlock_irqrestore(&fb_data->queue_lock, flags);
+               return IRQ_HANDLED;
+       }
+
+       /*
+        * 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, list) {
+
+               if (collision_update->collision_mask != 0)
+                       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) {
+               /* Is update list empty? */
+               if (list_empty(&fb_data->upd_buf_queue->list)) {
+                       dev_dbg(fb_data->dev, "No pending updates.\n");
+
+                       /* No updates pending, so we are done */
+                       spin_unlock_irqrestore(&fb_data->queue_lock, flags);
+                       return IRQ_HANDLED;
+               } 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->list.next,
+                                      struct update_data_list, list);
+                       list_del_init(&fb_data->cur_update->list);
+               }
+       }
+
+       /* LUTs are available, so we get one here */
+       fb_data->cur_update->lut_num = epdc_get_next_lut();
+
+       /* Associate LUT with update marker */
+       if ((fb_data->cur_update->upd_marker_data)
+               && (fb_data->cur_update->upd_marker_data->update_marker != 0))
+               fb_data->cur_update->upd_marker_data->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_order;
+
+       /* Enable Collision and WB complete IRQs */
+       epdc_working_buf_intr(true);
+       epdc_lut_complete_intr(fb_data->cur_update->lut_num, true);
+
+       /* Program EPDC update to process buffer */
+       next_upd_region = &fb_data->cur_update->upd_data.update_region;
+       if (fb_data->cur_update->upd_data.temp != TEMP_USE_AMBIENT) {
+               temp_index = mxc_epdc_fb_get_temp_index(fb_data, fb_data->cur_update->upd_data.temp);
+               epdc_set_temp(temp_index);
+       } else
+               epdc_set_temp(fb_data->temp_index);
+       epdc_set_update_addr(fb_data->cur_update->phys_addr + fb_data->cur_update->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);
+
+       epdc_submit_update(fb_data->cur_update->lut_num,
+                          fb_data->cur_update->upd_data.waveform_mode,
+                          fb_data->cur_update->upd_data.update_mode, false, 0);
+
+       /* Release buffer queues */
+       spin_unlock_irqrestore(&fb_data->queue_lock, flags);
+
+       return IRQ_HANDLED;
+}
+
+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;
+
+       upd_buf_ptr = (u32 *)fb_data->info.screen_base;
+
+       epdc_working_buf_intr(true);
+       epdc_lut_complete_intr(0, true);
+       fb_data->in_init = true;
+
+       /* 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;
+       }
+
+       /* 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);
+       epdc_submit_update(0, fb_data->wv_modes.mode_init, UPDATE_MODE_FULL, 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");
+                       return;
+               }
+               msleep(100);
+       }
+
+       dev_err(fb_data->dev, "Mode0 init failed!\n");
+
+       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 fb_var_screeninfo *screeninfo = &fb_data->epdc_fb_var;
+       u32 xres, yres;
+
+       if (fw == NULL) {
+               /* If default FW file load failed, we give up */
+               if (fb_data->fw_default_load)
+                       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;
+               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);
+
+               return;
+       }
+
+       wv_file = (struct mxcfb_waveform_data_file *)fw->data;
+
+       /* 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, GFP_KERNEL);
+
+       for (i = 0; i < fb_data->trt_entries; i++)
+               dev_dbg(fb_data->dev, "trt entry #%d = 0x%x\n", i, *((u8 *)&wv_file->data + i));
+
+       /* Copy TRT data */
+       memcpy(fb_data->temp_range_bounds, &wv_file->data, fb_data->trt_entries);
+
+       /* 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;
+
+       /* 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");
+               return;
+       }
+
+       memcpy(fb_data->waveform_buffer_virt, (u8 *)(fw->data) + wv_data_offs,
+               fb_data->waveform_buffer_size);
+
+       release_firmware(fw);
+
+       /* Enable clocks to access EPDC regs */
+       clk_enable(fb_data->epdc_clk_axi);
+
+       /* Enable pix clk for EPDC */
+       clk_enable(fb_data->epdc_clk_pix);
+       clk_set_rate(fb_data->epdc_clk_pix, fb_data->cur_mode->vmode->pixclock);
+
+       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;
+
+       /* 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 = WAVEFORM_MODE_AUTO;
+       update.update_marker = INIT_UPDATE_MARKER;
+       update.temp = TEMP_USE_AMBIENT;
+       update.flags = 0;
+
+       mxc_epdc_fb_send_update(&update, &fb_data->info);
+
+       /* Block on initial update */
+       ret = mxc_epdc_fb_wait_update_complete(update.update_marker,
+               &fb_data->info);
+       if (ret < 0)
+               dev_err(fb_data->dev,
+                       "Wait for update complete failed.  Error = 0x%x", ret);
+}
+
+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;
+
+       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);
+
+       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, max_pix_size, 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;
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE
+       struct mxcfb_update_data update;
+#endif
+
+       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;
+       }
+
+       if (fb_get_options(name, &options)) {
+               ret = -ENODEV;
+               goto out_fbdata;
+       }
+
+       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
+                               panel_str = opt;
+               }
+
+       fb_data->dev = &pdev->dev;
+
+       if (!fb_data->default_bpp)
+               fb_data->default_bpp = 16;
+
+       /* Set default (first defined mode) before searching for a match */
+       fb_data->cur_mode = &fb_data->pdata->epdc_mode[0];
+
+       if (panel_str)
+               for (i = 0; i < fb_data->pdata->num_modes; i++)
+                       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);
+
+       /*
+        * 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);
+       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);
+       max_pix_size = (max_pix_size > pix_size_rot) ?
+                               max_pix_size : pix_size_rot;
+
+       buf_size = 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 */
+       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;
+       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:
+               /*
+                * 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;
+       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_SNAPSHOT;
+
+       /* Initialize our internal copy of the screeninfo */
+       fb_data->epdc_fb_var = *var_info;
+       fb_data->fb_offset = 0;
+
+       /* Allocate head objects for our lists */
+       fb_data->upd_buf_queue =
+           kzalloc(sizeof(struct update_data_list), GFP_KERNEL);
+       fb_data->upd_buf_collision_list =
+           kzalloc(sizeof(struct update_data_list), GFP_KERNEL);
+       fb_data->upd_buf_free_list =
+           kzalloc(sizeof(struct update_data_list), GFP_KERNEL);
+       if ((fb_data->upd_buf_queue == NULL) || (fb_data->upd_buf_free_list == NULL)
+           || (fb_data->upd_buf_collision_list == NULL)) {
+               ret = -ENOMEM;
+               goto out_dma_fb;
+       }
+
+       /*
+        * Initialize lists for update requests, update collisions,
+        * and available update (PxP output) buffers
+        */
+       INIT_LIST_HEAD(&fb_data->upd_buf_queue->list);
+       INIT_LIST_HEAD(&fb_data->upd_buf_free_list->list);
+       INIT_LIST_HEAD(&fb_data->upd_buf_collision_list->list);
+
+       /* Allocate update buffers and add them to the list */
+       for (i = 0; i < EPDC_MAX_NUM_UPDATES; i++) {
+               upd_list = kzalloc(sizeof(*upd_list), GFP_KERNEL);
+               if (upd_list == NULL) {
+                       ret = -ENOMEM;
+                       goto out_upd_buffers;
+               }
+
+               /* Clear update data structure */
+               memset(&upd_list->upd_data, 0,
+                      sizeof(struct mxcfb_update_data));
+
+               /*
+                * Each update buffer is 1 byte per pixel, and can
+                * be as big as the full-screen frame buffer
+                */
+               upd_list->size = max_pix_size;
+
+               /* Allocate memory for PxP output buffer */
+               upd_list->virt_addr =
+                   dma_alloc_coherent(fb_data->info.device, upd_list->size,
+                                      &upd_list->phys_addr, GFP_DMA);
+               if (upd_list->virt_addr == NULL) {
+                       kfree(upd_list);
+                       ret = -ENOMEM;
+                       goto out_upd_buffers;
+               }
+
+               /* Add newly allocated buffer to free list */
+               list_add(&upd_list->list, &fb_data->upd_buf_free_list->list);
+
+               dev_dbg(fb_data->info.device, "allocated %d bytes @ 0x%08X\n",
+                       upd_list->size, upd_list->phys_addr);
+
+               /* Allocate memory for PxP SW workaround buffers */
+               /* These buffers are used to hold copy of the update region */
+               upd_list->virt_addr_copybuf =
+                   dma_alloc_coherent(fb_data->info.device, upd_list->size*2,
+                                      &upd_list->phys_addr_copybuf, GFP_DMA);
+               if (upd_list->virt_addr_copybuf == NULL) {
+                       ret = -ENOMEM;
+                       goto out_upd_buffers;
+               }
+       }
+
+       fb_data->working_buffer_size = vmode->yres * vmode->xres * 2;
+       /* Allocate memory for EPDC working buffer */
+       fb_data->working_buffer_virt =
+           dma_alloc_coherent(&pdev->dev, fb_data->working_buffer_size,
+                              &fb_data->working_buffer_phys, GFP_DMA);
+       if (fb_data->working_buffer_virt == NULL) {
+               dev_err(&pdev->dev, "Can't allocate mem for working buf!\n");
+               ret = -ENOMEM;
+               goto out_upd_buffers;
+       }
+
+       /* Initialize EPDC pins */
+       if (fb_data->pdata->get_pins)
+               fb_data->pdata->get_pins();
+
+       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_upd_buffers;
+       }
+       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_upd_buffers;
+       }
+
+       fb_data->in_init = false;
+
+       fb_data->hw_ready = 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 = 3;
+       fb_data->wv_modes.mode_gc8 = 2;
+       fb_data->wv_modes.mode_gc16 = 2;
+       fb_data->wv_modes.mode_gc32 = 2;
+
+       /* Initialize markers */
+       for (i = 0; i < EPDC_MAX_NUM_UPDATES; i++) {
+               fb_data->update_marker_array[i].update_marker = 0;
+               fb_data->update_marker_array[i].lut_num = INVALID_LUT;
+       }
+
+       /* Initialize all LUTs to inactive */
+       for (i = 0; i < EPDC_NUM_LUTS; i++)
+               fb_data->lut_update_order[i] = 0;
+
+       /* 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;
+       }
+       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;
+       }
+
+       INIT_DELAYED_WORK(&fb_data->epdc_done_work, epdc_done_work_func);
+       fb_data->epdc_submit_workqueue = create_rt_workqueue("submit");
+       INIT_WORK(&fb_data->epdc_submit_work, epdc_submit_work_func);
+
+       info->fbdefio = &mxc_epdc_fb_defio;
+#ifdef CONFIG_FB_MXC_EINK_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 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_irq;
+       }
+
+       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;
+
+       spin_lock_init(&fb_data->queue_lock);
+
+       mutex_init(&fb_data->pxp_mutex);
+
+       mutex_init(&fb_data->power_mutex);
+
+       /* PxP DMA interface */
+       dmaengine_get();
+
+       /*
+        * 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;
+
+       /*
+        * 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_GREY;
+
+       /*
+        * 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_idle = false;
+       fb_data->blank = FB_BLANK_UNBLANK;
+       fb_data->power_state = POWER_STATE_OFF;
+       fb_data->powering_down = false;
+       fb_data->pwrdown_delay = 0;
+
+       /* Register FB */
+       ret = register_framebuffer(info);
+       if (ret) {
+               dev_err(&pdev->dev,
+                       "register_framebuffer failed with error %d\n", ret);
+               goto out_dmaengine;
+       }
+
+       g_fb_data = fb_data;
+
+#ifdef 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
+
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE
+       /* If FB console included, update display to show logo */
+       update.update_region.left = 0;
+       update.update_region.width = info->var.xres;
+       update.update_region.top = 0;
+       update.update_region.height = info->var.yres;
+       update.update_mode = UPDATE_MODE_PARTIAL;
+       update.waveform_mode = WAVEFORM_MODE_AUTO;
+       update.update_marker = INIT_UPDATE_MARKER;
+       update.temp = TEMP_USE_AMBIENT;
+       update.flags = 0;
+
+       mxc_epdc_fb_send_update(&update, info);
+
+       ret = mxc_epdc_fb_wait_update_complete(update.update_marker, info);
+       if (ret < 0)
+               dev_err(fb_data->dev,
+                       "Wait for update complete failed.  Error = 0x%x", ret);
+#endif
+
+       goto out;
+
+out_dmaengine:
+       dmaengine_put();
+out_irq:
+       free_irq(fb_data->epdc_irq, fb_data);
+out_dma_work_buf:
+       dma_free_writecombine(&pdev->dev, fb_data->working_buffer_size,
+               fb_data->working_buffer_virt, fb_data->working_buffer_phys);
+       if (fb_data->pdata->put_pins)
+               fb_data->pdata->put_pins();
+out_upd_buffers:
+       list_for_each_entry_safe(plist, temp_list, &fb_data->upd_buf_free_list->list, list) {
+               list_del(&plist->list);
+               dma_free_writecombine(&pdev->dev, plist->size, plist->virt_addr,
+                                     plist->phys_addr);
+               dma_free_writecombine(&pdev->dev, plist->size*2,
+                                     plist->virt_addr_copybuf,
+                                     plist->phys_addr_copybuf);
+               kfree(plist);
+       }
+out_dma_fb:
+       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:
+       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);
+
+       mxc_epdc_fb_blank(FB_BLANK_POWERDOWN, &fb_data->info);
+
+       flush_workqueue(fb_data->epdc_submit_workqueue);
+       destroy_workqueue(fb_data->epdc_submit_workqueue);
+
+       regulator_put(fb_data->display_regulator);
+       regulator_put(fb_data->vcom_regulator);
+
+       unregister_framebuffer(&fb_data->info);
+       free_irq(fb_data->epdc_irq, fb_data);
+
+       dma_free_writecombine(&pdev->dev, fb_data->working_buffer_size,
+                               fb_data->working_buffer_virt,
+                               fb_data->working_buffer_phys);
+       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);
+       list_for_each_entry_safe(plist, temp_list, &fb_data->upd_buf_free_list->list, list) {
+               list_del(&plist->list);
+               dma_free_writecombine(&pdev->dev, plist->size, plist->virt_addr,
+                                     plist->phys_addr);
+               dma_free_writecombine(&pdev->dev, plist->size*2,
+                                     plist->virt_addr_copybuf,
+                                     plist->phys_addr_copybuf);
+               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);
+
+       dmaengine_put();
+
+       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
+static int mxc_epdc_fb_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct mxc_epdc_fb_data *data = platform_get_drvdata(pdev);
+       int ret;
+
+       ret = mxc_epdc_fb_blank(FB_BLANK_POWERDOWN, &data->info);
+       if (ret)
+               goto out;
+
+out:
+       return ret;
+}
+
+static int mxc_epdc_fb_resume(struct platform_device *pdev)
+{
+       struct mxc_epdc_fb_data *data = platform_get_drvdata(pdev);
+
+       mxc_epdc_fb_blank(FB_BLANK_UNBLANK, &data->info);
+       return 0;
+}
+#else
+#define mxc_epdc_fb_suspend    NULL
+#define mxc_epdc_fb_resume     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,
+       .driver = {
+                  .name = "mxc_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;
+
+       /* This call will signal wait_for_completion_timeout() in send_buffer_to_pxp */
+       complete(&fb_data->pxp_tx_cmpl);
+}
+
+/* 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, NULL, NULL);
+       if (!chan) {
+               dev_err(fb_data->dev, "Unsuccessfully received channel!!!!\n");
+               return -EBUSY;
+       }
+
+       dev_dbg(fb_data->dev, "Successfully received channel.\n");
+
+       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;
+
+       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) {
+               /*
+                * 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);
+
+       dev_dbg(fb_data->dev, "sg[0] = 0x%x, sg[1] = 0x%x\n",
+               sg_dma_address(&sg[0]), sg_dma_address(&sg[1]));
+
+       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->epdc_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;
+
+       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);
+       dev_dbg(fb_data->info.device, "%d: Submit %p #%d\n", __LINE__, txd,
+               cookie);
+       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(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;
+       }
+
+       *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 int __init mxc_epdc_fb_init(void)
+{
+       return platform_driver_register(&mxc_epdc_fb_driver);
+}
+late_initcall(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 (file)
index 0000000..7631445
--- /dev/null
@@ -0,0 +1,2075 @@
+/*
+ * 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 <linux/fsl_devices.h>
+#include <asm/mach-types.h>
+#include <mach/ipu-v3.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 {
+       char *fb_mode_str;
+       int default_bpp;
+       int cur_blank;
+       int next_blank;
+       ipu_channel_t ipu_ch;
+       int ipu_di;
+       u32 ipu_di_pix_fmt;
+       bool ipu_int_clk;
+       bool overlay;
+       bool alpha_chan_en;
+       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_alp_ch_irq;
+       uint32_t cur_ipu_buf;
+       uint32_t cur_ipu_alpha_buf;
+
+       u32 pseudo_palette[16];
+
+       bool wait4vsync;
+       uint32_t waitcnt;
+       struct semaphore flip_sem;
+       struct semaphore alpha_flip_sem;
+       struct completion vsync_complete;
+};
+
+struct mxcfb_mode {
+       int dev_mode;
+       int num_modes;
+       struct fb_videomode *mode;
+};
+
+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;
+LIST_HEAD(fb_alloc_list);
+static struct fb_info *mxcfb_info[3];
+static __initdata struct mxcfb_mode mxc_disp_mode[MXCFB_PORT_NUM];
+static __initdata int (*mxcfb_pre_setup[MXCFB_PORT_NUM])(struct fb_info *info);
+
+/*
+ * register pre-setup callback for some display
+ * driver which need prepare clk etc.
+ */
+void mxcfb_register_presetup(int disp_port,
+       int (*pre_setup)(struct fb_info *info))
+{
+       if (pre_setup)
+               mxcfb_pre_setup[disp_port] = pre_setup;
+}
+EXPORT_SYMBOL(mxcfb_register_presetup);
+
+/*
+ * mode register from each display driver before
+ * primary fb setting.
+ */
+void mxcfb_register_mode(int disp_port,
+       const struct fb_videomode *modedb,
+       int num_modes, int dev_mode)
+{
+       struct fb_videomode *mode;
+       int mode_sum;
+
+       if (disp_port > MXCFB_PORT_NUM)
+               return;
+
+       /*
+        * if there is new DDC device, overwrite old modes.
+        * if there is old DDC device while new device is not DDC,
+        * just keep old DDC modes.
+        */
+       if (dev_mode & MXC_DISP_DDC_DEV) {
+               if (mxc_disp_mode[disp_port].num_modes) {
+                       kfree(mxc_disp_mode[disp_port].mode);
+                       mxc_disp_mode[disp_port].num_modes = 0;
+               }
+       } else if (mxc_disp_mode[disp_port].dev_mode & MXC_DISP_DDC_DEV)
+               return;
+
+       mode_sum = mxc_disp_mode[disp_port].num_modes + num_modes;
+       mode = kzalloc(mode_sum * sizeof(struct fb_videomode), GFP_KERNEL);
+
+       if (mxc_disp_mode[disp_port].num_modes)
+               memcpy(mode, mxc_disp_mode[disp_port].mode,
+                       mxc_disp_mode[disp_port].num_modes
+                       * sizeof(struct fb_videomode));
+       if (modedb)
+               memcpy(mode + mxc_disp_mode[disp_port].num_modes,
+                       modedb, num_modes * sizeof(struct fb_videomode));
+
+       if (mxc_disp_mode[disp_port].num_modes)
+               kfree(mxc_disp_mode[disp_port].mode);
+
+       mxc_disp_mode[disp_port].mode = mode;
+       mxc_disp_mode[disp_port].num_modes += num_modes;
+       mxc_disp_mode[disp_port].dev_mode = dev_mode;
+
+       return;
+}
+EXPORT_SYMBOL(mxcfb_register_mode);
+
+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 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 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);
+static int mxcfb_unmap_video_memory(struct fb_info *fbi);
+static int mxcfb_option_setup(struct fb_info *info, char *options);
+
+/*
+ * 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 = 0;
+       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));
+       params.mem_dp_bg_sync.di = mxc_fbi->ipu_di;
+
+       /*
+        * Assuming interlaced means yuv output, below setting also
+        * valid for mem_dc_sync. FG should have the same vmode as BG.
+        */
+       if (mxc_fbi->ipu_ch == MEM_FG_SYNC) {
+               struct mxcfb_info *mxc_fbi_tmp;
+               int i;
+
+               for (i = 0; i < num_registered_fb; i++) {
+                       mxc_fbi_tmp = (struct mxcfb_info *)
+                               (registered_fb[i]->par);
+                       if (mxc_fbi_tmp->ipu_ch == MEM_BG_SYNC) {
+                               fbi->var.vmode =
+                               registered_fb[i]->var.vmode;
+                               mxc_fbi->ipu_di_pix_fmt =
+                               mxc_fbi_tmp->ipu_di_pix_fmt;
+                               break;
+                       }
+               }
+       }
+       if (mxc_fbi->ipu_ch == MEM_DC_SYNC) {
+               if (fbi->var.vmode & FB_VMODE_INTERLACED) {
+                       params.mem_dc_sync.interlaced = true;
+                       params.mem_dc_sync.out_pixel_fmt =
+                               IPU_PIX_FMT_YUV444;
+               } else {
+                       if (mxc_fbi->ipu_di_pix_fmt)
+                               params.mem_dc_sync.out_pixel_fmt = mxc_fbi->ipu_di_pix_fmt;
+                       else
+                               params.mem_dc_sync.out_pixel_fmt = IPU_PIX_FMT_RGB666;
+               }
+               params.mem_dc_sync.in_pixel_fmt = bpp_to_pixfmt(fbi);
+       } else {
+               if (fbi->var.vmode & FB_VMODE_INTERLACED) {
+                       params.mem_dp_bg_sync.interlaced = true;
+                       params.mem_dp_bg_sync.out_pixel_fmt =
+                               IPU_PIX_FMT_YUV444;
+               } else {
+                       if (mxc_fbi->ipu_di_pix_fmt)
+                               params.mem_dp_bg_sync.out_pixel_fmt = mxc_fbi->ipu_di_pix_fmt;
+                       else
+                               params.mem_dp_bg_sync.out_pixel_fmt = IPU_PIX_FMT_RGB666;
+               }
+               params.mem_dp_bg_sync.in_pixel_fmt = bpp_to_pixfmt(fbi);
+               if (mxc_fbi->alpha_chan_en)
+                       params.mem_dp_bg_sync.alpha_chan_en = true;
+       }
+       ipu_init_channel(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;
+
+       switch (bpp_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:
+               fb_stride = fbi->var.xres_virtual;
+               break;
+       default:
+               fb_stride = fbi->fix.line_length;
+       }
+
+       mxc_fbi->cur_ipu_buf = 1;
+       sema_init(&mxc_fbi->flip_sem, 1);
+       if (mxc_fbi->alpha_chan_en) {
+               mxc_fbi->cur_ipu_alpha_buf = 1;
+               sema_init(&mxc_fbi->alpha_flip_sem, 1);
+       }
+       fbi->var.xoffset = 0;
+
+       base = (fbi->var.yoffset * fbi->var.xres_virtual + fbi->var.xoffset);
+       base = (fbi->var.bits_per_pixel) * base / 8;
+       base += fbi->fix.smem_start;
+
+       retval = ipu_init_channel_buffer(mxc_fbi->ipu_ch, IPU_INPUT_BUFFER,
+                                        bpp_to_pixfmt(fbi),
+                                        fbi->var.xres, fbi->var.yres,
+                                        fb_stride,
+                                        IPU_ROTATE_NONE,
+                                        base,
+                                        base,
+                                        0, 0);
+       if (retval) {
+               dev_err(fbi->device,
+                       "ipu_init_channel_buffer error %d\n", retval);
+       }
+
+       if (mxc_fbi->alpha_chan_en) {
+               retval = ipu_init_channel_buffer(mxc_fbi->ipu_ch,
+                                                IPU_ALPHA_IN_BUFFER,
+                                                IPU_PIX_FMT_GENERIC,
+                                                fbi->var.xres, fbi->var.yres,
+                                                fbi->var.xres,
+                                                IPU_ROTATE_NONE,
+                                                mxc_fbi->alpha_phy_addr1,
+                                                mxc_fbi->alpha_phy_addr0,
+                                                0, 0);
+               if (retval) {
+                       dev_err(fbi->device,
+                               "ipu_init_channel_buffer error %d\n", retval);
+                       return retval;
+               }
+       }
+
+       return retval;
+}
+
+/*
+ * 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;
+
+       dev_dbg(fbi->device, "Reconfiguring framebuffer\n");
+
+       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 (!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->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;
+
+       _setup_disp_channel1(fbi);
+
+       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 = IPU_PIX_FMT_YUV444;
+               } else {
+                       if (mxc_fbi->ipu_di_pix_fmt)
+                               out_pixel_fmt = mxc_fbi->ipu_di_pix_fmt;
+                       else
+                               out_pixel_fmt = IPU_PIX_FMT_RGB666;
+               }
+               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_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_ch, 0, 0);
+       }
+
+       retval = _setup_disp_channel2(fbi);
+       if (retval)
+               return retval;
+
+       ipu_enable_channel(mxc_fbi->ipu_ch);
+
+       return retval;
+}
+
+static int _swap_channels(struct fb_info *fbi,
+                         struct fb_info *fbi_to, bool both_on)
+{
+       int retval, tmp;
+       ipu_channel_t old_ch;
+       struct mxcfb_info *mxc_fbi_from = (struct mxcfb_info *)fbi->par;
+       struct mxcfb_info *mxc_fbi_to = (struct mxcfb_info *)fbi_to->par;
+
+       if (both_on) {
+               ipu_disable_channel(mxc_fbi_to->ipu_ch, true);
+               ipu_uninit_channel(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;
+
+       _setup_disp_channel1(fbi);
+       retval = _setup_disp_channel2(fbi);
+       if (retval)
+               return retval;
+
+       /* switch between dp and dc, disable old idmac, enable new idmac */
+       retval = ipu_swap_channel(old_ch, mxc_fbi_from->ipu_ch);
+       ipu_uninit_channel(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_ch);
+       }
+
+       return retval;
+}
+
+static int swap_channels(struct fb_info *fbi)
+{
+       int i;
+       int swap_mode;
+       ipu_channel_t ch_to;
+       struct mxcfb_info *mxc_fbi_from = (struct mxcfb_info *)fbi->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;
+
+       for (i = 0; i < num_registered_fb; i++) {
+               mxc_fbi_to =
+                       (struct mxcfb_info *)mxcfb_info[i]->par;
+               if (mxc_fbi_to->ipu_ch == ch_to) {
+                       fbi_to = mxcfb_info[i];
+                       break;
+               }
+       }
+       if (fbi_to == NULL)
+               return -1;
+
+       ipu_clear_irq(mxc_fbi_from->ipu_ch_irq);
+       ipu_clear_irq(mxc_fbi_to->ipu_ch_irq);
+       ipu_free_irq(mxc_fbi_from->ipu_ch_irq, fbi);
+       ipu_free_irq(mxc_fbi_to->ipu_ch_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;
+       }
+
+       /* tvout di-1: for DC use UYVY, for DP use RGB */
+       if (mxc_fbi_from->ipu_di == 1 && ch_to == MEM_DC_SYNC) {
+               fbi->var.bits_per_pixel = 16;
+               fbi->var.nonstd = IPU_PIX_FMT_UYVY;
+       } else if (mxc_fbi_from->ipu_di == 1 && ch_to == MEM_BG_SYNC) {
+               fbi->var.nonstd = 0;
+       } else if (mxc_fbi_from->ipu_di == 0 && ch_to == MEM_DC_SYNC) {
+               fbi_to->var.nonstd = 0;
+       } else if (mxc_fbi_from->ipu_di == 0 && ch_to == MEM_BG_SYNC) {
+               fbi->var.bits_per_pixel = 16;
+               fbi->var.nonstd = IPU_PIX_FMT_UYVY;
+       }
+
+       switch (swap_mode) {
+       case BOTH_ON:
+               /* disable target->switch src->enable target */
+               _swap_channels(fbi, fbi_to, true);
+               break;
+       case SRC_ON:
+               /* just switch src */
+               _swap_channels(fbi, fbi_to, false);
+               break;
+       case TGT_ON:
+               /* just switch target */
+               _swap_channels(fbi_to, fbi, 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;
+               break;
+       default:
+               break;
+       }
+
+       if (ipu_request_irq(mxc_fbi_from->ipu_ch_irq, mxcfb_irq_handler, 0,
+               MXCFB_NAME, fbi) != 0) {
+               dev_err(fbi->device, "Error registering irq %d\n",
+                       mxc_fbi_from->ipu_ch_irq);
+               return -EBUSY;
+       }
+       ipu_disable_irq(mxc_fbi_from->ipu_ch_irq);
+       if (ipu_request_irq(mxc_fbi_to->ipu_ch_irq, mxcfb_irq_handler, 0,
+               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_ch_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;
+
+       /* fg should not bigger than bg */
+       if (mxc_fbi->ipu_ch == MEM_FG_SYNC) {
+               struct fb_info *fbi_tmp;
+               struct mxcfb_info *mxc_fbi_tmp;
+               int i, bg_xres, bg_yres;
+               int16_t pos_x, pos_y;
+
+               bg_xres = var->xres;
+               bg_yres = var->yres;
+
+               for (i = 0; i < num_registered_fb; i++) {
+                       fbi_tmp = registered_fb[i];
+                       mxc_fbi_tmp = (struct mxcfb_info *)
+                               (fbi_tmp->par);
+                       if (mxc_fbi_tmp->ipu_ch == MEM_BG_SYNC) {
+                               bg_xres = fbi_tmp->var.xres;
+                               bg_yres = fbi_tmp->var.yres;
+                               break;
+                       }
+               }
+
+               ipu_disp_get_window_pos(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->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 != 12) &&
+           (var->bits_per_pixel != 8))
+               var->bits_per_pixel = 16;
+
+       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;
+       }
+
+       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;
+}
+
+/*
+ * 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_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;
+                       int i;
+                       char *video_plane_idstr = "";
+
+                       if (copy_from_user(&la, (void *)arg, sizeof(la))) {
+                               retval = -EFAULT;
+                               break;
+                       }
+
+                       if (ipu_disp_set_global_alpha(mxc_fbi->ipu_ch,
+                                                     !(bool)la.enable, 0)) {
+                               retval = -EINVAL;
+                               break;
+                       }
+
+                       if (la.enable && !la.alpha_in_pixel) {
+                               mxc_fbi->alpha_chan_en = true;
+
+                               if (mxc_fbi->ipu_ch == MEM_FG_SYNC)
+                                       video_plane_idstr = "DISP3 BG";
+                               else if (mxc_fbi->ipu_ch == MEM_BG_SYNC)
+                                       video_plane_idstr = "DISP3 FG";
+
+                               for (i = 0; i < num_registered_fb; i++) {
+                                       char *idstr = registered_fb[i]->fix.id;
+                                       if (strcmp(idstr, video_plane_idstr) == 0) {
+                                               ((struct mxcfb_info *)(registered_fb[i]->par))->alpha_chan_en = false;
+                                               break;
+                                       }
+                               }
+                       } else
+                               mxc_fbi->alpha_chan_en = false;
+
+                       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;
+
+                       down(&mxc_fbi->alpha_flip_sem);
+
+                       mxc_fbi->cur_ipu_alpha_buf =
+                                               !mxc_fbi->cur_ipu_alpha_buf;
+                       if (ipu_update_channel_buffer(mxc_fbi->ipu_ch,
+                                                     IPU_ALPHA_IN_BUFFER,
+                                                     mxc_fbi->
+                                                       cur_ipu_alpha_buf,
+                                                     base) == 0) {
+                               ipu_select_buffer(mxc_fbi->ipu_ch,
+                                                 IPU_ALPHA_IN_BUFFER,
+                                                 mxc_fbi->cur_ipu_alpha_buf);
+                               ipu_clear_irq(ipu_alp_ch_irq);
+                               ipu_enable_irq(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_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_ch,
+                                                       gamma.enable,
+                                                       gamma.constk,
+                                                       gamma.slopek);
+                       break;
+               }
+       case MXCFB_WAIT_FOR_VSYNC:
+               {
+                       if (mxc_fbi->ipu_ch == MEM_FG_SYNC) {
+                               struct mxcfb_info *bg_mxcfbi = NULL;
+                               int i;
+                               for (i = 0; i < num_registered_fb; i++) {
+                                       bg_mxcfbi =
+                                       ((struct mxcfb_info *)(registered_fb[i]->par));
+
+                                       if (bg_mxcfbi->ipu_ch == MEM_BG_SYNC)
+                                               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_ch_irq);
+                       mxc_fbi->wait4vsync = 1;
+                       ipu_enable_irq(mxc_fbi->ipu_ch_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);
+                               mxc_fbi->wait4vsync = 0;
+                               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_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;
+                       struct fb_info *bg_fbi = NULL;
+                       struct mxcfb_info *bg_mxcfbi = NULL;
+                       int i;
+
+                       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;
+                       }
+
+                       for (i = 0; i < num_registered_fb; i++) {
+                               bg_mxcfbi =
+                               ((struct mxcfb_info *)(registered_fb[i]->par));
+
+                               if (bg_mxcfbi->ipu_ch == MEM_BG_SYNC) {
+                                       bg_fbi = registered_fb[i];
+                                       break;
+                               }
+                       }
+
+                       if (bg_fbi == NULL) {
+                               dev_err(fbi->device, "Cannot find the "
+                                       "background framebuffer\n");
+                               retval = -ENOENT;
+                               break;
+                       }
+
+                       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_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;
+               }
+       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;
+
+       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:
+               ipu_disable_channel(mxc_fbi->ipu_ch, true);
+               ipu_uninit_channel(mxc_fbi->ipu_ch);
+               break;
+       case FB_BLANK_UNBLANK:
+               mxcfb_set_par(info);
+               break;
+       }
+       mxc_fbi->cur_blank = blank;
+       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,
+                         *mxc_graphic_fbi = NULL;
+       u_int y_bottom;
+       unsigned long base, active_alpha_phy_addr = 0;
+       bool loc_alpha_en = false;
+       int i = 0;
+
+       if (info->var.yoffset == var->yoffset)
+               return 0;       /* No change, do nothing */
+
+       /* no pan display during fb blank */
+       if (mxc_fbi->ipu_ch == MEM_FG_SYNC) {
+               struct mxcfb_info *bg_mxcfbi = NULL;
+               int j;
+               for (j = 0; j < num_registered_fb; j++) {
+                       bg_mxcfbi =
+                               ((struct mxcfb_info *)(registered_fb[j]->par));
+
+                       if (bg_mxcfbi->ipu_ch == MEM_BG_SYNC)
+                               break;
+               }
+               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 (!(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) * base / 8;
+       base += info->fix.smem_start;
+
+       /* 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 *idstr = registered_fb[i]->fix.id;
+                       if ((strcmp(idstr, "DISP3 BG") == 0 ||
+                            strcmp(idstr, "DISP3 FG") == 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_buf ?
+                                       mxc_graphic_fbi->alpha_phy_addr1 :
+                                       mxc_graphic_fbi->alpha_phy_addr0;
+                               dev_dbg(info->device, "Updating SDC graphic "
+                                       "buf %d address=0x%08lX\n",
+                                       mxc_fbi->cur_ipu_buf,
+                                       active_alpha_phy_addr);
+                               break;
+                       }
+               }
+       }
+
+       down(&mxc_fbi->flip_sem);
+
+       mxc_fbi->cur_ipu_buf = !mxc_fbi->cur_ipu_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_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_ch,
+                                             IPU_ALPHA_IN_BUFFER,
+                                             mxc_fbi->cur_ipu_buf,
+                                             active_alpha_phy_addr) == 0) {
+                       ipu_select_buffer(mxc_graphic_fbi->ipu_ch,
+                                         IPU_ALPHA_IN_BUFFER,
+                                         mxc_fbi->cur_ipu_buf);
+               }
+
+               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);
+               mxc_fbi->cur_ipu_buf = !mxc_fbi->cur_ipu_buf;
+               ipu_clear_irq(mxc_fbi->ipu_ch_irq);
+               ipu_enable_irq(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;
+
+       if (mxc_fbi->wait4vsync) {
+               complete(&mxc_fbi->vsync_complete);
+               ipu_disable_irq(irq);
+               mxc_fbi->wait4vsync = 0;
+       } else {
+               if (!ipu_check_buffer_ready(mxc_fbi->ipu_ch,
+                               IPU_INPUT_BUFFER, mxc_fbi->cur_ipu_buf)
+                               || (mxc_fbi->waitcnt > 1)) {
+                       /*
+                        * This code wait for EOF irq to make sure current
+                        * buffer showed.
+                        *
+                        * Buffer ready will be clear after this buffer
+                        * begin to show. If it keep 1, it represents this
+                        * irq come from previous buffer. If so, wait for
+                        * EOF irq again.
+                        *
+                        * Normally, waitcnt will not > 1, if so, something
+                        * is wrong, then clear it manually.
+                        */
+                       if (mxc_fbi->waitcnt > 1)
+                               ipu_clear_buffer_ready(mxc_fbi->ipu_ch,
+                                               IPU_INPUT_BUFFER,
+                                               mxc_fbi->cur_ipu_buf);
+                       up(&mxc_fbi->flip_sem);
+                       ipu_disable_irq(irq);
+                       mxc_fbi->waitcnt = 0;
+               } else
+                       mxc_fbi->waitcnt++;
+       }
+       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;
+
+       up(&mxc_fbi->alpha_flip_sem);
+       ipu_disable_irq(irq);
+       return IRQ_HANDLED;
+}
+
+/*
+ * 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;
+       int saved_blank;
+#ifdef CONFIG_FB_MXC_LOW_PWR_DISPLAY
+       void *fbmem;
+#endif
+
+       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;
+}
+
+/*
+ * 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;
+
+       console_lock();
+       mxcfb_blank(mxc_fbi->next_blank, fbi);
+       fb_set_suspend(fbi, 0);
+       console_unlock();
+
+       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);
+       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))) {
+               int i;
+
+               for (i = 0; i < num_registered_fb; i++) {
+                       fg_mxcfbi =
+                               (struct mxcfb_info *)mxcfb_info[i]->par;
+                       if (fg_mxcfbi->ipu_ch == MEM_FG_SYNC)
+                               break;
+                       else
+                               fg_mxcfbi = NULL;
+               }
+               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;
+}
+DEVICE_ATTR(fsl_disp_property, 644, show_disp_chan, swap_disp_chan);
+
+static int mxcfb_setup(struct fb_info *fbi, struct platform_device *pdev)
+{
+       struct mxcfb_info *mxcfbi = (struct mxcfb_info *)fbi->par;
+       struct ipuv3_fb_platform_data *plat_data = pdev->dev.platform_data;
+       int ret = 0;
+
+       /* Need dummy values until real panel is configured */
+       fbi->var.xres = 240;
+       fbi->var.yres = 320;
+
+       if (!mxcfbi->default_bpp)
+               mxcfbi->default_bpp = 16;
+
+       if (plat_data && !mxcfbi->ipu_di_pix_fmt)
+               mxcfbi->ipu_di_pix_fmt = plat_data->interface_pix_fmt;
+
+       if (!mxcfbi->fb_mode_str && plat_data && plat_data->mode_str)
+               mxcfbi->fb_mode_str = plat_data->mode_str;
+
+       if (mxcfbi->fb_mode_str) {
+               if (mxcfbi->ipu_di >= 0) {
+                       const struct fb_videomode *mode = NULL;
+                       struct fb_videomode m;
+                       int num = 0, found = 0;
+
+                       dev_dbg(fbi->device, "Config display port %d\n",
+                                       mxcfbi->ipu_di);
+
+                       INIT_LIST_HEAD(&fbi->modelist);
+
+                       if (mxc_disp_mode[mxcfbi->ipu_di].num_modes) {
+                               mode = mxc_disp_mode[mxcfbi->ipu_di].mode;
+                               num = mxc_disp_mode[mxcfbi->ipu_di].num_modes;
+                               fb_videomode_to_modelist(mode, num, &fbi->modelist);
+                       }
+
+                       if ((mxc_disp_mode[mxcfbi->ipu_di].dev_mode
+                               & MXC_DISP_DDC_DEV) &&
+                               !list_empty(&fbi->modelist)) {
+                               dev_dbg(fbi->device,
+                                       "Look for video mode %s in ddc modelist\n",
+                                       mxcfbi->fb_mode_str);
+                               /*
+                                * For DDC mode, try to get compatible mode first.
+                                * If get one, try to find nearest mode, otherwise,
+                                * use first mode provide by DDC.
+                                */
+                               ret = fb_find_mode(&fbi->var, fbi,
+                                               mxcfbi->fb_mode_str, NULL, 0,
+                                               NULL, mxcfbi->default_bpp);
+                               if (ret) {
+                                       fb_var_to_videomode(&m, &fbi->var);
+                                       mode = fb_find_nearest_mode(&m,
+                                               &fbi->modelist);
+                                       fb_videomode_to_var(&fbi->var, mode);
+                               } else {
+                                       struct list_head *pos, *head;
+                                       struct fb_modelist *modelist;
+
+                                       head = &fbi->modelist;
+                                       list_for_each(pos, head) {
+                                               modelist = list_entry(pos,
+                                                       struct fb_modelist, list);
+                                               m = modelist->mode;
+                                               if (m.flag & FB_MODE_IS_FIRST)
+                                                       break;
+                                       }
+                                       /* if no first mode, use last one */
+                                       mode = &m;
+                                       fb_videomode_to_var(&fbi->var, mode);
+                               }
+                               found = 1;
+                       } else if (!list_empty(&fbi->modelist)) {
+                               dev_dbg(fbi->device,
+                                       "Look for video mode %s in spec modelist\n",
+                                       mxcfbi->fb_mode_str);
+                               /*
+                                * For specific mode, try to get specified mode
+                                * from fbi modelist.
+                                */
+                               ret = fb_find_mode(&fbi->var, fbi,
+                                               mxcfbi->fb_mode_str, mode, num,
+                                               NULL, mxcfbi->default_bpp);
+                               if (ret == 1)
+                                       found = 1;
+
+                       }
+                       /*
+                        * if no DDC mode and spec mode found,
+                        * try plat_data mode.
+                        */
+                       if (!found) {
+                               dev_dbg(fbi->device,
+                                       "Look for video mode %s in plat modelist\n",
+                                       mxcfbi->fb_mode_str);
+                               if (plat_data && plat_data->modes
+                                       && plat_data->num_modes)
+                                       ret = fb_find_mode(&fbi->var, fbi,
+                                               mxcfbi->fb_mode_str,
+                                               plat_data->modes,
+                                               plat_data->num_modes,
+                                               NULL,
+                                               mxcfbi->default_bpp);
+                               else
+                                       ret = fb_find_mode(&fbi->var, fbi,
+                                               mxcfbi->fb_mode_str, NULL, 0,
+                                               NULL, mxcfbi->default_bpp);
+                               if (ret)
+                                       found = 1;
+                       }
+
+                       if (!found) {
+                               dev_err(fbi->device,
+                                       "Not found any valid video mode");
+                               ret = -EINVAL;
+                               goto done;
+                       }
+
+                       /*added found mode to fbi modelist*/
+                       fb_var_to_videomode(&m, &fbi->var);
+                       fb_add_videomode(&m, &fbi->modelist);
+               }
+       }
+
+       mxcfb_check_var(&fbi->var, fbi);
+
+       /* Default Y virtual size is 3x panel size */
+       fbi->var.yres_virtual = fbi->var.yres * 3;
+
+       mxcfb_set_fix(fbi);
+
+       /* setup display */
+       if (mxcfbi->ipu_di >= 0)
+               if (mxcfb_pre_setup[mxcfbi->ipu_di])
+                       (mxcfb_pre_setup[mxcfbi->ipu_di])(fbi);
+
+       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();
+done:
+       return ret;
+}
+
+/*!
+ * 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 *mxcfbi;
+       struct resource *res;
+       char *options;
+       char name[] = "mxcdi0fb";
+       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;
+
+       name[5] += pdev->id;
+       if (fb_get_options(name, &options)) {
+               ret = -ENODEV;
+               goto err1;
+       }
+
+       if (options)
+               mxcfb_option_setup(fbi, options);
+
+       if (!g_dp_in_use) {
+               mxcfbi->ipu_ch_irq = IPU_IRQ_BG_SYNC_EOF;
+               mxcfbi->ipu_ch = MEM_BG_SYNC;
+               mxcfbi->cur_blank = mxcfbi->next_blank = FB_BLANK_UNBLANK;
+       } else {
+               mxcfbi->ipu_ch_irq = IPU_IRQ_DC_SYNC_EOF;
+               mxcfbi->ipu_ch = MEM_DC_SYNC;
+               mxcfbi->cur_blank = mxcfbi->next_blank = FB_BLANK_POWERDOWN;
+       }
+
+       mxcfbi->ipu_di = pdev->id;
+       mxcfbi->ipu_alp_ch_irq = -1;
+
+       if (pdev->id == 0) {
+               ipu_disp_set_global_alpha(mxcfbi->ipu_ch, true, 0x80);
+               ipu_disp_set_color_key(mxcfbi->ipu_ch, false, 0);
+               strcpy(fbi->fix.id, "DISP3 BG");
+
+               if (!g_dp_in_use)
+                       mxcfbi->ipu_alp_ch_irq = IPU_IRQ_BG_ALPHA_SYNC_EOF;
+               g_dp_in_use = true;
+       } else if (pdev->id == 1) {
+               strcpy(fbi->fix.id, "DISP3 BG - DI1");
+
+               if (!g_dp_in_use)
+                       mxcfbi->ipu_alp_ch_irq = IPU_IRQ_BG_ALPHA_SYNC_EOF;
+               g_dp_in_use = true;
+       } else if (pdev->id == 2) {     /* Overlay */
+               mxcfbi->ipu_ch_irq = IPU_IRQ_FG_SYNC_EOF;
+               mxcfbi->ipu_alp_ch_irq = IPU_IRQ_FG_ALPHA_SYNC_EOF;
+               mxcfbi->ipu_ch = MEM_FG_SYNC;
+               mxcfbi->ipu_di = -1;
+               mxcfbi->overlay = true;
+               mxcfbi->cur_blank = mxcfbi->next_blank = FB_BLANK_POWERDOWN;
+
+               strcpy(fbi->fix.id, "DISP3 FG");
+       }
+
+       mxcfb_info[pdev->id] = fbi;
+
+       if (ipu_request_irq(mxcfbi->ipu_ch_irq, 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(mxcfbi->ipu_ch_irq);
+
+       if (mxcfbi->ipu_alp_ch_irq != -1)
+               if (ipu_request_irq(mxcfbi->ipu_alp_ch_irq,
+                                       mxcfb_alpha_irq_handler, 0,
+                                       MXCFB_NAME, fbi) != 0) {
+                       dev_err(&pdev->dev, "Error registering alpha irq "
+                                       "handler.\n");
+                       ret = -EBUSY;
+                       goto err2;
+               }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (res && 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);
+       }
+
+       ret =  mxcfb_setup(fbi, pdev);
+       if (ret < 0)
+               goto err3;
+
+       ret = register_framebuffer(fbi);
+       if (ret < 0)
+               goto err3;
+
+       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\n", ret);
+
+#ifdef CONFIG_LOGO
+       fb_prepare_logo(fbi, 0);
+       fb_show_logo(fbi, 0);
+#endif
+
+       return 0;
+err3:
+       if (mxcfbi->ipu_alp_ch_irq != -1)
+               ipu_free_irq(mxcfbi->ipu_alp_ch_irq, fbi);
+err2:
+       ipu_free_irq(mxcfbi->ipu_ch_irq, fbi);
+err1:
+       fb_dealloc_cmap(&fbi->cmap);
+       framebuffer_release(fbi);
+err0:
+       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;
+
+       mxcfb_blank(FB_BLANK_POWERDOWN, fbi);
+       ipu_free_irq(mxc_fbi->ipu_ch_irq, fbi);
+       mxcfb_unmap_video_memory(fbi);
+
+       if (&fbi->cmap)
+               fb_dealloc_cmap(&fbi->cmap);
+
+       unregister_framebuffer(fbi);
+       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,
+};
+
+/*
+ * Parse user specified options (`video=trident:')
+ * example:
+ *     video=mxcdi0fb:RGB24, 1024x768M-16@60,bpp=16,noaccel
+ */
+static int mxcfb_option_setup(struct fb_info *info, char *options)
+{
+       struct mxcfb_info *mxcfbi = info->par;
+       char *opt;
+
+       if (!options || !*options)
+               return 0;
+
+       while ((opt = strsep(&options, ",")) != NULL) {
+               if (!*opt)
+                       continue;
+
+               if (!strncmp(opt, "RGB24", 5)) {
+                       mxcfbi->ipu_di_pix_fmt = IPU_PIX_FMT_RGB24;
+                       continue;
+               }
+               if (!strncmp(opt, "BGR24", 5)) {
+                       mxcfbi->ipu_di_pix_fmt = IPU_PIX_FMT_BGR24;
+                       continue;
+               }
+               if (!strncmp(opt, "GBR24", 5)) {
+                       mxcfbi->ipu_di_pix_fmt = IPU_PIX_FMT_GBR24;
+                       continue;
+               }
+               if (!strncmp(opt, "RGB565", 6)) {
+                       mxcfbi->ipu_di_pix_fmt = IPU_PIX_FMT_RGB565;
+                       continue;
+               }
+               if (!strncmp(opt, "RGB666", 6)) {
+                       mxcfbi->ipu_di_pix_fmt = IPU_PIX_FMT_RGB666;
+                       continue;
+               }
+               if (!strncmp(opt, "YUV444", 6)) {
+                       mxcfbi->ipu_di_pix_fmt = IPU_PIX_FMT_YUV444;
+                       continue;
+               }
+               if (!strncmp(opt, "LVDS666", 7)) {
+                       mxcfbi->ipu_di_pix_fmt = IPU_PIX_FMT_LVDS666;
+                       continue;
+               }
+               if (!strncmp(opt, "YUYV16", 6)) {
+                       mxcfbi->ipu_di_pix_fmt = IPU_PIX_FMT_YUYV;
+                       continue;
+               }
+               if (!strncmp(opt, "UYVY16", 6)) {
+                       mxcfbi->ipu_di_pix_fmt = IPU_PIX_FMT_UYVY;
+                       continue;
+               }
+               if (!strncmp(opt, "YVYU16", 6)) {
+                       mxcfbi->ipu_di_pix_fmt = IPU_PIX_FMT_YVYU;
+                       continue;
+               }
+               if (!strncmp(opt, "VYUY16", 6)) {
+                       mxcfbi->ipu_di_pix_fmt = IPU_PIX_FMT_VYUY;
+                       continue;
+               }
+               if (!strncmp(opt, "int_clk", 7)) {
+                       mxcfbi->ipu_int_clk = true;
+                       continue;
+               }
+               if (!strncmp(opt, "bpp=", 4))
+                       mxcfbi->default_bpp =
+                               simple_strtoul(opt + 4, NULL, 0);
+               else
+                       mxcfbi->fb_mode_str = 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)
+{
+       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/mxcfb.c b/drivers/video/mxc/mxcfb.c
new file mode 100644 (file)
index 0000000..52694e4
--- /dev/null
@@ -0,0 +1,1372 @@
+/*
+ * 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);
+       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 (file)
index 0000000..6668b88
--- /dev/null
@@ -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 (file)
index 0000000..3dbad0d
--- /dev/null
@@ -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 (file)
index 0000000..f7d191f
--- /dev/null
@@ -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 (file)
index 0000000..d7cf7d3
--- /dev/null
@@ -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_modedb.c b/drivers/video/mxc/mxcfb_modedb.c
new file mode 100644 (file)
index 0000000..acaaef2
--- /dev/null
@@ -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 (file)
index 0000000..0ba4d9e
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ * 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 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_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 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 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 (file)
index 0000000..67f20c6
--- /dev/null
@@ -0,0 +1,479 @@
+/*
+ * 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.
+ */
+
+/*!
+ * @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/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 IPU_DISP_PORT 0
+#define SII_EDID_LEN   256
+static bool g_enable_hdmi;
+
+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];
+} sii902x;
+
+static void sii902x_poweron(void);
+static void sii902x_poweroff(void);
+static void (*sii902x_reset) (void);
+
+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_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;
+
+       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 };
+
+       dat = i2c_smbus_read_byte_data(sii902x.client, 0x3D);
+       if (dat & 0x1) {
+               /* cable connection changes */
+               if (dat & 0x4) {
+                       sii902x.cable_plugin = 1;
+                       sprintf(event_string, "EVENT=plugin");
+
+                       /* make sure fb is powerdown */
+                       console_lock();
+                       fb_blank(sii902x.fbi, FB_BLANK_POWERDOWN);
+                       console_unlock();
+
+                       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++)
+                                               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.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();
+                               }
+
+                               console_lock();
+                               fb_blank(sii902x.fbi, FB_BLANK_UNBLANK);
+                               console_unlock();
+                       }
+               } else {
+                       sii902x.cable_plugin = 0;
+                       sprintf(event_string, "EVENT=plugout");
+                       console_lock();
+                       fb_blank(sii902x.fbi, FB_BLANK_POWERDOWN);
+                       console_unlock();
+               }
+               kobject_uevent_env(&sii902x.pdev->dev.kobj, KOBJ_CHANGE, envp);
+       }
+       i2c_smbus_write_byte_data(sii902x.client, 0x3D, dat);
+}
+
+static irqreturn_t sii902x_detect_handler(int irq, void *data)
+{
+       if (sii902x.fbi)
+               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;
+
+       /* assume sii902x on DI0 only */
+       if ((IPU_DISP_PORT)) {
+               if (strcmp(event->info->fix.id, "DISP3 BG - DI1"))
+                       return 0;
+       } else {
+               if (strcmp(event->info->fix.id, "DISP3 BG"))
+                       return 0;
+       }
+
+       switch (val) {
+       case FB_EVENT_FB_REGISTERED:
+               if (sii902x.fbi != NULL)
+                       break;
+               sii902x.fbi = fbi;
+               break;
+       case FB_EVENT_MODE_CHANGE:
+               sii902x_setup(fbi);
+               break;
+       case FB_EVENT_BLANK:
+               if (*((int *)event->data) == FB_BLANK_UNBLANK)
+                       sii902x_poweron();
+               else
+                       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;
+
+       if (g_enable_hdmi == false)
+               return -EPERM;
+
+       sii902x.client = client;
+
+       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 */
+       if (sii902x_read_edid(&edid_fbi) < 0)
+               dev_warn(&sii902x.client->dev, "Can not read edid\n");
+#if defined(CONFIG_MXC_IPU_V3) && defined(CONFIG_FB_MXC_SYNC_PANEL)
+       else
+               mxcfb_register_mode(IPU_DISP_PORT, edid_fbi.monspecs.modedb,
+                               edid_fbi.monspecs.modedb_len, MXC_DISP_DDC_DEV);
+#endif
+
+       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_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);
+
+       return 0;
+}
+
+static int __devexit sii902x_remove(struct i2c_client *client)
+{
+       fb_unregister_client(&nb);
+       sii902x_poweroff();
+       return 0;
+}
+
+static int sii902x_suspend(struct i2c_client *client, pm_message_t message)
+{
+       /*TODO*/
+       return 0;
+}
+
+static int sii902x_resume(struct i2c_client *client)
+{
+       /*TODO*/
+       return 0;
+}
+
+static void sii902x_poweron(void)
+{
+       /* 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)
+{
+       /* 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);
+
+       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,
+       .suspend = sii902x_suspend,
+       .resume = sii902x_resume,
+       .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);
+}
+
+static int __init enable_hdmi_setup(char *options)
+{
+       g_enable_hdmi = true;
+
+       return 1;
+}
+__setup("hdmi", enable_hdmi_setup);
+
+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/tve.c b/drivers/video/mxc/tve.c
new file mode 100644 (file)
index 0000000..3470bd4
--- /dev/null
@@ -0,0 +1,1288 @@
+/*
+ * 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>
+
+#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 IPU_DISP_PORT 1
+
+static int enabled;            /* enable power on or not */
+DEFINE_SPINLOCK(tve_lock);
+
+static struct fb_info *tve_fbi;
+static bool g_enable_tve;
+static bool g_enable_vga;
+
+struct tve_data {
+       struct platform_device *pdev;
+       int revision;
+       int cur_mode;
+       int output_mode;
+       int detect;
+       void *base;
+       int irq;
+       int blank;
+       struct clk *clk;
+       struct clk *di_clk;
+       struct regulator *dac_reg;
+       struct regulator *dig_reg;
+       struct delayed_work cd_work;
+} tve;
+
+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
+};
+
+
+struct tve_reg_mapping *tve_regs;
+struct tve_reg_fields_mapping *tve_reg_fields;
+
+static struct fb_videomode video_modes[] = {
+       {
+        /* 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 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,},
+};
+
+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(void)
+{
+       dev_dbg(&tve.pdev->dev, "tve_com_conf_reg 0x%x\n",
+                       __raw_readl(tve.base + tve_regs->tve_com_conf_reg));
+       dev_dbg(&tve.pdev->dev, "tve_cd_cont_reg 0x%x\n",
+                       __raw_readl(tve.base + tve_regs->tve_cd_cont_reg));
+       dev_dbg(&tve.pdev->dev, "tve_int_cont_reg 0x%x\n",
+                       __raw_readl(tve.base + tve_regs->tve_int_cont_reg));
+       dev_dbg(&tve.pdev->dev, "tve_tst_mode_reg 0x%x\n",
+                       __raw_readl(tve.base + tve_regs->tve_tst_mode_reg));
+       dev_dbg(&tve.pdev->dev, "tve_tvdac_cont_reg0 0x%x\n",
+                       __raw_readl(tve.base + tve_regs->tve_tvdac_cont_reg));
+       dev_dbg(&tve.pdev->dev, "tve_tvdac_cont_reg1 0x%x\n",
+                       __raw_readl(tve.base + tve_regs->tve_tvdac_cont_reg + 4));
+       dev_dbg(&tve.pdev->dev, "tve_tvdac_cont_reg2 0x%x\n",
+                       __raw_readl(tve.base + tve_regs->tve_tvdac_cont_reg + 8));
+}
+
+static int is_vga_enabled(void)
+{
+       u32 reg;
+
+       if (tve.revision == 2) {
+               reg = __raw_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 *fmt)
+{
+       int mode;
+
+       if (fb_mode_is_equal(fbi->mode, &video_modes[0])) {
+               *fmt = IPU_PIX_FMT_YUV444;
+               mode = TVOUT_FMT_NTSC;
+       } else if (fb_mode_is_equal(fbi->mode, &video_modes[1])) {
+               *fmt = IPU_PIX_FMT_YUV444;
+               mode = TVOUT_FMT_PAL;
+       } else if (fb_mode_is_equal(fbi->mode, &video_modes[2])) {
+               *fmt = IPU_PIX_FMT_YUV444;
+               mode = TVOUT_FMT_720P60;
+       } else if (fb_mode_is_equal(fbi->mode, &video_modes[3])) {
+               *fmt = IPU_PIX_FMT_YUV444;
+               mode = TVOUT_FMT_720P30;
+       } else if (fb_mode_is_equal(fbi->mode, &video_modes[4])) {
+               *fmt = IPU_PIX_FMT_YUV444;
+               mode = TVOUT_FMT_1080I60;
+       } else if (fb_mode_is_equal(fbi->mode, &video_modes[5])) {
+               *fmt = IPU_PIX_FMT_YUV444;
+               mode = TVOUT_FMT_1080I50;
+       } else if (fb_mode_is_equal(fbi->mode, &video_modes[6])) {
+               *fmt = IPU_PIX_FMT_YUV444;
+               mode = TVOUT_FMT_1080P30;
+       } else if (fb_mode_is_equal(fbi->mode, &video_modes[7])) {
+               *fmt = IPU_PIX_FMT_YUV444;
+               mode = TVOUT_FMT_1080P25;
+       } else if (fb_mode_is_equal(fbi->mode, &video_modes[8])) {
+               *fmt = IPU_PIX_FMT_YUV444;
+               mode = TVOUT_FMT_1080P24;
+       } else if (fb_mode_is_equal(fbi->mode, &video_modes_vga[0])) {
+               *fmt = IPU_PIX_FMT_GBR24;
+               mode = TVOUT_FMT_VGA_SVGA;
+       } else if (fb_mode_is_equal(fbi->mode, &video_modes_vga[1])) {
+               *fmt = IPU_PIX_FMT_GBR24;
+               mode = TVOUT_FMT_VGA_XGA;
+       } else if (fb_mode_is_equal(fbi->mode, &video_modes_vga[2])) {
+               *fmt = IPU_PIX_FMT_GBR24;
+               mode = TVOUT_FMT_VGA_SXGA;
+       } else if (fb_mode_is_equal(fbi->mode, &video_modes_vga[3])) {
+               *fmt = IPU_PIX_FMT_GBR24;
+               mode = TVOUT_FMT_VGA_WSXGA;
+       } else {
+               *fmt = IPU_PIX_FMT_YUV444;
+               mode = TVOUT_FMT_OFF;
+       }
+       return mode;
+}
+
+static void tve_disable_vga_mode(void)
+{
+       if (tve.revision == 2) {
+               u32 reg;
+               /* disable test mode */
+               reg = __raw_readl(tve.base + tve_regs->tve_tst_mode_reg);
+               reg = reg & ~TVEV2_DAC_TEST_MODE_MASK;
+               __raw_writel(reg, tve.base + tve_regs->tve_tst_mode_reg);
+       }
+}
+
+static void tve_set_tvout_mode(int mode)
+{
+       u32 conf_reg;
+
+       /* clear sync_ch and tvout_mode fields */
+       conf_reg = __raw_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;
+       __raw_writel(conf_reg, tve.base + tve_regs->tve_com_conf_reg);
+}
+
+static int _is_tvout_mode_hd_compatible(void)
+{
+       u32 conf_reg, mode;
+
+       conf_reg = __raw_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(void)
+{
+       u32 reg;
+
+       if (tve.revision == 2) {
+               /* set gain */
+               reg = __raw_readl(tve.base + tve_regs->tve_tvdac_cont_reg);
+               reg = (reg & ~TVEV2_DAC_GAIN_MASK) | 0;
+               __raw_writel(reg, tve.base + tve_regs->tve_tvdac_cont_reg);
+               reg = __raw_readl(tve.base + tve_regs->tve_tvdac_cont_reg + 4);
+               reg = (reg & ~TVEV2_DAC_GAIN_MASK) | 0;
+               __raw_writel(reg, tve.base + tve_regs->tve_tvdac_cont_reg + 4);
+               reg = __raw_readl(tve.base + tve_regs->tve_tvdac_cont_reg + 8);
+               reg = (reg & ~TVEV2_DAC_GAIN_MASK) | 0;
+               __raw_writel(reg, tve.base + tve_regs->tve_tvdac_cont_reg + 8);
+
+               /* set tve_com_conf_reg  */
+               reg = __raw_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;
+               __raw_writel(reg, tve.base + tve_regs->tve_com_conf_reg);
+
+               /* set test mode */
+               reg = __raw_readl(tve.base + tve_regs->tve_tst_mode_reg);
+               reg = (reg & ~TVEV2_DAC_TEST_MODE_MASK) | 1;
+               __raw_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(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_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 (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_set_tvout_mode(YPBPR);
+               reg = __raw_readl(tve.base + tve_regs->tve_com_conf_reg);
+               reg = (reg & ~TVE_STAND_MASK) | TVE_PAL_STAND;
+               __raw_writel(reg, tve.base + tve_regs->tve_com_conf_reg);
+               pr_debug("TVE: change to PAL video\n");
+       } else if (mode == TVOUT_FMT_NTSC) {
+               tve_disable_vga_mode();
+               tve_set_tvout_mode(YPBPR);
+               reg = __raw_readl(tve.base + tve_regs->tve_com_conf_reg);
+               reg = (reg & ~TVE_STAND_MASK) | TVE_NTSC_STAND;
+               __raw_writel(reg, tve.base + tve_regs->tve_com_conf_reg);
+               pr_debug("TVE: change to NTSC video\n");
+       } else if (mode == TVOUT_FMT_720P60) {
+               tve_disable_vga_mode();
+               if (!_is_tvout_mode_hd_compatible()) {
+                       tve_set_tvout_mode(YPBPR);
+                       pr_debug("The TV out mode is HD incompatible. Setting to YPBPR.");
+               }
+               reg = __raw_readl(tve.base + tve_regs->tve_com_conf_reg);
+               reg = (reg & ~TVE_STAND_MASK) | TVE_HD720P60_STAND;
+               __raw_writel(reg, tve.base + tve_regs->tve_com_conf_reg);
+               pr_debug("TVE: change to 720P60 video\n");
+       } else if (mode == TVOUT_FMT_720P30) {
+               tve_disable_vga_mode();
+               if (!_is_tvout_mode_hd_compatible()) {
+                       tve_set_tvout_mode(YPBPR);
+                       pr_debug("The TV out mode is HD incompatible. Setting to YPBPR.");
+               }
+               reg = __raw_readl(tve.base + tve_regs->tve_com_conf_reg);
+               reg = (reg & ~TVE_STAND_MASK) | TVE_HD720P30_STAND;
+               __raw_writel(reg, tve.base + tve_regs->tve_com_conf_reg);
+               pr_debug("TVE: change to 720P30 video\n");
+       } else if (mode == TVOUT_FMT_1080I60) {
+               tve_disable_vga_mode();
+               if (!_is_tvout_mode_hd_compatible()) {
+                       tve_set_tvout_mode(YPBPR);
+                       pr_debug("The TV out mode is HD incompatible. Setting to YPBPR.");
+               }
+               reg = __raw_readl(tve.base + tve_regs->tve_com_conf_reg);
+               reg = (reg & ~TVE_STAND_MASK) | TVE_HD1080I60_STAND;
+               __raw_writel(reg, tve.base + tve_regs->tve_com_conf_reg);
+               pr_debug("TVE: change to 1080I60 video\n");
+       } else if (mode == TVOUT_FMT_1080I50) {
+               tve_disable_vga_mode();
+               if (!_is_tvout_mode_hd_compatible()) {
+                       tve_set_tvout_mode(YPBPR);
+                       pr_debug("The TV out mode is HD incompatible. Setting to YPBPR.");
+               }
+               reg = __raw_readl(tve.base + tve_regs->tve_com_conf_reg);
+               reg = (reg & ~TVE_STAND_MASK) | TVE_HD1080I50_STAND;
+               __raw_writel(reg, tve.base + tve_regs->tve_com_conf_reg);
+               pr_debug("TVE: change to 1080I50 video\n");
+       } else if (mode == TVOUT_FMT_1080P30) {
+               tve_disable_vga_mode();
+               if (!_is_tvout_mode_hd_compatible()) {
+                       tve_set_tvout_mode(YPBPR);
+                       pr_debug("The TV out mode is HD incompatible. Setting to YPBPR.");
+               }
+               reg = __raw_readl(tve.base + tve_regs->tve_com_conf_reg);
+               reg = (reg & ~TVE_STAND_MASK) | TVE_HD1080P30_STAND;
+               __raw_writel(reg, tve.base + tve_regs->tve_com_conf_reg);
+               pr_debug("TVE: change to 1080P30 video\n");
+       } else if (mode == TVOUT_FMT_1080P25) {
+               tve_disable_vga_mode();
+               if (!_is_tvout_mode_hd_compatible()) {
+                       tve_set_tvout_mode(YPBPR);
+                       pr_debug("The TV out mode is HD incompatible. Setting to YPBPR.");
+               }
+               reg = __raw_readl(tve.base + tve_regs->tve_com_conf_reg);
+               reg = (reg & ~TVE_STAND_MASK) | TVE_HD1080P25_STAND;
+               __raw_writel(reg, tve.base + tve_regs->tve_com_conf_reg);
+               pr_debug("TVE: change to 1080P25 video\n");
+       } else if (mode == TVOUT_FMT_1080P24) {
+               tve_disable_vga_mode();
+               if (!_is_tvout_mode_hd_compatible()) {
+                       tve_set_tvout_mode(YPBPR);
+                       pr_debug("The TV out mode is HD incompatible. Setting to YPBPR.");
+               }
+               reg = __raw_readl(tve.base + tve_regs->tve_com_conf_reg);
+               reg = (reg & ~TVE_STAND_MASK) | TVE_HD1080P24_STAND;
+               __raw_writel(reg, tve.base + tve_regs->tve_com_conf_reg);
+               pr_debug("TVE: change to 1080P24 video\n");
+       } else if (is_vga_mode(mode)) {
+               /* do not need cable detect */
+               tve_setup_vga();
+               pr_debug("TVE: change to VGA video\n");
+       } else if (mode == TVOUT_FMT_OFF) {
+               __raw_writel(0x0, tve.base + tve_regs->tve_com_conf_reg);
+               pr_debug("TVE: change to OFF video\n");
+       } else {
+               pr_debug("TVE: no such video format.\n");
+       }
+
+       if (!enabled)
+               clk_disable(tve.clk);
+
+       spin_unlock_irqrestore(&tve_lock, lock_flags);
+       return 0;
+}
+
+/**
+ * tve_enable
+ * Enable the tve Power to begin TV encoder
+ */
+static void tve_enable(void)
+{
+       u32 reg;
+       unsigned long lock_flags;
+
+       spin_lock_irqsave(&tve_lock, lock_flags);
+       if (!enabled) {
+               enabled = 1;
+               clk_enable(tve.clk);
+               reg = __raw_readl(tve.base + tve_regs->tve_com_conf_reg);
+               __raw_writel(reg | TVE_IPU_CLK_ENABLE | TVE_ENABLE,
+                                       tve.base + tve_regs->tve_com_conf_reg);
+               pr_debug("TVE power on.\n");
+       }
+
+       if (is_vga_enabled()) {
+               /* disable interrupt */
+               pr_debug("TVE VGA disable cable detect.\n");
+               __raw_writel(0xffffffff, tve.base + tve_regs->tve_stat_reg);
+               __raw_writel(0, tve.base + tve_regs->tve_int_cont_reg);
+       } else {
+               /* enable interrupt */
+               pr_debug("TVE TVE enable cable detect.\n");
+               __raw_writel(0xffffffff, tve.base + tve_regs->tve_stat_reg);
+               __raw_writel(CD_SM_INT | CD_LM_INT | CD_MON_END_INT,
+                               tve.base + tve_regs->tve_int_cont_reg);
+       }
+
+       spin_unlock_irqrestore(&tve_lock, lock_flags);
+
+       tve_dump_regs();
+}
+
+/**
+ * tve_disable
+ * Disable the tve Power to stop TV encoder
+ */
+static void tve_disable(void)
+{
+       u32 reg;
+       unsigned long lock_flags;
+
+       spin_lock_irqsave(&tve_lock, lock_flags);
+       if (enabled) {
+               enabled = 0;
+               reg = __raw_readl(tve.base + tve_regs->tve_com_conf_reg);
+               __raw_writel(reg & ~TVE_ENABLE & ~TVE_IPU_CLK_ENABLE,
+                               tve.base + tve_regs->tve_com_conf_reg);
+               clk_disable(tve.clk);
+               pr_debug("TVE power off.\n");
+       }
+       spin_unlock_irqrestore(&tve_lock, lock_flags);
+}
+
+static int tve_update_detect_status(void)
+{
+       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;
+
+       spin_lock_irqsave(&tve_lock, lock_flags);
+
+       if (!enabled) {
+               pr_warning("Warning: update tve status while it disabled!\n");
+               tve.detect = 0;
+               goto done;
+       }
+
+       int_ctl = __raw_readl(tve.base + tve_regs->tve_int_cont_reg);
+       cd_cont_reg = __raw_readl(tve.base + tve_regs->tve_cd_cont_reg);
+
+       if ((cd_cont_reg & 0x1) == 0) {
+               pr_warning("Warning: pls enable TVE CD first!\n");
+               goto done;
+       }
+
+       stat = __raw_readl(tve.base + tve_regs->tve_stat_reg);
+       while (((stat & CD_MON_END_INT) == 0) && (timeout > 0)) {
+               spin_unlock_irqrestore(&tve_lock, lock_flags);
+               msleep(2);
+               spin_lock_irqsave(&tve_lock, lock_flags);
+               timeout -= 2;
+               if (!enabled) {
+                       pr_warning("Warning: update tve status while it disabled!\n");
+                       tve.detect = 0;
+                       goto done;
+               } else
+                       stat = __raw_readl(tve.base + tve_regs->tve_stat_reg);
+       }
+       if (((stat & CD_MON_END_INT) == 0) && (timeout <= 0)) {
+               pr_warning("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.output_mode);
+
+       /* clear interrupt */
+       __raw_writel(CD_MON_END_INT | CD_LM_INT | CD_SM_INT,
+                       tve.base + tve_regs->tve_stat_reg);
+
+       __raw_writel(int_ctl | CD_SM_INT | CD_LM_INT,
+                       tve.base + tve_regs->tve_int_cont_reg);
+
+       if (old_detect != tve.detect)
+               sysfs_notify(&tve.pdev->dev.kobj, NULL, "headphone");
+
+       dev_dbg(&tve.pdev->dev, "detect = %d mode = %d\n",
+                       tve.detect, tve.output_mode);
+done:
+       spin_unlock_irqrestore(&tve_lock, lock_flags);
+       return tve.detect;
+}
+
+static void cd_work_func(struct work_struct *work)
+{
+       tve_update_detect_status();
+}
+#if 0
+static int tve_man_detect(void)
+{
+       u32 cd_cont;
+       u32 int_cont;
+
+       if (!enabled)
+               return -1;
+
+       int_cont = __raw_readl(tve.base + tve_regs->tve_int_cont_reg);
+       __raw_writel(int_cont &
+                               ~(tve_reg_fields->cd_sm_int | tve_reg_fields->cd_lm_int),
+                               tve.base + tve_regs->tve_int_cont_reg);
+
+       cd_cont = __raw_readl(tve.base + tve_regs->tve_cd_cont_reg);
+       __raw_writel(cd_cont | tve_reg_fields->cd_trig_mode,
+                               tve.base + tve_regs->tve_cd_cont_reg);
+
+       __raw_writel(tve_reg_fields->cd_sm_int | tve_reg_fields->
+                       cd_lm_int | tve_reg_fields->
+                       cd_mon_end_int | tve_reg_fields->cd_man_trig,
+                       tve.base + tve_regs->tve_stat_reg);
+
+       while ((__raw_readl(tve.base + tve_regs->tve_stat_reg)
+               & tve_reg_fields->cd_mon_end_int) == 0)
+               msleep(5);
+
+       tve_update_detect_status();
+
+       __raw_writel(cd_cont, tve.base + tve_regs->tve_cd_cont_reg);
+       __raw_writel(int_cont, tve.base + tve_regs->tve_int_cont_reg);
+
+       return tve.detect;
+}
+#endif
+
+static irqreturn_t tve_detect_handler(int irq, void *data)
+{
+       u32 int_ctl = __raw_readl(tve.base + tve_regs->tve_int_cont_reg);
+
+       /* disable INT first */
+       int_ctl &= ~(CD_SM_INT | CD_LM_INT | CD_MON_END_INT);
+       __raw_writel(int_ctl, tve.base + tve_regs->tve_int_cont_reg);
+
+       __raw_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;
+}
+
+static inline void tve_set_di_fmt(struct fb_info *fbi, unsigned int fmt)
+{
+       mm_segment_t old_fs;
+
+       if (fbi->fbops->fb_ioctl) {
+               old_fs = get_fs();
+               set_fs(KERNEL_DS);
+               fbi->fbops->fb_ioctl(fbi, MXCFB_SET_DIFMT, (unsigned long)&fmt);
+               set_fs(old_fs);
+       }
+}
+
+/*!
+ * FB suspend/resume routing
+ */
+static int tve_suspend(void)
+{
+       if (enabled) {
+               __raw_writel(0, tve.base + tve_regs->tve_int_cont_reg);
+               __raw_writel(0, tve.base + tve_regs->tve_cd_cont_reg);
+               __raw_writel(0, tve.base + tve_regs->tve_com_conf_reg);
+               clk_disable(tve.clk);
+       }
+       return 0;
+}
+
+static int tve_resume(struct fb_info *fbi)
+{
+       int mode;
+
+       if (enabled) {
+               clk_enable(tve.clk);
+
+               /* Setup cable detect */
+               if (tve.revision == 1)
+                       __raw_writel(0x01067701,
+                               tve.base + tve_regs->tve_cd_cont_reg);
+               else
+                       __raw_writel(0x00770601,
+                               tve.base + tve_regs->tve_cd_cont_reg);
+
+               if (valid_mode(tve.cur_mode)) {
+                       mode = tve.cur_mode;
+                       tve_disable();
+                       tve.cur_mode = TVOUT_FMT_OFF;
+                       tve_setup(mode);
+               }
+               tve_enable();
+       }
+
+       return 0;
+}
+
+int tve_fb_setup(struct fb_info *fbi)
+{
+       int mode, fmt;
+
+       fbi->mode = (struct fb_videomode *)fb_match_mode(&fbi->var,
+                       &fbi->modelist);
+
+       if (!fbi->mode) {
+               pr_warning("TVE: can not find mode for xres=%d, yres=%d\n",
+                               fbi->var.xres, fbi->var.yres);
+               tve_disable();
+               tve.cur_mode = TVOUT_FMT_OFF;
+               return 0;
+       }
+
+       pr_debug("TVE: fb mode change event: xres=%d, yres=%d\n",
+                       fbi->mode->xres, fbi->mode->yres);
+
+       mode = get_video_mode(fbi, &fmt);
+       if (mode != TVOUT_FMT_OFF) {
+               tve_set_di_fmt(fbi, fmt);
+               tve_disable();
+               tve_setup(mode);
+               if (tve.blank == FB_BLANK_UNBLANK)
+                       tve_enable();
+       } else {
+               tve_disable();
+               tve_setup(mode);
+       }
+
+       return 0;
+}
+
+int tve_fb_event(struct notifier_block *nb, unsigned long val, void *v)
+{
+       struct fb_event *event = v;
+       struct fb_info *fbi = event->info;
+
+       if (strcmp(fbi->fix.id, "DISP3 BG - DI1"))
+               return 0;
+
+       switch (val) {
+       case FB_EVENT_FB_REGISTERED:
+               pr_debug("TVE: fb registered event\n");
+               if (tve_fbi != NULL)
+                       break;
+
+               tve_fbi = fbi;
+               break;
+       case FB_EVENT_MODE_CHANGE:
+       {
+               if (tve_fbi != fbi)
+                       break;
+
+               tve_fb_setup(fbi);
+               break;
+       }
+       case FB_EVENT_BLANK:
+               if ((tve_fbi != fbi) || (fbi->mode == NULL))
+                       return 0;
+
+               pr_debug("TVE: fb blank event\n");
+
+               if (*((int *)event->data) == FB_BLANK_UNBLANK) {
+                       if (tve.blank != FB_BLANK_UNBLANK) {
+                               int mode, fmt;
+                               mode = get_video_mode(fbi, &fmt);
+                               if (mode != TVOUT_FMT_OFF) {
+                                       if (tve.cur_mode != mode) {
+                                               tve_disable();
+                                               tve_setup(mode);
+                                       }
+                                       tve_enable();
+                               } else
+                                       tve_setup(mode);
+                               tve.blank = FB_BLANK_UNBLANK;
+                       }
+               } else {
+                       tve_disable();
+                       tve.blank = FB_BLANK_POWERDOWN;
+               }
+               break;
+       case FB_EVENT_SUSPEND:
+               tve_suspend();
+               break;
+       case FB_EVENT_RESUME:
+               tve_resume(fbi);
+               break;
+       }
+       return 0;
+}
+
+static struct notifier_block nb = {
+       .notifier_call = tve_fb_event,
+};
+
+static ssize_t show_headphone(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       int detect;
+
+       if (!enabled) {
+               strcpy(buf, "tve power off\n");
+               return strlen(buf);
+       }
+
+       detect = tve_update_detect_status();
+
+       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(void)
+{
+       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 = __raw_readl(tve.base);
+       switch (conf_reg) {
+       case 0x00842000:
+               rev = 1;
+               break;
+       case 0x00100000:
+               rev = 2;
+               break;
+       }
+       return rev;
+}
+
+int tve_fb_pre_setup(struct fb_info *fbi)
+{
+       if (fbi->fbops->fb_ioctl) {
+               mm_segment_t old_fs;
+
+               old_fs = get_fs();
+               set_fs(KERNEL_DS);
+               fbi->fbops->fb_ioctl(fbi,
+                               MXCFB_GET_FB_BLANK,
+                               (unsigned int)(&tve.blank));
+               set_fs(old_fs);
+       }
+       return tve_fb_setup(fbi);
+}
+
+static int tve_probe(struct platform_device *pdev)
+{
+       int ret;
+       struct resource *res;
+       struct fsl_mxc_tve_platform_data *plat_data = pdev->dev.platform_data;
+       u32 conf_reg;
+
+       if (g_enable_tve == false && g_enable_vga == false)
+               return -EPERM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (res == NULL)
+               return -ENOMEM;
+
+       tve.pdev = pdev;
+       tve.base = ioremap(res->start, res->end - res->start);
+
+       tve.irq = platform_get_irq(pdev, 0);
+       if (tve.irq < 0) {
+               ret = tve.irq;
+               goto err0;
+       }
+
+       ret = request_irq(tve.irq, tve_detect_handler, 0, pdev->name, pdev);
+       if (ret < 0)
+               goto err0;
+
+       ret = device_create_file(&pdev->dev, &dev_attr_headphone);
+       if (ret < 0)
+               goto err1;
+
+       tve.dac_reg = regulator_get(&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(&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(&pdev->dev, "tve_clk");
+       if (IS_ERR(tve.clk)) {
+               ret = PTR_ERR(tve.clk);
+               goto err2;
+       }
+       tve.di_clk = clk_get(NULL, "ipu_di1_clk");
+       if (IS_ERR(tve.di_clk)) {
+               ret = PTR_ERR(tve.di_clk);
+               goto err2;
+       }
+       clk_set_rate(tve.clk, 216000000);
+       clk_set_parent(tve.di_clk, tve.clk);
+       clk_enable(tve.clk);
+
+       tve.revision = _tve_get_revision();
+       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[0].left_margin = 121;
+               video_modes[0].right_margin = 16;
+               video_modes[0].upper_margin = 17;
+               video_modes[0].lower_margin = 5;
+               video_modes[1].left_margin = 131;
+               video_modes[1].right_margin = 12;
+               video_modes[1].upper_margin = 21;
+               video_modes[1].lower_margin = 3;
+       }*/
+
+       /* TVE is on disp port 1 */
+       if (tve.revision == 1) {
+               if (g_enable_tve)
+                       mxcfb_register_mode(IPU_DISP_PORT, video_modes,
+                                       3, MXC_DISP_SPEC_DEV);
+       } else {
+               if (g_enable_tve)
+                       mxcfb_register_mode(IPU_DISP_PORT, video_modes,
+                                       ARRAY_SIZE(video_modes),
+                                       MXC_DISP_SPEC_DEV);
+
+               if (cpu_is_mx53() && g_enable_vga)
+                       mxcfb_register_mode(IPU_DISP_PORT, video_modes_vga,
+                                       ARRAY_SIZE(video_modes_vga),
+                                       MXC_DISP_SPEC_DEV);
+       }
+       mxcfb_register_presetup(IPU_DISP_PORT, tve_fb_pre_setup);
+
+       /* 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)
+               __raw_writel(0x01067701, tve.base + tve_regs->tve_cd_cont_reg);
+       else
+               __raw_writel(0x00770601, tve.base + tve_regs->tve_cd_cont_reg);
+
+       conf_reg = 0;
+       __raw_writel(conf_reg, tve.base + tve_regs->tve_com_conf_reg);
+
+       __raw_writel(0x00000000, tve.base + tve_regs->tve_mv_cont_reg - 4 * 5);
+       __raw_writel(0x00000000, tve.base + tve_regs->tve_mv_cont_reg - 4 * 4);
+       __raw_writel(0x00000000, tve.base + tve_regs->tve_mv_cont_reg - 4 * 3);
+       __raw_writel(0x00000000, tve.base + tve_regs->tve_mv_cont_reg - 4 * 2);
+       __raw_writel(0x00000000, tve.base + tve_regs->tve_mv_cont_reg - 4);
+       __raw_writel(0x00000000, tve.base + tve_regs->tve_mv_cont_reg);
+
+       clk_disable(tve.clk);
+
+       ret = fb_register_client(&nb);
+       if (ret < 0)
+               goto err2;
+
+       tve.blank = -1;
+
+       return 0;
+err2:
+       device_remove_file(&pdev->dev, &dev_attr_headphone);
+err1:
+       free_irq(tve.irq, pdev);
+err0:
+       iounmap(tve.base);
+       return ret;
+}
+
+static int tve_remove(struct platform_device *pdev)
+{
+       if (enabled) {
+               clk_disable(tve.clk);
+               enabled = 0;
+       }
+       free_irq(tve.irq, pdev);
+       device_remove_file(&pdev->dev, &dev_attr_headphone);
+       fb_unregister_client(&nb);
+       return 0;
+}
+
+static struct platform_driver tve_driver = {
+       .driver = {
+                  .name = "mxc_tve",
+                  },
+       .probe = tve_probe,
+       .remove = tve_remove,
+};
+
+static int __init enable_tve_setup(char *options)
+{
+       g_enable_tve = true;
+
+       return 1;
+}
+__setup("tve", enable_tve_setup);
+
+static int __init enable_vga_setup(char *options)
+{
+       g_enable_vga = true;
+
+       return 1;
+}
+__setup("vga", enable_vga_setup);
+
+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");