ASoC: Ux500: Add MSP I2S-driver
authorOla Lilja <ola.o.lilja@stericsson.com>
Tue, 8 May 2012 13:57:18 +0000 (15:57 +0200)
committerMark Brown <broonie@opensource.wolfsonmicro.com>
Wed, 9 May 2012 11:52:59 +0000 (12:52 +0100)
Add driver for running I2S with the MSP-block.

Signed-off-by: Ola Lilja <ola.o.lilja@stericsson.com>
[Fixed trailing whitespace -- broonie]
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
sound/soc/Kconfig
sound/soc/Makefile
sound/soc/ux500/Kconfig [new file with mode: 0644]
sound/soc/ux500/Makefile [new file with mode: 0644]
sound/soc/ux500/ux500_msp_dai.c [new file with mode: 0644]
sound/soc/ux500/ux500_msp_dai.h [new file with mode: 0644]
sound/soc/ux500/ux500_msp_i2s.c [new file with mode: 0644]
sound/soc/ux500/ux500_msp_i2s.h [new file with mode: 0644]

index a7df779894d57e0a438e9e39070dd5d920e54197..40b2ad1bb1cd8b14cb401d60f096f07ee5cf0f3b 100644 (file)
@@ -47,6 +47,7 @@ source "sound/soc/s6000/Kconfig"
 source "sound/soc/sh/Kconfig"
 source "sound/soc/tegra/Kconfig"
 source "sound/soc/txx9/Kconfig"
+source "sound/soc/ux500/Kconfig"
 
 # Supported codecs
 source "sound/soc/codecs/Kconfig"
index 57d3463ce9d69bf910432decd3817b1662a9e621..70990f4017f4952b2f311a77c7c61dbe03332fba 100644 (file)
@@ -25,3 +25,4 @@ obj-$(CONFIG_SND_SOC) += s6000/
 obj-$(CONFIG_SND_SOC)  += sh/
 obj-$(CONFIG_SND_SOC)  += tegra/
 obj-$(CONFIG_SND_SOC)  += txx9/
+obj-$(CONFIG_SND_SOC)  += ux500/
diff --git a/sound/soc/ux500/Kconfig b/sound/soc/ux500/Kconfig
new file mode 100644 (file)
index 0000000..ac85234
--- /dev/null
@@ -0,0 +1,15 @@
+#
+# Ux500 SoC audio configuration
+#
+menuconfig SND_SOC_UX500
+       tristate "SoC Audio support for Ux500 platform"
+       depends on SND_SOC
+       help
+               Say Y if you want to enable ASoC-support for
+               any of the Ux500 platforms (e.g. U8500).
+
+config SND_SOC_UX500_PLAT_MSP_I2S
+       tristate "Platform - MSP (I2S)"
+       depends on SND_SOC_UX500
+       help
+               Say Y if you want to enable the Ux500 MSP I2S-driver.
diff --git a/sound/soc/ux500/Makefile b/sound/soc/ux500/Makefile
new file mode 100644 (file)
index 0000000..19974c5
--- /dev/null
@@ -0,0 +1,4 @@
+# Ux500 Platform Support
+
+snd-soc-ux500-plat-msp-i2s-objs := ux500_msp_dai.o ux500_msp_i2s.o
+obj-$(CONFIG_SND_SOC_UX500_PLAT_MSP_I2S) += snd-soc-ux500-plat-msp-i2s.o
diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c
new file mode 100644 (file)
index 0000000..93c6c40
--- /dev/null
@@ -0,0 +1,843 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ *
+ * Author: Ola Lilja <ola.o.lilja@stericsson.com>,
+ *         Roger Nilsson <roger.xr.nilsson@stericsson.com>
+ *         for ST-Ericsson.
+ *
+ * License terms:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mfd/dbx500-prcmu.h>
+
+#include <mach/hardware.h>
+#include <mach/board-mop500-msp.h>
+
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "ux500_msp_i2s.h"
+#include "ux500_msp_dai.h"
+
+static int setup_pcm_multichan(struct snd_soc_dai *dai,
+                       struct ux500_msp_config *msp_config)
+{
+       struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+       struct msp_multichannel_config *multi =
+                                       &msp_config->multichannel_config;
+
+       if (drvdata->slots > 1) {
+               msp_config->multichannel_configured = 1;
+
+               multi->tx_multichannel_enable = true;
+               multi->rx_multichannel_enable = true;
+               multi->rx_comparison_enable_mode = MSP_COMPARISON_DISABLED;
+
+               multi->tx_channel_0_enable = drvdata->tx_mask;
+               multi->tx_channel_1_enable = 0;
+               multi->tx_channel_2_enable = 0;
+               multi->tx_channel_3_enable = 0;
+
+               multi->rx_channel_0_enable = drvdata->rx_mask;
+               multi->rx_channel_1_enable = 0;
+               multi->rx_channel_2_enable = 0;
+               multi->rx_channel_3_enable = 0;
+
+               dev_dbg(dai->dev,
+                       "%s: Multichannel enabled. Slots: %d, TX: %u, RX: %u\n",
+                       __func__, drvdata->slots, multi->tx_channel_0_enable,
+                       multi->rx_channel_0_enable);
+       }
+
+       return 0;
+}
+
+static int setup_frameper(struct snd_soc_dai *dai, unsigned int rate,
+                       struct msp_protdesc *prot_desc)
+{
+       struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+
+       switch (drvdata->slots) {
+       case 1:
+               switch (rate) {
+               case 8000:
+                       prot_desc->frame_period =
+                               FRAME_PER_SINGLE_SLOT_8_KHZ;
+                       break;
+
+               case 16000:
+                       prot_desc->frame_period =
+                               FRAME_PER_SINGLE_SLOT_16_KHZ;
+                       break;
+
+               case 44100:
+                       prot_desc->frame_period =
+                               FRAME_PER_SINGLE_SLOT_44_1_KHZ;
+                       break;
+
+               case 48000:
+                       prot_desc->frame_period =
+                               FRAME_PER_SINGLE_SLOT_48_KHZ;
+                       break;
+
+               default:
+                       dev_err(dai->dev,
+                               "%s: Error: Unsupported sample-rate (freq = %d)!\n",
+                               __func__, rate);
+                       return -EINVAL;
+               }
+               break;
+
+       case 2:
+               prot_desc->frame_period = FRAME_PER_2_SLOTS;
+               break;
+
+       case 8:
+               prot_desc->frame_period = FRAME_PER_8_SLOTS;
+               break;
+
+       case 16:
+               prot_desc->frame_period = FRAME_PER_16_SLOTS;
+               break;
+       default:
+               dev_err(dai->dev,
+                       "%s: Error: Unsupported slot-count (slots = %d)!\n",
+                       __func__, drvdata->slots);
+               return -EINVAL;
+       }
+
+       prot_desc->clocks_per_frame =
+                       prot_desc->frame_period+1;
+
+       dev_dbg(dai->dev, "%s: Clocks per frame: %u\n",
+               __func__,
+               prot_desc->clocks_per_frame);
+
+       return 0;
+}
+
+static int setup_pcm_framing(struct snd_soc_dai *dai, unsigned int rate,
+                       struct msp_protdesc *prot_desc)
+{
+       struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+
+       u32 frame_length = MSP_FRAME_LEN_1;
+       prot_desc->frame_width = 0;
+
+       switch (drvdata->slots) {
+       case 1:
+               frame_length = MSP_FRAME_LEN_1;
+               break;
+
+       case 2:
+               frame_length = MSP_FRAME_LEN_2;
+               break;
+
+       case 8:
+               frame_length = MSP_FRAME_LEN_8;
+               break;
+
+       case 16:
+               frame_length = MSP_FRAME_LEN_16;
+               break;
+       default:
+               dev_err(dai->dev,
+                       "%s: Error: Unsupported slot-count (slots = %d)!\n",
+                       __func__, drvdata->slots);
+               return -EINVAL;
+       }
+
+       prot_desc->tx_frame_len_1 = frame_length;
+       prot_desc->rx_frame_len_1 = frame_length;
+       prot_desc->tx_frame_len_2 = frame_length;
+       prot_desc->rx_frame_len_2 = frame_length;
+
+       prot_desc->tx_elem_len_1 = MSP_ELEM_LEN_16;
+       prot_desc->rx_elem_len_1 = MSP_ELEM_LEN_16;
+       prot_desc->tx_elem_len_2 = MSP_ELEM_LEN_16;
+       prot_desc->rx_elem_len_2 = MSP_ELEM_LEN_16;
+
+       return setup_frameper(dai, rate, prot_desc);
+}
+
+static int setup_clocking(struct snd_soc_dai *dai,
+                       unsigned int fmt,
+                       struct ux500_msp_config *msp_config)
+{
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               break;
+
+       case SND_SOC_DAIFMT_NB_IF:
+               msp_config->tx_fsync_pol ^= 1 << TFSPOL_SHIFT;
+               msp_config->rx_fsync_pol ^= 1 << RFSPOL_SHIFT;
+
+               break;
+
+       default:
+               dev_err(dai->dev,
+                       "%s: Error: Unsopported inversion (fmt = 0x%x)!\n",
+                       __func__, fmt);
+
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               dev_dbg(dai->dev, "%s: Codec is master.\n", __func__);
+
+               msp_config->iodelay = 0x20;
+               msp_config->rx_fsync_sel = 0;
+               msp_config->tx_fsync_sel = 1 << TFSSEL_SHIFT;
+               msp_config->tx_clk_sel = 0;
+               msp_config->rx_clk_sel = 0;
+               msp_config->srg_clk_sel = 0x2 << SCKSEL_SHIFT;
+
+               break;
+
+       case SND_SOC_DAIFMT_CBS_CFS:
+               dev_dbg(dai->dev, "%s: Codec is slave.\n", __func__);
+
+               msp_config->tx_clk_sel = TX_CLK_SEL_SRG;
+               msp_config->tx_fsync_sel = TX_SYNC_SRG_PROG;
+               msp_config->rx_clk_sel = RX_CLK_SEL_SRG;
+               msp_config->rx_fsync_sel = RX_SYNC_SRG;
+               msp_config->srg_clk_sel = 1 << SCKSEL_SHIFT;
+
+               break;
+
+       default:
+               dev_err(dai->dev, "%s: Error: Unsopported master (fmt = 0x%x)!\n",
+                       __func__, fmt);
+
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int setup_pcm_protdesc(struct snd_soc_dai *dai,
+                               unsigned int fmt,
+                               struct msp_protdesc *prot_desc)
+{
+       prot_desc->rx_phase_mode = MSP_SINGLE_PHASE;
+       prot_desc->tx_phase_mode = MSP_SINGLE_PHASE;
+       prot_desc->rx_phase2_start_mode = MSP_PHASE2_START_MODE_IMEDIATE;
+       prot_desc->tx_phase2_start_mode = MSP_PHASE2_START_MODE_IMEDIATE;
+       prot_desc->rx_byte_order = MSP_BTF_MS_BIT_FIRST;
+       prot_desc->tx_byte_order = MSP_BTF_MS_BIT_FIRST;
+       prot_desc->tx_fsync_pol = MSP_FSYNC_POL(MSP_FSYNC_POL_ACT_HI);
+       prot_desc->rx_fsync_pol = MSP_FSYNC_POL_ACT_HI << RFSPOL_SHIFT;
+
+       if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_DSP_A) {
+               dev_dbg(dai->dev, "%s: DSP_A.\n", __func__);
+               prot_desc->rx_clk_pol = MSP_RISING_EDGE;
+               prot_desc->tx_clk_pol = MSP_FALLING_EDGE;
+
+               prot_desc->rx_data_delay = MSP_DELAY_1;
+               prot_desc->tx_data_delay = MSP_DELAY_1;
+       } else {
+               dev_dbg(dai->dev, "%s: DSP_B.\n", __func__);
+               prot_desc->rx_clk_pol = MSP_FALLING_EDGE;
+               prot_desc->tx_clk_pol = MSP_RISING_EDGE;
+
+               prot_desc->rx_data_delay = MSP_DELAY_0;
+               prot_desc->tx_data_delay = MSP_DELAY_0;
+       }
+
+       prot_desc->rx_half_word_swap = MSP_SWAP_NONE;
+       prot_desc->tx_half_word_swap = MSP_SWAP_NONE;
+       prot_desc->compression_mode = MSP_COMPRESS_MODE_LINEAR;
+       prot_desc->expansion_mode = MSP_EXPAND_MODE_LINEAR;
+       prot_desc->frame_sync_ignore = MSP_FSYNC_IGNORE;
+
+       return 0;
+}
+
+static int setup_i2s_protdesc(struct msp_protdesc *prot_desc)
+{
+       prot_desc->rx_phase_mode = MSP_DUAL_PHASE;
+       prot_desc->tx_phase_mode = MSP_DUAL_PHASE;
+       prot_desc->rx_phase2_start_mode = MSP_PHASE2_START_MODE_FSYNC;
+       prot_desc->tx_phase2_start_mode = MSP_PHASE2_START_MODE_FSYNC;
+       prot_desc->rx_byte_order = MSP_BTF_MS_BIT_FIRST;
+       prot_desc->tx_byte_order = MSP_BTF_MS_BIT_FIRST;
+       prot_desc->tx_fsync_pol = MSP_FSYNC_POL(MSP_FSYNC_POL_ACT_LO);
+       prot_desc->rx_fsync_pol = MSP_FSYNC_POL_ACT_LO << RFSPOL_SHIFT;
+
+       prot_desc->rx_frame_len_1 = MSP_FRAME_LEN_1;
+       prot_desc->rx_frame_len_2 = MSP_FRAME_LEN_1;
+       prot_desc->tx_frame_len_1 = MSP_FRAME_LEN_1;
+       prot_desc->tx_frame_len_2 = MSP_FRAME_LEN_1;
+       prot_desc->rx_elem_len_1 = MSP_ELEM_LEN_16;
+       prot_desc->rx_elem_len_2 = MSP_ELEM_LEN_16;
+       prot_desc->tx_elem_len_1 = MSP_ELEM_LEN_16;
+       prot_desc->tx_elem_len_2 = MSP_ELEM_LEN_16;
+
+       prot_desc->rx_clk_pol = MSP_RISING_EDGE;
+       prot_desc->tx_clk_pol = MSP_FALLING_EDGE;
+
+       prot_desc->rx_data_delay = MSP_DELAY_0;
+       prot_desc->tx_data_delay = MSP_DELAY_0;
+
+       prot_desc->tx_half_word_swap = MSP_SWAP_NONE;
+       prot_desc->rx_half_word_swap = MSP_SWAP_NONE;
+       prot_desc->compression_mode = MSP_COMPRESS_MODE_LINEAR;
+       prot_desc->expansion_mode = MSP_EXPAND_MODE_LINEAR;
+       prot_desc->frame_sync_ignore = MSP_FSYNC_IGNORE;
+
+       return 0;
+}
+
+static int setup_msp_config(struct snd_pcm_substream *substream,
+                       struct snd_soc_dai *dai,
+                       struct ux500_msp_config *msp_config)
+{
+       struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+       struct msp_protdesc *prot_desc = &msp_config->protdesc;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       unsigned int fmt = drvdata->fmt;
+       int ret;
+
+       memset(msp_config, 0, sizeof(*msp_config));
+
+       msp_config->f_inputclk = drvdata->master_clk;
+
+       msp_config->tx_fifo_config = TX_FIFO_ENABLE;
+       msp_config->rx_fifo_config = RX_FIFO_ENABLE;
+       msp_config->def_elem_len = 1;
+       msp_config->direction = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+                               MSP_DIR_TX : MSP_DIR_RX;
+       msp_config->data_size = MSP_DATA_BITS_32;
+       msp_config->frame_freq = runtime->rate;
+
+       dev_dbg(dai->dev, "%s: f_inputclk = %u, frame_freq = %u.\n",
+              __func__, msp_config->f_inputclk, msp_config->frame_freq);
+       /* To avoid division by zero */
+       prot_desc->clocks_per_frame = 1;
+
+       dev_dbg(dai->dev, "%s: rate: %u, channels: %d.\n", __func__,
+               runtime->rate, runtime->channels);
+       switch (fmt &
+               (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_MASTER_MASK)) {
+       case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
+               dev_dbg(dai->dev, "%s: SND_SOC_DAIFMT_I2S.\n", __func__);
+
+               msp_config->default_protdesc = 1;
+               msp_config->protocol = MSP_I2S_PROTOCOL;
+               break;
+
+       case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
+               dev_dbg(dai->dev, "%s: SND_SOC_DAIFMT_I2S.\n", __func__);
+
+               msp_config->data_size = MSP_DATA_BITS_16;
+               msp_config->protocol = MSP_I2S_PROTOCOL;
+
+               ret = setup_i2s_protdesc(prot_desc);
+               if (ret < 0)
+                       return ret;
+
+               break;
+
+       case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS:
+       case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM:
+       case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBS_CFS:
+       case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM:
+               dev_dbg(dai->dev, "%s: PCM format.\n", __func__);
+
+               msp_config->data_size = MSP_DATA_BITS_16;
+               msp_config->protocol = MSP_PCM_PROTOCOL;
+
+               ret = setup_pcm_protdesc(dai, fmt, prot_desc);
+               if (ret < 0)
+                       return ret;
+
+               ret = setup_pcm_multichan(dai, msp_config);
+               if (ret < 0)
+                       return ret;
+
+               ret = setup_pcm_framing(dai, runtime->rate, prot_desc);
+               if (ret < 0)
+                       return ret;
+
+               break;
+
+       default:
+               dev_err(dai->dev, "%s: Error: Unsopported format (%d)!\n",
+                       __func__, fmt);
+               return -EINVAL;
+       }
+
+       return setup_clocking(dai, fmt, msp_config);
+}
+
+static int ux500_msp_dai_startup(struct snd_pcm_substream *substream,
+                               struct snd_soc_dai *dai)
+{
+       int ret = 0;
+       struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+
+       dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n", __func__, dai->id,
+               snd_pcm_stream_str(substream));
+
+       /* Enable regulator */
+       ret = regulator_enable(drvdata->reg_vape);
+       if (ret != 0) {
+               dev_err(drvdata->msp->dev,
+                       "%s: Failed to enable regulator!\n", __func__);
+               return ret;
+       }
+
+       /* Enable clock */
+       dev_dbg(dai->dev, "%s: Enabling MSP-clock.\n", __func__);
+       clk_enable(drvdata->clk);
+
+       return 0;
+}
+
+static void ux500_msp_dai_shutdown(struct snd_pcm_substream *substream,
+                               struct snd_soc_dai *dai)
+{
+       int ret;
+       struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+       bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+
+       dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n", __func__, dai->id,
+               snd_pcm_stream_str(substream));
+
+       if (drvdata->vape_opp_constraint == 1) {
+               prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP,
+                                       "ux500_msp_i2s", 50);
+               drvdata->vape_opp_constraint = 0;
+       }
+
+       if (ux500_msp_i2s_close(drvdata->msp,
+                               is_playback ? MSP_DIR_TX : MSP_DIR_RX)) {
+               dev_err(dai->dev,
+                       "%s: Error: MSP %d (%s): Unable to close i2s.\n",
+                       __func__, dai->id, snd_pcm_stream_str(substream));
+       }
+
+       /* Disable clock */
+       clk_disable(drvdata->clk);
+
+       /* Disable regulator */
+       ret = regulator_disable(drvdata->reg_vape);
+       if (ret < 0)
+               dev_err(dai->dev,
+                       "%s: ERROR: Failed to disable regulator (%d)!\n",
+                       __func__, ret);
+}
+
+static int ux500_msp_dai_prepare(struct snd_pcm_substream *substream,
+                               struct snd_soc_dai *dai)
+{
+       int ret = 0;
+       struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct ux500_msp_config msp_config;
+
+       dev_dbg(dai->dev, "%s: MSP %d (%s): Enter (rate = %d).\n", __func__,
+               dai->id, snd_pcm_stream_str(substream), runtime->rate);
+
+       setup_msp_config(substream, dai, &msp_config);
+
+       ret = ux500_msp_i2s_open(drvdata->msp, &msp_config);
+       if (ret < 0) {
+               dev_err(dai->dev, "%s: Error: msp_setup failed (ret = %d)!\n",
+                       __func__, ret);
+               return ret;
+       }
+
+       /* Set OPP-level */
+       if ((drvdata->fmt & SND_SOC_DAIFMT_MASTER_MASK) &&
+               (drvdata->msp->f_bitclk > 19200000)) {
+               /* If the bit-clock is higher than 19.2MHz, Vape should be
+                * run in 100% OPP. Only when bit-clock is used (MSP master) */
+               prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP,
+                                       "ux500-msp-i2s", 100);
+               drvdata->vape_opp_constraint = 1;
+       } else {
+               prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP,
+                                       "ux500-msp-i2s", 50);
+               drvdata->vape_opp_constraint = 0;
+       }
+
+       return ret;
+}
+
+static int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream,
+                               struct snd_pcm_hw_params *params,
+                               struct snd_soc_dai *dai)
+{
+       unsigned int mask, slots_active;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+
+       dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n",
+                       __func__, dai->id, snd_pcm_stream_str(substream));
+
+       switch (drvdata->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               snd_pcm_hw_constraint_minmax(runtime,
+                               SNDRV_PCM_HW_PARAM_CHANNELS,
+                               1, 2);
+               break;
+
+       case SND_SOC_DAIFMT_DSP_B:
+       case SND_SOC_DAIFMT_DSP_A:
+               mask = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+                       drvdata->tx_mask :
+                       drvdata->rx_mask;
+
+               slots_active = hweight32(mask);
+               dev_dbg(dai->dev, "TDM-slots active: %d", slots_active);
+
+               snd_pcm_hw_constraint_minmax(runtime,
+                               SNDRV_PCM_HW_PARAM_CHANNELS,
+                               slots_active, slots_active);
+               break;
+
+       default:
+               dev_err(dai->dev,
+                       "%s: Error: Unsupported protocol (fmt = 0x%x)!\n",
+                       __func__, drvdata->fmt);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int ux500_msp_dai_set_dai_fmt(struct snd_soc_dai *dai,
+                               unsigned int fmt)
+{
+       struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+
+       dev_dbg(dai->dev, "%s: MSP %d: Enter.\n", __func__, dai->id);
+
+       switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK |
+               SND_SOC_DAIFMT_MASTER_MASK)) {
+       case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
+       case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
+       case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBS_CFS:
+       case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM:
+       case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS:
+       case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM:
+               break;
+
+       default:
+               dev_err(dai->dev,
+                       "%s: Error: Unsupported protocol/master (fmt = 0x%x)!\n",
+                       __func__, drvdata->fmt);
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+       case SND_SOC_DAIFMT_NB_IF:
+       case SND_SOC_DAIFMT_IB_IF:
+               break;
+
+       default:
+               dev_err(dai->dev,
+                       "%s: Error: Unsupported inversion (fmt = 0x%x)!\n",
+                       __func__, drvdata->fmt);
+               return -EINVAL;
+       }
+
+       drvdata->fmt = fmt;
+       return 0;
+}
+
+static int ux500_msp_dai_set_tdm_slot(struct snd_soc_dai *dai,
+                               unsigned int tx_mask,
+                               unsigned int rx_mask,
+                               int slots, int slot_width)
+{
+       struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+       unsigned int cap;
+
+       switch (slots) {
+       case 1:
+               cap = 0x01;
+               break;
+       case 2:
+               cap = 0x03;
+               break;
+       case 8:
+               cap = 0xFF;
+               break;
+       case 16:
+               cap = 0xFFFF;
+               break;
+       default:
+               dev_err(dai->dev, "%s: Error: Unsupported slot-count (%d)!\n",
+                       __func__, slots);
+               return -EINVAL;
+       }
+       drvdata->slots = slots;
+
+       if (!(slot_width == 16)) {
+               dev_err(dai->dev, "%s: Error: Unsupported slot-width (%d)!\n",
+                       __func__, slot_width);
+               return -EINVAL;
+       }
+       drvdata->slot_width = slot_width;
+
+       drvdata->tx_mask = tx_mask & cap;
+       drvdata->rx_mask = rx_mask & cap;
+
+       return 0;
+}
+
+static int ux500_msp_dai_set_dai_sysclk(struct snd_soc_dai *dai,
+                                       int clk_id, unsigned int freq, int dir)
+{
+       struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+
+       dev_dbg(dai->dev, "%s: MSP %d: Enter. clk-id: %d, freq: %u.\n",
+               __func__, dai->id, clk_id, freq);
+
+       switch (clk_id) {
+       case UX500_MSP_MASTER_CLOCK:
+               drvdata->master_clk = freq;
+               break;
+
+       default:
+               dev_err(dai->dev, "%s: MSP %d: Invalid clk-id (%d)!\n",
+                       __func__, dai->id, clk_id);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int ux500_msp_dai_trigger(struct snd_pcm_substream *substream,
+                               int cmd, struct snd_soc_dai *dai)
+{
+       int ret = 0;
+       struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+
+       dev_dbg(dai->dev, "%s: MSP %d (%s): Enter (msp->id = %d, cmd = %d).\n",
+               __func__, dai->id, snd_pcm_stream_str(substream),
+               (int)drvdata->msp->id, cmd);
+
+       ret = ux500_msp_i2s_trigger(drvdata->msp, cmd, substream->stream);
+
+       return ret;
+}
+
+static int ux500_msp_dai_probe(struct snd_soc_dai *dai)
+{
+       struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
+
+       drvdata->playback_dma_data.dma_cfg = drvdata->msp->dma_cfg_tx;
+       drvdata->capture_dma_data.dma_cfg = drvdata->msp->dma_cfg_rx;
+
+       dai->playback_dma_data = &drvdata->playback_dma_data;
+       dai->capture_dma_data = &drvdata->capture_dma_data;
+
+       drvdata->playback_dma_data.data_size = drvdata->slot_width;
+       drvdata->capture_dma_data.data_size = drvdata->slot_width;
+
+       return 0;
+}
+
+static struct snd_soc_dai_ops ux500_msp_dai_ops[] = {
+       {
+               .set_sysclk = ux500_msp_dai_set_dai_sysclk,
+               .set_fmt = ux500_msp_dai_set_dai_fmt,
+               .set_tdm_slot = ux500_msp_dai_set_tdm_slot,
+               .startup = ux500_msp_dai_startup,
+               .shutdown = ux500_msp_dai_shutdown,
+               .prepare = ux500_msp_dai_prepare,
+               .trigger = ux500_msp_dai_trigger,
+               .hw_params = ux500_msp_dai_hw_params,
+       }
+};
+
+static struct snd_soc_dai_driver ux500_msp_dai_drv[UX500_NBR_OF_DAI] = {
+       {
+               .name = "ux500-msp-i2s.0",
+               .probe = ux500_msp_dai_probe,
+               .id = 0,
+               .suspend = NULL,
+               .resume = NULL,
+               .playback = {
+                       .channels_min = UX500_MSP_MIN_CHANNELS,
+                       .channels_max = UX500_MSP_MAX_CHANNELS,
+                       .rates = UX500_I2S_RATES,
+                       .formats = UX500_I2S_FORMATS,
+               },
+               .capture = {
+                       .channels_min = UX500_MSP_MIN_CHANNELS,
+                       .channels_max = UX500_MSP_MAX_CHANNELS,
+                       .rates = UX500_I2S_RATES,
+                       .formats = UX500_I2S_FORMATS,
+               },
+               .ops = ux500_msp_dai_ops,
+       },
+       {
+               .name = "ux500-msp-i2s.1",
+               .probe = ux500_msp_dai_probe,
+               .id = 1,
+               .suspend = NULL,
+               .resume = NULL,
+               .playback = {
+                       .channels_min = UX500_MSP_MIN_CHANNELS,
+                       .channels_max = UX500_MSP_MAX_CHANNELS,
+                       .rates = UX500_I2S_RATES,
+                       .formats = UX500_I2S_FORMATS,
+               },
+               .capture = {
+                       .channels_min = UX500_MSP_MIN_CHANNELS,
+                       .channels_max = UX500_MSP_MAX_CHANNELS,
+                       .rates = UX500_I2S_RATES,
+                       .formats = UX500_I2S_FORMATS,
+               },
+               .ops = ux500_msp_dai_ops,
+       },
+       {
+               .name = "ux500-msp-i2s.2",
+               .id = 2,
+               .probe = ux500_msp_dai_probe,
+               .suspend = NULL,
+               .resume = NULL,
+               .playback = {
+                       .channels_min = UX500_MSP_MIN_CHANNELS,
+                       .channels_max = UX500_MSP_MAX_CHANNELS,
+                       .rates = UX500_I2S_RATES,
+                       .formats = UX500_I2S_FORMATS,
+               },
+               .capture = {
+                       .channels_min = UX500_MSP_MIN_CHANNELS,
+                       .channels_max = UX500_MSP_MAX_CHANNELS,
+                       .rates = UX500_I2S_RATES,
+                       .formats = UX500_I2S_FORMATS,
+               },
+               .ops = ux500_msp_dai_ops,
+       },
+       {
+               .name = "ux500-msp-i2s.3",
+               .probe = ux500_msp_dai_probe,
+               .id = 3,
+               .suspend = NULL,
+               .resume = NULL,
+               .playback = {
+                       .channels_min = UX500_MSP_MIN_CHANNELS,
+                       .channels_max = UX500_MSP_MAX_CHANNELS,
+                       .rates = UX500_I2S_RATES,
+                       .formats = UX500_I2S_FORMATS,
+               },
+               .capture = {
+                       .channels_min = UX500_MSP_MIN_CHANNELS,
+                       .channels_max = UX500_MSP_MAX_CHANNELS,
+                       .rates = UX500_I2S_RATES,
+                       .formats = UX500_I2S_FORMATS,
+               },
+               .ops = ux500_msp_dai_ops,
+       },
+};
+
+static int __devinit ux500_msp_drv_probe(struct platform_device *pdev)
+{
+       struct ux500_msp_i2s_drvdata *drvdata;
+       int ret = 0;
+
+       dev_dbg(&pdev->dev, "%s: Enter (pdev->name = %s).\n", __func__,
+               pdev->name);
+
+       drvdata = devm_kzalloc(&pdev->dev,
+                               sizeof(struct ux500_msp_i2s_drvdata),
+                               GFP_KERNEL);
+       drvdata->fmt = 0;
+       drvdata->slots = 1;
+       drvdata->tx_mask = 0x01;
+       drvdata->rx_mask = 0x01;
+       drvdata->slot_width = 16;
+       drvdata->master_clk = MSP_INPUT_FREQ_APB;
+
+       drvdata->reg_vape = devm_regulator_get(&pdev->dev, "v-ape");
+       if (IS_ERR(drvdata->reg_vape)) {
+               ret = (int)PTR_ERR(drvdata->reg_vape);
+               dev_err(&pdev->dev,
+                       "%s: ERROR: Failed to get Vape supply (%d)!\n",
+                       __func__, ret);
+               return ret;
+       }
+       prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, (char *)pdev->name, 50);
+
+       drvdata->clk = clk_get(&pdev->dev, NULL);
+       if (IS_ERR(drvdata->clk)) {
+               ret = (int)PTR_ERR(drvdata->clk);
+               dev_err(&pdev->dev, "%s: ERROR: clk_get failed (%d)!\n",
+                       __func__, ret);
+               goto err_clk;
+       }
+
+       ret = ux500_msp_i2s_init_msp(pdev, &drvdata->msp,
+                               pdev->dev.platform_data);
+       if (!drvdata->msp) {
+               dev_err(&pdev->dev,
+                       "%s: ERROR: Failed to init MSP-struct (%d)!",
+                       __func__, ret);
+               goto err_init_msp;
+       }
+       dev_set_drvdata(&pdev->dev, drvdata);
+
+       ret = snd_soc_register_dai(&pdev->dev,
+                               &ux500_msp_dai_drv[drvdata->msp->id]);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Error: %s: Failed to register MSP%d!\n",
+                       __func__, drvdata->msp->id);
+               goto err_init_msp;
+       }
+
+       return 0;
+
+err_init_msp:
+       clk_put(drvdata->clk);
+
+err_clk:
+       devm_regulator_put(drvdata->reg_vape);
+
+       return ret;
+}
+
+static int __devexit ux500_msp_drv_remove(struct platform_device *pdev)
+{
+       struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(&pdev->dev);
+
+       snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(ux500_msp_dai_drv));
+
+       devm_regulator_put(drvdata->reg_vape);
+       prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, "ux500_msp_i2s");
+
+       clk_put(drvdata->clk);
+
+       ux500_msp_i2s_cleanup_msp(pdev, drvdata->msp);
+
+       return 0;
+}
+
+static struct platform_driver msp_i2s_driver = {
+       .driver = {
+               .name = "ux500-msp-i2s",
+               .owner = THIS_MODULE,
+       },
+       .probe = ux500_msp_drv_probe,
+       .remove = ux500_msp_drv_remove,
+};
+module_platform_driver(msp_i2s_driver);
+
+MODULE_LICENSE("GPLv2");
diff --git a/sound/soc/ux500/ux500_msp_dai.h b/sound/soc/ux500/ux500_msp_dai.h
new file mode 100644 (file)
index 0000000..98202a3
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ *
+ * Author: Ola Lilja <ola.o.lilja@stericsson.com>,
+ *         Roger Nilsson <roger.xr.nilsson@stericsson.com>
+ *         for ST-Ericsson.
+ *
+ * License terms:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#ifndef UX500_msp_dai_H
+#define UX500_msp_dai_H
+
+#include <linux/types.h>
+#include <linux/spinlock.h>
+
+#include "ux500_msp_i2s.h"
+
+#define UX500_NBR_OF_DAI       4
+
+#define UX500_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |  \
+                       SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+#define UX500_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+
+#define FRAME_PER_SINGLE_SLOT_8_KHZ            31
+#define FRAME_PER_SINGLE_SLOT_16_KHZ   124
+#define FRAME_PER_SINGLE_SLOT_44_1_KHZ 63
+#define FRAME_PER_SINGLE_SLOT_48_KHZ   49
+#define FRAME_PER_2_SLOTS                              31
+#define FRAME_PER_8_SLOTS                              138
+#define FRAME_PER_16_SLOTS                             277
+
+#ifndef CONFIG_SND_SOC_UX500_AB5500
+#define UX500_MSP_INTERNAL_CLOCK_FREQ  40000000
+#define UX500_MSP1_INTERNAL_CLOCK_FREQ UX500_MSP_INTERNAL_CLOCK_FREQ
+#else
+#define UX500_MSP_INTERNAL_CLOCK_FREQ 13000000
+#define UX500_MSP1_INTERNAL_CLOCK_FREQ (UX500_MSP_INTERNAL_CLOCK_FREQ * 2)
+#endif
+
+#define UX500_MSP_MIN_CHANNELS         1
+#define UX500_MSP_MAX_CHANNELS         8
+
+#define PLAYBACK_CONFIGURED            1
+#define CAPTURE_CONFIGURED             2
+
+enum ux500_msp_clock_id {
+       UX500_MSP_MASTER_CLOCK,
+};
+
+struct ux500_msp_i2s_drvdata {
+       struct ux500_msp *msp;
+       struct regulator *reg_vape;
+       struct ux500_msp_dma_params playback_dma_data;
+       struct ux500_msp_dma_params capture_dma_data;
+       unsigned int fmt;
+       unsigned int tx_mask;
+       unsigned int rx_mask;
+       int slots;
+       int slot_width;
+       u8 configured;
+       int data_delay;
+
+       /* Clocks */
+       unsigned int master_clk;
+       struct clk *clk;
+
+       /* Regulators */
+       int vape_opp_constraint;
+};
+
+int ux500_msp_dai_set_data_delay(struct snd_soc_dai *dai, int delay);
+
+#endif
diff --git a/sound/soc/ux500/ux500_msp_i2s.c b/sound/soc/ux500/ux500_msp_i2s.c
new file mode 100644 (file)
index 0000000..496dec1
--- /dev/null
@@ -0,0 +1,742 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ *
+ * Author: Ola Lilja <ola.o.lilja@stericsson.com>,
+ *         Roger Nilsson <roger.xr.nilsson@stericsson.com>,
+ *         Sandeep Kaushik <sandeep.kaushik@st.com>
+ *         for ST-Ericsson.
+ *
+ * License terms:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include <mach/hardware.h>
+#include <mach/board-mop500-msp.h>
+
+#include <sound/soc.h>
+
+#include "ux500_msp_i2s.h"
+
+ /* Protocol desciptors */
+static const struct msp_protdesc prot_descs[] = {
+       { /* I2S */
+               MSP_SINGLE_PHASE,
+               MSP_SINGLE_PHASE,
+               MSP_PHASE2_START_MODE_IMEDIATE,
+               MSP_PHASE2_START_MODE_IMEDIATE,
+               MSP_BTF_MS_BIT_FIRST,
+               MSP_BTF_MS_BIT_FIRST,
+               MSP_FRAME_LEN_1,
+               MSP_FRAME_LEN_1,
+               MSP_FRAME_LEN_1,
+               MSP_FRAME_LEN_1,
+               MSP_ELEM_LEN_32,
+               MSP_ELEM_LEN_32,
+               MSP_ELEM_LEN_32,
+               MSP_ELEM_LEN_32,
+               MSP_DELAY_1,
+               MSP_DELAY_1,
+               MSP_RISING_EDGE,
+               MSP_FALLING_EDGE,
+               MSP_FSYNC_POL_ACT_LO,
+               MSP_FSYNC_POL_ACT_LO,
+               MSP_SWAP_NONE,
+               MSP_SWAP_NONE,
+               MSP_COMPRESS_MODE_LINEAR,
+               MSP_EXPAND_MODE_LINEAR,
+               MSP_FSYNC_IGNORE,
+               31,
+               15,
+               32,
+       }, { /* PCM */
+               MSP_DUAL_PHASE,
+               MSP_DUAL_PHASE,
+               MSP_PHASE2_START_MODE_FSYNC,
+               MSP_PHASE2_START_MODE_FSYNC,
+               MSP_BTF_MS_BIT_FIRST,
+               MSP_BTF_MS_BIT_FIRST,
+               MSP_FRAME_LEN_1,
+               MSP_FRAME_LEN_1,
+               MSP_FRAME_LEN_1,
+               MSP_FRAME_LEN_1,
+               MSP_ELEM_LEN_16,
+               MSP_ELEM_LEN_16,
+               MSP_ELEM_LEN_16,
+               MSP_ELEM_LEN_16,
+               MSP_DELAY_0,
+               MSP_DELAY_0,
+               MSP_RISING_EDGE,
+               MSP_FALLING_EDGE,
+               MSP_FSYNC_POL_ACT_HI,
+               MSP_FSYNC_POL_ACT_HI,
+               MSP_SWAP_NONE,
+               MSP_SWAP_NONE,
+               MSP_COMPRESS_MODE_LINEAR,
+               MSP_EXPAND_MODE_LINEAR,
+               MSP_FSYNC_IGNORE,
+               255,
+               0,
+               256,
+       }, { /* Companded PCM */
+               MSP_SINGLE_PHASE,
+               MSP_SINGLE_PHASE,
+               MSP_PHASE2_START_MODE_FSYNC,
+               MSP_PHASE2_START_MODE_FSYNC,
+               MSP_BTF_MS_BIT_FIRST,
+               MSP_BTF_MS_BIT_FIRST,
+               MSP_FRAME_LEN_1,
+               MSP_FRAME_LEN_1,
+               MSP_FRAME_LEN_1,
+               MSP_FRAME_LEN_1,
+               MSP_ELEM_LEN_8,
+               MSP_ELEM_LEN_8,
+               MSP_ELEM_LEN_8,
+               MSP_ELEM_LEN_8,
+               MSP_DELAY_0,
+               MSP_DELAY_0,
+               MSP_RISING_EDGE,
+               MSP_RISING_EDGE,
+               MSP_FSYNC_POL_ACT_HI,
+               MSP_FSYNC_POL_ACT_HI,
+               MSP_SWAP_NONE,
+               MSP_SWAP_NONE,
+               MSP_COMPRESS_MODE_LINEAR,
+               MSP_EXPAND_MODE_LINEAR,
+               MSP_FSYNC_IGNORE,
+               255,
+               0,
+               256,
+       },
+};
+
+static void set_prot_desc_tx(struct ux500_msp *msp,
+                       struct msp_protdesc *protdesc,
+                       enum msp_data_size data_size)
+{
+       u32 temp_reg = 0;
+
+       temp_reg |= MSP_P2_ENABLE_BIT(protdesc->tx_phase_mode);
+       temp_reg |= MSP_P2_START_MODE_BIT(protdesc->tx_phase2_start_mode);
+       temp_reg |= MSP_P1_FRAME_LEN_BITS(protdesc->tx_frame_len_1);
+       temp_reg |= MSP_P2_FRAME_LEN_BITS(protdesc->tx_frame_len_2);
+       if (msp->def_elem_len) {
+               temp_reg |= MSP_P1_ELEM_LEN_BITS(protdesc->tx_elem_len_1);
+               temp_reg |= MSP_P2_ELEM_LEN_BITS(protdesc->tx_elem_len_2);
+       } else {
+               temp_reg |= MSP_P1_ELEM_LEN_BITS(data_size);
+               temp_reg |= MSP_P2_ELEM_LEN_BITS(data_size);
+       }
+       temp_reg |= MSP_DATA_DELAY_BITS(protdesc->tx_data_delay);
+       temp_reg |= MSP_SET_ENDIANNES_BIT(protdesc->tx_byte_order);
+       temp_reg |= MSP_FSYNC_POL(protdesc->tx_fsync_pol);
+       temp_reg |= MSP_DATA_WORD_SWAP(protdesc->tx_half_word_swap);
+       temp_reg |= MSP_SET_COMPANDING_MODE(protdesc->compression_mode);
+       temp_reg |= MSP_SET_FSYNC_IGNORE(protdesc->frame_sync_ignore);
+
+       writel(temp_reg, msp->registers + MSP_TCF);
+}
+
+static void set_prot_desc_rx(struct ux500_msp *msp,
+                       struct msp_protdesc *protdesc,
+                       enum msp_data_size data_size)
+{
+       u32 temp_reg = 0;
+
+       temp_reg |= MSP_P2_ENABLE_BIT(protdesc->rx_phase_mode);
+       temp_reg |= MSP_P2_START_MODE_BIT(protdesc->rx_phase2_start_mode);
+       temp_reg |= MSP_P1_FRAME_LEN_BITS(protdesc->rx_frame_len_1);
+       temp_reg |= MSP_P2_FRAME_LEN_BITS(protdesc->rx_frame_len_2);
+       if (msp->def_elem_len) {
+               temp_reg |= MSP_P1_ELEM_LEN_BITS(protdesc->rx_elem_len_1);
+               temp_reg |= MSP_P2_ELEM_LEN_BITS(protdesc->rx_elem_len_2);
+       } else {
+               temp_reg |= MSP_P1_ELEM_LEN_BITS(data_size);
+               temp_reg |= MSP_P2_ELEM_LEN_BITS(data_size);
+       }
+
+       temp_reg |= MSP_DATA_DELAY_BITS(protdesc->rx_data_delay);
+       temp_reg |= MSP_SET_ENDIANNES_BIT(protdesc->rx_byte_order);
+       temp_reg |= MSP_FSYNC_POL(protdesc->rx_fsync_pol);
+       temp_reg |= MSP_DATA_WORD_SWAP(protdesc->rx_half_word_swap);
+       temp_reg |= MSP_SET_COMPANDING_MODE(protdesc->expansion_mode);
+       temp_reg |= MSP_SET_FSYNC_IGNORE(protdesc->frame_sync_ignore);
+
+       writel(temp_reg, msp->registers + MSP_RCF);
+}
+
+static int configure_protocol(struct ux500_msp *msp,
+                       struct ux500_msp_config *config)
+{
+       struct msp_protdesc *protdesc;
+       enum msp_data_size data_size;
+       u32 temp_reg = 0;
+
+       data_size = config->data_size;
+       msp->def_elem_len = config->def_elem_len;
+       if (config->default_protdesc == 1) {
+               if (config->protocol >= MSP_INVALID_PROTOCOL) {
+                       dev_err(msp->dev, "%s: ERROR: Invalid protocol!\n",
+                               __func__);
+                       return -EINVAL;
+               }
+               protdesc =
+                   (struct msp_protdesc *)&prot_descs[config->protocol];
+       } else {
+               protdesc = (struct msp_protdesc *)&config->protdesc;
+       }
+
+       if (data_size < MSP_DATA_BITS_DEFAULT || data_size > MSP_DATA_BITS_32) {
+               dev_err(msp->dev,
+                       "%s: ERROR: Invalid data-size requested (data_size = %d)!\n",
+                       __func__, data_size);
+               return -EINVAL;
+       }
+
+       if (config->direction & MSP_DIR_TX)
+               set_prot_desc_tx(msp, protdesc, data_size);
+       if (config->direction & MSP_DIR_RX)
+               set_prot_desc_rx(msp, protdesc, data_size);
+
+       /* The code below should not be separated. */
+       temp_reg = readl(msp->registers + MSP_GCR) & ~TX_CLK_POL_RISING;
+       temp_reg |= MSP_TX_CLKPOL_BIT(~protdesc->tx_clk_pol);
+       writel(temp_reg, msp->registers + MSP_GCR);
+       temp_reg = readl(msp->registers + MSP_GCR) & ~RX_CLK_POL_RISING;
+       temp_reg |= MSP_RX_CLKPOL_BIT(protdesc->rx_clk_pol);
+       writel(temp_reg, msp->registers + MSP_GCR);
+
+       return 0;
+}
+
+static int setup_bitclk(struct ux500_msp *msp, struct ux500_msp_config *config)
+{
+       u32 reg_val_GCR;
+       u32 frame_per = 0;
+       u32 sck_div = 0;
+       u32 frame_width = 0;
+       u32 temp_reg = 0;
+       struct msp_protdesc *protdesc = NULL;
+
+       reg_val_GCR = readl(msp->registers + MSP_GCR);
+       writel(reg_val_GCR & ~SRG_ENABLE, msp->registers + MSP_GCR);
+
+       if (config->default_protdesc)
+               protdesc =
+                       (struct msp_protdesc *)&prot_descs[config->protocol];
+       else
+               protdesc = (struct msp_protdesc *)&config->protdesc;
+
+       switch (config->protocol) {
+       case MSP_PCM_PROTOCOL:
+       case MSP_PCM_COMPAND_PROTOCOL:
+               frame_width = protdesc->frame_width;
+               sck_div = config->f_inputclk / (config->frame_freq *
+                       (protdesc->clocks_per_frame));
+               frame_per = protdesc->frame_period;
+               break;
+       case MSP_I2S_PROTOCOL:
+               frame_width = protdesc->frame_width;
+               sck_div = config->f_inputclk / (config->frame_freq *
+                       (protdesc->clocks_per_frame));
+               frame_per = protdesc->frame_period;
+               break;
+       default:
+               dev_err(msp->dev, "%s: ERROR: Unknown protocol (%d)!\n",
+                       __func__,
+                       config->protocol);
+               return -EINVAL;
+       }
+
+       temp_reg = (sck_div - 1) & SCK_DIV_MASK;
+       temp_reg |= FRAME_WIDTH_BITS(frame_width);
+       temp_reg |= FRAME_PERIOD_BITS(frame_per);
+       writel(temp_reg, msp->registers + MSP_SRG);
+
+       msp->f_bitclk = (config->f_inputclk)/(sck_div + 1);
+
+       /* Enable bit-clock */
+       udelay(100);
+       reg_val_GCR = readl(msp->registers + MSP_GCR);
+       writel(reg_val_GCR | SRG_ENABLE, msp->registers + MSP_GCR);
+       udelay(100);
+
+       return 0;
+}
+
+static int configure_multichannel(struct ux500_msp *msp,
+                               struct ux500_msp_config *config)
+{
+       struct msp_protdesc *protdesc;
+       struct msp_multichannel_config *mcfg;
+       u32 reg_val_MCR;
+
+       if (config->default_protdesc == 1) {
+               if (config->protocol >= MSP_INVALID_PROTOCOL) {
+                       dev_err(msp->dev,
+                               "%s: ERROR: Invalid protocol (%d)!\n",
+                               __func__, config->protocol);
+                       return -EINVAL;
+               }
+               protdesc = (struct msp_protdesc *)
+                               &prot_descs[config->protocol];
+       } else {
+               protdesc = (struct msp_protdesc *)&config->protdesc;
+       }
+
+       mcfg = &config->multichannel_config;
+       if (mcfg->tx_multichannel_enable) {
+               if (protdesc->tx_phase_mode == MSP_SINGLE_PHASE) {
+                       reg_val_MCR = readl(msp->registers + MSP_MCR);
+                       writel(reg_val_MCR | (mcfg->tx_multichannel_enable ?
+                                               1 << TMCEN_BIT : 0),
+                               msp->registers + MSP_MCR);
+                       writel(mcfg->tx_channel_0_enable,
+                               msp->registers + MSP_TCE0);
+                       writel(mcfg->tx_channel_1_enable,
+                               msp->registers + MSP_TCE1);
+                       writel(mcfg->tx_channel_2_enable,
+                               msp->registers + MSP_TCE2);
+                       writel(mcfg->tx_channel_3_enable,
+                               msp->registers + MSP_TCE3);
+               } else {
+                       dev_err(msp->dev,
+                               "%s: ERROR: Only single-phase supported (TX-mode: %d)!\n",
+                               __func__, protdesc->tx_phase_mode);
+                       return -EINVAL;
+               }
+       }
+       if (mcfg->rx_multichannel_enable) {
+               if (protdesc->rx_phase_mode == MSP_SINGLE_PHASE) {
+                       reg_val_MCR = readl(msp->registers + MSP_MCR);
+                       writel(reg_val_MCR | (mcfg->rx_multichannel_enable ?
+                                               1 << RMCEN_BIT : 0),
+                               msp->registers + MSP_MCR);
+                       writel(mcfg->rx_channel_0_enable,
+                                       msp->registers + MSP_RCE0);
+                       writel(mcfg->rx_channel_1_enable,
+                                       msp->registers + MSP_RCE1);
+                       writel(mcfg->rx_channel_2_enable,
+                                       msp->registers + MSP_RCE2);
+                       writel(mcfg->rx_channel_3_enable,
+                                       msp->registers + MSP_RCE3);
+               } else {
+                       dev_err(msp->dev,
+                               "%s: ERROR: Only single-phase supported (RX-mode: %d)!\n",
+                               __func__, protdesc->rx_phase_mode);
+                       return -EINVAL;
+               }
+               if (mcfg->rx_comparison_enable_mode) {
+                       reg_val_MCR = readl(msp->registers + MSP_MCR);
+                       writel(reg_val_MCR |
+                               (mcfg->rx_comparison_enable_mode << RCMPM_BIT),
+                               msp->registers + MSP_MCR);
+
+                       writel(mcfg->comparison_mask,
+                                       msp->registers + MSP_RCM);
+                       writel(mcfg->comparison_value,
+                                       msp->registers + MSP_RCV);
+
+               }
+       }
+
+       return 0;
+}
+
+static int enable_msp(struct ux500_msp *msp, struct ux500_msp_config *config)
+{
+       int status = 0;
+       u32 reg_val_DMACR, reg_val_GCR;
+
+       /* Check msp state whether in RUN or CONFIGURED Mode */
+       if ((msp->msp_state == MSP_STATE_IDLE) && (msp->plat_init)) {
+               status = msp->plat_init();
+               if (status) {
+                       dev_err(msp->dev, "%s: ERROR: Failed to init MSP (%d)!\n",
+                               __func__, status);
+                       return status;
+               }
+       }
+
+       /* Configure msp with protocol dependent settings */
+       configure_protocol(msp, config);
+       setup_bitclk(msp, config);
+       if (config->multichannel_configured == 1) {
+               status = configure_multichannel(msp, config);
+               if (status)
+                       dev_warn(msp->dev,
+                               "%s: WARN: configure_multichannel failed (%d)!\n",
+                               __func__, status);
+       }
+
+       /* Make sure the correct DMA-directions are configured */
+       if ((config->direction & MSP_DIR_RX) && (!msp->dma_cfg_rx)) {
+               dev_err(msp->dev, "%s: ERROR: MSP RX-mode is not configured!",
+                       __func__);
+               return -EINVAL;
+       }
+       if ((config->direction == MSP_DIR_TX) && (!msp->dma_cfg_tx)) {
+               dev_err(msp->dev, "%s: ERROR: MSP TX-mode is not configured!",
+                       __func__);
+               return -EINVAL;
+       }
+
+       reg_val_DMACR = readl(msp->registers + MSP_DMACR);
+       if (config->direction & MSP_DIR_RX)
+               reg_val_DMACR |= RX_DMA_ENABLE;
+       if (config->direction & MSP_DIR_TX)
+               reg_val_DMACR |= TX_DMA_ENABLE;
+       writel(reg_val_DMACR, msp->registers + MSP_DMACR);
+
+       writel(config->iodelay, msp->registers + MSP_IODLY);
+
+       /* Enable frame generation logic */
+       reg_val_GCR = readl(msp->registers + MSP_GCR);
+       writel(reg_val_GCR | FRAME_GEN_ENABLE, msp->registers + MSP_GCR);
+
+       return status;
+}
+
+static void flush_fifo_rx(struct ux500_msp *msp)
+{
+       u32 reg_val_DR, reg_val_GCR, reg_val_FLR;
+       u32 limit = 32;
+
+       reg_val_GCR = readl(msp->registers + MSP_GCR);
+       writel(reg_val_GCR | RX_ENABLE, msp->registers + MSP_GCR);
+
+       reg_val_FLR = readl(msp->registers + MSP_FLR);
+       while (!(reg_val_FLR & RX_FIFO_EMPTY) && limit--) {
+               reg_val_DR = readl(msp->registers + MSP_DR);
+               reg_val_FLR = readl(msp->registers + MSP_FLR);
+       }
+
+       writel(reg_val_GCR, msp->registers + MSP_GCR);
+}
+
+static void flush_fifo_tx(struct ux500_msp *msp)
+{
+       u32 reg_val_TSTDR, reg_val_GCR, reg_val_FLR;
+       u32 limit = 32;
+
+       reg_val_GCR = readl(msp->registers + MSP_GCR);
+       writel(reg_val_GCR | TX_ENABLE, msp->registers + MSP_GCR);
+       writel(MSP_ITCR_ITEN | MSP_ITCR_TESTFIFO, msp->registers + MSP_ITCR);
+
+       reg_val_FLR = readl(msp->registers + MSP_FLR);
+       while (!(reg_val_FLR & TX_FIFO_EMPTY) && limit--) {
+               reg_val_TSTDR = readl(msp->registers + MSP_TSTDR);
+               reg_val_FLR = readl(msp->registers + MSP_FLR);
+       }
+       writel(0x0, msp->registers + MSP_ITCR);
+       writel(reg_val_GCR, msp->registers + MSP_GCR);
+}
+
+int ux500_msp_i2s_open(struct ux500_msp *msp,
+               struct ux500_msp_config *config)
+{
+       u32 old_reg, new_reg, mask;
+       int res;
+       unsigned int tx_sel, rx_sel, tx_busy, rx_busy;
+
+       if (in_interrupt()) {
+               dev_err(msp->dev,
+                       "%s: ERROR: Open called in interrupt context!\n",
+                       __func__);
+               return -1;
+       }
+
+       tx_sel = (config->direction & MSP_DIR_TX) > 0;
+       rx_sel = (config->direction & MSP_DIR_RX) > 0;
+       if (!tx_sel && !rx_sel) {
+               dev_err(msp->dev, "%s: Error: No direction selected!\n",
+                       __func__);
+               return -EINVAL;
+       }
+
+       tx_busy = (msp->dir_busy & MSP_DIR_TX) > 0;
+       rx_busy = (msp->dir_busy & MSP_DIR_RX) > 0;
+       if (tx_busy && tx_sel) {
+               dev_err(msp->dev, "%s: Error: TX is in use!\n", __func__);
+               return -EBUSY;
+       }
+       if (rx_busy && rx_sel) {
+               dev_err(msp->dev, "%s: Error: RX is in use!\n", __func__);
+               return -EBUSY;
+       }
+
+       msp->dir_busy |= (tx_sel ? MSP_DIR_TX : 0) | (rx_sel ? MSP_DIR_RX : 0);
+
+       /* First do the global config register */
+       mask = RX_CLK_SEL_MASK | TX_CLK_SEL_MASK | RX_FSYNC_MASK |
+           TX_FSYNC_MASK | RX_SYNC_SEL_MASK | TX_SYNC_SEL_MASK |
+           RX_FIFO_ENABLE_MASK | TX_FIFO_ENABLE_MASK | SRG_CLK_SEL_MASK |
+           LOOPBACK_MASK | TX_EXTRA_DELAY_MASK;
+
+       new_reg = (config->tx_clk_sel | config->rx_clk_sel |
+               config->rx_fsync_pol | config->tx_fsync_pol |
+               config->rx_fsync_sel | config->tx_fsync_sel |
+               config->rx_fifo_config | config->tx_fifo_config |
+               config->srg_clk_sel | config->loopback_enable |
+               config->tx_data_enable);
+
+       old_reg = readl(msp->registers + MSP_GCR);
+       old_reg &= ~mask;
+       new_reg |= old_reg;
+       writel(new_reg, msp->registers + MSP_GCR);
+
+       res = enable_msp(msp, config);
+       if (res < 0) {
+               dev_err(msp->dev, "%s: ERROR: enable_msp failed (%d)!\n",
+                       __func__, res);
+               return -EBUSY;
+       }
+       if (config->loopback_enable & 0x80)
+               msp->loopback_enable = 1;
+
+       /* Flush FIFOs */
+       flush_fifo_tx(msp);
+       flush_fifo_rx(msp);
+
+       msp->msp_state = MSP_STATE_CONFIGURED;
+       return 0;
+}
+
+static void disable_msp_rx(struct ux500_msp *msp)
+{
+       u32 reg_val_GCR, reg_val_DMACR, reg_val_IMSC;
+
+       reg_val_GCR = readl(msp->registers + MSP_GCR);
+       writel(reg_val_GCR & ~RX_ENABLE, msp->registers + MSP_GCR);
+       reg_val_DMACR = readl(msp->registers + MSP_DMACR);
+       writel(reg_val_DMACR & ~RX_DMA_ENABLE, msp->registers + MSP_DMACR);
+       reg_val_IMSC = readl(msp->registers + MSP_IMSC);
+       writel(reg_val_IMSC &
+                       ~(RX_SERVICE_INT | RX_OVERRUN_ERROR_INT),
+                       msp->registers + MSP_IMSC);
+
+       msp->dir_busy &= ~MSP_DIR_RX;
+}
+
+static void disable_msp_tx(struct ux500_msp *msp)
+{
+       u32 reg_val_GCR, reg_val_DMACR, reg_val_IMSC;
+
+       reg_val_GCR = readl(msp->registers + MSP_GCR);
+       writel(reg_val_GCR & ~TX_ENABLE, msp->registers + MSP_GCR);
+       reg_val_DMACR = readl(msp->registers + MSP_DMACR);
+       writel(reg_val_DMACR & ~TX_DMA_ENABLE, msp->registers + MSP_DMACR);
+       reg_val_IMSC = readl(msp->registers + MSP_IMSC);
+       writel(reg_val_IMSC &
+                       ~(TX_SERVICE_INT | TX_UNDERRUN_ERR_INT),
+                       msp->registers + MSP_IMSC);
+
+       msp->dir_busy &= ~MSP_DIR_TX;
+}
+
+static int disable_msp(struct ux500_msp *msp, unsigned int dir)
+{
+       u32 reg_val_GCR;
+       int status = 0;
+       unsigned int disable_tx, disable_rx;
+
+       reg_val_GCR = readl(msp->registers + MSP_GCR);
+       disable_tx = dir & MSP_DIR_TX;
+       disable_rx = dir & MSP_DIR_TX;
+       if (disable_tx && disable_rx) {
+               reg_val_GCR = readl(msp->registers + MSP_GCR);
+               writel(reg_val_GCR | LOOPBACK_MASK,
+                               msp->registers + MSP_GCR);
+
+               /* Flush TX-FIFO */
+               flush_fifo_tx(msp);
+
+               /* Disable TX-channel */
+               writel((readl(msp->registers + MSP_GCR) &
+                              (~TX_ENABLE)), msp->registers + MSP_GCR);
+
+               /* Flush RX-FIFO */
+               flush_fifo_rx(msp);
+
+               /* Disable Loopback and Receive channel */
+               writel((readl(msp->registers + MSP_GCR) &
+                               (~(RX_ENABLE | LOOPBACK_MASK))),
+                               msp->registers + MSP_GCR);
+
+               disable_msp_tx(msp);
+               disable_msp_rx(msp);
+       } else if (disable_tx)
+               disable_msp_tx(msp);
+       else if (disable_rx)
+               disable_msp_rx(msp);
+
+       return status;
+}
+
+int ux500_msp_i2s_trigger(struct ux500_msp *msp, int cmd, int direction)
+{
+       u32 reg_val_GCR, enable_bit;
+
+       if (msp->msp_state == MSP_STATE_IDLE) {
+               dev_err(msp->dev, "%s: ERROR: MSP is not configured!\n",
+                       __func__);
+               return -EINVAL;
+       }
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               if (direction == SNDRV_PCM_STREAM_PLAYBACK)
+                       enable_bit = TX_ENABLE;
+               else
+                       enable_bit = RX_ENABLE;
+               reg_val_GCR = readl(msp->registers + MSP_GCR);
+               writel(reg_val_GCR | enable_bit, msp->registers + MSP_GCR);
+               break;
+
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               if (direction == SNDRV_PCM_STREAM_PLAYBACK)
+                       disable_msp_tx(msp);
+               else
+                       disable_msp_rx(msp);
+               break;
+       default:
+               return -EINVAL;
+               break;
+       }
+
+       return 0;
+}
+
+int ux500_msp_i2s_close(struct ux500_msp *msp, unsigned int dir)
+{
+       int status = 0;
+
+       dev_dbg(msp->dev, "%s: Enter (dir = 0x%01x).\n", __func__, dir);
+
+       status = disable_msp(msp, dir);
+       if (msp->dir_busy == 0) {
+               /* disable sample rate and frame generators */
+               msp->msp_state = MSP_STATE_IDLE;
+               writel((readl(msp->registers + MSP_GCR) &
+                              (~(FRAME_GEN_ENABLE | SRG_ENABLE))),
+                             msp->registers + MSP_GCR);
+               if (msp->plat_exit)
+                       status = msp->plat_exit();
+                       if (status)
+                               dev_warn(msp->dev,
+                                       "%s: WARN: ux500_msp_i2s_exit failed (%d)!\n",
+                                       __func__, status);
+               writel(0, msp->registers + MSP_GCR);
+               writel(0, msp->registers + MSP_TCF);
+               writel(0, msp->registers + MSP_RCF);
+               writel(0, msp->registers + MSP_DMACR);
+               writel(0, msp->registers + MSP_SRG);
+               writel(0, msp->registers + MSP_MCR);
+               writel(0, msp->registers + MSP_RCM);
+               writel(0, msp->registers + MSP_RCV);
+               writel(0, msp->registers + MSP_TCE0);
+               writel(0, msp->registers + MSP_TCE1);
+               writel(0, msp->registers + MSP_TCE2);
+               writel(0, msp->registers + MSP_TCE3);
+               writel(0, msp->registers + MSP_RCE0);
+               writel(0, msp->registers + MSP_RCE1);
+               writel(0, msp->registers + MSP_RCE2);
+               writel(0, msp->registers + MSP_RCE3);
+       }
+
+       return status;
+
+}
+
+int ux500_msp_i2s_init_msp(struct platform_device *pdev,
+                       struct ux500_msp **msp_p,
+                       struct msp_i2s_platform_data *platform_data)
+{
+       int ret = 0;
+       struct resource *res = NULL;
+       struct i2s_controller *i2s_cont;
+       struct ux500_msp *msp;
+
+       dev_dbg(&pdev->dev, "%s: Enter (name: %s, id: %d).\n", __func__,
+               pdev->name, platform_data->id);
+
+       *msp_p = devm_kzalloc(&pdev->dev, sizeof(struct ux500_msp), GFP_KERNEL);
+       msp = *msp_p;
+
+       msp->id = platform_data->id;
+       msp->dev = &pdev->dev;
+       msp->plat_init = platform_data->msp_i2s_init;
+       msp->plat_exit = platform_data->msp_i2s_exit;
+       msp->dma_cfg_rx = platform_data->msp_i2s_dma_rx;
+       msp->dma_cfg_tx = platform_data->msp_i2s_dma_tx;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (res == NULL) {
+               dev_err(&pdev->dev, "%s: ERROR: Unable to get resource!\n",
+                       __func__);
+               ret = -ENOMEM;
+               goto err_res;
+       }
+
+       msp->registers = ioremap(res->start, (res->end - res->start + 1));
+       if (msp->registers == NULL) {
+               dev_err(&pdev->dev, "%s: ERROR: ioremap failed!\n", __func__);
+               ret = -ENOMEM;
+               goto err_res;
+       }
+
+       msp->msp_state = MSP_STATE_IDLE;
+       msp->loopback_enable = 0;
+
+       /* I2S-controller is allocated and added in I2S controller class. */
+       i2s_cont = devm_kzalloc(&pdev->dev, sizeof(*i2s_cont), GFP_KERNEL);
+       if (!i2s_cont) {
+               dev_err(&pdev->dev,
+                       "%s: ERROR: Failed to allocate I2S-controller!\n",
+                       __func__);
+               goto err_i2s_cont;
+       }
+       i2s_cont->dev.parent = &pdev->dev;
+       i2s_cont->data = (void *)msp;
+       i2s_cont->id = (s16)msp->id;
+       snprintf(i2s_cont->name, sizeof(i2s_cont->name), "ux500-msp-i2s.%04x",
+               msp->id);
+       dev_dbg(&pdev->dev, "I2S device-name: '%s'\n", i2s_cont->name);
+       msp->i2s_cont = i2s_cont;
+
+       return 0;
+
+err_i2s_cont:
+       iounmap(msp->registers);
+
+err_res:
+       devm_kfree(&pdev->dev, msp);
+
+       return ret;
+}
+
+void ux500_msp_i2s_cleanup_msp(struct platform_device *pdev,
+                       struct ux500_msp *msp)
+{
+       dev_dbg(msp->dev, "%s: Enter (id = %d).\n", __func__, msp->id);
+
+       device_unregister(&msp->i2s_cont->dev);
+       devm_kfree(&pdev->dev, msp->i2s_cont);
+
+       iounmap(msp->registers);
+
+       devm_kfree(&pdev->dev, msp);
+}
+
+MODULE_LICENSE("GPLv2");
diff --git a/sound/soc/ux500/ux500_msp_i2s.h b/sound/soc/ux500/ux500_msp_i2s.h
new file mode 100644 (file)
index 0000000..7f71b4a
--- /dev/null
@@ -0,0 +1,553 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ *
+ * Author: Ola Lilja <ola.o.lilja@stericsson.com>,
+ *         for ST-Ericsson.
+ *
+ * License terms:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+
+#ifndef UX500_MSP_I2S_H
+#define UX500_MSP_I2S_H
+
+#include <linux/platform_device.h>
+
+#include <mach/board-mop500-msp.h>
+
+#define MSP_INPUT_FREQ_APB 48000000
+
+/*** Stereo mode. Used for APB data accesses as 16 bits accesses (mono),
+ *   32 bits accesses (stereo).
+ ***/
+enum msp_stereo_mode {
+       MSP_MONO,
+       MSP_STEREO
+};
+
+/* Direction (Transmit/Receive mode) */
+enum msp_direction {
+       MSP_TX = 1,
+       MSP_RX = 2
+};
+
+/* Transmit and receive configuration register */
+#define MSP_BIG_ENDIAN           0x00000000
+#define MSP_LITTLE_ENDIAN        0x00001000
+#define MSP_UNEXPECTED_FS_ABORT  0x00000000
+#define MSP_UNEXPECTED_FS_IGNORE 0x00008000
+#define MSP_NON_MODE_BIT_MASK    0x00009000
+
+/* Global configuration register */
+#define RX_ENABLE             0x00000001
+#define RX_FIFO_ENABLE        0x00000002
+#define RX_SYNC_SRG           0x00000010
+#define RX_CLK_POL_RISING     0x00000020
+#define RX_CLK_SEL_SRG        0x00000040
+#define TX_ENABLE             0x00000100
+#define TX_FIFO_ENABLE        0x00000200
+#define TX_SYNC_SRG_PROG      0x00001800
+#define TX_SYNC_SRG_AUTO      0x00001000
+#define TX_CLK_POL_RISING     0x00002000
+#define TX_CLK_SEL_SRG        0x00004000
+#define TX_EXTRA_DELAY_ENABLE 0x00008000
+#define SRG_ENABLE            0x00010000
+#define FRAME_GEN_ENABLE      0x00100000
+#define SRG_CLK_SEL_APB       0x00000000
+#define RX_FIFO_SYNC_HI       0x00000000
+#define TX_FIFO_SYNC_HI       0x00000000
+#define SPI_CLK_MODE_NORMAL   0x00000000
+
+#define MSP_FRAME_SIZE_AUTO -1
+
+#define MSP_DR         0x00
+#define MSP_GCR                0x04
+#define MSP_TCF                0x08
+#define MSP_RCF                0x0c
+#define MSP_SRG                0x10
+#define MSP_FLR                0x14
+#define MSP_DMACR      0x18
+
+#define MSP_IMSC       0x20
+#define MSP_RIS                0x24
+#define MSP_MIS                0x28
+#define MSP_ICR                0x2c
+#define MSP_MCR                0x30
+#define MSP_RCV                0x34
+#define MSP_RCM                0x38
+
+#define MSP_TCE0       0x40
+#define MSP_TCE1       0x44
+#define MSP_TCE2       0x48
+#define MSP_TCE3       0x4c
+
+#define MSP_RCE0       0x60
+#define MSP_RCE1       0x64
+#define MSP_RCE2       0x68
+#define MSP_RCE3       0x6c
+#define MSP_IODLY      0x70
+
+#define MSP_ITCR       0x80
+#define MSP_ITIP       0x84
+#define MSP_ITOP       0x88
+#define MSP_TSTDR      0x8c
+
+#define MSP_PID0       0xfe0
+#define MSP_PID1       0xfe4
+#define MSP_PID2       0xfe8
+#define MSP_PID3       0xfec
+
+#define MSP_CID0       0xff0
+#define MSP_CID1       0xff4
+#define MSP_CID2       0xff8
+#define MSP_CID3       0xffc
+
+/* Protocol dependant parameters list */
+#define RX_ENABLE_MASK         BIT(0)
+#define RX_FIFO_ENABLE_MASK    BIT(1)
+#define RX_FSYNC_MASK          BIT(2)
+#define DIRECT_COMPANDING_MASK BIT(3)
+#define RX_SYNC_SEL_MASK       BIT(4)
+#define RX_CLK_POL_MASK                BIT(5)
+#define RX_CLK_SEL_MASK                BIT(6)
+#define LOOPBACK_MASK          BIT(7)
+#define TX_ENABLE_MASK         BIT(8)
+#define TX_FIFO_ENABLE_MASK    BIT(9)
+#define TX_FSYNC_MASK          BIT(10)
+#define TX_MSP_TDR_TSR         BIT(11)
+#define TX_SYNC_SEL_MASK       (BIT(12) | BIT(11))
+#define TX_CLK_POL_MASK                BIT(13)
+#define TX_CLK_SEL_MASK                BIT(14)
+#define TX_EXTRA_DELAY_MASK    BIT(15)
+#define SRG_ENABLE_MASK                BIT(16)
+#define SRG_CLK_POL_MASK       BIT(17)
+#define SRG_CLK_SEL_MASK       (BIT(19) | BIT(18))
+#define FRAME_GEN_EN_MASK      BIT(20)
+#define SPI_CLK_MODE_MASK      (BIT(22) | BIT(21))
+#define SPI_BURST_MODE_MASK    BIT(23)
+
+#define RXEN_SHIFT             0
+#define RFFEN_SHIFT            1
+#define RFSPOL_SHIFT           2
+#define DCM_SHIFT              3
+#define RFSSEL_SHIFT           4
+#define RCKPOL_SHIFT           5
+#define RCKSEL_SHIFT           6
+#define LBM_SHIFT              7
+#define TXEN_SHIFT             8
+#define TFFEN_SHIFT            9
+#define TFSPOL_SHIFT           10
+#define TFSSEL_SHIFT           11
+#define TCKPOL_SHIFT           13
+#define TCKSEL_SHIFT           14
+#define TXDDL_SHIFT            15
+#define SGEN_SHIFT             16
+#define SCKPOL_SHIFT           17
+#define SCKSEL_SHIFT           18
+#define FGEN_SHIFT             20
+#define SPICKM_SHIFT           21
+#define TBSWAP_SHIFT           28
+
+#define RCKPOL_MASK            BIT(0)
+#define TCKPOL_MASK            BIT(0)
+#define SPICKM_MASK            (BIT(1) | BIT(0))
+#define MSP_RX_CLKPOL_BIT(n)     ((n & RCKPOL_MASK) << RCKPOL_SHIFT)
+#define MSP_TX_CLKPOL_BIT(n)     ((n & TCKPOL_MASK) << TCKPOL_SHIFT)
+
+#define P1ELEN_SHIFT           0
+#define P1FLEN_SHIFT           3
+#define DTYP_SHIFT             10
+#define ENDN_SHIFT             12
+#define DDLY_SHIFT             13
+#define FSIG_SHIFT             15
+#define P2ELEN_SHIFT           16
+#define P2FLEN_SHIFT           19
+#define P2SM_SHIFT             26
+#define P2EN_SHIFT             27
+#define FSYNC_SHIFT            15
+
+#define P1ELEN_MASK            0x00000007
+#define P2ELEN_MASK            0x00070000
+#define P1FLEN_MASK            0x00000378
+#define P2FLEN_MASK            0x03780000
+#define DDLY_MASK              0x00003000
+#define DTYP_MASK              0x00000600
+#define P2SM_MASK              0x04000000
+#define P2EN_MASK              0x08000000
+#define ENDN_MASK              0x00001000
+#define TFSPOL_MASK            0x00000400
+#define TBSWAP_MASK            0x30000000
+#define COMPANDING_MODE_MASK   0x00000c00
+#define FSYNC_MASK             0x00008000
+
+#define MSP_P1_ELEM_LEN_BITS(n)                (n & P1ELEN_MASK)
+#define MSP_P2_ELEM_LEN_BITS(n)                (((n) << P2ELEN_SHIFT) & P2ELEN_MASK)
+#define MSP_P1_FRAME_LEN_BITS(n)       (((n) << P1FLEN_SHIFT) & P1FLEN_MASK)
+#define MSP_P2_FRAME_LEN_BITS(n)       (((n) << P2FLEN_SHIFT) & P2FLEN_MASK)
+#define MSP_DATA_DELAY_BITS(n)         (((n) << DDLY_SHIFT) & DDLY_MASK)
+#define MSP_DATA_TYPE_BITS(n)          (((n) << DTYP_SHIFT) & DTYP_MASK)
+#define MSP_P2_START_MODE_BIT(n)       ((n << P2SM_SHIFT) & P2SM_MASK)
+#define MSP_P2_ENABLE_BIT(n)           ((n << P2EN_SHIFT) & P2EN_MASK)
+#define MSP_SET_ENDIANNES_BIT(n)       ((n << ENDN_SHIFT) & ENDN_MASK)
+#define MSP_FSYNC_POL(n)               ((n << TFSPOL_SHIFT) & TFSPOL_MASK)
+#define MSP_DATA_WORD_SWAP(n)          ((n << TBSWAP_SHIFT) & TBSWAP_MASK)
+#define MSP_SET_COMPANDING_MODE(n)     ((n << DTYP_SHIFT) & \
+                                               COMPANDING_MODE_MASK)
+#define MSP_SET_FSYNC_IGNORE(n)                ((n << FSYNC_SHIFT) & FSYNC_MASK)
+
+/* Flag register */
+#define RX_BUSY                        BIT(0)
+#define RX_FIFO_EMPTY          BIT(1)
+#define RX_FIFO_FULL           BIT(2)
+#define TX_BUSY                        BIT(3)
+#define TX_FIFO_EMPTY          BIT(4)
+#define TX_FIFO_FULL           BIT(5)
+
+#define RBUSY_SHIFT            0
+#define RFE_SHIFT              1
+#define RFU_SHIFT              2
+#define TBUSY_SHIFT            3
+#define TFE_SHIFT              4
+#define TFU_SHIFT              5
+
+/* Multichannel control register */
+#define RMCEN_SHIFT            0
+#define RMCSF_SHIFT            1
+#define RCMPM_SHIFT            3
+#define TMCEN_SHIFT            5
+#define TNCSF_SHIFT            6
+
+/* Sample rate generator register */
+#define SCKDIV_SHIFT           0
+#define FRWID_SHIFT            10
+#define FRPER_SHIFT            16
+
+#define SCK_DIV_MASK           0x0000003FF
+#define FRAME_WIDTH_BITS(n)    (((n) << FRWID_SHIFT)  & 0x0000FC00)
+#define FRAME_PERIOD_BITS(n)   (((n) << FRPER_SHIFT) & 0x1FFF0000)
+
+/* DMA controller register */
+#define RX_DMA_ENABLE          BIT(0)
+#define TX_DMA_ENABLE          BIT(1)
+
+#define RDMAE_SHIFT            0
+#define TDMAE_SHIFT            1
+
+/* Interrupt Register */
+#define RX_SERVICE_INT         BIT(0)
+#define RX_OVERRUN_ERROR_INT   BIT(1)
+#define RX_FSYNC_ERR_INT       BIT(2)
+#define RX_FSYNC_INT           BIT(3)
+#define TX_SERVICE_INT         BIT(4)
+#define TX_UNDERRUN_ERR_INT    BIT(5)
+#define TX_FSYNC_ERR_INT       BIT(6)
+#define TX_FSYNC_INT           BIT(7)
+#define ALL_INT                        0x000000ff
+
+/* MSP test control register */
+#define MSP_ITCR_ITEN          BIT(0)
+#define MSP_ITCR_TESTFIFO      BIT(1)
+
+#define RMCEN_BIT   0
+#define RMCSF_BIT   1
+#define RCMPM_BIT   3
+#define TMCEN_BIT   5
+#define TNCSF_BIT   6
+
+/* Single or dual phase mode */
+enum msp_phase_mode {
+       MSP_SINGLE_PHASE,
+       MSP_DUAL_PHASE
+};
+
+/* Frame length */
+enum msp_frame_length {
+       MSP_FRAME_LEN_1 = 0,
+       MSP_FRAME_LEN_2 = 1,
+       MSP_FRAME_LEN_4 = 3,
+       MSP_FRAME_LEN_8 = 7,
+       MSP_FRAME_LEN_12 = 11,
+       MSP_FRAME_LEN_16 = 15,
+       MSP_FRAME_LEN_20 = 19,
+       MSP_FRAME_LEN_32 = 31,
+       MSP_FRAME_LEN_48 = 47,
+       MSP_FRAME_LEN_64 = 63
+};
+
+/* Element length */
+enum msp_elem_length {
+       MSP_ELEM_LEN_8 = 0,
+       MSP_ELEM_LEN_10 = 1,
+       MSP_ELEM_LEN_12 = 2,
+       MSP_ELEM_LEN_14 = 3,
+       MSP_ELEM_LEN_16 = 4,
+       MSP_ELEM_LEN_20 = 5,
+       MSP_ELEM_LEN_24 = 6,
+       MSP_ELEM_LEN_32 = 7
+};
+
+enum msp_data_xfer_width {
+       MSP_DATA_TRANSFER_WIDTH_BYTE,
+       MSP_DATA_TRANSFER_WIDTH_HALFWORD,
+       MSP_DATA_TRANSFER_WIDTH_WORD
+};
+
+enum msp_frame_sync {
+       MSP_FSYNC_UNIGNORE = 0,
+       MSP_FSYNC_IGNORE = 1,
+};
+
+enum msp_phase2_start_mode {
+       MSP_PHASE2_START_MODE_IMEDIATE,
+       MSP_PHASE2_START_MODE_FSYNC
+};
+
+enum msp_btf {
+       MSP_BTF_MS_BIT_FIRST = 0,
+       MSP_BTF_LS_BIT_FIRST = 1
+};
+
+enum msp_fsync_pol {
+       MSP_FSYNC_POL_ACT_HI = 0,
+       MSP_FSYNC_POL_ACT_LO = 1
+};
+
+/* Data delay (in bit clock cycles) */
+enum msp_delay {
+       MSP_DELAY_0 = 0,
+       MSP_DELAY_1 = 1,
+       MSP_DELAY_2 = 2,
+       MSP_DELAY_3 = 3
+};
+
+/* Configurations of clocks (transmit, receive or sample rate generator) */
+enum msp_edge {
+       MSP_FALLING_EDGE = 0,
+       MSP_RISING_EDGE = 1,
+};
+
+enum msp_hws {
+       MSP_SWAP_NONE = 0,
+       MSP_SWAP_BYTE_PER_WORD = 1,
+       MSP_SWAP_BYTE_PER_HALF_WORD = 2,
+       MSP_SWAP_HALF_WORD_PER_WORD = 3
+};
+
+enum msp_compress_mode {
+       MSP_COMPRESS_MODE_LINEAR = 0,
+       MSP_COMPRESS_MODE_MU_LAW = 2,
+       MSP_COMPRESS_MODE_A_LAW = 3
+};
+
+enum msp_spi_burst_mode {
+       MSP_SPI_BURST_MODE_DISABLE = 0,
+       MSP_SPI_BURST_MODE_ENABLE = 1
+};
+
+enum msp_expand_mode {
+       MSP_EXPAND_MODE_LINEAR = 0,
+       MSP_EXPAND_MODE_LINEAR_SIGNED = 1,
+       MSP_EXPAND_MODE_MU_LAW = 2,
+       MSP_EXPAND_MODE_A_LAW = 3
+};
+
+#define MSP_FRAME_PERIOD_IN_MONO_MODE 256
+#define MSP_FRAME_PERIOD_IN_STEREO_MODE 32
+#define MSP_FRAME_WIDTH_IN_STEREO_MODE 16
+
+enum msp_protocol {
+       MSP_I2S_PROTOCOL,
+       MSP_PCM_PROTOCOL,
+       MSP_PCM_COMPAND_PROTOCOL,
+       MSP_INVALID_PROTOCOL
+};
+
+/*
+ * No of registers to backup during
+ * suspend resume
+ */
+#define MAX_MSP_BACKUP_REGS 36
+
+enum enum_i2s_controller {
+       MSP_0_I2S_CONTROLLER = 0,
+       MSP_1_I2S_CONTROLLER,
+       MSP_2_I2S_CONTROLLER,
+       MSP_3_I2S_CONTROLLER,
+};
+
+enum i2s_direction_t {
+       MSP_DIR_TX = 0x01,
+       MSP_DIR_RX = 0x02,
+};
+
+enum msp_data_size {
+       MSP_DATA_BITS_DEFAULT = -1,
+       MSP_DATA_BITS_8 = 0x00,
+       MSP_DATA_BITS_10,
+       MSP_DATA_BITS_12,
+       MSP_DATA_BITS_14,
+       MSP_DATA_BITS_16,
+       MSP_DATA_BITS_20,
+       MSP_DATA_BITS_24,
+       MSP_DATA_BITS_32,
+};
+
+enum msp_state {
+       MSP_STATE_IDLE = 0,
+       MSP_STATE_CONFIGURED = 1,
+       MSP_STATE_RUNNING = 2,
+};
+
+enum msp_rx_comparison_enable_mode {
+       MSP_COMPARISON_DISABLED = 0,
+       MSP_COMPARISON_NONEQUAL_ENABLED = 2,
+       MSP_COMPARISON_EQUAL_ENABLED = 3
+};
+
+struct msp_multichannel_config {
+       bool rx_multichannel_enable;
+       bool tx_multichannel_enable;
+       enum msp_rx_comparison_enable_mode rx_comparison_enable_mode;
+       u8 padding;
+       u32 comparison_value;
+       u32 comparison_mask;
+       u32 rx_channel_0_enable;
+       u32 rx_channel_1_enable;
+       u32 rx_channel_2_enable;
+       u32 rx_channel_3_enable;
+       u32 tx_channel_0_enable;
+       u32 tx_channel_1_enable;
+       u32 tx_channel_2_enable;
+       u32 tx_channel_3_enable;
+};
+
+struct msp_protdesc {
+       u32 rx_phase_mode;
+       u32 tx_phase_mode;
+       u32 rx_phase2_start_mode;
+       u32 tx_phase2_start_mode;
+       u32 rx_byte_order;
+       u32 tx_byte_order;
+       u32 rx_frame_len_1;
+       u32 rx_frame_len_2;
+       u32 tx_frame_len_1;
+       u32 tx_frame_len_2;
+       u32 rx_elem_len_1;
+       u32 rx_elem_len_2;
+       u32 tx_elem_len_1;
+       u32 tx_elem_len_2;
+       u32 rx_data_delay;
+       u32 tx_data_delay;
+       u32 rx_clk_pol;
+       u32 tx_clk_pol;
+       u32 rx_fsync_pol;
+       u32 tx_fsync_pol;
+       u32 rx_half_word_swap;
+       u32 tx_half_word_swap;
+       u32 compression_mode;
+       u32 expansion_mode;
+       u32 frame_sync_ignore;
+       u32 frame_period;
+       u32 frame_width;
+       u32 clocks_per_frame;
+};
+
+struct i2s_message {
+       enum i2s_direction_t i2s_direction;
+       void *txdata;
+       void *rxdata;
+       size_t txbytes;
+       size_t rxbytes;
+       int dma_flag;
+       int tx_offset;
+       int rx_offset;
+       bool cyclic_dma;
+       dma_addr_t buf_addr;
+       size_t buf_len;
+       size_t period_len;
+};
+
+struct i2s_controller {
+       struct module *owner;
+       unsigned int id;
+       unsigned int class;
+       const struct i2s_algorithm *algo; /* the algorithm to access the bus */
+       void *data;
+       struct mutex bus_lock;
+       struct device dev; /* the controller device */
+       char name[48];
+};
+
+struct ux500_msp_config {
+       unsigned int f_inputclk;
+       unsigned int rx_clk_sel;
+       unsigned int tx_clk_sel;
+       unsigned int srg_clk_sel;
+       unsigned int rx_fsync_pol;
+       unsigned int tx_fsync_pol;
+       unsigned int rx_fsync_sel;
+       unsigned int tx_fsync_sel;
+       unsigned int rx_fifo_config;
+       unsigned int tx_fifo_config;
+       unsigned int spi_clk_mode;
+       unsigned int spi_burst_mode;
+       unsigned int loopback_enable;
+       unsigned int tx_data_enable;
+       unsigned int default_protdesc;
+       struct msp_protdesc protdesc;
+       int multichannel_configured;
+       struct msp_multichannel_config multichannel_config;
+       unsigned int direction;
+       unsigned int protocol;
+       unsigned int frame_freq;
+       unsigned int frame_size;
+       enum msp_data_size data_size;
+       unsigned int def_elem_len;
+       unsigned int iodelay;
+       void (*handler) (void *data);
+       void *tx_callback_data;
+       void *rx_callback_data;
+};
+
+struct ux500_msp {
+       enum enum_i2s_controller id;
+       void __iomem *registers;
+       struct device *dev;
+       struct i2s_controller *i2s_cont;
+       struct stedma40_chan_cfg *dma_cfg_rx;
+       struct stedma40_chan_cfg *dma_cfg_tx;
+       struct dma_chan *tx_pipeid;
+       struct dma_chan *rx_pipeid;
+       enum msp_state msp_state;
+       int (*transfer) (struct ux500_msp *msp, struct i2s_message *message);
+       int (*plat_init) (void);
+       int (*plat_exit) (void);
+       struct timer_list notify_timer;
+       int def_elem_len;
+       unsigned int dir_busy;
+       int loopback_enable;
+       u32 backup_regs[MAX_MSP_BACKUP_REGS];
+       unsigned int f_bitclk;
+};
+
+struct ux500_msp_dma_params {
+       unsigned int data_size;
+       struct stedma40_chan_cfg *dma_cfg;
+};
+
+int ux500_msp_i2s_init_msp(struct platform_device *pdev,
+                       struct ux500_msp **msp_p,
+                       struct msp_i2s_platform_data *platform_data);
+void ux500_msp_i2s_cleanup_msp(struct platform_device *pdev,
+                       struct ux500_msp *msp);
+int ux500_msp_i2s_open(struct ux500_msp *msp, struct ux500_msp_config *config);
+int ux500_msp_i2s_close(struct ux500_msp *msp,
+                       unsigned int dir);
+int ux500_msp_i2s_trigger(struct ux500_msp *msp, int cmd,
+                       int direction);
+
+#endif
This page took 0.0492 seconds and 5 git commands to generate.