Merge remote-tracking branches 'asoc/topic/rcar', 'asoc/topic/rockchip', 'asoc/topic...
authorMark Brown <broonie@kernel.org>
Mon, 8 Dec 2014 13:12:07 +0000 (13:12 +0000)
committerMark Brown <broonie@kernel.org>
Mon, 8 Dec 2014 13:12:07 +0000 (13:12 +0000)
14 files changed:
Documentation/devicetree/bindings/sound/renesas,rsnd.txt
Documentation/devicetree/bindings/sound/rt5631.txt [new file with mode: 0644]
include/sound/rcar_snd.h
sound/soc/codecs/Kconfig
sound/soc/codecs/rt286.c
sound/soc/codecs/rt5631.c
sound/soc/rockchip/Kconfig
sound/soc/sh/rcar/adg.c
sound/soc/sh/rcar/core.c
sound/soc/sh/rcar/dvc.c
sound/soc/sh/rcar/gen.c
sound/soc/sh/rcar/rsnd.h
sound/soc/sh/rcar/src.c
sound/soc/sh/rcar/ssi.c

index aa697abf337e7aa645f435fb9e77b809f62a49df..2dd690bc19cc6436914a1d6ccdf03ed6f36b6c0b 100644 (file)
@@ -1,8 +1,12 @@
 Renesas R-Car sound
 
 Required properties:
-- compatible                   : "renesas,rcar_sound-gen1" if generation1
+- compatible                   : "renesas,rcar_sound-<soctype>", fallbacks
+                                 "renesas,rcar_sound-gen1" if generation1, and
                                  "renesas,rcar_sound-gen2" if generation2
+                                 Examples with soctypes are:
+                                   - "renesas,rcar_sound-r8a7790" (R-Car H2)
+                                   - "renesas,rcar_sound-r8a7791" (R-Car M2-W)
 - reg                          : Should contain the register physical address.
                                  required register is
                                   SRU/ADG/SSI      if generation1
@@ -35,9 +39,9 @@ DAI subnode properties:
 
 Example:
 
-rcar_sound: rcar_sound@0xffd90000 {
+rcar_sound: rcar_sound@ec500000 {
        #sound-dai-cells = <1>;
-       compatible = "renesas,rcar_sound-gen2";
+       compatible = "renesas,rcar_sound-r8a7791", "renesas,rcar_sound-gen2";
        reg =   <0 0xec500000 0 0x1000>, /* SCU */
                <0 0xec5a0000 0 0x100>,  /* ADG */
                <0 0xec540000 0 0x1000>, /* SSIU */
diff --git a/Documentation/devicetree/bindings/sound/rt5631.txt b/Documentation/devicetree/bindings/sound/rt5631.txt
new file mode 100644 (file)
index 0000000..92b986c
--- /dev/null
@@ -0,0 +1,48 @@
+ALC5631/RT5631 audio CODEC
+
+This device supports I2C only.
+
+Required properties:
+
+  - compatible : "realtek,alc5631" or "realtek,rt5631"
+
+  - reg : the I2C address of the device.
+
+Pins on the device (for linking into audio routes):
+
+  * SPK_OUT_R_P
+  * SPK_OUT_R_N
+  * SPK_OUT_L_P
+  * SPK_OUT_L_N
+  * HP_OUT_L
+  * HP_OUT_R
+  * AUX_OUT2_LP
+  * AUX_OUT2_RN
+  * AUX_OUT1_LP
+  * AUX_OUT1_RN
+  * AUX_IN_L_JD
+  * AUX_IN_R_JD
+  * MONO_IN_P
+  * MONO_IN_N
+  * MIC1_P
+  * MIC1_N
+  * MIC2_P
+  * MIC2_N
+  * MONO_OUT_P
+  * MONO_OUT_N
+  * MICBIAS1
+  * MICBIAS2
+
+Example:
+
+alc5631: alc5631@1a {
+       compatible = "realtek,alc5631";
+       reg = <0x1a>;
+};
+
+or
+
+rt5631: rt5631@1a {
+       compatible = "realtek,rt5631";
+       reg = <0x1a>;
+};
index d76412b84b48c224a84df86049308ebb675d59db..83284cae464c492bf77cf127958f46469ee4b9c0 100644 (file)
 #define RSND_SSI_CLK_PIN_SHARE         (1 << 31)
 #define RSND_SSI_NO_BUSIF              (1 << 30) /* SSI+DMA without BUSIF */
 
-#define RSND_SSI(_dma_id, _pio_irq, _flags)            \
-{ .dma_id = _dma_id, .pio_irq = _pio_irq, .flags = _flags }
+#define RSND_SSI(_dma_id, _irq, _flags)                \
+{ .dma_id = _dma_id, .irq = _irq, .flags = _flags }
 #define RSND_SSI_UNUSED \
-{ .dma_id = -1, .pio_irq = -1, .flags = 0 }
+{ .dma_id = -1, .irq = -1, .flags = 0 }
 
 struct rsnd_ssi_platform_info {
        int dma_id;
-       int pio_irq;
+       int irq;
        u32 flags;
 };
 
index 1362edd380e5379f4f281aa84f124dd7a5e0d687..3fbf59484a427f6fce3186a32a6bbc5373d270b6 100644 (file)
@@ -501,7 +501,8 @@ config SND_SOC_RT286
        depends on I2C
 
 config SND_SOC_RT5631
-       tristate
+       tristate "Realtek ALC5631/RT5631 CODEC"
+       depends on I2C
 
 config SND_SOC_RT5640
        tristate
index 4aa555cbcca81ce60b880d6159b1fb91777e9920..2cd4fe463102d4532458e1ed3402abd276be8fc1 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
 #include <linux/spi/spi.h>
+#include <linux/dmi.h>
 #include <linux/acpi.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 
 struct rt286_priv {
        struct regmap *regmap;
+       struct snd_soc_codec *codec;
        struct rt286_platform_data pdata;
        struct i2c_client *i2c;
        struct snd_soc_jack *jack;
        struct delayed_work jack_detect_work;
        int sys_clk;
+       int clk_id;
        struct reg_default *index_cache;
 };
 
@@ -188,7 +191,7 @@ static int rt286_hw_write(void *context, unsigned int reg, unsigned int value)
        u8 data[4];
        int ret, i;
 
-       /*handle index registers*/
+       /* handle index registers */
        if (reg <= 0xff) {
                rt286_hw_write(client, RT286_COEF_INDEX, reg);
                for (i = 0; i < INDEX_CACHE_SIZE; i++) {
@@ -231,7 +234,7 @@ static int rt286_hw_read(void *context, unsigned int reg, unsigned int *value)
        __be32 be_reg;
        unsigned int index, vid, buf = 0x0;
 
-       /*handle index registers*/
+       /* handle index registers */
        if (reg <= 0xff) {
                rt286_hw_write(client, RT286_COEF_INDEX, reg);
                reg = RT286_PROC_COEF;
@@ -298,7 +301,6 @@ static int rt286_support_power_controls[] = {
 static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
 {
        unsigned int val, buf;
-       int i;
 
        *hp = false;
        *mic = false;
@@ -309,67 +311,44 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
                if (*hp) {
                        /* power on HV,VERF */
                        regmap_update_bits(rt286->regmap,
-                               RT286_POWER_CTRL1, 0x1001, 0x0);
+                               RT286_DC_GAIN, 0x200, 0x200);
+
+                       snd_soc_dapm_force_enable_pin(&rt286->codec->dapm,
+                                                       "HV");
+                       snd_soc_dapm_force_enable_pin(&rt286->codec->dapm,
+                                                       "VREF");
                        /* power LDO1 */
-                       regmap_update_bits(rt286->regmap,
-                               RT286_POWER_CTRL2, 0x4, 0x4);
-                       regmap_write(rt286->regmap, RT286_SET_MIC1, 0x24);
-                       regmap_read(rt286->regmap, RT286_CBJ_CTRL2, &val);
+                       snd_soc_dapm_force_enable_pin(&rt286->codec->dapm,
+                                                       "LDO1");
+                       snd_soc_dapm_sync(&rt286->codec->dapm);
 
-                       msleep(200);
-                       i = 40;
-                       while (((val & 0x0800) == 0) && (i > 0)) {
-                               regmap_read(rt286->regmap,
-                                       RT286_CBJ_CTRL2, &val);
-                               i--;
-                               msleep(20);
-                       }
+                       regmap_write(rt286->regmap, RT286_SET_MIC1, 0x24);
+                       msleep(50);
 
-                       if (0x0400 == (val & 0x0700)) {
-                               *mic = false;
+                       regmap_update_bits(rt286->regmap,
+                               RT286_CBJ_CTRL1, 0xfcc0, 0xd400);
+                       msleep(300);
+                       regmap_read(rt286->regmap, RT286_CBJ_CTRL2, &val);
 
-                               regmap_write(rt286->regmap,
-                                       RT286_SET_MIC1, 0x20);
-                               /* power off HV,VERF */
-                               regmap_update_bits(rt286->regmap,
-                                       RT286_POWER_CTRL1, 0x1001, 0x1001);
-                               regmap_update_bits(rt286->regmap,
-                                       RT286_A_BIAS_CTRL3, 0xc000, 0x0000);
-                               regmap_update_bits(rt286->regmap,
-                                       RT286_CBJ_CTRL1, 0x0030, 0x0000);
-                               regmap_update_bits(rt286->regmap,
-                                       RT286_A_BIAS_CTRL2, 0xc000, 0x0000);
-                       } else if ((0x0200 == (val & 0x0700)) ||
-                               (0x0100 == (val & 0x0700))) {
+                       if (0x0070 == (val & 0x0070)) {
                                *mic = true;
-                               regmap_update_bits(rt286->regmap,
-                                       RT286_A_BIAS_CTRL3, 0xc000, 0x8000);
-                               regmap_update_bits(rt286->regmap,
-                                       RT286_CBJ_CTRL1, 0x0030, 0x0020);
-                               regmap_update_bits(rt286->regmap,
-                                       RT286_A_BIAS_CTRL2, 0xc000, 0x8000);
                        } else {
-                               *mic = false;
+                               regmap_update_bits(rt286->regmap,
+                                       RT286_CBJ_CTRL1, 0xfcc0, 0xe400);
+                               msleep(300);
+                               regmap_read(rt286->regmap,
+                                       RT286_CBJ_CTRL2, &val);
+                               if (0x0070 == (val & 0x0070))
+                                       *mic = true;
+                               else
+                                       *mic = false;
                        }
-
-                       regmap_update_bits(rt286->regmap,
-                                               RT286_MISC_CTRL1,
-                                               0x0060, 0x0000);
-               } else {
-                       regmap_update_bits(rt286->regmap,
-                                               RT286_MISC_CTRL1,
-                                               0x0060, 0x0020);
                        regmap_update_bits(rt286->regmap,
-                                               RT286_A_BIAS_CTRL3,
-                                               0xc000, 0x8000);
-                       regmap_update_bits(rt286->regmap,
-                                               RT286_CBJ_CTRL1,
-                                               0x0030, 0x0020);
-                       regmap_update_bits(rt286->regmap,
-                                               RT286_A_BIAS_CTRL2,
-                                               0xc000, 0x8000);
+                               RT286_DC_GAIN, 0x200, 0x0);
 
+               } else {
                        *mic = false;
+                       regmap_write(rt286->regmap, RT286_SET_MIC1, 0x20);
                }
        } else {
                regmap_read(rt286->regmap, RT286_GET_HP_SENSE, &buf);
@@ -378,6 +357,12 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
                *mic = buf & 0x80000000;
        }
 
+       snd_soc_dapm_disable_pin(&rt286->codec->dapm, "HV");
+       snd_soc_dapm_disable_pin(&rt286->codec->dapm, "VREF");
+       if (!*hp)
+               snd_soc_dapm_disable_pin(&rt286->codec->dapm, "LDO1");
+       snd_soc_dapm_sync(&rt286->codec->dapm);
+
        return 0;
 }
 
@@ -415,6 +400,17 @@ int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
 }
 EXPORT_SYMBOL_GPL(rt286_mic_detect);
 
+static int is_mclk_mode(struct snd_soc_dapm_widget *source,
+                        struct snd_soc_dapm_widget *sink)
+{
+       struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(source->codec);
+
+       if (rt286->clk_id == RT286_SCLK_S_MCLK)
+               return 1;
+       else
+               return 0;
+}
+
 static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6350, 50, 0);
 static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0);
 
@@ -568,7 +564,84 @@ static int rt286_adc_event(struct snd_soc_dapm_widget *w,
        return 0;
 }
 
+static int rt286_vref_event(struct snd_soc_dapm_widget *w,
+                            struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               snd_soc_update_bits(codec,
+                       RT286_CBJ_CTRL1, 0x0400, 0x0000);
+               mdelay(50);
+               break;
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static int rt286_ldo2_event(struct snd_soc_dapm_widget *w,
+                            struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               snd_soc_update_bits(codec, RT286_POWER_CTRL2, 0x38, 0x08);
+               break;
+       case SND_SOC_DAPM_PRE_PMD:
+               snd_soc_update_bits(codec, RT286_POWER_CTRL2, 0x38, 0x30);
+               break;
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static int rt286_mic1_event(struct snd_soc_dapm_widget *w,
+                            struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               snd_soc_update_bits(codec,
+                       RT286_A_BIAS_CTRL3, 0xc000, 0x8000);
+               snd_soc_update_bits(codec,
+                       RT286_A_BIAS_CTRL2, 0xc000, 0x8000);
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               snd_soc_update_bits(codec,
+                       RT286_A_BIAS_CTRL3, 0xc000, 0x0000);
+               snd_soc_update_bits(codec,
+                       RT286_A_BIAS_CTRL2, 0xc000, 0x0000);
+               break;
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
 static const struct snd_soc_dapm_widget rt286_dapm_widgets[] = {
+       SND_SOC_DAPM_SUPPLY_S("HV", 1, RT286_POWER_CTRL1,
+               12, 1, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("VREF", RT286_POWER_CTRL1,
+               0, 1, rt286_vref_event, SND_SOC_DAPM_PRE_PMU),
+       SND_SOC_DAPM_SUPPLY_S("LDO1", 1, RT286_POWER_CTRL2,
+               2, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("LDO2", 2, RT286_POWER_CTRL1,
+               13, 1, rt286_ldo2_event, SND_SOC_DAPM_PRE_PMD |
+               SND_SOC_DAPM_POST_PMU),
+       SND_SOC_DAPM_SUPPLY("MCLK MODE", RT286_PLL_CTRL1,
+               5, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("MIC1 Input Buffer", SND_SOC_NOPM,
+               0, 0, rt286_mic1_event, SND_SOC_DAPM_PRE_PMU |
+               SND_SOC_DAPM_POST_PMD),
+
        /* Input Lines */
        SND_SOC_DAPM_INPUT("DMIC1 Pin"),
        SND_SOC_DAPM_INPUT("DMIC2 Pin"),
@@ -642,6 +715,25 @@ static const struct snd_soc_dapm_widget rt286_dapm_widgets[] = {
 };
 
 static const struct snd_soc_dapm_route rt286_dapm_routes[] = {
+       {"ADC 0", NULL, "MCLK MODE", is_mclk_mode},
+       {"ADC 1", NULL, "MCLK MODE", is_mclk_mode},
+       {"Front", NULL, "MCLK MODE", is_mclk_mode},
+       {"Surround", NULL, "MCLK MODE", is_mclk_mode},
+
+       {"HP Power", NULL, "LDO1"},
+       {"HP Power", NULL, "LDO2"},
+
+       {"MIC1", NULL, "LDO1"},
+       {"MIC1", NULL, "LDO2"},
+       {"MIC1", NULL, "HV"},
+       {"MIC1", NULL, "VREF"},
+       {"MIC1", NULL, "MIC1 Input Buffer"},
+
+       {"SPO", NULL, "LDO1"},
+       {"SPO", NULL, "LDO2"},
+       {"SPO", NULL, "HV"},
+       {"SPO", NULL, "VREF"},
+
        {"DMIC1", NULL, "DMIC1 Pin"},
        {"DMIC2", NULL, "DMIC2 Pin"},
        {"DMIC1", NULL, "DMIC Receiver"},
@@ -880,6 +972,7 @@ static int rt286_set_dai_sysclk(struct snd_soc_dai *dai,
        }
 
        rt286->sys_clk = freq;
+       rt286->clk_id = clk_id;
 
        return 0;
 }
@@ -915,13 +1008,18 @@ static int rt286_set_bias_level(struct snd_soc_codec *codec,
 
        case SND_SOC_BIAS_ON:
                mdelay(10);
+               snd_soc_update_bits(codec,
+                       RT286_CBJ_CTRL1, 0x0400, 0x0400);
+               snd_soc_update_bits(codec,
+                       RT286_DC_GAIN, 0x200, 0x0);
+
                break;
 
        case SND_SOC_BIAS_STANDBY:
                snd_soc_write(codec,
                        RT286_SET_AUDIO_POWER, AC_PWRST_D3);
                snd_soc_update_bits(codec,
-                       RT286_DC_GAIN, 0x200, 0x0);
+                       RT286_CBJ_CTRL1, 0x0400, 0x0000);
                break;
 
        default:
@@ -962,6 +1060,7 @@ static int rt286_probe(struct snd_soc_codec *codec)
 {
        struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
 
+       rt286->codec = codec;
        codec->dapm.bias_level = SND_SOC_BIAS_OFF;
 
        if (rt286->i2c->irq) {
@@ -1107,6 +1206,16 @@ static const struct acpi_device_id rt286_acpi_match[] = {
 };
 MODULE_DEVICE_TABLE(acpi, rt286_acpi_match);
 
+static struct dmi_system_id force_combo_jack_table[] = {
+       {
+               .ident = "Intel Wilson Beach",
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_NAME, "Wilson Beach SDS")
+               }
+       },
+       { }
+};
+
 static int rt286_i2c_probe(struct i2c_client *i2c,
                           const struct i2c_device_id *id)
 {
@@ -1142,6 +1251,9 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
        if (pdata)
                rt286->pdata = *pdata;
 
+       if (dmi_check_system(force_combo_jack_table))
+               rt286->pdata.cbj_en = true;
+
        regmap_write(rt286->regmap, RT286_SET_AUDIO_POWER, AC_PWRST_D3);
 
        for (i = 0; i < RT286_POWER_REG_LEN; i++)
@@ -1152,7 +1264,6 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
        if (!rt286->pdata.cbj_en) {
                regmap_write(rt286->regmap, RT286_CBJ_CTRL2, 0x0000);
                regmap_write(rt286->regmap, RT286_MIC1_DET_CTRL, 0x0816);
-               regmap_write(rt286->regmap, RT286_MISC_CTRL1, 0x0000);
                regmap_update_bits(rt286->regmap,
                                        RT286_CBJ_CTRL1, 0xf000, 0xb000);
        } else {
@@ -1169,10 +1280,12 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
 
        mdelay(10);
 
-       /*Power down LDO2*/
-       regmap_update_bits(rt286->regmap, RT286_POWER_CTRL2, 0x8, 0x0);
+       regmap_write(rt286->regmap, RT286_MISC_CTRL1, 0x0000);
+       /* Power down LDO, VREF */
+       regmap_update_bits(rt286->regmap, RT286_POWER_CTRL2, 0xc, 0x0);
+       regmap_update_bits(rt286->regmap, RT286_POWER_CTRL1, 0x1001, 0x1001);
 
-       /*Set depop parameter*/
+       /* Set depop parameter */
        regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL2, 0x403a, 0x401a);
        regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL3, 0xf777, 0x4737);
        regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL4, 0x00ff, 0x003f);
index 1ba27db660a60519e05cf584caf10dec2a488836..6d7b7ca7d530f034d2e972ba8070c03280e22bd0 100644 (file)
@@ -1612,29 +1612,6 @@ static int rt5631_probe(struct snd_soc_codec *codec)
        return 0;
 }
 
-static int rt5631_remove(struct snd_soc_codec *codec)
-{
-       rt5631_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
-#ifdef CONFIG_PM
-static int rt5631_suspend(struct snd_soc_codec *codec)
-{
-       rt5631_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
-static int rt5631_resume(struct snd_soc_codec *codec)
-{
-       rt5631_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       return 0;
-}
-#else
-#define rt5631_suspend NULL
-#define rt5631_resume NULL
-#endif
-
 #define RT5631_STEREO_RATES SNDRV_PCM_RATE_8000_96000
 #define RT5631_FORMAT  (SNDRV_PCM_FMTBIT_S16_LE | \
                        SNDRV_PCM_FMTBIT_S20_3LE | \
@@ -1672,10 +1649,8 @@ static struct snd_soc_dai_driver rt5631_dai[] = {
 
 static struct snd_soc_codec_driver soc_codec_dev_rt5631 = {
        .probe = rt5631_probe,
-       .remove = rt5631_remove,
-       .suspend = rt5631_suspend,
-       .resume = rt5631_resume,
        .set_bias_level = rt5631_set_bias_level,
+       .suspend_bias_off = true,
        .controls = rt5631_snd_controls,
        .num_controls = ARRAY_SIZE(rt5631_snd_controls),
        .dapm_widgets = rt5631_dapm_widgets,
@@ -1686,10 +1661,20 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5631 = {
 
 static const struct i2c_device_id rt5631_i2c_id[] = {
        { "rt5631", 0 },
+       { "alc5631", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, rt5631_i2c_id);
 
+#ifdef CONFIG_OF
+static struct of_device_id rt5631_i2c_dt_ids[] = {
+       { .compatible = "realtek,rt5631"},
+       { .compatible = "realtek,alc5631"},
+       { }
+};
+MODULE_DEVICE_TABLE(of, rt5631_i2c_dt_ids);
+#endif
+
 static const struct regmap_config rt5631_regmap_config = {
        .reg_bits = 8,
        .val_bits = 16,
@@ -1734,6 +1719,7 @@ static struct i2c_driver rt5631_i2c_driver = {
        .driver = {
                .name = "rt5631",
                .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(rt5631_i2c_dt_ids),
        },
        .probe = rt5631_i2c_probe,
        .remove   = rt5631_i2c_remove,
index 78fc159559b00228ed055dfaa025bc58f3928e67..e18182699d83537205725a03f93507b1e88ed7f7 100644 (file)
@@ -1,11 +1,16 @@
 config SND_SOC_ROCKCHIP
        tristate "ASoC support for Rockchip"
        depends on COMPILE_TEST || ARCH_ROCKCHIP
-       select SND_SOC_GENERIC_DMAENGINE_PCM
        help
          Say Y or M if you want to add support for codecs attached to
          the Rockchip SoCs' Audio interfaces. You will also need to
          select the audio interfaces to support below.
 
 config SND_SOC_ROCKCHIP_I2S
-       tristate
+       tristate "Rockchip I2S Device Driver"
+       depends on CLKDEV_LOOKUP && SND_SOC_ROCKCHIP
+       select SND_SOC_GENERIC_DMAENGINE_PCM
+       help
+         Say Y or M if you want to add support for I2S driver for
+         Rockchip I2S device. The device supports upto maximum of
+         8 channels each for play and record.
index fc41a0e8b09fdbdeb669ed7f46e98a4eb78bf292..14d1a71934692fb18b2155311b59a30b7cda62b1 100644 (file)
@@ -430,7 +430,7 @@ int rsnd_adg_probe(struct platform_device *pdev,
        adg->clk[CLKI]  = devm_clk_get(dev, "clk_i");
 
        for_each_rsnd_clk(clk, adg, i)
-               dev_dbg(dev, "clk %d : %p\n", i, clk);
+               dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk));
 
        rsnd_adg_ssi_clk_init(priv, adg);
 
index 70042197f9e26eaf6f95f5f429435388b12976b4..75308bbc2ce896e4a266ca0830cde8f5c020a225 100644 (file)
@@ -349,7 +349,7 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma,
                                                     dma_name);
        if (!dma->chan) {
                dev_err(dev, "can't get dma channel\n");
-               return -EIO;
+               goto rsnd_dma_channel_err;
        }
 
        ret = dmaengine_slave_config(dma->chan, &cfg);
@@ -363,8 +363,15 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma,
 
 rsnd_dma_init_err:
        rsnd_dma_quit(priv, dma);
+rsnd_dma_channel_err:
 
-       return ret;
+       /*
+        * DMA failed. try to PIO mode
+        * see
+        *      rsnd_ssi_fallback()
+        *      rsnd_rdai_continuance_probe()
+        */
+       return -EAGAIN;
 }
 
 void  rsnd_dma_quit(struct rsnd_priv *priv,
@@ -409,9 +416,16 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod)
 ({                                                             \
        struct rsnd_priv *priv = rsnd_mod_to_priv(mod);         \
        struct device *dev = rsnd_priv_to_dev(priv);            \
-       dev_dbg(dev, "%s [%d] %s\n",                            \
-               rsnd_mod_name(mod), rsnd_mod_id(mod), #func);   \
-       (mod)->ops->func(mod, rdai);                            \
+       u32 mask = 1 << __rsnd_mod_shift_##func;                        \
+       u32 call = __rsnd_mod_call_##func << __rsnd_mod_shift_##func;   \
+       int ret = 0;                                                    \
+       if ((mod->status & mask) == call) {                             \
+               dev_dbg(dev, "%s[%d] %s\n",                             \
+                       rsnd_mod_name(mod), rsnd_mod_id(mod), #func);   \
+               ret = (mod)->ops->func(mod, rdai);                      \
+               mod->status = (mod->status & ~mask) | (~call & mask);   \
+       }                                                               \
+       ret;                                                            \
 })
 
 #define rsnd_mod_call(mod, func, rdai...)      \
@@ -456,6 +470,13 @@ static int rsnd_dai_connect(struct rsnd_mod *mod,
        return 0;
 }
 
+static void rsnd_dai_disconnect(struct rsnd_mod *mod,
+                               struct rsnd_dai_stream *io)
+{
+       mod->io = NULL;
+       io->mod[mod->type] = NULL;
+}
+
 int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai)
 {
        int id = rdai - priv->rdai;
@@ -686,6 +707,20 @@ static const struct snd_soc_dai_ops rsnd_soc_dai_ops = {
        ret;                                                    \
 })
 
+#define rsnd_path_break(priv, io, type)                                \
+{                                                              \
+       struct rsnd_mod *mod;                                   \
+       int id = -1;                                            \
+                                                               \
+       if (rsnd_is_enable_path(io, type)) {                    \
+               id = rsnd_info_id(priv, io, type);              \
+               if (id >= 0) {                                  \
+                       mod = rsnd_##type##_mod_get(priv, id);  \
+                       rsnd_dai_disconnect(mod, io);           \
+               }                                               \
+       }                                                       \
+}
+
 static int rsnd_path_init(struct rsnd_priv *priv,
                          struct rsnd_dai *rdai,
                          struct rsnd_dai_stream *io)
@@ -933,6 +968,150 @@ static struct snd_pcm_ops rsnd_pcm_ops = {
        .pointer        = rsnd_pointer,
 };
 
+/*
+ *             snd_kcontrol
+ */
+#define kcontrol_to_cfg(kctrl) ((struct rsnd_kctrl_cfg *)kctrl->private_value)
+static int rsnd_kctrl_info(struct snd_kcontrol *kctrl,
+                          struct snd_ctl_elem_info *uinfo)
+{
+       struct rsnd_kctrl_cfg *cfg = kcontrol_to_cfg(kctrl);
+
+       if (cfg->texts) {
+               uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+               uinfo->count = cfg->size;
+               uinfo->value.enumerated.items = cfg->max;
+               if (uinfo->value.enumerated.item >= cfg->max)
+                       uinfo->value.enumerated.item = cfg->max - 1;
+               strlcpy(uinfo->value.enumerated.name,
+                       cfg->texts[uinfo->value.enumerated.item],
+                       sizeof(uinfo->value.enumerated.name));
+       } else {
+               uinfo->count = cfg->size;
+               uinfo->value.integer.min = 0;
+               uinfo->value.integer.max = cfg->max;
+               uinfo->type = (cfg->max == 1) ?
+                       SNDRV_CTL_ELEM_TYPE_BOOLEAN :
+                       SNDRV_CTL_ELEM_TYPE_INTEGER;
+       }
+
+       return 0;
+}
+
+static int rsnd_kctrl_get(struct snd_kcontrol *kctrl,
+                         struct snd_ctl_elem_value *uc)
+{
+       struct rsnd_kctrl_cfg *cfg = kcontrol_to_cfg(kctrl);
+       int i;
+
+       for (i = 0; i < cfg->size; i++)
+               if (cfg->texts)
+                       uc->value.enumerated.item[i] = cfg->val[i];
+               else
+                       uc->value.integer.value[i] = cfg->val[i];
+
+       return 0;
+}
+
+static int rsnd_kctrl_put(struct snd_kcontrol *kctrl,
+                         struct snd_ctl_elem_value *uc)
+{
+       struct rsnd_mod *mod = snd_kcontrol_chip(kctrl);
+       struct rsnd_kctrl_cfg *cfg = kcontrol_to_cfg(kctrl);
+       int i, change = 0;
+
+       for (i = 0; i < cfg->size; i++) {
+               if (cfg->texts) {
+                       change |= (uc->value.enumerated.item[i] != cfg->val[i]);
+                       cfg->val[i] = uc->value.enumerated.item[i];
+               } else {
+                       change |= (uc->value.integer.value[i] != cfg->val[i]);
+                       cfg->val[i] = uc->value.integer.value[i];
+               }
+       }
+
+       if (change)
+               cfg->update(mod);
+
+       return change;
+}
+
+static int __rsnd_kctrl_new(struct rsnd_mod *mod,
+                           struct rsnd_dai *rdai,
+                           struct snd_soc_pcm_runtime *rtd,
+                           const unsigned char *name,
+                           struct rsnd_kctrl_cfg *cfg,
+                           void (*update)(struct rsnd_mod *mod))
+{
+       struct snd_card *card = rtd->card->snd_card;
+       struct snd_kcontrol *kctrl;
+       struct snd_kcontrol_new knew = {
+               .iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name           = name,
+               .info           = rsnd_kctrl_info,
+               .get            = rsnd_kctrl_get,
+               .put            = rsnd_kctrl_put,
+               .private_value  = (unsigned long)cfg,
+       };
+       int ret;
+
+       kctrl = snd_ctl_new1(&knew, mod);
+       if (!kctrl)
+               return -ENOMEM;
+
+       ret = snd_ctl_add(card, kctrl);
+       if (ret < 0)
+               return ret;
+
+       cfg->update = update;
+
+       return 0;
+}
+
+int rsnd_kctrl_new_m(struct rsnd_mod *mod,
+                    struct rsnd_dai *rdai,
+                    struct snd_soc_pcm_runtime *rtd,
+                    const unsigned char *name,
+                    void (*update)(struct rsnd_mod *mod),
+                    struct rsnd_kctrl_cfg_m *_cfg,
+                    u32 max)
+{
+       _cfg->cfg.max   = max;
+       _cfg->cfg.size  = RSND_DVC_CHANNELS;
+       _cfg->cfg.val   = _cfg->val;
+       return __rsnd_kctrl_new(mod, rdai, rtd, name, &_cfg->cfg, update);
+}
+
+int rsnd_kctrl_new_s(struct rsnd_mod *mod,
+                    struct rsnd_dai *rdai,
+                    struct snd_soc_pcm_runtime *rtd,
+                    const unsigned char *name,
+                    void (*update)(struct rsnd_mod *mod),
+                    struct rsnd_kctrl_cfg_s *_cfg,
+                    u32 max)
+{
+       _cfg->cfg.max   = max;
+       _cfg->cfg.size  = 1;
+       _cfg->cfg.val   = &_cfg->val;
+       return __rsnd_kctrl_new(mod, rdai, rtd, name, &_cfg->cfg, update);
+}
+
+int rsnd_kctrl_new_e(struct rsnd_mod *mod,
+                    struct rsnd_dai *rdai,
+                    struct snd_soc_pcm_runtime *rtd,
+                    const unsigned char *name,
+                    struct rsnd_kctrl_cfg_s *_cfg,
+                    void (*update)(struct rsnd_mod *mod),
+                    const char * const *texts,
+                    u32 max)
+{
+       _cfg->cfg.max   = max;
+       _cfg->cfg.size  = 1;
+       _cfg->cfg.val   = &_cfg->val;
+       _cfg->cfg.texts = texts;
+       return __rsnd_kctrl_new(mod, rdai, rtd, name, &_cfg->cfg, update);
+}
+
 /*
  *             snd_soc_platform
  */
@@ -976,6 +1155,49 @@ static const struct snd_soc_component_driver rsnd_soc_component = {
        .name           = "rsnd",
 };
 
+static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv,
+                                      struct rsnd_dai *rdai,
+                                      int is_play)
+{
+       struct rsnd_dai_stream *io = is_play ? &rdai->playback : &rdai->capture;
+       int ret;
+
+       ret = rsnd_dai_call(probe, io, rdai);
+       if (ret == -EAGAIN) {
+               /*
+                * Fallback to PIO mode
+                */
+
+               /*
+                * call "remove" for SSI/SRC/DVC
+                * SSI will be switch to PIO mode if it was DMA mode
+                * see
+                *      rsnd_dma_init()
+                *      rsnd_ssi_fallback()
+                */
+               rsnd_dai_call(remove, io, rdai);
+
+               /*
+                * remove SRC/DVC from DAI,
+                */
+               rsnd_path_break(priv, io, src);
+               rsnd_path_break(priv, io, dvc);
+
+               /*
+                * fallback
+                */
+               rsnd_dai_call(fallback, io, rdai);
+
+               /*
+                * retry to "probe".
+                * DAI has SSI which is PIO mode only now.
+                */
+               ret = rsnd_dai_call(probe, io, rdai);
+       }
+
+       return ret;
+}
+
 /*
  *     rsnd probe
  */
@@ -1037,11 +1259,11 @@ static int rsnd_probe(struct platform_device *pdev)
        }
 
        for_each_rsnd_dai(rdai, priv, i) {
-               ret = rsnd_dai_call(probe, &rdai->playback, rdai);
+               ret = rsnd_rdai_continuance_probe(priv, rdai, 1);
                if (ret)
                        goto exit_snd_probe;
 
-               ret = rsnd_dai_call(probe, &rdai->capture, rdai);
+               ret = rsnd_rdai_continuance_probe(priv, rdai, 0);
                if (ret)
                        goto exit_snd_probe;
        }
index 3f443930c2b19b7b934a1754b12c0844c4a8b310..5380a4827ba79ecb88bcaeb411bc6465aecb3019 100644 (file)
@@ -11,8 +11,6 @@
 #include "rsnd.h"
 
 #define RSND_DVC_NAME_SIZE     16
-#define RSND_DVC_VOLUME_MAX    100
-#define RSND_DVC_VOLUME_NUM    2
 
 #define DVC_NAME "dvc"
 
@@ -20,8 +18,11 @@ struct rsnd_dvc {
        struct rsnd_dvc_platform_info *info; /* rcar_snd.h */
        struct rsnd_mod mod;
        struct clk *clk;
-       u8 volume[RSND_DVC_VOLUME_NUM];
-       u8 mute[RSND_DVC_VOLUME_NUM];
+       struct rsnd_kctrl_cfg_m volume;
+       struct rsnd_kctrl_cfg_m mute;
+       struct rsnd_kctrl_cfg_s ren;    /* Ramp Enable */
+       struct rsnd_kctrl_cfg_s rup;    /* Ramp Rate Up */
+       struct rsnd_kctrl_cfg_s rdown;  /* Ramp Rate Down */
 };
 
 #define rsnd_mod_to_dvc(_mod)  \
@@ -33,23 +34,87 @@ struct rsnd_dvc {
             ((pos) = (struct rsnd_dvc *)(priv)->dvc + i);      \
             i++)
 
+static const char const *dvc_ramp_rate[] = {
+       "128 dB/1 step",         /* 00000 */
+       "64 dB/1 step",          /* 00001 */
+       "32 dB/1 step",          /* 00010 */
+       "16 dB/1 step",          /* 00011 */
+       "8 dB/1 step",           /* 00100 */
+       "4 dB/1 step",           /* 00101 */
+       "2 dB/1 step",           /* 00110 */
+       "1 dB/1 step",           /* 00111 */
+       "0.5 dB/1 step",         /* 01000 */
+       "0.25 dB/1 step",        /* 01001 */
+       "0.125 dB/1 step",       /* 01010 */
+       "0.125 dB/2 steps",      /* 01011 */
+       "0.125 dB/4 steps",      /* 01100 */
+       "0.125 dB/8 steps",      /* 01101 */
+       "0.125 dB/16 steps",     /* 01110 */
+       "0.125 dB/32 steps",     /* 01111 */
+       "0.125 dB/64 steps",     /* 10000 */
+       "0.125 dB/128 steps",    /* 10001 */
+       "0.125 dB/256 steps",    /* 10010 */
+       "0.125 dB/512 steps",    /* 10011 */
+       "0.125 dB/1024 steps",   /* 10100 */
+       "0.125 dB/2048 steps",   /* 10101 */
+       "0.125 dB/4096 steps",   /* 10110 */
+       "0.125 dB/8192 steps",   /* 10111 */
+};
+
 static void rsnd_dvc_volume_update(struct rsnd_mod *mod)
 {
        struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
-       u32 max = (0x00800000 - 1);
-       u32 vol[RSND_DVC_VOLUME_NUM];
+       u32 val[RSND_DVC_CHANNELS];
+       u32 dvucr = 0;
        u32 mute = 0;
        int i;
 
-       for (i = 0; i < RSND_DVC_VOLUME_NUM; i++) {
-               vol[i] = max / RSND_DVC_VOLUME_MAX * dvc->volume[i];
-               mute |= (!!dvc->mute[i]) << i;
+       for (i = 0; i < dvc->mute.cfg.size; i++)
+               mute |= (!!dvc->mute.cfg.val[i]) << i;
+
+       /* Disable DVC Register access */
+       rsnd_mod_write(mod, DVC_DVUER, 0);
+
+       /* Enable Ramp */
+       if (dvc->ren.val) {
+               dvucr |= 0x10;
+
+               /* Digital Volume Max */
+               for (i = 0; i < RSND_DVC_CHANNELS; i++)
+                       val[i] = dvc->volume.cfg.max;
+
+               rsnd_mod_write(mod, DVC_VRCTR, 0xff);
+               rsnd_mod_write(mod, DVC_VRPDR, dvc->rup.val << 8 |
+                                              dvc->rdown.val);
+               /*
+                * FIXME !!
+                * use scale-downed Digital Volume
+                * as Volume Ramp
+                * 7F FFFF -> 3FF
+                */
+               rsnd_mod_write(mod, DVC_VRDBR,
+                              0x3ff - (dvc->volume.val[0] >> 13));
+
+       } else {
+               for (i = 0; i < RSND_DVC_CHANNELS; i++)
+                       val[i] = dvc->volume.val[i];
+       }
+
+       /* Enable Digital Volume */
+       dvucr |= 0x100;
+       rsnd_mod_write(mod, DVC_VOL0R, val[0]);
+       rsnd_mod_write(mod, DVC_VOL1R, val[1]);
+
+       /*  Enable Mute */
+       if (mute) {
+               dvucr |= 0x1;
+               rsnd_mod_write(mod, DVC_ZCMCR, mute);
        }
 
-       rsnd_mod_write(mod, DVC_VOL0R, vol[0]);
-       rsnd_mod_write(mod, DVC_VOL1R, vol[1]);
+       rsnd_mod_write(mod, DVC_DVUCR, dvucr);
 
-       rsnd_mod_write(mod, DVC_ZCMCR, mute);
+       /* Enable DVC Register access */
+       rsnd_mod_write(mod, DVC_DVUER, 1);
 }
 
 static int rsnd_dvc_probe_gen2(struct rsnd_mod *mod,
@@ -58,7 +123,8 @@ static int rsnd_dvc_probe_gen2(struct rsnd_mod *mod,
        struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
        struct device *dev = rsnd_priv_to_dev(priv);
 
-       dev_dbg(dev, "%s (Gen2) is probed\n", rsnd_mod_name(mod));
+       dev_dbg(dev, "%s[%d] (Gen2) is probed\n",
+               rsnd_mod_name(mod), rsnd_mod_id(mod));
 
        return 0;
 }
@@ -102,16 +168,11 @@ static int rsnd_dvc_init(struct rsnd_mod *dvc_mod,
 
        rsnd_mod_write(dvc_mod, DVC_ADINR, rsnd_get_adinr(dvc_mod));
 
-       /*  enable Volume / Mute */
-       rsnd_mod_write(dvc_mod, DVC_DVUCR, 0x101);
-
        /* ch0/ch1 Volume */
        rsnd_dvc_volume_update(dvc_mod);
 
        rsnd_mod_write(dvc_mod, DVC_DVUIR, 0);
 
-       rsnd_mod_write(dvc_mod, DVC_DVUER, 1);
-
        rsnd_adg_set_cmd_timsel_gen2(rdai, dvc_mod, io);
 
        return 0;
@@ -143,86 +204,6 @@ static int rsnd_dvc_stop(struct rsnd_mod *mod,
        return 0;
 }
 
-static int rsnd_dvc_volume_info(struct snd_kcontrol *kctrl,
-                              struct snd_ctl_elem_info *uinfo)
-{
-       struct rsnd_mod *mod = snd_kcontrol_chip(kctrl);
-       struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
-       u8 *val = (u8 *)kctrl->private_value;
-
-       uinfo->count = RSND_DVC_VOLUME_NUM;
-       uinfo->value.integer.min = 0;
-
-       if (val == dvc->volume) {
-               uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-               uinfo->value.integer.max = RSND_DVC_VOLUME_MAX;
-       } else {
-               uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
-               uinfo->value.integer.max = 1;
-       }
-
-       return 0;
-}
-
-static int rsnd_dvc_volume_get(struct snd_kcontrol *kctrl,
-                             struct snd_ctl_elem_value *ucontrol)
-{
-       u8 *val = (u8 *)kctrl->private_value;
-       int i;
-
-       for (i = 0; i < RSND_DVC_VOLUME_NUM; i++)
-               ucontrol->value.integer.value[i] = val[i];
-
-       return 0;
-}
-
-static int rsnd_dvc_volume_put(struct snd_kcontrol *kctrl,
-                             struct snd_ctl_elem_value *ucontrol)
-{
-       struct rsnd_mod *mod = snd_kcontrol_chip(kctrl);
-       u8 *val = (u8 *)kctrl->private_value;
-       int i, change = 0;
-
-       for (i = 0; i < RSND_DVC_VOLUME_NUM; i++) {
-               change |= (ucontrol->value.integer.value[i] != val[i]);
-               val[i] = ucontrol->value.integer.value[i];
-       }
-
-       if (change)
-               rsnd_dvc_volume_update(mod);
-
-       return change;
-}
-
-static int __rsnd_dvc_pcm_new(struct rsnd_mod *mod,
-                             struct rsnd_dai *rdai,
-                             struct snd_soc_pcm_runtime *rtd,
-                             const unsigned char *name,
-                             u8 *private)
-{
-       struct snd_card *card = rtd->card->snd_card;
-       struct snd_kcontrol *kctrl;
-       struct snd_kcontrol_new knew = {
-               .iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name           = name,
-               .info           = rsnd_dvc_volume_info,
-               .get            = rsnd_dvc_volume_get,
-               .put            = rsnd_dvc_volume_put,
-               .private_value  = (unsigned long)private,
-       };
-       int ret;
-
-       kctrl = snd_ctl_new1(&knew, mod);
-       if (!kctrl)
-               return -ENOMEM;
-
-       ret = snd_ctl_add(card, kctrl);
-       if (ret < 0)
-               return ret;
-
-       return 0;
-}
-
 static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
                            struct rsnd_dai *rdai,
                            struct snd_soc_pcm_runtime *rtd)
@@ -232,18 +213,48 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
        int ret;
 
        /* Volume */
-       ret = __rsnd_dvc_pcm_new(mod, rdai, rtd,
+       ret = rsnd_kctrl_new_m(mod, rdai, rtd,
                        rsnd_dai_is_play(rdai, io) ?
                        "DVC Out Playback Volume" : "DVC In Capture Volume",
-                       dvc->volume);
+                       rsnd_dvc_volume_update,
+                       &dvc->volume, 0x00800000 - 1);
        if (ret < 0)
                return ret;
 
        /* Mute */
-       ret = __rsnd_dvc_pcm_new(mod, rdai, rtd,
+       ret = rsnd_kctrl_new_m(mod, rdai, rtd,
                        rsnd_dai_is_play(rdai, io) ?
                        "DVC Out Mute Switch" : "DVC In Mute Switch",
-                       dvc->mute);
+                       rsnd_dvc_volume_update,
+                       &dvc->mute, 1);
+       if (ret < 0)
+               return ret;
+
+       /* Ramp */
+       ret = rsnd_kctrl_new_s(mod, rdai, rtd,
+                       rsnd_dai_is_play(rdai, io) ?
+                       "DVC Out Ramp Switch" : "DVC In Ramp Switch",
+                       rsnd_dvc_volume_update,
+                       &dvc->ren, 1);
+       if (ret < 0)
+               return ret;
+
+       ret = rsnd_kctrl_new_e(mod, rdai, rtd,
+                       rsnd_dai_is_play(rdai, io) ?
+                       "DVC Out Ramp Up Rate" : "DVC In Ramp Up Rate",
+                       &dvc->rup,
+                       rsnd_dvc_volume_update,
+                       dvc_ramp_rate, ARRAY_SIZE(dvc_ramp_rate));
+       if (ret < 0)
+               return ret;
+
+       ret = rsnd_kctrl_new_e(mod, rdai, rtd,
+                       rsnd_dai_is_play(rdai, io) ?
+                       "DVC Out Ramp Down Rate" : "DVC In Ramp Down Rate",
+                       &dvc->rdown,
+                       rsnd_dvc_volume_update,
+                       dvc_ramp_rate, ARRAY_SIZE(dvc_ramp_rate));
+
        if (ret < 0)
                return ret;
 
index f95e7ab135e83266f958c49801284a6e780744a3..87a6f2d627753c0f69ba0b51fb7e2d0bc1438f45 100644 (file)
@@ -8,6 +8,17 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
+
+/*
+ * #define DEBUG
+ *
+ * you can also add below in
+ * ${LINUX}/drivers/base/regmap/regmap.c
+ * for regmap debug
+ *
+ * #define LOG_DEVICE "xxxx.rcar_sound"
+ */
+
 #include "rsnd.h"
 
 struct rsnd_gen {
@@ -67,9 +78,10 @@ u32 rsnd_read(struct rsnd_priv *priv,
        if (!rsnd_is_accessible_reg(priv, gen, reg))
                return 0;
 
-       regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val);
+       dev_dbg(dev, "r %s[%d] - %4d : %08x\n",
+               rsnd_mod_name(mod), rsnd_mod_id(mod), reg, val);
 
-       dev_dbg(dev, "r %s - 0x%04d : %08x\n", rsnd_mod_name(mod), reg, val);
+       regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val);
 
        return val;
 }
@@ -84,9 +96,10 @@ void rsnd_write(struct rsnd_priv *priv,
        if (!rsnd_is_accessible_reg(priv, gen, reg))
                return;
 
-       regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data);
+       dev_dbg(dev, "w %s[%d] - %4d : %08x\n",
+               rsnd_mod_name(mod), rsnd_mod_id(mod), reg, data);
 
-       dev_dbg(dev, "w %s - 0x%04d : %08x\n", rsnd_mod_name(mod), reg, data);
+       regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data);
 }
 
 void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod,
@@ -98,11 +111,11 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod,
        if (!rsnd_is_accessible_reg(priv, gen, reg))
                return;
 
+       dev_dbg(dev, "b %s[%d] - %4d : %08x/%08x\n",
+               rsnd_mod_name(mod), rsnd_mod_id(mod), reg, data, mask);
+
        regmap_fields_update_bits(gen->regs[reg], rsnd_mod_id(mod),
                                  mask, data);
-
-       dev_dbg(dev, "b %s - 0x%04d : %08x/%08x\n",
-               rsnd_mod_name(mod), reg, data, mask);
 }
 
 #define rsnd_gen_regmap_init(priv, id_size, reg_id, conf)              \
@@ -311,6 +324,9 @@ static int rsnd_gen2_probe(struct platform_device *pdev,
                RSND_GEN_M_REG(DVC_ADINR,       0xe08,  0x100),
                RSND_GEN_M_REG(DVC_DVUCR,       0xe10,  0x100),
                RSND_GEN_M_REG(DVC_ZCMCR,       0xe14,  0x100),
+               RSND_GEN_M_REG(DVC_VRCTR,       0xe18,  0x100),
+               RSND_GEN_M_REG(DVC_VRPDR,       0xe1c,  0x100),
+               RSND_GEN_M_REG(DVC_VRDBR,       0xe20,  0x100),
                RSND_GEN_M_REG(DVC_VOL0R,       0xe28,  0x100),
                RSND_GEN_M_REG(DVC_VOL1R,       0xe2c,  0x100),
                RSND_GEN_M_REG(DVC_DVUER,       0xe48,  0x100),
index d119adf97c9c07538f7f4c05583482b8c52fea56..5826c8abf7949d3ed1807dfe1c61a7fa8a219f84 100644 (file)
@@ -91,6 +91,9 @@ enum rsnd_reg {
        RSND_REG_SHARE20,
        RSND_REG_SHARE21,
        RSND_REG_SHARE22,
+       RSND_REG_SHARE23,
+       RSND_REG_SHARE24,
+       RSND_REG_SHARE25,
 
        RSND_REG_MAX,
 };
@@ -129,6 +132,9 @@ enum rsnd_reg {
 #define RSND_REG_CMD_CTRL              RSND_REG_SHARE20
 #define RSND_REG_CMDOUT_TIMSEL         RSND_REG_SHARE21
 #define RSND_REG_BUSIF_DALIGN          RSND_REG_SHARE22
+#define RSND_REG_DVC_VRCTR             RSND_REG_SHARE23
+#define RSND_REG_DVC_VRPDR             RSND_REG_SHARE24
+#define RSND_REG_DVC_VRDBR             RSND_REG_SHARE25
 
 struct rsnd_of_data;
 struct rsnd_priv;
@@ -200,6 +206,8 @@ struct rsnd_mod_ops {
        int (*pcm_new)(struct rsnd_mod *mod,
                       struct rsnd_dai *rdai,
                       struct snd_soc_pcm_runtime *rtd);
+       int (*fallback)(struct rsnd_mod *mod,
+                       struct rsnd_dai *rdai);
 };
 
 struct rsnd_dai_stream;
@@ -210,7 +218,35 @@ struct rsnd_mod {
        struct rsnd_mod_ops *ops;
        struct rsnd_dma dma;
        struct rsnd_dai_stream *io;
+       u32 status;
 };
+/*
+ * status
+ *
+ * bit
+ * 0   0: probe        1: remove
+ * 1   0: init         1: quit
+ * 2   0: start        1: stop
+ * 3   0: pcm_new
+ * 4   0: fallback
+ */
+#define __rsnd_mod_shift_probe         0
+#define __rsnd_mod_shift_remove                0
+#define __rsnd_mod_shift_init          1
+#define __rsnd_mod_shift_quit          1
+#define __rsnd_mod_shift_start         2
+#define __rsnd_mod_shift_stop          2
+#define __rsnd_mod_shift_pcm_new       3
+#define __rsnd_mod_shift_fallback      4
+
+#define __rsnd_mod_call_probe          0
+#define __rsnd_mod_call_remove         1
+#define __rsnd_mod_call_init           0
+#define __rsnd_mod_call_quit           1
+#define __rsnd_mod_call_start          0
+#define __rsnd_mod_call_stop           1
+#define __rsnd_mod_call_pcm_new                0
+#define __rsnd_mod_call_fallback       0
 
 #define rsnd_mod_to_priv(mod) ((mod)->priv)
 #define rsnd_mod_to_dma(mod) (&(mod)->dma)
@@ -267,7 +303,8 @@ struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id);
 int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io);
 int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai);
 #define rsnd_dai_get_platform_info(rdai) ((rdai)->info)
-#define rsnd_io_to_runtime(io) ((io)->substream->runtime)
+#define rsnd_io_to_runtime(io) ((io)->substream ? \
+                               (io)->substream->runtime : NULL)
 
 void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt);
 int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional);
@@ -381,6 +418,51 @@ struct rsnd_priv {
        is_play;                                                        \
 })
 
+/*
+ *     rsnd_kctrl
+ */
+struct rsnd_kctrl_cfg {
+       unsigned int max;
+       unsigned int size;
+       u32 *val;
+       const char * const *texts;
+       void (*update)(struct rsnd_mod *mod);
+};
+
+#define RSND_DVC_CHANNELS      2
+struct rsnd_kctrl_cfg_m {
+       struct rsnd_kctrl_cfg cfg;
+       u32 val[RSND_DVC_CHANNELS];
+};
+
+struct rsnd_kctrl_cfg_s {
+       struct rsnd_kctrl_cfg cfg;
+       u32 val;
+};
+
+int rsnd_kctrl_new_m(struct rsnd_mod *mod,
+                    struct rsnd_dai *rdai,
+                    struct snd_soc_pcm_runtime *rtd,
+                    const unsigned char *name,
+                    void (*update)(struct rsnd_mod *mod),
+                    struct rsnd_kctrl_cfg_m *_cfg,
+                    u32 max);
+int rsnd_kctrl_new_s(struct rsnd_mod *mod,
+                    struct rsnd_dai *rdai,
+                    struct snd_soc_pcm_runtime *rtd,
+                    const unsigned char *name,
+                    void (*update)(struct rsnd_mod *mod),
+                    struct rsnd_kctrl_cfg_s *_cfg,
+                    u32 max);
+int rsnd_kctrl_new_e(struct rsnd_mod *mod,
+                    struct rsnd_dai *rdai,
+                    struct snd_soc_pcm_runtime *rtd,
+                    const unsigned char *name,
+                    struct rsnd_kctrl_cfg_s *_cfg,
+                    void (*update)(struct rsnd_mod *mod),
+                    const char * const *texts,
+                    u32 max);
+
 /*
  *     R-Car SRC
  */
@@ -395,10 +477,11 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
                        struct rsnd_dai *rdai,
                        int use_busif);
 int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod,
-                      struct rsnd_dai *rdai,
-                      int use_busif);
-int rsnd_src_enable_ssi_irq(struct rsnd_mod *ssi_mod,
+                      struct rsnd_dai *rdai);
+int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod,
                            struct rsnd_dai *rdai);
+int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod,
+                            struct rsnd_dai *rdai);
 
 #define rsnd_src_nr(priv) ((priv)->src_nr)
 
@@ -410,6 +493,7 @@ int rsnd_ssi_probe(struct platform_device *pdev,
                   struct rsnd_priv *priv);
 struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
 int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);
+int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
 
 /*
  *     R-Car DVC
index 9183e0145503f65368b4a018eeb93f9a19eb8de6..eede3ac6eed232b12307881574a8d041f1d88352 100644 (file)
@@ -175,30 +175,47 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
 }
 
 int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod,
-                       struct rsnd_dai *rdai,
-                       int use_busif)
+                      struct rsnd_dai *rdai)
 {
        /*
         * DMA settings for SSIU
         */
-       if (use_busif)
-               rsnd_mod_write(ssi_mod, SSI_CTRL, 0);
+       rsnd_mod_write(ssi_mod, SSI_CTRL, 0);
 
        return 0;
 }
 
-int rsnd_src_enable_ssi_irq(struct rsnd_mod *ssi_mod,
+int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod,
                            struct rsnd_dai *rdai)
 {
        struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
 
-       /* enable PIO interrupt if Gen2 */
-       if (rsnd_is_gen2(priv))
+       if (rsnd_is_gen1(priv))
+               return 0;
+
+       /* enable SSI interrupt if Gen2 */
+       if (rsnd_ssi_is_dma_mode(ssi_mod))
+               rsnd_mod_write(ssi_mod, INT_ENABLE, 0x0e000000);
+       else
                rsnd_mod_write(ssi_mod, INT_ENABLE, 0x0f000000);
 
        return 0;
 }
 
+int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod,
+                           struct rsnd_dai *rdai)
+{
+       struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
+
+       if (rsnd_is_gen1(priv))
+               return 0;
+
+       /* disable SSI interrupt if Gen2 */
+       rsnd_mod_write(ssi_mod, INT_ENABLE, 0x00000000);
+
+       return 0;
+}
+
 unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
                                   struct rsnd_dai_stream *io,
                                   struct snd_pcm_runtime *runtime)
@@ -239,12 +256,6 @@ static int rsnd_src_set_convert_rate(struct rsnd_mod *mod,
        rsnd_mod_write(mod, SRC_SWRSR, 0);
        rsnd_mod_write(mod, SRC_SWRSR, 1);
 
-       /*
-        * Initialize the operation of the SRC internal circuits
-        * see rsnd_src_start()
-        */
-       rsnd_mod_write(mod, SRC_SRCIR, 1);
-
        /* Set channel number and output bit length */
        rsnd_mod_write(mod, SRC_ADINR, rsnd_get_adinr(mod));
 
@@ -269,6 +280,12 @@ static int rsnd_src_init(struct rsnd_mod *mod,
 
        clk_prepare_enable(src->clk);
 
+       /*
+        * Initialize the operation of the SRC internal circuits
+        * see rsnd_src_start()
+        */
+       rsnd_mod_write(mod, SRC_SRCIR, 1);
+
        return 0;
 }
 
@@ -282,32 +299,20 @@ static int rsnd_src_quit(struct rsnd_mod *mod,
        return 0;
 }
 
-static int rsnd_src_start(struct rsnd_mod *mod,
-                         struct rsnd_dai *rdai)
+static int rsnd_src_start(struct rsnd_mod *mod)
 {
-       struct rsnd_src *src = rsnd_mod_to_src(mod);
-
        /*
         * Cancel the initialization and operate the SRC function
-        * see rsnd_src_set_convert_rate()
+        * see rsnd_src_init()
         */
        rsnd_mod_write(mod, SRC_SRCIR, 0);
 
-       if (rsnd_src_convert_rate(src))
-               rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1);
-
        return 0;
 }
 
-
-static int rsnd_src_stop(struct rsnd_mod *mod,
-                        struct rsnd_dai *rdai)
+static int rsnd_src_stop(struct rsnd_mod *mod)
 {
-       struct rsnd_src *src = rsnd_mod_to_src(mod);
-
-       if (rsnd_src_convert_rate(src))
-               rsnd_mod_write(mod, SRC_ROUTE_MODE0, 0);
-
+       /* nothing to do */
        return 0;
 }
 
@@ -414,6 +419,7 @@ static int rsnd_src_set_convert_timing_gen1(struct rsnd_mod *mod,
 static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod,
                                          struct rsnd_dai *rdai)
 {
+       struct rsnd_src *src = rsnd_mod_to_src(mod);
        int ret;
 
        ret = rsnd_src_set_convert_rate(mod, rdai);
@@ -427,6 +433,10 @@ static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod,
        rsnd_mod_write(mod, SRC_MNFSR,
                       rsnd_mod_read(mod, SRC_IFSVR) / 100 * 98);
 
+       /* Gen1/Gen2 are not compatible */
+       if (rsnd_src_convert_rate(src))
+               rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1);
+
        /* no SRC_BFSSR settings, since SRC_SRCCR::BUFMD is 0 */
 
        return 0;
@@ -438,7 +448,8 @@ static int rsnd_src_probe_gen1(struct rsnd_mod *mod,
        struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
        struct device *dev = rsnd_priv_to_dev(priv);
 
-       dev_dbg(dev, "%s (Gen1) is probed\n", rsnd_mod_name(mod));
+       dev_dbg(dev, "%s[%d] (Gen1) is probed\n",
+               rsnd_mod_name(mod), rsnd_mod_id(mod));
 
        return 0;
 }
@@ -474,7 +485,7 @@ static int rsnd_src_start_gen1(struct rsnd_mod *mod,
 
        rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), (1 << id));
 
-       return rsnd_src_start(mod, rdai);
+       return rsnd_src_start(mod);
 }
 
 static int rsnd_src_stop_gen1(struct rsnd_mod *mod,
@@ -484,7 +495,7 @@ static int rsnd_src_stop_gen1(struct rsnd_mod *mod,
 
        rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), 0);
 
-       return rsnd_src_stop(mod, rdai);
+       return rsnd_src_stop(mod);
 }
 
 static struct rsnd_mod_ops rsnd_src_gen1_ops = {
@@ -507,16 +518,17 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod,
        struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
        struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
        struct rsnd_src *src = rsnd_mod_to_src(mod);
+       u32 convert_rate = rsnd_src_convert_rate(src);
        uint ratio;
        int ret;
 
        /* 6 - 1/6 are very enough ratio for SRC_BSDSR */
-       if (!rsnd_src_convert_rate(src))
+       if (!convert_rate)
                ratio = 0;
-       else if (rsnd_src_convert_rate(src) > runtime->rate)
-               ratio = 100 * rsnd_src_convert_rate(src) / runtime->rate;
+       else if (convert_rate > runtime->rate)
+               ratio = 100 * convert_rate / runtime->rate;
        else
-               ratio = 100 * runtime->rate / rsnd_src_convert_rate(src);
+               ratio = 100 * runtime->rate / convert_rate;
 
        if (ratio > 600) {
                dev_err(dev, "FSO/FSI ratio error\n");
@@ -529,6 +541,11 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod,
 
        rsnd_mod_write(mod, SRC_SRCCR, 0x00011110);
 
+       if (convert_rate) {
+               /* Gen1/Gen2 are not compatible */
+               rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1);
+       }
+
        switch (rsnd_mod_id(mod)) {
        case 5:
        case 6:
@@ -578,9 +595,11 @@ static int rsnd_src_probe_gen2(struct rsnd_mod *mod,
                            rsnd_info_is_playback(priv, src),
                            src->info->dma_id);
        if (ret < 0)
-               dev_err(dev, "SRC DMA failed\n");
-
-       dev_dbg(dev, "%s (Gen2) is probed\n", rsnd_mod_name(mod));
+               dev_err(dev, "%s[%d] (Gen2) failed\n",
+                       rsnd_mod_name(mod), rsnd_mod_id(mod));
+       else
+               dev_dbg(dev, "%s[%d] (Gen2) is probed\n",
+                       rsnd_mod_name(mod), rsnd_mod_id(mod));
 
        return ret;
 }
@@ -624,7 +643,7 @@ static int rsnd_src_start_gen2(struct rsnd_mod *mod,
 
        rsnd_mod_write(mod, SRC_CTRL, val);
 
-       return rsnd_src_start(mod, rdai);
+       return rsnd_src_start(mod);
 }
 
 static int rsnd_src_stop_gen2(struct rsnd_mod *mod,
@@ -636,7 +655,7 @@ static int rsnd_src_stop_gen2(struct rsnd_mod *mod,
 
        rsnd_dma_stop(rsnd_mod_to_dma(&src->mod));
 
-       return rsnd_src_stop(mod, rdai);
+       return rsnd_src_stop(mod);
 }
 
 static struct rsnd_mod_ops rsnd_src_gen2_ops = {
index 34e84009162bc7c49e4d6b00401e7c4be0c6a907..3844fbef46649db6133b7e3e4266d8f014eb28ae 100644 (file)
@@ -68,7 +68,6 @@ struct rsnd_ssi {
        struct rsnd_dai *rdai;
        u32 cr_own;
        u32 cr_clk;
-       u32 cr_etc;
        int err;
        unsigned int usrcnt;
        unsigned int rate;
@@ -83,7 +82,7 @@ struct rsnd_ssi {
 #define rsnd_ssi_nr(priv) ((priv)->ssi_nr)
 #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
 #define rsnd_dma_to_ssi(dma)  rsnd_mod_to_ssi(rsnd_dma_to_mod(dma))
-#define rsnd_ssi_pio_available(ssi) ((ssi)->info->pio_irq > 0)
+#define rsnd_ssi_pio_available(ssi) ((ssi)->info->irq > 0)
 #define rsnd_ssi_dma_available(ssi) \
        rsnd_dma_available(rsnd_mod_to_dma(&(ssi)->mod))
 #define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent)
@@ -96,6 +95,9 @@ static int rsnd_ssi_use_busif(struct rsnd_mod *mod)
        struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
        int use_busif = 0;
 
+       if (!rsnd_ssi_is_dma_mode(mod))
+               return 0;
+
        if (!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_NO_BUSIF))
                use_busif = 1;
        if (rsnd_io_to_mod_src(io))
@@ -159,7 +161,8 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
                                ssi->cr_clk     = FORCE | SWL_32 |
                                                  SCKD | SWSD | CKDV(j);
 
-                               dev_dbg(dev, "ssi%d outputs %u Hz\n",
+                               dev_dbg(dev, "%s[%d] outputs %u Hz\n",
+                                       rsnd_mod_name(&ssi->mod),
                                        rsnd_mod_id(&ssi->mod), rate);
 
                                return 0;
@@ -184,6 +187,7 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
 {
        struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod);
        struct device *dev = rsnd_priv_to_dev(priv);
+       u32 cr_mode;
        u32 cr;
 
        if (0 == ssi->usrcnt) {
@@ -197,16 +201,29 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
                }
        }
 
+       cr_mode = rsnd_ssi_is_dma_mode(&ssi->mod) ?
+               DMEN :  /* DMA : enable DMA */
+               DIEN;   /* PIO : enable Data interrupt */
+
+
        cr  =   ssi->cr_own     |
                ssi->cr_clk     |
-               ssi->cr_etc     |
-               EN;
+               cr_mode         |
+               UIEN | OIEN | EN;
 
        rsnd_mod_write(&ssi->mod, SSICR, cr);
 
+       /* enable WS continue */
+       if (rsnd_dai_is_clk_master(rdai))
+               rsnd_mod_write(&ssi->mod, SSIWSR, CONT);
+
+       /* clear error status */
+       rsnd_mod_write(&ssi->mod, SSISR, 0);
+
        ssi->usrcnt++;
 
-       dev_dbg(dev, "ssi%d hw started\n", rsnd_mod_id(&ssi->mod));
+       dev_dbg(dev, "%s[%d] hw started\n",
+               rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod));
 }
 
 static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi,
@@ -249,7 +266,8 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi,
                clk_disable_unprepare(ssi->clk);
        }
 
-       dev_dbg(dev, "ssi%d hw stopped\n", rsnd_mod_id(&ssi->mod));
+       dev_dbg(dev, "%s[%d] hw stopped\n",
+               rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod));
 }
 
 /*
@@ -334,25 +352,54 @@ static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status)
        }
 }
 
-/*
- *             SSI PIO
- */
-static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data)
+static int rsnd_ssi_start(struct rsnd_mod *mod,
+                         struct rsnd_dai *rdai)
+{
+       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+
+       rsnd_src_ssiu_start(mod, rdai, rsnd_ssi_use_busif(mod));
+
+       rsnd_ssi_hw_start(ssi, rdai, io);
+
+       rsnd_src_ssi_irq_enable(mod, rdai);
+
+       return 0;
+}
+
+static int rsnd_ssi_stop(struct rsnd_mod *mod,
+                        struct rsnd_dai *rdai)
+{
+       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+
+       rsnd_src_ssi_irq_disable(mod, rdai);
+
+       rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR));
+
+       rsnd_ssi_hw_stop(ssi, rdai);
+
+       rsnd_src_ssiu_stop(mod, rdai);
+
+       return 0;
+}
+
+static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
 {
        struct rsnd_ssi *ssi = data;
+       struct rsnd_dai *rdai = ssi->rdai;
        struct rsnd_mod *mod = &ssi->mod;
        struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
        u32 status = rsnd_mod_read(mod, SSISR);
-       irqreturn_t ret = IRQ_NONE;
 
-       if (io && (status & DIRQ)) {
-               struct rsnd_dai *rdai = ssi->rdai;
+       if (!io)
+               return IRQ_NONE;
+
+       /* PIO only */
+       if (status & DIRQ) {
                struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
                u32 *buf = (u32 *)(runtime->dma_area +
                                   rsnd_dai_pointer_offset(io, 0));
 
-               rsnd_ssi_record_error(ssi, status);
-
                /*
                 * 8/16/32 data can be assesse to TDR/RDR register
                 * directly as 32bit data
@@ -364,73 +411,60 @@ static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data)
                        *buf = rsnd_mod_read(mod, SSIRDR);
 
                rsnd_dai_pointer_update(io, sizeof(*buf));
+       }
+
+       /* PIO / DMA */
+       if (status & (UIRQ | OIRQ)) {
+               struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+               struct device *dev = rsnd_priv_to_dev(priv);
+
+               /*
+                * restart SSI
+                */
+               rsnd_ssi_stop(mod, rdai);
+               rsnd_ssi_start(mod, rdai);
 
-               ret = IRQ_HANDLED;
+               dev_dbg(dev, "%s[%d] restart\n",
+                       rsnd_mod_name(mod), rsnd_mod_id(mod));
        }
 
-       return ret;
+       rsnd_ssi_record_error(ssi, status);
+
+       return IRQ_HANDLED;
 }
 
+/*
+ *             SSI PIO
+ */
 static int rsnd_ssi_pio_probe(struct rsnd_mod *mod,
                              struct rsnd_dai *rdai)
 {
        struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
        struct device *dev = rsnd_priv_to_dev(priv);
        struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-       int irq = ssi->info->pio_irq;
        int ret;
 
-       ret = devm_request_irq(dev, irq,
-                              rsnd_ssi_pio_interrupt,
+       ret = devm_request_irq(dev, ssi->info->irq,
+                              rsnd_ssi_interrupt,
                               IRQF_SHARED,
                               dev_name(dev), ssi);
        if (ret)
-               dev_err(dev, "SSI request interrupt failed\n");
-
-       dev_dbg(dev, "%s (PIO) is probed\n", rsnd_mod_name(mod));
+               dev_err(dev, "%s[%d] (PIO) request interrupt failed\n",
+                       rsnd_mod_name(mod), rsnd_mod_id(mod));
+       else
+               dev_dbg(dev, "%s[%d] (PIO) is probed\n",
+                       rsnd_mod_name(mod), rsnd_mod_id(mod));
 
        return ret;
 }
 
-static int rsnd_ssi_pio_start(struct rsnd_mod *mod,
-                             struct rsnd_dai *rdai)
-{
-       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
-
-       /* enable PIO IRQ */
-       ssi->cr_etc = UIEN | OIEN | DIEN;
-
-       rsnd_src_ssiu_start(mod, rdai, 0);
-
-       rsnd_src_enable_ssi_irq(mod, rdai);
-
-       rsnd_ssi_hw_start(ssi, rdai, io);
-
-       return 0;
-}
-
-static int rsnd_ssi_pio_stop(struct rsnd_mod *mod,
-                            struct rsnd_dai *rdai)
-{
-       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-
-       ssi->cr_etc = 0;
-
-       rsnd_ssi_hw_stop(ssi, rdai);
-
-       rsnd_src_ssiu_stop(mod, rdai, 0);
-
-       return 0;
-}
-
 static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
        .name   = SSI_NAME,
        .probe  = rsnd_ssi_pio_probe,
        .init   = rsnd_ssi_init,
        .quit   = rsnd_ssi_quit,
-       .start  = rsnd_ssi_pio_start,
-       .stop   = rsnd_ssi_pio_stop,
+       .start  = rsnd_ssi_start,
+       .stop   = rsnd_ssi_stop,
 };
 
 static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
@@ -442,15 +476,28 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
        int dma_id = ssi->info->dma_id;
        int ret;
 
+       ret = devm_request_irq(dev, ssi->info->irq,
+                              rsnd_ssi_interrupt,
+                              IRQF_SHARED,
+                              dev_name(dev), ssi);
+       if (ret)
+               goto rsnd_ssi_dma_probe_fail;
+
        ret = rsnd_dma_init(
                priv, rsnd_mod_to_dma(mod),
                rsnd_info_is_playback(priv, ssi),
                dma_id);
+       if (ret)
+               goto rsnd_ssi_dma_probe_fail;
 
-       if (ret < 0)
-               dev_err(dev, "SSI DMA failed\n");
+       dev_dbg(dev, "%s[%d] (DMA) is probed\n",
+               rsnd_mod_name(mod), rsnd_mod_id(mod));
+
+       return ret;
 
-       dev_dbg(dev, "%s (DMA) is probed\n", rsnd_mod_name(mod));
+rsnd_ssi_dma_probe_fail:
+       dev_err(dev, "%s[%d] (DMA) is failed\n",
+               rsnd_mod_name(mod), rsnd_mod_id(mod));
 
        return ret;
 }
@@ -458,30 +505,48 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
 static int rsnd_ssi_dma_remove(struct rsnd_mod *mod,
                               struct rsnd_dai *rdai)
 {
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+       struct device *dev = rsnd_priv_to_dev(priv);
+       int irq = ssi->info->irq;
+
        rsnd_dma_quit(rsnd_mod_to_priv(mod), rsnd_mod_to_dma(mod));
 
+       /* PIO will request IRQ again */
+       devm_free_irq(dev, irq, ssi);
+
        return 0;
 }
 
-static int rsnd_ssi_dma_start(struct rsnd_mod *mod,
-                             struct rsnd_dai *rdai)
+static int rsnd_ssi_fallback(struct rsnd_mod *mod,
+                            struct rsnd_dai *rdai)
 {
-       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-       struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod);
-       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       struct device *dev = rsnd_priv_to_dev(priv);
 
-       /* enable DMA transfer */
-       ssi->cr_etc = DMEN;
+       /*
+        * fallback to PIO
+        *
+        * SSI .probe might be called again.
+        * see
+        *      rsnd_rdai_continuance_probe()
+        */
+       mod->ops = &rsnd_ssi_pio_ops;
 
-       rsnd_src_ssiu_start(mod, rdai, rsnd_ssi_use_busif(mod));
+       dev_info(dev, "%s[%d] fallback to PIO mode\n",
+                rsnd_mod_name(mod), rsnd_mod_id(mod));
 
-       rsnd_dma_start(dma);
+       return 0;
+}
 
-       rsnd_ssi_hw_start(ssi, ssi->rdai, io);
+static int rsnd_ssi_dma_start(struct rsnd_mod *mod,
+                             struct rsnd_dai *rdai)
+{
+       struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
 
-       /* enable WS continue */
-       if (rsnd_dai_is_clk_master(rdai))
-               rsnd_mod_write(&ssi->mod, SSIWSR, CONT);
+       rsnd_ssi_start(mod, rdai);
+
+       rsnd_dma_start(dma);
 
        return 0;
 }
@@ -489,18 +554,11 @@ static int rsnd_ssi_dma_start(struct rsnd_mod *mod,
 static int rsnd_ssi_dma_stop(struct rsnd_mod *mod,
                             struct rsnd_dai *rdai)
 {
-       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-       struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod);
-
-       ssi->cr_etc = 0;
-
-       rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR));
-
-       rsnd_ssi_hw_stop(ssi, rdai);
+       struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
 
        rsnd_dma_stop(dma);
 
-       rsnd_src_ssiu_stop(mod, rdai, 1);
+       rsnd_ssi_stop(mod, rdai);
 
        return 0;
 }
@@ -519,8 +577,15 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
        .quit   = rsnd_ssi_quit,
        .start  = rsnd_ssi_dma_start,
        .stop   = rsnd_ssi_dma_stop,
+       .fallback = rsnd_ssi_fallback,
 };
 
+int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod)
+{
+       return mod->ops == &rsnd_ssi_dma_ops;
+}
+
+
 /*
  *             Non SSI
  */
@@ -614,7 +679,7 @@ static void rsnd_of_parse_ssi(struct platform_device *pdev,
                /*
                 * irq
                 */
-               ssi_info->pio_irq = irq_of_parse_and_map(np, 0);
+               ssi_info->irq = irq_of_parse_and_map(np, 0);
 
                /*
                 * DMA