summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/sound/renesas,rsnd.txt10
-rw-r--r--Documentation/devicetree/bindings/sound/rt5631.txt48
-rw-r--r--include/sound/rcar_snd.h8
-rw-r--r--sound/soc/codecs/Kconfig3
-rw-r--r--sound/soc/codecs/rt286.c231
-rw-r--r--sound/soc/codecs/rt5631.c38
-rw-r--r--sound/soc/rockchip/Kconfig9
-rw-r--r--sound/soc/sh/rcar/adg.c2
-rw-r--r--sound/soc/sh/rcar/core.c236
-rw-r--r--sound/soc/sh/rcar/dvc.c215
-rw-r--r--sound/soc/sh/rcar/gen.c30
-rw-r--r--sound/soc/sh/rcar/rsnd.h92
-rw-r--r--sound/soc/sh/rcar/src.c101
-rw-r--r--sound/soc/sh/rcar/ssi.c233
14 files changed, 915 insertions, 341 deletions
diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt
index aa697abf337e..2dd690bc19cc 100644
--- a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt
+++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt
@@ -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
index 000000000000..92b986ca337b
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/rt5631.txt
@@ -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>;
+};
diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h
index d76412b84b48..83284cae464c 100644
--- a/include/sound/rcar_snd.h
+++ b/include/sound/rcar_snd.h
@@ -36,14 +36,14 @@
#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;
};
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 1362edd380e5..3fbf59484a42 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -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
diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c
index 4aa555cbcca8..2cd4fe463102 100644
--- a/sound/soc/codecs/rt286.c
+++ b/sound/soc/codecs/rt286.c
@@ -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>
@@ -36,11 +37,13 @@
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);
diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c
index 1ba27db660a6..6d7b7ca7d530 100644
--- a/sound/soc/codecs/rt5631.c
+++ b/sound/soc/codecs/rt5631.c
@@ -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,
diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig
index 78fc159559b0..e18182699d83 100644
--- a/sound/soc/rockchip/Kconfig
+++ b/sound/soc/rockchip/Kconfig
@@ -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.
diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c
index fc41a0e8b09f..14d1a7193469 100644
--- a/sound/soc/sh/rcar/adg.c
+++ b/sound/soc/sh/rcar/adg.c
@@ -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);
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index 70042197f9e2..75308bbc2ce8 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -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)
@@ -934,6 +969,150 @@ static struct snd_pcm_ops rsnd_pcm_ops = {
};
/*
+ * 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;
}
diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c
index 3f443930c2b1..5380a4827ba7 100644
--- a/sound/soc/sh/rcar/dvc.c
+++ b/sound/soc/sh/rcar/dvc.c
@@ -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;
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c
index f95e7ab135e8..87a6f2d62775 100644
--- a/sound/soc/sh/rcar/gen.c
+++ b/sound/soc/sh/rcar/gen.c
@@ -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),
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
index d119adf97c9c..5826c8abf794 100644
--- a/sound/soc/sh/rcar/rsnd.h
+++ b/sound/soc/sh/rcar/rsnd.h
@@ -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);
@@ -382,6 +419,51 @@ struct rsnd_priv {
})
/*
+ * 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
*/
int rsnd_src_probe(struct platform_device *pdev,
@@ -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
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c
index 9183e0145503..eede3ac6eed2 100644
--- a/sound/soc/sh/rcar/src.c
+++ b/sound/soc/sh/rcar/src.c
@@ -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 = {
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
index 34e84009162b..3844fbef4664 100644
--- a/sound/soc/sh/rcar/ssi.c
+++ b/sound/soc/sh/rcar/ssi.c
@@ -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