summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorQuinn Jensen <quinn.jensen@freescale.com>2007-10-24 21:17:16 -0600
committerQuinn Jensen <quinn.jensen@freescale.com>2007-10-24 21:17:16 -0600
commitc71cbf8d5b51090c103fe85f0f306823278853d1 (patch)
tree9e96cecdda6f11e201b0cd75fd20e87aee03eddd /drivers
parentea2b7e75d21a9c89fd599aa61033efc8f1cd1b0e (diff)
This patch adds MX-specific drivers to the linux 2.6.22 kernel
for MX platforms. Drivers include: Digital Audio Mux (DAM), MPEG4, Image Processing Unit (IPU), Power Management, SSI, and VPU (Video Processing Unit) drivers. http://www.bitshrine.org/gpp/linux-2.6.22-mx-drivers_mxc.patch
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/mxc/Kconfig18
-rw-r--r--drivers/mxc/Makefile11
-rw-r--r--drivers/mxc/dam/Kconfig14
-rw-r--r--drivers/mxc/dam/Makefile11
-rw-r--r--drivers/mxc/dam/dam.c427
-rw-r--r--drivers/mxc/dam/dam.h258
-rw-r--r--drivers/mxc/dam/dam_v1.c616
-rw-r--r--drivers/mxc/hmp4e/Kconfig24
-rw-r--r--drivers/mxc/hmp4e/Makefile9
-rw-r--r--drivers/mxc/hmp4e/mxc_hmp4e.c811
-rw-r--r--drivers/mxc/hmp4e/mxc_hmp4e.h70
-rw-r--r--drivers/mxc/ipu/Kconfig17
-rw-r--r--drivers/mxc/ipu/Makefile5
-rw-r--r--drivers/mxc/ipu/ipu_adc.c677
-rw-r--r--drivers/mxc/ipu/ipu_common.c1800
-rw-r--r--drivers/mxc/ipu/ipu_csi.c217
-rw-r--r--drivers/mxc/ipu/ipu_device.c600
-rw-r--r--drivers/mxc/ipu/ipu_ic.c579
-rw-r--r--drivers/mxc/ipu/ipu_param_mem.h176
-rw-r--r--drivers/mxc/ipu/ipu_prv.h59
-rw-r--r--drivers/mxc/ipu/ipu_regs.h396
-rw-r--r--drivers/mxc/ipu/ipu_sdc.c355
-rw-r--r--drivers/mxc/ipu/pf/Kconfig7
-rw-r--r--drivers/mxc/ipu/pf/Makefile2
-rw-r--r--drivers/mxc/ipu/pf/mxc_pf.c995
-rw-r--r--drivers/mxc/pm/Kconfig40
-rw-r--r--drivers/mxc/pm/Makefile13
-rw-r--r--drivers/mxc/pm/dptc.c890
-rw-r--r--drivers/mxc/pm/dptc_mx27.c1416
-rw-r--r--drivers/mxc/pm/dvfs_dptc.c1248
-rw-r--r--drivers/mxc/pm/dvfs_dptc.h80
-rw-r--r--drivers/mxc/pm/dvfs_dptc_table_mx27.h69
-rw-r--r--drivers/mxc/pm/dvfs_dptc_table_mx31.h157
-rw-r--r--drivers/mxc/pm/dvfs_dptc_table_mx31_27ckih.h152
-rw-r--r--drivers/mxc/pm/dvfs_dptc_table_mx31_rev2.h158
-rw-r--r--drivers/mxc/ssi/Kconfig13
-rw-r--r--drivers/mxc/ssi/Makefile9
-rw-r--r--drivers/mxc/ssi/registers.h190
-rw-r--r--drivers/mxc/ssi/ssi.c1155
-rw-r--r--drivers/mxc/ssi/ssi.h565
-rw-r--r--drivers/mxc/ssi/ssi_types.h359
-rw-r--r--drivers/mxc/vpu/Kconfig22
-rw-r--r--drivers/mxc/vpu/Makefile10
-rw-r--r--drivers/mxc/vpu/mxc_vl2cc.c123
-rw-r--r--drivers/mxc/vpu/mxc_vpu.c489
46 files changed, 15313 insertions, 0 deletions
diff --git a/drivers/Makefile b/drivers/Makefile
index adad2f3d438a..6e3a8a854db4 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -70,6 +70,7 @@ obj-$(CONFIG_EDAC) += edac/
obj-$(CONFIG_MCA) += mca/
obj-$(CONFIG_EISA) += eisa/
obj-$(CONFIG_CPU_FREQ) += cpufreq/
+obj-$(CONFIG_ARCH_MXC) += mxc/
obj-$(CONFIG_MMC) += mmc/
obj-$(CONFIG_NEW_LEDS) += leds/
obj-$(CONFIG_INFINIBAND) += infiniband/
diff --git a/drivers/mxc/Kconfig b/drivers/mxc/Kconfig
new file mode 100644
index 000000000000..f913e03af789
--- /dev/null
+++ b/drivers/mxc/Kconfig
@@ -0,0 +1,18 @@
+# drivers/video/mxc/Kconfig
+
+if ARCH_MXC
+
+menu "MXC support drivers"
+
+source "drivers/mxc/ipu/Kconfig"
+source "drivers/mxc/ssi/Kconfig"
+source "drivers/mxc/dam/Kconfig"
+source "drivers/mxc/pmic/Kconfig"
+source "drivers/mxc/pm/Kconfig"
+source "drivers/mxc/security/Kconfig"
+source "drivers/mxc/hmp4e/Kconfig"
+source "drivers/mxc/vpu/Kconfig"
+
+endmenu
+
+endif
diff --git a/drivers/mxc/Makefile b/drivers/mxc/Makefile
new file mode 100644
index 000000000000..4a7029df5200
--- /dev/null
+++ b/drivers/mxc/Makefile
@@ -0,0 +1,11 @@
+obj-$(CONFIG_MXC_IPU) += ipu/
+obj-$(CONFIG_MXC_SSI) += ssi/
+obj-$(CONFIG_MXC_DAM) += dam/
+
+obj-$(CONFIG_MXC_PMIC) += pmic/
+
+obj-$(CONFIG_MXC_DPTC) += pm/
+obj-$(CONFIG_MX27_DPTC) += pm/
+obj-$(CONFIG_MXC_HMP4E) += hmp4e/
+obj-y += security/
+obj-$(CONFIG_MXC_VPU) += vpu/
diff --git a/drivers/mxc/dam/Kconfig b/drivers/mxc/dam/Kconfig
new file mode 100644
index 000000000000..3df2f8da2ecd
--- /dev/null
+++ b/drivers/mxc/dam/Kconfig
@@ -0,0 +1,14 @@
+#
+# 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
index 000000000000..0596557c99b6
--- /dev/null
+++ b/drivers/mxc/dam/Makefile
@@ -0,0 +1,11 @@
+#
+# 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
index 000000000000..54c7dbc78625
--- /dev/null
+++ b/drivers/mxc/dam/dam.c
@@ -0,0 +1,427 @@
+/*
+ * Copyright 2004-2007 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 <asm/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
index 000000000000..cb9ead53f689
--- /dev/null
+++ b/drivers/mxc/dam/dam.h
@@ -0,0 +1,258 @@
+/*
+ * Copyright 2004-2007 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
index 000000000000..ee5575213e65
--- /dev/null
+++ b/drivers/mxc/dam/dam_v1.c
@@ -0,0 +1,616 @@
+/*
+ * Copyright 2004-2007 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 <asm/uaccess.h>
+#include "dam.h"
+
+/*!
+ * This include to define bool type, false and true definitions.
+ */
+#include <asm/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 class_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 = 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");
+ class_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
+ class_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/hmp4e/Kconfig b/drivers/mxc/hmp4e/Kconfig
new file mode 100644
index 000000000000..01b92a4714f1
--- /dev/null
+++ b/drivers/mxc/hmp4e/Kconfig
@@ -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
index 000000000000..512f3e0f665a
--- /dev/null
+++ b/drivers/mxc/hmp4e/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for the MPEG4 Encoder kernel module.
+
+obj-$(CONFIG_MXC_HMP4E) += mxc_hmp4e.o
+
+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
index 000000000000..459d2f34a3e0
--- /dev/null
+++ b/drivers/mxc/hmp4e/mxc_hmp4e.c
@@ -0,0 +1,811 @@
+/*
+ * Copyright 2005-2007 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 "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 = false;
+
+ulong base_port = MPEG4_ENC_BASE_ADDR;
+u32 irq = INT_MPEG4_ENC;
+
+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 class_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 = 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)
+{
+ class_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
index 000000000000..53f173f1321e
--- /dev/null
+++ b/drivers/mxc/hmp4e/mxc_hmp4e.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2005-2007 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/ipu/Kconfig b/drivers/mxc/ipu/Kconfig
new file mode 100644
index 000000000000..e13759c38425
--- /dev/null
+++ b/drivers/mxc/ipu/Kconfig
@@ -0,0 +1,17 @@
+menu "MXC IPU"
+
+config MXC_IPU
+ bool "MXC Image Processing Unit"
+ depends on ARCH_MXC
+ depends on !ARCH_MX27
+ help
+ If you plan to use the Image Processing unit in the MXC, say
+ Y here. If unsure, select Y.
+
+config MXC_IPU_LPMC
+ bool
+ depends on MXC_IPU
+
+source "drivers/mxc/ipu/pf/Kconfig"
+
+endmenu
diff --git a/drivers/mxc/ipu/Makefile b/drivers/mxc/ipu/Makefile
new file mode 100644
index 000000000000..f94f0eb64dca
--- /dev/null
+++ b/drivers/mxc/ipu/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_MXC_IPU) += mxc_ipu.o
+
+mxc_ipu-objs := ipu_common.o ipu_sdc.o ipu_adc.o ipu_ic.o ipu_csi.o ipu_device.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
index 000000000000..85a53688b06e
--- /dev/null
+++ b/drivers/mxc/ipu/ipu_adc.c
@@ -0,0 +1,677 @@
+/*
+ * Copyright 2005-2007 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 <asm/io.h>
+#include <asm/arch/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;
+ }
+}
+
+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_common.c b/drivers/mxc/ipu/ipu_common.c
new file mode 100644
index 000000000000..6abe712c5ba3
--- /dev/null
+++ b/drivers/mxc/ipu/ipu_common.c
@@ -0,0 +1,1800 @@
+/*
+ * Copyright 2005-2007 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 <asm/io.h>
+#include <asm/arch/ipu.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 = 0;
+static uint32_t g_channel_init_mask_backup = 0;
+static bool g_csi_used = false;
+
+/* 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);
+
+inline static 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)));
+};
+
+inline static 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 platform_device *pdev = to_platform_device(dev);
+ 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);
+
+ __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(0x00000070L, 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");
+ break;
+ }
+
+ /* 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;
+ uint32_t stride_bytes;
+
+ dma_chan = channel_2_dma(channel, type);
+ stride_bytes = stride * bytes_per_pixel(pixel_fmt);
+
+ if (dma_chan == IDMA_CHAN_INVALID)
+ return -EINVAL;
+
+ if (stride_bytes % 4) {
+ dev_err(g_ipu_dev,
+ "Stride length must be 32-bit aligned, stride = %d, bytes = %d\n",
+ stride, stride_bytes);
+ return -EINVAL;
+ }
+ /* IC channels' stride must be multiple of 8 pixels */
+ if ((dma_chan <= 13) && (stride % 8)) {
+ dev_err(g_ipu_dev, "Stride 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_bytes,
+ 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 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 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 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 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;
+
+ /* 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) {
+ uint32_t 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) {
+ uint32_t timeout = 5;
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+ ipu_clear_irq(IPU_IRQ_SDC_BG_EOF);
+ if (_ipu_sdc_bg_uninit() && wait_for_stop) {
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+ while (!ipu_get_irq_status(IPU_IRQ_SDC_BG_EOF)) {
+ msleep(5);
+ timeout--;
+ if (timeout == 0)
+ break;
+ }
+ } else {
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ }
+ } else if (channel == MEM_SDC_FG) {
+ uint32_t timeout = 5;
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+ ipu_clear_irq(IPU_IRQ_SDC_FG_EOF);
+ if (_ipu_sdc_fg_uninit() && wait_for_stop) {
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+ while (!ipu_get_irq_status(IPU_IRQ_SDC_FG_EOF)) {
+ msleep(5);
+ timeout--;
+ if (timeout == 0)
+ break;
+ }
+ } else {
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ }
+ }
+
+ 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);
+
+ /* Reset to buffer 0 */
+ __raw_writel(chan_mask, IPU_CHA_CUR_BUF);
+
+ /* 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;
+}
+
+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;
+
+ if (g_ipu_irq[1]) {
+ disable_irq(g_ipu_irq[0]);
+ disable_irq(g_ipu_irq[1]);
+ }
+
+ 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);
+ }
+
+ if (g_ipu_irq[1]) {
+ enable_irq(g_ipu_irq[0]);
+ enable_irq(g_ipu_irq[1]);
+ }
+ 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;
+}
+
+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;
+ }
+ }
+ 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;
+ }
+ }
+ 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_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
index 000000000000..ece04f003f85
--- /dev/null
+++ b/drivers/mxc/ipu/ipu_csi.c
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2005-2007 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/errno.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <asm/arch/ipu.h>
+
+#include "ipu_prv.h"
+#include "ipu_regs.h"
+
+static bool gipu_csi_get_mclk_flag = false;
+static int csi_mclk_flag = 0;
+
+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) ||
+ (sig.clk_mode == IPU_CSI_CLK_MODE_CCIR656_INTERLACED)) {
+ __raw_writel(0x40030, CSI_CCIR_CODE_1);
+ __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
+ *
+ */
+void ipu_csi_get_window_size(uint32_t * width, uint32_t * height)
+{
+ 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
+ *
+ */
+void ipu_csi_set_window_size(uint32_t width, uint32_t height)
+{
+ __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
+ *
+ */
+void ipu_csi_set_window_pos(uint32_t left, uint32_t top)
+{
+ uint32_t temp = __raw_readl(CSI_OUT_FRM_CTRL);
+ temp &= 0xffff0000;
+ temp = top | (left << 8) | temp;
+ __raw_writel(temp, CSI_OUT_FRM_CTRL);
+}
+
+/* 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
index 000000000000..c777d143146b
--- /dev/null
+++ b/drivers/mxc/ipu/ipu_device.c
@@ -0,0 +1,600 @@
+/*
+ * Copyright 2005-2007 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 <asm/io.h>
+#include <asm/arch/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 = 0;
+int read_ptr = 0;
+int write_ptr = 0;
+
+typedef struct _event_type {
+ int irq;
+ void *dev;
+} event_type;
+
+event_type events[MAX_Q_SIZE];
+
+int register_ipu_device(void);
+
+/* Static functions */
+
+int get_events(event_type * p)
+{
+ unsigned long flags;
+ int ret = 0;
+ spin_lock_irqsave(&queue_lock, flags);
+ if (pending_events != 0) {
+ *p = events[read_ptr];
+ read_ptr++;
+ pending_events--;
+ if (read_ptr >= MAX_Q_SIZE)
+ read_ptr = 0;
+ } else {
+ ret = -1;
+ }
+
+ spin_unlock_irqrestore(&queue_lock, flags);
+ return ret;
+}
+
+static irqreturn_t mxc_ipu_generic_handler(int irq, void *dev_id)
+{
+ event_type 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_sdc_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 */
+ {
+ event_type ev;
+ int r = -1;
+ r = get_events(&ev);
+ if (r == -1) {
+ wait_event_interruptible(waitq,
+ (pending_events != 0));
+ r = get_events(&ev);
+ }
+ ret = -1;
+ if (r == 0) {
+ if (!copy_to_user((event_type *) arg, &ev,
+ sizeof(event_type))) {
+ 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;
+ ipu_csi_get_window_size(&w.width, &w.height);
+ 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;
+ if (copy_from_user
+ (&w, (ipu_csi_window_size *) arg, sizeof(w)))
+ return -EFAULT;
+ ipu_csi_set_window_size(w.width, w.height);
+ }
+ break;
+ case IPU_CSI_SET_WINDOW:
+ {
+ ipu_csi_window p;
+ if (copy_from_user
+ (&p, (ipu_csi_window *) arg, sizeof(p)))
+ return -EFAULT;
+ ipu_csi_set_window_pos(p.left, p.top);
+ }
+ 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;
+ default:
+ break;
+
+ }
+ return ret;
+}
+
+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,
+ .release = mxc_ipu_release,
+ .ioctl = mxc_ipu_ioctl
+};
+
+int register_ipu_device()
+{
+ int ret = 0;
+ struct class_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 =
+ class_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
index 000000000000..83fc349deb10
--- /dev/null
+++ b/drivers/mxc/ipu/ipu_ic.c
@@ -0,0 +1,579 @@
+/*
+ * Copyright 2005-2007 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 <asm/io.h>
+#include <asm/arch/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);
+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 (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
index 000000000000..07bd03a81e3f
--- /dev/null
+++ b/drivers/mxc/ipu/ipu_param_mem.h
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2005-2007 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
index 000000000000..1da41734f36e
--- /dev/null
+++ b/drivers/mxc/ipu/ipu_prv.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2005-2007 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 <asm/arch/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
index 000000000000..d3ac76ea7834
--- /dev/null
+++ b/drivers/mxc/ipu/ipu_regs.h
@@ -0,0 +1,396 @@
+/*
+ * Copyright 2005-2007 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
index 000000000000..6feb24d63cfd
--- /dev/null
+++ b/drivers/mxc/ipu/ipu_sdc.c
@@ -0,0 +1,355 @@
+/*
+ * Copyright 2005-2007 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 <asm/io.h>
+#include <asm/arch/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 vSyncWidth, 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 ((vSyncWidth == 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_start_width + h_end_width - 1) << 16);
+ __raw_writel(reg, SDC_HOR_CONF);
+
+ reg = ((uint32_t) (vSyncWidth - 1) << 26) | SDC_V_SYNC_WIDTH_L |
+ ((uint32_t) (height + v_start_width + v_end_width - 1) << 16);
+ __raw_writel(reg, SDC_VER_CONF);
+
+ g_h_start_width = h_start_width;
+ g_v_start_width = v_start_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_sdc_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_sdc_set_window_pos);
diff --git a/drivers/mxc/ipu/pf/Kconfig b/drivers/mxc/ipu/pf/Kconfig
new file mode 100644
index 000000000000..02cda917fb7a
--- /dev/null
+++ b/drivers/mxc/ipu/pf/Kconfig
@@ -0,0 +1,7 @@
+config MXC_IPU_PF
+ tristate "MXC MPEG4/H.264 Post Filter Driver"
+ depends on MXC_IPU
+ 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
index 000000000000..3fdccc453f24
--- /dev/null
+++ b/drivers/mxc/ipu/pf/Makefile
@@ -0,0 +1,2 @@
+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
index 000000000000..fe5057ed3d2b
--- /dev/null
+++ b/drivers/mxc/ipu/pf/mxc_pf.c
@@ -0,0 +1,995 @@
+/*
+ * Copyright 2005-2007 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 <asm/arch/ipu.h>
+#include <asm/arch/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;
+ bool buffer_dirty;
+ struct semaphore busy_lock;
+};
+
+static struct mxc_pf_data pf_data;
+static u8 open_count = 0;
+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;
+
+ 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);
+
+ /*Update y, u, v buffers in DMA Channels */
+ if ((retval =
+ mxc_pf_start(&start_params.in, &start_params.out,
+ start_params.qp_buf))
+ < 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.buffer_dirty = true;
+ 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)
+{
+ if (pf_data.buffer_dirty) {
+ flush_cache_all();
+// l2_flush_all();
+ }
+ pf_data.buffer_dirty = false;
+
+ 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 = 0;
+
+/*!
+ * Post Filter driver module initialization function.
+ */
+int mxc_pf_dev_init(void)
+{
+ int ret = 0;
+ struct class_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 = 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) {
+ class_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/pm/Kconfig b/drivers/mxc/pm/Kconfig
new file mode 100644
index 000000000000..ccf444e057ea
--- /dev/null
+++ b/drivers/mxc/pm/Kconfig
@@ -0,0 +1,40 @@
+#
+# Power Managment devices
+#
+
+menu "Advanced Power Management devices"
+
+config MXC_DPTC
+ bool "MXC DPTC driver"
+ depends on ARCH_MX3 && MXC_PMIC_MC13783
+ default y
+ help
+ This selects the Freescale MXC Internal DPTC driver.
+ If unsure, say N.
+
+config MX27_DPTC
+ bool "MXC DPTC driver"
+ depends on ARCH_MX27 && MXC_PMIC_MC13783
+ default y
+ help
+ This selects the Freescale MX27 Internal DPTC driver.
+ If unsure, say N.
+
+config MXC_DVFS
+ bool "MXC DVFS driver"
+ depends on ARCH_MX3
+ default y
+ help
+ This selects the Freescale MXC Internal DVFS driver.
+ If unsure, say N.
+
+config MXC_DVFS_SDMA
+ bool "MXC DVFS SDMA support"
+ depends on MXC_DVFS
+ default n
+ help
+ This selects the Freescale MXC Internal DVFS driver SDMA support.
+ If unsure, say N.
+
+endmenu
+
diff --git a/drivers/mxc/pm/Makefile b/drivers/mxc/pm/Makefile
new file mode 100644
index 000000000000..70e965813976
--- /dev/null
+++ b/drivers/mxc/pm/Makefile
@@ -0,0 +1,13 @@
+#
+# Makefile for Power Managment devices.
+#
+
+# Module dependencies for the MXC driver
+obj-$(CONFIG_MXC_DPTC) += dptc.o
+
+obj-$(CONFIG_MXC_DPTC) += dvfs_dptc.o
+
+obj-$(CONFIG_MXC_DVFS) += dvfs_dptc.o
+
+obj-$(CONFIG_MX27_DPTC) += dptc_mx27.o
+
diff --git a/drivers/mxc/pm/dptc.c b/drivers/mxc/pm/dptc.c
new file mode 100644
index 000000000000..db28b95fc6f3
--- /dev/null
+++ b/drivers/mxc/pm/dptc.c
@@ -0,0 +1,890 @@
+/*
+ * Copyright 2004-2007 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 dptc.c
+ *
+ * @brief Driver for the Freescale Semiconductor MXC DPTC module.
+ *
+ * The DPTC driver
+ * driver is designed as a character driver which interacts with the MXC DPTC
+ * hardware. Upon initialization, the DPTC driver initializes the DPTC hardware
+ * sets up driver nodes attaches to the DPTC interrupt and initializes internal
+ * data structures. When the DPTC interrupt occurs the driver checks the cause
+ * of the interrupt (lower voltage, increase voltage or emergency) and changes
+ * the CPU voltage according to translation table that is loaded into the driver
+ * (the voltage changes are done by calling some routines in the mc13783 driver).
+ * The driver read method is used to read the currently loaded DPTC translation
+ * table and the write method is used in-order to update the translation table.
+ * Driver ioctls are used to change driver parameters and enable/disable the
+ * DPTC operation.
+ *
+ * @ingroup PM_MX31
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <asm/uaccess.h>
+#include <asm/hardware.h>
+#include <linux/workqueue.h>
+#include <linux/proc_fs.h>
+#include <asm/semaphore.h>
+#include <linux/jiffies.h>
+#include <linux/platform_device.h>
+#include <asm/arch/sdma.h>
+#include <asm/arch/pmic_power.h>
+
+/*
+ * Module header file
+ */
+#include <asm/arch/dptc.h>
+
+/*
+ * CRM registers
+ */
+#ifdef CONFIG_ARCH_MX3
+#include "../../../arch/arm/mach-mx3/crm_regs.h"
+#endif
+
+/*!
+ * The dvfs_dptc_params structure holds all the internal DPTC driver parameters
+ * (current working point, current frequency, translation table and DPTC
+ * log buffer).
+ */
+static dvfs_dptc_params_s *dvfs_dptc_params;
+
+static struct delayed_work dptc_work;
+
+#ifndef CONFIG_MXC_DVFS_SDMA
+static unsigned long ptvai;
+#endif
+
+/*!
+ * Spinlock to protect CRM register accesses
+ */
+static DEFINE_SPINLOCK(mxc_crm_lock);
+
+/*!
+ * This function is called to read the contents of a CCM_MCU register
+ *
+ * @param reg_offset the CCM_MCU register that will read
+ *
+ * @return the register contents
+ */
+static unsigned long mxc_ccm_get_reg(unsigned int reg_offset)
+{
+ unsigned long reg;
+
+ reg = __raw_readl(reg_offset);
+ return reg;
+}
+
+/*!
+ * This function is called to modify the contents of a CCM_MCU register
+ *
+ * @param reg_offset the CCM_MCU register that will read
+ * @param mask the mask to be used to clear the bits that are to be modified
+ * @param data the data that should be written to the register
+ */
+static void mxc_ccm_modify_reg(unsigned int reg_offset, unsigned int mask,
+ unsigned int data)
+{
+ unsigned long flags;
+ unsigned long reg;
+
+ spin_lock_irqsave(&mxc_crm_lock, flags);
+ reg = __raw_readl(reg_offset);
+ reg = (reg & (~mask)) | data;
+ __raw_writel(reg, reg_offset);
+ spin_unlock_irqrestore(&mxc_crm_lock, flags);
+}
+
+/*!
+ * Enable DPTC hardware
+ */
+static void dptc_enable_dptc(void)
+{
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0, MXC_CCM_PMCR0_DPTEN,
+ MXC_CCM_PMCR0_DPTEN);
+}
+
+/*!
+ * Disable DPTC hardware
+ */
+static void dptc_disable_dptc(void)
+{
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0, MXC_CCM_PMCR0_DPTEN, 0);
+}
+
+/*!
+ * Mask DPTC interrupt
+ */
+static void dptc_mask_dptc_int(void)
+{
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0, MXC_CCM_PMCR0_PTVAIM,
+ MXC_CCM_PMCR0_PTVAIM);
+}
+
+/*!
+ * Unmask DPTC interrupt
+ */
+static void dptc_unmask_dptc_int(void)
+{
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0, MXC_CCM_PMCR0_PTVAIM, 0);
+}
+
+/*!
+ * Read the PTVAI bits from the CCM
+ *
+ * @return PTVAI bits value
+ */
+static unsigned long dptc_get_ptvai(void)
+{
+ return (mxc_ccm_get_reg(MXC_CCM_PMCR0) & MXC_CCM_PMCR0_PTVAI_MASK)
+ >> MXC_CCM_PMCR0_PTVAI_OFFSET;
+}
+
+/*!
+ * Clear DCR bits of the CCM
+ */
+static void dptc_clear_dcr(void)
+{
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0, MXC_CCM_PMCR0_DCR, 0);
+}
+
+/*!
+ * If value equal to PTVAI bits indicates working point decrease
+ */
+#define DPTC_DECREASE (unsigned long)0x1
+
+/*!
+ * If value equal to PTVAI bits indicates working point increase
+ */
+#define DPTC_INCREASE (unsigned long)0x2
+
+/*!
+ * If value equal to PTVAI bits indicates working point increase to maximum
+ */
+#define DPTC_EMERG (unsigned long)0x3
+
+#ifdef CONFIG_MXC_DVFS
+#ifndef CONFIG_MXC_DVFS_SDMA
+/*
+ * MCU will get DPTC interrupt
+ */
+static void dptc_set_ptvis(void)
+{
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0, MXC_CCM_PMCR0_PTVIS,
+ MXC_CCM_PMCR0_PTVIS);
+}
+#endif
+#endif
+
+/*!
+ * This function enables the DPTC reference circuits.
+ *
+ * @param params pointer to the DVFS & DPTC driver parameters structure.
+ * @param rc_state each high bit specifies which
+ * reference circuite to enable
+ * @return 0 on success, error code on failure
+ */
+int enable_ref_circuits(dvfs_dptc_params_s * params, unsigned char rc_state)
+{
+ int ret_val;
+
+ if (params->dptc_is_active == FALSE) {
+ params->rc_state = rc_state;
+
+ if (rc_state & 0x1) {
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0, MXC_CCM_PMCR0_DRCE0,
+ MXC_CCM_PMCR0_DRCE0);
+ pr_debug("Ref circuit 0 enabled\n");
+ }
+ if (rc_state & 0x2) {
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0, MXC_CCM_PMCR0_DRCE1,
+ MXC_CCM_PMCR0_DRCE1);
+ pr_debug("Ref circuit 1 enabled\n");
+ }
+ if (rc_state & 0x4) {
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0, MXC_CCM_PMCR0_DRCE2,
+ MXC_CCM_PMCR0_DRCE2);
+ pr_debug("Ref circuit 2 enabled\n");
+ }
+ if (rc_state & 0x8) {
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0, MXC_CCM_PMCR0_DRCE3,
+ MXC_CCM_PMCR0_DRCE3);
+ pr_debug("Ref circuit 3 enabled\n");
+ }
+
+ ret_val = 0;
+ } else {
+ ret_val = -EINVAL;
+ }
+
+ return ret_val;
+}
+
+/*!
+ * This function disables the DPTC reference circuits.
+ *
+ * @param params pointer to the DVFS & DPTC driver parameters structure.
+ * @param rc_state each high bit specifies which
+ * reference circuite to disable
+ * @return 0 on success, error code on failure
+ */
+int disable_ref_circuits(dvfs_dptc_params_s * params, unsigned char rc_state)
+{
+ int ret_val;
+
+ if (params->dptc_is_active == FALSE) {
+ params->rc_state &= ~rc_state;
+
+ if (rc_state & 0x1) {
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0,
+ MXC_CCM_PMCR0_DRCE0, 0);
+ pr_debug("Ref circuit 0 disabled\n");
+ }
+ if (rc_state & 0x2) {
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0,
+ MXC_CCM_PMCR0_DRCE1, 0);
+ pr_debug("Ref circuit 1 disabled\n");
+ }
+ if (rc_state & 0x4) {
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0,
+ MXC_CCM_PMCR0_DRCE2, 0);
+ pr_debug("Ref circuit 2 disabled\n");
+ }
+ if (rc_state & 0x8) {
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0,
+ MXC_CCM_PMCR0_DRCE3, 0);
+ pr_debug("Ref circuit 3 disabled\n");
+ }
+
+ ret_val = 0;
+ } else {
+ ret_val = -EINVAL;
+ }
+
+ return ret_val;
+}
+
+static void dptc_workqueue_handler(struct work_struct *work)
+{
+ dvfs_dptc_params_s *params;
+
+ params = (dvfs_dptc_params_s *) work_data_bits(work);
+
+ pr_debug("In %s: PTVAI = %lu\n", __FUNCTION__, dptc_get_ptvai());
+ pr_debug("PMCR0 = 0x%lx ", mxc_ccm_get_reg(MXC_CCM_PMCR0));
+ pr_debug("DCVR0 = 0x%lx ", mxc_ccm_get_reg(MXC_CCM_DCVR0));
+ pr_debug("DCVR1 = 0x%lx ", mxc_ccm_get_reg(MXC_CCM_DCVR1));
+ pr_debug("DCVR2 = 0x%lx ", mxc_ccm_get_reg(MXC_CCM_DCVR2));
+ pr_debug("DCVR3 = 0x%lx ", mxc_ccm_get_reg(MXC_CCM_DCVR3));
+ pr_debug("PTVAI = 0x%lx\n", ptvai);
+}
+
+/*!
+ * This function is the DPTC Interrupt handler.
+ * This function wakes-up the dptc_workqueue_handler function that handles the
+ * DPTC interrupt.
+ */
+void dptc_irq(void)
+{
+#ifndef CONFIG_MXC_DVFS_SDMA
+ ptvai = dptc_get_ptvai();
+
+ pr_debug("ptvai = 0x%lx (0x%x)!!!!!!!\n", ptvai,
+ __raw_readl(MXC_CCM_PMCR0));
+ if (ptvai != 0) {
+ dptc_mask_dptc_int();
+ dptc_disable_dptc();
+
+ schedule_delayed_work(&dptc_work, 0);
+ }
+#else
+ schedule_delayed_work(&dptc_work, 0);
+#endif
+}
+
+/*!
+ * This function updates the CPU voltage, produced by mc13783, by calling mc13783
+ * driver functions.
+ *
+ * @param dvfs_dptc_tables_ptr pointer to the DPTC translation table.
+ * @param wp current wp value.
+ * frequency.
+ *
+ */
+void set_pmic_voltage(dvfs_dptc_tables_s * dvfs_dptc_tables_ptr, int wp)
+{
+ /* Call mc13783 functions */
+ t_regulator_voltage volt;
+
+ /* Normal mode setting */
+ volt.sw1a = dvfs_dptc_tables_ptr->wp[wp].pmic_values[0];
+ pmic_power_regulator_set_voltage(SW_SW1A, volt);
+
+#ifdef CONFIG_MXC_DVFS
+ volt.sw1a = dvfs_dptc_tables_ptr->wp[wp].pmic_values[1];
+ pmic_power_switcher_set_dvs(SW_SW1A, volt);
+
+ volt.sw1b = dvfs_dptc_tables_ptr->wp[wp].pmic_values[2];
+ pmic_power_switcher_set_dvs(SW_SW1B, volt);
+
+ volt.sw1b = dvfs_dptc_tables_ptr->wp[wp].pmic_values[3];
+ pmic_power_switcher_set_stby(SW_SW1B, volt);
+#endif
+
+ if (cpu_is_mx31()) {
+ pr_debug("DPVV = 0x%lx (0x%lx)\n",
+ mxc_ccm_get_reg(MXC_CCM_PMCR0) & MXC_CCM_PMCR0_DPVV,
+ mxc_ccm_get_reg(MXC_CCM_PMCR0));
+ }
+}
+
+/*!
+ * This function enables the DPTC module. this function updates the DPTC
+ * thresholds, updates the mc13783, unmasks the DPTC interrupt and enables
+ * the DPTC module
+ *
+ * @param params pointer to the DVFS & DPTC driver parameters structure.
+ *
+ * @return 0 if DPTC module was enabled else returns -EINVAL.
+ */
+int start_dptc(dvfs_dptc_params_s * params)
+{
+ int freq_index = 0;
+
+ /* Check if DPTC module isn't already active */
+ if (params->dptc_is_active == FALSE) {
+
+ enable_ref_circuits(params, params->rc_state);
+ disable_ref_circuits(params, ~params->rc_state);
+
+ /*
+ * Set the DPTC thresholds and mc13783 voltage to
+ * correspond to the current working point and frequency.
+ */
+ set_pmic_voltage(params->dvfs_dptc_tables_ptr,
+ params->dvfs_dptc_tables_ptr->curr_wp);
+
+#ifdef CONFIG_MXC_DVFS
+ freq_index = dvfs_get_dvsup();
+#endif
+
+ update_dptc_thresholds(params->dvfs_dptc_tables_ptr,
+ params->dvfs_dptc_tables_ptr->curr_wp,
+ freq_index);
+
+ /* Mark DPCT module as active */
+ params->dptc_is_active = TRUE;
+
+ /* Enable the DPTC module and unmask the DPTC interrupt */
+ dptc_enable_dptc();
+ dptc_unmask_dptc_int();
+
+ return 0;
+ }
+
+ /* DPTC module already active return error */
+ return -EINVAL;
+}
+
+/*!
+ * This function disables the DPTC module.
+ *
+ * @param params pointer to the DVFS & DPTC driver parameters structure.
+ *
+ * @return 0 if DPTC module was disabled else returns -EINVAL.
+ */
+int stop_dptc(dvfs_dptc_params_s * params)
+{
+ /* Check if DPTC module isn't already disabled */
+ if (params->dptc_is_active != FALSE) {
+
+ /* Disable the DPTC module and mask the DPTC interrupt */
+ dptc_disable_dptc();
+ dptc_mask_dptc_int();
+
+ /* Set working point 0 */
+ set_dptc_wp(params, 0);
+
+ /* Mark DPCT module as inactive */
+ params->dptc_is_active = FALSE;
+
+ return 0;
+ }
+
+ /* DPTC module already disabled, return error */
+ return -EINVAL;
+}
+
+/*!
+ * This function updates the drivers current working point index. This index is
+ * used for access the current DTPC table entry and it corresponds to the
+ * current CPU working point measured by the DPTC hardware.
+ *
+ * @param params pointer to the DVFS & DPTC driver parameters structure.
+ * @param new_wp New working point index value to be set.
+ *
+ */
+void set_dptc_wp(dvfs_dptc_params_s * params, int new_wp)
+{
+ int freq_index = 0;
+
+#ifdef CONFIG_MXC_DVFS
+ freq_index = dvfs_get_dvsup();
+#endif
+
+ /*
+ * Check if new index is smaller than the maximal working point
+ * index in the DPTC translation table and larger that 0.
+ */
+ if ((new_wp < params->dvfs_dptc_tables_ptr->wp_num)
+ && (new_wp >= 0)) {
+ /* Set current working point index to new index */
+ params->dvfs_dptc_tables_ptr->curr_wp = new_wp;
+ }
+
+ /*
+ * Check if new index is larger than the maximal working point index in
+ * the DPTC translation table.
+ */
+ if (new_wp >= params->dvfs_dptc_tables_ptr->wp_num) {
+ /*
+ * Set current working point index to maximal working point
+ * index in the DPTC translation table.
+ */
+ params->dvfs_dptc_tables_ptr->curr_wp =
+ params->dvfs_dptc_tables_ptr->wp_num - 1;
+ }
+
+ /* Check if new index is smaller than 0. */
+ if (new_wp < 0) {
+ /* Set current working point index to 0 (minimal value) */
+ params->dvfs_dptc_tables_ptr->curr_wp = 0;
+ }
+#ifndef CONFIG_MXC_DVFS_SDMA
+ /* Update the DPTC hardware thresholds */
+ update_dptc_thresholds(params->dvfs_dptc_tables_ptr,
+ params->dvfs_dptc_tables_ptr->curr_wp,
+ freq_index);
+#else
+ params->prev_wp = params->dvfs_dptc_tables_ptr->curr_wp;
+#endif
+
+ /* Update the mc13783 voltage */
+ set_pmic_voltage(params->dvfs_dptc_tables_ptr,
+ params->dvfs_dptc_tables_ptr->curr_wp);
+
+ /* Write DPTC changes in the DPTC log buffer */
+ add_dptc_log_entry(params, &params->dptc_log_buffer,
+ params->dvfs_dptc_tables_ptr->curr_wp, freq_index);
+
+ pr_debug("Current wp: %d\n", params->dvfs_dptc_tables_ptr->curr_wp);
+}
+
+/*!
+ * This function updates the DPTC threshold registers.
+ *
+ * @param dvfs_dptc_tables_ptr pointer to the DPTC translation table.
+ * @param wp current wp value.
+ * @param freq_index translation table index of the current CPU
+ * frequency.
+ *
+ */
+void update_dptc_thresholds(dvfs_dptc_tables_s * dvfs_dptc_tables_ptr,
+ int wp, int freq_index)
+{
+ dcvr_state *dcvr;
+
+#ifdef CONFIG_MXC_DVFS_SDMA
+
+ dcvr_state **dcvr_arr;
+ dcvr_state *curr_freq_dcvr;
+
+ dcvr_arr = dvfs_dptc_tables_ptr->dcvr;
+ dcvr_arr = sdma_phys_to_virt((unsigned long)dcvr_arr);
+ curr_freq_dcvr = dcvr_arr[freq_index];
+ curr_freq_dcvr = sdma_phys_to_virt((unsigned long)curr_freq_dcvr);
+ dcvr = &curr_freq_dcvr[wp];
+#else
+ /* Calculate current table entry offset in the DPTC translation table */
+ dcvr = &dvfs_dptc_tables_ptr->dcvr[freq_index][wp];
+#endif
+ /* Update DPTC threshold registers */
+ mxc_ccm_modify_reg(MXC_CCM_DCVR0, 0xffffffff, dcvr->dcvr_reg[0].AsInt);
+ mxc_ccm_modify_reg(MXC_CCM_DCVR1, 0xffffffff, dcvr->dcvr_reg[1].AsInt);
+ mxc_ccm_modify_reg(MXC_CCM_DCVR2, 0xffffffff, dcvr->dcvr_reg[2].AsInt);
+ mxc_ccm_modify_reg(MXC_CCM_DCVR3, 0xffffffff, dcvr->dcvr_reg[3].AsInt);
+}
+
+/*!
+ * This function increments a log buffer index (head or tail)
+ * by the value of val.
+ *
+ * @param index pointer to the DPTC log buffer index that
+ * we wish to change.
+ * @param val the value in which the index should be incremented.
+ *
+ */
+static void inc_log_index(int *index, int val)
+{
+ *index = (*index + val) % LOG_ENTRIES;
+}
+
+/*!
+ * This function returns the number of entries in the DPTC log buffer.
+ *
+ * @param dptc_log pointer to the DPTC log buffer structure.
+ *
+ * @return number of log buffer entries.
+ *
+ */
+static int get_entry_count(dptc_log_s * dptc_log)
+{
+ return ((dptc_log->head - dptc_log->tail + LOG_ENTRIES) % LOG_ENTRIES);
+}
+
+/*!
+ * This function is used by the proc file system to read the DPTC log buffer.
+ * Each time the DPTC proc file system file is read this function is called
+ * and returns the data written in the log buffer.
+ *
+ * @param buf pointer to the buffer the data should be written to.
+ * @param start pointer to the pointer where the new data is
+ * written to.
+ * procedure should update the start pointer to point to
+ * where in the buffer the data was written.
+ * @param offset current offset in the DPTC proc file.
+ * @param count number of bytes to read.
+ * @param eof pointer to eof flag. should be set to 1 when
+ * reaching eof.
+ * @param data driver specific data pointer.
+ *
+ * @return number byte read from the log buffer.
+ *
+ */
+static int read_log(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data)
+{
+ int entries_to_read;
+ int num_of_entries;
+ int entries_to_end_of_buffer, entries_left;
+ void *entry_ptr;
+ char *buf_ptr;
+ dvfs_dptc_params_s *params;
+
+ params = (dvfs_dptc_params_s *) data;
+
+ /* Calculate number of log entries to read */
+ entries_to_read = count / sizeof(dptc_log_entry_s);
+ /* Get the number of current log buffer entries */
+ num_of_entries = get_entry_count(&params->dptc_log_buffer);
+
+ /*
+ * If number of entries to read is larger that the number of entries
+ * in the log buffer set number of entries to read to number of
+ * entries in the log buffer and set eof flag to 1
+ */
+ if (num_of_entries < entries_to_read) {
+ entries_to_read = num_of_entries;
+ *eof = 1;
+ }
+
+ /*
+ * Down the log buffer mutex to exclude others from reading and
+ * writing to the log buffer.
+ */
+ if (down_interruptible(&params->dptc_log_buffer.mutex)) {
+ return -EAGAIN;
+ }
+
+ if (num_of_entries == 0 && offset == 0) {
+ inc_log_index(&params->dptc_log_buffer.tail, -1);
+ num_of_entries++;
+ entries_to_read++;
+ }
+
+ /* get the pointer of the last (oldest) entry in the log buffer */
+ entry_ptr = (void *)&params->dptc_log_buffer.
+ entries[params->dptc_log_buffer.tail];
+
+ /* Check if tail index wraps during current read */
+ if ((params->dptc_log_buffer.tail + entries_to_read) < LOG_ENTRIES) {
+ /* No tail wrap around copy data from log buffer to buf */
+ memcpy(buf, entry_ptr,
+ (entries_to_read * sizeof(dptc_log_entry_s)));
+ } else {
+ /*
+ * Tail wrap around.
+ * First copy data from current position until end of buffer,
+ * after that copy the rest from start of the log buffer.
+ */
+ entries_to_end_of_buffer = LOG_ENTRIES -
+ params->dptc_log_buffer.tail;
+ memcpy(buf, entry_ptr,
+ (entries_to_end_of_buffer * sizeof(dptc_log_entry_s)));
+
+ entry_ptr = (void *)&params->dptc_log_buffer.entries[0];
+ buf_ptr = buf +
+ (entries_to_end_of_buffer * sizeof(dptc_log_entry_s));
+ entries_left = entries_to_read - entries_to_end_of_buffer;
+ memcpy(buf_ptr, entry_ptr,
+ (entries_left * sizeof(dptc_log_entry_s)));
+ }
+
+ /* Increment the tail index by the number of entries read */
+ inc_log_index(&params->dptc_log_buffer.tail, entries_to_read);
+
+ /* Up the log buffer mutex to allow access to the log buffer */
+ up(&params->dptc_log_buffer.mutex);
+
+ /* set start of data to point to buf */
+ *start = buf;
+
+ return (entries_to_read * sizeof(dptc_log_entry_s));
+}
+
+/*!
+ * This function initializes the DPTC log buffer.
+ *
+ * @param params pointer to \b dvfs_dptc_params_s.
+ * @param dptc_log pointer to the DPTC log buffer structure.
+ *
+ */
+void init_dptc_log(dvfs_dptc_params_s * params, dptc_log_s * dptc_log)
+{
+ dptc_log->tail = 0;
+ dptc_log->head = 0;
+
+ /* initialize log buffer mutex used for accessing the log buffer */
+ sema_init(&dptc_log->mutex, 1);
+
+ /* add the first log buffer entry */
+ add_dptc_log_entry(params, dptc_log,
+ params->dvfs_dptc_tables_ptr->curr_wp,
+ params->current_freq_index);
+}
+
+/*!
+ * This function adds a new entry to the DPTC log buffer.
+ *
+ * @param params pointer to the DVFS & DPTC driver parameters structure.
+ * @param dptc_log pointer to the DPTC log buffer structure.
+ * @param wp value of the working point index written
+ * to the log buffer.
+ * @param freq_index value of the frequency index written to
+ * the log buffer.
+ *
+ * @return number of log buffer entries.
+ *
+ */
+void add_dptc_log_entry(dvfs_dptc_params_s * params,
+ dptc_log_s * dptc_log, int wp, int freq_index)
+{
+ /*
+ * Down the log buffer mutex to exclude others from reading and
+ * writing to the log buffer.
+ */
+ if (down_interruptible(&dptc_log->mutex)) {
+ return;
+ }
+
+ /* Write values to log buffer */
+ dptc_log->entries[dptc_log->head].jiffies = jiffies;
+ dptc_log->entries[dptc_log->head].wp = wp;
+
+ dptc_log->entries[dptc_log->head].voltage = wp;
+
+#ifdef CONFIG_MXC_DVFS
+ freq_index = dptc_get_ptvai();
+#endif
+
+ dptc_log->entries[dptc_log->head].freq = freq_index;
+
+ /* Increment the head index by 1 */
+ inc_log_index(&dptc_log->head, 1);
+ /* If head index reaches the tail increment the tail by 1 */
+ if (dptc_log->head == dptc_log->tail) {
+ inc_log_index(&dptc_log->tail, 1);
+ }
+
+ /* Up the log buffer mutex to allow access to the log buffer */
+ up(&dptc_log->mutex);
+}
+
+/*!
+ * This function is called to put the DPTC in a low power state.
+ *
+ */
+void dptc_suspend(void)
+{
+#ifdef CONFIG_MXC_DPTC
+ if (dvfs_dptc_params->dptc_is_active) {
+ dptc_disable_dptc();
+ set_dptc_wp(dvfs_dptc_params,
+ dvfs_dptc_params->
+ dvfs_dptc_tables_ptr->curr_wp - 1);
+ }
+#endif
+}
+
+/*!
+ * This function is called to resume the DPTC from a low power state.
+ *
+ */
+void dptc_resume(void)
+{
+#ifdef CONFIG_MXC_DPTC
+ if (dvfs_dptc_params->dptc_is_active) {
+ dptc_enable_dptc();
+ dptc_unmask_dptc_int();
+ }
+#endif
+}
+
+/*!
+ * This function initializes the DPTC hardware
+ *
+ * @param params pointer to the DPTC driver parameters structure.
+ *
+ */
+int __init init_dptc_controller(dvfs_dptc_params_s * params)
+{
+ dvfs_dptc_params = params;
+
+ INIT_DELAYED_WORK(&dptc_work, dptc_workqueue_handler);
+
+ if (create_proc_read_entry(PROC_NODE_NAME, 0,
+ NULL, read_log, params) == NULL) {
+ /*
+ * Error creating proc file system entry.
+ * Exit and return error code
+ */
+ printk(KERN_ERR "DPTC: Unable create proc entry");
+ return -EFAULT;
+ }
+
+ /* Initialize the DPTC log buffer */
+ init_dptc_log(params, &params->dptc_log_buffer);
+
+ set_dptc_curr_freq(params, 0);
+ set_dptc_wp(params, 0);
+
+ /* By default all reference circuits are enabled */
+ params->rc_state = DPTC_REF_CIRCUITS_STATUS;
+
+ dptc_clear_dcr();
+
+ /* Disable DPTC hardware and mask DPTC interrupt */
+ dptc_disable_dptc();
+ dptc_mask_dptc_int();
+
+#ifdef CONFIG_MXC_DVFS
+#ifndef CONFIG_MXC_DVFS_SDMA
+ dptc_set_ptvis();
+#endif
+#endif
+ printk(KERN_INFO "DPTC controller initialized\n");
+
+ return 0;
+}
+
+/*!
+ * This function updates the drivers current frequency index.This index is
+ * used for access the current DTPC table entry and it corresponds to the
+ * current CPU frequency (each CPU frequency has a separate index number
+ * according to the loaded DPTC table).
+ *
+ * @param params pointer to the DVFS & DPTC driver parameters structure.
+ * @param freq_index New frequency index value to be set.
+ *
+ * @return 0 if the frequency index was updated (the new index is a
+ * valid index and the DPTC module isn't active) else returns
+ * -EINVAL.
+ *
+ */
+int set_dptc_curr_freq(dvfs_dptc_params_s * params, unsigned int freq_index)
+{
+ /*
+ * Check if the new index value is a valid frequency index (smaller
+ * than the maximal index in the DPTC table) and if the DPTC module
+ * is disabled.
+ */
+ if ((freq_index < params->dvfs_dptc_tables_ptr->dvfs_state_num)
+ && (params->dptc_is_active == FALSE)) {
+ /*
+ * Index is valid and DPTC module is
+ * disabled -> change frequency index.
+ */
+ params->current_freq_index = freq_index;
+ add_dptc_log_entry(params, &params->dptc_log_buffer,
+ params->dvfs_dptc_tables_ptr->curr_wp,
+ params->current_freq_index);
+
+ return 0;
+ }
+
+ /* Invalid index or DPTC module is active -> return error */
+ return -EINVAL;
+}
+
+#ifdef CONFIG_MXC_DVFS_SDMA
+/*
+ * DPTC SDMA callback.
+ * Updates the mc13783 voltage
+ *
+ * @param params pointer to the DVFS & DPTC driver parameters structure.
+ */
+void dptc_sdma_callback(dvfs_dptc_params_s * params)
+{
+ printk(KERN_INFO "In %s: params->dvfs_dptc_tables_ptr->curr_wp = %d\n",
+ __FUNCTION__, params->dvfs_dptc_tables_ptr->curr_wp);
+}
+#endif
+
+/*!
+ * This function is called to put the DPTC in a low power state.
+ *
+ * @param pdev the device structure used to give information on which
+ * device to suspend (not relevant for DPTC)
+ * @param state the power state the device is entering
+ *
+ * @return The function always returns 0.
+ */
+int mxc_dptc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ dptc_suspend();
+ return 0;
+}
+
+/*!
+ * This function is called to resume the DPTC from a low power state.
+ *
+ * @param pdev the device structure used to give information on which
+ * device to suspend (not relevant for DPTC)
+ *
+ * @return The function always returns 0.
+ */
+int mxc_dptc_resume(struct platform_device *pdev)
+{
+ dptc_resume();
+ return 0;
+}
+
+EXPORT_SYMBOL(dptc_suspend);
+EXPORT_SYMBOL(dptc_resume);
diff --git a/drivers/mxc/pm/dptc_mx27.c b/drivers/mxc/pm/dptc_mx27.c
new file mode 100644
index 000000000000..138adc26a8a9
--- /dev/null
+++ b/drivers/mxc/pm/dptc_mx27.c
@@ -0,0 +1,1416 @@
+/*
+ * Copyright 2004-2007 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 dptc_mx27.c
+ *
+ * @brief Driver for the Freescale Semiconductor MX27 DPTC module.
+ *
+ * The DPTC driver is designed as a character driver which interacts with the
+ * MX27 DPTC hardware. Upon initialization, the DPTC driver initializes the
+ * DPTC hardware sets up driver nodes attaches to the DPTC interrupt and
+ * initializes internal data structures. When the DPTC interrupt occurs the
+ * driver checks the cause of the interrupt (lower voltage, increase voltage or
+ * emergency) and changes the CPU voltage according to translation table that
+ * is loaded into the driver(the voltage changes are done by calling some
+ * routines in the mc13783 driver). The driver read method is used to read the
+ * currently loaded DPTC translation table and the write method is used
+ * in-order to update the translation table. Driver ioctls are used to change
+ * driver parameters and enable/disable the DPTC operation.
+ *
+ * @ingroup PM_MX27
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <asm/uaccess.h>
+#include <linux/workqueue.h>
+#include <linux/proc_fs.h>
+#include <asm/semaphore.h>
+#include <linux/vmalloc.h>
+#include <asm/arch/pmic_power.h>
+#include <asm/arch/dvfs_dptc_struct.h>
+#include "dvfs_dptc.h"
+
+#define MX27_PMCR_BASE_ADDR (SYSCTRL_BASE_ADDR + 0x60)
+#define MX27_DCVR_BASE_ADDR (SYSCTRL_BASE_ADDR + 0x64)
+#define MX27_DCVR0_OFFSET 0
+#define MX27_DCVR1_OFFSET 4
+#define MX27_DCVR2_OFFSET 8
+#define MX27_DCVR3_OFFSET 12
+
+#define MX27_PMCR_MC_STATUS_OFFSET 31
+#define MX27_PMCR_MC_STATUS_MASK (1 << 31)
+#define MX27_PMCR_EM_INTR_OFFSET 30
+#define MX27_PMCR_EM_INTR_MASK (1 << 30)
+#define MX27_PMCR_UP_INTR_OFFSET 29
+#define MX27_PMCR_UP_INTR_MASK (1 << 29)
+#define MX27_PMCR_LO_INTR_OFFSET 28
+#define MX27_PMCR_LO_INTR_MASK (1 << 28)
+#define MX27_PMCR_REFCNT_OFFSET 16
+#define MX27_PMCR_REFCNT_MASK (0x7FF << 16)
+#define MX27_PMCR_DCR_OFFSET 9
+#define MX27_PMCR_DCR_MASK (1 << 9)
+#define MX27_PMCR_RCLKON_OFFSET 8
+#define MX27_PMCR_RCLKON_MASK (1 << 8)
+#define MX27_PMCR_DRCE3_OFFSET 7
+#define MX27_PMCR_DRCE3_MASK (1 << 7)
+#define MX27_PMCR_DRCE2_OFFSET 6
+#define MX27_PMCR_DRCE2_MASK (1 << 6)
+#define MX27_PMCR_DRCE1_OFFSET 5
+#define MX27_PMCR_DRCE1_MASK (1 << 5)
+#define MX27_PMCR_DRCE0_OFFSET 4
+#define MX27_PMCR_DRCE0_MASK (1 << 4)
+#define MX27_PMCR_DIM_OFFSET 2
+#define MX27_PMCR_DIM_MASK (0x3 << 2)
+#define MX27_PMCR_DIE_OFFSET 1
+#define MX27_PMCR_DIE_MASK (1 << 1)
+#define MX27_PMCR_DPTEN_OFFSET 0
+#define MX27_PMCR_DPTEN_MASK (1 << 0)
+
+/*!
+ * DPTC proc file system entry name
+ */
+#define PROC_NODE_NAME "dptc"
+
+/*
+ * Prototypes
+ */
+static int dptc_mx27_open(struct inode *inode, struct file *filp);
+static int dptc_mx27_release(struct inode *inode, struct file *filp);
+static int dptc_mx27_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+static irqreturn_t dptc_mx27_irq(int irq, void *dev_id);
+
+/*
+ * Global variables
+ */
+
+/*!
+ * The dvfs_dptc_params structure holds all the internal DPTC driver parameters
+ * (current working point, current frequency, translation table and DPTC
+ * log buffer).
+ */
+static dvfs_dptc_params_s dptc_params;
+
+static struct work_struct dptc_work;
+static volatile u32 dptc_intr_status;
+static u32 dptc_initial_wp;
+
+/*!
+ * Holds the automatically selected DPTC driver major number.
+ */
+static int major;
+
+static struct class *mxc_dvfs_dptc_class;
+
+/*
+ * This mutex makes the Read,Write and IOCTL command mutual exclusive.
+ */
+DECLARE_MUTEX(access_mutex);
+
+/*!
+ * This structure contains pointers for device driver entry point.
+ * The driver register function in init module will call this
+ * structure.
+ */
+static struct file_operations fops = {
+ .open = dptc_mx27_open,
+ .release = dptc_mx27_release,
+ .ioctl = dptc_mx27_ioctl,
+};
+
+static
+void mx27_pmcr_modify_reg(u32 mask, u32 val)
+{
+ volatile u32 reg;
+ reg = __raw_readl(IO_ADDRESS(MX27_PMCR_BASE_ADDR));
+ reg = (reg & (~mask)) | val;
+ __raw_writel(reg, IO_ADDRESS(MX27_PMCR_BASE_ADDR));
+}
+
+static
+void mx27_dcvr_modify_reg(u32 offset, u32 val)
+{
+ __raw_writel(val, IO_ADDRESS(MX27_DCVR_BASE_ADDR) + offset);
+}
+
+/*!
+ * Enable DPTC hardware
+ */
+static void dptc_enable_dptc(void)
+{
+ mx27_pmcr_modify_reg(MX27_PMCR_DPTEN_MASK, 1);
+}
+
+/*!
+ * Disable DPTC hardware
+ */
+static void dptc_disable_dptc(void)
+{
+ mx27_pmcr_modify_reg(MX27_PMCR_DPTEN_MASK, 0);
+}
+
+/*!
+ * Mask DPTC interrupt
+ */
+static void dptc_mask_dptc_int(void)
+{
+ mx27_pmcr_modify_reg(MX27_PMCR_DIE_MASK, (0 << MX27_PMCR_DIE_OFFSET));
+}
+
+/*!
+ * Unmask DPTC interrupt
+ */
+static void dptc_unmask_dptc_int(void)
+{
+ mx27_pmcr_modify_reg(MX27_PMCR_DIE_MASK, (1 << MX27_PMCR_DIE_OFFSET));
+}
+
+/*!
+ * Clear DCR bits of the CCM
+ */
+static void dptc_clear_dcr(void)
+{
+ mx27_pmcr_modify_reg(MX27_PMCR_DCR_MASK, (0 << MX27_PMCR_DCR_OFFSET));
+}
+
+/*!
+ * This function enables the DPTC reference circuits.
+ *
+ * @param rc_state each high bit specifies which
+ * reference circuite to enable
+ * @return 0 on success, error code on failure
+ */
+static int enable_ref_circuits(unsigned char rc_state)
+{
+ int ret_val;
+
+ if (dptc_params.dptc_is_active == FALSE) {
+ dptc_params.rc_state = rc_state;
+
+ if (rc_state & 0x1) {
+ mx27_pmcr_modify_reg(MX27_PMCR_DRCE0_MASK,
+ (1 << MX27_PMCR_DRCE0_OFFSET));
+ }
+ if (rc_state & 0x2) {
+ mx27_pmcr_modify_reg(MX27_PMCR_DRCE1_MASK,
+ (1 << MX27_PMCR_DRCE1_OFFSET));
+ }
+ if (rc_state & 0x4) {
+ mx27_pmcr_modify_reg(MX27_PMCR_DRCE2_MASK,
+ (1 << MX27_PMCR_DRCE2_OFFSET));
+ }
+ if (rc_state & 0x8) {
+ mx27_pmcr_modify_reg(MX27_PMCR_DRCE3_MASK,
+ (1 << MX27_PMCR_DRCE3_OFFSET));
+ }
+
+ ret_val = 0;
+ } else {
+ ret_val = -EINVAL;
+ }
+
+ return ret_val;
+}
+
+/*!
+ * This function disables the DPTC reference circuits.
+ *
+ * @param rc_state each high bit specifies which
+ * reference circuite to disable
+ * @return 0 on success, error code on failure
+ */
+static int disable_ref_circuits(unsigned char rc_state)
+{
+ int ret_val;
+
+ if (dptc_params.dptc_is_active == FALSE) {
+ dptc_params.rc_state &= ~rc_state;
+
+ if (rc_state & 0x1) {
+ mx27_pmcr_modify_reg(MX27_PMCR_DRCE0_MASK,
+ (0 << MX27_PMCR_DRCE0_OFFSET));
+ }
+ if (rc_state & 0x2) {
+ mx27_pmcr_modify_reg(MX27_PMCR_DRCE1_MASK,
+ (0 << MX27_PMCR_DRCE1_OFFSET));
+ }
+ if (rc_state & 0x4) {
+ mx27_pmcr_modify_reg(MX27_PMCR_DRCE2_MASK,
+ (0 << MX27_PMCR_DRCE2_OFFSET));
+ }
+ if (rc_state & 0x8) {
+ mx27_pmcr_modify_reg(MX27_PMCR_DRCE3_MASK,
+ (0 << MX27_PMCR_DRCE3_OFFSET));
+ }
+
+ ret_val = 0;
+ } else {
+ ret_val = -EINVAL;
+ }
+
+ return ret_val;
+}
+
+/*!
+ * This function updates the CPU voltage, produced by MC13783, by calling
+ * MC13783 driver functions.
+ *
+ * @param dvfs_dptc_tables_ptr pointer to the DPTC translation table.
+ * @param wp current wp value.
+ *
+ */
+static void set_pmic_voltage(dvfs_dptc_tables_s * dvfs_dptc_tables_ptr, int wp)
+{
+ /* Call MC13783 functions */
+ t_regulator_voltage volt;
+
+ volt.sw1a = dvfs_dptc_tables_ptr->wp[wp].pmic_values[0];
+ pmic_power_regulator_set_voltage(SW_SW1A, volt);
+}
+
+/*!
+ * This function updates the DPTC threshold registers.
+ *
+ * @param dvfs_dptc_tables_ptr pointer to the DPTC translation table.
+ * @param wp current wp value.
+ *
+ */
+static void update_dptc_thresholds(dvfs_dptc_tables_s * dvfs_dptc_tables_ptr,
+ int wp)
+{
+ dcvr_state *dcvr;
+
+ /* Calculate current table entry offset in the DPTC translation table */
+ dcvr = &dvfs_dptc_tables_ptr->dcvr[0][wp];
+
+ /* Update DPTC threshold registers */
+ mx27_dcvr_modify_reg(MX27_DCVR0_OFFSET, dcvr->dcvr_reg[0].AsInt);
+ mx27_dcvr_modify_reg(MX27_DCVR1_OFFSET, dcvr->dcvr_reg[1].AsInt);
+ mx27_dcvr_modify_reg(MX27_DCVR2_OFFSET, dcvr->dcvr_reg[2].AsInt);
+ mx27_dcvr_modify_reg(MX27_DCVR3_OFFSET, dcvr->dcvr_reg[3].AsInt);
+}
+
+/*!
+ * This function increments a log buffer index (head or tail)
+ * by the value of val.
+ *
+ * @param index pointer to the DPTC log buffer index that
+ * we wish to change.
+ * @param val the value in which the index should be incremented.
+ *
+ */
+static void inc_log_index(int *index, int val)
+{
+ *index = (*index + val) % LOG_ENTRIES;
+}
+
+/*!
+ * This function adds a new entry to the DPTC log buffer.
+ *
+ * @param dptc_log pointer to the DPTC log buffer structure.
+ * @param wp value of the working point index written
+ * to the log buffer.
+ *
+ * @return number of log buffer entries.
+ *
+ */
+static void add_dptc_log_entry(dptc_log_s * dptc_log, int wp)
+{
+ /*
+ * Down the log buffer mutex to exclude others from reading and
+ * writing to the log buffer.
+ */
+ if (down_interruptible(&dptc_log->mutex)) {
+ return;
+ }
+
+ /* Write values to log buffer */
+ dptc_log->entries[dptc_log->head].jiffies = jiffies;
+ dptc_log->entries[dptc_log->head].wp = wp;
+
+ dptc_log->entries[dptc_log->head].voltage = wp;
+ dptc_log->entries[dptc_log->head].freq = 0;
+
+ /* Increment the head index by 1 */
+ inc_log_index(&dptc_log->head, 1);
+ /* If head index reaches the tail increment the tail by 1 */
+ if (dptc_log->head == dptc_log->tail) {
+ inc_log_index(&dptc_log->tail, 1);
+ }
+
+ /* Up the log buffer mutex to allow access to the log buffer */
+ up(&dptc_log->mutex);
+}
+
+/*!
+ * This function updates the drivers current working point index. This index is
+ * used for access the current DTPC table entry and it corresponds to the
+ * current CPU working point measured by the DPTC hardware.
+ *
+ * @param new_wp New working point index value to be set.
+ *
+ */
+static void set_dptc_wp(int new_wp)
+{
+ /*
+ * Check if new index is smaller than the maximal working point
+ * index in the DPTC translation table and larger that 0.
+ */
+ if ((new_wp < dptc_params.dvfs_dptc_tables_ptr->wp_num)
+ && (new_wp >= 0)) {
+ /* Set current working point index to new index */
+ dptc_params.dvfs_dptc_tables_ptr->curr_wp = new_wp;
+ }
+
+ /*
+ * Check if new index is larger than the maximal working point index in
+ * the DPTC translation table.
+ */
+ if (new_wp >= dptc_params.dvfs_dptc_tables_ptr->wp_num) {
+ /*
+ * Set current working point index to maximal working point
+ * index in the DPTC translation table.
+ */
+ dptc_params.dvfs_dptc_tables_ptr->curr_wp =
+ dptc_params.dvfs_dptc_tables_ptr->wp_num - 1;
+ }
+
+ /* Check if new index is smaller than 0. */
+ if (new_wp < 0) {
+ /* Set current working point index to 0 (minimal value) */
+ dptc_params.dvfs_dptc_tables_ptr->curr_wp = 0;
+ }
+
+ /* Update the DPTC hardware thresholds */
+ update_dptc_thresholds(dptc_params.dvfs_dptc_tables_ptr,
+ dptc_params.dvfs_dptc_tables_ptr->curr_wp);
+
+ /* Update the MC13783 voltage */
+ set_pmic_voltage(dptc_params.dvfs_dptc_tables_ptr,
+ dptc_params.dvfs_dptc_tables_ptr->curr_wp);
+
+ /* Write DPTC changes in the DPTC log buffer */
+ add_dptc_log_entry(&dptc_params.dptc_log_buffer,
+ dptc_params.dvfs_dptc_tables_ptr->curr_wp);
+
+}
+
+static void dptc_workqueue_handler(struct work_struct *work)
+{
+ if (dptc_intr_status & 0x4) {
+ /* Chip working point has increased dramatically,
+ * raise working point to maximum */
+ set_dptc_wp(dptc_params.dvfs_dptc_tables_ptr->curr_wp - 2);
+ } else if (dptc_intr_status & 0x2) {
+ /* Chip working point has increased, raise working point
+ * by one */
+ set_dptc_wp(dptc_params.dvfs_dptc_tables_ptr->curr_wp + 1);
+ } else {
+ /* Chip working point has decreased, lower working point
+ * by one */
+ set_dptc_wp(dptc_params.dvfs_dptc_tables_ptr->curr_wp - 1);
+ }
+
+ /*
+ * If the DPTC module is still active, re-enable
+ * the DPTC hardware
+ */
+ if (dptc_params.dptc_is_active) {
+ dptc_enable_dptc();
+ dptc_unmask_dptc_int();
+ }
+}
+
+/*!
+ * This function enables the DPTC module. this function updates the DPTC
+ * thresholds, updates the MC13783, unmasks the DPTC interrupt and enables
+ * the DPTC module
+ *
+ * @return 0 if DPTC module was enabled else returns -EINVAL.
+ */
+static int start_dptc(void)
+{
+ /* Check if DPTC module isn't already active */
+ if (dptc_params.dptc_is_active == FALSE) {
+
+ enable_ref_circuits(dptc_params.rc_state);
+ disable_ref_circuits(~dptc_params.rc_state);
+
+ /*
+ * Set the DPTC thresholds and MC13783 voltage to
+ * correspond to the current working point and frequency.
+ */
+ set_pmic_voltage(dptc_params.dvfs_dptc_tables_ptr,
+ dptc_params.dvfs_dptc_tables_ptr->curr_wp);
+
+ update_dptc_thresholds(dptc_params.dvfs_dptc_tables_ptr,
+ dptc_params.dvfs_dptc_tables_ptr->
+ curr_wp);
+
+ /* Mark DPTC module as active */
+ dptc_params.dptc_is_active = TRUE;
+
+ /* Enable the DPTC module and unmask the DPTC interrupt */
+ dptc_enable_dptc();
+ dptc_unmask_dptc_int();
+
+ return 0;
+ }
+
+ /* DPTC module already active return error */
+ return -EINVAL;
+}
+
+/*!
+ * This function disables the DPTC module.
+ *
+ * @return 0 if DPTC module was disabled else returns -EINVAL.
+ */
+static int stop_dptc(void)
+{
+ /* Check if DPTC module isn't already disabled */
+ if (dptc_params.dptc_is_active != FALSE) {
+
+ /* Disable the DPTC module and mask the DPTC interrupt */
+ dptc_disable_dptc();
+ dptc_mask_dptc_int();
+
+ /* Set working point to default */
+ set_dptc_wp(dptc_initial_wp);
+
+ /* Mark DPCT module as inactive */
+ dptc_params.dptc_is_active = FALSE;
+
+ return 0;
+ }
+
+ /* DPTC module already disabled, return error */
+ return -EINVAL;
+}
+
+static void init_dptc_wp(void)
+{
+ /* Call MC13783 functions */
+ t_regulator_voltage volt;
+ int i;
+
+ /* Normal mode setting */
+ pmic_power_regulator_get_voltage(SW_SW1A, &volt);
+ for (i = 0; i < dptc_params.dvfs_dptc_tables_ptr->wp_num; i++) {
+ if (volt.sw1a ==
+ dptc_params.dvfs_dptc_tables_ptr->wp[i].pmic_values[0]) {
+ break;
+ }
+ }
+
+ /*
+ * Check if new index is smaller than the maximal working point
+ * index in the DPTC translation table and larger that 0.
+ */
+ if ((i < dptc_params.dvfs_dptc_tables_ptr->wp_num) && (i >= 0)) {
+ /* Set current working point index to new index */
+ dptc_params.dvfs_dptc_tables_ptr->curr_wp = i;
+ }
+
+ /*
+ * Check if new index is larger than the maximal working point index in
+ * the DPTC translation table.
+ */
+ if (i >= dptc_params.dvfs_dptc_tables_ptr->wp_num) {
+ /*
+ * Set current working point index to maximal working point
+ * index in the DPTC translation table.
+ */
+ dptc_params.dvfs_dptc_tables_ptr->curr_wp =
+ dptc_params.dvfs_dptc_tables_ptr->wp_num - 1;
+ }
+
+ dptc_initial_wp = dptc_params.dvfs_dptc_tables_ptr->curr_wp;
+
+ /* Initialize the log buffer */
+ add_dptc_log_entry(&dptc_params.dptc_log_buffer,
+ dptc_params.dvfs_dptc_tables_ptr->curr_wp);
+}
+
+/*!
+ * This function returns the number of entries in the DPTC log buffer.
+ *
+ * @param dptc_log pointer to the DPTC log buffer structure.
+ *
+ * @return number of log buffer entries.
+ *
+ */
+static int get_entry_count(dptc_log_s * dptc_log)
+{
+ return ((dptc_log->head - dptc_log->tail + LOG_ENTRIES) % LOG_ENTRIES);
+}
+
+/*!
+ * This function is used by the proc file system to read the DPTC log buffer.
+ * Each time the DPTC proc file system file is read this function is called
+ * and returns the data written in the log buffer.
+ *
+ * @param buf pointer to the buffer the data should be written to.
+ * @param start pointer to the pointer where the new data is
+ * written to.
+ * procedure should update the start pointer to point to
+ * where in the buffer the data was written.
+ * @param offset current offset in the DPTC proc file.
+ * @param count number of bytes to read.
+ * @param eof pointer to eof flag. should be set to 1 when
+ * reaching eof.
+ * @param data driver specific data pointer.
+ *
+ * @return number byte read from the log buffer.
+ *
+ */
+static int read_log(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data)
+{
+ int entries_to_read;
+ int num_of_entries;
+ int entries_to_end_of_buffer, entries_left;
+ void *entry_ptr;
+ char *buf_ptr;
+ dvfs_dptc_params_s *params;
+
+ params = (dvfs_dptc_params_s *) data;
+
+ /* Calculate number of log entries to read */
+ entries_to_read = count / sizeof(dptc_log_entry_s);
+ /* Get the number of current log buffer entries */
+ num_of_entries = get_entry_count(&params->dptc_log_buffer);
+
+ /*
+ * If number of entries to read is larger that the number of entries
+ * in the log buffer set number of entries to read to number of
+ * entries in the log buffer and set eof flag to 1
+ */
+ if (num_of_entries < entries_to_read) {
+ entries_to_read = num_of_entries;
+ *eof = 1;
+ }
+
+ /*
+ * Down the log buffer mutex to exclude others from reading and
+ * writing to the log buffer.
+ */
+ if (down_interruptible(&params->dptc_log_buffer.mutex)) {
+ return -EAGAIN;
+ }
+
+ if (num_of_entries == 0 && offset == 0) {
+ inc_log_index(&params->dptc_log_buffer.tail, -1);
+ num_of_entries++;
+ entries_to_read++;
+ }
+
+ /* get the pointer of the last (oldest) entry in the log buffer */
+ entry_ptr = (void *)&params->dptc_log_buffer.
+ entries[params->dptc_log_buffer.tail];
+
+ /* Check if tail index wraps during current read */
+ if ((params->dptc_log_buffer.tail + entries_to_read) < LOG_ENTRIES) {
+ /* No tail wrap around copy data from log buffer to buf */
+ memcpy(buf, entry_ptr,
+ (entries_to_read * sizeof(dptc_log_entry_s)));
+ } else {
+ /*
+ * Tail wrap around.
+ * First copy data from current position until end of buffer,
+ * after that copy the rest from start of the log buffer.
+ */
+ entries_to_end_of_buffer = LOG_ENTRIES -
+ params->dptc_log_buffer.tail;
+ memcpy(buf, entry_ptr,
+ (entries_to_end_of_buffer * sizeof(dptc_log_entry_s)));
+
+ entry_ptr = (void *)&params->dptc_log_buffer.entries[0];
+ buf_ptr = buf +
+ (entries_to_end_of_buffer * sizeof(dptc_log_entry_s));
+ entries_left = entries_to_read - entries_to_end_of_buffer;
+ memcpy(buf_ptr, entry_ptr,
+ (entries_left * sizeof(dptc_log_entry_s)));
+ }
+
+ /* Increment the tail index by the number of entries read */
+ inc_log_index(&params->dptc_log_buffer.tail, entries_to_read);
+
+ /* Up the log buffer mutex to allow access to the log buffer */
+ up(&params->dptc_log_buffer.mutex);
+
+ /* set start of data to point to buf */
+ *start = buf;
+
+ return (entries_to_read * sizeof(dptc_log_entry_s));
+}
+
+/*!
+ * This function initializes the DPTC log buffer.
+ *
+ * @param dptc_log pointer to the DPTC log buffer structure.
+ *
+ */
+static void init_dptc_log(dptc_log_s * dptc_log)
+{
+ dptc_log->tail = 0;
+ dptc_log->head = 0;
+
+ /* initialize log buffer mutex used for accessing the log buffer */
+ sema_init(&dptc_log->mutex, 1);
+}
+
+/*!
+ * This function initializes the DPTC hardware
+ *
+ */
+static int init_dptc_controller(void)
+{
+ INIT_WORK(&dptc_work, dptc_workqueue_handler);
+
+ if (create_proc_read_entry(PROC_NODE_NAME, 0,
+ NULL, read_log, &dptc_params) == NULL) {
+ /*
+ * Error creating proc file system entry.
+ * Exit and return error code
+ */
+ printk(KERN_ERR "DPTC: Unable create proc entry");
+ return -EFAULT;
+ }
+
+ /* Initialize the DPTC log buffer */
+ init_dptc_log(&dptc_params.dptc_log_buffer);
+
+ init_dptc_wp();
+
+ /* By default all reference circuits are enabled */
+ dptc_params.rc_state = DPTC_REF_CIRCUITS_STATUS;
+
+ dptc_clear_dcr();
+
+ /* Disable DPTC hardware and mask DPTC interrupt */
+ dptc_disable_dptc();
+ dptc_mask_dptc_int();
+
+ printk(KERN_INFO "DPTC controller initialized\n");
+ return 0;
+}
+
+/*!
+ * This function is called to put the DPTC in a low power state.
+ *
+ * @param pdev the device structure used to give information on which
+ * device to suspend (not relevant for DPTC)
+ * @param state the power state the device is entering
+ *
+ * @return The function always returns 0.
+ */
+int mxc_dptc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ if (dptc_params.dptc_is_active) {
+ dptc_disable_dptc();
+ set_dptc_wp(dptc_params.dvfs_dptc_tables_ptr->curr_wp - 1);
+ }
+
+ dptc_params.suspended = 1;
+
+ return 0;
+}
+
+/*!
+ * This function is called to resume the DPTC from a low power state.
+ *
+ * @param pdev the device structure used to give information on which
+ * device to suspend (not relevant for DPTC)
+ *
+ * @return The function always returns 0.
+ */
+int mxc_dptc_resume(struct platform_device *pdev)
+{
+ dptc_params.suspended = 0;
+
+ if (dptc_params.dptc_is_active) {
+ dptc_enable_dptc();
+ dptc_unmask_dptc_int();
+ }
+
+ return 0;
+}
+
+/*!
+ * This function frees power management table structures
+ */
+static void free_dvfs_dptc_table(void)
+{
+ int i;
+
+ for (i = 0; i < dptc_params.dvfs_dptc_tables_ptr->dvfs_state_num; i++) {
+ kfree(dptc_params.dvfs_dptc_tables_ptr->dcvr[i]);
+ }
+
+ kfree(dptc_params.dvfs_dptc_tables_ptr->dcvr);
+ kfree(dptc_params.dvfs_dptc_tables_ptr->table);
+ kfree(dptc_params.dvfs_dptc_tables_ptr->wp);
+
+ kfree(dptc_params.dvfs_dptc_tables_ptr);
+
+ dptc_params.dvfs_dptc_tables_ptr = 0;
+}
+
+/*
+ * DVFS & DPTC table parsing function
+ * reads the next line of the table in text format
+ *
+ * @param str pointer to the previous line
+ *
+ * @return pointer to the next line
+ */
+static char *pm_table_get_next_line(char *str)
+{
+ char *line_ptr;
+ int flag = 0;
+
+ if (strlen(str) == 0)
+ return str;
+
+ line_ptr = strchr(str, '\n') + 1;
+
+ while (!flag) {
+ if (strlen(line_ptr) == 0) {
+ flag = 1;
+ } else if (line_ptr[0] == '\n') {
+ line_ptr++;
+ } else if (line_ptr[0] == '#') {
+ line_ptr = pm_table_get_next_line(line_ptr);
+ } else {
+ flag = 1;
+ }
+ }
+
+ return line_ptr;
+}
+
+/*
+ * DVFS & DPTC table parsing function
+ * sets the values of DVFS & DPTC tables from
+ * table in text format
+ *
+ * @param pm_table pointer to the table in binary format
+ * @param pm_str pointer to the table in text format
+ *
+ * @return 0 on success, error code on failure
+ */
+static int dvfs_dptc_parse_table(dvfs_dptc_tables_s * pm_table, char *pm_str)
+{
+ char *pm_str_ptr;
+ int i, j, n;
+ dptc_wp *wp;
+
+ pm_str_ptr = pm_str;
+
+ n = sscanf(pm_str_ptr, "WORKING POINT %d\n", &pm_table->wp_num);
+
+ if (n != 1) {
+ printk(KERN_WARNING "Failed read WORKING POINT number\n");
+ return -1;
+ }
+
+ pm_table->curr_wp = 0;
+
+ pm_str_ptr = pm_table_get_next_line(pm_str_ptr);
+ pm_table->dvfs_state_num = 1;
+
+ pm_table->wp =
+ (dptc_wp *) kmalloc(sizeof(dptc_wp) * pm_table->wp_num, GFP_KERNEL);
+ if (!pm_table->wp) {
+ printk(KERN_ERR "Failed allocating memory\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < pm_table->wp_num; i++) {
+
+ wp = &pm_table->wp[i];
+
+ wp->wp_index = i;
+
+ n = sscanf(pm_str_ptr, "WP 0x%x\n",
+ (unsigned int *)&wp->pmic_values[0]);
+
+ if (n != 1) {
+ printk(KERN_WARNING "Failed read WP %d\n", i);
+ kfree(pm_table->wp);
+ return -1;
+ }
+
+ pm_str_ptr = pm_table_get_next_line(pm_str_ptr);
+
+ }
+
+ pm_table->table =
+ (dvfs_state *) kmalloc(sizeof(dvfs_state) *
+ pm_table->dvfs_state_num, GFP_KERNEL);
+
+ if (!pm_table->table) {
+ printk(KERN_WARNING "Failed allocating memory\n");
+ kfree(pm_table->wp);
+ return -ENOMEM;
+ }
+
+ pm_table->dcvr =
+ (dcvr_state **) kmalloc(sizeof(dcvr_state *) *
+ pm_table->dvfs_state_num, GFP_KERNEL);
+
+ if (!pm_table->dcvr) {
+ printk(KERN_WARNING "Failed allocating memory\n");
+ kfree(pm_table->table);
+ kfree(pm_table->wp);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < pm_table->dvfs_state_num; i++) {
+ pm_table->dcvr[i] =
+ (dcvr_state *) kmalloc(sizeof(dcvr_state) *
+ pm_table->wp_num, GFP_KERNEL);
+
+ if (!pm_table->dcvr[i]) {
+ printk(KERN_WARNING "Failed allocating memory\n");
+
+ for (j = i - 1; j >= 0; j--) {
+ kfree(pm_table->dcvr[j]);
+ }
+
+ kfree(pm_table->dcvr);
+ return -ENOMEM;
+ }
+
+ for (j = 0; j < pm_table->wp_num; j++) {
+
+ n = sscanf(pm_str_ptr, "DCVR 0x%x 0x%x 0x%x 0x%x\n",
+ &pm_table->dcvr[i][j].dcvr_reg[0].AsInt,
+ &pm_table->dcvr[i][j].dcvr_reg[1].AsInt,
+ &pm_table->dcvr[i][j].dcvr_reg[2].AsInt,
+ &pm_table->dcvr[i][j].dcvr_reg[3].AsInt);
+
+ if (n != 4) {
+ printk(KERN_WARNING "Failed read FREQ %d\n", i);
+
+ for (j = i; j >= 0; j--) {
+ kfree(pm_table->dcvr[j]);
+ }
+ kfree(pm_table->dcvr);
+ kfree(pm_table->table);
+ kfree(pm_table->wp);
+ return -1;
+ }
+
+ pm_str_ptr = pm_table_get_next_line(pm_str_ptr);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Initializes the default values of DVFS & DPTC table
+ *
+ * @return 0 on success, error code on failure
+ */
+static int __init dvfs_dptc_init_default_table(void)
+{
+ int res = 0;
+ char *table_str;
+
+ dvfs_dptc_tables_s *default_table;
+
+ default_table = kmalloc(sizeof(dvfs_dptc_tables_s), GFP_KERNEL);
+
+ if (!default_table) {
+ return -ENOMEM;
+ }
+
+ table_str = default_table_str;
+
+ memset(default_table, 0, sizeof(dvfs_dptc_tables_s));
+ res = dvfs_dptc_parse_table(default_table, table_str);
+
+ if (res == 0) {
+ dptc_params.dvfs_dptc_tables_ptr = default_table;
+ }
+
+ return res;
+}
+
+/*!
+ * This function is called when the driver is opened. This function
+ * checks if the user that open the device has root privileges.
+ *
+ * @param inode Pointer to device inode
+ * @param filp Pointer to device file structure
+ *
+ * @return The function returns 0 on success and a non-zero value on
+ * failure.
+ */
+static int dptc_mx27_open(struct inode *inode, struct file *filp)
+{
+ /*
+ * check if the program that opened the driver has root
+ * privileges, if not return error.
+ */
+ if (!capable(CAP_SYS_ADMIN)) {
+ return -EACCES;
+ }
+
+ if (dptc_params.suspended) {
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+/*!
+ * This function is called when the driver is close.
+ *
+ * @param inode Pointer to device inode
+ * @param filp Pointer to device file structure
+ *
+ * @return The function returns 0 on success and a non-zero value on
+ * failure.
+ *
+ */
+static int dptc_mx27_release(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+/*!
+ * This function dumps dptc translation table into string pointer
+ *
+ * @param str string pointer
+ */
+static void dvfs_dptc_dump_table(char *str)
+{
+ int i, j;
+ dcvr_state **dcvr_arr;
+ dcvr_state *dcvr_row;
+
+ memset(str, 0, MAX_TABLE_SIZE);
+
+ sprintf(str, "WORKING POINT %d\n",
+ dptc_params.dvfs_dptc_tables_ptr->wp_num);
+ str += strlen(str);
+
+ for (i = 0; i < dptc_params.dvfs_dptc_tables_ptr->wp_num; i++) {
+ sprintf(str, "WP 0x%x\n", (unsigned int)
+ dptc_params.dvfs_dptc_tables_ptr->wp[i].pmic_values[0]);
+ str += strlen(str);
+ }
+
+ dcvr_arr = dptc_params.dvfs_dptc_tables_ptr->dcvr;
+ for (i = 0; i < dptc_params.dvfs_dptc_tables_ptr->dvfs_state_num; i++) {
+ dcvr_row = dcvr_arr[i];
+
+ for (j = 0; j < dptc_params.dvfs_dptc_tables_ptr->wp_num; j++) {
+ sprintf(str,
+ "DCVR 0x%x 0x%x 0x%x 0x%x\n",
+ dcvr_row[j].dcvr_reg[0].AsInt,
+ dcvr_row[j].dcvr_reg[1].AsInt,
+ dcvr_row[j].dcvr_reg[2].AsInt,
+ dcvr_row[j].dcvr_reg[3].AsInt);
+
+ str += strlen(str);
+ }
+ }
+}
+
+/*!
+ * This function reads DVFS & DPTC translation table from user
+ *
+ * @param user_table pointer to user table
+ * @return 0 on success, error code on failure
+ */
+static int dvfs_dptc_set_table(char *user_table)
+{
+ int ret_val = -ENOIOCTLCMD;
+ char *tmp_str;
+ char *tmp_str_ptr;
+ dvfs_dptc_tables_s *dptc_table;
+
+ if (dptc_params.dptc_is_active == TRUE) {
+ ret_val = -EINVAL;
+ return ret_val;
+ }
+
+ tmp_str = vmalloc(MAX_TABLE_SIZE);
+
+ if (tmp_str < 0) {
+ ret_val = (int)tmp_str;
+ } else {
+ memset(tmp_str, 0, MAX_TABLE_SIZE);
+ tmp_str_ptr = tmp_str;
+
+ /*
+ * read num_of_wp and dvfs_state_num
+ * parameters from new table
+ */
+ while (tmp_str_ptr - tmp_str < MAX_TABLE_SIZE &&
+ (!copy_from_user(tmp_str_ptr, user_table, 1)) &&
+ tmp_str_ptr[0] != 0) {
+ tmp_str_ptr++;
+ user_table++;
+ }
+ if (tmp_str_ptr == tmp_str) {
+ /* error reading from table */
+ printk(KERN_ERR "Failed reading table from user, \
+didn't copy a character\n");
+ ret_val = -EFAULT;
+ } else if (tmp_str_ptr - tmp_str == MAX_TABLE_SIZE) {
+ /* error reading from table */
+ printk(KERN_ERR "Failed reading table from user, \
+read more than %d\n", MAX_TABLE_SIZE);
+ ret_val = -EFAULT;
+ } else {
+ /*
+ * copy table from user and set it as
+ * the current DPTC table
+ */
+ dptc_table = kmalloc(sizeof(dvfs_dptc_tables_s),
+ GFP_KERNEL);
+
+ if (!dptc_table) {
+ ret_val = -ENOMEM;
+ } else {
+ ret_val =
+ dvfs_dptc_parse_table(dptc_table, tmp_str);
+
+ if (ret_val == 0) {
+ free_dvfs_dptc_table();
+ dptc_params.dvfs_dptc_tables_ptr =
+ dptc_table;
+
+ set_dptc_wp(dptc_initial_wp);
+ }
+ }
+
+ }
+
+ vfree(tmp_str);
+ }
+
+ return ret_val;
+}
+
+/*!
+ * This function is called when a ioctl call is made from user space.
+ *
+ * @param inode Pointer to device inode
+ * @param filp Pointer to device file structure
+ * @param cmd Ioctl command
+ * @param arg Ioctl argument
+ *
+ * Following are the ioctl commands for user to use:\n
+ * DPTC_IOCTENABLE : Enables the DPTC module.\n
+ * DPTC_IOCTDISABLE : Disables the DPTC module.\n
+ * DPTC_IOCSENABLERC : Enables DPTC reference circuits.\n
+ * DPTC_IOCSDISABLERC : Disables DPTC reference circuits.\n
+ * DPTC_IOCGETSTATE : Returns 1 if the DPTC module is enabled,
+ * returns 0 if the DPTC module is disabled.\n
+ * DPTC_IOCSWP : Sets working point.\n
+ * PM_IOCSTABLE : Sets translation table.\n
+ * PM_IOCGTABLE : Gets translation table.\n
+ * PM_IOCGFREQ : Returns current CPU frequency in Hz
+ *
+ * @return The function returns 0 on success and a non-zero value on
+ * failure.
+ */
+static int dptc_mx27_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct clk *clk;
+ unsigned int tmp;
+ int ret_val = -ENOIOCTLCMD;
+ char *tmp_str;
+
+ tmp = arg;
+
+ if (dptc_params.suspended) {
+ return -EPERM;
+ }
+
+ down(&access_mutex);
+
+ pr_debug("DVFS_DPTC ioctl (%d)\n", cmd);
+
+ switch (cmd) {
+ /* Enable the DPTC module */
+ case DPTC_IOCTENABLE:
+ ret_val = start_dptc();
+ break;
+
+ /* Disable the DPTC module */
+ case DPTC_IOCTDISABLE:
+ ret_val = stop_dptc();
+ break;
+
+ case DPTC_IOCSENABLERC:
+ ret_val = enable_ref_circuits(tmp);
+ break;
+
+ case DPTC_IOCSDISABLERC:
+ ret_val = disable_ref_circuits(tmp);
+ break;
+ /*
+ * Return the DPTC module current state.
+ * Returns 1 if the DPTC module is enabled, else returns 0
+ */
+ case DPTC_IOCGSTATE:
+ ret_val = dptc_params.dptc_is_active;
+ break;
+ case DPTC_IOCSWP:
+ if (dptc_params.dptc_is_active == FALSE) {
+ if (arg >= 0 &&
+ arg < dptc_params.dvfs_dptc_tables_ptr->wp_num) {
+ set_dptc_wp(arg);
+ ret_val = 0;
+ } else {
+ ret_val = -EINVAL;
+ }
+ } else {
+ ret_val = -EINVAL;
+ }
+ break;
+
+ /* Update DPTC table */
+ case PM_IOCSTABLE:
+ ret_val = dvfs_dptc_set_table((char *)arg);
+ break;
+
+ case PM_IOCGTABLE:
+ tmp_str = vmalloc(MAX_TABLE_SIZE);
+ if (tmp_str < 0) {
+ ret_val = (int)tmp_str;
+ } else {
+ dvfs_dptc_dump_table(tmp_str);
+ if (copy_to_user((char *)tmp, tmp_str, strlen(tmp_str))) {
+ printk(KERN_ERR
+ "Failed copy %d characters to 0x%x\n",
+ strlen(tmp_str), tmp);
+ ret_val = -EFAULT;
+ } else {
+ ret_val = 0;
+ }
+ vfree(tmp_str);
+ }
+ break;
+
+ case PM_IOCGFREQ:
+ clk = clk_get(NULL, "cpu_clk");
+ ret_val = clk_get_rate(clk);
+ break;
+
+ /* Unknown ioctl command -> return error */
+ default:
+ printk(KERN_ERR "Unknown ioctl command 0x%x\n", cmd);
+ ret_val = -ENOIOCTLCMD;
+ }
+
+ up(&access_mutex);
+
+ return ret_val;
+}
+
+/*!
+ * This function is the DPTC Interrupt handler.
+ * This function wakes-up the dptc_workqueue_handler function that handles the
+ * DPTC interrupt.
+ *
+ * @param irq The Interrupt number
+ * @param dev_id Driver private data
+ *
+ * @result The function returns \b IRQ_RETVAL(1) if interrupt was handled,
+ * returns \b IRQ_RETVAL(0) if the interrupt was not handled.
+ * \b IRQ_RETVAL is defined in include/linux/interrupt.h.
+ */
+static irqreturn_t dptc_mx27_irq(int irq, void *dev_id)
+{
+ if (dptc_params.dptc_is_active == TRUE) {
+ dptc_intr_status = __raw_readl(IO_ADDRESS(MX27_PMCR_BASE_ADDR));
+
+ /* Acknowledge the interrupt */
+ __raw_writel(dptc_intr_status, IO_ADDRESS(MX27_PMCR_BASE_ADDR));
+
+ /* Extract the interrupt cause */
+ dptc_intr_status =
+ (dptc_intr_status >> MX27_PMCR_LO_INTR_OFFSET) & 0x7;
+
+ if (dptc_intr_status != 0) {
+ dptc_mask_dptc_int();
+ dptc_disable_dptc();
+ schedule_work(&dptc_work);
+ }
+ }
+
+ return IRQ_RETVAL(1);
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxc_dptc_driver = {
+ .driver = {
+ .name = "mxc_dptc",
+ .bus = &platform_bus_type,
+ },
+ .suspend = mxc_dptc_suspend,
+ .resume = mxc_dptc_resume,
+};
+
+/*!
+ * This is platform device structure for adding MU
+ */
+static struct platform_device mxc_dptc_device = {
+ .name = "mxc_dptc",
+ .id = 0,
+};
+
+/*!
+ * This function is called for module initialization.
+ * It initializes the driver data structures, sets up the DPTC hardware,
+ * registers the DPTC driver, creates a proc file system read entry and
+ * attaches the driver to the DPTC interrupt.
+ *
+ * @return 0 to indicate success else returns a negative number.
+ *
+ */
+static int __init dptc_mx27_init(void)
+{
+ int res;
+ struct class_device *temp_class;
+
+ res = dvfs_dptc_init_default_table();
+
+ if (res < 0) {
+ printk(KERN_WARNING "Failed parsing default DPTC table\n");
+ return res;
+ }
+
+ /* Initialize DPTC hardware */
+ res = init_dptc_controller();
+ if (res < 0) {
+ free_dvfs_dptc_table();
+ }
+
+ /* Initialize internal driver structures */
+ dptc_params.dptc_is_active = FALSE;
+
+ /*
+ * Register DPTC driver as a char driver with an automatically allocated
+ * major number.
+ */
+ major = register_chrdev(0, DEVICE_NAME, &fops);
+
+ /*
+ * Return error if a negative major number is returned.
+ */
+ if (major < 0) {
+ printk(KERN_ERR
+ "DPTC: Registering driver failed with %d\n", major);
+ free_dvfs_dptc_table();
+ return major;
+ }
+
+ mxc_dvfs_dptc_class = class_create(THIS_MODULE, DEVICE_NAME);
+ if (IS_ERR(mxc_dvfs_dptc_class)) {
+ printk(KERN_ERR "DPTC: Error creating class.\n");
+ goto err_out;
+ }
+
+ temp_class =
+ class_device_create(mxc_dvfs_dptc_class, NULL, MKDEV(major, 0),
+ NULL, DEVICE_NAME);
+ if (IS_ERR(temp_class)) {
+ printk(KERN_ERR "DPTC: Error creating class device.\n");
+ goto err_out;
+ }
+
+ /* request the DPTC interrupt */
+ res = request_irq(INT_CCM, dptc_mx27_irq, 0, DEVICE_NAME, NULL);
+
+ /*
+ * If res is not 0, then where was an error
+ * during attaching to DPTC interrupt.
+ * Exit and return error code.
+ */
+ if (res) {
+ printk(KERN_ERR "DPTC: Unable to attach to DPTC interrupt");
+ free_dvfs_dptc_table();
+ goto err_out;
+ }
+
+ /* Register low power modes functions */
+ res = platform_driver_register(&mxc_dptc_driver);
+ if (res == 0) {
+ res = platform_device_register(&mxc_dptc_device);
+ if (res != 0) {
+ free_dvfs_dptc_table();
+ goto err_out;
+ }
+ }
+
+ dptc_params.suspended = FALSE;
+
+ return 0;
+
+ err_out:
+ printk(KERN_WARNING "MX27 DPTC driver was not initialized\n");
+ class_device_destroy(mxc_dvfs_dptc_class, MKDEV(major, 0));
+ class_destroy(mxc_dvfs_dptc_class);
+ unregister_chrdev(major, DEVICE_NAME);
+ return -1;
+}
+
+/*!
+ * This function is called whenever the module is removed from the kernel. It
+ * unregisters the DVFS & DPTC driver from kernel, frees the irq number
+ * and removes the proc file system entry.
+ */
+static void __exit dptc_mx27_cleanup(void)
+{
+ free_dvfs_dptc_table();
+
+ /* Un-register the driver and remove its node */
+ class_device_destroy(mxc_dvfs_dptc_class, MKDEV(major, 0));
+ class_destroy(mxc_dvfs_dptc_class);
+ unregister_chrdev(major, DEVICE_NAME);
+
+ /* release the DPTC interrupt */
+ free_irq(INT_CCM, NULL);
+
+ /* Unregister low power modes functions */
+ platform_driver_unregister(&mxc_dptc_driver);
+ platform_device_unregister(&mxc_dptc_device);
+
+ /* remove the DPTC proc file system entry */
+ remove_proc_entry(PROC_NODE_NAME, NULL);
+}
+
+module_init(dptc_mx27_init);
+module_exit(dptc_mx27_cleanup);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MX27 DPTC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pm/dvfs_dptc.c b/drivers/mxc/pm/dvfs_dptc.c
new file mode 100644
index 000000000000..1bd465f02abe
--- /dev/null
+++ b/drivers/mxc/pm/dvfs_dptc.c
@@ -0,0 +1,1248 @@
+/*
+ * Copyright 2004-2007 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 dvfs_dptc.c
+ *
+ * @brief Driver for the Freescale Semiconductor MXC DVFS & DPTC module.
+ *
+ * The DVFS & DPTC driver
+ * driver is designed as a character driver which interacts with the MXC
+ * DVFS & DPTC hardware. Upon initialization, the DVFS & DPTC driver initializes
+ * the DVFS & DPTC hardware sets up driver nodes attaches to the DVFS & DPTC
+ * interrupts and initializes internal data structures. When the DVFS or DPTC
+ * interrupt occurs the driver checks the cause of the interrupt
+ * (lower voltage/frequency, increase voltage/frequency or emergency) and changes
+ * the CPU voltage and/or frequency according to translation table that is loaded
+ * into the driver (the voltage changes are done by calling some routines
+ * of the mc13783 driver).
+ *
+ * @ingroup PM_MX31
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <asm/uaccess.h>
+#include <linux/workqueue.h>
+#include <linux/proc_fs.h>
+#include <asm/semaphore.h>
+#include <linux/jiffies.h>
+#include <linux/vmalloc.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/pmic_external.h>
+
+/*
+ * Module header files
+ */
+#include "dvfs_dptc.h"
+#include <asm/arch/dptc.h>
+
+#ifdef CONFIG_MXC_DVFS
+#include <asm/arch/dvfs.h>
+#endif
+
+/*
+ * Prototypes
+ */
+static int dvfs_dptc_open(struct inode *inode, struct file *filp);
+static int dvfs_dptc_release(struct inode *inode, struct file *filp);
+static int dvfs_dptc_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+#ifdef CONFIG_MXC_DVFS_SDMA
+static ssize_t dvfs_dptc_read(struct file *filp, char __user * buf,
+ size_t count, loff_t * ppos);
+#endif
+
+#ifndef CONFIG_MXC_DVFS_SDMA
+static irqreturn_t dvfs_dptc_irq(int irq, void *dev_id);
+#else
+static void dvfs_dptc_sdma_callback(dvfs_dptc_params_s * params);
+#endif
+
+#ifdef CONFIG_MXC_DPTC
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxc_dptc_driver = {
+ .driver = {
+ .name = "mxc_dptc",
+ },
+ .suspend = mxc_dptc_suspend,
+ .resume = mxc_dptc_resume,
+};
+
+/*!
+ * This is platform device structure for adding MU
+ */
+static struct platform_device mxc_dptc_device = {
+ .name = "mxc_dptc",
+ .id = 0,
+};
+#endif
+
+/*
+ * Global variables
+ */
+
+/*!
+ * The dvfs_dptc_params structure holds all the internal DPTC driver parameters
+ * (current working point, current frequency, translation table and DPTC
+ * log buffer).
+ */
+static dvfs_dptc_params_s dvfs_dptc_params;
+
+/*!
+ * Holds the automatically selected DPTC driver major number.
+ */
+static int major;
+
+static struct class *mxc_dvfs_dptc_class;
+
+/*
+ * This mutex makes the Read,Write and IOCTL command mutual exclusive.
+ */
+DECLARE_MUTEX(access_mutex);
+
+/*!
+ * This structure contains pointers for device driver entry point.
+ * The driver register function in init module will call this
+ * structure.
+ */
+static struct file_operations fops = {
+ .open = dvfs_dptc_open,
+ .release = dvfs_dptc_release,
+ .ioctl = dvfs_dptc_ioctl,
+#ifdef CONFIG_MXC_DVFS_SDMA
+ .read = dvfs_dptc_read,
+#endif
+};
+
+#ifdef CONFIG_MXC_DVFS_SDMA
+/*
+ * Update pointers to physical addresses of DVFS & DPTC table
+ * for SDMA usage
+ *
+ * @param dvfs_dptc_tables_ptr pointer to the DVFS &
+ * DPTC translation table.
+ */
+static void dvfs_dptc_virt_2_phys(dvfs_dptc_tables_s * dvfs_dptc_table)
+{
+ int i;
+
+ /* Update DCVR pointers */
+ for (i = 0; i < dvfs_dptc_table->dvfs_state_num; i++) {
+ dvfs_dptc_table->dcvr[i] = (dcvr_state *)
+ sdma_virt_to_phys(dvfs_dptc_table->dcvr[i]);
+ }
+ dvfs_dptc_table->dcvr = (dcvr_state **)
+ sdma_virt_to_phys(dvfs_dptc_table->dcvr);
+ dvfs_dptc_table->table = (dvfs_state *)
+ sdma_virt_to_phys(dvfs_dptc_table->table);
+}
+
+/*
+ * Update pointers to virtual addresses of DVFS & DPTC table
+ * for ARM usage
+ *
+ * @param dvfs_dptc_tables_ptr pointer to the DVFS &
+ * DPTC translation table.
+ */
+static void dvfs_dptc_phys_2_virt(dvfs_dptc_tables_s * dvfs_dptc_table)
+{
+ int i;
+
+ dvfs_dptc_table->table = sdma_phys_to_virt
+ ((unsigned long)dvfs_dptc_table->table);
+ dvfs_dptc_table->dcvr = sdma_phys_to_virt
+ ((unsigned long)dvfs_dptc_table->dcvr);
+
+ /* Update DCVR pointers */
+ for (i = 0; i < dvfs_dptc_table->dvfs_state_num; i++) {
+ dvfs_dptc_table->dcvr[i] =
+ sdma_phys_to_virt((unsigned long)dvfs_dptc_table->dcvr[i]);
+ }
+}
+#endif
+
+/*!
+ * This function frees power management table structures
+ */
+static void free_dvfs_dptc_table(void)
+{
+ int i;
+
+#ifdef CONFIG_MXC_DVFS_SDMA
+ dvfs_dptc_phys_2_virt(dvfs_dptc_params.dvfs_dptc_tables_ptr);
+#endif
+
+ for (i = 0;
+ i < dvfs_dptc_params.dvfs_dptc_tables_ptr->dvfs_state_num; i++) {
+ sdma_free(dvfs_dptc_params.dvfs_dptc_tables_ptr->dcvr[i]);
+ }
+
+ sdma_free(dvfs_dptc_params.dvfs_dptc_tables_ptr->dcvr);
+ sdma_free(dvfs_dptc_params.dvfs_dptc_tables_ptr->table);
+ sdma_free(dvfs_dptc_params.dvfs_dptc_tables_ptr->wp);
+
+ sdma_free(dvfs_dptc_params.dvfs_dptc_tables_ptr);
+
+ dvfs_dptc_params.dvfs_dptc_tables_ptr = 0;
+}
+
+/*
+ * DVFS & DPTC table parsing function
+ * reads the next line of the table in text format
+ *
+ * @param str pointer to the previous line
+ *
+ * @return pointer to the next line
+ */
+static char *pm_table_get_next_line(char *str)
+{
+ char *line_ptr;
+ int flag = 0;
+
+ if (strlen(str) == 0)
+ return str;
+
+ line_ptr = strchr(str, '\n') + 1;
+
+ while (!flag) {
+ if (strlen(line_ptr) == 0) {
+ flag = 1;
+ } else if (line_ptr[0] == '\n') {
+ line_ptr++;
+ } else if (line_ptr[0] == '#') {
+ line_ptr = pm_table_get_next_line(line_ptr);
+ } else {
+ flag = 1;
+ }
+ }
+
+ return line_ptr;
+}
+
+/*
+ * DVFS & DPTC table parsing function
+ * sets the values of DVFS & DPTC tables from
+ * table in text format
+ *
+ * @param pm_table pointer to the table in binary format
+ * @param pm_str pointer to the table in text format
+ *
+ * @return 0 on success, error code on failure
+ */
+static int dvfs_dptc_parse_table(dvfs_dptc_tables_s * pm_table, char *pm_str)
+{
+ char *pm_str_ptr;
+ int i, j, n;
+ dptc_wp *wp;
+ dvfs_state *table;
+
+ pm_str_ptr = pm_str;
+
+ n = sscanf(pm_str_ptr, "WORKING POINT %d\n", &pm_table->wp_num);
+
+ if (n != 1) {
+ printk(KERN_WARNING "Failed read WORKING POINT number\n");
+ return -1;
+ }
+
+ pm_table->curr_wp = 0;
+
+ pm_str_ptr = pm_table_get_next_line(pm_str_ptr);
+
+ if (cpu_is_mx31() || cpu_is_mx32()) {
+ pm_table->dvfs_state_num = 4;
+ pm_table->use_four_freq = 1;
+ } else {
+ pm_table->dvfs_state_num = 1;
+ }
+
+ pm_table->wp =
+ (dptc_wp *) sdma_malloc(sizeof(dptc_wp) * pm_table->wp_num);
+ if (!pm_table->wp) {
+ printk(KERN_ERR "Failed allocating memory\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < pm_table->wp_num; i++) {
+
+ wp = &pm_table->wp[i];
+
+ wp->wp_index = i;
+
+ if (cpu_is_mx31() || cpu_is_mx32()) {
+ n = sscanf(pm_str_ptr, "WP 0x%x 0x%x 0x%x 0x%x\n",
+ (unsigned int *)&wp->pmic_values[0],
+ (unsigned int *)&wp->pmic_values[1],
+ (unsigned int *)&wp->pmic_values[2],
+ (unsigned int *)&wp->pmic_values[3]);
+
+ if (n != 4) {
+ printk(KERN_WARNING "Failed read WP %d\n", i);
+ sdma_free(pm_table->wp);
+ return -1;
+ }
+ } else {
+ n = sscanf(pm_str_ptr, "WP 0x%x\n",
+ (unsigned int *)&wp->pmic_values[0]);
+
+ if (n != 1) {
+ printk(KERN_WARNING "Failed read WP %d\n", i);
+ sdma_free(pm_table->wp);
+ return -1;
+ }
+ }
+
+ pm_str_ptr = pm_table_get_next_line(pm_str_ptr);
+
+ }
+
+ pm_table->table =
+ (dvfs_state *) sdma_malloc(sizeof(dvfs_state) *
+ pm_table->dvfs_state_num);
+
+ if (!pm_table->table) {
+ printk(KERN_WARNING "Failed allocating memory\n");
+ sdma_free(pm_table->wp);
+ return -ENOMEM;
+ }
+
+ if (cpu_is_mx31() || cpu_is_mx32()) {
+ for (i = 0; i < pm_table->dvfs_state_num; i++) {
+ table = &pm_table->table[i];
+
+ n = sscanf(pm_str_ptr,
+ "FREQ %d %d 0x%x 0x%x 0x%x 0x%x %d\n",
+ (unsigned int *)&table->pll_sw_up,
+ (unsigned int *)&table->pll_sw_down,
+ (unsigned int *)&table->pdr0_up,
+ (unsigned int *)&table->pdr0_down,
+ (unsigned int *)&table->pll_up,
+ (unsigned int *)&table->pll_down,
+ (unsigned int *)&table->vscnt);
+
+ if (n != 7) {
+ printk(KERN_WARNING "Failed read FREQ %d\n", i);
+ sdma_free(pm_table->table);
+ sdma_free(pm_table->wp);
+ return -1;
+ }
+
+ pm_str_ptr = pm_table_get_next_line(pm_str_ptr);
+ }
+ }
+
+ pm_table->dcvr =
+ (dcvr_state **) sdma_malloc(sizeof(dcvr_state *) *
+ pm_table->dvfs_state_num);
+
+ if (!pm_table->dcvr) {
+ printk(KERN_WARNING "Failed allocating memory\n");
+ sdma_free(pm_table->table);
+ sdma_free(pm_table->wp);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < pm_table->dvfs_state_num; i++) {
+ pm_table->dcvr[i] =
+ (dcvr_state *) sdma_malloc(sizeof(dcvr_state) *
+ pm_table->wp_num);
+
+ if (!pm_table->dcvr[i]) {
+ printk(KERN_WARNING "Failed allocating memory\n");
+
+ for (j = i - 1; j >= 0; j--) {
+ sdma_free(pm_table->dcvr[j]);
+ }
+
+ sdma_free(pm_table->dcvr);
+ return -ENOMEM;
+ }
+
+ for (j = 0; j < pm_table->wp_num; j++) {
+
+ n = sscanf(pm_str_ptr, "DCVR 0x%x 0x%x 0x%x 0x%x\n",
+ &pm_table->dcvr[i][j].dcvr_reg[0].AsInt,
+ &pm_table->dcvr[i][j].dcvr_reg[1].AsInt,
+ &pm_table->dcvr[i][j].dcvr_reg[2].AsInt,
+ &pm_table->dcvr[i][j].dcvr_reg[3].AsInt);
+
+ if (n != 4) {
+ printk(KERN_WARNING "Failed read FREQ %d\n", i);
+
+ for (j = i; j >= 0; j--) {
+ sdma_free(pm_table->dcvr[j]);
+ }
+ sdma_free(pm_table->dcvr);
+ sdma_free(pm_table->table);
+ sdma_free(pm_table->wp);
+ return -1;
+ }
+
+ pm_str_ptr = pm_table_get_next_line(pm_str_ptr);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Initializes the default values of DVFS & DPTC table
+ *
+ * @return 0 on success, error code on failure
+ */
+static int __init dvfs_dptc_init_default_table(void)
+{
+ int res = 0;
+ char *table_str;
+ struct clk *clk;
+
+ dvfs_dptc_tables_s *default_table;
+
+ default_table = sdma_malloc(sizeof(dvfs_dptc_tables_s));
+
+ if (!default_table) {
+ return -ENOMEM;
+ }
+
+ table_str = default_table_str;
+ if (cpu_is_mx31() || cpu_is_mx32()) {
+ if (cpu_is_mx31_rev(CHIP_REV_2_0) < 0) {
+ clk = clk_get(NULL, "ckih");
+ if (clk_get_rate(clk) == 27000000) {
+ printk(KERN_INFO
+ "DVFS & DPTC: using 27MHz CKIH table\n");
+#ifdef CONFIG_ARCH_MX3
+ table_str = default_table_str_27ckih;
+#endif
+ }
+ } else {
+#ifdef CONFIG_ARCH_MX3
+ table_str = default_table_str_rev2;
+#endif
+ }
+ clk_put(clk);
+ }
+
+ memset(default_table, 0, sizeof(dvfs_dptc_tables_s));
+ res = dvfs_dptc_parse_table(default_table, table_str);
+
+ if (res == 0) {
+ dvfs_dptc_params.dvfs_dptc_tables_ptr = default_table;
+ }
+
+ return res;
+}
+
+#ifdef CONFIG_MXC_DVFS_SDMA
+/*!
+ * This function is called for SDMA channel initialization.
+ *
+ * @param params pointer to the DPTC driver parameters structure.
+ *
+ * @return 0 to indicate success else returns a negative number.
+ */
+static int init_sdma_channel(dvfs_dptc_params_s * params)
+{
+ dma_channel_params sdma_params;
+ dma_request_t sdma_request;
+ int i;
+ int res = 0;
+
+ params->sdma_channel = 0;
+ res = mxc_request_dma(&params->sdma_channel, "DVFS_DPTC");
+ if (res < 0) {
+ printk(KERN_ERR "Failed allocate SDMA channel for DVFS_DPTC\n");
+ return res;
+ }
+
+ memset(&sdma_params, 0, sizeof(dma_channel_params));
+ sdma_params.peripheral_type = CCM;
+ sdma_params.transfer_type = per_2_emi;
+ sdma_params.event_id = DMA_REQ_CCM;
+ sdma_params.callback = (dma_callback_t) dvfs_dptc_sdma_callback;
+ sdma_params.arg = params;
+ sdma_params.per_address = CCM_BASE_ADDR;
+ sdma_params.watermark_level =
+ sdma_virt_to_phys(params->dvfs_dptc_tables_ptr);
+ sdma_params.bd_number = 2;
+
+ res = mxc_dma_setup_channel(params->sdma_channel, &sdma_params);
+
+ if (res == 0) {
+ memset(&sdma_request, 0, sizeof(dma_request_t));
+
+ for (i = 0; i < DVFS_LB_SDMA_BD; i++) {
+ sdma_request.destAddr = (__u8 *)
+ (params->dvfs_log_buffer_phys +
+ i * (DVFS_LB_SIZE * DVFS_LB_SAMPLE_SIZE / 8) /
+ DVFS_LB_SDMA_BD);
+ sdma_request.count = DVFS_LB_SIZE / DVFS_LB_SDMA_BD;
+ sdma_request.bd_cont = 1;
+
+ mxc_dma_set_config(params->sdma_channel, &sdma_request,
+ i);
+ }
+
+ mxc_dma_start(params->sdma_channel);
+ }
+
+ return res;
+}
+#endif
+
+/*!
+ * This function is called for module initialization.
+ * It initializes the driver data structures, sets up the DPTC hardware,
+ * registers the DPTC driver, creates a proc file system read entry and
+ * attaches the driver to the DPTC interrupt.
+ *
+ * @return 0 to indicate success else returns a negative number.
+ *
+ */
+static int __init dvfs_dptc_init(void)
+{
+ int res = 0;
+ struct class_device *temp_class;
+
+ res = dvfs_dptc_init_default_table();
+
+ if (res < 0) {
+ printk(KERN_WARNING "Failed parsing default DPTC table\n");
+ return res;
+ }
+#ifdef CONFIG_MXC_DPTC
+ /* Initialize DPTC hardware */
+ res = init_dptc_controller(&dvfs_dptc_params);
+ if (res < 0) {
+ free_dvfs_dptc_table();
+ return res;
+ }
+#endif
+
+#ifdef CONFIG_MXC_DVFS
+ /* Initialize DVFS hardware */
+ res = init_dvfs_controller(&dvfs_dptc_params);
+ if (res < 0) {
+ free_dvfs_dptc_table();
+ return res;
+ }
+
+ /* Enable 4 mc13783 output voltages */
+ pmic_write_reg(REG_ARBITRATION_SWITCHERS, 1, (1 << 5));
+
+ /* Enable mc13783 voltage ready signal */
+ pmic_write_reg(REG_INTERRUPT_MASK_1, 0, (1 << 11));
+
+ /* Set mc13783 DVS speed 25mV each 4us */
+ pmic_write_reg(REG_SWITCHERS_4, 1, (1 << 6));
+ pmic_write_reg(REG_SWITCHERS_4, 0, (1 << 7));
+
+ dvfs_update_freqs_table(dvfs_dptc_params.dvfs_dptc_tables_ptr);
+#endif
+
+#ifdef CONFIG_MXC_DVFS_SDMA
+ /* Update addresses to physical */
+ if (res == 0) {
+ dvfs_dptc_virt_2_phys(dvfs_dptc_params.dvfs_dptc_tables_ptr);
+ }
+
+ res = init_sdma_channel(&dvfs_dptc_params);
+ if (res < 0) {
+ free_dvfs_dptc_table();
+ return res;
+ }
+#endif
+
+ /* Initialize internal driver structures */
+ dvfs_dptc_params.dptc_is_active = FALSE;
+
+#ifdef CONFIG_MXC_DVFS
+ dvfs_dptc_params.dvfs_is_active = FALSE;
+#endif
+
+ /*
+ * Register DPTC driver as a char driver with an automatically allocated
+ * major number.
+ */
+ major = register_chrdev(0, DEVICE_NAME, &fops);
+
+ /*
+ * Return error if a negative major number is returned.
+ */
+ if (major < 0) {
+ printk(KERN_ERR
+ "DPTC: Registering driver failed with %d\n", major);
+ free_dvfs_dptc_table();
+ return major;
+ }
+
+ mxc_dvfs_dptc_class = class_create(THIS_MODULE, DEVICE_NAME);
+ if (IS_ERR(mxc_dvfs_dptc_class)) {
+ printk(KERN_ERR "DPTC: Error creating class.\n");
+ res = PTR_ERR(mxc_dvfs_dptc_class);
+ goto err_out1;
+ }
+
+ temp_class =
+ class_device_create(mxc_dvfs_dptc_class, NULL, MKDEV(major, 0),
+ NULL, DEVICE_NAME);
+ if (IS_ERR(temp_class)) {
+ printk(KERN_ERR "DPTC: Error creating class device.\n");
+ res = PTR_ERR(temp_class);
+ goto err_out2;
+ }
+#ifndef CONFIG_MXC_DVFS_SDMA
+ /* request the DPTC interrupt */
+ res = request_irq(INT_CCM, dvfs_dptc_irq, 0, DEVICE_NAME, NULL);
+ /*
+ * If res is not 0, then where was an error
+ * during attaching to DPTC interrupt.
+ * Exit and return error code.
+ */
+ if (res) {
+ printk(KERN_ERR "DPTC: Unable to attach to DPTC interrupt");
+ goto err_out3;
+ }
+ /* request the DVFS interrupt */
+ res = request_irq(INT_DVFS, dvfs_dptc_irq, 0, DEVICE_NAME, NULL);
+ if (res) {
+ printk(KERN_ERR "DVFS: Unable to attach to DVFS interrupt");
+ goto err_out4;
+ }
+#endif
+
+#ifdef CONFIG_MXC_DPTC
+ /* Register low power modes functions */
+ res = platform_driver_register(&mxc_dptc_driver);
+ if (res == 0) {
+ res = platform_device_register(&mxc_dptc_device);
+ if (res != 0) {
+ goto err_out5;
+ }
+ }
+#endif
+ dvfs_dptc_params.suspended = FALSE;
+
+ return res;
+
+ err_out5:
+ free_irq(INT_DVFS, NULL);
+#ifndef CONFIG_MXC_DVFS_SDMA
+ err_out4:
+#endif
+ free_irq(INT_CCM, NULL);
+#ifndef CONFIG_MXC_DVFS_SDMA
+ err_out3:
+#endif
+ class_device_destroy(mxc_dvfs_dptc_class, MKDEV(major, 0));
+ err_out2:
+ class_destroy(mxc_dvfs_dptc_class);
+ err_out1:
+ unregister_chrdev(major, DEVICE_NAME);
+ free_dvfs_dptc_table();
+ printk(KERN_ERR "DVFS&DPTC driver was not initialized\n");
+ return res;
+}
+
+/*!
+ * This function is called whenever the module is removed from the kernel. It
+ * unregisters the DVFS & DPTC driver from kernel, frees the irq number
+ * and removes the proc file system entry.
+ */
+static void __exit dvfs_dptc_cleanup(void)
+{
+#ifdef CONFIG_MXC_DPTC
+ /* Unregister low power modes functions */
+ platform_driver_unregister(&mxc_dptc_driver);
+ platform_device_unregister(&mxc_dptc_device);
+#endif
+
+ free_dvfs_dptc_table();
+
+ /* Un-register the driver and remove its node */
+ class_device_destroy(mxc_dvfs_dptc_class, MKDEV(major, 0));
+ class_destroy(mxc_dvfs_dptc_class);
+ unregister_chrdev(major, DEVICE_NAME);
+
+ /* release the DPTC interrupt */
+ free_irq(INT_CCM, NULL);
+ /* release the DVFS interrupt */
+ free_irq(INT_DVFS, NULL);
+
+ /* remove the DPTC proc file system entry */
+ remove_proc_entry(PROC_NODE_NAME, NULL);
+}
+
+/*!
+ * This function is called when the driver is opened. This function
+ * checks if the user that open the device has root privileges.
+ *
+ * @param inode Pointer to device inode
+ * @param filp Pointer to device file structure
+ *
+ * @return The function returns 0 on success and a non-zero value on
+ * failure.
+ */
+static int dvfs_dptc_open(struct inode *inode, struct file *filp)
+{
+ /*
+ * check if the program that opened the driver has root
+ * privileges, if not return error.
+ */
+ if (!capable(CAP_SYS_ADMIN)) {
+ return -EACCES;
+ }
+
+ if (dvfs_dptc_params.suspended) {
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+/*!
+ * This function is called when the driver is close.
+ *
+ * @param inode Pointer to device inode
+ * @param filp Pointer to device file structure
+ *
+ * @return The function returns 0 on success and a non-zero value on
+ * failure.
+ *
+ */
+static int dvfs_dptc_release(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+/*!
+ * This function dumps dptc translation table into string pointer
+ *
+ * @param str string pointer
+ */
+static void dvfs_dptc_dump_table(char *str)
+{
+ int i, j;
+ dcvr_state **dcvr_arr;
+ dcvr_state *dcvr_row;
+ dvfs_state *table;
+
+ memset(str, 0, MAX_TABLE_SIZE);
+
+ sprintf(str, "WORKING POINT %d\n",
+ dvfs_dptc_params.dvfs_dptc_tables_ptr->wp_num);
+ str += strlen(str);
+
+ for (i = 0; i < dvfs_dptc_params.dvfs_dptc_tables_ptr->wp_num; i++) {
+ if (cpu_is_mx31() || cpu_is_mx32()) {
+ sprintf(str, "WP 0x%x 0x%x 0x%x 0x%x\n", (unsigned int)
+ dvfs_dptc_params.dvfs_dptc_tables_ptr->wp[i].
+ pmic_values[0], (unsigned int)
+ dvfs_dptc_params.dvfs_dptc_tables_ptr->wp[i].
+ pmic_values[1], (unsigned int)
+ dvfs_dptc_params.dvfs_dptc_tables_ptr->wp[i].
+ pmic_values[2], (unsigned int)
+ dvfs_dptc_params.dvfs_dptc_tables_ptr->wp[i].
+ pmic_values[3]);
+ } else {
+ sprintf(str, "WP 0x%x\n", (unsigned int)
+ dvfs_dptc_params.dvfs_dptc_tables_ptr->wp[i].
+ pmic_values[0]);
+ }
+
+ str += strlen(str);
+ }
+
+ if (cpu_is_mx31() || cpu_is_mx32()) {
+ for (i = 0;
+ i < dvfs_dptc_params.dvfs_dptc_tables_ptr->dvfs_state_num;
+ i++) {
+ table = dvfs_dptc_params.dvfs_dptc_tables_ptr->table;
+#ifdef CONFIG_MXC_DVFS_SDMA
+ table = sdma_phys_to_virt((unsigned long)table);
+#endif
+ sprintf(str,
+ "FREQ %d %d 0x%x 0x%x 0x%x 0x%x %d\n",
+ (unsigned int)table[i].pll_sw_up,
+ (unsigned int)table[i].pll_sw_down,
+ (unsigned int)table[i].pdr0_up,
+ (unsigned int)table[i].pdr0_down,
+ (unsigned int)table[i].pll_up,
+ (unsigned int)table[i].pll_down,
+ (unsigned int)table[i].vscnt);
+
+ str += strlen(str);
+ }
+ }
+
+ for (i = 0;
+ i < dvfs_dptc_params.dvfs_dptc_tables_ptr->dvfs_state_num; i++) {
+ dcvr_arr = dvfs_dptc_params.dvfs_dptc_tables_ptr->dcvr;
+#ifdef CONFIG_MXC_DVFS_SDMA
+ dcvr_arr = sdma_phys_to_virt((unsigned long)dcvr_arr);
+#endif
+ dcvr_row = dcvr_arr[i];
+#ifdef CONFIG_MXC_DVFS_SDMA
+ dcvr_row = sdma_phys_to_virt((unsigned long)dcvr_row);
+#endif
+
+ for (j = 0;
+ j < dvfs_dptc_params.dvfs_dptc_tables_ptr->wp_num; j++) {
+ sprintf(str,
+ "DCVR 0x%x 0x%x 0x%x 0x%x\n",
+ dcvr_row[j].dcvr_reg[0].AsInt,
+ dcvr_row[j].dcvr_reg[1].AsInt,
+ dcvr_row[j].dcvr_reg[2].AsInt,
+ dcvr_row[j].dcvr_reg[3].AsInt);
+
+ str += strlen(str);
+ }
+ }
+}
+
+/*!
+ * This function reads DVFS & DPTC translation table from user
+ *
+ * @param user_table pointer to user table
+ * @return 0 on success, error code on failure
+ */
+int dvfs_dptc_set_table(char *user_table)
+{
+ int ret_val = -ENOIOCTLCMD;
+ char *tmp_str;
+ char *tmp_str_ptr;
+ dvfs_dptc_tables_s *dptc_table;
+
+ if ((cpu_is_mx31() || cpu_is_mx32()) &&
+ (dvfs_dptc_params.dptc_is_active == TRUE ||
+ dvfs_dptc_params.dvfs_is_active == TRUE)) {
+ ret_val = -EINVAL;
+ return ret_val;
+ } else if (dvfs_dptc_params.dptc_is_active == TRUE) {
+ ret_val = -EINVAL;
+ return ret_val;
+ }
+
+ tmp_str = vmalloc(MAX_TABLE_SIZE);
+
+ if (tmp_str < 0) {
+ ret_val = (int)tmp_str;
+ } else {
+ memset(tmp_str, 0, MAX_TABLE_SIZE);
+ tmp_str_ptr = tmp_str;
+
+ /*
+ * read num_of_wp and dvfs_state_num
+ * parameters from new table
+ */
+ while (tmp_str_ptr - tmp_str < MAX_TABLE_SIZE &&
+ (!copy_from_user(tmp_str_ptr, user_table, 1)) &&
+ tmp_str_ptr[0] != 0) {
+ tmp_str_ptr++;
+ user_table++;
+ }
+ if (tmp_str_ptr == tmp_str) {
+ /* error reading from table */
+ printk(KERN_ERR "Failed reading table from user, \
+didn't copy a character\n");
+ ret_val = -EFAULT;
+ } else if (tmp_str_ptr - tmp_str == MAX_TABLE_SIZE) {
+ /* error reading from table */
+ printk(KERN_ERR "Failed reading table from user, \
+read more than %d\n", MAX_TABLE_SIZE);
+ ret_val = -EFAULT;
+ } else {
+ /*
+ * copy table from user and set it as
+ * the current DPTC table
+ */
+ dptc_table = sdma_malloc(sizeof(dvfs_dptc_tables_s));
+
+ if (!dptc_table) {
+ ret_val = -ENOMEM;
+ } else {
+ ret_val =
+ dvfs_dptc_parse_table(dptc_table, tmp_str);
+
+ if (ret_val == 0) {
+ free_dvfs_dptc_table();
+ dvfs_dptc_params.dvfs_dptc_tables_ptr =
+ dptc_table;
+
+#ifdef CONFIG_MXC_DVFS
+ dvfs_update_freqs_table
+ (dvfs_dptc_params.
+ dvfs_dptc_tables_ptr);
+#endif
+
+#ifdef CONFIG_MXC_DVFS_SDMA
+ /* Update addresses to physical */
+ dvfs_dptc_virt_2_phys(dvfs_dptc_params.
+ dvfs_dptc_tables_ptr);
+ mxc_free_dma(dvfs_dptc_params.
+ sdma_channel);
+ init_sdma_channel(&dvfs_dptc_params);
+#endif
+#ifdef CONFIG_MXC_DPTC
+ set_dptc_curr_freq(&dvfs_dptc_params,
+ 0);
+ set_dptc_wp(&dvfs_dptc_params, 0);
+#endif
+ }
+ }
+
+ }
+
+ vfree(tmp_str);
+ }
+
+ return ret_val;
+}
+
+#ifdef CONFIG_MXC_DVFS_SDMA
+static ssize_t dvfs_dptc_read(struct file *filp, char __user * buf,
+ size_t count, loff_t * ppos)
+{
+ size_t count0, count1;
+
+ while (dvfs_dptc_params.chars_in_buffer < count) {
+ //count = dvfs_dptc_params.chars_in_buffer;
+ waitqueue_active(&dvfs_dptc_params.dvfs_pred_wait);
+ wake_up(&dvfs_dptc_params.dvfs_pred_wait);
+ schedule();
+ }
+
+ if (dvfs_dptc_params.read_ptr + count <
+ dvfs_dptc_params.dvfs_log_buffer +
+ DVFS_LB_SIZE * DVFS_LB_SAMPLE_SIZE / 8) {
+ count0 = count;
+ count1 = 0;
+ } else {
+ count0 =
+ dvfs_dptc_params.dvfs_log_buffer +
+ DVFS_LB_SIZE * DVFS_LB_SAMPLE_SIZE / 8 -
+ dvfs_dptc_params.read_ptr;
+ count1 = count - count0;
+ }
+
+ copy_to_user(buf, dvfs_dptc_params.read_ptr, count0);
+ copy_to_user(buf + count0, dvfs_dptc_params.dvfs_log_buffer, count1);
+
+ if (count1 == 0) {
+ dvfs_dptc_params.read_ptr += count;
+ } else {
+ dvfs_dptc_params.read_ptr =
+ dvfs_dptc_params.dvfs_log_buffer + count1;
+ }
+
+ if (dvfs_dptc_params.read_ptr ==
+ dvfs_dptc_params.dvfs_log_buffer +
+ DVFS_LB_SIZE * DVFS_LB_SAMPLE_SIZE / 8) {
+ dvfs_dptc_params.read_ptr = dvfs_dptc_params.dvfs_log_buffer;
+ }
+
+ dvfs_dptc_params.chars_in_buffer -= count;
+
+ return count;
+}
+#endif
+
+/*!
+ * This function is called when a ioctl call is made from user space.
+ *
+ * @param inode Pointer to device inode
+ * @param filp Pointer to device file structure
+ * @param cmd Ioctl command
+ * @param arg Ioctl argument
+ *
+ * Following are the ioctl commands for user to use:\n
+ * DPTC_IOCTENABLE : Enables the DPTC module.\n
+ * DPTC_IOCTDISABLE : Disables the DPTC module.\n
+ * DPTC_IOCSENABLERC : Enables DPTC reference circuits.\n
+ * DPTC_IOCSDISABLERC : Disables DPTC reference circuits.\n
+ * DPTC_IOCGETSTATE : Returns 1 if the DPTC module is enabled,
+ * returns 0 if the DPTC module is disabled.\n
+ * DPTC_IOCSWP : Sets working point.\n
+ * PM_IOCSTABLE : Sets translation table.\n
+ * PM_IOCGTABLE : Gets translation table.\n
+ * DVFS_IOCTENABLE : Enables DVFS
+ * DVFS_IOCTDISABLE : Disables DVFS
+ * DVFS_IOCGSTATE : Returns 1 if the DVFS module is enabled,
+ * returns 0 if the DVFS module is disabled.\n
+ * DVFS_IOCSSWGP : Sets the value of DVFS SW general
+ * purpose bits.\n
+ * DVFS_IOCSWFI : Sets the status of WFI monitoring.\n
+ * PM_IOCGFREQ : Returns current CPU frequency in Hz
+ * DVFS_IOCSFREQ : Sets DVFS frequency when DVFS\n
+ * HW is disabled.\n
+ *
+ * @return The function returns 0 on success and a non-zero value on
+ * failure.
+ */
+static int dvfs_dptc_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct clk *clk;
+ unsigned int tmp;
+ int ret_val = -ENOIOCTLCMD;
+ char *tmp_str;
+
+ tmp = arg;
+
+ if (dvfs_dptc_params.suspended) {
+ return -EPERM;
+ }
+
+ down(&access_mutex);
+
+ pr_debug("DVFS_DPTC ioctl (%d)\n", cmd);
+
+ switch (cmd) {
+#ifdef CONFIG_MXC_DPTC
+ /* Enable the DPTC module */
+ case DPTC_IOCTENABLE:
+ ret_val = start_dptc(&dvfs_dptc_params);
+ break;
+
+ /* Disable the DPTC module */
+ case DPTC_IOCTDISABLE:
+ ret_val = stop_dptc(&dvfs_dptc_params);
+ break;
+
+ case DPTC_IOCSENABLERC:
+ ret_val = enable_ref_circuits(&dvfs_dptc_params, tmp);
+ break;
+
+ case DPTC_IOCSDISABLERC:
+ ret_val = disable_ref_circuits(&dvfs_dptc_params, tmp);
+ break;
+ /*
+ * Return the DPTC module current state.
+ * Returns 1 if the DPTC module is enabled, else returns 0
+ */
+ case DPTC_IOCGSTATE:
+ ret_val = dvfs_dptc_params.dptc_is_active;
+ break;
+ case DPTC_IOCSWP:
+ if (dvfs_dptc_params.dptc_is_active == FALSE) {
+ if (arg >= 0 &&
+ arg <
+ dvfs_dptc_params.dvfs_dptc_tables_ptr->wp_num) {
+ set_dptc_wp(&dvfs_dptc_params, arg);
+ ret_val = 0;
+ } else {
+ ret_val = -EINVAL;
+ }
+ } else {
+ ret_val = -EINVAL;
+ }
+ break;
+
+#endif /* CONFIG_MXC_DPTC */
+
+ /* Update DPTC table */
+ case PM_IOCSTABLE:
+ ret_val = dvfs_dptc_set_table((char *)arg);
+ break;
+
+ case PM_IOCGTABLE:
+ tmp_str = vmalloc(MAX_TABLE_SIZE);
+ if (tmp_str < 0) {
+ ret_val = (int)tmp_str;
+ } else {
+ dvfs_dptc_dump_table(tmp_str);
+ if (copy_to_user((char *)tmp, tmp_str, strlen(tmp_str))) {
+ printk(KERN_ERR
+ "Failed copy %d characters to 0x%x\n",
+ strlen(tmp_str), tmp);
+ ret_val = -EFAULT;
+ } else {
+ ret_val = 0;
+ }
+ vfree(tmp_str);
+ }
+ break;
+
+#ifdef CONFIG_MXC_DVFS
+ /* Enable the DVFS module */
+ case DVFS_IOCTENABLE:
+ ret_val = start_dvfs(&dvfs_dptc_params);
+ break;
+
+ /* Disable the DVFS module */
+ case DVFS_IOCTDISABLE:
+ ret_val = stop_dvfs(&dvfs_dptc_params);
+ break;
+ /*
+ * Return the DVFS module current state.
+ * Returns 1 if the DPTC module is enabled, else returns 0
+ */
+ case DVFS_IOCGSTATE:
+ ret_val = dvfs_dptc_params.dvfs_is_active;
+ break;
+ case DVFS_IOCSSWGP:
+ ret_val = set_sw_gp((unsigned char)arg);
+ break;
+ case DVFS_IOCSWFI:
+ ret_val = set_wfi((unsigned char)arg);
+ break;
+ case DVFS_IOCSFREQ:
+ if (dvfs_dptc_params.dvfs_is_active == FALSE ||
+ dvfs_dptc_params.dvfs_mode == DVFS_PRED_MODE) {
+ if (arg >= 0 &&
+ arg <
+ dvfs_dptc_params.dvfs_dptc_tables_ptr->
+ dvfs_state_num) {
+ ret_val = dvfs_set_state(arg);
+ } else {
+ ret_val = -EINVAL;
+ }
+ } else {
+ ret_val = -EINVAL;
+ }
+ break;
+ case DVFS_IOCSMODE:
+#ifdef CONFIG_MXC_DVFS_SDMA
+ if (dvfs_dptc_params.dvfs_is_active == FALSE) {
+ if ((unsigned int)arg == DVFS_HW_MODE ||
+ (unsigned int)arg == DVFS_PRED_MODE) {
+ dvfs_dptc_params.dvfs_mode = (unsigned int)arg;
+ ret_val = 0;
+ } else {
+ ret_val = -EINVAL;
+ }
+ } else {
+ ret_val = -EINVAL;
+ }
+#else
+ /* Predictive mode is supported only in SDMA mode */
+ ret_val = -EINVAL;
+#endif
+ break;
+#endif /* CONFIG_MXC_DVFS */
+ case PM_IOCGFREQ:
+ clk = clk_get(NULL, "cpu_clk");
+ ret_val = clk_get_rate(clk);
+ break;
+
+ /* Unknown ioctl command -> return error */
+ default:
+ printk(KERN_ERR "Unknown ioctl command 0x%x\n", cmd);
+ ret_val = -ENOIOCTLCMD;
+ }
+
+ up(&access_mutex);
+
+ return ret_val;
+}
+
+#ifndef CONFIG_MXC_DVFS_SDMA
+/*!
+ * This function is the DPTC & DVFS Interrupt handler.
+ * This function wakes-up the dvfs_dptc_workqueue_handler function that handles the
+ * DPTC interrupt.
+ *
+ * @param irq The Interrupt number
+ * @param dev_id Driver private data
+ *
+ * @result The function returns \b IRQ_RETVAL(1) if interrupt was handled,
+ * returns \b IRQ_RETVAL(0) if the interrupt was not handled.
+ * \b IRQ_RETVAL is defined in include/linux/interrupt.h.
+ */
+static irqreturn_t dvfs_dptc_irq(int irq, void *dev_id)
+{
+
+#ifdef CONFIG_MXC_DPTC
+ if (dvfs_dptc_params.dptc_is_active == TRUE) {
+ dptc_irq();
+ }
+#endif
+
+#ifdef CONFIG_MXC_DVFS
+ if (dvfs_dptc_params.dvfs_is_active == TRUE) {
+ dvfs_irq(&dvfs_dptc_params);
+ }
+#endif
+
+ return IRQ_RETVAL(1);
+}
+#else
+/*!
+ * This function is the DPTC & DVFS SDMA callback.
+ *
+ * @param params pointer to the DVFS & DPTC driver parameters structure.
+ */
+static void dvfs_dptc_sdma_callback(dvfs_dptc_params_s * params)
+{
+ dma_request_t sdma_request_params;
+ int i;
+
+ for (i = 0; i < DVFS_LB_SDMA_BD; i++) {
+ mxc_dma_get_config(params->sdma_channel,
+ &sdma_request_params, i);
+
+ if (sdma_request_params.bd_error == 1) {
+ printk(KERN_WARNING
+ "Error in DVFS-DPTC buffer descriptor\n");
+ }
+
+ if (sdma_request_params.bd_done == 0) {
+ params->chars_in_buffer +=
+ (DVFS_LB_SIZE * DVFS_LB_SAMPLE_SIZE / 8) /
+ DVFS_LB_SDMA_BD;
+
+ if (params->chars_in_buffer >
+ (DVFS_LB_SIZE * DVFS_LB_SAMPLE_SIZE / 8)) {
+ params->chars_in_buffer =
+ DVFS_LB_SIZE * DVFS_LB_SAMPLE_SIZE / 8;
+ params->read_ptr = params->dvfs_log_buffer;
+ }
+
+ sdma_request_params.destAddr =
+ (__u8 *) (params->dvfs_log_buffer_phys +
+ i * (DVFS_LB_SIZE * DVFS_LB_SAMPLE_SIZE /
+ 8) / DVFS_LB_SDMA_BD);
+ sdma_request_params.count =
+ DVFS_LB_SIZE / DVFS_LB_SDMA_BD;
+ sdma_request_params.bd_cont = 1;
+ mxc_dma_set_config(params->sdma_channel,
+ &sdma_request_params, i);
+
+ if (params->dvfs_mode == DVFS_PRED_MODE) {
+ wake_up_interruptible(&params->dvfs_pred_wait);
+ }
+ }
+ }
+
+#ifdef CONFIG_MXC_DPTC
+ if (params->prev_wp != params->dvfs_dptc_tables_ptr->curr_wp) {
+ dptc_irq();
+ }
+#endif
+}
+#endif
+
+module_init(dvfs_dptc_init);
+module_exit(dvfs_dptc_cleanup);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("DVFS & DPTC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pm/dvfs_dptc.h b/drivers/mxc/pm/dvfs_dptc.h
new file mode 100644
index 000000000000..a3d8b1b31a15
--- /dev/null
+++ b/drivers/mxc/pm/dvfs_dptc.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2004-2007 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 dvfs_dptc.h
+ *
+ * @brief MXC dvfs & dptc header file.
+ *
+ * @ingroup PM_MX27 PM_MX31
+ */
+#ifndef __DVFS_DPTC_H__
+#define __DVFS_DPTC_H__
+
+#include <asm/arch/pm_api.h>
+#include <asm/hardware.h>
+#include <asm/arch/dvfs_dptc_struct.h>
+
+#ifdef CONFIG_ARCH_MX27
+#include <asm/arch/dma.h>
+#else
+#include <asm/arch/sdma.h>
+#endif
+
+#ifdef CONFIG_ARCH_MX3
+#include "dvfs_dptc_table_mx31.h"
+#include "dvfs_dptc_table_mx31_27ckih.h"
+#include "dvfs_dptc_table_mx31_rev2.h"
+#endif
+#ifdef CONFIG_ARCH_MX27
+#include "dvfs_dptc_table_mx27.h"
+#endif
+
+#ifdef CONFIG_MXC_DVFS
+#ifndef CONFIG_MXC_DPTC
+/*!
+ * DPTC Module Name
+ */
+#define DEVICE_NAME "dvfs"
+
+/*!
+ * DPTC driver node Name
+ */
+#define NODE_NAME "dvfs"
+#endif /* ifndef CONFIG_MXC_DPTC */
+#ifdef CONFIG_MXC_DPTC
+/*!
+ * DPTC Module Name
+ */
+#define DEVICE_NAME "dvfs_dptc"
+
+/*!
+ * DPTC driver node Name
+ */
+#define NODE_NAME "dvfs_dptc"
+#endif /* ifdef CONFIG_MXC_DPTC */
+#else /* ifdef CONFIG_MXC_DVFS */
+/*!
+ * DPTC Module Name
+ */
+#define DEVICE_NAME "dptc"
+
+/*!
+ * DPTC driver node Name
+ */
+#define NODE_NAME "dptc"
+#endif /* ifdef CONFIG_MXC_DVFS */
+
+#define MAX_TABLE_SIZE 8192
+
+#endif /* __DPTC_H__ */
diff --git a/drivers/mxc/pm/dvfs_dptc_table_mx27.h b/drivers/mxc/pm/dvfs_dptc_table_mx27.h
new file mode 100644
index 000000000000..83438707ee92
--- /dev/null
+++ b/drivers/mxc/pm/dvfs_dptc_table_mx27.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2004-2007 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 dptc.h
+ *
+ * @brief i.MX27 dptc table file.
+ *
+ * @ingroup PM_MX27
+ */
+#ifndef __DVFS_DPTC_TABLE_MX27_H__
+#define __DVFS_DPTC_TABLE_MX27_H__
+
+/*!
+ * Default DPTC table definition
+ */
+#define NUM_OF_FREQS 1
+#define NUM_OF_WP 17
+
+static char *default_table_str = "WORKING POINT 17\n\
+\n\
+WP 0x1c\n\
+WP 0x1b\n\
+WP 0x1a\n\
+WP 0x19\n\
+WP 0x18\n\
+WP 0x17\n\
+WP 0x16\n\
+WP 0x15\n\
+WP 0x14\n\
+WP 0x13\n\
+WP 0x12\n\
+WP 0x11\n\
+WP 0x10\n\
+WP 0xf\n\
+WP 0xe\n\
+WP 0xd\n\
+WP 0xc\n\
+\n\
+DCVR 0xffe00000 0x18e2e85b 0xffe00000 0x25c4688a \n\
+DCVR 0xffe00000 0x18e2e85b 0xffe00000 0x25c4688a \n\
+DCVR 0xffe00000 0x1902e85b 0xffe00000 0x25e4688a \n\
+DCVR 0xffe00000 0x1922e85b 0xffe00000 0x25e4688a \n\
+DCVR 0xffe00000 0x1942ec5b 0xffe00000 0x2604688a \n\
+DCVR 0xffe00000 0x1942ec5b 0xffe00000 0x26646c8a \n\
+DCVR 0xffe00000 0x1962ec5b 0xffe00000 0x26c4708b \n\
+DCVR 0xffe00000 0x1962ec5b 0xffe00000 0x26e4708b \n\
+DCVR 0xffe00000 0x1982f05c 0xffe00000 0x2704748b \n\
+DCVR 0xffe00000 0x19c2f05c 0xffe00000 0x2744748b \n\
+DCVR 0xffe00000 0x1a02f45c 0xffe00000 0x2784788b \n\
+DCVR 0xffe00000 0x1a42f45c 0xffe00000 0x27c47c8b \n\
+DCVR 0xffe00000 0x1a82f85c 0xffe00000 0x2824808c \n\
+DCVR 0xffe00000 0x1aa2f85c 0xffe00000 0x2884848c \n\
+DCVR 0xffe00000 0x1ac2fc5c 0xffe00000 0x28e4888c \n\
+DCVR 0xffe00000 0x1ae2fc5c 0xffe00000 0x2924888c \n\
+DCVR 0xffe00000 0x1b23005d 0xffe00000 0x29648c8c \n\
+";
+
+#endif
diff --git a/drivers/mxc/pm/dvfs_dptc_table_mx31.h b/drivers/mxc/pm/dvfs_dptc_table_mx31.h
new file mode 100644
index 000000000000..81fe80f28af8
--- /dev/null
+++ b/drivers/mxc/pm/dvfs_dptc_table_mx31.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2004-2007 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
+ */
+
+/*
+ * 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 dvfs_dptc_table_mx31.h
+ *
+ * @brief MX31 dvfs & dptc table file for CKIH clock 26MHz.
+ *
+ * @ingroup PM_MX31
+ */
+#ifndef __DVFS_DPTC_TABLE_MX31_H__
+#define __DVFS_DPTC_TABLE_MX31_H__
+
+#define NUM_OF_FREQS 4
+#define NUM_OF_WP 17
+
+/*!
+ * Default DPTC table definition.
+ * The table doesn't use PLL switch, because on DDR boards
+ * PLL switch is not possible due to HW issue.
+ * For SDR boards new table can be loaded.
+ *
+ * The table keeps the same voltage of 3.5V for frequencies lower than 399MHz.
+ * Theoretically we don't need DPTC for these frequencies,
+ * but we have to keep DPTC enabled for fluent DVFS switching
+ * back to high frequency.
+ */
+static char *default_table_str = "WORKING POINT 17\n\
+\n\
+# mc13783 switcher SW values for each working point\n\
+# The first line is for WP of highest voltage\n\
+# The first column is for highest frequency\n\
+# SW1A SW1A DVS SW1B DVS SW1B STANDBY\n\
+WP 0x1d 0x12 0x12 0x12\n\
+WP 0x1c 0x12 0x12 0x12\n\
+WP 0x1b 0x12 0x12 0x12\n\
+WP 0x1a 0x12 0x12 0x12\n\
+WP 0x19 0x12 0x12 0x12\n\
+WP 0x18 0x12 0x12 0x12\n\
+WP 0x17 0x12 0x12 0x12\n\
+WP 0x16 0x12 0x12 0x12\n\
+WP 0x15 0x12 0x12 0x12\n\
+WP 0x14 0x12 0x12 0x12\n\
+WP 0x13 0x12 0x12 0x12\n\
+WP 0x12 0x12 0x12 0x12\n\
+WP 0x12 0x12 0x12 0x12\n\
+WP 0x12 0x12 0x12 0x12\n\
+WP 0x12 0x12 0x12 0x12\n\
+WP 0x12 0x12 0x12 0x12\n\
+WP 0x12 0x12 0x12 0x12\n\
+\n\
+# pll_sw_up pll_sw_down pdr_up pdr_down pll_up pll_down vscnt\n\
+# 532MHz\n\
+FREQ 0 0 0xff871e58 0xff871e59 0x33280c 0x33280c 7\n\
+# 266MHz\n\
+FREQ 0 0 0xff871e58 0xff871e5b 0x33280c 0x33280c 7\n\
+# 133MHz\n\
+FREQ 0 0 0xff871e58 0xff871e5b 0x33280c 0x33280c 7\n\
+# 133MHz\n\
+FREQ 0 0 0xff871e58 0xff871e5b 0x33280c 0x33280c 7\n\
+# 532MHz\n\
+DCVR 0xffc00000 0x95c00000 0xffc00000 0xe5800000\n\
+DCVR 0xffc00000 0x95e3e8e4 0xffc00000 0xe5b6fda0\n\
+DCVR 0xffc00000 0x95e3e8e4 0xffc00000 0xe5b6fda0\n\
+DCVR 0xffc00000 0x95e3e8e8 0xffc00000 0xe5f70da4\n\
+DCVR 0xffc00000 0x9623f8e8 0xffc00000 0xe6371da8\n\
+DCVR 0xffc00000 0x966408f0 0xffc00000 0xe6b73db0\n\
+DCVR 0xffc00000 0x96e428f4 0xffc00000 0xe7776dbc\n\
+DCVR 0xffc00000 0x976448fc 0xffc00000 0xe8379dc8\n\
+DCVR 0xffc00000 0x97e46904 0xffc00000 0xe977ddd8\n\
+DCVR 0xffc00000 0x98a48910 0xffc00000 0xeab81de8\n\
+DCVR 0xffc00000 0x9964b918 0xffc00000 0xebf86df8\n\
+DCVR 0xffc00000 0xffe4e924 0xffc00000 0xfff8ae08\n\
+DCVR 0xffc00000 0xffe5192c 0xffc00000 0xfff8fe1c\n\
+DCVR 0xffc00000 0xffe54938 0xffc00000 0xfff95e2c\n\
+DCVR 0xffc00000 0xffe57944 0xffc00000 0xfff9ae44\n\
+DCVR 0xffc00000 0xffe5b954 0xffc00000 0xfffa0e58\n\
+DCVR 0xffc00000 0xffe5e960 0xffc00000 0xfffa6e70\n\
+\n\
+# 266MHz\n\
+DCVR 0xffc00000 0x95c00000 0xffc00000 0xe5800000\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe58dc368\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe58dc368\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe5cdc368\n\
+DCVR 0xffc00000 0x9609023c 0xffc00000 0xe60dc36c\n\
+DCVR 0xffc00000 0x9649023c 0xffc00000 0xe68dd36c\n\
+DCVR 0xffc00000 0x96c9023c 0xffc00000 0xe74dd370\n\
+DCVR 0xffc00000 0x97491240 0xffc00000 0xe80de374\n\
+DCVR 0xffc00000 0x97c92240 0xffc00000 0xe94df374\n\
+DCVR 0xffc00000 0x98892244 0xffc00000 0xea8e0378\n\
+DCVR 0xffc00000 0x99493248 0xffc00000 0xebce137c\n\
+DCVR 0xffc00000 0xffc93248 0xffc00000 0xffce3384\n\
+DCVR 0xffc00000 0xffc9424c 0xffc00000 0xffce4388\n\
+DCVR 0xffc00000 0xffc95250 0xffc00000 0xffce538c\n\
+DCVR 0xffc00000 0xffc96250 0xffc00000 0xffce7390\n\
+DCVR 0xffc00000 0xffc97254 0xffc00000 0xffce8394\n\
+DCVR 0xffc00000 0xffc98258 0xffc00000 0xffcea39c\n\
+\n\
+# 133MHz\n\
+DCVR 0xffc00000 0x95c00000 0xffc00000 0xe5800000\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe58dc368\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe58dc368\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe5cdc368\n\
+DCVR 0xffc00000 0x9609023c 0xffc00000 0xe60dc36c\n\
+DCVR 0xffc00000 0x9649023c 0xffc00000 0xe68dd36c\n\
+DCVR 0xffc00000 0x96c9023c 0xffc00000 0xe74dd370\n\
+DCVR 0xffc00000 0x97491240 0xffc00000 0xe80de374\n\
+DCVR 0xffc00000 0x97c92240 0xffc00000 0xe94df374\n\
+DCVR 0xffc00000 0x98892244 0xffc00000 0xea8e0378\n\
+DCVR 0xffc00000 0x99493248 0xffc00000 0xebce137c\n\
+DCVR 0xffc00000 0xffc93248 0xffc00000 0xffce3384\n\
+DCVR 0xffc00000 0xffc9424c 0xffc00000 0xffce4388\n\
+DCVR 0xffc00000 0xffc95250 0xffc00000 0xffce538c\n\
+DCVR 0xffc00000 0xffc96250 0xffc00000 0xffce7390\n\
+DCVR 0xffc00000 0xffc97254 0xffc00000 0xffce8394\n\
+DCVR 0xffc00000 0xffc98258 0xffc00000 0xffcea39c\n\
+\n\
+# 133MHz\n\
+DCVR 0xffc00000 0x95c00000 0xffc00000 0xe5800000\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe58dc368\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe58dc368\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe5cdc368\n\
+DCVR 0xffc00000 0x9609023c 0xffc00000 0xe60dc36c\n\
+DCVR 0xffc00000 0x9649023c 0xffc00000 0xe68dd36c\n\
+DCVR 0xffc00000 0x96c9023c 0xffc00000 0xe74dd370\n\
+DCVR 0xffc00000 0x97491240 0xffc00000 0xe80de374\n\
+DCVR 0xffc00000 0x97c92240 0xffc00000 0xe94df374\n\
+DCVR 0xffc00000 0x98892244 0xffc00000 0xea8e0378\n\
+DCVR 0xffc00000 0x99493248 0xffc00000 0xebce137c\n\
+DCVR 0xffc00000 0xffc93248 0xffc00000 0xffce3384\n\
+DCVR 0xffc00000 0xffc9424c 0xffc00000 0xffce4388\n\
+DCVR 0xffc00000 0xffc95250 0xffc00000 0xffce538c\n\
+DCVR 0xffc00000 0xffc96250 0xffc00000 0xffce7390\n\
+DCVR 0xffc00000 0xffc97254 0xffc00000 0xffce8394\n\
+DCVR 0xffc00000 0xffc98258 0xffc00000 0xffcea39c\n\
+";
+
+#endif
diff --git a/drivers/mxc/pm/dvfs_dptc_table_mx31_27ckih.h b/drivers/mxc/pm/dvfs_dptc_table_mx31_27ckih.h
new file mode 100644
index 000000000000..ff8b520776c0
--- /dev/null
+++ b/drivers/mxc/pm/dvfs_dptc_table_mx31_27ckih.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2004-2007 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
+ */
+
+/*
+ * 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 dptc.h
+ *
+ * @brief MX31 dvfs & dptc table file for CKIH clock 27MHz.
+ *
+ * @ingroup PM_MX31
+ */
+#ifndef __DVFS_DPTC_TABLE_MX31_27CKIH_H__
+#define __DVFS_DPTC_TABLE_MX31_27CKIH_H__
+
+#define NUM_OF_FREQS 4
+#define NUM_OF_WP 17
+
+/*!
+ * Default DPTC table definition.
+ * The table doesn't use PLL switch, because on DDR boards
+ * PLL switch is not possible due to HW issue.
+ * For SDR boards new table can be loaded.
+ *
+ * The table keeps the same voltage of 1.35V for frequencies lower than 399MHz.
+ * Theoretically we don't need DPTC for these frequencies,
+ * but we have to keep DPTC enabled for fluent DVFS switching
+ * back to high frequency.
+ */
+static char *default_table_str_27ckih = "WORKING POINT 17\n\
+WP 0x1d 0x12 0x12 0x12\n\
+WP 0x1c 0x12 0x12 0x12\n\
+WP 0x1b 0x12 0x12 0x12\n\
+WP 0x1a 0x12 0x12 0x12\n\
+WP 0x19 0x12 0x12 0x12\n\
+WP 0x18 0x12 0x12 0x12\n\
+WP 0x17 0x12 0x12 0x12\n\
+WP 0x16 0x12 0x12 0x12\n\
+WP 0x15 0x12 0x12 0x12\n\
+WP 0x14 0x12 0x12 0x12\n\
+WP 0x13 0x12 0x12 0x12\n\
+WP 0x12 0x12 0x12 0x12\n\
+WP 0x11 0x12 0x12 0x12\n\
+WP 0x10 0x12 0x12 0x12\n\
+WP 0xf 0x12 0x12 0x12\n\
+WP 0xe 0x12 0x12 0x12\n\
+WP 0xd 0x12 0x12 0x12\n\
+\n\
+# pll_sw_up pll_sw_down pdr_up pdr_down pll_up pll_down vscnt\n\
+# 532MHz\n\
+FREQ 0 0 0xff871e58 0xff871e59 0xe240d 0xe240d 7\n\
+# 266MHz\n\
+FREQ 0 0 0xff871e58 0xff871e5b 0xe240d 0xe240d 7\n\
+# 133MHz\n\
+FREQ 0 0 0xff871e58 0xff871e5b 0xe240d 0xe240d 7\n\
+# 133MHz\n\
+FREQ 0 0 0xff871e58 0xff871e5b 0xe240d 0xe240d 7\n\
+# 532MHz\n\
+DCVR 0xffc00000 0x90400000 0xffc00000 0xdd000000\n\
+DCVR 0xffc00000 0x90629890 0xffc00000 0xdd34ed20\n\
+DCVR 0xffc00000 0x90629890 0xffc00000 0xdd34ed20\n\
+DCVR 0xffc00000 0x90629894 0xffc00000 0xdd74fd24\n\
+DCVR 0xffc00000 0x90a2a894 0xffc00000 0xddb50d28\n\
+DCVR 0xffc00000 0x90e2b89c 0xffc00000 0xde352d30\n\
+DCVR 0xffc00000 0x9162d8a0 0xffc00000 0xdef55d38\n\
+DCVR 0xffc00000 0x91e2f8a8 0xffc00000 0xdfb58d44\n\
+DCVR 0xffc00000 0x926308b0 0xffc00000 0xe0b5cd54\n\
+DCVR 0xffc00000 0x92e328bc 0xffc00000 0xe1f60d64\n\
+DCVR 0xffc00000 0x93a358c0 0xffc00000 0xe3365d74\n\
+DCVR 0xffc00000 0xf66388cc 0xffc00000 0xf6768d84\n\
+DCVR 0xffc00000 0xf663b8d4 0xffc00000 0xf676dd98\n\
+DCVR 0xffc00000 0xf663e8e0 0xffc00000 0xf6773da4\n\
+DCVR 0xffc00000 0xf66418ec 0xffc00000 0xf6778dbc\n\
+DCVR 0xffc00000 0xf66458fc 0xffc00000 0xf677edd0\n\
+DCVR 0xffc00000 0xf6648908 0xffc00000 0xf6783de8\n\
+\n\
+# 266MHz\n\
+DCVR 0xffc00000 0x90400000 0xffc00000 0xdd000000\n\
+DCVR 0xffc00000 0x9048a224 0xffc00000 0xdd0d4348\n\
+DCVR 0xffc00000 0x9048a224 0xffc00000 0xdd0d4348\n\
+DCVR 0xffc00000 0x9048a224 0xffc00000 0xdd4d4348\n\
+DCVR 0xffc00000 0x9088b228 0xffc00000 0xdd8d434c\n\
+DCVR 0xffc00000 0x90c8b228 0xffc00000 0xde0d534c\n\
+DCVR 0xffc00000 0x9148b228 0xffc00000 0xdecd5350\n\
+DCVR 0xffc00000 0x91c8c22c 0xffc00000 0xdf8d6354\n\
+DCVR 0xffc00000 0x9248d22c 0xffc00000 0xe08d7354\n\
+DCVR 0xffc00000 0x92c8d230 0xffc00000 0xe1cd8358\n\
+DCVR 0xffc00000 0x9388e234 0xffc00000 0xe30d935c\n\
+DCVR 0xffc00000 0xf648e234 0xffc00000 0xf64db364\n\
+DCVR 0xffc00000 0xf648f238 0xffc00000 0xf64dc368\n\
+DCVR 0xffc00000 0xf648f23c 0xffc00000 0xf64dd36c\n\
+DCVR 0xffc00000 0xf649023c 0xffc00000 0xf64de370\n\
+DCVR 0xffc00000 0xf649123c 0xffc00000 0xf64df374\n\
+DCVR 0xffc00000 0xf6492240 0xffc00000 0xf64e1378\n\
+\n\
+# 133MHz\n\
+DCVR 0xffc00000 0x90400000 0xffc00000 0xdd000000\n\
+DCVR 0xffc00000 0x9048a224 0xffc00000 0xdd0d4348\n\
+DCVR 0xffc00000 0x9048a224 0xffc00000 0xdd0d4348\n\
+DCVR 0xffc00000 0x9048a224 0xffc00000 0xdd4d4348\n\
+DCVR 0xffc00000 0x9088b228 0xffc00000 0xdd8d434c\n\
+DCVR 0xffc00000 0x90c8b228 0xffc00000 0xde0d534c\n\
+DCVR 0xffc00000 0x9148b228 0xffc00000 0xdecd5350\n\
+DCVR 0xffc00000 0x91c8c22c 0xffc00000 0xdf8d6354\n\
+DCVR 0xffc00000 0x9248d22c 0xffc00000 0xe08d7354\n\
+DCVR 0xffc00000 0x92c8d230 0xffc00000 0xe1cd8358\n\
+DCVR 0xffc00000 0x9388e234 0xffc00000 0xe30d935c\n\
+DCVR 0xffc00000 0xf648e234 0xffc00000 0xf64db364\n\
+DCVR 0xffc00000 0xf648f238 0xffc00000 0xf64dc368\n\
+DCVR 0xffc00000 0xf648f23c 0xffc00000 0xf64dd36c\n\
+DCVR 0xffc00000 0xf649023c 0xffc00000 0xf64de370\n\
+DCVR 0xffc00000 0xf649123c 0xffc00000 0xf64df374\n\
+DCVR 0xffc00000 0xf6492240 0xffc00000 0xf64e1378\n\
+\n\
+# 133MHz\n\
+DCVR 0xffc00000 0x90400000 0xffc00000 0xdd000000\n\
+DCVR 0xffc00000 0x9048a224 0xffc00000 0xdd0d4348\n\
+DCVR 0xffc00000 0x9048a224 0xffc00000 0xdd0d4348\n\
+DCVR 0xffc00000 0x9048a224 0xffc00000 0xdd4d4348\n\
+DCVR 0xffc00000 0x9088b228 0xffc00000 0xdd8d434c\n\
+DCVR 0xffc00000 0x90c8b228 0xffc00000 0xde0d534c\n\
+DCVR 0xffc00000 0x9148b228 0xffc00000 0xdecd5350\n\
+DCVR 0xffc00000 0x91c8c22c 0xffc00000 0xdf8d6354\n\
+DCVR 0xffc00000 0x9248d22c 0xffc00000 0xe08d7354\n\
+DCVR 0xffc00000 0x92c8d230 0xffc00000 0xe1cd8358\n\
+DCVR 0xffc00000 0x9388e234 0xffc00000 0xe30d935c\n\
+DCVR 0xffc00000 0xf648e234 0xffc00000 0xf64db364\n\
+DCVR 0xffc00000 0xf648f238 0xffc00000 0xf64dc368\n\
+DCVR 0xffc00000 0xf648f23c 0xffc00000 0xf64dd36c\n\
+DCVR 0xffc00000 0xf649023c 0xffc00000 0xf64de370\n\
+DCVR 0xffc00000 0xf649123c 0xffc00000 0xf64df374\n\
+DCVR 0xffc00000 0xf6492240 0xffc00000 0xf64e1378\n\
+";
+
+#endif
diff --git a/drivers/mxc/pm/dvfs_dptc_table_mx31_rev2.h b/drivers/mxc/pm/dvfs_dptc_table_mx31_rev2.h
new file mode 100644
index 000000000000..cb0f64c98749
--- /dev/null
+++ b/drivers/mxc/pm/dvfs_dptc_table_mx31_rev2.h
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2004-2007 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
+ */
+
+/*
+ * 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 dvfs_dptc_table_mx31_rev2.h
+ *
+ * @brief MX31 dvfs & dptc table file for MX31 2.0
+ *
+ * @ingroup PM_MX31
+ */
+#ifndef __DVFS_DPTC_TABLE_MX3_REV2_H__
+#define __DVFS_DPTC_TABLE_MX3_REV2_H__
+
+#define NUM_OF_FREQS 4
+#define NUM_OF_WP 17
+
+/*!
+ * Default DPTC table definition.
+ * The table doesn't use PLL switch, because on DDR boards
+ * PLL switch is not possible due to HW issue.
+ * For SDR boards new table can be loaded.
+ *
+ * The table keeps the same voltage of 3.5V for frequencies lower than 399MHz.
+ * Theoretically we don't need DPTC for these frequencies,
+ * but we have to keep DPTC enabled for fluent DVFS switching
+ * back to high frequency.
+ */
+static char *default_table_str_rev2 = "WORKING POINT 17\n\
+\n\
+# mc13783 switcher SW values for each working point\n\
+# The first line is for WP of highest voltage\n\
+# The first column is for highest frequency\n\
+# SW1A SW1A DVS SW1B DVS SW1B STANDBY\n\
+WP 0x1d 0xc 0xc 0xc\n\
+WP 0x1c 0xc 0xc 0xc\n\
+WP 0x1b 0xc 0xc 0xc\n\
+WP 0x1a 0xc 0xc 0xc\n\
+WP 0x19 0xc 0xc 0xc\n\
+WP 0x18 0xc 0xc 0xc\n\
+WP 0x17 0xc 0xc 0xc\n\
+WP 0x16 0xc 0xc 0xc\n\
+WP 0x15 0xc 0xc 0xc\n\
+WP 0x14 0xc 0xc 0xc\n\
+WP 0x13 0xc 0xc 0xc\n\
+WP 0x12 0xc 0xc 0xc\n\
+WP 0x11 0xc 0xc 0xc\n\
+WP 0x10 0xc 0xc 0xc\n\
+WP 0xf 0xc 0xc 0xc\n\
+WP 0xe 0xc 0xc 0xc\n\
+WP 0xd 0xc 0xc 0xc\n\
+\n\
+# pll_sw_up pll_sw_down pdr_up pdr_down pll_up pll_down vscnt\n\
+# 532MHz\n\
+FREQ 0 1 0xff871e58 0xff871650 0x0033280c 0x00331c23 1\n\
+# 399MHz\n\
+FREQ 1 1 0xff871e58 0xff871e59 0x0033280c 0x0033280c 4\n\
+# 266MHz\n\
+FREQ 0 0 0xff871e58 0xff871e5b 0x0033280c 0x0033280c 4\n\
+# 133MHz\n\
+FREQ 0 0 0xff871e58 0xff871e5b 0x0033280c 0x0033280c 4\n\
+\n\
+# 532MHz\n\
+DCVR 0xffc00000 0x95c00000 0xffc00000 0xe5800000\n\
+DCVR 0xffc00000 0x95e3e8e4 0xffc00000 0xe5b6fda0\n\
+DCVR 0xffc00000 0x95e3e8e4 0xffc00000 0xe5b6fda0\n\
+DCVR 0xffc00000 0x95e3e8e8 0xffc00000 0xe5f70da4\n\
+DCVR 0xffc00000 0x9623f8e8 0xffc00000 0xe6371da8\n\
+DCVR 0xffc00000 0x966408f0 0xffc00000 0xe6b73db0\n\
+DCVR 0xffc00000 0x96e428f4 0xffc00000 0xe7776dbc\n\
+DCVR 0xffc00000 0x976448fc 0xffc00000 0xe8379dc8\n\
+DCVR 0xffc00000 0x97e46904 0xffc00000 0xe977ddd8\n\
+DCVR 0xffc00000 0x98a48910 0xffc00000 0xeab81de8\n\
+DCVR 0xffc00000 0x9964b918 0xffc00000 0xebf86df8\n\
+DCVR 0xffc00000 0xffe4e924 0xffc00000 0xfff8ae08\n\
+DCVR 0xffc00000 0xffe5192c 0xffc00000 0xfff8fe1c\n\
+DCVR 0xffc00000 0xffe54938 0xffc00000 0xfff95e2c\n\
+DCVR 0xffc00000 0xffe57944 0xffc00000 0xfff9ae44\n\
+DCVR 0xffc00000 0xffe5b954 0xffc00000 0xfffa0e58\n\
+DCVR 0xffc00000 0xffe5e960 0xffc00000 0xfffa6e70\n\
+\n\
+# 266MHz\n\
+DCVR 0xffc00000 0x95c00000 0xffc00000 0xe5800000\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe58dc368\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe58dc368\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe5cdc368\n\
+DCVR 0xffc00000 0x9609023c 0xffc00000 0xe60dc36c\n\
+DCVR 0xffc00000 0x9649023c 0xffc00000 0xe68dd36c\n\
+DCVR 0xffc00000 0x96c9023c 0xffc00000 0xe74dd370\n\
+DCVR 0xffc00000 0x97491240 0xffc00000 0xe80de374\n\
+DCVR 0xffc00000 0x97c92240 0xffc00000 0xe94df374\n\
+DCVR 0xffc00000 0x98892244 0xffc00000 0xea8e0378\n\
+DCVR 0xffc00000 0x99493248 0xffc00000 0xebce137c\n\
+DCVR 0xffc00000 0xffc93248 0xffc00000 0xffce3384\n\
+DCVR 0xffc00000 0xffc9424c 0xffc00000 0xffce4388\n\
+DCVR 0xffc00000 0xffc95250 0xffc00000 0xffce538c\n\
+DCVR 0xffc00000 0xffc96250 0xffc00000 0xffce7390\n\
+DCVR 0xffc00000 0xffc97254 0xffc00000 0xffce8394\n\
+DCVR 0xffc00000 0xffc98258 0xffc00000 0xffcea39c\n\
+\n\
+# 133MHz\n\
+DCVR 0xffc00000 0x95c00000 0xffc00000 0xe5800000\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe58dc368\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe58dc368\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe5cdc368\n\
+DCVR 0xffc00000 0x9609023c 0xffc00000 0xe60dc36c\n\
+DCVR 0xffc00000 0x9649023c 0xffc00000 0xe68dd36c\n\
+DCVR 0xffc00000 0x96c9023c 0xffc00000 0xe74dd370\n\
+DCVR 0xffc00000 0x97491240 0xffc00000 0xe80de374\n\
+DCVR 0xffc00000 0x97c92240 0xffc00000 0xe94df374\n\
+DCVR 0xffc00000 0x98892244 0xffc00000 0xea8e0378\n\
+DCVR 0xffc00000 0x99493248 0xffc00000 0xebce137c\n\
+DCVR 0xffc00000 0xffc93248 0xffc00000 0xffce3384\n\
+DCVR 0xffc00000 0xffc9424c 0xffc00000 0xffce4388\n\
+DCVR 0xffc00000 0xffc95250 0xffc00000 0xffce538c\n\
+DCVR 0xffc00000 0xffc96250 0xffc00000 0xffce7390\n\
+DCVR 0xffc00000 0xffc97254 0xffc00000 0xffce8394\n\
+DCVR 0xffc00000 0xffc98258 0xffc00000 0xffcea39c\n\
+\n\
+# 133MHz\n\
+DCVR 0xffc00000 0x95c00000 0xffc00000 0xe5800000\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe58dc368\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe58dc368\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe5cdc368\n\
+DCVR 0xffc00000 0x9609023c 0xffc00000 0xe60dc36c\n\
+DCVR 0xffc00000 0x9649023c 0xffc00000 0xe68dd36c\n\
+DCVR 0xffc00000 0x96c9023c 0xffc00000 0xe74dd370\n\
+DCVR 0xffc00000 0x97491240 0xffc00000 0xe80de374\n\
+DCVR 0xffc00000 0x97c92240 0xffc00000 0xe94df374\n\
+DCVR 0xffc00000 0x98892244 0xffc00000 0xea8e0378\n\
+DCVR 0xffc00000 0x99493248 0xffc00000 0xebce137c\n\
+DCVR 0xffc00000 0xffc93248 0xffc00000 0xffce3384\n\
+DCVR 0xffc00000 0xffc9424c 0xffc00000 0xffce4388\n\
+DCVR 0xffc00000 0xffc95250 0xffc00000 0xffce538c\n\
+DCVR 0xffc00000 0xffc96250 0xffc00000 0xffce7390\n\
+DCVR 0xffc00000 0xffc97254 0xffc00000 0xffce8394\n\
+DCVR 0xffc00000 0xffc98258 0xffc00000 0xffcea39c\n\
+";
+
+#endif
diff --git a/drivers/mxc/ssi/Kconfig b/drivers/mxc/ssi/Kconfig
new file mode 100644
index 000000000000..f4b8803bec52
--- /dev/null
+++ b/drivers/mxc/ssi/Kconfig
@@ -0,0 +1,13 @@
+#
+# 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
index 000000000000..c0c88729026d
--- /dev/null
+++ b/drivers/mxc/ssi/Makefile
@@ -0,0 +1,9 @@
+#
+# 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
index 000000000000..ac91de8ea49f
--- /dev/null
+++ b/drivers/mxc/ssi/registers.h
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2004-2007 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 <asm/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
+
+/*! 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
+
+/*! 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
+
+/*!
+ * 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
+
+/*!
+ * 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
+/*!
+ * SISR Register definition
+ */
+#define SSI_IRQ_STATUS_NUMBER 19
+/*!
+ * 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
index 000000000000..244bd9ad818d
--- /dev/null
+++ b/drivers/mxc/ssi/ssi.c
@@ -0,0 +1,1155 @@
+/*
+ * Copyright 2004-2007 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/slab.h>
+#include <asm/uaccess.h>
+#include <asm/arch/clock.h>
+
+#include "registers.h"
+#include "ssi.h"
+
+static spinlock_t ssi_lock;
+
+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);
+
+unsigned int get_ssi_base_addr(unsigned int ssi)
+{
+ unsigned int base_addr;
+ base_addr = (ssi == SSI1) ? IO_ADDRESS(SSI1_BASE_ADDR) :
+ IO_ADDRESS(SSI2_BASE_ADDR);
+ return base_addr;
+}
+
+void set_register_bits(unsigned int mask, unsigned int data,
+ unsigned int offset, unsigned int ssi)
+{
+ volatile unsigned long reg = 0;
+ unsigned int base_addr = 0;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&ssi_lock, flags);
+ base_addr = get_ssi_base_addr(ssi);
+ 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)
+{
+ volatile unsigned long reg = 0;
+ unsigned int base_addr = 0;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&ssi_lock, flags);
+ base_addr = get_ssi_base_addr(ssi);
+ reg = __raw_readl(base_addr + offset);
+ spin_unlock_irqrestore(&ssi_lock, flags);
+
+ return reg;
+}
+
+void set_register(unsigned int data, unsigned int offset, unsigned int ssi)
+{
+ unsigned int base_addr = 0;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&ssi_lock, flags);
+ base_addr = get_ssi_base_addr(ssi);
+ __raw_writel(data, base_addr + offset);
+ spin_unlock_irqrestore(&ssi_lock, flags);
+
+}
+
+/*!
+ * 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 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);
+ printk(KERN_INFO "SSI module loaded successfully\n");
+ return 0;
+}
+
+/*!
+ * 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)
+{
+ 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
index 000000000000..01078ae87325
--- /dev/null
+++ b/drivers/mxc/ssi/ssi.h
@@ -0,0 +1,565 @@
+/*
+ * Copyright 2004-2007 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 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
index 000000000000..793faf9be7e5
--- /dev/null
+++ b/drivers/mxc/ssi/ssi_types.h
@@ -0,0 +1,359 @@
+/*
+ * Copyright 2004-2007 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
+ */
+ 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_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
index 000000000000..c4e64197074e
--- /dev/null
+++ b/drivers/mxc/vpu/Kconfig
@@ -0,0 +1,22 @@
+#
+# 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)
+ default y
+ ---help---
+ The VPU codec device provides codec function for H.264/MPEG4/H.263
+
+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
index 000000000000..88e8f2c084a0
--- /dev/null
+++ b/drivers/mxc/vpu/Makefile
@@ -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
index 000000000000..cd062d28aef5
--- /dev/null
+++ b/drivers/mxc/vpu/mxc_vl2cc.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2007 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 <asm/hardware.h>
+#include <asm/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
index 000000000000..be9e443f83a8
--- /dev/null
+++ b/drivers/mxc/vpu/mxc_vpu.c
@@ -0,0 +1,489 @@
+/*
+ * Copyright 2006-2007 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/autoconf.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/wait.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/sizes.h>
+#include <asm/dma-mapping.h>
+#include <asm/hardware.h>
+
+#include <asm/arch/mxc_vpu.h>
+
+#define BIT_INT_CLEAR 0x00C
+#define BIT_INT_STATUS 0x010
+#define BIT_INT_ENABLE 0x170
+
+typedef struct vpu_t {
+ struct fasync_struct *async_queue;
+} vpu_t;
+
+/* To track the allocated memory buffer */
+typedef struct memalloc_record {
+ struct list_head list;
+ vpu_mem_desc mem;
+} memalloc_record;
+
+static DEFINE_SPINLOCK(vpu_lock);
+static LIST_HEAD(head);
+
+static int vpu_major = 0;
+static struct class *vpu_class;
+static struct vpu_t vpu_data;
+static u8 open_count = 0;
+static struct clk *vpu_clk;
+
+/* implement the blocking ioctl */
+static int codec_done = 0;
+static wait_queue_head_t vpu_queue;
+
+/*!
+ * Private function to free buffers
+ * @return status 0 success.
+ */
+static int vpu_free_buffers(void)
+{
+ struct memalloc_record *rec, *n;
+ vpu_mem_desc mem;
+
+ spin_lock(&vpu_lock);
+ list_for_each_entry_safe(rec, n, &head, list) {
+ mem = rec->mem;
+ if (mem.cpu_addr != 0) {
+ dma_free_coherent(0, PAGE_ALIGN(mem.size),
+ (void *)mem.cpu_addr, mem.phy_addr);
+ pr_debug("[FREE] freed paddr=0x%08X\n", mem.phy_addr);
+
+ /* delete from list */
+ list_del(&rec->list);
+ kfree(rec);
+ }
+ }
+ spin_unlock(&vpu_lock);
+
+ return 0;
+}
+
+/*!
+ * @brief vpu interrupt handler
+ */
+static irqreturn_t vpu_irq_handler(int irq, void *dev_id)
+{
+ struct vpu_t *dev;
+ dev = (struct vpu_t *)dev_id;
+ __raw_readl(IO_ADDRESS(VPU_BASE_ADDR + BIT_INT_STATUS));
+ __raw_writel(0x1, IO_ADDRESS(VPU_BASE_ADDR + BIT_INT_CLEAR));
+ if (dev->async_queue)
+ kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
+
+ codec_done = 1;
+ wake_up_interruptible(&vpu_queue);
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * @brief vpu hardware enable function
+ *
+ * @return 0 on success or negative error code on error
+ */
+static int vpu_hardware_enable(void)
+{
+ if (cpu_is_mx32()) {
+ vl2cc_enable();
+ }
+ clk_enable(vpu_clk);
+ return 0;
+}
+
+/*!
+ * @brief vpu hardware disable function
+ *
+ * @return 0 on success or negative error code on error
+ */
+static int vpu_hardware_disable(void)
+{
+ if (cpu_is_mx32()) {
+ vl2cc_disable();
+ }
+ clk_disable(vpu_clk);
+ return 0;
+
+}
+
+/*!
+ * @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)
+{
+ if (open_count++ == 0) {
+ filp->private_data = (void *)(&vpu_data);
+ vpu_hardware_enable();
+ } else {
+ printk(KERN_ERR "VPU has already been opened.\n");
+ return -EACCES;
+ }
+
+ 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 int vpu_ioctl(struct inode *inode, 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), (vpu_mem_desc *) arg,
+ sizeof(vpu_mem_desc));
+ if (ret) {
+ kfree(rec);
+ return -EFAULT;
+ }
+
+ pr_debug("[ALLOC] mem alloc size = 0x%x\n",
+ rec->mem.size);
+ rec->mem.cpu_addr = (unsigned long)
+ dma_alloc_coherent(NULL,
+ PAGE_ALIGN(rec->mem.size),
+ (dma_addr_t
+ *) (&(rec->mem.phy_addr)),
+ GFP_DMA | GFP_KERNEL);
+ pr_debug("[ALLOC] mem alloc cpu_addr = 0x%x\n",
+ rec->mem.cpu_addr);
+ if ((void *)(rec->mem.cpu_addr) == NULL) {
+ kfree(rec);
+ printk(KERN_ERR
+ "Physical memory allocation error!\n");
+ ret = -1;
+ break;
+ }
+ ret = copy_to_user((void __user *)arg, &(rec->mem),
+ sizeof(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;
+ vpu_mem_desc vpu_mem;
+
+ ret = copy_from_user(&vpu_mem, (vpu_mem_desc *) arg,
+ sizeof(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) {
+ dma_free_coherent(NULL,
+ PAGE_ALIGN(vpu_mem.size),
+ (void *)vpu_mem.cpu_addr,
+ (dma_addr_t) vpu_mem.
+ phy_addr);
+ }
+
+ 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;
+ }
+
+ codec_done = 0;
+ break;
+ }
+ case VPU_IOC_VL2CC_FLUSH:
+ if (cpu_is_mx32()) {
+ vl2cc_flush();
+ }
+ 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)
+{
+ if (--open_count == 0) {
+ vpu_free_buffers();
+ vpu_hardware_disable();
+ }
+
+ 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_t *dev = (struct vpu_t *)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 = 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_noncached(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,
+ .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 class_device *temp_class;
+ struct resource *res;
+
+ if (cpu_is_mx32()) {
+ /* Obtain VL2CC base address */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ printk(KERN_ERR "vpu: unable to get VL2CC base\n");
+ return -ENOENT;
+ }
+
+ err = vl2cc_init(res->start);
+ if (err != 0)
+ return err;
+ }
+
+ 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 = 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;
+ }
+
+ err = request_irq(INT_VPU, vpu_irq_handler, 0, "VPU_CODEC_IRQ",
+ (void *)(&vpu_data));
+ if (err)
+ goto err_out_class;
+
+ printk(KERN_INFO "VPU initialized\n");
+ goto out;
+
+ err_out_class:
+ class_device_destroy(vpu_class, MKDEV(vpu_major, 0));
+ class_destroy(vpu_class);
+ err_out_chrdev:
+ unregister_chrdev(vpu_major, "mxc_vpu");
+ error:
+ if (cpu_is_mx32()) {
+ vl2cc_cleanup();
+ }
+ out:
+ return err;
+}
+
+/*! Driver definition
+ *
+ */
+static struct platform_driver mxcvpu_driver = {
+ .driver = {
+ .name = "mxc_vpu",
+ },
+ .probe = vpu_dev_probe,
+};
+
+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)
+{
+ free_irq(INT_VPU, (void *)(&vpu_data));
+ if (vpu_major > 0) {
+ class_device_destroy(vpu_class, MKDEV(vpu_major, 0));
+ class_destroy(vpu_class);
+ if (unregister_chrdev(vpu_major, "mxc_vpu") < 0) {
+ printk(KERN_ERR
+ "Failed to unregister vpu from devfs\n");
+ return;
+ }
+ vpu_major = 0;
+ }
+
+ if (cpu_is_mx32()) {
+ vl2cc_cleanup();
+ }
+
+ clk_put(vpu_clk);
+
+ platform_driver_unregister(&mxcvpu_driver);
+ return;
+}
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Linux VPU driver for Freescale i.MX27");
+MODULE_LICENSE("GPL");
+
+module_init(vpu_init);
+module_exit(vpu_exit);