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-", 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 #include #include +#include #include #include #include @@ -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); + snd_soc_dapm_force_enable_pin(&rt286->codec->dapm, + "LDO1"); + snd_soc_dapm_sync(&rt286->codec->dapm); + regmap_write(rt286->regmap, RT286_SET_MIC1, 0x24); + msleep(50); + + regmap_update_bits(rt286->regmap, + RT286_CBJ_CTRL1, 0xfcc0, 0xd400); + msleep(300); regmap_read(rt286->regmap, RT286_CBJ_CTRL2, &val); - msleep(200); - i = 40; - while (((val & 0x0800) == 0) && (i > 0)) { + if (0x0070 == (val & 0x0070)) { + *mic = true; + } else { + regmap_update_bits(rt286->regmap, + RT286_CBJ_CTRL1, 0xfcc0, 0xe400); + msleep(300); regmap_read(rt286->regmap, RT286_CBJ_CTRL2, &val); - i--; - msleep(20); + if (0x0070 == (val & 0x0070)) + *mic = true; + else + *mic = false; } - - if (0x0400 == (val & 0x0700)) { - *mic = false; - - 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))) { - *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_MISC_CTRL1, - 0x0060, 0x0000); + RT286_DC_GAIN, 0x200, 0x0); + } 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); - *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) @@ -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; } 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]; } - rsnd_mod_write(mod, DVC_VOL0R, vol[0]); - rsnd_mod_write(mod, DVC_VOL1R, vol[1]); + /* Enable Digital Volume */ + dvucr |= 0x100; + rsnd_mod_write(mod, DVC_VOL0R, val[0]); + rsnd_mod_write(mod, DVC_VOL1R, val[1]); - rsnd_mod_write(mod, DVC_ZCMCR, mute); + /* Enable Mute */ + if (mute) { + dvucr |= 0x1; + rsnd_mod_write(mod, DVC_ZCMCR, mute); + } + + rsnd_mod_write(mod, DVC_DVUCR, dvucr); + + /* 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); @@ -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 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)); - - ret = IRQ_HANDLED; } - return ret; + /* 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); + + dev_dbg(dev, "%s[%d] restart\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); + } + + 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)); - dev_dbg(dev, "%s (DMA) is probed\n", rsnd_mod_name(mod)); + return ret; + +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,49 +505,60 @@ 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_fallback(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); + + /* + * fallback to PIO + * + * SSI .probe might be called again. + * see + * rsnd_rdai_continuance_probe() + */ + mod->ops = &rsnd_ssi_pio_ops; + + dev_info(dev, "%s[%d] fallback to PIO mode\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); + return 0; } static int rsnd_ssi_dma_start(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_dma *dma = rsnd_mod_to_dma(mod); - /* enable DMA transfer */ - ssi->cr_etc = DMEN; - - rsnd_src_ssiu_start(mod, rdai, rsnd_ssi_use_busif(mod)); + rsnd_ssi_start(mod, rdai); rsnd_dma_start(dma); - rsnd_ssi_hw_start(ssi, ssi->rdai, io); - - /* enable WS continue */ - if (rsnd_dai_is_clk_master(rdai)) - rsnd_mod_write(&ssi->mod, SSIWSR, CONT); - return 0; } 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