Initial Contribution

msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142

Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/arch/arm/mach-msm/qdsp5v2/mi2s.c b/arch/arm/mach-msm/qdsp5v2/mi2s.c
new file mode 100644
index 0000000..e38f164
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/mi2s.c
@@ -0,0 +1,885 @@
+/* Copyright (c) 2009,2011 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+
+#include <mach/qdsp5v2/mi2s.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+
+#define DEBUG
+#ifdef DEBUG
+#define dprintk(format, arg...) \
+printk(KERN_DEBUG format, ## arg)
+#else
+#define dprintk(format, arg...) do {} while (0)
+#endif
+
+/*----------------------------------------------------------------------------
+ * Preprocessor Definitions and Constants
+ * -------------------------------------------------------------------------*/
+
+/* Device Types */
+#define HDMI 0
+#define CODEC_RX 1
+#define CODEC_TX 2
+
+/* Static offset for now. If different target have different
+ * offset, update to platform data model
+ */
+#define MI2S_RESET_OFFSET   0x0
+#define MI2S_MODE_OFFSET    0x4
+#define MI2S_TX_MODE_OFFSET 0x8
+#define MI2S_RX_MODE_OFFSET 0xc
+
+#define MI2S_SD_N_EN_MASK 0xF0
+#define MI2S_TX_RX_N_MASK 0x0F
+
+#define MI2S_RESET__MI2S_RESET__RESET  0x1
+#define MI2S_RESET__MI2S_RESET__ACTIVE 0x0
+#define MI2S_MODE__MI2S_MASTER__MASTER 0x1
+#define MI2S_MODE__MI2S_MASTER__SLAVE  0x0
+#define MI2S_MODE__MI2S_TX_RX_WORD_TYPE__16_BIT 0x1
+#define MI2S_MODE__MI2S_TX_RX_WORD_TYPE__24_BIT 0x2
+#define MI2S_MODE__MI2S_TX_RX_WORD_TYPE__32_BIT 0x3
+#define MI2S_TX_MODE__MI2S_TX_CODEC_16_MONO_MODE__RAW 0x0
+#define MI2S_TX_MODE__MI2S_TX_CODEC_16_MONO_MODE__PACKED 0x1
+#define MI2S_TX_MODE__MI2S_TX_STEREO_MODE__MONO_SAMPLE   0x0
+#define MI2S_TX_MODE__MI2S_TX_STEREO_MODE__STEREO_SAMPLE 0x1
+#define MI2S_TX_MODE__MI2S_TX_CH_TYPE__2_CHANNEL 0x0
+#define MI2S_TX_MODE__MI2S_TX_CH_TYPE__4_CHANNEL 0x1
+#define MI2S_TX_MODE__MI2S_TX_CH_TYPE__6_CHANNEL 0x2
+#define MI2S_TX_MODE__MI2S_TX_CH_TYPE__8_CHANNEL 0x3
+#define MI2S_TX_MODE__MI2S_TX_DMA_ACK_SYNCH_EN__SYNC_ENABLE 0x1
+#define MI2S_RX_MODE__MI2S_RX_CODEC_16_MONO_MODE__RAW 0x0
+#define MI2S_RX_MODE__MI2S_RX_CODEC_16_MONO_MODE__PACKED 0x1
+#define MI2S_RX_MODE__MI2S_RX_STEREO_MODE__MONO_SAMPLE   0x0
+#define MI2S_RX_MODE__MI2S_RX_STEREO_MODE__STEREO_SAMPLE 0x1
+#define MI2S_RX_MODE__MI2S_RX_CH_TYPE__2_CH 0x0
+#define MI2S_RX_MODE__MI2S_RX_DMA_ACK_SYNCH_EN__SYNC_ENABLE 0x1
+
+#define HWIO_AUDIO1_MI2S_MODE_MI2S_MASTER_BMSK				0x1000
+#define HWIO_AUDIO1_MI2S_MODE_MI2S_MASTER_SHFT				0xC
+#define HWIO_AUDIO1_MI2S_MODE_MI2S_TX_RX_WORD_TYPE_BMSK  		0x300
+#define HWIO_AUDIO1_MI2S_MODE_MI2S_TX_RX_WORD_TYPE_SHFT  		0x8
+#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_BMSK		0x4
+#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_SHFT		0x2
+#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_P_MONO_BMSK                    0x2
+#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_P_MONO_SHFT                    0x1
+#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_BMSK			0x18
+#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_SHFT			0x3
+#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_4_0_CH_MAP_BMSK			0x80
+#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_4_0_CH_MAP_SHFT			0x7
+#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_2_0_CH_MAP_BMSK			0x60
+#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_2_0_CH_MAP_SHFT			0x5
+#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_DMA_ACK_SYNCH_EN_BMSK		0x1
+#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_I2S_LINE_BMSK			0x60
+#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_I2S_LINE_SHFT			0x5
+#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_BMSK		0x4
+#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_SHFT		0x2
+#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CODEC_P_MONO_BMSK              0x2
+#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CODEC_P_MONO_SHFT              0x1
+#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CH_TYPE_BMSK			0x18
+#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CH_TYPE_SHFT			0x3
+#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_DMA_ACK_SYNCH_EN_BMSK		0x1
+
+/* Max number of channels */
+#define MAX_NUM_CHANNELS_OUT 8
+#define MAX_NUM_CHANNELS_IN  2
+
+/* Num of SD Lines */
+#define MAX_SD_LINES 4
+
+#define MI2S_SD_0_EN_MAP  0x10
+#define MI2S_SD_1_EN_MAP  0x20
+#define MI2S_SD_2_EN_MAP  0x40
+#define MI2S_SD_3_EN_MAP  0x80
+#define MI2S_SD_0_TX_MAP  0x01
+#define MI2S_SD_1_TX_MAP  0x02
+#define MI2S_SD_2_TX_MAP  0x04
+#define MI2S_SD_3_TX_MAP  0x08
+
+struct mi2s_state {
+	void __iomem *mi2s_hdmi_base;
+	void __iomem *mi2s_rx_base;
+	void __iomem *mi2s_tx_base;
+	struct mutex mutex_lock;
+
+};
+
+static struct mi2s_state the_mi2s_state;
+
+static void __iomem *get_base_addr(struct mi2s_state *mi2s, uint8_t dev_id)
+{
+	switch (dev_id) {
+	case HDMI:
+		return mi2s->mi2s_hdmi_base;
+	case CODEC_RX:
+		return mi2s->mi2s_rx_base;
+	case CODEC_TX:
+		return mi2s->mi2s_tx_base;
+	default:
+		break;
+	}
+	return ERR_PTR(-ENODEV);
+}
+
+static void mi2s_reset(struct mi2s_state *mi2s, uint8_t dev_id)
+{
+	void __iomem *baddr = get_base_addr(mi2s, dev_id);
+	if (!IS_ERR(baddr))
+		writel(MI2S_RESET__MI2S_RESET__RESET,
+		baddr + MI2S_RESET_OFFSET);
+}
+
+static void mi2s_release(struct mi2s_state *mi2s, uint8_t dev_id)
+{
+	void __iomem *baddr = get_base_addr(mi2s, dev_id);
+	if (!IS_ERR(baddr))
+		writel(MI2S_RESET__MI2S_RESET__ACTIVE,
+		baddr + MI2S_RESET_OFFSET);
+}
+
+static void mi2s_master(struct mi2s_state *mi2s, uint8_t dev_id, bool master)
+{
+	void __iomem *baddr = get_base_addr(mi2s, dev_id);
+	uint32_t val;
+	if (!IS_ERR(baddr)) {
+		val = readl(baddr + MI2S_MODE_OFFSET);
+		if (master) {
+			writel(
+			((val & ~HWIO_AUDIO1_MI2S_MODE_MI2S_MASTER_BMSK) |
+			 (MI2S_MODE__MI2S_MASTER__MASTER <<
+			  HWIO_AUDIO1_MI2S_MODE_MI2S_MASTER_SHFT)),
+			baddr + MI2S_MODE_OFFSET);
+		} else {
+			writel(
+			((val & ~HWIO_AUDIO1_MI2S_MODE_MI2S_MASTER_BMSK) |
+			 (MI2S_MODE__MI2S_MASTER__SLAVE <<
+			  HWIO_AUDIO1_MI2S_MODE_MI2S_MASTER_SHFT)),
+			baddr + MI2S_MODE_OFFSET);
+		}
+	}
+}
+
+static void mi2s_set_word_type(struct mi2s_state *mi2s, uint8_t dev_id,
+	uint8_t size)
+{
+	void __iomem *baddr = get_base_addr(mi2s, dev_id);
+	uint32_t val;
+	if (!IS_ERR(baddr)) {
+		val = readl(baddr + MI2S_MODE_OFFSET);
+		switch (size) {
+		case WT_16_BIT:
+			writel(
+			((val &
+			~HWIO_AUDIO1_MI2S_MODE_MI2S_TX_RX_WORD_TYPE_BMSK) |
+			(MI2S_MODE__MI2S_TX_RX_WORD_TYPE__16_BIT <<
+			HWIO_AUDIO1_MI2S_MODE_MI2S_TX_RX_WORD_TYPE_SHFT)),
+			baddr + MI2S_MODE_OFFSET);
+			break;
+		case WT_24_BIT:
+			writel(
+			((val &
+			~HWIO_AUDIO1_MI2S_MODE_MI2S_TX_RX_WORD_TYPE_BMSK) |
+			(MI2S_MODE__MI2S_TX_RX_WORD_TYPE__24_BIT <<
+			HWIO_AUDIO1_MI2S_MODE_MI2S_TX_RX_WORD_TYPE_SHFT)),
+			baddr + MI2S_MODE_OFFSET);
+			break;
+		case WT_32_BIT:
+			writel(
+			((val &
+			~HWIO_AUDIO1_MI2S_MODE_MI2S_TX_RX_WORD_TYPE_BMSK) |
+			(MI2S_MODE__MI2S_TX_RX_WORD_TYPE__32_BIT <<
+			HWIO_AUDIO1_MI2S_MODE_MI2S_TX_RX_WORD_TYPE_SHFT)),
+			baddr + MI2S_MODE_OFFSET);
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+static void mi2s_set_sd(struct mi2s_state *mi2s, uint8_t dev_id, uint8_t sd_map)
+{
+	void __iomem *baddr = get_base_addr(mi2s, dev_id);
+	uint32_t val;
+	if (!IS_ERR(baddr)) {
+		val = readl(baddr + MI2S_MODE_OFFSET) &
+			~(MI2S_SD_N_EN_MASK | MI2S_TX_RX_N_MASK);
+		writel(val | sd_map, baddr + MI2S_MODE_OFFSET);
+	}
+}
+
+static void mi2s_set_output_num_channels(struct mi2s_state *mi2s,
+	uint8_t dev_id, uint8_t channels)
+{
+	void __iomem *baddr = get_base_addr(mi2s, dev_id);
+	uint32_t val;
+	if (!IS_ERR(baddr)) {
+		val = readl(baddr + MI2S_TX_MODE_OFFSET);
+		if (channels == MI2S_CHAN_MONO_RAW) {
+			val = (val &
+			~(HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_BMSK |
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_P_MONO_BMSK)) |
+			((MI2S_TX_MODE__MI2S_TX_STEREO_MODE__MONO_SAMPLE <<
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_SHFT) |
+			(MI2S_TX_MODE__MI2S_TX_CODEC_16_MONO_MODE__RAW <<
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_P_MONO_SHFT));
+		} else if (channels == MI2S_CHAN_MONO_PACKED) {
+			val = (val &
+			~(HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_BMSK |
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_P_MONO_BMSK)) |
+			((MI2S_TX_MODE__MI2S_TX_STEREO_MODE__MONO_SAMPLE <<
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_SHFT) |
+			(MI2S_TX_MODE__MI2S_TX_CODEC_16_MONO_MODE__PACKED <<
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_P_MONO_SHFT));
+		} else if (channels == MI2S_CHAN_STEREO) {
+			val = (val &
+			~(HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_BMSK |
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_BMSK)) |
+			((MI2S_TX_MODE__MI2S_TX_STEREO_MODE__STEREO_SAMPLE <<
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_SHFT) |
+			(MI2S_TX_MODE__MI2S_TX_CH_TYPE__2_CHANNEL <<
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_SHFT));
+		} else if (channels == MI2S_CHAN_4CHANNELS) {
+			val = (val &
+			~(HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_BMSK |
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_BMSK)) |
+			((MI2S_TX_MODE__MI2S_TX_STEREO_MODE__STEREO_SAMPLE <<
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_SHFT) |
+			(MI2S_TX_MODE__MI2S_TX_CH_TYPE__4_CHANNEL <<
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_SHFT));
+		} else if (channels == MI2S_CHAN_6CHANNELS) {
+			val = (val &
+			~(HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_BMSK |
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_BMSK)) |
+			((MI2S_TX_MODE__MI2S_TX_STEREO_MODE__STEREO_SAMPLE <<
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_SHFT) |
+			(MI2S_TX_MODE__MI2S_TX_CH_TYPE__6_CHANNEL <<
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_SHFT));
+		} else if (channels == MI2S_CHAN_8CHANNELS) {
+			val = (val &
+			~(HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_BMSK |
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_BMSK)) |
+			((MI2S_TX_MODE__MI2S_TX_STEREO_MODE__STEREO_SAMPLE <<
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_SHFT) |
+			(MI2S_TX_MODE__MI2S_TX_CH_TYPE__8_CHANNEL <<
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_SHFT));
+		}
+		writel(val, baddr + MI2S_TX_MODE_OFFSET);
+	}
+}
+
+static void mi2s_set_output_4ch_map(struct mi2s_state *mi2s, uint8_t dev_id,
+	bool high_low)
+{
+	void __iomem *baddr = get_base_addr(mi2s, dev_id);
+	uint32_t val;
+	if (!IS_ERR(baddr)) {
+		val = readl(baddr + MI2S_TX_MODE_OFFSET);
+		val = (val & ~HWIO_AUDIO1_MI2S_TX_MODE_MI2S_4_0_CH_MAP_BMSK) |
+			(high_low <<
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_4_0_CH_MAP_SHFT);
+		writel(val, baddr + MI2S_TX_MODE_OFFSET);
+	}
+}
+
+static void mi2s_set_output_2ch_map(struct mi2s_state *mi2s, uint8_t dev_id,
+	uint8_t sd_line)
+{
+	void __iomem *baddr = get_base_addr(mi2s, dev_id);
+	uint32_t val;
+
+	if (!IS_ERR(baddr)) {
+		val = readl(baddr + MI2S_TX_MODE_OFFSET);
+		if (sd_line < 4) {
+			val = (val &
+			~HWIO_AUDIO1_MI2S_TX_MODE_MI2S_2_0_CH_MAP_BMSK) |
+			(sd_line <<
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_2_0_CH_MAP_SHFT);
+			writel(val, baddr + MI2S_TX_MODE_OFFSET);
+		}
+	}
+}
+
+static void mi2s_set_output_clk_synch(struct mi2s_state *mi2s, uint8_t dev_id)
+{
+	void __iomem *baddr = get_base_addr(mi2s, dev_id);
+	uint32_t val;
+
+	if (!IS_ERR(baddr)) {
+		val = readl(baddr + MI2S_TX_MODE_OFFSET);
+		writel(((val &
+		~HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_DMA_ACK_SYNCH_EN_BMSK) |
+		MI2S_TX_MODE__MI2S_TX_DMA_ACK_SYNCH_EN__SYNC_ENABLE),
+		baddr + MI2S_TX_MODE_OFFSET);
+	}
+}
+
+static void mi2s_set_input_sd_line(struct mi2s_state *mi2s, uint8_t dev_id,
+	uint8_t sd_line)
+{
+	void __iomem *baddr = get_base_addr(mi2s, dev_id);
+	uint32_t val;
+
+	if (!IS_ERR(baddr)) {
+		val = readl(baddr + MI2S_RX_MODE_OFFSET);
+		if (sd_line < 4) {
+			val = (val &
+			~HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_I2S_LINE_BMSK) |
+			(sd_line <<
+			HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_I2S_LINE_SHFT);
+			writel(val, baddr + MI2S_RX_MODE_OFFSET);
+		}
+	}
+}
+
+static void mi2s_set_input_num_channels(struct mi2s_state *mi2s, uint8_t dev_id,
+	uint8_t channels)
+{
+	void __iomem *baddr = get_base_addr(mi2s, dev_id);
+	uint32_t val;
+
+	if (!IS_ERR(baddr)) {
+		val = readl(baddr + MI2S_RX_MODE_OFFSET);
+		if (channels == MI2S_CHAN_MONO_RAW) {
+			val = (val &
+			~(HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_BMSK |
+			HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CODEC_P_MONO_BMSK)) |
+			((MI2S_RX_MODE__MI2S_RX_STEREO_MODE__MONO_SAMPLE <<
+			HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_SHFT) |
+			(MI2S_RX_MODE__MI2S_RX_CODEC_16_MONO_MODE__RAW <<
+			HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CODEC_P_MONO_SHFT));
+		} else if (channels == MI2S_CHAN_MONO_PACKED) {
+			val = (val &
+			~(HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_BMSK |
+			HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CODEC_P_MONO_BMSK)) |
+			((MI2S_RX_MODE__MI2S_RX_STEREO_MODE__MONO_SAMPLE <<
+			HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_SHFT) |
+			(MI2S_RX_MODE__MI2S_RX_CODEC_16_MONO_MODE__PACKED <<
+			HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CODEC_P_MONO_SHFT));
+		} else if (channels == MI2S_CHAN_STEREO) {
+
+			if (dev_id == HDMI)
+				val = (val &
+			~(HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_BMSK |
+			HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CH_TYPE_BMSK)) |
+			((MI2S_RX_MODE__MI2S_RX_STEREO_MODE__STEREO_SAMPLE <<
+			HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_SHFT) |
+			(MI2S_RX_MODE__MI2S_RX_CH_TYPE__2_CH <<
+			HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CH_TYPE_SHFT));
+
+			else
+				val = (val &
+			~(HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_BMSK |
+			HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CH_TYPE_BMSK)) |
+			((MI2S_RX_MODE__MI2S_RX_STEREO_MODE__STEREO_SAMPLE <<
+			HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_SHFT) |
+			(MI2S_RX_MODE__MI2S_RX_CODEC_16_MONO_MODE__PACKED <<
+			HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CODEC_P_MONO_SHFT) |
+			(MI2S_RX_MODE__MI2S_RX_CH_TYPE__2_CH <<
+			HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CH_TYPE_SHFT));
+
+
+		}
+		writel(val, baddr + MI2S_RX_MODE_OFFSET);
+	}
+}
+
+static void mi2s_set_input_clk_synch(struct mi2s_state *mi2s, uint8_t dev_id)
+{
+	void __iomem *baddr = get_base_addr(mi2s, dev_id);
+	uint32_t val;
+
+	if (!IS_ERR(baddr)) {
+		val = readl(baddr + MI2S_RX_MODE_OFFSET);
+		writel(
+		((val &
+		~HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_DMA_ACK_SYNCH_EN_BMSK) |
+		MI2S_RX_MODE__MI2S_RX_DMA_ACK_SYNCH_EN__SYNC_ENABLE),
+		baddr + MI2S_RX_MODE_OFFSET);
+	}
+}
+
+
+static u8 num_of_bits_set(u8 sd_line_mask)
+{
+	u8 num_bits_set = 0;
+
+	while (sd_line_mask) {
+
+		if (sd_line_mask & 1)
+			num_bits_set++;
+		sd_line_mask = sd_line_mask >> 1;
+	}
+	return num_bits_set;
+}
+
+
+bool mi2s_set_hdmi_output_path(uint8_t channels, uint8_t size,
+		uint8_t sd_line_mask)
+{
+	bool ret_val = MI2S_TRUE;
+	struct mi2s_state *mi2s = &the_mi2s_state;
+	u8 sd_line, num_of_sd_lines = 0;
+	void __iomem *baddr;
+	uint32_t val;
+
+	pr_debug("%s: channels = %u size = %u sd_line_mask = 0x%x\n", __func__,
+		channels, size, sd_line_mask);
+
+	if ((channels == 0) ||  (channels > MAX_NUM_CHANNELS_OUT) ||
+		((channels != 1) && (channels % 2 != 0))) {
+
+		pr_err("%s: invalid number of channels. channels = %u\n",
+				__func__, channels);
+		return  MI2S_FALSE;
+	}
+
+	sd_line_mask &=  MI2S_SD_LINE_MASK;
+
+	if (!sd_line_mask) {
+		pr_err("%s: Did not set any data lines to use "
+			" sd_line_mask =0x%x\n", __func__, sd_line_mask);
+		return  MI2S_FALSE;
+	}
+
+	mutex_lock(&mi2s->mutex_lock);
+	/* Put device in reset */
+	mi2s_reset(mi2s, HDMI);
+
+	mi2s_master(mi2s, HDMI, 1);
+
+	/* Set word type */
+	if (size <= WT_MAX)
+		mi2s_set_word_type(mi2s, HDMI, size);
+	else
+		ret_val = MI2S_FALSE;
+
+	/* Enable clock crossing synchronization of RD DMA ACK */
+	mi2s_set_output_clk_synch(mi2s, HDMI);
+
+	mi2s_set_output_num_channels(mi2s, HDMI, channels);
+
+	num_of_sd_lines = num_of_bits_set(sd_line_mask);
+	/*Second argument to find_first_bit should be maximum number of
+	bit*/
+
+	sd_line = find_first_bit((unsigned long *)&sd_line_mask,
+			sizeof(sd_line_mask) * 8);
+	pr_debug("sd_line = %d\n", sd_line);
+
+	if (channels == 1) {
+
+		if (num_of_sd_lines != 1) {
+			pr_err("%s: for one channel only one SD lines is"
+				" needed. num_of_sd_lines = %u\n",
+				__func__, num_of_sd_lines);
+
+			ret_val = MI2S_FALSE;
+			goto error;
+		}
+
+		if (sd_line != 0) {
+			pr_err("%s: for one channel tx, need to use SD_0 "
+					"sd_line = %u\n", __func__, sd_line);
+
+			ret_val = MI2S_FALSE;
+			goto error;
+		}
+
+		/* Enable SD line 0 for Tx (only option for
+			 * mono audio)
+		 */
+		mi2s_set_sd(mi2s, HDMI, MI2S_SD_0_EN_MAP | MI2S_SD_0_TX_MAP);
+
+	} else if (channels == 2) {
+
+		if (num_of_sd_lines != 1) {
+			pr_err("%s: for two channel only one SD lines is"
+				" needed. num_of_sd_lines = %u\n",
+				__func__, num_of_sd_lines);
+			ret_val = MI2S_FALSE;
+			goto error;
+		}
+
+		/* Enable single SD line for Tx */
+		mi2s_set_sd(mi2s, HDMI, (MI2S_SD_0_EN_MAP << sd_line) |
+				(MI2S_SD_0_TX_MAP << sd_line));
+
+		/* Set 2-channel mapping */
+		mi2s_set_output_2ch_map(mi2s, HDMI, sd_line);
+
+	} else if (channels == 4) {
+
+		if (num_of_sd_lines != 2) {
+			pr_err("%s: for 4 channels two SD lines are"
+				" needed. num_of_sd_lines = %u\\n",
+				__func__, num_of_sd_lines);
+			ret_val = MI2S_FALSE;
+			goto error;
+		}
+
+		if ((sd_line_mask && MI2S_SD_0) &&
+				(sd_line_mask && MI2S_SD_1)) {
+
+			mi2s_set_sd(mi2s, HDMI, (MI2S_SD_0_EN_MAP |
+				MI2S_SD_1_EN_MAP) | (MI2S_SD_0_TX_MAP |
+				MI2S_SD_1_TX_MAP));
+			mi2s_set_output_4ch_map(mi2s, HDMI, MI2S_FALSE);
+
+		} else if ((sd_line_mask && MI2S_SD_2) &&
+				(sd_line_mask && MI2S_SD_3)) {
+
+			mi2s_set_sd(mi2s, HDMI, (MI2S_SD_2_EN_MAP |
+				MI2S_SD_3_EN_MAP) | (MI2S_SD_2_TX_MAP |
+				MI2S_SD_3_TX_MAP));
+
+			mi2s_set_output_4ch_map(mi2s, HDMI, MI2S_TRUE);
+		} else {
+
+			pr_err("%s: for 4 channels invalid SD lines usage"
+				" sd_line_mask = 0x%x\n",
+				__func__, sd_line_mask);
+			ret_val = MI2S_FALSE;
+			goto error;
+		}
+	} else if (channels == 6) {
+
+		if (num_of_sd_lines != 3) {
+			pr_err("%s: for 6 channels three SD lines are"
+				" needed. num_of_sd_lines = %u\n",
+				__func__, num_of_sd_lines);
+			ret_val = MI2S_FALSE;
+			goto error;
+		}
+
+		if ((sd_line_mask && MI2S_SD_0) &&
+			(sd_line_mask && MI2S_SD_1) &&
+			(sd_line_mask && MI2S_SD_2)) {
+
+			mi2s_set_sd(mi2s, HDMI, (MI2S_SD_0_EN_MAP |
+				MI2S_SD_1_EN_MAP | MI2S_SD_2_EN_MAP) |
+				(MI2S_SD_0_TX_MAP | MI2S_SD_1_TX_MAP |
+				MI2S_SD_2_TX_MAP));
+
+		} else if ((sd_line_mask && MI2S_SD_1) &&
+				(sd_line_mask && MI2S_SD_2) &&
+				(sd_line_mask && MI2S_SD_3)) {
+
+			mi2s_set_sd(mi2s, HDMI, (MI2S_SD_1_EN_MAP |
+				MI2S_SD_2_EN_MAP | MI2S_SD_3_EN_MAP) |
+				(MI2S_SD_1_TX_MAP | MI2S_SD_2_TX_MAP |
+				MI2S_SD_3_TX_MAP));
+
+		} else {
+
+			pr_err("%s: for 6 channels invalid SD lines usage"
+				" sd_line_mask = 0x%x\n",
+				__func__, sd_line_mask);
+			ret_val = MI2S_FALSE;
+			goto error;
+		}
+	} else if (channels == 8) {
+
+		if (num_of_sd_lines != 4) {
+			pr_err("%s: for 8 channels four SD lines are"
+				" needed. num_of_sd_lines = %u\n",
+				__func__, num_of_sd_lines);
+			ret_val = MI2S_FALSE;
+			goto error;
+		}
+
+		mi2s_set_sd(mi2s, HDMI, (MI2S_SD_0_EN_MAP |
+			MI2S_SD_1_EN_MAP | MI2S_SD_2_EN_MAP |
+			MI2S_SD_3_EN_MAP) | (MI2S_SD_0_TX_MAP |
+			MI2S_SD_1_TX_MAP | MI2S_SD_2_TX_MAP |
+			MI2S_SD_3_TX_MAP));
+	} else {
+		pr_err("%s: invalid number channels = %u\n",
+				__func__, channels);
+			ret_val = MI2S_FALSE;
+			goto error;
+	}
+
+	baddr = get_base_addr(mi2s, HDMI);
+
+	val = readl(baddr + MI2S_MODE_OFFSET);
+	pr_debug("%s(): MI2S_MODE = 0x%x\n", __func__, val);
+
+	val = readl(baddr + MI2S_TX_MODE_OFFSET);
+	pr_debug("%s(): MI2S_TX_MODE = 0x%x\n", __func__, val);
+
+
+error:
+	/* Release device from reset */
+	mi2s_release(mi2s, HDMI);
+
+	mutex_unlock(&mi2s->mutex_lock);
+	mb();
+	return ret_val;
+}
+EXPORT_SYMBOL(mi2s_set_hdmi_output_path);
+
+bool mi2s_set_hdmi_input_path(uint8_t channels, uint8_t size,
+		uint8_t sd_line_mask)
+{
+	bool ret_val = MI2S_TRUE;
+	struct mi2s_state *mi2s = &the_mi2s_state;
+	u8 sd_line, num_of_sd_lines = 0;
+	void __iomem *baddr;
+	uint32_t val;
+
+	pr_debug("%s: channels = %u size = %u sd_line_mask = 0x%x\n", __func__,
+		channels, size, sd_line_mask);
+
+	if ((channels != 1) && (channels != MAX_NUM_CHANNELS_IN)) {
+
+		pr_err("%s: invalid number of channels. channels = %u\n",
+				__func__, channels);
+		return  MI2S_FALSE;
+	}
+
+	if (size > WT_MAX) {
+
+		pr_err("%s: mi2s word size can not be greater than 32 bits\n",
+				__func__);
+		return MI2S_FALSE;
+	}
+
+	sd_line_mask &=  MI2S_SD_LINE_MASK;
+
+	if (!sd_line_mask) {
+		pr_err("%s: Did not set any data lines to use "
+			" sd_line_mask =0x%x\n", __func__, sd_line_mask);
+		return  MI2S_FALSE;
+	}
+
+	num_of_sd_lines = num_of_bits_set(sd_line_mask);
+
+	if (num_of_sd_lines != 1) {
+		pr_err("%s: for two channel input only one SD lines is"
+			" needed. num_of_sd_lines = %u sd_line_mask = 0x%x\n",
+			__func__, num_of_sd_lines, sd_line_mask);
+		return MI2S_FALSE;
+	}
+
+	/*Second argument to find_first_bit should be maximum number of
+	bits interested*/
+	sd_line = find_first_bit((unsigned long *)&sd_line_mask,
+			sizeof(sd_line_mask) * 8);
+	pr_debug("sd_line = %d\n", sd_line);
+
+	/* Ensure sd_line parameter is valid (0-max) */
+	if (sd_line > MAX_SD_LINES) {
+		pr_err("%s: Line number can not be greater than = %u\n",
+			__func__, MAX_SD_LINES);
+		return MI2S_FALSE;
+	}
+
+	mutex_lock(&mi2s->mutex_lock);
+	/* Put device in reset */
+	mi2s_reset(mi2s, HDMI);
+
+	mi2s_master(mi2s, HDMI, 1);
+
+	/* Set word type */
+	mi2s_set_word_type(mi2s, HDMI, size);
+
+	/* Enable clock crossing synchronization of WR DMA ACK */
+	mi2s_set_input_clk_synch(mi2s, HDMI);
+
+	/* Ensure channels parameter is valid (non-zero, less than max,
+	 * and even or mono)
+	 */
+	mi2s_set_input_num_channels(mi2s, HDMI, channels);
+
+	mi2s_set_input_sd_line(mi2s, HDMI, sd_line);
+
+	mi2s_set_sd(mi2s, HDMI, (MI2S_SD_0_EN_MAP << sd_line));
+
+	baddr = get_base_addr(mi2s, HDMI);
+
+	val = readl(baddr + MI2S_MODE_OFFSET);
+	pr_debug("%s(): MI2S_MODE = 0x%x\n", __func__, val);
+
+	val = readl(baddr + MI2S_RX_MODE_OFFSET);
+	pr_debug("%s(): MI2S_RX_MODE = 0x%x\n", __func__, val);
+
+	/* Release device from reset */
+	mi2s_release(mi2s, HDMI);
+
+	mutex_unlock(&mi2s->mutex_lock);
+	mb();
+	return ret_val;
+}
+EXPORT_SYMBOL(mi2s_set_hdmi_input_path);
+
+bool mi2s_set_codec_output_path(uint8_t channels, uint8_t size)
+{
+	bool ret_val = MI2S_TRUE;
+	struct mi2s_state *mi2s = &the_mi2s_state;
+
+	mutex_lock(&mi2s->mutex_lock);
+	/* Put device in reset */
+	mi2s_reset(mi2s, CODEC_TX);
+
+	mi2s_master(mi2s, CODEC_TX, 1);
+
+	/* Enable clock crossing synchronization of RD DMA ACK */
+	mi2s_set_output_clk_synch(mi2s, CODEC_TX);
+
+	/* Set word type */
+	if (size <= WT_MAX)
+		mi2s_set_word_type(mi2s, CODEC_TX, size);
+	else
+		ret_val = MI2S_FALSE;
+
+	mi2s_set_output_num_channels(mi2s, CODEC_TX, channels);
+
+	/* Enable SD line */
+	mi2s_set_sd(mi2s, CODEC_TX, MI2S_SD_0_EN_MAP | MI2S_SD_0_TX_MAP);
+
+	/* Release device from reset */
+	mi2s_release(mi2s, CODEC_TX);
+
+	mutex_unlock(&mi2s->mutex_lock);
+	mb();
+	return ret_val;
+}
+EXPORT_SYMBOL(mi2s_set_codec_output_path);
+
+bool mi2s_set_codec_input_path(uint8_t channels, uint8_t size)
+{
+	bool ret_val = MI2S_TRUE;
+	struct mi2s_state *mi2s = &the_mi2s_state;
+
+	mutex_lock(&the_mi2s_state.mutex_lock);
+	/* Put device in reset */
+	mi2s_reset(mi2s, CODEC_RX);
+
+	mi2s_master(mi2s, CODEC_RX, 1);
+
+	/* Enable clock crossing synchronization of WR DMA ACK */
+	mi2s_set_input_clk_synch(mi2s, CODEC_RX);
+
+	/* Set word type */
+	if (size <= WT_MAX)
+		mi2s_set_word_type(mi2s, CODEC_RX, size);
+	else
+		ret_val = MI2S_FALSE;
+
+	mi2s_set_input_num_channels(mi2s, CODEC_RX, channels);
+
+	/* Enable SD line */
+	mi2s_set_sd(mi2s, CODEC_RX, MI2S_SD_0_EN_MAP);
+
+	/* Release device from reset */
+	mi2s_release(mi2s, CODEC_RX);
+
+	mutex_unlock(&mi2s->mutex_lock);
+	mb();
+	return ret_val;
+}
+EXPORT_SYMBOL(mi2s_set_codec_input_path);
+
+
+static int mi2s_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct resource *mem_src;
+
+	mem_src = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hdmi");
+	if (!mem_src) {
+		rc = -ENODEV;
+		goto error_hdmi;
+	}
+	the_mi2s_state.mi2s_hdmi_base = ioremap(mem_src->start,
+		(mem_src->end - mem_src->start) + 1);
+	if (!the_mi2s_state.mi2s_hdmi_base) {
+		rc = -ENOMEM;
+		goto error_hdmi;
+	}
+	mem_src = platform_get_resource_byname(pdev,
+		IORESOURCE_MEM, "codec_rx");
+	if (!mem_src) {
+		rc = -ENODEV;
+		goto error_codec_rx;
+	}
+	the_mi2s_state.mi2s_rx_base = ioremap(mem_src->start,
+		(mem_src->end - mem_src->start) + 1);
+	if (!the_mi2s_state.mi2s_rx_base) {
+		rc = -ENOMEM;
+		goto error_codec_rx;
+	}
+	mem_src = platform_get_resource_byname(pdev,
+		IORESOURCE_MEM, "codec_tx");
+	if (!mem_src) {
+		rc = -ENODEV;
+		goto error_codec_tx;
+	}
+	the_mi2s_state.mi2s_tx_base = ioremap(mem_src->start,
+		(mem_src->end - mem_src->start) + 1);
+	if (!the_mi2s_state.mi2s_tx_base) {
+		rc = -ENOMEM;
+		goto error_codec_tx;
+	}
+	mutex_init(&the_mi2s_state.mutex_lock);
+
+	return rc;
+
+error_codec_tx:
+	iounmap(the_mi2s_state.mi2s_rx_base);
+error_codec_rx:
+	iounmap(the_mi2s_state.mi2s_hdmi_base);
+error_hdmi:
+	return rc;
+
+}
+
+static int mi2s_remove(struct platform_device *pdev)
+{
+	iounmap(the_mi2s_state.mi2s_tx_base);
+	iounmap(the_mi2s_state.mi2s_rx_base);
+	iounmap(the_mi2s_state.mi2s_hdmi_base);
+	return 0;
+}
+
+static struct platform_driver mi2s_driver = {
+	.probe = mi2s_probe,
+	.remove = mi2s_remove,
+	.driver = {
+		.name = "mi2s",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init mi2s_init(void)
+{
+	return platform_driver_register(&mi2s_driver);
+}
+
+static void __exit mi2s_exit(void)
+{
+	platform_driver_unregister(&mi2s_driver);
+}
+
+module_init(mi2s_init);
+module_exit(mi2s_exit);
+
+MODULE_DESCRIPTION("MSM MI2S driver");
+MODULE_LICENSE("GPL v2");