Merge remote-tracking branches 'asoc/topic/es8328', 'asoc/topic/find-dai', 'asoc...
authorMark Brown <broonie@kernel.org>
Fri, 13 May 2016 13:27:01 +0000 (14:27 +0100)
committerMark Brown <broonie@kernel.org>
Fri, 13 May 2016 13:27:01 +0000 (14:27 +0100)
Documentation/devicetree/bindings/sound/fsl-sai.txt
MAINTAINERS
include/linux/mfd/syscon/imx6q-iomuxc-gpr.h
include/sound/soc.h
sound/soc/codecs/es8328.c
sound/soc/codecs/es8328.h
sound/soc/fsl/fsl_sai.c
sound/soc/fsl/fsl_ssi.c
sound/soc/soc-core.c

index 044e5d76e2dd32f251be377266b8fa0e3436e584..740b467adf7d1d46f318d5a2a6bf74f35cfa80bc 100644 (file)
@@ -7,8 +7,8 @@ codec/DSP interfaces.
 
 Required properties:
 
-  - compatible         : Compatible list, contains "fsl,vf610-sai" or
-                         "fsl,imx6sx-sai".
+  - compatible         : Compatible list, contains "fsl,vf610-sai",
+                         "fsl,imx6sx-sai" or "fsl,imx6ul-sai"
 
   - reg                        : Offset and length of the register set for the device.
 
@@ -48,6 +48,11 @@ Required properties:
                          receive data by following their own bit clocks and
                          frame sync clocks separately.
 
+Optional properties (for mx6ul):
+
+  - fsl,sai-mclk-direction-output: This is a boolean property. If present,
+                        indicates that SAI will output the SAI MCLK clock.
+
 Note:
 - If both fsl,sai-asynchronous and fsl,sai-synchronous-rx are absent, the
   default synchronous mode (sync Rx with Tx) will be used, which means both
index a727d9959ecd1d982e8e35ab20d3ee2eaeed57a5..09a9cf1e0a8a88149f94c6c4ece08f4b47720c89 100644 (file)
@@ -4661,6 +4661,7 @@ FREESCALE SOC SOUND DRIVERS
 M:     Timur Tabi <timur@tabi.org>
 M:     Nicolin Chen <nicoleotsuka@gmail.com>
 M:     Xiubo Li <Xiubo.Lee@gmail.com>
+R:     Fabio Estevam <fabio.estevam@nxp.com>
 L:     alsa-devel@alsa-project.org (moderated for non-subscribers)
 L:     linuxppc-dev@lists.ozlabs.org
 S:     Maintained
index 238c8db953eba25d61151709062968d669ae0b6d..68353822afceb7b37b95ee0a2a5e3504823559dc 100644 (file)
 #define IMX6UL_GPR1_ENET2_CLK_OUTPUT           (0x1 << 18)
 #define IMX6UL_GPR1_ENET_CLK_DIR               (0x3 << 17)
 #define IMX6UL_GPR1_ENET_CLK_OUTPUT            (0x3 << 17)
+#define IMX6UL_GPR1_SAI1_MCLK_DIR              (0x1 << 19)
+#define IMX6UL_GPR1_SAI2_MCLK_DIR              (0x1 << 20)
+#define IMX6UL_GPR1_SAI3_MCLK_DIR              (0x1 << 21)
+#define IMX6UL_GPR1_SAI_MCLK_MASK              (0x7 << 19)
+#define MCLK_DIR(x) (x == 1 ? IMX6UL_GPR1_SAI1_MCLK_DIR : x == 2 ? \
+                    IMX6UL_GPR1_SAI2_MCLK_DIR : IMX6UL_GPR1_SAI3_MCLK_DIR)
 
 #endif /* __LINUX_IMX6Q_IOMUXC_GPR_H */
index ef25e86d51ee68ad588790b7e212cf1d0d95b092..fd7b58a58d6f9c26b484210a237df709e3fb2f4d 100644 (file)
@@ -1683,6 +1683,9 @@ void snd_soc_remove_dai_link(struct snd_soc_card *card,
 int snd_soc_register_dai(struct snd_soc_component *component,
        struct snd_soc_dai_driver *dai_drv);
 
+struct snd_soc_dai *snd_soc_find_dai(
+       const struct snd_soc_dai_link_component *dlc);
+
 #include <sound/soc-dai.h>
 
 #ifdef CONFIG_DEBUG_FS
index afa6c5db9dccc9b00f049f8421b04e3091a1bcfc..2086d7107622d17ac3d750591f91c58db4993d8d 100644 (file)
 #include <sound/tlv.h>
 #include "es8328.h"
 
-#define ES8328_SYSCLK_RATE_1X 11289600
-#define ES8328_SYSCLK_RATE_2X 22579200
+static const unsigned int rates_12288[] = {
+       8000, 12000, 16000, 24000, 32000, 48000, 96000,
+};
 
-/* Run the codec at 22.5792 or 11.2896 MHz to support these rates */
-static struct {
-       int rate;
-       u8 ratio;
-} mclk_ratios[] = {
-       { 8000, 9 },
-       {11025, 7 },
-       {22050, 4 },
-       {44100, 2 },
+static const int ratios_12288[] = {
+       10, 7, 6, 4, 3, 2, 0,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_12288 = {
+       .count  = ARRAY_SIZE(rates_12288),
+       .list   = rates_12288,
+};
+
+static const unsigned int rates_11289[] = {
+       8018, 11025, 22050, 44100, 88200,
+};
+
+static const int ratios_11289[] = {
+       9, 7, 4, 2, 0,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_11289 = {
+       .count  = ARRAY_SIZE(rates_11289),
+       .list   = rates_11289,
 };
 
 /* regulator supplies for sgtl5000, VDDD is an optional external supply */
@@ -57,16 +69,28 @@ static const char * const supply_names[ES8328_SUPPLY_NUM] = {
        "HPVDD",
 };
 
-#define ES8328_RATES (SNDRV_PCM_RATE_44100 | \
+#define ES8328_RATES (SNDRV_PCM_RATE_96000 | \
+               SNDRV_PCM_RATE_48000 | \
+               SNDRV_PCM_RATE_44100 | \
+               SNDRV_PCM_RATE_32000 | \
                SNDRV_PCM_RATE_22050 | \
-               SNDRV_PCM_RATE_11025)
-#define ES8328_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+               SNDRV_PCM_RATE_16000 | \
+               SNDRV_PCM_RATE_11025 | \
+               SNDRV_PCM_RATE_8000)
+#define ES8328_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+               SNDRV_PCM_FMTBIT_S18_3LE | \
+               SNDRV_PCM_FMTBIT_S20_3LE | \
+               SNDRV_PCM_FMTBIT_S24_LE | \
+               SNDRV_PCM_FMTBIT_S32_LE)
 
 struct es8328_priv {
        struct regmap *regmap;
        struct clk *clk;
        int playback_fs;
        bool deemph;
+       int mclkdiv2;
+       const struct snd_pcm_hw_constraint_list *sysclk_constraints;
+       const int *mclk_ratios;
        struct regulator_bulk_data supplies[ES8328_SUPPLY_NUM];
 };
 
@@ -439,54 +463,131 @@ static int es8328_mute(struct snd_soc_dai *dai, int mute)
                        mute ? ES8328_DACCONTROL3_DACMUTE : 0);
 }
 
+static int es8328_startup(struct snd_pcm_substream *substream,
+                         struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
+
+       if (es8328->sysclk_constraints)
+               snd_pcm_hw_constraint_list(substream->runtime, 0,
+                               SNDRV_PCM_HW_PARAM_RATE,
+                               es8328->sysclk_constraints);
+
+       return 0;
+}
+
 static int es8328_hw_params(struct snd_pcm_substream *substream,
        struct snd_pcm_hw_params *params,
        struct snd_soc_dai *dai)
 {
        struct snd_soc_codec *codec = dai->codec;
        struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
-       int clk_rate;
        int i;
        int reg;
-       u8 ratio;
+       int wl;
+       int ratio;
+
+       if (!es8328->sysclk_constraints) {
+               dev_err(codec->dev, "No MCLK configured\n");
+               return -EINVAL;
+       }
 
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
                reg = ES8328_DACCONTROL2;
        else
                reg = ES8328_ADCCONTROL5;
 
-       clk_rate = clk_get_rate(es8328->clk);
+       for (i = 0; i < es8328->sysclk_constraints->count; i++)
+               if (es8328->sysclk_constraints->list[i] == params_rate(params))
+                       break;
 
-       if ((clk_rate != ES8328_SYSCLK_RATE_1X) &&
-               (clk_rate != ES8328_SYSCLK_RATE_2X)) {
-               dev_err(codec->dev,
-                       "%s: clock is running at %d Hz, not %d or %d Hz\n",
-                        __func__, clk_rate,
-                        ES8328_SYSCLK_RATE_1X, ES8328_SYSCLK_RATE_2X);
+       if (i == es8328->sysclk_constraints->count) {
+               dev_err(codec->dev, "LRCLK %d unsupported with current clock\n",
+                       params_rate(params));
                return -EINVAL;
        }
 
-       /* find master mode MCLK to sampling frequency ratio */
-       ratio = mclk_ratios[0].rate;
-       for (i = 1; i < ARRAY_SIZE(mclk_ratios); i++)
-               if (params_rate(params) <= mclk_ratios[i].rate)
-                       ratio = mclk_ratios[i].ratio;
+       ratio = es8328->mclk_ratios[i];
+       snd_soc_update_bits(codec, ES8328_MASTERMODE,
+                       ES8328_MASTERMODE_MCLKDIV2,
+                       es8328->mclkdiv2 ? ES8328_MASTERMODE_MCLKDIV2 : 0);
+
+       switch (params_width(params)) {
+       case 16:
+               wl = 3;
+               break;
+       case 18:
+               wl = 2;
+               break;
+       case 20:
+               wl = 1;
+               break;
+       case 24:
+               wl = 0;
+               break;
+       case 32:
+               wl = 4;
+               break;
+       default:
+               return -EINVAL;
+       }
 
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               snd_soc_update_bits(codec, ES8328_DACCONTROL1,
+                               ES8328_DACCONTROL1_DACWL_MASK,
+                               wl << ES8328_DACCONTROL1_DACWL_SHIFT);
+
                es8328->playback_fs = params_rate(params);
                es8328_set_deemph(codec);
-       }
+       } else
+               snd_soc_update_bits(codec, ES8328_ADCCONTROL4,
+                               ES8328_ADCCONTROL4_ADCWL_MASK,
+                               wl << ES8328_ADCCONTROL4_ADCWL_SHIFT);
 
        return snd_soc_update_bits(codec, reg, ES8328_RATEMASK, ratio);
 }
 
+static int es8328_set_sysclk(struct snd_soc_dai *codec_dai,
+               int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
+       int mclkdiv2 = 0;
+
+       switch (freq) {
+       case 0:
+               es8328->sysclk_constraints = NULL;
+               es8328->mclk_ratios = NULL;
+               break;
+       case 22579200:
+               mclkdiv2 = 1;
+               /* fallthru */
+       case 11289600:
+               es8328->sysclk_constraints = &constraints_11289;
+               es8328->mclk_ratios = ratios_11289;
+               break;
+       case 24576000:
+               mclkdiv2 = 1;
+               /* fallthru */
+       case 12288000:
+               es8328->sysclk_constraints = &constraints_12288;
+               es8328->mclk_ratios = ratios_12288;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       es8328->mclkdiv2 = mclkdiv2;
+       return 0;
+}
+
 static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
                unsigned int fmt)
 {
        struct snd_soc_codec *codec = codec_dai->codec;
-       struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
-       int clk_rate;
-       u8 mode = ES8328_DACCONTROL1_DACWL_16;
+       u8 dac_mode = 0;
+       u8 adc_mode = 0;
 
        /* set master/slave audio interface */
        if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBM_CFM)
@@ -495,13 +596,16 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
        /* interface format */
        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
        case SND_SOC_DAIFMT_I2S:
-               mode |= ES8328_DACCONTROL1_DACFORMAT_I2S;
+               dac_mode |= ES8328_DACCONTROL1_DACFORMAT_I2S;
+               adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_I2S;
                break;
        case SND_SOC_DAIFMT_RIGHT_J:
-               mode |= ES8328_DACCONTROL1_DACFORMAT_RJUST;
+               dac_mode |= ES8328_DACCONTROL1_DACFORMAT_RJUST;
+               adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_RJUST;
                break;
        case SND_SOC_DAIFMT_LEFT_J:
-               mode |= ES8328_DACCONTROL1_DACFORMAT_LJUST;
+               dac_mode |= ES8328_DACCONTROL1_DACFORMAT_LJUST;
+               adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_LJUST;
                break;
        default:
                return -EINVAL;
@@ -511,18 +615,14 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
        if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF)
                return -EINVAL;
 
-       snd_soc_write(codec, ES8328_DACCONTROL1, mode);
-       snd_soc_write(codec, ES8328_ADCCONTROL4, mode);
+       snd_soc_update_bits(codec, ES8328_DACCONTROL1,
+                       ES8328_DACCONTROL1_DACFORMAT_MASK, dac_mode);
+       snd_soc_update_bits(codec, ES8328_ADCCONTROL4,
+                       ES8328_ADCCONTROL4_ADCFORMAT_MASK, adc_mode);
 
        /* Master serial port mode, with BCLK generated automatically */
-       clk_rate = clk_get_rate(es8328->clk);
-       if (clk_rate == ES8328_SYSCLK_RATE_1X)
-               snd_soc_write(codec, ES8328_MASTERMODE,
-                               ES8328_MASTERMODE_MSC);
-       else
-               snd_soc_write(codec, ES8328_MASTERMODE,
-                               ES8328_MASTERMODE_MCLKDIV2 |
-                               ES8328_MASTERMODE_MSC);
+       snd_soc_update_bits(codec, ES8328_MASTERMODE,
+                       ES8328_MASTERMODE_MSC, ES8328_MASTERMODE_MSC);
 
        return 0;
 }
@@ -579,8 +679,10 @@ static int es8328_set_bias_level(struct snd_soc_codec *codec,
 }
 
 static const struct snd_soc_dai_ops es8328_dai_ops = {
+       .startup        = es8328_startup,
        .hw_params      = es8328_hw_params,
        .digital_mute   = es8328_mute,
+       .set_sysclk     = es8328_set_sysclk,
        .set_fmt        = es8328_set_dai_fmt,
 };
 
@@ -601,6 +703,7 @@ static struct snd_soc_dai_driver es8328_dai = {
                .formats = ES8328_FORMATS,
        },
        .ops = &es8328_dai_ops,
+       .symmetric_rates = 1,
 };
 
 static int es8328_suspend(struct snd_soc_codec *codec)
@@ -708,6 +811,7 @@ const struct regmap_config es8328_regmap_config = {
        .val_bits       = 8,
        .max_register   = ES8328_REG_MAX,
        .cache_type     = REGCACHE_RBTREE,
+       .use_single_rw  = true,
 };
 EXPORT_SYMBOL_GPL(es8328_regmap_config);
 
index 156c748c89c7e55d86421b27522b22f9b16d6f76..1a736e72a929999d3117a154cf8bc99188f2332e 100644 (file)
@@ -22,7 +22,7 @@ int es8328_probe(struct device *dev, struct regmap *regmap);
 #define ES8328_CONTROL1_VMIDSEL_50k (1 << 0)
 #define ES8328_CONTROL1_VMIDSEL_500k (2 << 0)
 #define ES8328_CONTROL1_VMIDSEL_5k (3 << 0)
-#define ES8328_CONTROL1_VMIDSEL_MASK (7 << 0)
+#define ES8328_CONTROL1_VMIDSEL_MASK (3 << 0)
 #define ES8328_CONTROL1_ENREF (1 << 2)
 #define ES8328_CONTROL1_SEQEN (1 << 3)
 #define ES8328_CONTROL1_SAMEFS (1 << 4)
@@ -84,7 +84,20 @@ int es8328_probe(struct device *dev, struct regmap *regmap);
 #define ES8328_ADCCONTROL1     0x09
 #define ES8328_ADCCONTROL2     0x0a
 #define ES8328_ADCCONTROL3     0x0b
+
 #define ES8328_ADCCONTROL4     0x0c
+#define ES8328_ADCCONTROL4_ADCFORMAT_MASK (3 << 0)
+#define ES8328_ADCCONTROL4_ADCFORMAT_I2S (0 << 0)
+#define ES8328_ADCCONTROL4_ADCFORMAT_LJUST (1 << 0)
+#define ES8328_ADCCONTROL4_ADCFORMAT_RJUST (2 << 0)
+#define ES8328_ADCCONTROL4_ADCFORMAT_PCM (3 << 0)
+#define ES8328_ADCCONTROL4_ADCWL_SHIFT 2
+#define ES8328_ADCCONTROL4_ADCWL_MASK (7 << 2)
+#define ES8328_ADCCONTROL4_ADCLRP_I2S_POL_NORMAL (0 << 5)
+#define ES8328_ADCCONTROL4_ADCLRP_I2S_POL_INV (1 << 5)
+#define ES8328_ADCCONTROL4_ADCLRP_PCM_MSB_CLK2 (0 << 5)
+#define ES8328_ADCCONTROL4_ADCLRP_PCM_MSB_CLK1 (1 << 5)
+
 #define ES8328_ADCCONTROL5     0x0d
 #define ES8328_ADCCONTROL5_RATEMASK (0x1f << 0)
 
@@ -109,15 +122,13 @@ int es8328_probe(struct device *dev, struct regmap *regmap);
 #define ES8328_ADCCONTROL14    0x16
 
 #define ES8328_DACCONTROL1     0x17
+#define ES8328_DACCONTROL1_DACFORMAT_MASK (3 << 1)
 #define ES8328_DACCONTROL1_DACFORMAT_I2S (0 << 1)
 #define ES8328_DACCONTROL1_DACFORMAT_LJUST (1 << 1)
 #define ES8328_DACCONTROL1_DACFORMAT_RJUST (2 << 1)
 #define ES8328_DACCONTROL1_DACFORMAT_PCM (3 << 1)
-#define ES8328_DACCONTROL1_DACWL_24 (0 << 3)
-#define ES8328_DACCONTROL1_DACWL_20 (1 << 3)
-#define ES8328_DACCONTROL1_DACWL_18 (2 << 3)
-#define ES8328_DACCONTROL1_DACWL_16 (3 << 3)
-#define ES8328_DACCONTROL1_DACWL_32 (4 << 3)
+#define ES8328_DACCONTROL1_DACWL_SHIFT 3
+#define ES8328_DACCONTROL1_DACWL_MASK (7 << 3)
 #define ES8328_DACCONTROL1_DACLRP_I2S_POL_NORMAL (0 << 6)
 #define ES8328_DACCONTROL1_DACLRP_I2S_POL_INV (1 << 6)
 #define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK2 (0 << 6)
index 0754df771e3b4c8a945c29e8c3c99f5c23403719..2147994ab46f6826824a3954947ed45285e0baae 100644 (file)
@@ -21,6 +21,8 @@
 #include <sound/core.h>
 #include <sound/dmaengine_pcm.h>
 #include <sound/pcm_params.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
 
 #include "fsl_sai.h"
 #include "imx-pcm.h"
@@ -786,10 +788,12 @@ static int fsl_sai_probe(struct platform_device *pdev)
 {
        struct device_node *np = pdev->dev.of_node;
        struct fsl_sai *sai;
+       struct regmap *gpr;
        struct resource *res;
        void __iomem *base;
        char tmp[8];
        int irq, ret, i;
+       int index;
 
        sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
        if (!sai)
@@ -797,7 +801,8 @@ static int fsl_sai_probe(struct platform_device *pdev)
 
        sai->pdev = pdev;
 
-       if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx6sx-sai"))
+       if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx6sx-sai") ||
+           of_device_is_compatible(pdev->dev.of_node, "fsl,imx6ul-sai"))
                sai->sai_on_imx = true;
 
        sai->is_lsb_first = of_property_read_bool(np, "lsb-first");
@@ -877,6 +882,22 @@ static int fsl_sai_probe(struct platform_device *pdev)
                fsl_sai_dai.symmetric_samplebits = 0;
        }
 
+       if (of_find_property(np, "fsl,sai-mclk-direction-output", NULL) &&
+           of_device_is_compatible(pdev->dev.of_node, "fsl,imx6ul-sai")) {
+               gpr = syscon_regmap_lookup_by_compatible("fsl,imx6ul-iomuxc-gpr");
+               if (IS_ERR(gpr)) {
+                       dev_err(&pdev->dev, "cannot find iomuxc registers\n");
+                       return PTR_ERR(gpr);
+               }
+
+               index = of_alias_get_id(np, "sai");
+               if (index < 0)
+                       return index;
+
+               regmap_update_bits(gpr, IOMUXC_GPR1, MCLK_DIR(index),
+                                  MCLK_DIR(index));
+       }
+
        sai->dma_params_rx.addr = res->start + FSL_SAI_RDR;
        sai->dma_params_tx.addr = res->start + FSL_SAI_TDR;
        sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX;
@@ -898,6 +919,7 @@ static int fsl_sai_probe(struct platform_device *pdev)
 static const struct of_device_id fsl_sai_ids[] = {
        { .compatible = "fsl,vf610-sai", },
        { .compatible = "fsl,imx6sx-sai", },
+       { .compatible = "fsl,imx6ul-sai", },
        { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, fsl_sai_ids);
index 08dcbbf60adbcf8b7461b6a88959492af3fba5ea..632ecc0e39562aab90a880e0c8b678a4ccea1078 100644 (file)
@@ -262,6 +262,7 @@ struct fsl_ssi_private {
        struct fsl_ssi_dbg dbg_stats;
 
        const struct fsl_ssi_soc_data *soc;
+       struct device *dev;
 };
 
 /*
@@ -400,6 +401,26 @@ static void fsl_ssi_rxtx_config(struct fsl_ssi_private *ssi_private,
        }
 }
 
+/*
+ * Clear RX or TX FIFO to remove samples from the previous
+ * stream session which may be still present in the FIFO and
+ * may introduce bad samples and/or channel slipping.
+ *
+ * Note: The SOR is not documented in recent IMX datasheet, but
+ * is described in IMX51 reference manual at section 56.3.3.15.
+ */
+static void fsl_ssi_fifo_clear(struct fsl_ssi_private *ssi_private,
+               bool is_rx)
+{
+       if (is_rx) {
+               regmap_update_bits(ssi_private->regs, CCSR_SSI_SOR,
+                       CCSR_SSI_SOR_RX_CLR, CCSR_SSI_SOR_RX_CLR);
+       } else {
+               regmap_update_bits(ssi_private->regs, CCSR_SSI_SOR,
+                       CCSR_SSI_SOR_TX_CLR, CCSR_SSI_SOR_TX_CLR);
+       }
+}
+
 /*
  * Calculate the bits that have to be disabled for the current stream that is
  * getting disabled. This keeps the bits enabled that are necessary for the
@@ -475,9 +496,11 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable,
         * (online configuration)
         */
        if (enable) {
-               regmap_update_bits(regs, CCSR_SSI_SIER, vals->sier, vals->sier);
+               fsl_ssi_fifo_clear(ssi_private, vals->scr & CCSR_SSI_SCR_RE);
+
                regmap_update_bits(regs, CCSR_SSI_SRCR, vals->srcr, vals->srcr);
                regmap_update_bits(regs, CCSR_SSI_STCR, vals->stcr, vals->stcr);
+               regmap_update_bits(regs, CCSR_SSI_SIER, vals->sier, vals->sier);
        } else {
                u32 sier;
                u32 srcr;
@@ -507,8 +530,40 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable,
 
 config_done:
        /* Enabling of subunits is done after configuration */
-       if (enable)
+       if (enable) {
+               if (ssi_private->use_dma && (vals->scr & CCSR_SSI_SCR_TE)) {
+                       /*
+                        * Be sure the Tx FIFO is filled when TE is set.
+                        * Otherwise, there are some chances to start the
+                        * playback with some void samples inserted first,
+                        * generating a channel slip.
+                        *
+                        * First, SSIEN must be set, to let the FIFO be filled.
+                        *
+                        * Notes:
+                        * - Limit this fix to the DMA case until FIQ cases can
+                        *   be tested.
+                        * - Limit the length of the busy loop to not lock the
+                        *   system too long, even if 1-2 loops are sufficient
+                        *   in general.
+                        */
+                       int i;
+                       int max_loop = 100;
+                       regmap_update_bits(regs, CCSR_SSI_SCR,
+                                       CCSR_SSI_SCR_SSIEN, CCSR_SSI_SCR_SSIEN);
+                       for (i = 0; i < max_loop; i++) {
+                               u32 sfcsr;
+                               regmap_read(regs, CCSR_SSI_SFCSR, &sfcsr);
+                               if (CCSR_SSI_SFCSR_TFCNT0(sfcsr))
+                                       break;
+                       }
+                       if (i == max_loop) {
+                               dev_err(ssi_private->dev,
+                                       "Timeout waiting TX FIFO filling\n");
+                       }
+               }
                regmap_update_bits(regs, CCSR_SSI_SCR, vals->scr, vals->scr);
+       }
 }
 
 
@@ -671,6 +726,15 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream,
        if (IS_ERR(ssi_private->baudclk))
                return -EINVAL;
 
+       /*
+        * Hardware limitation: The bclk rate must be
+        * never greater than 1/5 IPG clock rate
+        */
+       if (freq * 5 > clk_get_rate(ssi_private->clk)) {
+               dev_err(cpu_dai->dev, "bitclk > ipgclk/5\n");
+               return -EINVAL;
+       }
+
        baudclk_is_used = ssi_private->baudclk_streams & ~(BIT(substream->stream));
 
        /* It should be already enough to divide clock by setting pm alone */
@@ -687,13 +751,6 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream,
                else
                        clkrate = clk_round_rate(ssi_private->baudclk, tmprate);
 
-               /*
-                * Hardware limitation: The bclk rate must be
-                * never greater than 1/5 IPG clock rate
-                */
-               if (clkrate * 5 > clk_get_rate(ssi_private->clk))
-                       continue;
-
                clkrate /= factor;
                afreq = clkrate / (i + 1);
 
@@ -1159,14 +1216,14 @@ static struct snd_soc_dai_driver fsl_ssi_dai_template = {
        .playback = {
                .stream_name = "CPU-Playback",
                .channels_min = 1,
-               .channels_max = 2,
+               .channels_max = 32,
                .rates = FSLSSI_I2S_RATES,
                .formats = FSLSSI_I2S_FORMATS,
        },
        .capture = {
                .stream_name = "CPU-Capture",
                .channels_min = 1,
-               .channels_max = 2,
+               .channels_max = 32,
                .rates = FSLSSI_I2S_RATES,
                .formats = FSLSSI_I2S_FORMATS,
        },
@@ -1403,6 +1460,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
        }
 
        ssi_private->soc = of_id->data;
+       ssi_private->dev = &pdev->dev;
 
        sprop = of_get_property(np, "fsl,mode", NULL);
        if (sprop) {
index d2e62b159610bba4dce327a259affc62a92a6cde..16369cad480388c326f2463273e17ebef0324924 100644 (file)
@@ -930,7 +930,18 @@ static struct snd_soc_component *soc_find_component(
        return NULL;
 }
 
-static struct snd_soc_dai *snd_soc_find_dai(
+/**
+ * snd_soc_find_dai - Find a registered DAI
+ *
+ * @dlc: name of the DAI and optional component info to match
+ *
+ * This function will search all regsitered components and their DAIs to
+ * find the DAI of the same name. The component's of_node and name
+ * should also match if being specified.
+ *
+ * Return: pointer of DAI, or NULL if not found.
+ */
+struct snd_soc_dai *snd_soc_find_dai(
        const struct snd_soc_dai_link_component *dlc)
 {
        struct snd_soc_component *component;
@@ -959,6 +970,7 @@ static struct snd_soc_dai *snd_soc_find_dai(
 
        return NULL;
 }
+EXPORT_SYMBOL_GPL(snd_soc_find_dai);
 
 static bool soc_is_dai_link_bound(struct snd_soc_card *card,
                struct snd_soc_dai_link *dai_link)