From 3cc7780b6fc04318ab08d84f739503989200cf55 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Mon, 19 Oct 2015 17:42:23 -0700 Subject: ASoC: fsl_sai: fix Rx synchrounous mode When using the Rx clock for both, transmitter and receiver, the transmitter needs to be set to synchronous with receiver. This reverts 855675f6e6a6 ("ASoC: fsl_sai: Set SYNC bit of TCR2 to Asynchronous Mode"), which, judiging from the commit log, seems to mixed up between the two synchronous modes: The boolean sai->synchronous[TX] is indicating wheather the SAI should work in Rx synchronous mode (sync Tx with Rx), hence if the value is true, the SYNC field of TCR2 needs to be set to 0x1 ("Synchronous with receiver"). Signed-off-by: Stefan Agner Acked-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index a18fd92c4a85..1f0e5527a2fe 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -454,7 +454,8 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, * Rx sync with Tx clocks: Clear SYNC for Tx, set it for Rx. * Tx sync with Rx clocks: Clear SYNC for Rx, set it for Tx. */ - regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_SYNC, 0); + regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_SYNC, + sai->synchronous[TX] ? FSL_SAI_CR2_SYNC : 0); regmap_update_bits(sai->regmap, FSL_SAI_RCR2, FSL_SAI_CR2_SYNC, sai->synchronous[RX] ? FSL_SAI_CR2_SYNC : 0); -- cgit v1.2.3 From 0b170f7acd2ab1ca0771b933493b9241706117b4 Mon Sep 17 00:00:00 2001 From: Jiada Wang Date: Fri, 23 Oct 2015 14:18:48 +0900 Subject: ASoC: wm8962: set ALC2 as non-volatile register Previously ALC2 register is set as a volatile register, declare it as one of ALC Coefficients register together with other non-volatile registers will cause issue, in case wm8962 has enter suspend mode, and cache_only flag is set, any attempt to read from ALC2 will fail. Because the 5 status bits in ALC2 aren't used anywhere nor are useful to end user, so this patch removes ALC2 register from volatile register list to make ALC2 be possible to be accessed when cache_only flag is set. Signed-off-by: Jiada Wang Acked-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm8962.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index b4eb975da981..2976200abb3a 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -131,7 +131,7 @@ static const struct reg_default wm8962_reg[] = { { 15, 0x6243 }, /* R15 - Software Reset */ { 17, 0x007B }, /* R17 - ALC1 */ - + { 18, 0x0000 }, /* R18 - ALC2 */ { 19, 0x1C32 }, /* R19 - ALC3 */ { 20, 0x3200 }, /* R20 - Noise Gate */ { 21, 0x00C0 }, /* R21 - Left ADC volume */ @@ -794,7 +794,6 @@ static bool wm8962_volatile_register(struct device *dev, unsigned int reg) case WM8962_CLOCKING1: case WM8962_CLOCKING2: case WM8962_SOFTWARE_RESET: - case WM8962_ALC2: case WM8962_THERMAL_SHUTDOWN_STATUS: case WM8962_ADDITIONAL_CONTROL_4: case WM8962_DC_SERVO_6: -- cgit v1.2.3 From 341604ad839d10314af51669fd454dc0aa2ef288 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 3 Nov 2015 14:24:12 +0000 Subject: ASoC: arizona: fix range of OPCLK_REF The code was able to generate illegal OPCLK_REF values because the reference frequency tables listed all values of SYSCLK instead of valid values for OPCLK_REF clock. The maximum OPCLK_REF clock is 49.152MHz or 45.1584MHz. Signed-off-by: Richard Fitzgerald Signed-off-by: Mark Brown --- sound/soc/codecs/arizona.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 8a2221ab3d10..586789597ecd 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -979,24 +979,18 @@ void arizona_init_dvfs(struct arizona_priv *priv) } EXPORT_SYMBOL_GPL(arizona_init_dvfs); -static unsigned int arizona_sysclk_48k_rates[] = { +static unsigned int arizona_opclk_ref_48k_rates[] = { 6144000, 12288000, 24576000, 49152000, - 73728000, - 98304000, - 147456000, }; -static unsigned int arizona_sysclk_44k1_rates[] = { +static unsigned int arizona_opclk_ref_44k1_rates[] = { 5644800, 11289600, 22579200, 45158400, - 67737600, - 90316800, - 135475200, }; static int arizona_set_opclk(struct snd_soc_codec *codec, unsigned int clk, @@ -1021,11 +1015,11 @@ static int arizona_set_opclk(struct snd_soc_codec *codec, unsigned int clk, } if (refclk % 8000) - rates = arizona_sysclk_44k1_rates; + rates = arizona_opclk_ref_44k1_rates; else - rates = arizona_sysclk_48k_rates; + rates = arizona_opclk_ref_48k_rates; - for (ref = 0; ref < ARRAY_SIZE(arizona_sysclk_48k_rates) && + for (ref = 0; ref < ARRAY_SIZE(arizona_opclk_ref_48k_rates) && rates[ref] <= refclk; ref++) { div = 1; while (rates[ref] / div >= freq && div < 32) { -- cgit v1.2.3 From 41a59cae585678136c28cdcbba9cb2faf27685f5 Mon Sep 17 00:00:00 2001 From: JongHo Kim Date: Tue, 3 Nov 2015 11:06:32 +0900 Subject: ASoC: wm8960: Fix the Input PGA Mute switch Change the xinvert value from 0 to 1 on the "Capture Switch" control WM8960 datasheet is shown as follows: Bit 7 at 00h and 01h register address 1 : Enable Mute, 0 : Disable Mute Signed-off-by: JongHo Kim Acked-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm8960.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index e3b7d0c57411..bbe24275f8c9 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -223,7 +223,7 @@ SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL, SOC_DOUBLE_R("Capture Volume ZC Switch", WM8960_LINVOL, WM8960_RINVOL, 6, 1, 0), SOC_DOUBLE_R("Capture Switch", WM8960_LINVOL, WM8960_RINVOL, - 7, 1, 0), + 7, 1, 1), SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT3 Volume", WM8960_INBMIX1, 4, 7, 0, boost_tlv), -- cgit v1.2.3 From 7099ee85e6af56828c46255f43adb15ed47e67df Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Thu, 5 Nov 2015 19:55:51 +0800 Subject: ASoC: rt5645: Power up the RC clock to make sure the speaker volume adjust properly Signed-off-by: Oder Chiou Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 28132375e427..672fafd8314a 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -245,7 +245,7 @@ struct rt5645_priv { struct snd_soc_jack *hp_jack; struct snd_soc_jack *mic_jack; struct snd_soc_jack *btn_jack; - struct delayed_work jack_detect_work; + struct delayed_work jack_detect_work, rcclock_work; struct regulator_bulk_data supplies[ARRAY_SIZE(rt5645_supply_names)]; struct rt5645_eq_param_s *eq_param; @@ -565,12 +565,33 @@ static int rt5645_hweq_put(struct snd_kcontrol *kcontrol, .put = rt5645_hweq_put \ } +static int rt5645_spk_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); + int ret; + + cancel_delayed_work_sync(&rt5645->rcclock_work); + + regmap_update_bits(rt5645->regmap, RT5645_MICBIAS, + RT5645_PWR_CLK25M_MASK, RT5645_PWR_CLK25M_PU); + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + + queue_delayed_work(system_power_efficient_wq, &rt5645->rcclock_work, + msecs_to_jiffies(200)); + + return ret; +} + static const struct snd_kcontrol_new rt5645_snd_controls[] = { /* Speaker Output Volume */ SOC_DOUBLE("Speaker Channel Switch", RT5645_SPK_VOL, RT5645_VOL_L_SFT, RT5645_VOL_R_SFT, 1, 1), - SOC_DOUBLE_TLV("Speaker Playback Volume", RT5645_SPK_VOL, - RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, out_vol_tlv), + SOC_DOUBLE_EXT_TLV("Speaker Playback Volume", RT5645_SPK_VOL, + RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, snd_soc_get_volsw, + rt5645_spk_put_volsw, out_vol_tlv), /* ClassD modulator Speaker Gain Ratio */ SOC_SINGLE_TLV("Speaker ClassD Playback Volume", RT5645_SPO_CLSD_RATIO, @@ -3122,6 +3143,15 @@ static void rt5645_jack_detect_work(struct work_struct *work) SND_JACK_BTN_2 | SND_JACK_BTN_3); } +static void rt5645_rcclock_work(struct work_struct *work) +{ + struct rt5645_priv *rt5645 = + container_of(work, struct rt5645_priv, rcclock_work.work); + + regmap_update_bits(rt5645->regmap, RT5645_MICBIAS, + RT5645_PWR_CLK25M_MASK, RT5645_PWR_CLK25M_PD); +} + static irqreturn_t rt5645_irq(int irq, void *data) { struct rt5645_priv *rt5645 = data; @@ -3587,6 +3617,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, } INIT_DELAYED_WORK(&rt5645->jack_detect_work, rt5645_jack_detect_work); + INIT_DELAYED_WORK(&rt5645->rcclock_work, rt5645_rcclock_work); if (rt5645->i2c->irq) { ret = request_threaded_irq(rt5645->i2c->irq, NULL, rt5645_irq, @@ -3621,6 +3652,7 @@ static int rt5645_i2c_remove(struct i2c_client *i2c) free_irq(i2c->irq, rt5645); cancel_delayed_work_sync(&rt5645->jack_detect_work); + cancel_delayed_work_sync(&rt5645->rcclock_work); snd_soc_unregister_codec(&i2c->dev); regulator_bulk_disable(ARRAY_SIZE(rt5645->supplies), rt5645->supplies); -- cgit v1.2.3 From 474d147ad1ecc98e50a65c9f350fadfcc37a8bb4 Mon Sep 17 00:00:00 2001 From: Adam Sampson Date: Tue, 27 Oct 2015 21:00:45 +0000 Subject: ASoC: sun4i-codec: use consistent names for PA controls The power amplifier for the headphone output is called "the PA" and "the headphone amplifier" in Allwinner's documentation for the A10 and A20. sun4i-codec calls it "PA" in some places and "Pre-Amplifier" (which isn't really accurate) in others, leading to user-visible controls with different names referring to the same device. When this driver implements audio input, it'll also need to expose controls for the line and mic input preamps, so just referring to "the Pre-Amplifier" will be ambiguous. Change it to use "Power Amplifier" consistently for the power amplifier's controls. Signed-off-by: Adam Sampson Acked-by: Maxime Ripard Signed-off-by: Mark Brown --- sound/soc/sunxi/sun4i-codec.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index bcbf4da168b6..1bb896d78d09 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -2,6 +2,7 @@ * Copyright 2014 Emilio López * Copyright 2014 Jon Smirl * Copyright 2015 Maxime Ripard + * Copyright 2015 Adam Sampson * * Based on the Allwinner SDK driver, released under the GPL. * @@ -404,7 +405,7 @@ static const struct snd_kcontrol_new sun4i_codec_pa_mute = static DECLARE_TLV_DB_SCALE(sun4i_codec_pa_volume_scale, -6300, 100, 1); static const struct snd_kcontrol_new sun4i_codec_widgets[] = { - SOC_SINGLE_TLV("PA Volume", SUN4I_CODEC_DAC_ACTL, + SOC_SINGLE_TLV("Power Amplifier Volume", SUN4I_CODEC_DAC_ACTL, SUN4I_CODEC_DAC_ACTL_PA_VOL, 0x3F, 0, sun4i_codec_pa_volume_scale), }; @@ -452,12 +453,12 @@ static const struct snd_soc_dapm_widget sun4i_codec_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("Mixer Enable", SUN4I_CODEC_DAC_ACTL, SUN4I_CODEC_DAC_ACTL_MIXEN, 0, NULL, 0), - /* Pre-Amplifier */ - SND_SOC_DAPM_MIXER("Pre-Amplifier", SUN4I_CODEC_ADC_ACTL, + /* Power Amplifier */ + SND_SOC_DAPM_MIXER("Power Amplifier", SUN4I_CODEC_ADC_ACTL, SUN4I_CODEC_ADC_ACTL_PA_EN, 0, sun4i_codec_pa_mixer_controls, ARRAY_SIZE(sun4i_codec_pa_mixer_controls)), - SND_SOC_DAPM_SWITCH("Pre-Amplifier Mute", SND_SOC_NOPM, 0, 0, + SND_SOC_DAPM_SWITCH("Power Amplifier Mute", SND_SOC_NOPM, 0, 0, &sun4i_codec_pa_mute), SND_SOC_DAPM_OUTPUT("HP Right"), @@ -480,16 +481,16 @@ static const struct snd_soc_dapm_route sun4i_codec_dapm_routes[] = { { "Left Mixer", NULL, "Mixer Enable" }, { "Left Mixer", "Left DAC Playback Switch", "Left DAC" }, - /* Pre-Amplifier Mixer Routes */ - { "Pre-Amplifier", "Mixer Playback Switch", "Left Mixer" }, - { "Pre-Amplifier", "Mixer Playback Switch", "Right Mixer" }, - { "Pre-Amplifier", "DAC Playback Switch", "Left DAC" }, - { "Pre-Amplifier", "DAC Playback Switch", "Right DAC" }, + /* Power Amplifier Routes */ + { "Power Amplifier", "Mixer Playback Switch", "Left Mixer" }, + { "Power Amplifier", "Mixer Playback Switch", "Right Mixer" }, + { "Power Amplifier", "DAC Playback Switch", "Left DAC" }, + { "Power Amplifier", "DAC Playback Switch", "Right DAC" }, - /* PA -> HP path */ - { "Pre-Amplifier Mute", "Switch", "Pre-Amplifier" }, - { "HP Right", NULL, "Pre-Amplifier Mute" }, - { "HP Left", NULL, "Pre-Amplifier Mute" }, + /* Headphone Output Routes */ + { "Power Amplifier Mute", "Switch", "Power Amplifier" }, + { "HP Right", NULL, "Power Amplifier Mute" }, + { "HP Left", NULL, "Power Amplifier Mute" }, }; static struct snd_soc_codec_driver sun4i_codec_codec = { -- cgit v1.2.3 From 021c5d9469960b8c68aa1d1825f7bfd8d61e157d Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 5 Nov 2015 23:53:03 +0000 Subject: ASoC: rsnd: fixup SCU_SYS_INT_EN1 address cfcefe0126 ("ASoC: rsnd: add recovery support for under/over flow error on SRC") added SCU_SYS_INT_EN1 address, but it should be 0x1d4, not 0x1c4. This patch fixup it. Fixes: cfcefe0126 ("ASoC: rsnd: add recovery support for under/over flow error on SRC") Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown Cc: stable@vger.kernel.org --- sound/soc/sh/rcar/gen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index f04d17bc6e3d..916b38d54fda 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -231,7 +231,7 @@ static int rsnd_gen2_probe(struct platform_device *pdev, RSND_GEN_S_REG(SCU_SYS_STATUS0, 0x1c8), RSND_GEN_S_REG(SCU_SYS_INT_EN0, 0x1cc), RSND_GEN_S_REG(SCU_SYS_STATUS1, 0x1d0), - RSND_GEN_S_REG(SCU_SYS_INT_EN1, 0x1c4), + RSND_GEN_S_REG(SCU_SYS_INT_EN1, 0x1d4), RSND_GEN_M_REG(SRC_SWRSR, 0x200, 0x40), RSND_GEN_M_REG(SRC_SRCIR, 0x204, 0x40), RSND_GEN_M_REG(SRC_ADINR, 0x214, 0x40), -- cgit v1.2.3 From 1bdd593247ee5a74eb58828a4cf18bdc8a5f1baa Mon Sep 17 00:00:00 2001 From: Andreas Dannenberg Date: Mon, 9 Nov 2015 12:19:19 -0600 Subject: ASoC: davinci-mcasp: Fix TDM slot rx/tx mask associations Fixes the associations between the tx_mask and rx_mask and the associated playback / capture streams during setting of the TDM slot. With this patch in place it is now possible for example to only populate tx_mask (leaving rx_mask as 0) for output-only codecs to control the TDM slot(s) the McASP serial port uses for transmit. Before that, this scenario would incorrectly rely on the rx_mask for this. Signed-off-by: Andreas Dannenberg Reviewed-by: Jyri Sarha Acked-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/davinci/davinci-mcasp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 4495a40a9468..caa0bebcd7f4 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -681,8 +681,8 @@ static int davinci_mcasp_set_tdm_slot(struct snd_soc_dai *dai, } mcasp->tdm_slots = slots; - mcasp->tdm_mask[SNDRV_PCM_STREAM_PLAYBACK] = rx_mask; - mcasp->tdm_mask[SNDRV_PCM_STREAM_CAPTURE] = tx_mask; + mcasp->tdm_mask[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask; + mcasp->tdm_mask[SNDRV_PCM_STREAM_CAPTURE] = rx_mask; mcasp->slot_width = slot_width; return davinci_mcasp_set_ch_constraints(mcasp); -- cgit v1.2.3 From fd589a1be20fdd76ef97700dd0185e7a060546dc Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Tue, 10 Nov 2015 18:12:42 +0200 Subject: ASoC: dapm: Reset dapm wcache after freeing damp widgets If there is anything in damp->path_source_cache or damp->path_sink_cache, it can not be valid after the widgets have been freed. Without this patch a repeated remove and load of a machine driver may cause NULL pointer reference in dapm_wcache_lookup() when a freed widget, not belonging to any list, is haunting in the wcache. Signed-off-by: Jyri Sarha Reported-by: Felipe Balbi Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 1 + sound/soc/soc-dapm.c | 7 +++++++ sound/soc/soc-topology.c | 1 + 3 files changed, 9 insertions(+) (limited to 'sound') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 7855cfe46b69..95a937eafb79 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -398,6 +398,7 @@ int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm, int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_route *route, int num); void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w); +void snd_soc_dapm_reset_cache(struct snd_soc_dapm_context *dapm); /* dapm events */ void snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 016eba10b1ec..7d009428934a 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2293,6 +2293,12 @@ void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w) kfree(w); } +void snd_soc_dapm_reset_cache(struct snd_soc_dapm_context *dapm) +{ + dapm->path_sink_cache.widget = NULL; + dapm->path_source_cache.widget = NULL; +} + /* free all dapm widgets and resources */ static void dapm_free_widgets(struct snd_soc_dapm_context *dapm) { @@ -2303,6 +2309,7 @@ static void dapm_free_widgets(struct snd_soc_dapm_context *dapm) continue; snd_soc_dapm_free_widget(w); } + snd_soc_dapm_reset_cache(dapm); } static struct snd_soc_dapm_widget *dapm_find_widget( diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 8d7ec80af51b..cce63fe65dd9 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1805,6 +1805,7 @@ void snd_soc_tplg_widget_remove_all(struct snd_soc_dapm_context *dapm, snd_soc_tplg_widget_remove(w); snd_soc_dapm_free_widget(w); } + snd_soc_dapm_reset_cache(dapm); } EXPORT_SYMBOL_GPL(snd_soc_tplg_widget_remove_all); -- cgit v1.2.3 From 2f64b6ed44c26eeb3d1bf5428936629cf552eda7 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 10 Nov 2015 14:40:55 +0800 Subject: ASoC: rl6231: avoid using divisible by 3 for DMIC clk Few codecs will meet no DMIC clock output issue when select a divided number which is divisible by 3. To prevent this issue, the patch ignore the numbers when calculating the DMIC clock divider. Signed-off-by: Bard Liao Signed-off-by: Mark Brown --- sound/soc/codecs/rl6231.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/rl6231.c b/sound/soc/codecs/rl6231.c index aca479fa7670..18b42925314e 100644 --- a/sound/soc/codecs/rl6231.c +++ b/sound/soc/codecs/rl6231.c @@ -80,6 +80,8 @@ int rl6231_calc_dmic_clk(int rate) } for (i = 0; i < ARRAY_SIZE(div); i++) { + if ((div[i] % 3) == 0) + continue; /* find divider that gives DMIC frequency below 3MHz */ if (3000000 * div[i] >= rate) return i; -- cgit v1.2.3 From c22d7666c5c4473cfffe8c40fcf86bd6e16317df Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Mon, 9 Nov 2015 18:01:04 +0800 Subject: ASoC: rt5677: Avoid the pop sound that comes from the filter power The patch changes the type of DACs mixer to AUTODISABLE and add the delay time after power up to avoid the pop sound that comes from the filter power. Signed-off-by: Oder Chiou Signed-off-by: Mark Brown --- sound/soc/codecs/rt5677.c | 100 ++++++++++++++++++++++++++++------------------ 1 file changed, 61 insertions(+), 39 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index b4cd7e3bf5f8..69d987a9935c 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -1386,90 +1386,90 @@ static const struct snd_kcontrol_new rt5677_dac_r_mix[] = { }; static const struct snd_kcontrol_new rt5677_sto1_dac_l_mix[] = { - SOC_DAPM_SINGLE("ST L Switch", RT5677_STO1_DAC_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("ST L Switch", RT5677_STO1_DAC_MIXER, RT5677_M_ST_DAC1_L_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC1 L Switch", RT5677_STO1_DAC_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC1 L Switch", RT5677_STO1_DAC_MIXER, RT5677_M_DAC1_L_STO_L_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC2 L Switch", RT5677_STO1_DAC_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC2 L Switch", RT5677_STO1_DAC_MIXER, RT5677_M_DAC2_L_STO_L_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC1 R Switch", RT5677_STO1_DAC_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC1 R Switch", RT5677_STO1_DAC_MIXER, RT5677_M_DAC1_R_STO_L_SFT, 1, 1), }; static const struct snd_kcontrol_new rt5677_sto1_dac_r_mix[] = { - SOC_DAPM_SINGLE("ST R Switch", RT5677_STO1_DAC_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("ST R Switch", RT5677_STO1_DAC_MIXER, RT5677_M_ST_DAC1_R_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC1 R Switch", RT5677_STO1_DAC_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC1 R Switch", RT5677_STO1_DAC_MIXER, RT5677_M_DAC1_R_STO_R_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC2 R Switch", RT5677_STO1_DAC_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC2 R Switch", RT5677_STO1_DAC_MIXER, RT5677_M_DAC2_R_STO_R_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC1 L Switch", RT5677_STO1_DAC_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC1 L Switch", RT5677_STO1_DAC_MIXER, RT5677_M_DAC1_L_STO_R_SFT, 1, 1), }; static const struct snd_kcontrol_new rt5677_mono_dac_l_mix[] = { - SOC_DAPM_SINGLE("ST L Switch", RT5677_MONO_DAC_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("ST L Switch", RT5677_MONO_DAC_MIXER, RT5677_M_ST_DAC2_L_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC1 L Switch", RT5677_MONO_DAC_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC1 L Switch", RT5677_MONO_DAC_MIXER, RT5677_M_DAC1_L_MONO_L_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC2 L Switch", RT5677_MONO_DAC_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC2 L Switch", RT5677_MONO_DAC_MIXER, RT5677_M_DAC2_L_MONO_L_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC2 R Switch", RT5677_MONO_DAC_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC2 R Switch", RT5677_MONO_DAC_MIXER, RT5677_M_DAC2_R_MONO_L_SFT, 1, 1), }; static const struct snd_kcontrol_new rt5677_mono_dac_r_mix[] = { - SOC_DAPM_SINGLE("ST R Switch", RT5677_MONO_DAC_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("ST R Switch", RT5677_MONO_DAC_MIXER, RT5677_M_ST_DAC2_R_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC1 R Switch", RT5677_MONO_DAC_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC1 R Switch", RT5677_MONO_DAC_MIXER, RT5677_M_DAC1_R_MONO_R_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC2 R Switch", RT5677_MONO_DAC_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC2 R Switch", RT5677_MONO_DAC_MIXER, RT5677_M_DAC2_R_MONO_R_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC2 L Switch", RT5677_MONO_DAC_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC2 L Switch", RT5677_MONO_DAC_MIXER, RT5677_M_DAC2_L_MONO_R_SFT, 1, 1), }; static const struct snd_kcontrol_new rt5677_dd1_l_mix[] = { - SOC_DAPM_SINGLE("Sto DAC Mix L Switch", RT5677_DD1_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("Sto DAC Mix L Switch", RT5677_DD1_MIXER, RT5677_M_STO_L_DD1_L_SFT, 1, 1), - SOC_DAPM_SINGLE("Mono DAC Mix L Switch", RT5677_DD1_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("Mono DAC Mix L Switch", RT5677_DD1_MIXER, RT5677_M_MONO_L_DD1_L_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC3 L Switch", RT5677_DD1_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC3 L Switch", RT5677_DD1_MIXER, RT5677_M_DAC3_L_DD1_L_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC3 R Switch", RT5677_DD1_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC3 R Switch", RT5677_DD1_MIXER, RT5677_M_DAC3_R_DD1_L_SFT, 1, 1), }; static const struct snd_kcontrol_new rt5677_dd1_r_mix[] = { - SOC_DAPM_SINGLE("Sto DAC Mix R Switch", RT5677_DD1_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("Sto DAC Mix R Switch", RT5677_DD1_MIXER, RT5677_M_STO_R_DD1_R_SFT, 1, 1), - SOC_DAPM_SINGLE("Mono DAC Mix R Switch", RT5677_DD1_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("Mono DAC Mix R Switch", RT5677_DD1_MIXER, RT5677_M_MONO_R_DD1_R_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC3 R Switch", RT5677_DD1_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC3 R Switch", RT5677_DD1_MIXER, RT5677_M_DAC3_R_DD1_R_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC3 L Switch", RT5677_DD1_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC3 L Switch", RT5677_DD1_MIXER, RT5677_M_DAC3_L_DD1_R_SFT, 1, 1), }; static const struct snd_kcontrol_new rt5677_dd2_l_mix[] = { - SOC_DAPM_SINGLE("Sto DAC Mix L Switch", RT5677_DD2_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("Sto DAC Mix L Switch", RT5677_DD2_MIXER, RT5677_M_STO_L_DD2_L_SFT, 1, 1), - SOC_DAPM_SINGLE("Mono DAC Mix L Switch", RT5677_DD2_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("Mono DAC Mix L Switch", RT5677_DD2_MIXER, RT5677_M_MONO_L_DD2_L_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC4 L Switch", RT5677_DD2_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC4 L Switch", RT5677_DD2_MIXER, RT5677_M_DAC4_L_DD2_L_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC4 R Switch", RT5677_DD2_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC4 R Switch", RT5677_DD2_MIXER, RT5677_M_DAC4_R_DD2_L_SFT, 1, 1), }; static const struct snd_kcontrol_new rt5677_dd2_r_mix[] = { - SOC_DAPM_SINGLE("Sto DAC Mix R Switch", RT5677_DD2_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("Sto DAC Mix R Switch", RT5677_DD2_MIXER, RT5677_M_STO_R_DD2_R_SFT, 1, 1), - SOC_DAPM_SINGLE("Mono DAC Mix R Switch", RT5677_DD2_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("Mono DAC Mix R Switch", RT5677_DD2_MIXER, RT5677_M_MONO_R_DD2_R_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC4 R Switch", RT5677_DD2_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC4 R Switch", RT5677_DD2_MIXER, RT5677_M_DAC4_R_DD2_R_SFT, 1, 1), - SOC_DAPM_SINGLE("DAC4 L Switch", RT5677_DD2_MIXER, + SOC_DAPM_SINGLE_AUTODISABLE("DAC4 L Switch", RT5677_DD2_MIXER, RT5677_M_DAC4_L_DD2_R_SFT, 1, 1), }; @@ -2596,6 +2596,21 @@ static int rt5677_vref_event(struct snd_soc_dapm_widget *w, return 0; } +static int rt5677_filter_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + msleep(50); + break; + + default: + return 0; + } + + return 0; +} + static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("PLL1", RT5677_PWR_ANLG2, RT5677_PWR_PLL1_BIT, 0, rt5677_set_pll1_event, SND_SOC_DAPM_PRE_PMU | @@ -3072,19 +3087,26 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = { /* DAC Mixer */ SND_SOC_DAPM_SUPPLY("dac stereo1 filter", RT5677_PWR_DIG2, - RT5677_PWR_DAC_S1F_BIT, 0, NULL, 0), + RT5677_PWR_DAC_S1F_BIT, 0, rt5677_filter_power_event, + SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_SUPPLY("dac mono2 left filter", RT5677_PWR_DIG2, - RT5677_PWR_DAC_M2F_L_BIT, 0, NULL, 0), + RT5677_PWR_DAC_M2F_L_BIT, 0, rt5677_filter_power_event, + SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_SUPPLY("dac mono2 right filter", RT5677_PWR_DIG2, - RT5677_PWR_DAC_M2F_R_BIT, 0, NULL, 0), + RT5677_PWR_DAC_M2F_R_BIT, 0, rt5677_filter_power_event, + SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_SUPPLY("dac mono3 left filter", RT5677_PWR_DIG2, - RT5677_PWR_DAC_M3F_L_BIT, 0, NULL, 0), + RT5677_PWR_DAC_M3F_L_BIT, 0, rt5677_filter_power_event, + SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_SUPPLY("dac mono3 right filter", RT5677_PWR_DIG2, - RT5677_PWR_DAC_M3F_R_BIT, 0, NULL, 0), + RT5677_PWR_DAC_M3F_R_BIT, 0, rt5677_filter_power_event, + SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_SUPPLY("dac mono4 left filter", RT5677_PWR_DIG2, - RT5677_PWR_DAC_M4F_L_BIT, 0, NULL, 0), + RT5677_PWR_DAC_M4F_L_BIT, 0, rt5677_filter_power_event, + SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_SUPPLY("dac mono4 right filter", RT5677_PWR_DIG2, - RT5677_PWR_DAC_M4F_R_BIT, 0, NULL, 0), + RT5677_PWR_DAC_M4F_R_BIT, 0, rt5677_filter_power_event, + SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0, rt5677_sto1_dac_l_mix, ARRAY_SIZE(rt5677_sto1_dac_l_mix)), -- cgit v1.2.3 From 7115cb913d9e2d68583cf76578b32568bc8ea83f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 10 Nov 2015 07:39:17 +0000 Subject: ASoC: rsnd: make sure SRC In Rate feature enablement SRC In Rate convert feature cannot be used if data path is using DVC. This patch judges it, and not allowed to use it in such case. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/src.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'sound') diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 261b50217c48..68b439ed22d7 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -923,6 +923,7 @@ static int rsnd_src_pcm_new_gen2(struct rsnd_mod *mod, struct snd_soc_pcm_runtime *rtd) { struct rsnd_dai *rdai = rsnd_io_to_rdai(io); + struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); struct rsnd_src *src = rsnd_mod_to_src(mod); int ret; @@ -936,6 +937,12 @@ static int rsnd_src_pcm_new_gen2(struct rsnd_mod *mod, if (!rsnd_rdai_is_clk_master(rdai)) return 0; + /* + * SRC In doesn't work if DVC was enabled + */ + if (dvc && !rsnd_io_is_play(io)) + return 0; + /* * enable sync convert */ -- cgit v1.2.3 From 86f799b82f5c011404ddef54600bc5e99b7e0cf2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 14 Nov 2015 17:46:31 +0100 Subject: ALSA: hda - Fix noise on Dell Latitude E6440 Dell Latitude E6440 (1028:05bd) needs the same fixup as applied to other Latitude E7xxx models for the click noise due to the recent power-saving changes. Bugzilla: http://bugzilla.opensuse.org/show_bug.cgi?id=954876 Cc: # v4.1+ Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 2f7b065f9ac4..081ef9bdf0df 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5182,6 +5182,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x079b, "Acer Aspire V5-573G", ALC282_FIXUP_ASPIRE_V5_PINS), SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z), SND_PCI_QUIRK(0x1028, 0x054b, "Dell XPS one 2710", ALC275_FIXUP_DELL_XPS), + SND_PCI_QUIRK(0x1028, 0x05bd, "Dell Latitude E6440", ALC292_FIXUP_DELL_E7X), SND_PCI_QUIRK(0x1028, 0x05ca, "Dell Latitude E7240", ALC292_FIXUP_DELL_E7X), SND_PCI_QUIRK(0x1028, 0x05cb, "Dell Latitude E7440", ALC292_FIXUP_DELL_E7X), SND_PCI_QUIRK(0x1028, 0x05da, "Dell Vostro 5460", ALC290_FIXUP_SUBWOOFER), -- cgit v1.2.3 From 5d5563b14fe34021b690eb3edc54abcc876e417c Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 14 Nov 2015 16:42:04 +0900 Subject: ALSA: dice: fix detection of Loud devices Commit a471fcde8c2c ("ALSA: dice: fix detection of Weiss devices") adds a quirk of Weiss models. According to users' reports, Loud models also have the similar quirk. They have 0x10 in the category field. This commit adds support for Mackie Onyx Blackbird and Onyx-i series. As long as I know, Dice-based models produced by Focusrite/Alesis/PreSonus/M-Audio/TC Electronic have default value (0x04) in their category field, thus it may be reasonable to add a condition statement for Loud models, instead of removing the check of category value. Reported-by: Rouge Etienne Reported-by: Etilem Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/dice/dice.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'sound') diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index 5d99436dfcae..0cda05c72f50 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -12,9 +12,11 @@ MODULE_AUTHOR("Clemens Ladisch "); MODULE_LICENSE("GPL v2"); #define OUI_WEISS 0x001c6a +#define OUI_LOUD 0x000ff2 #define DICE_CATEGORY_ID 0x04 #define WEISS_CATEGORY_ID 0x00 +#define LOUD_CATEGORY_ID 0x10 static int dice_interface_check(struct fw_unit *unit) { @@ -57,6 +59,8 @@ static int dice_interface_check(struct fw_unit *unit) } if (vendor == OUI_WEISS) category = WEISS_CATEGORY_ID; + else if (vendor == OUI_LOUD) + category = LOUD_CATEGORY_ID; else category = DICE_CATEGORY_ID; if (device->config_rom[3] != ((vendor << 8) | category) || -- cgit v1.2.3 From ea54a37442639cf884918de69db46caf693490f8 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 14 Nov 2015 16:54:50 +0900 Subject: ALSA: fireworks: move mutex from function callees to callers Currently, critical section is protected by mutex in functions of fireworks_stream.c. Callers increments/decrements substreams counter before calling the functions. Moving mutex to the callers code allows to change type of the substeram counter from atomic_t to unsigned int. This commit is a preparation for obsoleting usage of atomic_t for substream counter. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/fireworks/fireworks_midi.c | 8 ++++++++ sound/firewire/fireworks/fireworks_pcm.c | 20 ++++++++++++++++---- sound/firewire/fireworks/fireworks_stream.c | 7 ------- 3 files changed, 24 insertions(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/firewire/fireworks/fireworks_midi.c b/sound/firewire/fireworks/fireworks_midi.c index fba01bbba456..38232dcf6e03 100644 --- a/sound/firewire/fireworks/fireworks_midi.c +++ b/sound/firewire/fireworks/fireworks_midi.c @@ -17,8 +17,10 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream) if (err < 0) goto end; + mutex_lock(&efw->mutex); atomic_inc(&efw->capture_substreams); err = snd_efw_stream_start_duplex(efw, 0); + mutex_unlock(&efw->mutex); if (err < 0) snd_efw_stream_lock_release(efw); @@ -35,8 +37,10 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream) if (err < 0) goto end; + mutex_lock(&efw->mutex); atomic_inc(&efw->playback_substreams); err = snd_efw_stream_start_duplex(efw, 0); + mutex_unlock(&efw->mutex); if (err < 0) snd_efw_stream_lock_release(efw); end: @@ -47,8 +51,10 @@ static int midi_capture_close(struct snd_rawmidi_substream *substream) { struct snd_efw *efw = substream->rmidi->private_data; + mutex_lock(&efw->mutex); atomic_dec(&efw->capture_substreams); snd_efw_stream_stop_duplex(efw); + mutex_unlock(&efw->mutex); snd_efw_stream_lock_release(efw); return 0; @@ -58,8 +64,10 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream) { struct snd_efw *efw = substream->rmidi->private_data; + mutex_lock(&efw->mutex); atomic_dec(&efw->playback_substreams); snd_efw_stream_stop_duplex(efw); + mutex_unlock(&efw->mutex); snd_efw_stream_lock_release(efw); return 0; diff --git a/sound/firewire/fireworks/fireworks_pcm.c b/sound/firewire/fireworks/fireworks_pcm.c index d27135bac513..69f15a6d6f88 100644 --- a/sound/firewire/fireworks/fireworks_pcm.c +++ b/sound/firewire/fireworks/fireworks_pcm.c @@ -251,8 +251,11 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream, if (err < 0) return err; - if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) + if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { + mutex_lock(&efw->mutex); atomic_inc(&efw->capture_substreams); + mutex_unlock(&efw->mutex); + } amdtp_am824_set_pcm_format(&efw->tx_stream, params_format(hw_params)); @@ -269,8 +272,11 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream, if (err < 0) return err; - if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) + if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { + mutex_lock(&efw->mutex); atomic_inc(&efw->playback_substreams); + mutex_unlock(&efw->mutex); + } amdtp_am824_set_pcm_format(&efw->rx_stream, params_format(hw_params)); @@ -281,8 +287,11 @@ static int pcm_capture_hw_free(struct snd_pcm_substream *substream) { struct snd_efw *efw = substream->private_data; - if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) + if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) { + mutex_lock(&efw->mutex); atomic_dec(&efw->capture_substreams); + mutex_unlock(&efw->mutex); + } snd_efw_stream_stop_duplex(efw); @@ -292,8 +301,11 @@ static int pcm_playback_hw_free(struct snd_pcm_substream *substream) { struct snd_efw *efw = substream->private_data; - if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) + if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) { + mutex_lock(&efw->mutex); atomic_dec(&efw->playback_substreams); + mutex_unlock(&efw->mutex); + } snd_efw_stream_stop_duplex(efw); diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c index 759f6e3ed44a..69307452dee7 100644 --- a/sound/firewire/fireworks/fireworks_stream.c +++ b/sound/firewire/fireworks/fireworks_stream.c @@ -214,8 +214,6 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate) unsigned int curr_rate; int err = 0; - mutex_lock(&efw->mutex); - /* Need no substreams */ if ((atomic_read(&efw->playback_substreams) == 0) && (atomic_read(&efw->capture_substreams) == 0)) @@ -286,7 +284,6 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate) } } end: - mutex_unlock(&efw->mutex); return err; } @@ -307,16 +304,12 @@ void snd_efw_stream_stop_duplex(struct snd_efw *efw) master_substreams = &efw->capture_substreams; } - mutex_lock(&efw->mutex); - if (atomic_read(slave_substreams) == 0) { stop_stream(efw, slave); if (atomic_read(master_substreams) == 0) stop_stream(efw, master); } - - mutex_unlock(&efw->mutex); } void snd_efw_stream_update_duplex(struct snd_efw *efw) -- cgit v1.2.3 From 4d2c50a0a9ca75fcd0fd57947fb7b394932e482a Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 14 Nov 2015 16:54:51 +0900 Subject: ALSA: fireworks: change type of substream counter from atomic_t to unsigned int The counter is incremented/decremented in critical section protected with mutex. Therefore, no need to use atomic_t. This commit changes the type to unsigned int. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/fireworks/fireworks.h | 4 ++-- sound/firewire/fireworks/fireworks_midi.c | 8 ++++---- sound/firewire/fireworks/fireworks_pcm.c | 8 ++++---- sound/firewire/fireworks/fireworks_stream.c | 25 ++++++++++++------------- 4 files changed, 22 insertions(+), 23 deletions(-) (limited to 'sound') diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h index c7cb7deafe48..96c4e0c6a9bd 100644 --- a/sound/firewire/fireworks/fireworks.h +++ b/sound/firewire/fireworks/fireworks.h @@ -86,8 +86,8 @@ struct snd_efw { struct amdtp_stream rx_stream; struct cmp_connection out_conn; struct cmp_connection in_conn; - atomic_t capture_substreams; - atomic_t playback_substreams; + unsigned int capture_substreams; + unsigned int playback_substreams; /* hardware metering parameters */ unsigned int phys_out; diff --git a/sound/firewire/fireworks/fireworks_midi.c b/sound/firewire/fireworks/fireworks_midi.c index 38232dcf6e03..3e8c4cf9fe1e 100644 --- a/sound/firewire/fireworks/fireworks_midi.c +++ b/sound/firewire/fireworks/fireworks_midi.c @@ -18,7 +18,7 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream) goto end; mutex_lock(&efw->mutex); - atomic_inc(&efw->capture_substreams); + efw->capture_substreams++; err = snd_efw_stream_start_duplex(efw, 0); mutex_unlock(&efw->mutex); if (err < 0) @@ -38,7 +38,7 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream) goto end; mutex_lock(&efw->mutex); - atomic_inc(&efw->playback_substreams); + efw->playback_substreams++; err = snd_efw_stream_start_duplex(efw, 0); mutex_unlock(&efw->mutex); if (err < 0) @@ -52,7 +52,7 @@ static int midi_capture_close(struct snd_rawmidi_substream *substream) struct snd_efw *efw = substream->rmidi->private_data; mutex_lock(&efw->mutex); - atomic_dec(&efw->capture_substreams); + efw->capture_substreams--; snd_efw_stream_stop_duplex(efw); mutex_unlock(&efw->mutex); @@ -65,7 +65,7 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream) struct snd_efw *efw = substream->rmidi->private_data; mutex_lock(&efw->mutex); - atomic_dec(&efw->playback_substreams); + efw->playback_substreams--; snd_efw_stream_stop_duplex(efw); mutex_unlock(&efw->mutex); diff --git a/sound/firewire/fireworks/fireworks_pcm.c b/sound/firewire/fireworks/fireworks_pcm.c index 69f15a6d6f88..f4fbf75ed198 100644 --- a/sound/firewire/fireworks/fireworks_pcm.c +++ b/sound/firewire/fireworks/fireworks_pcm.c @@ -253,7 +253,7 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream, if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&efw->mutex); - atomic_inc(&efw->capture_substreams); + efw->capture_substreams++; mutex_unlock(&efw->mutex); } @@ -274,7 +274,7 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream, if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&efw->mutex); - atomic_inc(&efw->playback_substreams); + efw->playback_substreams++; mutex_unlock(&efw->mutex); } @@ -289,7 +289,7 @@ static int pcm_capture_hw_free(struct snd_pcm_substream *substream) if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) { mutex_lock(&efw->mutex); - atomic_dec(&efw->capture_substreams); + efw->capture_substreams--; mutex_unlock(&efw->mutex); } @@ -303,7 +303,7 @@ static int pcm_playback_hw_free(struct snd_pcm_substream *substream) if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) { mutex_lock(&efw->mutex); - atomic_dec(&efw->playback_substreams); + efw->playback_substreams--; mutex_unlock(&efw->mutex); } diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c index 69307452dee7..968a40a1beb2 100644 --- a/sound/firewire/fireworks/fireworks_stream.c +++ b/sound/firewire/fireworks/fireworks_stream.c @@ -209,14 +209,13 @@ end: int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate) { struct amdtp_stream *master, *slave; - atomic_t *slave_substreams; + unsigned int slave_substreams; enum cip_flags sync_mode; unsigned int curr_rate; int err = 0; /* Need no substreams */ - if ((atomic_read(&efw->playback_substreams) == 0) && - (atomic_read(&efw->capture_substreams) == 0)) + if (efw->playback_substreams == 0 && efw->capture_substreams == 0) goto end; err = get_sync_mode(efw, &sync_mode); @@ -225,11 +224,11 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate) if (sync_mode == CIP_SYNC_TO_DEVICE) { master = &efw->tx_stream; slave = &efw->rx_stream; - slave_substreams = &efw->playback_substreams; + slave_substreams = efw->playback_substreams; } else { master = &efw->rx_stream; slave = &efw->tx_stream; - slave_substreams = &efw->capture_substreams; + slave_substreams = efw->capture_substreams; } /* @@ -275,7 +274,7 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate) } /* start slave if needed */ - if (atomic_read(slave_substreams) > 0 && !amdtp_stream_running(slave)) { + if (slave_substreams > 0 && !amdtp_stream_running(slave)) { err = start_stream(efw, slave, rate); if (err < 0) { dev_err(&efw->unit->device, @@ -290,24 +289,24 @@ end: void snd_efw_stream_stop_duplex(struct snd_efw *efw) { struct amdtp_stream *master, *slave; - atomic_t *master_substreams, *slave_substreams; + unsigned int master_substreams, slave_substreams; if (efw->master == &efw->rx_stream) { slave = &efw->tx_stream; master = &efw->rx_stream; - slave_substreams = &efw->capture_substreams; - master_substreams = &efw->playback_substreams; + slave_substreams = efw->capture_substreams; + master_substreams = efw->playback_substreams; } else { slave = &efw->rx_stream; master = &efw->tx_stream; - slave_substreams = &efw->playback_substreams; - master_substreams = &efw->capture_substreams; + slave_substreams = efw->playback_substreams; + master_substreams = efw->capture_substreams; } - if (atomic_read(slave_substreams) == 0) { + if (slave_substreams == 0) { stop_stream(efw, slave); - if (atomic_read(master_substreams) == 0) + if (master_substreams == 0) stop_stream(efw, master); } } -- cgit v1.2.3 From bc0e7345168c0f7483d2d1da86285d89136417cd Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 20 Oct 2015 11:46:31 +0200 Subject: ALSA: pcm_dmaengine: Properly synchronize DMA on shutdown Use the new dmaengine_synchronize() function to make sure that all complete callbacks have finished running before the runtime data, which is accessed in the completed callback, is freed. This fixes a long standing use-after-free race condition that has been observed on some systems. Signed-off-by: Lars-Peter Clausen Reviewed-by: Takashi Iwai Signed-off-by: Vinod Koul --- sound/core/pcm_dmaengine.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c index fba365a78390..697c166acf05 100644 --- a/sound/core/pcm_dmaengine.c +++ b/sound/core/pcm_dmaengine.c @@ -202,13 +202,13 @@ int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd) if (runtime->info & SNDRV_PCM_INFO_PAUSE) dmaengine_pause(prtd->dma_chan); else - dmaengine_terminate_all(prtd->dma_chan); + dmaengine_terminate_async(prtd->dma_chan); break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: dmaengine_pause(prtd->dma_chan); break; case SNDRV_PCM_TRIGGER_STOP: - dmaengine_terminate_all(prtd->dma_chan); + dmaengine_terminate_async(prtd->dma_chan); break; default: return -EINVAL; @@ -346,6 +346,7 @@ int snd_dmaengine_pcm_close(struct snd_pcm_substream *substream) { struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); + dmaengine_synchronize(prtd->dma_chan); kfree(prtd); return 0; @@ -362,9 +363,11 @@ int snd_dmaengine_pcm_close_release_chan(struct snd_pcm_substream *substream) { struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); + dmaengine_synchronize(prtd->dma_chan); dma_release_channel(prtd->dma_chan); + kfree(prtd); - return snd_dmaengine_pcm_close(substream); + return 0; } EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close_release_chan); -- cgit v1.2.3 From 98d362becb6621bebdda7ed0eac7ad7ec6c37898 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 15 Nov 2015 22:37:44 +0100 Subject: ALSA: usb-audio: add packet size quirk for the Medeli DD305 Signed-off-by: Clemens Ladisch Cc: Signed-off-by: Takashi Iwai --- sound/usb/midi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/usb/midi.c b/sound/usb/midi.c index 7661616f3636..68e9f9a83fde 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -1341,6 +1341,7 @@ static int snd_usbmidi_out_endpoint_create(struct snd_usb_midi *umidi, * Various chips declare a packet size larger than 4 bytes, but * do not actually work with larger packets: */ + case USB_ID(0x0a67, 0x5011): /* Medeli DD305 */ case USB_ID(0x0a92, 0x1020): /* ESI M4U */ case USB_ID(0x1430, 0x474b): /* RedOctane GH MIDI INTERFACE */ case USB_ID(0x15ca, 0x0101): /* Textech USB Midi Cable */ -- cgit v1.2.3 From 1ca8b201309d842642f221db7f02f71c0af5be2d Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 15 Nov 2015 22:38:29 +0100 Subject: ALSA: usb-audio: prevent CH345 multiport output SysEx corruption The CH345 USB MIDI chip has two output ports. However, they are multiplexed through one pin, and the number of ports cannot be reduced even for hardware that implements only one connector, so for those devices, data sent to either port ends up on the same hardware output. This becomes a problem when both ports are used at the same time, as longer MIDI commands (such as SysEx messages) are likely to be interrupted by messages from the other port, and thus to get lost. It would not be possible for the driver to detect how many ports the device actually has, except that in practice, _all_ devices built with the CH345 have only one port. So we can just ignore the device's descriptors, and hardcode one output port. Signed-off-by: Clemens Ladisch Cc: stable@vger.kernel.org Signed-off-by: Takashi Iwai --- sound/usb/midi.c | 3 +++ sound/usb/quirks-table.h | 11 +++++++++++ sound/usb/quirks.c | 1 + sound/usb/usbaudio.h | 1 + 4 files changed, 16 insertions(+) (limited to 'sound') diff --git a/sound/usb/midi.c b/sound/usb/midi.c index 68e9f9a83fde..010094abf752 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -2377,6 +2377,9 @@ int snd_usbmidi_create(struct snd_card *card, if (err < 0) break; + err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints); + break; + case QUIRK_MIDI_CH345: err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints); break; default: diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 1a1e2e4df35e..c60a776e815d 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -2829,6 +2829,17 @@ YAMAHA_DEVICE(0x7010, "UB99"), .idProduct = 0x1020, }, +/* QinHeng devices */ +{ + USB_DEVICE(0x1a86, 0x752d), + .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { + .vendor_name = "QinHeng", + .product_name = "CH345", + .ifnum = 1, + .type = QUIRK_MIDI_CH345 + } +}, + /* KeithMcMillen Stringport */ { USB_DEVICE(0x1f38, 0x0001), diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 5ca80e7d30cd..7016ad898187 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -538,6 +538,7 @@ int snd_usb_create_quirk(struct snd_usb_audio *chip, [QUIRK_MIDI_CME] = create_any_midi_quirk, [QUIRK_MIDI_AKAI] = create_any_midi_quirk, [QUIRK_MIDI_FTDI] = create_any_midi_quirk, + [QUIRK_MIDI_CH345] = create_any_midi_quirk, [QUIRK_AUDIO_STANDARD_INTERFACE] = create_standard_audio_quirk, [QUIRK_AUDIO_FIXED_ENDPOINT] = create_fixed_stream_quirk, [QUIRK_AUDIO_EDIROL_UAXX] = create_uaxx_quirk, diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 15a12715bd05..b665d85555cb 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -95,6 +95,7 @@ enum quirk_type { QUIRK_MIDI_AKAI, QUIRK_MIDI_US122L, QUIRK_MIDI_FTDI, + QUIRK_MIDI_CH345, QUIRK_AUDIO_STANDARD_INTERFACE, QUIRK_AUDIO_FIXED_ENDPOINT, QUIRK_AUDIO_EDIROL_UAXX, -- cgit v1.2.3 From a91e627e3f0ed820b11d86cdc04df38f65f33a70 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Sun, 15 Nov 2015 22:39:08 +0100 Subject: ALSA: usb-audio: work around CH345 input SysEx corruption One of the many faults of the QinHeng CH345 USB MIDI interface chip is that it does not handle received SysEx messages correctly -- every second event packet has a wrong code index number, which is the one from the last seen message, instead of 4. For example, the two messages "FE F0 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E F7" result in the following event packets: correct: CH345: 0F FE 00 00 0F FE 00 00 04 F0 01 02 04 F0 01 02 04 03 04 05 0F 03 04 05 04 06 07 08 04 06 07 08 04 09 0A 0B 0F 09 0A 0B 04 0C 0D 0E 04 0C 0D 0E 05 F7 00 00 05 F7 00 00 A class-compliant driver must interpret an event packet with CIN 15 as having a single data byte, so the other two bytes would be ignored. The message received by the host would then be missing two bytes out of six; in this example, "F0 01 02 03 06 07 08 09 0C 0D 0E F7". These corrupted SysEx event packages contain only data bytes, while the CH345 uses event packets with a correct CIN value only for messages with a status byte, so it is possible to distinguish between these two cases by checking for the presence of this status byte. (Other bugs in the CH345's input handling, such as the corruption resulting from running status, cannot be worked around.) Signed-off-by: Clemens Ladisch Cc: stable@vger.kernel.org Signed-off-by: Takashi Iwai --- sound/usb/midi.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'sound') diff --git a/sound/usb/midi.c b/sound/usb/midi.c index 010094abf752..5b4c58c3e2c5 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -174,6 +174,8 @@ struct snd_usb_midi_in_endpoint { u8 running_status_length; } ports[0x10]; u8 seen_f5; + bool in_sysex; + u8 last_cin; u8 error_resubmit; int current_port; }; @@ -467,6 +469,39 @@ static void snd_usbmidi_maudio_broken_running_status_input( } } +/* + * QinHeng CH345 is buggy: every second packet inside a SysEx has not CIN 4 + * but the previously seen CIN, but still with three data bytes. + */ +static void ch345_broken_sysex_input(struct snd_usb_midi_in_endpoint *ep, + uint8_t *buffer, int buffer_length) +{ + unsigned int i, cin, length; + + for (i = 0; i + 3 < buffer_length; i += 4) { + if (buffer[i] == 0 && i > 0) + break; + cin = buffer[i] & 0x0f; + if (ep->in_sysex && + cin == ep->last_cin && + (buffer[i + 1 + (cin == 0x6)] & 0x80) == 0) + cin = 0x4; +#if 0 + if (buffer[i + 1] == 0x90) { + /* + * Either a corrupted running status or a real note-on + * message; impossible to detect reliably. + */ + } +#endif + length = snd_usbmidi_cin_length[cin]; + snd_usbmidi_input_data(ep, 0, &buffer[i + 1], length); + ep->in_sysex = cin == 0x4; + if (!ep->in_sysex) + ep->last_cin = cin; + } +} + /* * CME protocol: like the standard protocol, but SysEx commands are sent as a * single USB packet preceded by a 0x0F byte. @@ -660,6 +695,12 @@ static struct usb_protocol_ops snd_usbmidi_cme_ops = { .output_packet = snd_usbmidi_output_standard_packet, }; +static struct usb_protocol_ops snd_usbmidi_ch345_broken_sysex_ops = { + .input = ch345_broken_sysex_input, + .output = snd_usbmidi_standard_output, + .output_packet = snd_usbmidi_output_standard_packet, +}; + /* * AKAI MPD16 protocol: * @@ -2380,6 +2421,7 @@ int snd_usbmidi_create(struct snd_card *card, err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints); break; case QUIRK_MIDI_CH345: + umidi->usb_protocol_ops = &snd_usbmidi_ch345_broken_sysex_ops; err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints); break; default: -- cgit v1.2.3 From 3c7a09358729e64119669f454fb1ac3c5cd20b63 Mon Sep 17 00:00:00 2001 From: Cheah Kok Cheong Date: Sun, 15 Nov 2015 05:31:30 +0800 Subject: ALSA: ua101: replace le16_to_cpu() with usb_endpoint_maxp() Commit 939f325f4a0f ("usb: add usb_endpoint_maxp() macro") and commit 29cc88979a88 ("USB: use usb_endpoint_maxp() instead of le16_to_cpu()") introduced a new helper macro. This trivial patch convert remaining users found in ua101 driver. Signed-off-by: Cheah Kok Cheong Acked-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/usb/misc/ua101.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c index 9581089c28c5..c19a5dd05631 100644 --- a/sound/usb/misc/ua101.c +++ b/sound/usb/misc/ua101.c @@ -1037,7 +1037,7 @@ static int detect_usb_format(struct ua101 *ua) return -ENXIO; } ua->capture.usb_pipe = usb_rcvisocpipe(ua->dev, usb_endpoint_num(epd)); - ua->capture.max_packet_bytes = le16_to_cpu(epd->wMaxPacketSize); + ua->capture.max_packet_bytes = usb_endpoint_maxp(epd); epd = &ua->intf[INTF_PLAYBACK]->altsetting[1].endpoint[0].desc; if (!usb_endpoint_is_isoc_out(epd)) { @@ -1045,7 +1045,7 @@ static int detect_usb_format(struct ua101 *ua) return -ENXIO; } ua->playback.usb_pipe = usb_sndisocpipe(ua->dev, usb_endpoint_num(epd)); - ua->playback.max_packet_bytes = le16_to_cpu(epd->wMaxPacketSize); + ua->playback.max_packet_bytes = usb_endpoint_maxp(epd); return 0; } -- cgit v1.2.3 From bef3c4ef7e05cada90b5aba2ca75a29441da9532 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 3 Nov 2015 15:05:50 +0000 Subject: ASoC: wm8998: Remove duplicated consts The SOC_xxx_DECL() macros already include 'const' so there's no need to put a const in the source where they are used. Signed-off-by: Richard Fitzgerald Signed-off-by: Mark Brown --- sound/soc/codecs/wm8998.c | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm8998.c b/sound/soc/codecs/wm8998.c index 8782dfb628ab..7719bc509e50 100644 --- a/sound/soc/codecs/wm8998.c +++ b/sound/soc/codecs/wm8998.c @@ -199,20 +199,20 @@ static const char * const wm8998_inmux_texts[] = { "B", }; -static const SOC_ENUM_SINGLE_DECL(wm8998_in1muxl_enum, - ARIZONA_ADC_DIGITAL_VOLUME_1L, - ARIZONA_IN1L_SRC_SHIFT, - wm8998_inmux_texts); +static SOC_ENUM_SINGLE_DECL(wm8998_in1muxl_enum, + ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_SRC_SHIFT, + wm8998_inmux_texts); -static const SOC_ENUM_SINGLE_DECL(wm8998_in1muxr_enum, - ARIZONA_ADC_DIGITAL_VOLUME_1R, - ARIZONA_IN1R_SRC_SHIFT, - wm8998_inmux_texts); +static SOC_ENUM_SINGLE_DECL(wm8998_in1muxr_enum, + ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_SRC_SHIFT, + wm8998_inmux_texts); -static const SOC_ENUM_SINGLE_DECL(wm8998_in2mux_enum, - ARIZONA_ADC_DIGITAL_VOLUME_2L, - ARIZONA_IN2L_SRC_SHIFT, - wm8998_inmux_texts); +static SOC_ENUM_SINGLE_DECL(wm8998_in2mux_enum, + ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_SRC_SHIFT, + wm8998_inmux_texts); static const struct snd_kcontrol_new wm8998_in1mux[2] = { SOC_DAPM_ENUM_EXT("IN1L Mux", wm8998_in1muxl_enum, @@ -522,17 +522,17 @@ static const unsigned int wm8998_aec_loopback_values[] = { 0, 1, 2, 3, 4, 6, 7, 8, 9, }; -static const SOC_VALUE_ENUM_SINGLE_DECL(wm8998_aec1_loopback, - ARIZONA_DAC_AEC_CONTROL_1, - ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf, - wm8998_aec_loopback_texts, - wm8998_aec_loopback_values); - -static const SOC_VALUE_ENUM_SINGLE_DECL(wm8998_aec2_loopback, - ARIZONA_DAC_AEC_CONTROL_2, - ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf, - wm8998_aec_loopback_texts, - wm8998_aec_loopback_values); +static SOC_VALUE_ENUM_SINGLE_DECL(wm8998_aec1_loopback, + ARIZONA_DAC_AEC_CONTROL_1, + ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf, + wm8998_aec_loopback_texts, + wm8998_aec_loopback_values); + +static SOC_VALUE_ENUM_SINGLE_DECL(wm8998_aec2_loopback, + ARIZONA_DAC_AEC_CONTROL_2, + ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf, + wm8998_aec_loopback_texts, + wm8998_aec_loopback_values); static const struct snd_kcontrol_new wm8998_aec_loopback_mux[] = { SOC_DAPM_ENUM("AEC1 Loopback", wm8998_aec1_loopback), -- cgit v1.2.3 From 6610550c4c2663f51cec308a88870da20db48113 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 3 Nov 2015 15:08:35 +0000 Subject: ASoC: cs47l24: Add driver for Cirrus Logic CS47L24 and WM1831 codecs This patch adds support for the Cirrus Logic CS47L24 and WM1831 codecs. Signed-off-by: Richard Fitzgerald Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 8 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/cs47l24.c | 1148 ++++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/cs47l24.h | 23 + 4 files changed, 1181 insertions(+) create mode 100644 sound/soc/codecs/cs47l24.c create mode 100644 sound/soc/codecs/cs47l24.h (limited to 'sound') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index cfdafc4c11ea..55e14a3ed5e1 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -55,6 +55,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_CS4271_SPI if SPI_MASTER select SND_SOC_CS42XX8_I2C if I2C select SND_SOC_CS4349 if I2C + select SND_SOC_CS47L24 if MFD_CS47L24 select SND_SOC_CX20442 if TTY select SND_SOC_DA7210 if SND_SOC_I2C_AND_SPI select SND_SOC_DA7213 if I2C @@ -195,10 +196,12 @@ config SND_SOC_88PM860X config SND_SOC_ARIZONA tristate + default y if SND_SOC_CS47L24=y default y if SND_SOC_WM5102=y default y if SND_SOC_WM5110=y default y if SND_SOC_WM8997=y default y if SND_SOC_WM8998=y + default m if SND_SOC_CS47L24=m default m if SND_SOC_WM5102=m default m if SND_SOC_WM5110=m default m if SND_SOC_WM8997=m @@ -211,9 +214,11 @@ config SND_SOC_WM_HUBS config SND_SOC_WM_ADSP tristate + default y if SND_SOC_CS47L24=y default y if SND_SOC_WM5102=y default y if SND_SOC_WM5110=y default y if SND_SOC_WM2200=y + default m if SND_SOC_CS47L24=m default m if SND_SOC_WM5102=m default m if SND_SOC_WM5110=m default m if SND_SOC_WM2200=m @@ -422,6 +427,9 @@ config SND_SOC_CS4349 tristate "Cirrus Logic CS4349 CODEC" depends on I2C +config SND_SOC_CS47L24 + tristate + config SND_SOC_CX20442 tristate depends on TTY diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index f632fc42f59f..c1d73fe4cf6b 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -47,6 +47,7 @@ snd-soc-cs4271-spi-objs := cs4271-spi.o snd-soc-cs42xx8-objs := cs42xx8.o snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o snd-soc-cs4349-objs := cs4349.o +snd-soc-cs47l24-objs := cs47l24.o snd-soc-cx20442-objs := cx20442.o snd-soc-da7210-objs := da7210.o snd-soc-da7213-objs := da7213.o @@ -242,6 +243,7 @@ obj-$(CONFIG_SND_SOC_CS4271_SPI) += snd-soc-cs4271-spi.o obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o obj-$(CONFIG_SND_SOC_CS4349) += snd-soc-cs4349.o +obj-$(CONFIG_SND_SOC_CS47L24) += snd-soc-cs47l24.o obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o obj-$(CONFIG_SND_SOC_DA7213) += snd-soc-da7213.o diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c new file mode 100644 index 000000000000..dc5ae7f7a1bd --- /dev/null +++ b/sound/soc/codecs/cs47l24.c @@ -0,0 +1,1148 @@ +/* + * cs47l24.h -- ALSA SoC Audio driver for Cirrus Logic CS47L24 + * + * Copyright 2015 Cirrus Logic Inc. + * + * Author: Richard Fitzgerald + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "arizona.h" +#include "wm_adsp.h" +#include "cs47l24.h" + +struct cs47l24_priv { + struct arizona_priv core; + struct arizona_fll fll[2]; +}; + +static const struct wm_adsp_region cs47l24_dsp2_regions[] = { + { .type = WMFW_ADSP2_PM, .base = 0x200000 }, + { .type = WMFW_ADSP2_ZM, .base = 0x280000 }, + { .type = WMFW_ADSP2_XM, .base = 0x290000 }, + { .type = WMFW_ADSP2_YM, .base = 0x2a8000 }, +}; + +static const struct wm_adsp_region cs47l24_dsp3_regions[] = { + { .type = WMFW_ADSP2_PM, .base = 0x300000 }, + { .type = WMFW_ADSP2_ZM, .base = 0x380000 }, + { .type = WMFW_ADSP2_XM, .base = 0x390000 }, + { .type = WMFW_ADSP2_YM, .base = 0x3a8000 }, +}; + +static const struct wm_adsp_region *cs47l24_dsp_regions[] = { + cs47l24_dsp2_regions, + cs47l24_dsp3_regions, +}; + +static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); +static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); +static DECLARE_TLV_DB_SCALE(noise_tlv, -13200, 600, 0); +static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0); + +#define CS47L24_NG_SRC(name, base) \ + SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \ + SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \ + SOC_SINGLE(name " NG SPKOUT Switch", base, 6, 1, 0) + +static const struct snd_kcontrol_new cs47l24_snd_controls[] = { +SOC_ENUM("IN1 OSR", arizona_in_dmic_osr[0]), +SOC_ENUM("IN2 OSR", arizona_in_dmic_osr[1]), + +SOC_ENUM("IN HPF Cutoff Frequency", arizona_in_hpf_cut_enum), + +SOC_SINGLE("IN1L HPF Switch", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN1R HPF Switch", ARIZONA_IN1R_CONTROL, + ARIZONA_IN1R_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN2L HPF Switch", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN2R HPF Switch", ARIZONA_IN2R_CONTROL, + ARIZONA_IN2R_HPF_SHIFT, 1, 0), + +SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2R, + ARIZONA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), + +SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp), +SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp), + +ARIZONA_MIXER_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE), + +ARIZONA_EQ_CONTROL("EQ1 Coefficients", ARIZONA_EQ1_2), +SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B3 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +ARIZONA_EQ_CONTROL("EQ2 Coefficients", ARIZONA_EQ2_2), +SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B3 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +ARIZONA_MIXER_CONTROLS("DRC1L", ARIZONA_DRC1LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DRC1R", ARIZONA_DRC1RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DRC2L", ARIZONA_DRC2LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DRC2R", ARIZONA_DRC2RMIX_INPUT_1_SOURCE), + +SND_SOC_BYTES_MASK("DRC1", ARIZONA_DRC1_CTRL1, 5, + ARIZONA_DRC1R_ENA | ARIZONA_DRC1L_ENA), +SND_SOC_BYTES_MASK("DRC2", ARIZONA_DRC2_CTRL1, 5, + ARIZONA_DRC2R_ENA | ARIZONA_DRC2L_ENA), + +ARIZONA_MIXER_CONTROLS("LHPF1", ARIZONA_HPLP1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF3", ARIZONA_HPLP3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF4", ARIZONA_HPLP4MIX_INPUT_1_SOURCE), + +ARIZONA_LHPF_CONTROL("LHPF1 Coefficients", ARIZONA_HPLPF1_2), +ARIZONA_LHPF_CONTROL("LHPF2 Coefficients", ARIZONA_HPLPF2_2), +ARIZONA_LHPF_CONTROL("LHPF3 Coefficients", ARIZONA_HPLPF3_2), +ARIZONA_LHPF_CONTROL("LHPF4 Coefficients", ARIZONA_HPLPF4_2), + +SOC_ENUM("LHPF1 Mode", arizona_lhpf1_mode), +SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode), +SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode), +SOC_ENUM("LHPF4 Mode", arizona_lhpf4_mode), + +SOC_ENUM("ISRC1 FSL", arizona_isrc_fsl[0]), +SOC_ENUM("ISRC2 FSL", arizona_isrc_fsl[1]), +SOC_ENUM("ISRC3 FSL", arizona_isrc_fsl[2]), +SOC_ENUM("ISRC1 FSH", arizona_isrc_fsh[0]), +SOC_ENUM("ISRC2 FSH", arizona_isrc_fsh[1]), +SOC_ENUM("ISRC3 FSH", arizona_isrc_fsh[2]), +SOC_ENUM("ASRC RATE 1", arizona_asrc_rate1), + +ARIZONA_MIXER_CONTROLS("DSP2L", ARIZONA_DSP2LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DSP2R", ARIZONA_DSP2RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DSP3L", ARIZONA_DSP3LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DSP3R", ARIZONA_DSP3RMIX_INPUT_1_SOURCE), + +SOC_SINGLE_TLV("Noise Generator Volume", ARIZONA_COMFORT_NOISE_GENERATOR, + ARIZONA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, noise_tlv), + +ARIZONA_MIXER_CONTROLS("HPOUT1L", ARIZONA_OUT1LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("HPOUT1R", ARIZONA_OUT1RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKOUT", ARIZONA_OUT4LMIX_INPUT_1_SOURCE), + +SOC_SINGLE("HPOUT1 SC Protect Switch", ARIZONA_HP1_SHORT_CIRCUIT_CTRL, + ARIZONA_HP1_SC_ENA_SHIFT, 1, 0), + +SOC_DOUBLE_R("HPOUT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("Speaker Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_OUT4L_MUTE_SHIFT, 1, 1), + +SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("Speaker Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_OUT4L_VOL_SHIFT, + 0xbf, 0, digital_tlv), + +SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp), +SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp), + +SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_ENA_SHIFT, 1, 0), +SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv), +SOC_ENUM("Noise Gate Hold", arizona_ng_hold), + +CS47L24_NG_SRC("HPOUT1L", ARIZONA_NOISE_GATE_SELECT_1L), +CS47L24_NG_SRC("HPOUT1R", ARIZONA_NOISE_GATE_SELECT_1R), +CS47L24_NG_SRC("SPKOUT", ARIZONA_NOISE_GATE_SELECT_4L), + +ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX4", ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX5", ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX6", ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX7", ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX8", ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE), + +ARIZONA_MIXER_CONTROLS("AIF2TX1", ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX3", ARIZONA_AIF2TX3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX4", ARIZONA_AIF2TX4MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX5", ARIZONA_AIF2TX5MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX6", ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE), + +ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE), +}; + +ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(EQ2, ARIZONA_EQ2MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(DRC1L, ARIZONA_DRC1LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DRC1R, ARIZONA_DRC1RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DRC2L, ARIZONA_DRC2LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DRC2R, ARIZONA_DRC2RMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(LHPF1, ARIZONA_HPLP1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF2, ARIZONA_HPLP2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF3, ARIZONA_HPLP3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF4, ARIZONA_HPLP4MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(DSP2L, ARIZONA_DSP2LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DSP2R, ARIZONA_DSP2RMIX_INPUT_1_SOURCE); +ARIZONA_DSP_AUX_ENUMS(DSP2, ARIZONA_DSP2AUX1MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(DSP3L, ARIZONA_DSP3LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DSP3R, ARIZONA_DSP3RMIX_INPUT_1_SOURCE); +ARIZONA_DSP_AUX_ENUMS(DSP3, ARIZONA_DSP3AUX1MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(PWM1, ARIZONA_PWM1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(PWM2, ARIZONA_PWM2MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(OUT1L, ARIZONA_OUT1LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT1R, ARIZONA_OUT1RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKOUT, ARIZONA_OUT4LMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF1TX1, ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX2, ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX3, ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX4, ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX5, ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX6, ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX7, ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX8, ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF2TX1, ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX3, ARIZONA_AIF2TX3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX4, ARIZONA_AIF2TX4MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX5, ARIZONA_AIF2TX5MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX6, ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC1INT1, ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT2, ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT3, ARIZONA_ISRC1INT3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT4, ARIZONA_ISRC1INT4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC1DEC1, ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC2, ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC3, ARIZONA_ISRC1DEC3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC4, ARIZONA_ISRC1DEC4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC2INT1, ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2INT2, ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2INT3, ARIZONA_ISRC2INT3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2INT4, ARIZONA_ISRC2INT4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC2DEC1, ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2DEC2, ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2DEC3, ARIZONA_ISRC2DEC3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2DEC4, ARIZONA_ISRC2DEC4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC3INT1, ARIZONA_ISRC3INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC3INT2, ARIZONA_ISRC3INT2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC3INT3, ARIZONA_ISRC3INT3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC3INT4, ARIZONA_ISRC3INT4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC3DEC1, ARIZONA_ISRC3DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC3DEC2, ARIZONA_ISRC3DEC2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC3DEC3, ARIZONA_ISRC3DEC3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC3DEC4, ARIZONA_ISRC3DEC4MIX_INPUT_1_SOURCE); + +static const char * const cs47l24_aec_loopback_texts[] = { + "HPOUT1L", "HPOUT1R", "SPKOUT", +}; + +static const unsigned int cs47l24_aec_loopback_values[] = { + 0, 1, 6, +}; + +static const struct soc_enum cs47l24_aec_loopback = + SOC_VALUE_ENUM_SINGLE(ARIZONA_DAC_AEC_CONTROL_1, + ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf, + ARRAY_SIZE(cs47l24_aec_loopback_texts), + cs47l24_aec_loopback_texts, + cs47l24_aec_loopback_values); + +static const struct snd_kcontrol_new cs47l24_aec_loopback_mux = + SOC_DAPM_ENUM("AEC Loopback", cs47l24_aec_loopback); + +static const struct snd_soc_dapm_widget cs47l24_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, + ARIZONA_SYSCLK_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1, + ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK, + ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK, + ARIZONA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS), +SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDD", 0, 0), + +SND_SOC_DAPM_SIGGEN("TONE"), +SND_SOC_DAPM_SIGGEN("NOISE"), +SND_SOC_DAPM_SIGGEN("HAPTICS"), + +SND_SOC_DAPM_INPUT("IN1L"), +SND_SOC_DAPM_INPUT("IN1R"), +SND_SOC_DAPM_INPUT("IN2L"), +SND_SOC_DAPM_INPUT("IN2R"), + +SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"), +SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"), + +SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2R_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_SUPPLY("MICBIAS1", ARIZONA_MIC_BIAS_CTRL_1, + ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS2", ARIZONA_MIC_BIAS_CTRL_2, + ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Noise Generator", ARIZONA_COMFORT_NOISE_GENERATOR, + ARIZONA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Tone Generator 1", ARIZONA_TONE_GENERATOR_1, + ARIZONA_TONE1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("Tone Generator 2", ARIZONA_TONE_GENERATOR_1, + ARIZONA_TONE2_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("EQ1", ARIZONA_EQ1_1, ARIZONA_EQ1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ2", ARIZONA_EQ2_1, ARIZONA_EQ2_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("DRC1L", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC1R", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1R_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC2L", ARIZONA_DRC2_CTRL1, ARIZONA_DRC2L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC2R", ARIZONA_DRC2_CTRL1, ARIZONA_DRC2R_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("LHPF1", ARIZONA_HPLPF1_1, ARIZONA_LHPF1_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF2", ARIZONA_HPLPF2_1, ARIZONA_LHPF2_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF3", ARIZONA_HPLPF3_1, ARIZONA_LHPF3_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF4", ARIZONA_HPLPF4_1, ARIZONA_LHPF4_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("PWM1 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM1_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_PGA("PWM2 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM2_ENA_SHIFT, + 0, NULL, 0), + +SND_SOC_DAPM_PGA("ASRC1L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("ASRC1R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1R_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0, + NULL, 0), + +WM_ADSP2("DSP2", 1), +WM_ADSP2("DSP3", 2), + +SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT2", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT3", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT4", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC1DEC1", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC2", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC3", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC4", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2INT1", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT2", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT3", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT4", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2DEC1", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC2", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC3", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC4", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC3INT1", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3INT2", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_INT1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3INT3", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_INT2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3INT4", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_INT3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC3DEC1", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3DEC2", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_DEC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3DEC3", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_DEC2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3DEC4", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_DEC3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1, + ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0, + &cs47l24_aec_loopback_mux), + +SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0, + ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 0, + ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0, + ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0, + ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM, + ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM, + ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + +ARIZONA_MIXER_WIDGETS(EQ1, "EQ1"), +ARIZONA_MIXER_WIDGETS(EQ2, "EQ2"), + +ARIZONA_MIXER_WIDGETS(DRC1L, "DRC1L"), +ARIZONA_MIXER_WIDGETS(DRC1R, "DRC1R"), +ARIZONA_MIXER_WIDGETS(DRC2L, "DRC2L"), +ARIZONA_MIXER_WIDGETS(DRC2R, "DRC2R"), + +ARIZONA_MIXER_WIDGETS(LHPF1, "LHPF1"), +ARIZONA_MIXER_WIDGETS(LHPF2, "LHPF2"), +ARIZONA_MIXER_WIDGETS(LHPF3, "LHPF3"), +ARIZONA_MIXER_WIDGETS(LHPF4, "LHPF4"), + +ARIZONA_MIXER_WIDGETS(PWM1, "PWM1"), +ARIZONA_MIXER_WIDGETS(PWM2, "PWM2"), + +ARIZONA_MIXER_WIDGETS(OUT1L, "HPOUT1L"), +ARIZONA_MIXER_WIDGETS(OUT1R, "HPOUT1R"), +ARIZONA_MIXER_WIDGETS(SPKOUT, "SPKOUT"), + +ARIZONA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"), +ARIZONA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"), +ARIZONA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"), +ARIZONA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"), +ARIZONA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"), +ARIZONA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"), +ARIZONA_MIXER_WIDGETS(AIF1TX7, "AIF1TX7"), +ARIZONA_MIXER_WIDGETS(AIF1TX8, "AIF1TX8"), + +ARIZONA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"), +ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"), +ARIZONA_MIXER_WIDGETS(AIF2TX3, "AIF2TX3"), +ARIZONA_MIXER_WIDGETS(AIF2TX4, "AIF2TX4"), +ARIZONA_MIXER_WIDGETS(AIF2TX5, "AIF2TX5"), +ARIZONA_MIXER_WIDGETS(AIF2TX6, "AIF2TX6"), + +ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"), +ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"), + +ARIZONA_MUX_WIDGETS(ASRC1L, "ASRC1L"), +ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"), +ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"), +ARIZONA_MUX_WIDGETS(ASRC2R, "ASRC2R"), + +ARIZONA_DSP_WIDGETS(DSP2, "DSP2"), +ARIZONA_DSP_WIDGETS(DSP3, "DSP3"), + +ARIZONA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"), +ARIZONA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"), +ARIZONA_MUX_WIDGETS(ISRC1DEC3, "ISRC1DEC3"), +ARIZONA_MUX_WIDGETS(ISRC1DEC4, "ISRC1DEC4"), + +ARIZONA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"), +ARIZONA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"), +ARIZONA_MUX_WIDGETS(ISRC1INT3, "ISRC1INT3"), +ARIZONA_MUX_WIDGETS(ISRC1INT4, "ISRC1INT4"), + +ARIZONA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"), +ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"), +ARIZONA_MUX_WIDGETS(ISRC2DEC3, "ISRC2DEC3"), +ARIZONA_MUX_WIDGETS(ISRC2DEC4, "ISRC2DEC4"), + +ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"), +ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"), +ARIZONA_MUX_WIDGETS(ISRC2INT3, "ISRC2INT3"), +ARIZONA_MUX_WIDGETS(ISRC2INT4, "ISRC2INT4"), + +ARIZONA_MUX_WIDGETS(ISRC3DEC1, "ISRC3DEC1"), +ARIZONA_MUX_WIDGETS(ISRC3DEC2, "ISRC3DEC2"), +ARIZONA_MUX_WIDGETS(ISRC3DEC3, "ISRC3DEC3"), +ARIZONA_MUX_WIDGETS(ISRC3DEC4, "ISRC3DEC4"), + +ARIZONA_MUX_WIDGETS(ISRC3INT1, "ISRC3INT1"), +ARIZONA_MUX_WIDGETS(ISRC3INT2, "ISRC3INT2"), +ARIZONA_MUX_WIDGETS(ISRC3INT3, "ISRC3INT3"), +ARIZONA_MUX_WIDGETS(ISRC3INT4, "ISRC3INT4"), + +SND_SOC_DAPM_OUTPUT("HPOUT1L"), +SND_SOC_DAPM_OUTPUT("HPOUT1R"), +SND_SOC_DAPM_OUTPUT("SPKOUTN"), +SND_SOC_DAPM_OUTPUT("SPKOUTP"), + +SND_SOC_DAPM_OUTPUT("MICSUPP"), +}; + +#define ARIZONA_MIXER_INPUT_ROUTES(name) \ + { name, "Noise Generator", "Noise Generator" }, \ + { name, "Tone Generator 1", "Tone Generator 1" }, \ + { name, "Tone Generator 2", "Tone Generator 2" }, \ + { name, "Haptics", "HAPTICS" }, \ + { name, "AEC", "AEC Loopback" }, \ + { name, "IN1L", "IN1L PGA" }, \ + { name, "IN1R", "IN1R PGA" }, \ + { name, "IN2L", "IN2L PGA" }, \ + { name, "IN2R", "IN2R PGA" }, \ + { name, "AIF1RX1", "AIF1RX1" }, \ + { name, "AIF1RX2", "AIF1RX2" }, \ + { name, "AIF1RX3", "AIF1RX3" }, \ + { name, "AIF1RX4", "AIF1RX4" }, \ + { name, "AIF1RX5", "AIF1RX5" }, \ + { name, "AIF1RX6", "AIF1RX6" }, \ + { name, "AIF1RX7", "AIF1RX7" }, \ + { name, "AIF1RX8", "AIF1RX8" }, \ + { name, "AIF2RX1", "AIF2RX1" }, \ + { name, "AIF2RX2", "AIF2RX2" }, \ + { name, "AIF2RX3", "AIF2RX3" }, \ + { name, "AIF2RX4", "AIF2RX4" }, \ + { name, "AIF2RX5", "AIF2RX5" }, \ + { name, "AIF2RX6", "AIF2RX6" }, \ + { name, "AIF3RX1", "AIF3RX1" }, \ + { name, "AIF3RX2", "AIF3RX2" }, \ + { name, "EQ1", "EQ1" }, \ + { name, "EQ2", "EQ2" }, \ + { name, "DRC1L", "DRC1L" }, \ + { name, "DRC1R", "DRC1R" }, \ + { name, "DRC2L", "DRC2L" }, \ + { name, "DRC2R", "DRC2R" }, \ + { name, "LHPF1", "LHPF1" }, \ + { name, "LHPF2", "LHPF2" }, \ + { name, "LHPF3", "LHPF3" }, \ + { name, "LHPF4", "LHPF4" }, \ + { name, "ASRC1L", "ASRC1L" }, \ + { name, "ASRC1R", "ASRC1R" }, \ + { name, "ASRC2L", "ASRC2L" }, \ + { name, "ASRC2R", "ASRC2R" }, \ + { name, "ISRC1DEC1", "ISRC1DEC1" }, \ + { name, "ISRC1DEC2", "ISRC1DEC2" }, \ + { name, "ISRC1DEC3", "ISRC1DEC3" }, \ + { name, "ISRC1DEC4", "ISRC1DEC4" }, \ + { name, "ISRC1INT1", "ISRC1INT1" }, \ + { name, "ISRC1INT2", "ISRC1INT2" }, \ + { name, "ISRC1INT3", "ISRC1INT3" }, \ + { name, "ISRC1INT4", "ISRC1INT4" }, \ + { name, "ISRC2DEC1", "ISRC2DEC1" }, \ + { name, "ISRC2DEC2", "ISRC2DEC2" }, \ + { name, "ISRC2DEC3", "ISRC2DEC3" }, \ + { name, "ISRC2DEC4", "ISRC2DEC4" }, \ + { name, "ISRC2INT1", "ISRC2INT1" }, \ + { name, "ISRC2INT2", "ISRC2INT2" }, \ + { name, "ISRC2INT3", "ISRC2INT3" }, \ + { name, "ISRC2INT4", "ISRC2INT4" }, \ + { name, "ISRC3DEC1", "ISRC3DEC1" }, \ + { name, "ISRC3DEC2", "ISRC3DEC2" }, \ + { name, "ISRC3DEC3", "ISRC3DEC3" }, \ + { name, "ISRC3DEC4", "ISRC3DEC4" }, \ + { name, "ISRC3INT1", "ISRC3INT1" }, \ + { name, "ISRC3INT2", "ISRC3INT2" }, \ + { name, "ISRC3INT3", "ISRC3INT3" }, \ + { name, "ISRC3INT4", "ISRC3INT4" }, \ + { name, "DSP2.1", "DSP2" }, \ + { name, "DSP2.2", "DSP2" }, \ + { name, "DSP2.3", "DSP2" }, \ + { name, "DSP2.4", "DSP2" }, \ + { name, "DSP2.5", "DSP2" }, \ + { name, "DSP2.6", "DSP2" }, \ + { name, "DSP3.1", "DSP3" }, \ + { name, "DSP3.2", "DSP3" }, \ + { name, "DSP3.3", "DSP3" }, \ + { name, "DSP3.4", "DSP3" }, \ + { name, "DSP3.5", "DSP3" }, \ + { name, "DSP3.6", "DSP3" } + +static const struct snd_soc_dapm_route cs47l24_dapm_routes[] = { + { "OUT1L", NULL, "CPVDD" }, + { "OUT1R", NULL, "CPVDD" }, + + { "OUT4L", NULL, "SPKVDD" }, + + { "OUT1L", NULL, "SYSCLK" }, + { "OUT1R", NULL, "SYSCLK" }, + { "OUT4L", NULL, "SYSCLK" }, + + { "IN1L", NULL, "SYSCLK" }, + { "IN1R", NULL, "SYSCLK" }, + { "IN2L", NULL, "SYSCLK" }, + { "IN2R", NULL, "SYSCLK" }, + + { "MICBIAS1", NULL, "MICVDD" }, + { "MICBIAS2", NULL, "MICVDD" }, + + { "Noise Generator", NULL, "SYSCLK" }, + { "Tone Generator 1", NULL, "SYSCLK" }, + { "Tone Generator 2", NULL, "SYSCLK" }, + + { "Noise Generator", NULL, "NOISE" }, + { "Tone Generator 1", NULL, "TONE" }, + { "Tone Generator 2", NULL, "TONE" }, + + { "AIF1 Capture", NULL, "AIF1TX1" }, + { "AIF1 Capture", NULL, "AIF1TX2" }, + { "AIF1 Capture", NULL, "AIF1TX3" }, + { "AIF1 Capture", NULL, "AIF1TX4" }, + { "AIF1 Capture", NULL, "AIF1TX5" }, + { "AIF1 Capture", NULL, "AIF1TX6" }, + { "AIF1 Capture", NULL, "AIF1TX7" }, + { "AIF1 Capture", NULL, "AIF1TX8" }, + + { "AIF1RX1", NULL, "AIF1 Playback" }, + { "AIF1RX2", NULL, "AIF1 Playback" }, + { "AIF1RX3", NULL, "AIF1 Playback" }, + { "AIF1RX4", NULL, "AIF1 Playback" }, + { "AIF1RX5", NULL, "AIF1 Playback" }, + { "AIF1RX6", NULL, "AIF1 Playback" }, + { "AIF1RX7", NULL, "AIF1 Playback" }, + { "AIF1RX8", NULL, "AIF1 Playback" }, + + { "AIF2 Capture", NULL, "AIF2TX1" }, + { "AIF2 Capture", NULL, "AIF2TX2" }, + { "AIF2 Capture", NULL, "AIF2TX3" }, + { "AIF2 Capture", NULL, "AIF2TX4" }, + { "AIF2 Capture", NULL, "AIF2TX5" }, + { "AIF2 Capture", NULL, "AIF2TX6" }, + + { "AIF2RX1", NULL, "AIF2 Playback" }, + { "AIF2RX2", NULL, "AIF2 Playback" }, + { "AIF2RX3", NULL, "AIF2 Playback" }, + { "AIF2RX4", NULL, "AIF2 Playback" }, + { "AIF2RX5", NULL, "AIF2 Playback" }, + { "AIF2RX6", NULL, "AIF2 Playback" }, + + { "AIF3 Capture", NULL, "AIF3TX1" }, + { "AIF3 Capture", NULL, "AIF3TX2" }, + + { "AIF3RX1", NULL, "AIF3 Playback" }, + { "AIF3RX2", NULL, "AIF3 Playback" }, + + { "AIF1 Playback", NULL, "SYSCLK" }, + { "AIF2 Playback", NULL, "SYSCLK" }, + { "AIF3 Playback", NULL, "SYSCLK" }, + + { "AIF1 Capture", NULL, "SYSCLK" }, + { "AIF2 Capture", NULL, "SYSCLK" }, + { "AIF3 Capture", NULL, "SYSCLK" }, + + { "IN1L PGA", NULL, "IN1L" }, + { "IN1R PGA", NULL, "IN1R" }, + + { "IN2L PGA", NULL, "IN2L" }, + { "IN2R PGA", NULL, "IN2R" }, + + ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"), + ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"), + + ARIZONA_MIXER_ROUTES("OUT4L", "SPKOUT"), + + ARIZONA_MIXER_ROUTES("PWM1 Driver", "PWM1"), + ARIZONA_MIXER_ROUTES("PWM2 Driver", "PWM2"), + + ARIZONA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"), + ARIZONA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"), + ARIZONA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"), + ARIZONA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"), + ARIZONA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"), + ARIZONA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"), + ARIZONA_MIXER_ROUTES("AIF1TX7", "AIF1TX7"), + ARIZONA_MIXER_ROUTES("AIF1TX8", "AIF1TX8"), + + ARIZONA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"), + ARIZONA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"), + ARIZONA_MIXER_ROUTES("AIF2TX3", "AIF2TX3"), + ARIZONA_MIXER_ROUTES("AIF2TX4", "AIF2TX4"), + ARIZONA_MIXER_ROUTES("AIF2TX5", "AIF2TX5"), + ARIZONA_MIXER_ROUTES("AIF2TX6", "AIF2TX6"), + + ARIZONA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"), + ARIZONA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"), + + ARIZONA_MIXER_ROUTES("EQ1", "EQ1"), + ARIZONA_MIXER_ROUTES("EQ2", "EQ2"), + + ARIZONA_MIXER_ROUTES("DRC1L", "DRC1L"), + ARIZONA_MIXER_ROUTES("DRC1R", "DRC1R"), + ARIZONA_MIXER_ROUTES("DRC2L", "DRC2L"), + ARIZONA_MIXER_ROUTES("DRC2R", "DRC2R"), + + ARIZONA_MIXER_ROUTES("LHPF1", "LHPF1"), + ARIZONA_MIXER_ROUTES("LHPF2", "LHPF2"), + ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"), + ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"), + + ARIZONA_MUX_ROUTES("ASRC1L", "ASRC1L"), + ARIZONA_MUX_ROUTES("ASRC1R", "ASRC1R"), + ARIZONA_MUX_ROUTES("ASRC2L", "ASRC2L"), + ARIZONA_MUX_ROUTES("ASRC2R", "ASRC2R"), + + ARIZONA_DSP_ROUTES("DSP2"), + ARIZONA_DSP_ROUTES("DSP3"), + + ARIZONA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"), + ARIZONA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"), + ARIZONA_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"), + ARIZONA_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"), + + ARIZONA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"), + ARIZONA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"), + ARIZONA_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"), + ARIZONA_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"), + + ARIZONA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"), + ARIZONA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"), + ARIZONA_MUX_ROUTES("ISRC2INT3", "ISRC2INT3"), + ARIZONA_MUX_ROUTES("ISRC2INT4", "ISRC2INT4"), + + ARIZONA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"), + ARIZONA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"), + ARIZONA_MUX_ROUTES("ISRC2DEC3", "ISRC2DEC3"), + ARIZONA_MUX_ROUTES("ISRC2DEC4", "ISRC2DEC4"), + + ARIZONA_MUX_ROUTES("ISRC3INT1", "ISRC3INT1"), + ARIZONA_MUX_ROUTES("ISRC3INT2", "ISRC3INT2"), + ARIZONA_MUX_ROUTES("ISRC3INT3", "ISRC3INT3"), + ARIZONA_MUX_ROUTES("ISRC3INT4", "ISRC3INT4"), + + ARIZONA_MUX_ROUTES("ISRC3DEC1", "ISRC3DEC1"), + ARIZONA_MUX_ROUTES("ISRC3DEC2", "ISRC3DEC2"), + ARIZONA_MUX_ROUTES("ISRC3DEC3", "ISRC3DEC3"), + ARIZONA_MUX_ROUTES("ISRC3DEC4", "ISRC3DEC4"), + + { "AEC Loopback", "HPOUT1L", "OUT1L" }, + { "AEC Loopback", "HPOUT1R", "OUT1R" }, + { "HPOUT1L", NULL, "OUT1L" }, + { "HPOUT1R", NULL, "OUT1R" }, + + { "AEC Loopback", "SPKOUT", "OUT4L" }, + { "SPKOUTN", NULL, "OUT4L" }, + { "SPKOUTP", NULL, "OUT4L" }, + + { "MICSUPP", NULL, "SYSCLK" }, + + { "DRC1 Signal Activity", NULL, "DRC1L" }, + { "DRC1 Signal Activity", NULL, "DRC1R" }, + { "DRC2 Signal Activity", NULL, "DRC2L" }, + { "DRC2 Signal Activity", NULL, "DRC2R" }, +}; + +static int cs47l24_set_fll(struct snd_soc_codec *codec, int fll_id, int source, + unsigned int Fref, unsigned int Fout) +{ + struct cs47l24_priv *cs47l24 = snd_soc_codec_get_drvdata(codec); + + switch (fll_id) { + case CS47L24_FLL1: + return arizona_set_fll(&cs47l24->fll[0], source, Fref, Fout); + case CS47L24_FLL2: + return arizona_set_fll(&cs47l24->fll[1], source, Fref, Fout); + case CS47L24_FLL1_REFCLK: + return arizona_set_fll_refclk(&cs47l24->fll[0], source, Fref, + Fout); + case CS47L24_FLL2_REFCLK: + return arizona_set_fll_refclk(&cs47l24->fll[1], source, Fref, + Fout); + default: + return -EINVAL; + } +} + +#define CS47L24_RATES SNDRV_PCM_RATE_8000_192000 + +#define CS47L24_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver cs47l24_dai[] = { + { + .name = "cs47l24-aif1", + .id = 1, + .base = ARIZONA_AIF1_BCLK_CTRL, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 8, + .rates = CS47L24_RATES, + .formats = CS47L24_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 8, + .rates = CS47L24_RATES, + .formats = CS47L24_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rates = 1, + .symmetric_samplebits = 1, + }, + { + .name = "cs47l24-aif2", + .id = 2, + .base = ARIZONA_AIF2_BCLK_CTRL, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 6, + .rates = CS47L24_RATES, + .formats = CS47L24_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 6, + .rates = CS47L24_RATES, + .formats = CS47L24_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rates = 1, + .symmetric_samplebits = 1, + }, + { + .name = "cs47l24-aif3", + .id = 3, + .base = ARIZONA_AIF3_BCLK_CTRL, + .playback = { + .stream_name = "AIF3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = CS47L24_RATES, + .formats = CS47L24_FORMATS, + }, + .capture = { + .stream_name = "AIF3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = CS47L24_RATES, + .formats = CS47L24_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rates = 1, + .symmetric_samplebits = 1, + }, +}; + +static int cs47l24_codec_probe(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(codec); + int ret; + + priv->core.arizona->dapm = dapm; + + arizona_init_spk(codec); + arizona_init_gpio(codec); + arizona_init_mono(codec); + + ret = wm_adsp2_codec_probe(&priv->core.adsp[1], codec); + if (ret) + goto err_adsp2_codec_probe; + + ret = wm_adsp2_codec_probe(&priv->core.adsp[2], codec); + if (ret) + goto err_adsp2_codec_probe; + + ret = snd_soc_add_codec_controls(codec, + &arizona_adsp2_rate_controls[1], 2); + if (ret) + goto err_adsp2_codec_probe; + + snd_soc_dapm_disable_pin(dapm, "HAPTICS"); + + return 0; + +err_adsp2_codec_probe: + wm_adsp2_codec_remove(&priv->core.adsp[1], codec); + wm_adsp2_codec_remove(&priv->core.adsp[2], codec); + + return ret; +} + +static int cs47l24_codec_remove(struct snd_soc_codec *codec) +{ + struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(codec); + + + wm_adsp2_codec_remove(&priv->core.adsp[1], codec); + wm_adsp2_codec_remove(&priv->core.adsp[2], codec); + + priv->core.arizona->dapm = NULL; + + return 0; +} + +#define CS47L24_DIG_VU 0x0200 + +static unsigned int cs47l24_digital_vu[] = { + ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, + ARIZONA_DAC_DIGITAL_VOLUME_4L, +}; + +static struct regmap *cs47l24_get_regmap(struct device *dev) +{ + struct cs47l24_priv *priv = dev_get_drvdata(dev); + + return priv->core.arizona->regmap; +} + +static struct snd_soc_codec_driver soc_codec_dev_cs47l24 = { + .probe = cs47l24_codec_probe, + .remove = cs47l24_codec_remove, + .get_regmap = cs47l24_get_regmap, + + .idle_bias_off = true, + + .set_sysclk = arizona_set_sysclk, + .set_pll = cs47l24_set_fll, + + .controls = cs47l24_snd_controls, + .num_controls = ARRAY_SIZE(cs47l24_snd_controls), + .dapm_widgets = cs47l24_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs47l24_dapm_widgets), + .dapm_routes = cs47l24_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(cs47l24_dapm_routes), +}; + +static int cs47l24_probe(struct platform_device *pdev) +{ + struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); + struct cs47l24_priv *cs47l24; + int i, ret; + + BUILD_BUG_ON(ARRAY_SIZE(cs47l24_dai) > ARIZONA_MAX_DAI); + + cs47l24 = devm_kzalloc(&pdev->dev, sizeof(struct cs47l24_priv), + GFP_KERNEL); + if (!cs47l24) + return -ENOMEM; + + platform_set_drvdata(pdev, cs47l24); + + cs47l24->core.arizona = arizona; + cs47l24->core.num_inputs = 4; + + for (i = 1; i <= 2; i++) { + cs47l24->core.adsp[i].part = "cs47l24"; + cs47l24->core.adsp[i].num = i + 1; + cs47l24->core.adsp[i].type = WMFW_ADSP2; + cs47l24->core.adsp[i].dev = arizona->dev; + cs47l24->core.adsp[i].regmap = arizona->regmap; + + cs47l24->core.adsp[i].base = ARIZONA_DSP1_CONTROL_1 + + (0x100 * i); + cs47l24->core.adsp[i].mem = cs47l24_dsp_regions[i - 1]; + cs47l24->core.adsp[i].num_mems = + ARRAY_SIZE(cs47l24_dsp2_regions); + + ret = wm_adsp2_init(&cs47l24->core.adsp[i]); + if (ret != 0) + return ret; + } + + for (i = 0; i < ARRAY_SIZE(cs47l24->fll); i++) + cs47l24->fll[i].vco_mult = 3; + + arizona_init_fll(arizona, 1, ARIZONA_FLL1_CONTROL_1 - 1, + ARIZONA_IRQ_FLL1_LOCK, ARIZONA_IRQ_FLL1_CLOCK_OK, + &cs47l24->fll[0]); + arizona_init_fll(arizona, 2, ARIZONA_FLL2_CONTROL_1 - 1, + ARIZONA_IRQ_FLL2_LOCK, ARIZONA_IRQ_FLL2_CLOCK_OK, + &cs47l24->fll[1]); + + /* SR2 fixed at 8kHz, SR3 fixed at 16kHz */ + regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_2, + ARIZONA_SAMPLE_RATE_2_MASK, 0x11); + regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_3, + ARIZONA_SAMPLE_RATE_3_MASK, 0x12); + + for (i = 0; i < ARRAY_SIZE(cs47l24_dai); i++) + arizona_init_dai(&cs47l24->core, i); + + /* Latch volume update bits */ + for (i = 0; i < ARRAY_SIZE(cs47l24_digital_vu); i++) + regmap_update_bits(arizona->regmap, cs47l24_digital_vu[i], + CS47L24_DIG_VU, CS47L24_DIG_VU); + + pm_runtime_enable(&pdev->dev); + pm_runtime_idle(&pdev->dev); + + return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_cs47l24, + cs47l24_dai, ARRAY_SIZE(cs47l24_dai)); +} + +static int cs47l24_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static struct platform_driver cs47l24_codec_driver = { + .driver = { + .name = "cs47l24-codec", + }, + .probe = cs47l24_probe, + .remove = cs47l24_remove, +}; + +module_platform_driver(cs47l24_codec_driver); + +MODULE_DESCRIPTION("ASoC CS47L24 driver"); +MODULE_AUTHOR("Richard Fitzgerald "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:cs47l24-codec"); diff --git a/sound/soc/codecs/cs47l24.h b/sound/soc/codecs/cs47l24.h new file mode 100644 index 000000000000..77ab2b77b2e6 --- /dev/null +++ b/sound/soc/codecs/cs47l24.h @@ -0,0 +1,23 @@ +/* + * cs47l24.h -- ALSA SoC Audio driver for Cirrus Logic CS47L24 + * + * Copyright 2015 Cirrus Logic Inc. + * + * Author: Richard Fitzgerald + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _CS47L24_H +#define _CS47L24_H + +#include "arizona.h" + +#define CS47L24_FLL1 1 +#define CS47L24_FLL2 2 +#define CS47L24_FLL1_REFCLK 3 +#define CS47L24_FLL2_REFCLK 4 + +#endif -- cgit v1.2.3 From a737447d080929c54c664adc9c62eadab9e86d3e Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 24 Oct 2015 14:28:33 +0800 Subject: ASoC: da7219: Use logical instead of bitwise OR for boolean expression Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/codecs/da7219.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index f238c1e8a69c..e36a7b79b494 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -1306,7 +1306,7 @@ static int da7219_hw_params(struct snd_pcm_substream *substream, } channels = params_channels(params); - if ((channels < 1) | (channels > DA7219_DAI_CH_NUM_MAX)) { + if ((channels < 1) || (channels > DA7219_DAI_CH_NUM_MAX)) { dev_err(codec->dev, "Invalid number of channels, only 1 to %d supported\n", DA7219_DAI_CH_NUM_MAX); -- cgit v1.2.3 From 800cff79d7ab55c5ac4b7cef3e8d6d4a23a838d5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 28 Oct 2015 16:40:18 +0100 Subject: ASoC: Fix typo in kernel doc comment for snd_soc_put_volsw_sx() Spotted by kbuild bot: sound/soc/soc-ops.c:415: warning: No description found for parameter 'ucontrol' sound/soc/soc-ops.c:415: warning: Excess function parameter 'uinfo' description in 'snd_soc_put_volsw_sx' Reported-by: kbuild test robot Signed-off-by: Takashi Iwai Signed-off-by: Mark Brown --- sound/soc/soc-ops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c index ecd38e52285a..2f67ba6d7a8f 100644 --- a/sound/soc/soc-ops.c +++ b/sound/soc/soc-ops.c @@ -404,7 +404,7 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw_sx); /** * snd_soc_put_volsw_sx - double mixer set callback * @kcontrol: mixer control - * @uinfo: control element information + * @ucontrol: control element information * * Callback to set the value of a double mixer control that spans 2 registers. * -- cgit v1.2.3 From 95f444dc9371a3910179a9621c8b94f0f60f5f04 Mon Sep 17 00:00:00 2001 From: Koro Chen Date: Wed, 28 Oct 2015 10:15:34 +0800 Subject: ASoC: dpcm: Make BE prepare possible in suspend state During suspend/resume, there is a flow that if a driver does not support SNDRV_PCM_INFO_RESUME, it will fail at snd_pcm_resume(), and user space can then issue SNDRV_PCM_IOCTL_PREPARE to let audio continue to play. However, in dpcm_be_dai_prepare() it only allows BEs to be prepared in state SND_SOC_DPCM_STATE_HW_PARAMS or SND_SOC_DPCM_STATE_STOP. The BE state will then stay in SND_SOC_DPCM_STATE_SUSPEND, consequently dpcm_be_dai_shutdown() is skipped in the end of playback and be_substream->runtime is not cleared while this runtime is actually freed by snd_pcm_detach_substream(). If another suspend comes, a NULL pointer dereference will happen in snd_pcm_suspend() when accessing BE substream's runtime. Signed-off-by: Koro Chen Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- sound/soc/soc-pcm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index c86dc96e8986..c48232211c56 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -2115,7 +2115,8 @@ int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream) continue; if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && - (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)) + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND)) continue; dev_dbg(be->dev, "ASoC: prepare BE %s\n", -- cgit v1.2.3 From 8973112aa41b8ad956a5b47f2fe17bc2a5cf2645 Mon Sep 17 00:00:00 2001 From: Zidan Wang Date: Mon, 26 Oct 2015 15:19:02 +0800 Subject: ASoC: fsl_esai: ETDR and TX0~5 registers are non volatile ETDR and TX0~5 registers are writable and not readable. So they are non volatile. Remove them from volatile list, and add default register value for them. Signed-off-by: Zidan Wang Acked-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_esai.c | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index 59f234e51971..504e7318f225 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -653,21 +653,28 @@ static const struct snd_soc_component_driver fsl_esai_component = { }; static const struct reg_default fsl_esai_reg_defaults[] = { - {0x8, 0x00000000}, - {0x10, 0x00000000}, - {0x18, 0x00000000}, - {0x98, 0x00000000}, - {0xd0, 0x00000000}, - {0xd4, 0x00000000}, - {0xd8, 0x00000000}, - {0xdc, 0x00000000}, - {0xe0, 0x00000000}, - {0xe4, 0x0000ffff}, - {0xe8, 0x0000ffff}, - {0xec, 0x0000ffff}, - {0xf0, 0x0000ffff}, - {0xf8, 0x00000000}, - {0xfc, 0x00000000}, + {REG_ESAI_ETDR, 0x00000000}, + {REG_ESAI_ECR, 0x00000000}, + {REG_ESAI_TFCR, 0x00000000}, + {REG_ESAI_RFCR, 0x00000000}, + {REG_ESAI_TX0, 0x00000000}, + {REG_ESAI_TX1, 0x00000000}, + {REG_ESAI_TX2, 0x00000000}, + {REG_ESAI_TX3, 0x00000000}, + {REG_ESAI_TX4, 0x00000000}, + {REG_ESAI_TX5, 0x00000000}, + {REG_ESAI_TSR, 0x00000000}, + {REG_ESAI_SAICR, 0x00000000}, + {REG_ESAI_TCR, 0x00000000}, + {REG_ESAI_TCCR, 0x00000000}, + {REG_ESAI_RCR, 0x00000000}, + {REG_ESAI_RCCR, 0x00000000}, + {REG_ESAI_TSMA, 0x0000ffff}, + {REG_ESAI_TSMB, 0x0000ffff}, + {REG_ESAI_RSMA, 0x0000ffff}, + {REG_ESAI_RSMB, 0x0000ffff}, + {REG_ESAI_PRRC, 0x00000000}, + {REG_ESAI_PCRC, 0x00000000}, }; static bool fsl_esai_readable_reg(struct device *dev, unsigned int reg) @@ -705,17 +712,10 @@ static bool fsl_esai_readable_reg(struct device *dev, unsigned int reg) static bool fsl_esai_volatile_reg(struct device *dev, unsigned int reg) { switch (reg) { - case REG_ESAI_ETDR: case REG_ESAI_ERDR: case REG_ESAI_ESR: case REG_ESAI_TFSR: case REG_ESAI_RFSR: - case REG_ESAI_TX0: - case REG_ESAI_TX1: - case REG_ESAI_TX2: - case REG_ESAI_TX3: - case REG_ESAI_TX4: - case REG_ESAI_TX5: case REG_ESAI_RX0: case REG_ESAI_RX1: case REG_ESAI_RX2: -- cgit v1.2.3 From 3f6f5b0cb3e3dc8fdd4eb826f30257df423b37cb Mon Sep 17 00:00:00 2001 From: Zidan Wang Date: Mon, 26 Oct 2015 15:19:03 +0800 Subject: ASoC: fsl-sai: add default register map for regmap cache FSL_SAI_TDR register is writable and not readable. According to regmap_volatile() function, if FSL_SAI_TDR want to be volatile, it should be readable. So we should remove FSL_SAI_TDR from volatile register list. If the flat cache don't have default register map, when do regcache_sync operation, the non volatile and writable registers will be synchronised to 0. FSL_SAI_TDR reigster will be written a 0 and cause channel swap. So add default register map for flat cache, and such register will not be written. Signed-off-by: Zidan Wang Acked-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index a4435f5e3be9..987fc5478abd 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -608,6 +608,22 @@ static const struct snd_soc_component_driver fsl_component = { .name = "fsl-sai", }; +static struct reg_default fsl_sai_reg_defaults[] = { + {FSL_SAI_TCR1, 0}, + {FSL_SAI_TCR2, 0}, + {FSL_SAI_TCR3, 0}, + {FSL_SAI_TCR4, 0}, + {FSL_SAI_TCR5, 0}, + {FSL_SAI_TDR, 0}, + {FSL_SAI_TMR, 0}, + {FSL_SAI_RCR1, 0}, + {FSL_SAI_RCR2, 0}, + {FSL_SAI_RCR3, 0}, + {FSL_SAI_RCR4, 0}, + {FSL_SAI_RCR5, 0}, + {FSL_SAI_RMR, 0}, +}; + static bool fsl_sai_readable_reg(struct device *dev, unsigned int reg) { switch (reg) { @@ -641,13 +657,11 @@ static bool fsl_sai_volatile_reg(struct device *dev, unsigned int reg) case FSL_SAI_RCSR: case FSL_SAI_TFR: case FSL_SAI_RFR: - case FSL_SAI_TDR: case FSL_SAI_RDR: return true; default: return false; } - } static bool fsl_sai_writeable_reg(struct device *dev, unsigned int reg) @@ -680,6 +694,8 @@ static const struct regmap_config fsl_sai_regmap_config = { .val_bits = 32, .max_register = FSL_SAI_RMR, + .reg_defaults = fsl_sai_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(fsl_sai_reg_defaults), .readable_reg = fsl_sai_readable_reg, .volatile_reg = fsl_sai_volatile_reg, .writeable_reg = fsl_sai_writeable_reg, -- cgit v1.2.3 From 9f1206dc76a726e1c7b0e2583345c29fd1e75286 Mon Sep 17 00:00:00 2001 From: Zidan Wang Date: Mon, 26 Oct 2015 15:19:04 +0800 Subject: ASoC: fsl_spdif: STL and STR registers are non volatile STL and STR registers are writable and not readable. So they are non volatile. Remove them from volatile list, and add default register value for them. Signed-off-by: Zidan Wang Acked-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_spdif.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 3d59bb6719f2..28a882336904 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -1006,12 +1006,14 @@ static const struct snd_soc_component_driver fsl_spdif_component = { /* FSL SPDIF REGMAP */ static const struct reg_default fsl_spdif_reg_defaults[] = { - {0x0, 0x00000400}, - {0x4, 0x00000000}, - {0xc, 0x00000000}, - {0x34, 0x00000000}, - {0x38, 0x00000000}, - {0x50, 0x00020f00}, + {REG_SPDIF_SCR, 0x00000400}, + {REG_SPDIF_SRCD, 0x00000000}, + {REG_SPDIF_SIE, 0x00000000}, + {REG_SPDIF_STL, 0x00000000}, + {REG_SPDIF_STR, 0x00000000}, + {REG_SPDIF_STCSCH, 0x00000000}, + {REG_SPDIF_STCSCL, 0x00000000}, + {REG_SPDIF_STC, 0x00020f00}, }; static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg) @@ -1049,8 +1051,6 @@ static bool fsl_spdif_volatile_reg(struct device *dev, unsigned int reg) case REG_SPDIF_SRCSL: case REG_SPDIF_SRU: case REG_SPDIF_SRQ: - case REG_SPDIF_STL: - case REG_SPDIF_STR: case REG_SPDIF_SRFM: return true; default: -- cgit v1.2.3 From f4faa29e5d134fdff00403936ab10fea7683913e Mon Sep 17 00:00:00 2001 From: Zidan Wang Date: Mon, 26 Oct 2015 15:19:05 +0800 Subject: ASoC: fsl_ssi: using macro for default register map using macro for default register map Signed-off-by: Zidan Wang Acked-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_ssi.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 95d2392303eb..674abf778715 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -113,17 +113,17 @@ struct fsl_ssi_rxtx_reg_val { }; static const struct reg_default fsl_ssi_reg_defaults[] = { - {0x10, 0x00000000}, - {0x18, 0x00003003}, - {0x1c, 0x00000200}, - {0x20, 0x00000200}, - {0x24, 0x00040000}, - {0x28, 0x00040000}, - {0x38, 0x00000000}, - {0x48, 0x00000000}, - {0x4c, 0x00000000}, - {0x54, 0x00000000}, - {0x58, 0x00000000}, + {CCSR_SSI_SCR, 0x00000000}, + {CCSR_SSI_SIER, 0x00003003}, + {CCSR_SSI_STCR, 0x00000200}, + {CCSR_SSI_SRCR, 0x00000200}, + {CCSR_SSI_STCCR, 0x00040000}, + {CCSR_SSI_SRCCR, 0x00040000}, + {CCSR_SSI_SACNT, 0x00000000}, + {CCSR_SSI_STMSK, 0x00000000}, + {CCSR_SSI_SRMSK, 0x00000000}, + {CCSR_SSI_SACCEN, 0x00000000}, + {CCSR_SSI_SACCDIS, 0x00000000}, }; static bool fsl_ssi_readable_reg(struct device *dev, unsigned int reg) -- cgit v1.2.3 From 14b947d9ced4f723b5bfd3f6ec614aa28b5d4cfb Mon Sep 17 00:00:00 2001 From: "Damien.Horsley" Date: Wed, 4 Nov 2015 14:40:48 +0000 Subject: ASoC: img: Add driver for I2S input controller Add driver for Imagination Technologies I2S input controller Signed-off-by: Damien.Horsley Signed-off-by: Mark Brown --- sound/soc/Kconfig | 1 + sound/soc/Makefile | 1 + sound/soc/img/Kconfig | 12 ++ sound/soc/img/Makefile | 1 + sound/soc/img/img-i2s-in.c | 516 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 531 insertions(+) create mode 100644 sound/soc/img/Kconfig create mode 100644 sound/soc/img/Makefile create mode 100644 sound/soc/img/img-i2s-in.c (limited to 'sound') diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 7ff7d88e46dd..a012b2655e84 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -50,6 +50,7 @@ source "sound/soc/jz4740/Kconfig" source "sound/soc/nuc900/Kconfig" source "sound/soc/omap/Kconfig" source "sound/soc/kirkwood/Kconfig" +source "sound/soc/img/Kconfig" source "sound/soc/intel/Kconfig" source "sound/soc/mediatek/Kconfig" source "sound/soc/mxs/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 8eb06db32fa0..78625fae78d6 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_SND_SOC) += davinci/ obj-$(CONFIG_SND_SOC) += dwc/ obj-$(CONFIG_SND_SOC) += fsl/ obj-$(CONFIG_SND_SOC) += jz4740/ +obj-$(CONFIG_SND_SOC) += img/ obj-$(CONFIG_SND_SOC) += intel/ obj-$(CONFIG_SND_SOC) += mediatek/ obj-$(CONFIG_SND_SOC) += mxs/ diff --git a/sound/soc/img/Kconfig b/sound/soc/img/Kconfig new file mode 100644 index 000000000000..f9f73d0323f1 --- /dev/null +++ b/sound/soc/img/Kconfig @@ -0,0 +1,12 @@ +config SND_SOC_IMG + bool "Audio support for Imagination Technologies designs" + help + Audio support for Imagination Technologies audio hardware + +config SND_SOC_IMG_I2S_IN + tristate "Imagination I2S Input Device Driver" + depends on SND_SOC_IMG + select SND_SOC_GENERIC_DMAENGINE_PCM + help + Say Y or M if you want to add support for I2S in driver for + Imagination Technologies I2S in device. diff --git a/sound/soc/img/Makefile b/sound/soc/img/Makefile new file mode 100644 index 000000000000..fe8426b369c2 --- /dev/null +++ b/sound/soc/img/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_SND_SOC_IMG_I2S_IN) += img-i2s-in.o diff --git a/sound/soc/img/img-i2s-in.c b/sound/soc/img/img-i2s-in.c new file mode 100644 index 000000000000..0389203f8560 --- /dev/null +++ b/sound/soc/img/img-i2s-in.c @@ -0,0 +1,516 @@ +/* + * IMG I2S input controller driver + * + * Copyright (C) 2015 Imagination Technologies Ltd. + * + * Author: Damien Horsley + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define IMG_I2S_IN_RX_FIFO 0x0 + +#define IMG_I2S_IN_CTL 0x4 +#define IMG_I2S_IN_CTL_ACTIVE_CHAN_MASK 0xfffffffc +#define IMG_I2S_IN_CTL_ACTIVE_CH_SHIFT 2 +#define IMG_I2S_IN_CTL_16PACK_MASK BIT(1) +#define IMG_I2S_IN_CTL_ME_MASK BIT(0) + +#define IMG_I2S_IN_CH_CTL 0x4 +#define IMG_I2S_IN_CH_CTL_CCDEL_MASK 0x38000 +#define IMG_I2S_IN_CH_CTL_CCDEL_SHIFT 15 +#define IMG_I2S_IN_CH_CTL_FEN_MASK BIT(14) +#define IMG_I2S_IN_CH_CTL_FMODE_MASK BIT(13) +#define IMG_I2S_IN_CH_CTL_16PACK_MASK BIT(12) +#define IMG_I2S_IN_CH_CTL_JUST_MASK BIT(10) +#define IMG_I2S_IN_CH_CTL_PACKH_MASK BIT(9) +#define IMG_I2S_IN_CH_CTL_CLK_TRANS_MASK BIT(8) +#define IMG_I2S_IN_CH_CTL_BLKP_MASK BIT(7) +#define IMG_I2S_IN_CH_CTL_FIFO_FLUSH_MASK BIT(6) +#define IMG_I2S_IN_CH_CTL_LRD_MASK BIT(3) +#define IMG_I2S_IN_CH_CTL_FW_MASK BIT(2) +#define IMG_I2S_IN_CH_CTL_SW_MASK BIT(1) +#define IMG_I2S_IN_CH_CTL_ME_MASK BIT(0) + +#define IMG_I2S_IN_CH_STRIDE 0x20 + +struct img_i2s_in { + void __iomem *base; + struct clk *clk_sys; + struct snd_dmaengine_dai_dma_data dma_data; + struct device *dev; + unsigned int max_i2s_chan; + void __iomem *channel_base; + unsigned int active_channels; + struct snd_soc_dai_driver dai_driver; +}; + +static inline void img_i2s_in_writel(struct img_i2s_in *i2s, u32 val, u32 reg) +{ + writel(val, i2s->base + reg); +} + +static inline u32 img_i2s_in_readl(struct img_i2s_in *i2s, u32 reg) +{ + return readl(i2s->base + reg); +} + +static inline void img_i2s_in_ch_writel(struct img_i2s_in *i2s, u32 chan, + u32 val, u32 reg) +{ + writel(val, i2s->channel_base + (chan * IMG_I2S_IN_CH_STRIDE) + reg); +} + +static inline u32 img_i2s_in_ch_readl(struct img_i2s_in *i2s, u32 chan, + u32 reg) +{ + return readl(i2s->channel_base + (chan * IMG_I2S_IN_CH_STRIDE) + reg); +} + +static inline void img_i2s_in_ch_disable(struct img_i2s_in *i2s, u32 chan) +{ + u32 reg; + + reg = img_i2s_in_ch_readl(i2s, chan, IMG_I2S_IN_CH_CTL); + reg &= ~IMG_I2S_IN_CH_CTL_ME_MASK; + img_i2s_in_ch_writel(i2s, chan, reg, IMG_I2S_IN_CH_CTL); +} + +static inline void img_i2s_in_ch_enable(struct img_i2s_in *i2s, u32 chan) +{ + u32 reg; + + reg = img_i2s_in_ch_readl(i2s, chan, IMG_I2S_IN_CH_CTL); + reg |= IMG_I2S_IN_CH_CTL_ME_MASK; + img_i2s_in_ch_writel(i2s, chan, reg, IMG_I2S_IN_CH_CTL); +} + +static inline void img_i2s_in_disable(struct img_i2s_in *i2s) +{ + u32 reg; + + reg = img_i2s_in_readl(i2s, IMG_I2S_IN_CTL); + reg &= ~IMG_I2S_IN_CTL_ME_MASK; + img_i2s_in_writel(i2s, reg, IMG_I2S_IN_CTL); +} + +static inline void img_i2s_in_enable(struct img_i2s_in *i2s) +{ + u32 reg; + + reg = img_i2s_in_readl(i2s, IMG_I2S_IN_CTL); + reg |= IMG_I2S_IN_CTL_ME_MASK; + img_i2s_in_writel(i2s, reg, IMG_I2S_IN_CTL); +} + +static inline void img_i2s_in_flush(struct img_i2s_in *i2s) +{ + int i; + u32 reg; + + for (i = 0; i < i2s->active_channels; i++) { + reg = img_i2s_in_ch_readl(i2s, i, IMG_I2S_IN_CH_CTL); + reg |= IMG_I2S_IN_CH_CTL_FIFO_FLUSH_MASK; + img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL); + reg &= ~IMG_I2S_IN_CH_CTL_FIFO_FLUSH_MASK; + img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL); + } +} + +static int img_i2s_in_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + img_i2s_in_enable(i2s); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + img_i2s_in_disable(i2s); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int img_i2s_in_check_rate(struct img_i2s_in *i2s, + unsigned int sample_rate, unsigned int frame_size, + unsigned int *bclk_filter_enable, + unsigned int *bclk_filter_value) +{ + unsigned int bclk_freq, cur_freq; + + bclk_freq = sample_rate * frame_size; + + cur_freq = clk_get_rate(i2s->clk_sys); + + if (cur_freq >= bclk_freq * 8) { + *bclk_filter_enable = 1; + *bclk_filter_value = 0; + } else if (cur_freq >= bclk_freq * 7) { + *bclk_filter_enable = 1; + *bclk_filter_value = 1; + } else if (cur_freq >= bclk_freq * 6) { + *bclk_filter_enable = 0; + *bclk_filter_value = 0; + } else { + dev_err(i2s->dev, + "Sys clock rate %u insufficient for sample rate %u\n", + cur_freq, sample_rate); + return -EINVAL; + } + + return 0; +} + +static int img_i2s_in_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai); + unsigned int rate, channels, i2s_channels, frame_size; + unsigned int bclk_filter_enable, bclk_filter_value; + int i, ret = 0; + u32 reg, control_mask, chan_control_mask; + u32 control_set = 0, chan_control_set = 0; + snd_pcm_format_t format; + + rate = params_rate(params); + format = params_format(params); + channels = params_channels(params); + i2s_channels = channels / 2; + + switch (format) { + case SNDRV_PCM_FORMAT_S32_LE: + frame_size = 64; + chan_control_set |= IMG_I2S_IN_CH_CTL_SW_MASK; + chan_control_set |= IMG_I2S_IN_CH_CTL_FW_MASK; + chan_control_set |= IMG_I2S_IN_CH_CTL_PACKH_MASK; + break; + case SNDRV_PCM_FORMAT_S24_LE: + frame_size = 64; + chan_control_set |= IMG_I2S_IN_CH_CTL_SW_MASK; + chan_control_set |= IMG_I2S_IN_CH_CTL_FW_MASK; + break; + case SNDRV_PCM_FORMAT_S16_LE: + frame_size = 32; + control_set |= IMG_I2S_IN_CTL_16PACK_MASK; + chan_control_set |= IMG_I2S_IN_CH_CTL_16PACK_MASK; + break; + default: + return -EINVAL; + } + + if ((channels < 2) || + (channels > (i2s->max_i2s_chan * 2)) || + (channels % 2)) + return -EINVAL; + + control_set |= ((i2s_channels - 1) << IMG_I2S_IN_CTL_ACTIVE_CH_SHIFT); + + ret = img_i2s_in_check_rate(i2s, rate, frame_size, + &bclk_filter_enable, &bclk_filter_value); + if (ret < 0) + return ret; + + if (bclk_filter_enable) + chan_control_set |= IMG_I2S_IN_CH_CTL_FEN_MASK; + + if (bclk_filter_value) + chan_control_set |= IMG_I2S_IN_CH_CTL_FMODE_MASK; + + control_mask = IMG_I2S_IN_CTL_16PACK_MASK | + IMG_I2S_IN_CTL_ACTIVE_CHAN_MASK; + + chan_control_mask = IMG_I2S_IN_CH_CTL_16PACK_MASK | + IMG_I2S_IN_CH_CTL_FEN_MASK | + IMG_I2S_IN_CH_CTL_FMODE_MASK | + IMG_I2S_IN_CH_CTL_SW_MASK | + IMG_I2S_IN_CH_CTL_FW_MASK | + IMG_I2S_IN_CH_CTL_PACKH_MASK; + + reg = img_i2s_in_readl(i2s, IMG_I2S_IN_CTL); + reg = (reg & ~control_mask) | control_set; + img_i2s_in_writel(i2s, reg, IMG_I2S_IN_CTL); + + for (i = 0; i < i2s->active_channels; i++) + img_i2s_in_ch_disable(i2s, i); + + for (i = 0; i < i2s->max_i2s_chan; i++) { + reg = img_i2s_in_ch_readl(i2s, i, IMG_I2S_IN_CH_CTL); + reg = (reg & ~chan_control_mask) | chan_control_set; + img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL); + } + + i2s->active_channels = i2s_channels; + + img_i2s_in_flush(i2s); + + for (i = 0; i < i2s->active_channels; i++) + img_i2s_in_ch_enable(i2s, i); + + return 0; +} + +static int img_i2s_in_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai); + int i; + u32 chan_control_mask, lrd_set = 0, blkp_set = 0, chan_control_set = 0; + u32 reg; + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + lrd_set |= IMG_I2S_IN_CH_CTL_LRD_MASK; + break; + case SND_SOC_DAIFMT_NB_IF: + break; + case SND_SOC_DAIFMT_IB_NF: + lrd_set |= IMG_I2S_IN_CH_CTL_LRD_MASK; + blkp_set |= IMG_I2S_IN_CH_CTL_BLKP_MASK; + break; + case SND_SOC_DAIFMT_IB_IF: + blkp_set |= IMG_I2S_IN_CH_CTL_BLKP_MASK; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + chan_control_set |= IMG_I2S_IN_CH_CTL_CLK_TRANS_MASK; + break; + case SND_SOC_DAIFMT_LEFT_J: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + break; + default: + return -EINVAL; + } + + chan_control_mask = IMG_I2S_IN_CH_CTL_CLK_TRANS_MASK; + + for (i = 0; i < i2s->active_channels; i++) + img_i2s_in_ch_disable(i2s, i); + + /* + * BLKP and LRD must be set during separate register writes + */ + for (i = 0; i < i2s->max_i2s_chan; i++) { + reg = img_i2s_in_ch_readl(i2s, i, IMG_I2S_IN_CH_CTL); + reg = (reg & ~chan_control_mask) | chan_control_set; + img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL); + reg = (reg & ~IMG_I2S_IN_CH_CTL_BLKP_MASK) | blkp_set; + img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL); + reg = (reg & ~IMG_I2S_IN_CH_CTL_LRD_MASK) | lrd_set; + img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL); + } + + for (i = 0; i < i2s->active_channels; i++) + img_i2s_in_ch_enable(i2s, i); + + return 0; +} + +static const struct snd_soc_dai_ops img_i2s_in_dai_ops = { + .trigger = img_i2s_in_trigger, + .hw_params = img_i2s_in_hw_params, + .set_fmt = img_i2s_in_set_fmt +}; + +static int img_i2s_in_dai_probe(struct snd_soc_dai *dai) +{ + struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, NULL, &i2s->dma_data); + + return 0; +} + +static const struct snd_soc_component_driver img_i2s_in_component = { + .name = "img-i2s-in" +}; + +static int img_i2s_in_dma_prepare_slave_config(struct snd_pcm_substream *st, + struct snd_pcm_hw_params *params, struct dma_slave_config *sc) +{ + unsigned int i2s_channels = params_channels(params) / 2; + struct snd_soc_pcm_runtime *rtd = st->private_data; + struct snd_dmaengine_dai_dma_data *dma_data; + int ret; + + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, st); + + ret = snd_hwparams_to_dma_slave_config(st, params, sc); + if (ret) + return ret; + + sc->src_addr = dma_data->addr; + sc->src_addr_width = dma_data->addr_width; + sc->src_maxburst = 4 * i2s_channels; + + return 0; +} + +static const struct snd_dmaengine_pcm_config img_i2s_in_dma_config = { + .prepare_slave_config = img_i2s_in_dma_prepare_slave_config +}; + +static int img_i2s_in_probe(struct platform_device *pdev) +{ + struct img_i2s_in *i2s; + struct resource *res; + void __iomem *base; + int ret, i; + struct reset_control *rst; + unsigned int max_i2s_chan_pow_2; + struct device *dev = &pdev->dev; + + i2s = devm_kzalloc(dev, sizeof(*i2s), GFP_KERNEL); + if (!i2s) + return -ENOMEM; + + platform_set_drvdata(pdev, i2s); + + i2s->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + i2s->base = base; + + if (of_property_read_u32(pdev->dev.of_node, "img,i2s-channels", + &i2s->max_i2s_chan)) { + dev_err(dev, "No img,i2s-channels property\n"); + return -EINVAL; + } + + max_i2s_chan_pow_2 = 1 << get_count_order(i2s->max_i2s_chan); + + i2s->channel_base = base + (max_i2s_chan_pow_2 * 0x20); + + i2s->clk_sys = devm_clk_get(dev, "sys"); + if (IS_ERR(i2s->clk_sys)) { + if (PTR_ERR(i2s->clk_sys) != -EPROBE_DEFER) + dev_err(dev, "Failed to acquire clock 'sys'\n"); + return PTR_ERR(i2s->clk_sys); + } + + ret = clk_prepare_enable(i2s->clk_sys); + if (ret) + return ret; + + i2s->active_channels = 1; + i2s->dma_data.addr = res->start + IMG_I2S_IN_RX_FIFO; + i2s->dma_data.addr_width = 4; + + i2s->dai_driver.probe = img_i2s_in_dai_probe; + i2s->dai_driver.capture.channels_min = 2; + i2s->dai_driver.capture.channels_max = i2s->max_i2s_chan * 2; + i2s->dai_driver.capture.rates = SNDRV_PCM_RATE_8000_192000; + i2s->dai_driver.capture.formats = SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE; + i2s->dai_driver.ops = &img_i2s_in_dai_ops; + + rst = devm_reset_control_get(dev, "rst"); + if (IS_ERR(rst)) { + if (PTR_ERR(rst) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto err_clk_disable; + } + + dev_dbg(dev, "No top level reset found\n"); + + img_i2s_in_disable(i2s); + + for (i = 0; i < i2s->max_i2s_chan; i++) + img_i2s_in_ch_disable(i2s, i); + } else { + reset_control_assert(rst); + reset_control_deassert(rst); + } + + img_i2s_in_writel(i2s, 0, IMG_I2S_IN_CTL); + + for (i = 0; i < i2s->max_i2s_chan; i++) + img_i2s_in_ch_writel(i2s, i, + (4 << IMG_I2S_IN_CH_CTL_CCDEL_SHIFT) | + IMG_I2S_IN_CH_CTL_JUST_MASK | + IMG_I2S_IN_CH_CTL_FW_MASK, IMG_I2S_IN_CH_CTL); + + ret = devm_snd_soc_register_component(dev, &img_i2s_in_component, + &i2s->dai_driver, 1); + if (ret) + goto err_clk_disable; + + ret = devm_snd_dmaengine_pcm_register(dev, &img_i2s_in_dma_config, 0); + if (ret) + goto err_clk_disable; + + return 0; + +err_clk_disable: + clk_disable_unprepare(i2s->clk_sys); + + return ret; +} + +static int img_i2s_in_dev_remove(struct platform_device *pdev) +{ + struct img_i2s_in *i2s = platform_get_drvdata(pdev); + + clk_disable_unprepare(i2s->clk_sys); + + return 0; +} + +static const struct of_device_id img_i2s_in_of_match[] = { + { .compatible = "img,i2s-in" }, + {} +}; +MODULE_DEVICE_TABLE(of, img_i2s_in_of_match); + +static struct platform_driver img_i2s_in_driver = { + .driver = { + .name = "img-i2s-in", + .of_match_table = img_i2s_in_of_match + }, + .probe = img_i2s_in_probe, + .remove = img_i2s_in_dev_remove +}; +module_platform_driver(img_i2s_in_driver); + +MODULE_AUTHOR("Damien Horsley "); +MODULE_DESCRIPTION("IMG I2S Input Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From d0e3992c939cb146a0de9e7c74a227e8be4629a9 Mon Sep 17 00:00:00 2001 From: "Damien.Horsley" Date: Wed, 4 Nov 2015 14:40:50 +0000 Subject: ASoC: img: Add driver for I2S output controller Add driver for Imagination Technologies I2S output controller Signed-off-by: Damien.Horsley Signed-off-by: Mark Brown --- sound/soc/img/Kconfig | 8 + sound/soc/img/Makefile | 1 + sound/soc/img/img-i2s-out.c | 565 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 574 insertions(+) create mode 100644 sound/soc/img/img-i2s-out.c (limited to 'sound') diff --git a/sound/soc/img/Kconfig b/sound/soc/img/Kconfig index f9f73d0323f1..fe83c8e344b2 100644 --- a/sound/soc/img/Kconfig +++ b/sound/soc/img/Kconfig @@ -10,3 +10,11 @@ config SND_SOC_IMG_I2S_IN help Say Y or M if you want to add support for I2S in driver for Imagination Technologies I2S in device. + +config SND_SOC_IMG_I2S_OUT + tristate "Imagination I2S Output Device Driver" + depends on SND_SOC_IMG + select SND_SOC_GENERIC_DMAENGINE_PCM + help + Say Y or M if you want to add support for I2S out driver for + Imagination Technologies I2S out device. diff --git a/sound/soc/img/Makefile b/sound/soc/img/Makefile index fe8426b369c2..c41a4af8dd89 100644 --- a/sound/soc/img/Makefile +++ b/sound/soc/img/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_SND_SOC_IMG_I2S_IN) += img-i2s-in.o +obj-$(CONFIG_SND_SOC_IMG_I2S_OUT) += img-i2s-out.o diff --git a/sound/soc/img/img-i2s-out.c b/sound/soc/img/img-i2s-out.c new file mode 100644 index 000000000000..5f997135a8ae --- /dev/null +++ b/sound/soc/img/img-i2s-out.c @@ -0,0 +1,565 @@ +/* + * IMG I2S output controller driver + * + * Copyright (C) 2015 Imagination Technologies Ltd. + * + * Author: Damien Horsley + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define IMG_I2S_OUT_TX_FIFO 0x0 + +#define IMG_I2S_OUT_CTL 0x4 +#define IMG_I2S_OUT_CTL_DATA_EN_MASK BIT(24) +#define IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK 0xffe000 +#define IMG_I2S_OUT_CTL_ACTIVE_CHAN_SHIFT 13 +#define IMG_I2S_OUT_CTL_FRM_SIZE_MASK BIT(8) +#define IMG_I2S_OUT_CTL_MASTER_MASK BIT(6) +#define IMG_I2S_OUT_CTL_CLK_MASK BIT(5) +#define IMG_I2S_OUT_CTL_CLK_EN_MASK BIT(4) +#define IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK BIT(3) +#define IMG_I2S_OUT_CTL_BCLK_POL_MASK BIT(2) +#define IMG_I2S_OUT_CTL_ME_MASK BIT(0) + +#define IMG_I2S_OUT_CH_CTL 0x4 +#define IMG_I2S_OUT_CHAN_CTL_CH_MASK BIT(11) +#define IMG_I2S_OUT_CHAN_CTL_LT_MASK BIT(10) +#define IMG_I2S_OUT_CHAN_CTL_FMT_MASK 0xf0 +#define IMG_I2S_OUT_CHAN_CTL_FMT_SHIFT 4 +#define IMG_I2S_OUT_CHAN_CTL_JUST_MASK BIT(3) +#define IMG_I2S_OUT_CHAN_CTL_CLKT_MASK BIT(1) +#define IMG_I2S_OUT_CHAN_CTL_ME_MASK BIT(0) + +#define IMG_I2S_OUT_CH_STRIDE 0x20 + +struct img_i2s_out { + void __iomem *base; + struct clk *clk_sys; + struct clk *clk_ref; + struct snd_dmaengine_dai_dma_data dma_data; + struct device *dev; + unsigned int max_i2s_chan; + void __iomem *channel_base; + bool force_clk_active; + unsigned int active_channels; + struct reset_control *rst; + struct snd_soc_dai_driver dai_driver; +}; + +static int img_i2s_out_suspend(struct device *dev) +{ + struct img_i2s_out *i2s = dev_get_drvdata(dev); + + if (!i2s->force_clk_active) + clk_disable_unprepare(i2s->clk_ref); + + return 0; +} + +static int img_i2s_out_resume(struct device *dev) +{ + struct img_i2s_out *i2s = dev_get_drvdata(dev); + int ret; + + if (!i2s->force_clk_active) { + ret = clk_prepare_enable(i2s->clk_ref); + if (ret) { + dev_err(dev, "clk_enable failed: %d\n", ret); + return ret; + } + } + + return 0; +} + +static inline void img_i2s_out_writel(struct img_i2s_out *i2s, u32 val, + u32 reg) +{ + writel(val, i2s->base + reg); +} + +static inline u32 img_i2s_out_readl(struct img_i2s_out *i2s, u32 reg) +{ + return readl(i2s->base + reg); +} + +static inline void img_i2s_out_ch_writel(struct img_i2s_out *i2s, + u32 chan, u32 val, u32 reg) +{ + writel(val, i2s->channel_base + (chan * IMG_I2S_OUT_CH_STRIDE) + reg); +} + +static inline u32 img_i2s_out_ch_readl(struct img_i2s_out *i2s, u32 chan, + u32 reg) +{ + return readl(i2s->channel_base + (chan * IMG_I2S_OUT_CH_STRIDE) + reg); +} + +static inline void img_i2s_out_ch_disable(struct img_i2s_out *i2s, u32 chan) +{ + u32 reg; + + reg = img_i2s_out_ch_readl(i2s, chan, IMG_I2S_OUT_CH_CTL); + reg &= ~IMG_I2S_OUT_CHAN_CTL_ME_MASK; + img_i2s_out_ch_writel(i2s, chan, reg, IMG_I2S_OUT_CH_CTL); +} + +static inline void img_i2s_out_ch_enable(struct img_i2s_out *i2s, u32 chan) +{ + u32 reg; + + reg = img_i2s_out_ch_readl(i2s, chan, IMG_I2S_OUT_CH_CTL); + reg |= IMG_I2S_OUT_CHAN_CTL_ME_MASK; + img_i2s_out_ch_writel(i2s, chan, reg, IMG_I2S_OUT_CH_CTL); +} + +static inline void img_i2s_out_disable(struct img_i2s_out *i2s) +{ + u32 reg; + + reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL); + reg &= ~IMG_I2S_OUT_CTL_ME_MASK; + img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL); +} + +static inline void img_i2s_out_enable(struct img_i2s_out *i2s) +{ + u32 reg; + + reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL); + reg |= IMG_I2S_OUT_CTL_ME_MASK; + img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL); +} + +static void img_i2s_out_reset(struct img_i2s_out *i2s) +{ + int i; + u32 core_ctl, chan_ctl; + + core_ctl = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL) & + ~IMG_I2S_OUT_CTL_ME_MASK & + ~IMG_I2S_OUT_CTL_DATA_EN_MASK; + + if (!i2s->force_clk_active) + core_ctl &= ~IMG_I2S_OUT_CTL_CLK_EN_MASK; + + chan_ctl = img_i2s_out_ch_readl(i2s, 0, IMG_I2S_OUT_CH_CTL) & + ~IMG_I2S_OUT_CHAN_CTL_ME_MASK; + + reset_control_assert(i2s->rst); + reset_control_deassert(i2s->rst); + + for (i = 0; i < i2s->max_i2s_chan; i++) + img_i2s_out_ch_writel(i2s, i, chan_ctl, IMG_I2S_OUT_CH_CTL); + + for (i = 0; i < i2s->active_channels; i++) + img_i2s_out_ch_enable(i2s, i); + + img_i2s_out_writel(i2s, core_ctl, IMG_I2S_OUT_CTL); + img_i2s_out_enable(i2s); +} + +static int img_i2s_out_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai); + u32 reg; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL); + if (!i2s->force_clk_active) + reg |= IMG_I2S_OUT_CTL_CLK_EN_MASK; + reg |= IMG_I2S_OUT_CTL_DATA_EN_MASK; + img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + img_i2s_out_reset(i2s); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int img_i2s_out_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai); + unsigned int channels, i2s_channels; + long pre_div_a, pre_div_b, diff_a, diff_b, rate, clk_rate; + int i; + u32 reg, control_mask, control_set = 0; + snd_pcm_format_t format; + + rate = params_rate(params); + format = params_format(params); + channels = params_channels(params); + i2s_channels = channels / 2; + + if (format != SNDRV_PCM_FORMAT_S32_LE) + return -EINVAL; + + if ((channels < 2) || + (channels > (i2s->max_i2s_chan * 2)) || + (channels % 2)) + return -EINVAL; + + pre_div_a = clk_round_rate(i2s->clk_ref, rate * 256); + if (pre_div_a < 0) + return pre_div_a; + pre_div_b = clk_round_rate(i2s->clk_ref, rate * 384); + if (pre_div_b < 0) + return pre_div_b; + + diff_a = abs((pre_div_a / 256) - rate); + diff_b = abs((pre_div_b / 384) - rate); + + /* If diffs are equal, use lower clock rate */ + if (diff_a > diff_b) + clk_set_rate(i2s->clk_ref, pre_div_b); + else + clk_set_rate(i2s->clk_ref, pre_div_a); + + /* + * Another driver (eg alsa machine driver) may have rejected the above + * change. Get the current rate and set the register bit according to + * the new minimum diff + */ + clk_rate = clk_get_rate(i2s->clk_ref); + + diff_a = abs((clk_rate / 256) - rate); + diff_b = abs((clk_rate / 384) - rate); + + if (diff_a > diff_b) + control_set |= IMG_I2S_OUT_CTL_CLK_MASK; + + control_set |= ((i2s_channels - 1) << + IMG_I2S_OUT_CTL_ACTIVE_CHAN_SHIFT) & + IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK; + + control_mask = IMG_I2S_OUT_CTL_CLK_MASK | + IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK; + + img_i2s_out_disable(i2s); + + reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL); + reg = (reg & ~control_mask) | control_set; + img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL); + + for (i = 0; i < i2s_channels; i++) + img_i2s_out_ch_enable(i2s, i); + + for (; i < i2s->max_i2s_chan; i++) + img_i2s_out_ch_disable(i2s, i); + + img_i2s_out_enable(i2s); + + i2s->active_channels = i2s_channels; + + return 0; +} + +static int img_i2s_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai); + int i; + bool force_clk_active; + u32 chan_control_mask, control_mask, chan_control_set = 0; + u32 reg, control_set = 0; + + force_clk_active = ((fmt & SND_SOC_DAIFMT_CLOCK_MASK) == + SND_SOC_DAIFMT_CONT); + + if (force_clk_active) + control_set |= IMG_I2S_OUT_CTL_CLK_EN_MASK; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + break; + case SND_SOC_DAIFMT_CBS_CFS: + control_set |= IMG_I2S_OUT_CTL_MASTER_MASK; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + control_set |= IMG_I2S_OUT_CTL_BCLK_POL_MASK; + break; + case SND_SOC_DAIFMT_NB_IF: + control_set |= IMG_I2S_OUT_CTL_BCLK_POL_MASK; + control_set |= IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK; + break; + case SND_SOC_DAIFMT_IB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + control_set |= IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + chan_control_set |= IMG_I2S_OUT_CHAN_CTL_CLKT_MASK; + break; + case SND_SOC_DAIFMT_LEFT_J: + break; + default: + return -EINVAL; + } + + control_mask = IMG_I2S_OUT_CTL_CLK_EN_MASK | + IMG_I2S_OUT_CTL_MASTER_MASK | + IMG_I2S_OUT_CTL_BCLK_POL_MASK | + IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK; + + chan_control_mask = IMG_I2S_OUT_CHAN_CTL_CLKT_MASK; + + img_i2s_out_disable(i2s); + + reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL); + reg = (reg & ~control_mask) | control_set; + img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL); + + for (i = 0; i < i2s->active_channels; i++) + img_i2s_out_ch_disable(i2s, i); + + for (i = 0; i < i2s->max_i2s_chan; i++) { + reg = img_i2s_out_ch_readl(i2s, i, IMG_I2S_OUT_CH_CTL); + reg = (reg & ~chan_control_mask) | chan_control_set; + img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL); + } + + for (i = 0; i < i2s->active_channels; i++) + img_i2s_out_ch_enable(i2s, i); + + img_i2s_out_enable(i2s); + + i2s->force_clk_active = force_clk_active; + + return 0; +} + +static const struct snd_soc_dai_ops img_i2s_out_dai_ops = { + .trigger = img_i2s_out_trigger, + .hw_params = img_i2s_out_hw_params, + .set_fmt = img_i2s_out_set_fmt +}; + +static int img_i2s_out_dai_probe(struct snd_soc_dai *dai) +{ + struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, &i2s->dma_data, NULL); + + return 0; +} + +static const struct snd_soc_component_driver img_i2s_out_component = { + .name = "img-i2s-out" +}; + +static int img_i2s_out_dma_prepare_slave_config(struct snd_pcm_substream *st, + struct snd_pcm_hw_params *params, struct dma_slave_config *sc) +{ + unsigned int i2s_channels = params_channels(params) / 2; + struct snd_soc_pcm_runtime *rtd = st->private_data; + struct snd_dmaengine_dai_dma_data *dma_data; + int ret; + + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, st); + + ret = snd_hwparams_to_dma_slave_config(st, params, sc); + if (ret) + return ret; + + sc->dst_addr = dma_data->addr; + sc->dst_addr_width = dma_data->addr_width; + sc->dst_maxburst = 4 * i2s_channels; + + return 0; +} + +static const struct snd_dmaengine_pcm_config img_i2s_out_dma_config = { + .prepare_slave_config = img_i2s_out_dma_prepare_slave_config +}; + +static int img_i2s_out_probe(struct platform_device *pdev) +{ + struct img_i2s_out *i2s; + struct resource *res; + void __iomem *base; + int i, ret; + unsigned int max_i2s_chan_pow_2; + u32 reg; + struct device *dev = &pdev->dev; + + i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); + if (!i2s) + return -ENOMEM; + + platform_set_drvdata(pdev, i2s); + + i2s->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + i2s->base = base; + + if (of_property_read_u32(pdev->dev.of_node, "img,i2s-channels", + &i2s->max_i2s_chan)) { + dev_err(&pdev->dev, "No img,i2s-channels property\n"); + return -EINVAL; + } + + max_i2s_chan_pow_2 = 1 << get_count_order(i2s->max_i2s_chan); + + i2s->channel_base = base + (max_i2s_chan_pow_2 * 0x20); + + i2s->rst = devm_reset_control_get(&pdev->dev, "rst"); + if (IS_ERR(i2s->rst)) { + if (PTR_ERR(i2s->rst) != -EPROBE_DEFER) + dev_err(&pdev->dev, "No top level reset found\n"); + return PTR_ERR(i2s->rst); + } + + i2s->clk_sys = devm_clk_get(&pdev->dev, "sys"); + if (IS_ERR(i2s->clk_sys)) { + if (PTR_ERR(i2s->clk_sys) != -EPROBE_DEFER) + dev_err(dev, "Failed to acquire clock 'sys'\n"); + return PTR_ERR(i2s->clk_sys); + } + + i2s->clk_ref = devm_clk_get(&pdev->dev, "ref"); + if (IS_ERR(i2s->clk_ref)) { + if (PTR_ERR(i2s->clk_ref) != -EPROBE_DEFER) + dev_err(dev, "Failed to acquire clock 'ref'\n"); + return PTR_ERR(i2s->clk_ref); + } + + ret = clk_prepare_enable(i2s->clk_sys); + if (ret) + return ret; + + reg = IMG_I2S_OUT_CTL_FRM_SIZE_MASK; + img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL); + + reg = IMG_I2S_OUT_CHAN_CTL_JUST_MASK | + IMG_I2S_OUT_CHAN_CTL_LT_MASK | + IMG_I2S_OUT_CHAN_CTL_CH_MASK | + (8 << IMG_I2S_OUT_CHAN_CTL_FMT_SHIFT); + + for (i = 0; i < i2s->max_i2s_chan; i++) + img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL); + + img_i2s_out_reset(i2s); + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = img_i2s_out_resume(&pdev->dev); + if (ret) + goto err_pm_disable; + } + + i2s->active_channels = 1; + i2s->dma_data.addr = res->start + IMG_I2S_OUT_TX_FIFO; + i2s->dma_data.addr_width = 4; + i2s->dma_data.maxburst = 4; + + i2s->dai_driver.probe = img_i2s_out_dai_probe; + i2s->dai_driver.playback.channels_min = 2; + i2s->dai_driver.playback.channels_max = i2s->max_i2s_chan * 2; + i2s->dai_driver.playback.rates = SNDRV_PCM_RATE_8000_192000; + i2s->dai_driver.playback.formats = SNDRV_PCM_FMTBIT_S32_LE; + i2s->dai_driver.ops = &img_i2s_out_dai_ops; + + ret = devm_snd_soc_register_component(&pdev->dev, + &img_i2s_out_component, &i2s->dai_driver, 1); + if (ret) + goto err_suspend; + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, + &img_i2s_out_dma_config, 0); + if (ret) + goto err_suspend; + + return 0; + +err_suspend: + if (!pm_runtime_status_suspended(&pdev->dev)) + img_i2s_out_suspend(&pdev->dev); +err_pm_disable: + pm_runtime_disable(&pdev->dev); + clk_disable_unprepare(i2s->clk_sys); + + return ret; +} + +static int img_i2s_out_dev_remove(struct platform_device *pdev) +{ + struct img_i2s_out *i2s = platform_get_drvdata(pdev); + + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + img_i2s_out_suspend(&pdev->dev); + + clk_disable_unprepare(i2s->clk_sys); + + return 0; +} + +static const struct of_device_id img_i2s_out_of_match[] = { + { .compatible = "img,i2s-out" }, + {} +}; +MODULE_DEVICE_TABLE(of, img_i2s_out_of_match); + +static const struct dev_pm_ops img_i2s_out_pm_ops = { + SET_RUNTIME_PM_OPS(img_i2s_out_suspend, + img_i2s_out_resume, NULL) +}; + +static struct platform_driver img_i2s_out_driver = { + .driver = { + .name = "img-i2s-out", + .of_match_table = img_i2s_out_of_match, + .pm = &img_i2s_out_pm_ops + }, + .probe = img_i2s_out_probe, + .remove = img_i2s_out_dev_remove +}; +module_platform_driver(img_i2s_out_driver); + +MODULE_AUTHOR("Damien Horsley "); +MODULE_DESCRIPTION("IMG I2S Output Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 8ceb3b259cddb9b0505a6697cdefd3110445d1d7 Mon Sep 17 00:00:00 2001 From: "Damien.Horsley" Date: Wed, 4 Nov 2015 14:40:52 +0000 Subject: ASoC: img: Add driver for parallel output controller Add driver for Imagination Technologies parallel output controller Signed-off-by: Damien.Horsley Signed-off-by: Mark Brown --- sound/soc/img/Kconfig | 8 + sound/soc/img/Makefile | 1 + sound/soc/img/img-parallel-out.c | 327 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 336 insertions(+) create mode 100644 sound/soc/img/img-parallel-out.c (limited to 'sound') diff --git a/sound/soc/img/Kconfig b/sound/soc/img/Kconfig index fe83c8e344b2..3bb507e5570c 100644 --- a/sound/soc/img/Kconfig +++ b/sound/soc/img/Kconfig @@ -18,3 +18,11 @@ config SND_SOC_IMG_I2S_OUT help Say Y or M if you want to add support for I2S out driver for Imagination Technologies I2S out device. + +config SND_SOC_IMG_PARALLEL_OUT + tristate "Imagination Parallel Output Device Driver" + depends on SND_SOC_IMG + select SND_SOC_GENERIC_DMAENGINE_PCM + help + Say Y or M if you want to add support for parallel out driver for + Imagination Technologies parallel out device. diff --git a/sound/soc/img/Makefile b/sound/soc/img/Makefile index c41a4af8dd89..da8976322488 100644 --- a/sound/soc/img/Makefile +++ b/sound/soc/img/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_SND_SOC_IMG_I2S_IN) += img-i2s-in.o obj-$(CONFIG_SND_SOC_IMG_I2S_OUT) += img-i2s-out.o +obj-$(CONFIG_SND_SOC_IMG_PARALLEL_OUT) += img-parallel-out.o diff --git a/sound/soc/img/img-parallel-out.c b/sound/soc/img/img-parallel-out.c new file mode 100644 index 000000000000..beda18b18c64 --- /dev/null +++ b/sound/soc/img/img-parallel-out.c @@ -0,0 +1,327 @@ +/* + * IMG parallel output controller driver + * + * Copyright (C) 2015 Imagination Technologies Ltd. + * + * Author: Damien Horsley + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define IMG_PRL_OUT_TX_FIFO 0 + +#define IMG_PRL_OUT_CTL 0x4 +#define IMG_PRL_OUT_CTL_CH_MASK BIT(4) +#define IMG_PRL_OUT_CTL_PACKH_MASK BIT(3) +#define IMG_PRL_OUT_CTL_EDGE_MASK BIT(2) +#define IMG_PRL_OUT_CTL_ME_MASK BIT(1) +#define IMG_PRL_OUT_CTL_SRST_MASK BIT(0) + +struct img_prl_out { + void __iomem *base; + struct clk *clk_sys; + struct clk *clk_ref; + struct snd_dmaengine_dai_dma_data dma_data; + struct device *dev; + struct reset_control *rst; +}; + +static int img_prl_out_suspend(struct device *dev) +{ + struct img_prl_out *prl = dev_get_drvdata(dev); + + clk_disable_unprepare(prl->clk_ref); + + return 0; +} + +static int img_prl_out_resume(struct device *dev) +{ + struct img_prl_out *prl = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(prl->clk_ref); + if (ret) { + dev_err(dev, "clk_enable failed: %d\n", ret); + return ret; + } + + return 0; +} + +static inline void img_prl_out_writel(struct img_prl_out *prl, + u32 val, u32 reg) +{ + writel(val, prl->base + reg); +} + +static inline u32 img_prl_out_readl(struct img_prl_out *prl, u32 reg) +{ + return readl(prl->base + reg); +} + +static void img_prl_out_reset(struct img_prl_out *prl) +{ + u32 ctl; + + ctl = img_prl_out_readl(prl, IMG_PRL_OUT_CTL) & + ~IMG_PRL_OUT_CTL_ME_MASK; + + reset_control_assert(prl->rst); + reset_control_deassert(prl->rst); + + img_prl_out_writel(prl, ctl, IMG_PRL_OUT_CTL); +} + +static int img_prl_out_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai); + u32 reg; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + reg = img_prl_out_readl(prl, IMG_PRL_OUT_CTL); + reg |= IMG_PRL_OUT_CTL_ME_MASK; + img_prl_out_writel(prl, reg, IMG_PRL_OUT_CTL); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + img_prl_out_reset(prl); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int img_prl_out_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai); + unsigned int rate, channels; + u32 reg, control_set = 0; + snd_pcm_format_t format; + + rate = params_rate(params); + format = params_format(params); + channels = params_channels(params); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S32_LE: + control_set |= IMG_PRL_OUT_CTL_PACKH_MASK; + break; + case SNDRV_PCM_FORMAT_S24_LE: + break; + default: + return -EINVAL; + } + + if (channels != 2) + return -EINVAL; + + clk_set_rate(prl->clk_ref, rate * 256); + + reg = img_prl_out_readl(prl, IMG_PRL_OUT_CTL); + reg = (reg & ~IMG_PRL_OUT_CTL_PACKH_MASK) | control_set; + img_prl_out_writel(prl, reg, IMG_PRL_OUT_CTL); + + return 0; +} + +static int img_prl_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai); + u32 reg, control_set; + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + control_set |= IMG_PRL_OUT_CTL_EDGE_MASK; + break; + default: + return -EINVAL; + } + + reg = img_prl_out_readl(prl, IMG_PRL_OUT_CTL); + reg = (reg & ~IMG_PRL_OUT_CTL_EDGE_MASK) | control_set; + img_prl_out_writel(prl, reg, IMG_PRL_OUT_CTL); + + return 0; +} + +static const struct snd_soc_dai_ops img_prl_out_dai_ops = { + .trigger = img_prl_out_trigger, + .hw_params = img_prl_out_hw_params, + .set_fmt = img_prl_out_set_fmt +}; + +static int img_prl_out_dai_probe(struct snd_soc_dai *dai) +{ + struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, &prl->dma_data, NULL); + + return 0; +} + +static struct snd_soc_dai_driver img_prl_out_dai = { + .probe = img_prl_out_dai_probe, + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE + }, + .ops = &img_prl_out_dai_ops +}; + +static const struct snd_soc_component_driver img_prl_out_component = { + .name = "img-prl-out" +}; + +static int img_prl_out_probe(struct platform_device *pdev) +{ + struct img_prl_out *prl; + struct resource *res; + void __iomem *base; + int ret; + struct device *dev = &pdev->dev; + + prl = devm_kzalloc(&pdev->dev, sizeof(*prl), GFP_KERNEL); + if (!prl) + return -ENOMEM; + + platform_set_drvdata(pdev, prl); + + prl->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + prl->base = base; + + prl->rst = devm_reset_control_get(&pdev->dev, "rst"); + if (IS_ERR(prl->rst)) { + if (PTR_ERR(prl->rst) != -EPROBE_DEFER) + dev_err(&pdev->dev, "No top level reset found\n"); + return PTR_ERR(prl->rst); + } + + prl->clk_sys = devm_clk_get(&pdev->dev, "sys"); + if (IS_ERR(prl->clk_sys)) { + if (PTR_ERR(prl->clk_sys) != -EPROBE_DEFER) + dev_err(dev, "Failed to acquire clock 'sys'\n"); + return PTR_ERR(prl->clk_sys); + } + + prl->clk_ref = devm_clk_get(&pdev->dev, "ref"); + if (IS_ERR(prl->clk_ref)) { + if (PTR_ERR(prl->clk_ref) != -EPROBE_DEFER) + dev_err(dev, "Failed to acquire clock 'ref'\n"); + return PTR_ERR(prl->clk_ref); + } + + ret = clk_prepare_enable(prl->clk_sys); + if (ret) + return ret; + + img_prl_out_writel(prl, IMG_PRL_OUT_CTL_EDGE_MASK, IMG_PRL_OUT_CTL); + img_prl_out_reset(prl); + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = img_prl_out_resume(&pdev->dev); + if (ret) + goto err_pm_disable; + } + + prl->dma_data.addr = res->start + IMG_PRL_OUT_TX_FIFO; + prl->dma_data.addr_width = 4; + prl->dma_data.maxburst = 4; + + ret = devm_snd_soc_register_component(&pdev->dev, + &img_prl_out_component, + &img_prl_out_dai, 1); + if (ret) + goto err_suspend; + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + if (ret) + goto err_suspend; + + return 0; + +err_suspend: + if (!pm_runtime_status_suspended(&pdev->dev)) + img_prl_out_suspend(&pdev->dev); +err_pm_disable: + pm_runtime_disable(&pdev->dev); + clk_disable_unprepare(prl->clk_sys); + + return ret; +} + +static int img_prl_out_dev_remove(struct platform_device *pdev) +{ + struct img_prl_out *prl = platform_get_drvdata(pdev); + + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + img_prl_out_suspend(&pdev->dev); + + clk_disable_unprepare(prl->clk_sys); + + return 0; +} + +static const struct of_device_id img_prl_out_of_match[] = { + { .compatible = "img,parallel-out" }, + {} +}; +MODULE_DEVICE_TABLE(of, img_prl_out_of_match); + +static const struct dev_pm_ops img_prl_out_pm_ops = { + SET_RUNTIME_PM_OPS(img_prl_out_suspend, + img_prl_out_resume, NULL) +}; + +static struct platform_driver img_prl_out_driver = { + .driver = { + .name = "img-parallel-out", + .of_match_table = img_prl_out_of_match, + .pm = &img_prl_out_pm_ops + }, + .probe = img_prl_out_probe, + .remove = img_prl_out_dev_remove +}; +module_platform_driver(img_prl_out_driver); + +MODULE_AUTHOR("Damien Horsley "); +MODULE_DESCRIPTION("IMG Parallel Output Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From c4458b740e6b7a0d9ccf680ac81c05a99f602b79 Mon Sep 17 00:00:00 2001 From: "Damien.Horsley" Date: Wed, 4 Nov 2015 14:40:54 +0000 Subject: ASoC: img: Add driver for SPDIF input controller Add driver for Imagination Technologies SDPIF input controller Signed-off-by: Damien.Horsley Signed-off-by: Mark Brown --- sound/soc/img/Kconfig | 8 + sound/soc/img/Makefile | 1 + sound/soc/img/img-spdif-in.c | 806 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 815 insertions(+) create mode 100644 sound/soc/img/img-spdif-in.c (limited to 'sound') diff --git a/sound/soc/img/Kconfig b/sound/soc/img/Kconfig index 3bb507e5570c..161ce90441b7 100644 --- a/sound/soc/img/Kconfig +++ b/sound/soc/img/Kconfig @@ -26,3 +26,11 @@ config SND_SOC_IMG_PARALLEL_OUT help Say Y or M if you want to add support for parallel out driver for Imagination Technologies parallel out device. + +config SND_SOC_IMG_SPDIF_IN + tristate "Imagination SPDIF Input Device Driver" + depends on SND_SOC_IMG + select SND_SOC_GENERIC_DMAENGINE_PCM + help + Say Y or M if you want to add support for SPDIF input driver for + Imagination Technologies SPDIF input device. diff --git a/sound/soc/img/Makefile b/sound/soc/img/Makefile index da8976322488..85ded5eede5f 100644 --- a/sound/soc/img/Makefile +++ b/sound/soc/img/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_SND_SOC_IMG_I2S_IN) += img-i2s-in.o obj-$(CONFIG_SND_SOC_IMG_I2S_OUT) += img-i2s-out.o obj-$(CONFIG_SND_SOC_IMG_PARALLEL_OUT) += img-parallel-out.o +obj-$(CONFIG_SND_SOC_IMG_SPDIF_IN) += img-spdif-in.o diff --git a/sound/soc/img/img-spdif-in.c b/sound/soc/img/img-spdif-in.c new file mode 100644 index 000000000000..4d9953d318af --- /dev/null +++ b/sound/soc/img/img-spdif-in.c @@ -0,0 +1,806 @@ +/* + * IMG SPDIF input controller driver + * + * Copyright (C) 2015 Imagination Technologies Ltd. + * + * Author: Damien Horsley + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define IMG_SPDIF_IN_RX_FIFO_OFFSET 0 + +#define IMG_SPDIF_IN_CTL 0x4 +#define IMG_SPDIF_IN_CTL_LOCKLO_MASK 0xff +#define IMG_SPDIF_IN_CTL_LOCKLO_SHIFT 0 +#define IMG_SPDIF_IN_CTL_LOCKHI_MASK 0xff00 +#define IMG_SPDIF_IN_CTL_LOCKHI_SHIFT 8 +#define IMG_SPDIF_IN_CTL_TRK_MASK 0xff0000 +#define IMG_SPDIF_IN_CTL_TRK_SHIFT 16 +#define IMG_SPDIF_IN_CTL_SRD_MASK 0x70000000 +#define IMG_SPDIF_IN_CTL_SRD_SHIFT 28 +#define IMG_SPDIF_IN_CTL_SRT_MASK BIT(31) + +#define IMG_SPDIF_IN_STATUS 0x8 +#define IMG_SPDIF_IN_STATUS_SAM_MASK 0x7000 +#define IMG_SPDIF_IN_STATUS_SAM_SHIFT 12 +#define IMG_SPDIF_IN_STATUS_LOCK_MASK BIT(15) +#define IMG_SPDIF_IN_STATUS_LOCK_SHIFT 15 + +#define IMG_SPDIF_IN_CLKGEN 0x1c +#define IMG_SPDIF_IN_CLKGEN_NOM_MASK 0x3ff +#define IMG_SPDIF_IN_CLKGEN_NOM_SHIFT 0 +#define IMG_SPDIF_IN_CLKGEN_HLD_MASK 0x3ff0000 +#define IMG_SPDIF_IN_CLKGEN_HLD_SHIFT 16 + +#define IMG_SPDIF_IN_CSL 0x20 + +#define IMG_SPDIF_IN_CSH 0x24 +#define IMG_SPDIF_IN_CSH_MASK 0xff +#define IMG_SPDIF_IN_CSH_SHIFT 0 + +#define IMG_SPDIF_IN_SOFT_RESET 0x28 +#define IMG_SPDIF_IN_SOFT_RESET_MASK BIT(0) + +#define IMG_SPDIF_IN_ACLKGEN_START 0x2c +#define IMG_SPDIF_IN_ACLKGEN_NOM_MASK 0x3ff +#define IMG_SPDIF_IN_ACLKGEN_NOM_SHIFT 0 +#define IMG_SPDIF_IN_ACLKGEN_HLD_MASK 0xffc00 +#define IMG_SPDIF_IN_ACLKGEN_HLD_SHIFT 10 +#define IMG_SPDIF_IN_ACLKGEN_TRK_MASK 0xff00000 +#define IMG_SPDIF_IN_ACLKGEN_TRK_SHIFT 20 + +#define IMG_SPDIF_IN_NUM_ACLKGEN 4 + +struct img_spdif_in { + spinlock_t lock; + void __iomem *base; + struct clk *clk_sys; + struct snd_dmaengine_dai_dma_data dma_data; + struct device *dev; + unsigned int trk; + bool multi_freq; + int lock_acquire; + int lock_release; + unsigned int single_freq; + unsigned int multi_freqs[IMG_SPDIF_IN_NUM_ACLKGEN]; + bool active; + + /* Write-only registers */ + unsigned int aclkgen_regs[IMG_SPDIF_IN_NUM_ACLKGEN]; +}; + +static inline void img_spdif_in_writel(struct img_spdif_in *spdif, + u32 val, u32 reg) +{ + writel(val, spdif->base + reg); +} + +static inline u32 img_spdif_in_readl(struct img_spdif_in *spdif, u32 reg) +{ + return readl(spdif->base + reg); +} + +static inline void img_spdif_in_aclkgen_writel(struct img_spdif_in *spdif, + u32 index) +{ + img_spdif_in_writel(spdif, spdif->aclkgen_regs[index], + IMG_SPDIF_IN_ACLKGEN_START + (index * 0x4)); +} + +static int img_spdif_in_check_max_rate(struct img_spdif_in *spdif, + unsigned int sample_rate, unsigned long *actual_freq) +{ + unsigned long min_freq, freq_t; + + /* Clock rate must be at least 24x the bit rate */ + min_freq = sample_rate * 2 * 32 * 24; + + freq_t = clk_get_rate(spdif->clk_sys); + + if (freq_t < min_freq) + return -EINVAL; + + *actual_freq = freq_t; + + return 0; +} + +static int img_spdif_in_do_clkgen_calc(unsigned int rate, unsigned int *pnom, + unsigned int *phld, unsigned long clk_rate) +{ + unsigned int ori, nom, hld; + + /* + * Calculate oversampling ratio, nominal phase increment and hold + * increment for the given rate / frequency + */ + + if (!rate) + return -EINVAL; + + ori = clk_rate / (rate * 64); + + if (!ori) + return -EINVAL; + + nom = (4096 / ori) + 1; + do + hld = 4096 - (--nom * (ori - 1)); + while (hld < 120); + + *pnom = nom; + *phld = hld; + + return 0; +} + +static int img_spdif_in_do_clkgen_single(struct img_spdif_in *spdif, + unsigned int rate) +{ + unsigned int nom, hld; + unsigned long flags, clk_rate; + int ret = 0; + u32 reg; + + ret = img_spdif_in_check_max_rate(spdif, rate, &clk_rate); + if (ret) + return ret; + + ret = img_spdif_in_do_clkgen_calc(rate, &nom, &hld, clk_rate); + if (ret) + return ret; + + reg = (nom << IMG_SPDIF_IN_CLKGEN_NOM_SHIFT) & + IMG_SPDIF_IN_CLKGEN_NOM_MASK; + reg |= (hld << IMG_SPDIF_IN_CLKGEN_HLD_SHIFT) & + IMG_SPDIF_IN_CLKGEN_HLD_MASK; + + spin_lock_irqsave(&spdif->lock, flags); + + if (spdif->active) { + spin_unlock_irqrestore(&spdif->lock, flags); + return -EBUSY; + } + + img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CLKGEN); + + spdif->single_freq = rate; + + spin_unlock_irqrestore(&spdif->lock, flags); + + return 0; +} + +static int img_spdif_in_do_clkgen_multi(struct img_spdif_in *spdif, + unsigned int multi_freqs[]) +{ + unsigned int nom, hld, rate, max_rate = 0; + unsigned long flags, clk_rate; + int i, ret = 0; + u32 reg, trk_reg, temp_regs[IMG_SPDIF_IN_NUM_ACLKGEN]; + + for (i = 0; i < IMG_SPDIF_IN_NUM_ACLKGEN; i++) + if (multi_freqs[i] > max_rate) + max_rate = multi_freqs[i]; + + ret = img_spdif_in_check_max_rate(spdif, max_rate, &clk_rate); + if (ret) + return ret; + + for (i = 0; i < IMG_SPDIF_IN_NUM_ACLKGEN; i++) { + rate = multi_freqs[i]; + + ret = img_spdif_in_do_clkgen_calc(rate, &nom, &hld, clk_rate); + if (ret) + return ret; + + reg = (nom << IMG_SPDIF_IN_ACLKGEN_NOM_SHIFT) & + IMG_SPDIF_IN_ACLKGEN_NOM_MASK; + reg |= (hld << IMG_SPDIF_IN_ACLKGEN_HLD_SHIFT) & + IMG_SPDIF_IN_ACLKGEN_HLD_MASK; + temp_regs[i] = reg; + } + + spin_lock_irqsave(&spdif->lock, flags); + + if (spdif->active) { + spin_unlock_irqrestore(&spdif->lock, flags); + return -EBUSY; + } + + trk_reg = spdif->trk << IMG_SPDIF_IN_ACLKGEN_TRK_SHIFT; + + for (i = 0; i < IMG_SPDIF_IN_NUM_ACLKGEN; i++) { + spdif->aclkgen_regs[i] = temp_regs[i] | trk_reg; + img_spdif_in_aclkgen_writel(spdif, i); + } + + spdif->multi_freq = true; + spdif->multi_freqs[0] = multi_freqs[0]; + spdif->multi_freqs[1] = multi_freqs[1]; + spdif->multi_freqs[2] = multi_freqs[2]; + spdif->multi_freqs[3] = multi_freqs[3]; + + spin_unlock_irqrestore(&spdif->lock, flags); + + return 0; +} + +static int img_spdif_in_iec958_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + + return 0; +} + +static int img_spdif_in_get_status_mask(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.iec958.status[0] = 0xff; + ucontrol->value.iec958.status[1] = 0xff; + ucontrol->value.iec958.status[2] = 0xff; + ucontrol->value.iec958.status[3] = 0xff; + ucontrol->value.iec958.status[4] = 0xff; + + return 0; +} + +static int img_spdif_in_get_status(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai); + u32 reg; + + reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CSL); + ucontrol->value.iec958.status[0] = reg & 0xff; + ucontrol->value.iec958.status[1] = (reg >> 8) & 0xff; + ucontrol->value.iec958.status[2] = (reg >> 16) & 0xff; + ucontrol->value.iec958.status[3] = (reg >> 24) & 0xff; + reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CSH); + ucontrol->value.iec958.status[4] = (reg & IMG_SPDIF_IN_CSH_MASK) + >> IMG_SPDIF_IN_CSH_SHIFT; + + return 0; +} + +static int img_spdif_in_info_multi_freq(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = IMG_SPDIF_IN_NUM_ACLKGEN; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = LONG_MAX; + + return 0; +} + +static int img_spdif_in_get_multi_freq(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai); + unsigned long flags; + + spin_lock_irqsave(&spdif->lock, flags); + if (spdif->multi_freq) { + ucontrol->value.integer.value[0] = spdif->multi_freqs[0]; + ucontrol->value.integer.value[1] = spdif->multi_freqs[1]; + ucontrol->value.integer.value[2] = spdif->multi_freqs[2]; + ucontrol->value.integer.value[3] = spdif->multi_freqs[3]; + } else { + ucontrol->value.integer.value[0] = 0; + ucontrol->value.integer.value[1] = 0; + ucontrol->value.integer.value[2] = 0; + ucontrol->value.integer.value[3] = 0; + } + spin_unlock_irqrestore(&spdif->lock, flags); + + return 0; +} + +static int img_spdif_in_set_multi_freq(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai); + unsigned int multi_freqs[IMG_SPDIF_IN_NUM_ACLKGEN]; + bool multi_freq; + unsigned long flags; + + if ((ucontrol->value.integer.value[0] == 0) && + (ucontrol->value.integer.value[1] == 0) && + (ucontrol->value.integer.value[2] == 0) && + (ucontrol->value.integer.value[3] == 0)) { + multi_freq = false; + } else { + multi_freqs[0] = ucontrol->value.integer.value[0]; + multi_freqs[1] = ucontrol->value.integer.value[1]; + multi_freqs[2] = ucontrol->value.integer.value[2]; + multi_freqs[3] = ucontrol->value.integer.value[3]; + multi_freq = true; + } + + if (multi_freq) + return img_spdif_in_do_clkgen_multi(spdif, multi_freqs); + + spin_lock_irqsave(&spdif->lock, flags); + + if (spdif->active) { + spin_unlock_irqrestore(&spdif->lock, flags); + return -EBUSY; + } + + spdif->multi_freq = false; + + spin_unlock_irqrestore(&spdif->lock, flags); + + return 0; +} + +static int img_spdif_in_info_lock_freq(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = LONG_MAX; + + return 0; +} + +static int img_spdif_in_get_lock_freq(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uc) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai); + u32 reg; + int i; + unsigned long flags; + + spin_lock_irqsave(&spdif->lock, flags); + + reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_STATUS); + if (reg & IMG_SPDIF_IN_STATUS_LOCK_MASK) { + if (spdif->multi_freq) { + i = ((reg & IMG_SPDIF_IN_STATUS_SAM_MASK) >> + IMG_SPDIF_IN_STATUS_SAM_SHIFT) - 1; + uc->value.integer.value[0] = spdif->multi_freqs[i]; + } else { + uc->value.integer.value[0] = spdif->single_freq; + } + } else { + uc->value.integer.value[0] = 0; + } + + spin_unlock_irqrestore(&spdif->lock, flags); + + return 0; +} + +static int img_spdif_in_info_trk(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 255; + + return 0; +} + +static int img_spdif_in_get_trk(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai); + + ucontrol->value.integer.value[0] = spdif->trk; + + return 0; +} + +static int img_spdif_in_set_trk(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai); + unsigned long flags; + int i; + u32 reg; + + spin_lock_irqsave(&spdif->lock, flags); + + if (spdif->active) { + spin_unlock_irqrestore(&spdif->lock, flags); + return -EBUSY; + } + + spdif->trk = ucontrol->value.integer.value[0]; + + reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL); + reg &= ~IMG_SPDIF_IN_CTL_TRK_MASK; + reg |= spdif->trk << IMG_SPDIF_IN_CTL_TRK_SHIFT; + img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL); + + for (i = 0; i < IMG_SPDIF_IN_NUM_ACLKGEN; i++) { + spdif->aclkgen_regs[i] = (spdif->aclkgen_regs[i] & + ~IMG_SPDIF_IN_ACLKGEN_TRK_MASK) | + (spdif->trk << IMG_SPDIF_IN_ACLKGEN_TRK_SHIFT); + + img_spdif_in_aclkgen_writel(spdif, i); + } + + spin_unlock_irqrestore(&spdif->lock, flags); + + return 0; +} + +static int img_spdif_in_info_lock(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = -128; + uinfo->value.integer.max = 127; + + return 0; +} + +static int img_spdif_in_get_lock_acquire(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai); + + ucontrol->value.integer.value[0] = spdif->lock_acquire; + + return 0; +} + +static int img_spdif_in_set_lock_acquire(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai); + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&spdif->lock, flags); + + if (spdif->active) { + spin_unlock_irqrestore(&spdif->lock, flags); + return -EBUSY; + } + + spdif->lock_acquire = ucontrol->value.integer.value[0]; + + reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL); + reg &= ~IMG_SPDIF_IN_CTL_LOCKHI_MASK; + reg |= (spdif->lock_acquire << IMG_SPDIF_IN_CTL_LOCKHI_SHIFT) & + IMG_SPDIF_IN_CTL_LOCKHI_MASK; + img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL); + + spin_unlock_irqrestore(&spdif->lock, flags); + + return 0; +} + +static int img_spdif_in_get_lock_release(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai); + + ucontrol->value.integer.value[0] = spdif->lock_release; + + return 0; +} + +static int img_spdif_in_set_lock_release(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(cpu_dai); + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&spdif->lock, flags); + + if (spdif->active) { + spin_unlock_irqrestore(&spdif->lock, flags); + return -EBUSY; + } + + spdif->lock_release = ucontrol->value.integer.value[0]; + + reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL); + reg &= ~IMG_SPDIF_IN_CTL_LOCKLO_MASK; + reg |= (spdif->lock_release << IMG_SPDIF_IN_CTL_LOCKLO_SHIFT) & + IMG_SPDIF_IN_CTL_LOCKLO_MASK; + img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL); + + spin_unlock_irqrestore(&spdif->lock, flags); + + return 0; +} + +static struct snd_kcontrol_new img_spdif_in_controls[] = { + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK), + .info = img_spdif_in_iec958_info, + .get = img_spdif_in_get_status_mask + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT), + .info = img_spdif_in_iec958_info, + .get = img_spdif_in_get_status + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "SPDIF In Multi Frequency Acquire", + .info = img_spdif_in_info_multi_freq, + .get = img_spdif_in_get_multi_freq, + .put = img_spdif_in_set_multi_freq + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "SPDIF In Lock Frequency", + .info = img_spdif_in_info_lock_freq, + .get = img_spdif_in_get_lock_freq + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "SPDIF In Lock TRK", + .info = img_spdif_in_info_trk, + .get = img_spdif_in_get_trk, + .put = img_spdif_in_set_trk + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "SPDIF In Lock Acquire Threshold", + .info = img_spdif_in_info_lock, + .get = img_spdif_in_get_lock_acquire, + .put = img_spdif_in_set_lock_acquire + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "SPDIF In Lock Release Threshold", + .info = img_spdif_in_info_lock, + .get = img_spdif_in_get_lock_release, + .put = img_spdif_in_set_lock_release + } +}; + +static int img_spdif_in_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + unsigned long flags; + struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(dai); + int ret = 0; + u32 reg; + + spin_lock_irqsave(&spdif->lock, flags); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL); + if (spdif->multi_freq) + reg &= ~IMG_SPDIF_IN_CTL_SRD_MASK; + else + reg |= (1UL << IMG_SPDIF_IN_CTL_SRD_SHIFT); + reg |= IMG_SPDIF_IN_CTL_SRT_MASK; + img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL); + spdif->active = true; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + reg = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL); + reg &= ~IMG_SPDIF_IN_CTL_SRT_MASK; + img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL); + spdif->active = false; + break; + default: + ret = -EINVAL; + } + + spin_unlock_irqrestore(&spdif->lock, flags); + + return ret; +} + +static int img_spdif_in_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(dai); + unsigned int rate, channels; + snd_pcm_format_t format; + + rate = params_rate(params); + channels = params_channels(params); + format = params_format(params); + + if (format != SNDRV_PCM_FORMAT_S32_LE) + return -EINVAL; + + if (channels != 2) + return -EINVAL; + + return img_spdif_in_do_clkgen_single(spdif, rate); +} + +static const struct snd_soc_dai_ops img_spdif_in_dai_ops = { + .trigger = img_spdif_in_trigger, + .hw_params = img_spdif_in_hw_params +}; + +static int img_spdif_in_dai_probe(struct snd_soc_dai *dai) +{ + struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, NULL, &spdif->dma_data); + + snd_soc_add_dai_controls(dai, img_spdif_in_controls, + ARRAY_SIZE(img_spdif_in_controls)); + + return 0; +} + +static struct snd_soc_dai_driver img_spdif_in_dai = { + .probe = img_spdif_in_dai_probe, + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S32_LE + }, + .ops = &img_spdif_in_dai_ops +}; + +static const struct snd_soc_component_driver img_spdif_in_component = { + .name = "img-spdif-in" +}; + +static int img_spdif_in_probe(struct platform_device *pdev) +{ + struct img_spdif_in *spdif; + struct resource *res; + void __iomem *base; + int ret; + struct reset_control *rst; + u32 reg; + struct device *dev = &pdev->dev; + + spdif = devm_kzalloc(&pdev->dev, sizeof(*spdif), GFP_KERNEL); + if (!spdif) + return -ENOMEM; + + platform_set_drvdata(pdev, spdif); + + spdif->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + spdif->base = base; + + spdif->clk_sys = devm_clk_get(dev, "sys"); + if (IS_ERR(spdif->clk_sys)) { + if (PTR_ERR(spdif->clk_sys) != -EPROBE_DEFER) + dev_err(dev, "Failed to acquire clock 'sys'\n"); + return PTR_ERR(spdif->clk_sys); + } + + ret = clk_prepare_enable(spdif->clk_sys); + if (ret) + return ret; + + rst = devm_reset_control_get(&pdev->dev, "rst"); + if (IS_ERR(rst)) { + if (PTR_ERR(rst) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto err_clk_disable; + } + dev_dbg(dev, "No top level reset found\n"); + img_spdif_in_writel(spdif, IMG_SPDIF_IN_SOFT_RESET_MASK, + IMG_SPDIF_IN_SOFT_RESET); + img_spdif_in_writel(spdif, 0, IMG_SPDIF_IN_SOFT_RESET); + } else { + reset_control_assert(rst); + reset_control_deassert(rst); + } + + spin_lock_init(&spdif->lock); + + spdif->dma_data.addr = res->start + IMG_SPDIF_IN_RX_FIFO_OFFSET; + spdif->dma_data.addr_width = 4; + spdif->dma_data.maxburst = 4; + spdif->trk = 0x80; + spdif->lock_acquire = 4; + spdif->lock_release = -128; + + reg = (spdif->lock_acquire << IMG_SPDIF_IN_CTL_LOCKHI_SHIFT) & + IMG_SPDIF_IN_CTL_LOCKHI_MASK; + reg |= (spdif->lock_release << IMG_SPDIF_IN_CTL_LOCKLO_SHIFT) & + IMG_SPDIF_IN_CTL_LOCKLO_MASK; + reg |= (spdif->trk << IMG_SPDIF_IN_CTL_TRK_SHIFT) & + IMG_SPDIF_IN_CTL_TRK_MASK; + img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL); + + ret = devm_snd_soc_register_component(&pdev->dev, + &img_spdif_in_component, &img_spdif_in_dai, 1); + if (ret) + goto err_clk_disable; + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + if (ret) + goto err_clk_disable; + + return 0; + +err_clk_disable: + clk_disable_unprepare(spdif->clk_sys); + + return ret; +} + +static int img_spdif_in_dev_remove(struct platform_device *pdev) +{ + struct img_spdif_in *spdif = platform_get_drvdata(pdev); + + clk_disable_unprepare(spdif->clk_sys); + + return 0; +} + +static const struct of_device_id img_spdif_in_of_match[] = { + { .compatible = "img,spdif-in" }, + {} +}; +MODULE_DEVICE_TABLE(of, img_spdif_in_of_match); + +static struct platform_driver img_spdif_in_driver = { + .driver = { + .name = "img-spdif-in", + .of_match_table = img_spdif_in_of_match + }, + .probe = img_spdif_in_probe, + .remove = img_spdif_in_dev_remove +}; +module_platform_driver(img_spdif_in_driver); + +MODULE_AUTHOR("Damien Horsley "); +MODULE_DESCRIPTION("IMG SPDIF Input driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 3958232273d791629d8fffc67b6c5b895ab1e91a Mon Sep 17 00:00:00 2001 From: "Damien.Horsley" Date: Wed, 4 Nov 2015 14:40:57 +0000 Subject: ASoC: img: Add driver for SPDIF output controller Add driver for Imagination Technologies SPDIF output controller Signed-off-by: Damien.Horsley Signed-off-by: Mark Brown --- sound/soc/img/Kconfig | 8 + sound/soc/img/Makefile | 1 + sound/soc/img/img-spdif-out.c | 441 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 450 insertions(+) create mode 100644 sound/soc/img/img-spdif-out.c (limited to 'sound') diff --git a/sound/soc/img/Kconfig b/sound/soc/img/Kconfig index 161ce90441b7..d08537ecb915 100644 --- a/sound/soc/img/Kconfig +++ b/sound/soc/img/Kconfig @@ -34,3 +34,11 @@ config SND_SOC_IMG_SPDIF_IN help Say Y or M if you want to add support for SPDIF input driver for Imagination Technologies SPDIF input device. + +config SND_SOC_IMG_SPDIF_OUT + tristate "Imagination SPDIF Output Device Driver" + depends on SND_SOC_IMG + select SND_SOC_GENERIC_DMAENGINE_PCM + help + Say Y or M if you want to add support for SPDIF out driver for + Imagination Technologies SPDIF out device. diff --git a/sound/soc/img/Makefile b/sound/soc/img/Makefile index 85ded5eede5f..1a44fb4b08fe 100644 --- a/sound/soc/img/Makefile +++ b/sound/soc/img/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_SND_SOC_IMG_I2S_IN) += img-i2s-in.o obj-$(CONFIG_SND_SOC_IMG_I2S_OUT) += img-i2s-out.o obj-$(CONFIG_SND_SOC_IMG_PARALLEL_OUT) += img-parallel-out.o obj-$(CONFIG_SND_SOC_IMG_SPDIF_IN) += img-spdif-in.o +obj-$(CONFIG_SND_SOC_IMG_SPDIF_OUT) += img-spdif-out.o diff --git a/sound/soc/img/img-spdif-out.c b/sound/soc/img/img-spdif-out.c new file mode 100644 index 000000000000..08f93a5dadfe --- /dev/null +++ b/sound/soc/img/img-spdif-out.c @@ -0,0 +1,441 @@ +/* + * IMG SPDIF output controller driver + * + * Copyright (C) 2015 Imagination Technologies Ltd. + * + * Author: Damien Horsley + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define IMG_SPDIF_OUT_TX_FIFO 0x0 + +#define IMG_SPDIF_OUT_CTL 0x4 +#define IMG_SPDIF_OUT_CTL_FS_MASK BIT(4) +#define IMG_SPDIF_OUT_CTL_CLK_MASK BIT(2) +#define IMG_SPDIF_OUT_CTL_SRT_MASK BIT(0) + +#define IMG_SPDIF_OUT_CSL 0x14 + +#define IMG_SPDIF_OUT_CSH_UV 0x18 +#define IMG_SPDIF_OUT_CSH_UV_CSH_SHIFT 0 +#define IMG_SPDIF_OUT_CSH_UV_CSH_MASK 0xff + +struct img_spdif_out { + spinlock_t lock; + void __iomem *base; + struct clk *clk_sys; + struct clk *clk_ref; + struct snd_dmaengine_dai_dma_data dma_data; + struct device *dev; + struct reset_control *rst; +}; + +static int img_spdif_out_suspend(struct device *dev) +{ + struct img_spdif_out *spdif = dev_get_drvdata(dev); + + clk_disable_unprepare(spdif->clk_ref); + + return 0; +} + +static int img_spdif_out_resume(struct device *dev) +{ + struct img_spdif_out *spdif = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(spdif->clk_ref); + if (ret) { + dev_err(dev, "clk_enable failed: %d\n", ret); + return ret; + } + + return 0; +} + +static inline void img_spdif_out_writel(struct img_spdif_out *spdif, u32 val, + u32 reg) +{ + writel(val, spdif->base + reg); +} + +static inline u32 img_spdif_out_readl(struct img_spdif_out *spdif, u32 reg) +{ + return readl(spdif->base + reg); +} + +static void img_spdif_out_reset(struct img_spdif_out *spdif) +{ + u32 ctl, status_low, status_high; + + ctl = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CTL) & + ~IMG_SPDIF_OUT_CTL_SRT_MASK; + status_low = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSL); + status_high = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSH_UV); + + reset_control_assert(spdif->rst); + reset_control_deassert(spdif->rst); + + img_spdif_out_writel(spdif, ctl, IMG_SPDIF_OUT_CTL); + img_spdif_out_writel(spdif, status_low, IMG_SPDIF_OUT_CSL); + img_spdif_out_writel(spdif, status_high, IMG_SPDIF_OUT_CSH_UV); +} + +static int img_spdif_out_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + + return 0; +} + +static int img_spdif_out_get_status_mask(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.iec958.status[0] = 0xff; + ucontrol->value.iec958.status[1] = 0xff; + ucontrol->value.iec958.status[2] = 0xff; + ucontrol->value.iec958.status[3] = 0xff; + ucontrol->value.iec958.status[4] = 0xff; + + return 0; +} + +static int img_spdif_out_get_status(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct img_spdif_out *spdif = snd_soc_dai_get_drvdata(cpu_dai); + u32 reg; + unsigned long flags; + + spin_lock_irqsave(&spdif->lock, flags); + + reg = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSL); + ucontrol->value.iec958.status[0] = reg & 0xff; + ucontrol->value.iec958.status[1] = (reg >> 8) & 0xff; + ucontrol->value.iec958.status[2] = (reg >> 16) & 0xff; + ucontrol->value.iec958.status[3] = (reg >> 24) & 0xff; + + reg = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSH_UV); + ucontrol->value.iec958.status[4] = + (reg & IMG_SPDIF_OUT_CSH_UV_CSH_MASK) >> + IMG_SPDIF_OUT_CSH_UV_CSH_SHIFT; + + spin_unlock_irqrestore(&spdif->lock, flags); + + return 0; +} + +static int img_spdif_out_set_status(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct img_spdif_out *spdif = snd_soc_dai_get_drvdata(cpu_dai); + u32 reg; + unsigned long flags; + + reg = ((u32)ucontrol->value.iec958.status[3] << 24); + reg |= ((u32)ucontrol->value.iec958.status[2] << 16); + reg |= ((u32)ucontrol->value.iec958.status[1] << 8); + reg |= (u32)ucontrol->value.iec958.status[0]; + + spin_lock_irqsave(&spdif->lock, flags); + + img_spdif_out_writel(spdif, reg, IMG_SPDIF_OUT_CSL); + + reg = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSH_UV); + reg &= ~IMG_SPDIF_OUT_CSH_UV_CSH_MASK; + reg |= (u32)ucontrol->value.iec958.status[4] << + IMG_SPDIF_OUT_CSH_UV_CSH_SHIFT; + img_spdif_out_writel(spdif, reg, IMG_SPDIF_OUT_CSH_UV); + + spin_unlock_irqrestore(&spdif->lock, flags); + + return 0; +} + +static struct snd_kcontrol_new img_spdif_out_controls[] = { + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK), + .info = img_spdif_out_info, + .get = img_spdif_out_get_status_mask + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), + .info = img_spdif_out_info, + .get = img_spdif_out_get_status, + .put = img_spdif_out_set_status + } +}; + +static int img_spdif_out_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct img_spdif_out *spdif = snd_soc_dai_get_drvdata(dai); + u32 reg; + unsigned long flags; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + reg = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CTL); + reg |= IMG_SPDIF_OUT_CTL_SRT_MASK; + img_spdif_out_writel(spdif, reg, IMG_SPDIF_OUT_CTL); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + spin_lock_irqsave(&spdif->lock, flags); + img_spdif_out_reset(spdif); + spin_unlock_irqrestore(&spdif->lock, flags); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int img_spdif_out_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct img_spdif_out *spdif = snd_soc_dai_get_drvdata(dai); + unsigned int channels; + long pre_div_a, pre_div_b, diff_a, diff_b, rate, clk_rate; + u32 reg; + snd_pcm_format_t format; + + rate = params_rate(params); + format = params_format(params); + channels = params_channels(params); + + dev_dbg(spdif->dev, "hw_params rate %ld channels %u format %u\n", + rate, channels, format); + + if (format != SNDRV_PCM_FORMAT_S32_LE) + return -EINVAL; + + if (channels != 2) + return -EINVAL; + + pre_div_a = clk_round_rate(spdif->clk_ref, rate * 256); + if (pre_div_a < 0) + return pre_div_a; + pre_div_b = clk_round_rate(spdif->clk_ref, rate * 384); + if (pre_div_b < 0) + return pre_div_b; + + diff_a = abs((pre_div_a / 256) - rate); + diff_b = abs((pre_div_b / 384) - rate); + + /* If diffs are equal, use lower clock rate */ + if (diff_a > diff_b) + clk_set_rate(spdif->clk_ref, pre_div_b); + else + clk_set_rate(spdif->clk_ref, pre_div_a); + + /* + * Another driver (eg machine driver) may have rejected the above + * change. Get the current rate and set the register bit according to + * the new min diff + */ + clk_rate = clk_get_rate(spdif->clk_ref); + + diff_a = abs((clk_rate / 256) - rate); + diff_b = abs((clk_rate / 384) - rate); + + reg = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CTL); + if (diff_a <= diff_b) + reg &= ~IMG_SPDIF_OUT_CTL_CLK_MASK; + else + reg |= IMG_SPDIF_OUT_CTL_CLK_MASK; + img_spdif_out_writel(spdif, reg, IMG_SPDIF_OUT_CTL); + + return 0; +} + +static const struct snd_soc_dai_ops img_spdif_out_dai_ops = { + .trigger = img_spdif_out_trigger, + .hw_params = img_spdif_out_hw_params +}; + +static int img_spdif_out_dai_probe(struct snd_soc_dai *dai) +{ + struct img_spdif_out *spdif = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, &spdif->dma_data, NULL); + + snd_soc_add_dai_controls(dai, img_spdif_out_controls, + ARRAY_SIZE(img_spdif_out_controls)); + + return 0; +} + +static struct snd_soc_dai_driver img_spdif_out_dai = { + .probe = img_spdif_out_dai_probe, + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S32_LE + }, + .ops = &img_spdif_out_dai_ops +}; + +static const struct snd_soc_component_driver img_spdif_out_component = { + .name = "img-spdif-out" +}; + +static int img_spdif_out_probe(struct platform_device *pdev) +{ + struct img_spdif_out *spdif; + struct resource *res; + void __iomem *base; + int ret; + struct device *dev = &pdev->dev; + + spdif = devm_kzalloc(&pdev->dev, sizeof(*spdif), GFP_KERNEL); + if (!spdif) + return -ENOMEM; + + platform_set_drvdata(pdev, spdif); + + spdif->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + spdif->base = base; + + spdif->rst = devm_reset_control_get(&pdev->dev, "rst"); + if (IS_ERR(spdif->rst)) { + if (PTR_ERR(spdif->rst) != -EPROBE_DEFER) + dev_err(&pdev->dev, "No top level reset found\n"); + return PTR_ERR(spdif->rst); + } + + spdif->clk_sys = devm_clk_get(&pdev->dev, "sys"); + if (IS_ERR(spdif->clk_sys)) { + if (PTR_ERR(spdif->clk_sys) != -EPROBE_DEFER) + dev_err(dev, "Failed to acquire clock 'sys'\n"); + return PTR_ERR(spdif->clk_sys); + } + + spdif->clk_ref = devm_clk_get(&pdev->dev, "ref"); + if (IS_ERR(spdif->clk_ref)) { + if (PTR_ERR(spdif->clk_ref) != -EPROBE_DEFER) + dev_err(dev, "Failed to acquire clock 'ref'\n"); + return PTR_ERR(spdif->clk_ref); + } + + ret = clk_prepare_enable(spdif->clk_sys); + if (ret) + return ret; + + img_spdif_out_writel(spdif, IMG_SPDIF_OUT_CTL_FS_MASK, + IMG_SPDIF_OUT_CTL); + + img_spdif_out_reset(spdif); + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = img_spdif_out_resume(&pdev->dev); + if (ret) + goto err_pm_disable; + } + + spin_lock_init(&spdif->lock); + + spdif->dma_data.addr = res->start + IMG_SPDIF_OUT_TX_FIFO; + spdif->dma_data.addr_width = 4; + spdif->dma_data.maxburst = 4; + + ret = devm_snd_soc_register_component(&pdev->dev, + &img_spdif_out_component, + &img_spdif_out_dai, 1); + if (ret) + goto err_suspend; + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + if (ret) + goto err_suspend; + + dev_dbg(&pdev->dev, "Probe successful\n"); + + return 0; + +err_suspend: + if (!pm_runtime_status_suspended(&pdev->dev)) + img_spdif_out_suspend(&pdev->dev); +err_pm_disable: + pm_runtime_disable(&pdev->dev); + clk_disable_unprepare(spdif->clk_sys); + + return ret; +} + +static int img_spdif_out_dev_remove(struct platform_device *pdev) +{ + struct img_spdif_out *spdif = platform_get_drvdata(pdev); + + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + img_spdif_out_suspend(&pdev->dev); + + clk_disable_unprepare(spdif->clk_sys); + + return 0; +} + +static const struct of_device_id img_spdif_out_of_match[] = { + { .compatible = "img,spdif-out" }, + {} +}; +MODULE_DEVICE_TABLE(of, img_spdif_out_of_match); + +static const struct dev_pm_ops img_spdif_out_pm_ops = { + SET_RUNTIME_PM_OPS(img_spdif_out_suspend, + img_spdif_out_resume, NULL) +}; + +static struct platform_driver img_spdif_out_driver = { + .driver = { + .name = "img-spdif-out", + .of_match_table = img_spdif_out_of_match, + .pm = &img_spdif_out_pm_ops + }, + .probe = img_spdif_out_probe, + .remove = img_spdif_out_dev_remove +}; +module_platform_driver(img_spdif_out_driver); + +MODULE_AUTHOR("Damien Horsley "); +MODULE_DESCRIPTION("IMG SPDIF Output driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 7f0e823d58b7574cbe417d5bbc285891baed4437 Mon Sep 17 00:00:00 2001 From: "Damien.Horsley" Date: Tue, 10 Nov 2015 14:09:35 +0000 Subject: ASoC: img: parallel out: Add missing initialiser Add missing initialiser for control_set variable in img_prl_out_set_fmt Signed-off-by: Damien.Horsley Signed-off-by: Mark Brown --- sound/soc/img/img-parallel-out.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/img/img-parallel-out.c b/sound/soc/img/img-parallel-out.c index beda18b18c64..c1610a054d65 100644 --- a/sound/soc/img/img-parallel-out.c +++ b/sound/soc/img/img-parallel-out.c @@ -154,7 +154,7 @@ static int img_prl_out_hw_params(struct snd_pcm_substream *substream, static int img_prl_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai); - u32 reg, control_set; + u32 reg, control_set = 0; switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: -- cgit v1.2.3 From a28f51db28a3bb550ee54e4e67b4b1d04b4b393a Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Tue, 27 Oct 2015 09:22:44 +0900 Subject: ASoC: Intel: Skylake: Fix to correct check for non DSP widget To get the FE copier module, the check to ignore non DSP widgets was wrong. This path corrects the check to ignore non DSP widget. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index a7854c8fc523..98ccd42b8867 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -846,7 +846,7 @@ skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream) w = dai->playback_widget; snd_soc_dapm_widget_for_each_sink_path(w, p) { if (p->connect && p->sink->power && - is_skl_dsp_widget_type(p->sink)) + !is_skl_dsp_widget_type(p->sink)) continue; if (p->sink->priv) { @@ -859,7 +859,7 @@ skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream) w = dai->capture_widget; snd_soc_dapm_widget_for_each_source_path(w, p) { if (p->connect && p->source->power && - is_skl_dsp_widget_type(p->source)) + !is_skl_dsp_widget_type(p->source)) continue; if (p->source->priv) { -- cgit v1.2.3 From 4bd073f93f13ad5de8affb173056827117a4a930 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Tue, 27 Oct 2015 09:22:45 +0900 Subject: ASoC: Intel: Skylake: Fix not to ignore return value in be hw_params Return value from skl_tplg_be_update_params() is ignored. But if the blob is null then the hw_params needs to return error. This patch fixes the issue by not ignoring return value from skl_tplg_be_update_params(). Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-pcm.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index a2f94ce1679d..1242beac4e46 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -291,9 +291,8 @@ static int skl_be_hw_params(struct snd_pcm_substream *substream, p_params.ch = params_channels(params); p_params.s_freq = params_rate(params); p_params.stream = substream->stream; - skl_tplg_be_update_params(dai, &p_params); - return 0; + return skl_tplg_be_update_params(dai, &p_params); } static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, @@ -352,9 +351,7 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream, p_params.stream = substream->stream; p_params.link_dma_id = hdac_stream(link_dev)->stream_tag - 1; - skl_tplg_be_update_params(dai, &p_params); - - return 0; + return skl_tplg_be_update_params(dai, &p_params); } static int skl_link_pcm_prepare(struct snd_pcm_substream *substream, -- cgit v1.2.3 From 6654f39eb4a65b61c3550be352662f57a2701bbc Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Tue, 27 Oct 2015 09:22:46 +0900 Subject: ASoC: Intel: Skylake: Fix to add 32 bit in update FE params In case of 32 bit, the FE update params returns error as it falls thru to default case. This patch adds 32 bit depth handling in update FE params. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 98ccd42b8867..313a02d8db01 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -809,6 +809,7 @@ int skl_tplg_update_pipe_params(struct device *dev, break; case SKL_DEPTH_24BIT: + case SKL_DEPTH_32BIT: format->bit_depth = SKL_DEPTH_32BIT; break; -- cgit v1.2.3 From 029890c67ae6f95c3f7d84af9b7e555515b33193 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Tue, 27 Oct 2015 09:22:47 +0900 Subject: ASoC: Intel: Skylake: Fix to ignore codec_mask check in probe We have both I2S and hda codec support in the driver. codec_mask check is relevant only for hda codec and some boards may have only I2S Codec, so removed probe error in case no hda codec is found and update the log to info as it may not be error. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 5319529aedf7..211ef6e2fa21 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -434,8 +434,7 @@ static int skl_first_init(struct hdac_ext_bus *ebus) /* codec detection */ if (!bus->codec_mask) { - dev_err(bus->dev, "no codecs found!\n"); - return -ENODEV; + dev_info(bus->dev, "no hda codecs found!\n"); } return 0; -- cgit v1.2.3 From b30c275e449ac1c7e149e2138a342c407d8cab3b Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Tue, 27 Oct 2015 09:22:48 +0900 Subject: ASoC: Intel: Skylake: Fix to ignore blob check if link type is HDA If link type is HDA, NHLT blob is null, as NHLT defines non HDA links only. So we should ignore blob query for HDA links. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 313a02d8db01..e11a9e44d064 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -921,6 +921,9 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, memcpy(pipe->p_params, params, sizeof(*params)); + if (link_type == NHLT_LINK_HDA) + return 0; + /* update the blob based on virtual bus_id*/ cfg = skl_get_ep_blob(skl, mconfig->vbus_id, link_type, params->s_fmt, params->ch, -- cgit v1.2.3 From 4f7457089df2984aeee59ec01525aa9917e889e7 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Tue, 27 Oct 2015 09:22:49 +0900 Subject: ASoC: Intel: Skylake: Fix support for multiple pins in a module For supporting multiple dynamic pins, module state check is incorrect. In case of unbind, module state need to be changed to uninit if all pins in the module is is unbind state. To handle module state correctly add pin state and use pin state check to set module state correctly. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-messages.c | 83 ++++++++++++++++++++++------------ sound/soc/intel/skylake/skl-topology.c | 1 + sound/soc/intel/skylake/skl-topology.h | 10 +++- 3 files changed, 65 insertions(+), 29 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 50a109503a3f..ee059589e9f0 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -571,10 +571,10 @@ static int skl_get_queue_index(struct skl_module_pin *mpin, * In static, the pin_index is fixed based on module_id and instance id */ static int skl_alloc_queue(struct skl_module_pin *mpin, - struct skl_module_inst_id id, int max) + struct skl_module_cfg *tgt_cfg, int max) { int i; - + struct skl_module_inst_id id = tgt_cfg->id; /* * if pin in dynamic, find first free pin * otherwise find match module and instance id pin as topology will @@ -583,16 +583,23 @@ static int skl_alloc_queue(struct skl_module_pin *mpin, */ for (i = 0; i < max; i++) { if (mpin[i].is_dynamic) { - if (!mpin[i].in_use) { + if (!mpin[i].in_use && + mpin[i].pin_state == SKL_PIN_UNBIND) { + mpin[i].in_use = true; mpin[i].id.module_id = id.module_id; mpin[i].id.instance_id = id.instance_id; + mpin[i].tgt_mcfg = tgt_cfg; return i; } } else { if (mpin[i].id.module_id == id.module_id && - mpin[i].id.instance_id == id.instance_id) + mpin[i].id.instance_id == id.instance_id && + mpin[i].pin_state == SKL_PIN_UNBIND) { + + mpin[i].tgt_mcfg = tgt_cfg; return i; + } } } @@ -606,6 +613,28 @@ static void skl_free_queue(struct skl_module_pin *mpin, int q_index) mpin[q_index].id.module_id = 0; mpin[q_index].id.instance_id = 0; } + mpin[q_index].pin_state = SKL_PIN_UNBIND; + mpin[q_index].tgt_mcfg = NULL; +} + +/* Module state will be set to unint, if all the out pin state is UNBIND */ + +static void skl_clear_module_state(struct skl_module_pin *mpin, int max, + struct skl_module_cfg *mcfg) +{ + int i; + bool found = false; + + for (i = 0; i < max; i++) { + if (mpin[i].pin_state == SKL_PIN_UNBIND) + continue; + found = true; + break; + } + + if (!found) + mcfg->m_state = SKL_MODULE_UNINIT; + return; } /* @@ -682,37 +711,30 @@ int skl_unbind_modules(struct skl_sst *ctx, struct skl_module_inst_id dst_id = dst_mcfg->id; int in_max = dst_mcfg->max_in_queue; int out_max = src_mcfg->max_out_queue; - int src_index, dst_index; + int src_index, dst_index, src_pin_state, dst_pin_state; skl_dump_bind_info(ctx, src_mcfg, dst_mcfg); - if (src_mcfg->m_state != SKL_MODULE_BIND_DONE) - return 0; - - /* - * if intra module unbind, check if both modules are BIND, - * then send unbind - */ - if ((src_mcfg->pipe->ppl_id != dst_mcfg->pipe->ppl_id) && - dst_mcfg->m_state != SKL_MODULE_BIND_DONE) - return 0; - else if (src_mcfg->m_state < SKL_MODULE_INIT_DONE && - dst_mcfg->m_state < SKL_MODULE_INIT_DONE) - return 0; - /* get src queue index */ src_index = skl_get_queue_index(src_mcfg->m_out_pin, dst_id, out_max); if (src_index < 0) return -EINVAL; - msg.src_queue = src_mcfg->m_out_pin[src_index].pin_index; + msg.src_queue = src_index; /* get dst queue index */ dst_index = skl_get_queue_index(dst_mcfg->m_in_pin, src_id, in_max); if (dst_index < 0) return -EINVAL; - msg.dst_queue = dst_mcfg->m_in_pin[dst_index].pin_index; + msg.dst_queue = dst_index; + + src_pin_state = src_mcfg->m_out_pin[src_index].pin_state; + dst_pin_state = dst_mcfg->m_in_pin[dst_index].pin_state; + + if (src_pin_state != SKL_PIN_BIND_DONE || + dst_pin_state != SKL_PIN_BIND_DONE) + return 0; msg.module_id = src_mcfg->id.module_id; msg.instance_id = src_mcfg->id.instance_id; @@ -722,10 +744,15 @@ int skl_unbind_modules(struct skl_sst *ctx, ret = skl_ipc_bind_unbind(&ctx->ipc, &msg); if (!ret) { - src_mcfg->m_state = SKL_MODULE_UNINIT; /* free queue only if unbind is success */ skl_free_queue(src_mcfg->m_out_pin, src_index); skl_free_queue(dst_mcfg->m_in_pin, dst_index); + + /* + * check only if src module bind state, bind is + * always from src -> sink + */ + skl_clear_module_state(src_mcfg->m_out_pin, out_max, src_mcfg); } return ret; @@ -744,8 +771,6 @@ int skl_bind_modules(struct skl_sst *ctx, { int ret; struct skl_ipc_bind_unbind_msg msg; - struct skl_module_inst_id src_id = src_mcfg->id; - struct skl_module_inst_id dst_id = dst_mcfg->id; int in_max = dst_mcfg->max_in_queue; int out_max = src_mcfg->max_out_queue; int src_index, dst_index; @@ -756,18 +781,18 @@ int skl_bind_modules(struct skl_sst *ctx, dst_mcfg->m_state < SKL_MODULE_INIT_DONE) return 0; - src_index = skl_alloc_queue(src_mcfg->m_out_pin, dst_id, out_max); + src_index = skl_alloc_queue(src_mcfg->m_out_pin, dst_mcfg, out_max); if (src_index < 0) return -EINVAL; - msg.src_queue = src_mcfg->m_out_pin[src_index].pin_index; - dst_index = skl_alloc_queue(dst_mcfg->m_in_pin, src_id, in_max); + msg.src_queue = src_index; + dst_index = skl_alloc_queue(dst_mcfg->m_in_pin, src_mcfg, in_max); if (dst_index < 0) { skl_free_queue(src_mcfg->m_out_pin, src_index); return -EINVAL; } - msg.dst_queue = dst_mcfg->m_in_pin[dst_index].pin_index; + msg.dst_queue = dst_index; dev_dbg(ctx->dev, "src queue = %d dst queue =%d\n", msg.src_queue, msg.dst_queue); @@ -782,6 +807,8 @@ int skl_bind_modules(struct skl_sst *ctx, if (!ret) { src_mcfg->m_state = SKL_MODULE_BIND_DONE; + src_mcfg->m_out_pin[src_index].pin_state = SKL_PIN_BIND_DONE; + dst_mcfg->m_in_pin[dst_index].pin_state = SKL_PIN_BIND_DONE; } else { /* error case , if IPC fails, clear the queue index */ skl_free_queue(src_mcfg->m_out_pin, src_index); diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index e11a9e44d064..e8258d4807ff 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1049,6 +1049,7 @@ static void skl_fill_module_pin_info(struct skl_dfw_module_pin *dfw_pin, m_pin[i].id.instance_id = dfw_pin[i].instance_id; m_pin[i].in_use = false; m_pin[i].is_dynamic = is_dynamic; + m_pin[i].pin_state = SKL_PIN_UNBIND; } } diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 76053a8de41c..cd8768308f29 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -180,16 +180,24 @@ struct skl_module_fmt { u32 ch_cfg; }; +struct skl_module_cfg; + struct skl_module_inst_id { u32 module_id; u32 instance_id; }; +enum skl_module_pin_state { + SKL_PIN_UNBIND = 0, + SKL_PIN_BIND_DONE = 1, +}; + struct skl_module_pin { struct skl_module_inst_id id; - u8 pin_index; bool is_dynamic; bool in_use; + enum skl_module_pin_state pin_state; + struct skl_module_cfg *tgt_mcfg; }; struct skl_specific_cfg { -- cgit v1.2.3 From 83b50246d3f193ce7f0546786097ee673c359eb2 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Tue, 27 Oct 2015 09:22:50 +0900 Subject: ASoC: Intel: Skylake: Fix bit depth when querying the NHLT blob Bps calculation is not correct as this needs to be based on valid bit depth. 16 bit fmt bit depth is 16 bit and for 24 and 32 bit as it is container size This patch fixes the bps. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-nhlt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index b0c7bd113aac..3ff22eba5875 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -115,7 +115,7 @@ struct nhlt_specific_cfg struct device *dev = bus->dev; struct nhlt_specific_cfg *sp_config; struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt; - u16 bps = num_ch * s_fmt; + u16 bps = (s_fmt == 16) ? 16 : 32; u8 j; dump_config(dev, instance, link_type, s_fmt, num_ch, s_rate, dirn, bps); -- cgit v1.2.3 From ce1b5551a06af31a72feeb50c02a9fe22599926a Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Tue, 27 Oct 2015 09:22:51 +0900 Subject: ASoC: Intel: Skylake: use module_pin info for unbind in_pin and out_pin list for a module has the information about the module that are bound together. So we can directly look at pin information of module for binding and unbind. As a result the preinitialized dapm_path_last we had is removed and code and memory optimzed. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-pcm.c | 1 - sound/soc/intel/skylake/skl-topology.c | 108 +++++++++++---------------------- sound/soc/intel/skylake/skl-topology.h | 5 -- sound/soc/intel/skylake/skl.h | 1 - 4 files changed, 34 insertions(+), 81 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 1242beac4e46..1a9cd00c0b0a 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -938,7 +938,6 @@ int skl_platform_register(struct device *dev) struct skl *skl = ebus_to_skl(ebus); INIT_LIST_HEAD(&skl->ppl_list); - INIT_LIST_HEAD(&skl->dapm_path_list); ret = snd_soc_register_platform(dev, &skl_platform_drv); if (ret) { diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index e8258d4807ff..abbf8e7eb3e7 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -411,7 +411,6 @@ static int skl_tplg_pga_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, struct skl *skl) { struct snd_soc_dapm_path *p; - struct skl_dapm_path_list *path_list; struct snd_soc_dapm_widget *source, *sink; struct skl_module_cfg *src_mconfig, *sink_mconfig; struct skl_sst *ctx = skl->skl_sst; @@ -455,16 +454,6 @@ static int skl_tplg_pga_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, if (ret) return ret; } - - path_list = kzalloc( - sizeof(struct skl_dapm_path_list), - GFP_KERNEL); - if (path_list == NULL) - return -ENOMEM; - - /* Add connected path to one global list */ - path_list->dapm_path = p; - list_add_tail(&path_list->node, &skl->dapm_path_list); break; } } @@ -552,54 +541,37 @@ static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w, static int skl_tplg_mixer_dapm_pre_pmd_event(struct snd_soc_dapm_widget *w, struct skl *skl) { - struct snd_soc_dapm_widget *source, *sink; struct skl_module_cfg *src_mconfig, *sink_mconfig; - int ret = 0, path_found = 0; - struct skl_dapm_path_list *path_list, *tmp_list; + int ret = 0, i; struct skl_sst *ctx = skl->skl_sst; - sink = w; - sink_mconfig = sink->priv; + sink_mconfig = w->priv; /* Stop the pipe */ ret = skl_stop_pipe(ctx, sink_mconfig->pipe); if (ret) return ret; - /* - * This list, dapm_path_list handling here does not need any locks - * as we are under dapm lock while handling widget events. - * List can be manipulated safely only under dapm widgets handler - * routines - */ - list_for_each_entry_safe(path_list, tmp_list, - &skl->dapm_path_list, node) { - if (path_list->dapm_path->sink == sink) { - dev_dbg(ctx->dev, "Path found = %s\n", - path_list->dapm_path->name); - source = path_list->dapm_path->source; - src_mconfig = source->priv; - path_found = 1; + for (i = 0; i < sink_mconfig->max_in_queue; i++) { + if (sink_mconfig->m_in_pin[i].pin_state == SKL_PIN_BIND_DONE) { + src_mconfig = sink_mconfig->m_in_pin[i].tgt_mcfg; + if (!src_mconfig) + continue; + /* + * If path_found == 1, that means pmd for source + * pipe has not occurred, source is connected to + * some other sink. so its responsibility of sink + * to unbind itself from source. + */ + ret = skl_stop_pipe(ctx, src_mconfig->pipe); + if (ret < 0) + return ret; - list_del(&path_list->node); - kfree(path_list); - break; + ret = skl_unbind_modules(ctx, + src_mconfig, sink_mconfig); } } - /* - * If path_found == 1, that means pmd for source pipe has - * not occurred, source is connected to some other sink. - * so its responsibility of sink to unbind itself from source. - */ - if (path_found) { - ret = skl_stop_pipe(ctx, src_mconfig->pipe); - if (ret < 0) - return ret; - - ret = skl_unbind_modules(ctx, src_mconfig, sink_mconfig); - } - return ret; } @@ -653,14 +625,11 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w, static int skl_tplg_pga_dapm_post_pmd_event(struct snd_soc_dapm_widget *w, struct skl *skl) { - struct snd_soc_dapm_widget *source, *sink; struct skl_module_cfg *src_mconfig, *sink_mconfig; - int ret = 0, path_found = 0; - struct skl_dapm_path_list *path_list, *tmp_path_list; + int ret = 0, i; struct skl_sst *ctx = skl->skl_sst; - source = w; - src_mconfig = source->priv; + src_mconfig = w->priv; skl_tplg_free_pipe_mcps(skl, src_mconfig); /* Stop the pipe since this is a mixin module */ @@ -668,32 +637,23 @@ static int skl_tplg_pga_dapm_post_pmd_event(struct snd_soc_dapm_widget *w, if (ret) return ret; - list_for_each_entry_safe(path_list, tmp_path_list, &skl->dapm_path_list, node) { - if (path_list->dapm_path->source == source) { - dev_dbg(ctx->dev, "Path found = %s\n", - path_list->dapm_path->name); - sink = path_list->dapm_path->sink; - sink_mconfig = sink->priv; - path_found = 1; - - list_del(&path_list->node); - kfree(path_list); - break; + for (i = 0; i < src_mconfig->max_out_queue; i++) { + if (src_mconfig->m_out_pin[i].pin_state == SKL_PIN_BIND_DONE) { + sink_mconfig = src_mconfig->m_out_pin[i].tgt_mcfg; + if (!sink_mconfig) + continue; + /* + * This is a connecter and if path is found that means + * unbind between source and sink has not happened yet + */ + ret = skl_stop_pipe(ctx, sink_mconfig->pipe); + if (ret < 0) + return ret; + ret = skl_unbind_modules(ctx, src_mconfig, + sink_mconfig); } } - /* - * This is a connector and if path is found that means - * unbind between source and sink has not happened yet - */ - if (path_found) { - ret = skl_stop_pipe(ctx, src_mconfig->pipe); - if (ret < 0) - return ret; - - ret = skl_unbind_modules(ctx, src_mconfig, sink_mconfig); - } - return ret; } diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index cd8768308f29..1b35cb6c397a 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -280,11 +280,6 @@ struct skl_pipeline { struct list_head node; }; -struct skl_dapm_path_list { - struct snd_soc_dapm_path *dapm_path; - struct list_head node; -}; - static inline struct skl *get_skl_ctx(struct device *dev) { struct hdac_ext_bus *ebus = dev_get_drvdata(dev); diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index dd2e79ae45a8..f803ebb10605 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -67,7 +67,6 @@ struct skl { struct skl_dsp_resource resource; struct list_head ppl_list; - struct list_head dapm_path_list; }; #define skl_to_ebus(s) (&(s)->ebus) -- cgit v1.2.3 From 8724ff17521a91a87971027cf78631030091bc52 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Tue, 27 Oct 2015 09:22:52 +0900 Subject: ASoC: Intel: Skylake: Add support for virtual dsp widgets In SKL topology routes, some paths can be connected by a widget which are not a DSP FW widget and virtual with respect to firmware. In these case when module has to bind, then the virtual DSP modules needs to skipped till a actual DSP module is found which connects the pipelines. So we need to walk the graph and find a widget which is real in nature. This patch adds that support and splits skl_tplg_pga_dapm_pre_pmu_event() fn with parsing code to skl_tplg_bind_sinks() fn and call that recursively as well as while parsing The patch moves code a bit while splitting so diffstat doesn't tell real picture Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 133 ++++++++++++++++++++------------- 1 file changed, 83 insertions(+), 50 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index abbf8e7eb3e7..0c6e7833e652 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -397,40 +397,24 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, return 0; } -/* - * A PGA represents a module in a pipeline. So in the Pre-PMU event of PGA - * we need to do following: - * - Bind to sink pipeline - * Since the sink pipes can be running and we don't get mixer event on - * connect for already running mixer, we need to find the sink pipes - * here and bind to them. This way dynamic connect works. - * - Start sink pipeline, if not running - * - Then run current pipe - */ -static int skl_tplg_pga_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, - struct skl *skl) +static int skl_tplg_bind_sinks(struct snd_soc_dapm_widget *w, + struct skl *skl, + struct skl_module_cfg *src_mconfig) { struct snd_soc_dapm_path *p; - struct snd_soc_dapm_widget *source, *sink; - struct skl_module_cfg *src_mconfig, *sink_mconfig; + struct snd_soc_dapm_widget *sink = NULL; + struct skl_module_cfg *sink_mconfig; struct skl_sst *ctx = skl->skl_sst; - int ret = 0; - - source = w; - src_mconfig = source->priv; + int ret; - /* - * find which sink it is connected to, bind with the sink, - * if sink is not started, start sink pipe first, then start - * this pipe - */ - snd_soc_dapm_widget_for_each_source_path(w, p) { + snd_soc_dapm_widget_for_each_sink_path(w, p) { if (!p->connect) continue; dev_dbg(ctx->dev, "%s: src widget=%s\n", __func__, w->name); dev_dbg(ctx->dev, "%s: sink widget=%s\n", __func__, p->sink->name); + sink = p->sink; /* * here we will check widgets in sink pipelines, so that * can be any widgets type and we are only interested if @@ -440,7 +424,6 @@ static int skl_tplg_pga_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, is_skl_dsp_widget_type(p->sink)) { sink = p->sink; - src_mconfig = source->priv; sink_mconfig = sink->priv; /* Bind source to sink, mixin is always source */ @@ -454,10 +437,43 @@ static int skl_tplg_pga_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, if (ret) return ret; } - break; } } + if (!sink) + return skl_tplg_bind_sinks(sink, skl, src_mconfig); + + return 0; +} + +/* + * A PGA represents a module in a pipeline. So in the Pre-PMU event of PGA + * we need to do following: + * - Bind to sink pipeline + * Since the sink pipes can be running and we don't get mixer event on + * connect for already running mixer, we need to find the sink pipes + * here and bind to them. This way dynamic connect works. + * - Start sink pipeline, if not running + * - Then run current pipe + */ +static int skl_tplg_pga_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, + struct skl *skl) +{ + struct skl_module_cfg *src_mconfig; + struct skl_sst *ctx = skl->skl_sst; + int ret = 0; + + src_mconfig = w->priv; + + /* + * find which sink it is connected to, bind with the sink, + * if sink is not started, start sink pipe first, then start + * this pipe + */ + ret = skl_tplg_bind_sinks(w, skl, src_mconfig); + if (ret) + return ret; + /* Start source pipe last after starting all sinks */ ret = skl_run_pipe(ctx, src_mconfig->pipe); if (ret) @@ -466,6 +482,38 @@ static int skl_tplg_pga_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, return 0; } +static struct snd_soc_dapm_widget *skl_get_src_dsp_widget( + struct snd_soc_dapm_widget *w, struct skl *skl) +{ + struct snd_soc_dapm_path *p; + struct snd_soc_dapm_widget *src_w = NULL; + struct skl_sst *ctx = skl->skl_sst; + + snd_soc_dapm_widget_for_each_source_path(w, p) { + src_w = p->source; + if (!p->connect) + continue; + + dev_dbg(ctx->dev, "sink widget=%s\n", w->name); + dev_dbg(ctx->dev, "src widget=%s\n", p->source->name); + + /* + * here we will check widgets in sink pipelines, so that can + * be any widgets type and we are only interested if they are + * ones used for SKL so check that first + */ + if ((p->source->priv != NULL) && + is_skl_dsp_widget_type(p->source)) { + return p->source; + } + } + + if (src_w != NULL) + return skl_get_src_dsp_widget(src_w, skl); + + return NULL; +} + /* * in the Post-PMU event of mixer we need to do following: * - Check if this pipe is running @@ -479,7 +527,6 @@ static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w, struct skl *skl) { int ret = 0; - struct snd_soc_dapm_path *p; struct snd_soc_dapm_widget *source, *sink; struct skl_module_cfg *src_mconfig, *sink_mconfig; struct skl_sst *ctx = skl->skl_sst; @@ -493,32 +540,18 @@ static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w, * one more sink before this sink got connected, Since source is * started, bind this sink to source and start this pipe. */ - snd_soc_dapm_widget_for_each_sink_path(w, p) { - if (!p->connect) - continue; - - dev_dbg(ctx->dev, "sink widget=%s\n", w->name); - dev_dbg(ctx->dev, "src widget=%s\n", p->source->name); + source = skl_get_src_dsp_widget(w, skl); + if (source != NULL) { + src_mconfig = source->priv; + sink_mconfig = sink->priv; + src_pipe_started = 1; /* - * here we will check widgets in sink pipelines, so that - * can be any widgets type and we are only interested if - * they are ones used for SKL so check that first + * check pipe state, then no need to bind or start the + * pipe */ - if ((p->source->priv != NULL) && - is_skl_dsp_widget_type(p->source)) { - source = p->source; - src_mconfig = source->priv; - sink_mconfig = sink->priv; - src_pipe_started = 1; - - /* - * check pipe state, then no need to bind or start - * the pipe - */ - if (src_mconfig->pipe->state != SKL_PIPE_STARTED) - src_pipe_started = 0; - } + if (src_mconfig->pipe->state != SKL_PIPE_STARTED) + src_pipe_started = 0; } if (src_pipe_started) { -- cgit v1.2.3 From d1730c3dd90bfac6dffc29b1575837d45edca8cc Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Tue, 27 Oct 2015 09:22:53 +0900 Subject: ASoC: Intel: Skylake: Fix DSP pipe underrun/overrun issue While rigourous testing of SKL drivers, we noticed underuns and overuns and on debug realized that we need to change driver handling of FE pipe startup and shutdown We need to start DMA and then run pipe together and not split these up. Similarly while stopping we should stop pipe and then DMA in a sequence. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-pcm.c | 133 +++++++++++++++++++-------------- sound/soc/intel/skylake/skl-topology.c | 13 ++-- 2 files changed, 85 insertions(+), 61 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 1a9cd00c0b0a..2517ec576ffc 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -295,29 +295,101 @@ static int skl_be_hw_params(struct snd_pcm_substream *substream, return skl_tplg_be_update_params(dai, &p_params); } +static int skl_decoupled_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + struct hdac_ext_bus *ebus = get_bus_ctx(substream); + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_ext_stream *stream; + int start; + unsigned long cookie; + struct hdac_stream *hstr; + + stream = get_hdac_ext_stream(substream); + hstr = hdac_stream(stream); + + if (!hstr->prepared) + return -EPIPE; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + start = 1; + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + start = 0; + break; + + default: + return -EINVAL; + } + + spin_lock_irqsave(&bus->reg_lock, cookie); + + if (start) { + snd_hdac_stream_start(hdac_stream(stream), true); + snd_hdac_stream_timecounter_init(hstr, 0); + } else { + snd_hdac_stream_stop(hdac_stream(stream)); + } + + spin_unlock_irqrestore(&bus->reg_lock, cookie); + + return 0; +} + static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct skl *skl = get_skl_ctx(dai->dev); struct skl_sst *ctx = skl->skl_sst; struct skl_module_cfg *mconfig; + int ret; mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream); if (!mconfig) return -EIO; switch (cmd) { + case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: + /* + * Start HOST DMA and Start FE Pipe.This is to make sure that + * there are no underrun/overrun in the case when the FE + * pipeline is started but there is a delay in starting the + * DMA channel on the host. + */ + ret = skl_decoupled_trigger(substream, cmd); + if (ret < 0) + return ret; return skl_run_pipe(ctx, mconfig->pipe); + break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: - return skl_stop_pipe(ctx, mconfig->pipe); + case SNDRV_PCM_TRIGGER_STOP: + /* + * Stop FE Pipe first and stop DMA. This is to make sure that + * there are no underrun/overrun in the case if there is a delay + * between the two operations. + */ + ret = skl_stop_pipe(ctx, mconfig->pipe); + if (ret < 0) + return ret; + + ret = skl_decoupled_trigger(substream, cmd); + break; default: - return 0; + return -EINVAL; } + + return 0; } static int skl_link_hw_params(struct snd_pcm_substream *substream, @@ -685,66 +757,15 @@ static int skl_coupled_trigger(struct snd_pcm_substream *substream, return 0; } -static int skl_decoupled_trigger(struct snd_pcm_substream *substream, - int cmd) -{ - struct hdac_ext_bus *ebus = get_bus_ctx(substream); - struct hdac_bus *bus = ebus_to_hbus(ebus); - struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct hdac_ext_stream *stream; - int start; - unsigned long cookie; - struct hdac_stream *hstr; - - dev_dbg(bus->dev, "In %s cmd=%d streamname=%s\n", __func__, cmd, cpu_dai->name); - - stream = get_hdac_ext_stream(substream); - hstr = hdac_stream(stream); - - if (!hstr->prepared) - return -EPIPE; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - case SNDRV_PCM_TRIGGER_RESUME: - start = 1; - break; - - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_STOP: - start = 0; - break; - - default: - return -EINVAL; - } - - spin_lock_irqsave(&bus->reg_lock, cookie); - - if (start) - snd_hdac_stream_start(hdac_stream(stream), true); - else - snd_hdac_stream_stop(hdac_stream(stream)); - - if (start) - snd_hdac_stream_timecounter_init(hstr, 0); - - spin_unlock_irqrestore(&bus->reg_lock, cookie); - - return 0; -} static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct hdac_ext_bus *ebus = get_bus_ctx(substream); - if (ebus->ppcap) - return skl_decoupled_trigger(substream, cmd); - else + if (!ebus->ppcap) return skl_coupled_trigger(substream, cmd); + + return 0; } /* calculate runtime delay from LPIB */ diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 0c6e7833e652..2f263ddd696d 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -433,7 +433,10 @@ static int skl_tplg_bind_sinks(struct snd_soc_dapm_widget *w, /* Start sinks pipe first */ if (sink_mconfig->pipe->state != SKL_PIPE_STARTED) { - ret = skl_run_pipe(ctx, sink_mconfig->pipe); + if (sink_mconfig->pipe->conn_type != + SKL_PIPE_CONN_TYPE_FE) + ret = skl_run_pipe(ctx, + sink_mconfig->pipe); if (ret) return ret; } @@ -475,9 +478,8 @@ static int skl_tplg_pga_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, return ret; /* Start source pipe last after starting all sinks */ - ret = skl_run_pipe(ctx, src_mconfig->pipe); - if (ret) - return ret; + if (src_mconfig->pipe->conn_type != SKL_PIPE_CONN_TYPE_FE) + return skl_run_pipe(ctx, src_mconfig->pipe); return 0; } @@ -559,7 +561,8 @@ static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w, if (ret) return ret; - ret = skl_run_pipe(ctx, sink_mconfig->pipe); + if (sink_mconfig->pipe->conn_type != SKL_PIPE_CONN_TYPE_FE) + ret = skl_run_pipe(ctx, sink_mconfig->pipe); } return ret; -- cgit v1.2.3 From 9a03cb49c138146476261e5f9e3189a2631e70c1 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Tue, 27 Oct 2015 09:22:54 +0900 Subject: ASoC: Intel: Skylake: Fix to remove be copier widget power check ASoC core already checks if BE is active. If BE is active, hw_params callback is ignored. This patch removes the redundant check in driver for copier widget power check in update be hw_params. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 2f263ddd696d..7311cd317d87 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -950,18 +950,13 @@ static int skl_tplg_be_set_src_pipe_params(struct snd_soc_dai *dai, if (p->connect && is_skl_dsp_widget_type(p->source) && p->source->priv) { - if (!p->source->power) { - ret = skl_tplg_be_fill_pipe_params( - dai, p->source->priv, - params); - if (ret < 0) - return ret; - } else { - return -EBUSY; - } + ret = skl_tplg_be_fill_pipe_params(dai, + p->source->priv, params); + if (ret < 0) + return ret; } else { - ret = skl_tplg_be_set_src_pipe_params( - dai, p->source, params); + ret = skl_tplg_be_set_src_pipe_params(dai, + p->source, params); if (ret < 0) return ret; } @@ -980,15 +975,10 @@ static int skl_tplg_be_set_sink_pipe_params(struct snd_soc_dai *dai, if (p->connect && is_skl_dsp_widget_type(p->sink) && p->sink->priv) { - if (!p->sink->power) { - ret = skl_tplg_be_fill_pipe_params( - dai, p->sink->priv, params); - if (ret < 0) - return ret; - } else { - return -EBUSY; - } - + ret = skl_tplg_be_fill_pipe_params(dai, + p->sink->priv, params); + if (ret < 0) + return ret; } else { ret = skl_tplg_be_set_sink_pipe_params( dai, p->sink, params); -- cgit v1.2.3 From 4cd9899f0d16b475e31b20771de6f580b977daa4 Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Tue, 27 Oct 2015 09:22:55 +0900 Subject: ASoC: Intel: Skylake: Add multiple pin formats The module pin formats are considered homogeneous, but some modules can have different pcm formats on different pins, like reference signal for a module. This patch add support for configuration of each pin of module and allows us to specify if pins and homogeneous or heterogeneous Signed-off-by: Hardik T Shah Signed-off-by: Omair M Abdullah Signed-off-by: Jeeja KP Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-messages.c | 8 +-- sound/soc/intel/skylake/skl-topology.c | 90 ++++++++++++++++------------ sound/soc/intel/skylake/skl-topology.h | 12 +++- sound/soc/intel/skylake/skl-tplg-interface.h | 18 +++++- 4 files changed, 83 insertions(+), 45 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index ee059589e9f0..07d3bf4a8bdd 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -280,7 +280,7 @@ static void skl_set_base_module_format(struct skl_sst *ctx, struct skl_module_cfg *mconfig, struct skl_base_cfg *base_cfg) { - struct skl_module_fmt *format = &mconfig->in_fmt; + struct skl_module_fmt *format = &mconfig->in_fmt[0]; base_cfg->audio_fmt.number_of_channels = (u8)format->channels; @@ -399,7 +399,7 @@ static void skl_setup_out_format(struct skl_sst *ctx, struct skl_module_cfg *mconfig, struct skl_audio_data_format *out_fmt) { - struct skl_module_fmt *format = &mconfig->out_fmt; + struct skl_module_fmt *format = &mconfig->out_fmt[0]; out_fmt->number_of_channels = (u8)format->channels; out_fmt->s_freq = format->s_freq; @@ -423,7 +423,7 @@ static void skl_set_src_format(struct skl_sst *ctx, struct skl_module_cfg *mconfig, struct skl_src_module_cfg *src_mconfig) { - struct skl_module_fmt *fmt = &mconfig->out_fmt; + struct skl_module_fmt *fmt = &mconfig->out_fmt[0]; skl_set_base_module_format(ctx, mconfig, (struct skl_base_cfg *)src_mconfig); @@ -440,7 +440,7 @@ static void skl_set_updown_mixer_format(struct skl_sst *ctx, struct skl_module_cfg *mconfig, struct skl_up_down_mixer_cfg *mixer_mconfig) { - struct skl_module_fmt *fmt = &mconfig->out_fmt; + struct skl_module_fmt *fmt = &mconfig->out_fmt[0]; int i = 0; skl_set_base_module_format(ctx, mconfig, diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 7311cd317d87..37e5c4fc0f10 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -129,17 +129,15 @@ static void skl_dump_mconfig(struct skl_sst *ctx, { dev_dbg(ctx->dev, "Dumping config\n"); dev_dbg(ctx->dev, "Input Format:\n"); - dev_dbg(ctx->dev, "channels = %d\n", mcfg->in_fmt.channels); - dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->in_fmt.s_freq); - dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->in_fmt.ch_cfg); - dev_dbg(ctx->dev, "valid bit depth = %d\n", - mcfg->in_fmt.valid_bit_depth); + dev_dbg(ctx->dev, "channels = %d\n", mcfg->in_fmt[0].channels); + dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->in_fmt[0].s_freq); + dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->in_fmt[0].ch_cfg); + dev_dbg(ctx->dev, "valid bit depth = %d\n", mcfg->in_fmt[0].valid_bit_depth); dev_dbg(ctx->dev, "Output Format:\n"); - dev_dbg(ctx->dev, "channels = %d\n", mcfg->out_fmt.channels); - dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->out_fmt.s_freq); - dev_dbg(ctx->dev, "valid bit depth = %d\n", - mcfg->out_fmt.valid_bit_depth); - dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->out_fmt.ch_cfg); + dev_dbg(ctx->dev, "channels = %d\n", mcfg->out_fmt[0].channels); + dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->out_fmt[0].s_freq); + dev_dbg(ctx->dev, "valid bit depth = %d\n", mcfg->out_fmt[0].valid_bit_depth); + dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->out_fmt[0].ch_cfg); } static void skl_tplg_update_params(struct skl_module_fmt *fmt, @@ -171,8 +169,9 @@ static void skl_tplg_update_params_fixup(struct skl_module_cfg *m_cfg, int in_fixup, out_fixup; struct skl_module_fmt *in_fmt, *out_fmt; - in_fmt = &m_cfg->in_fmt; - out_fmt = &m_cfg->out_fmt; + /* Fixups will be applied to pin 0 only */ + in_fmt = &m_cfg->in_fmt[0]; + out_fmt = &m_cfg->out_fmt[0]; if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (is_fe) { @@ -209,18 +208,25 @@ static void skl_tplg_update_buffer_size(struct skl_sst *ctx, struct skl_module_cfg *mcfg) { int multiplier = 1; + struct skl_module_fmt *in_fmt, *out_fmt; + + + /* Since fixups is applied to pin 0 only, ibs, obs needs + * change for pin 0 only + */ + in_fmt = &mcfg->in_fmt[0]; + out_fmt = &mcfg->out_fmt[0]; if (mcfg->m_type == SKL_MODULE_TYPE_SRCINT) multiplier = 5; - - mcfg->ibs = (mcfg->in_fmt.s_freq / 1000) * - (mcfg->in_fmt.channels) * - (mcfg->in_fmt.bit_depth >> 3) * + mcfg->ibs = (in_fmt->s_freq / 1000) * + (mcfg->in_fmt->channels) * + (mcfg->in_fmt->bit_depth >> 3) * multiplier; - mcfg->obs = (mcfg->out_fmt.s_freq / 1000) * - (mcfg->out_fmt.channels) * - (mcfg->out_fmt.bit_depth >> 3) * + mcfg->obs = (mcfg->out_fmt->s_freq / 1000) * + (mcfg->out_fmt->channels) * + (mcfg->out_fmt->bit_depth >> 3) * multiplier; } @@ -786,9 +792,9 @@ int skl_tplg_update_pipe_params(struct device *dev, memcpy(pipe->p_params, params, sizeof(*params)); if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) - format = &mconfig->in_fmt; + format = &mconfig->in_fmt[0]; else - format = &mconfig->out_fmt; + format = &mconfig->out_fmt[0]; /* set the hw_params */ format->s_freq = params->s_freq; @@ -1083,6 +1089,24 @@ static struct skl_pipe *skl_tplg_add_pipe(struct device *dev, return ppl->pipe; } +static void skl_tplg_fill_fmt(struct skl_module_fmt *dst_fmt, + struct skl_dfw_module_fmt *src_fmt, + int pins) +{ + int i; + + for (i = 0; i < pins; i++) { + dst_fmt[i].channels = src_fmt[i].channels; + dst_fmt[i].s_freq = src_fmt[i].freq; + dst_fmt[i].bit_depth = src_fmt[i].bit_depth; + dst_fmt[i].valid_bit_depth = src_fmt[i].valid_bit_depth; + dst_fmt[i].ch_cfg = src_fmt[i].ch_cfg; + dst_fmt[i].ch_map = src_fmt[i].ch_map; + dst_fmt[i].interleaving_style = src_fmt[i].interleaving_style; + dst_fmt[i].sample_type = src_fmt[i].sample_type; + } +} + /* * Topology core widget load callback * @@ -1121,18 +1145,11 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, mconfig->max_in_queue = dfw_config->max_in_queue; mconfig->max_out_queue = dfw_config->max_out_queue; mconfig->is_loadable = dfw_config->is_loadable; - mconfig->in_fmt.channels = dfw_config->in_fmt.channels; - mconfig->in_fmt.s_freq = dfw_config->in_fmt.freq; - mconfig->in_fmt.bit_depth = dfw_config->in_fmt.bit_depth; - mconfig->in_fmt.valid_bit_depth = - dfw_config->in_fmt.valid_bit_depth; - mconfig->in_fmt.ch_cfg = dfw_config->in_fmt.ch_cfg; - mconfig->out_fmt.channels = dfw_config->out_fmt.channels; - mconfig->out_fmt.s_freq = dfw_config->out_fmt.freq; - mconfig->out_fmt.bit_depth = dfw_config->out_fmt.bit_depth; - mconfig->out_fmt.valid_bit_depth = - dfw_config->out_fmt.valid_bit_depth; - mconfig->out_fmt.ch_cfg = dfw_config->out_fmt.ch_cfg; + skl_tplg_fill_fmt(mconfig->in_fmt, dfw_config->in_fmt, + MODULE_MAX_IN_PINS); + skl_tplg_fill_fmt(mconfig->out_fmt, dfw_config->out_fmt, + MODULE_MAX_OUT_PINS); + mconfig->params_fixup = dfw_config->params_fixup; mconfig->converter = dfw_config->converter; mconfig->m_type = dfw_config->module_type; @@ -1147,10 +1164,9 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, mconfig->time_slot = dfw_config->time_slot; mconfig->formats_config.caps_size = dfw_config->caps.caps_size; - mconfig->m_in_pin = devm_kzalloc(bus->dev, - (mconfig->max_in_queue) * - sizeof(*mconfig->m_in_pin), - GFP_KERNEL); + mconfig->m_in_pin = devm_kzalloc(bus->dev, (mconfig->max_in_queue) * + sizeof(*mconfig->m_in_pin), + GFP_KERNEL); if (!mconfig->m_in_pin) return -ENOMEM; diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 1b35cb6c397a..3b63450c6d5e 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -36,6 +36,9 @@ /* Maximum number of coefficients up down mixer module */ #define UP_DOWN_MIXER_MAX_COEFF 6 +#define MODULE_MAX_IN_PINS 8 +#define MODULE_MAX_OUT_PINS 8 + enum skl_channel_index { SKL_CHANNEL_LEFT = 0, SKL_CHANNEL_RIGHT = 1, @@ -178,6 +181,9 @@ struct skl_module_fmt { u32 bit_depth; u32 valid_bit_depth; u32 ch_cfg; + u32 interleaving_style; + u32 sample_type; + u32 ch_map; }; struct skl_module_cfg; @@ -247,8 +253,10 @@ enum skl_module_state { struct skl_module_cfg { struct skl_module_inst_id id; - struct skl_module_fmt in_fmt; - struct skl_module_fmt out_fmt; + bool homogenous_inputs; + bool homogenous_outputs; + struct skl_module_fmt in_fmt[MODULE_MAX_IN_PINS]; + struct skl_module_fmt out_fmt[MODULE_MAX_OUT_PINS]; u8 max_in_queue; u8 max_out_queue; u8 in_queue_mask; diff --git a/sound/soc/intel/skylake/skl-tplg-interface.h b/sound/soc/intel/skylake/skl-tplg-interface.h index 2bc396d54cbe..7bd9af7ee15c 100644 --- a/sound/soc/intel/skylake/skl-tplg-interface.h +++ b/sound/soc/intel/skylake/skl-tplg-interface.h @@ -110,6 +110,17 @@ enum skl_dev_type { SKL_DEVICE_NONE }; +enum module_pin_type { + /* All pins of the module takes same PCM inputs or outputs + * e.g. mixout + */ + SKL_PIN_TYPE_HOMOGENEOUS, + /* All pins of the module takes different PCM inputs or outputs + * e.g mux + */ + SKL_PIN_TYPE_HETEROGENEOUS, +}; + struct skl_dfw_module_pin { u16 module_id; u16 instance_id; @@ -121,6 +132,9 @@ struct skl_dfw_module_fmt { u32 bit_depth; u32 valid_bit_depth; u32 ch_cfg; + u32 interleaving_style; + u32 sample_type; + u32 ch_map; } __packed; struct skl_dfw_module_caps { @@ -156,8 +170,8 @@ struct skl_dfw_module { u8 is_dynamic_in_pin; u8 is_dynamic_out_pin; struct skl_dfw_pipe pipe; - struct skl_dfw_module_fmt in_fmt; - struct skl_dfw_module_fmt out_fmt; + struct skl_dfw_module_fmt in_fmt[MAX_IN_QUEUE]; + struct skl_dfw_module_fmt out_fmt[MAX_OUT_QUEUE]; struct skl_dfw_module_pin in_pin[MAX_IN_QUEUE]; struct skl_dfw_module_pin out_pin[MAX_OUT_QUEUE]; struct skl_dfw_module_caps caps; -- cgit v1.2.3 From 04afbbbb1cbacb4b18b2e30dd2b5b83531ecf01d Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Tue, 27 Oct 2015 09:22:56 +0900 Subject: ASoC: Intel: Skylake: Update the topology interface structure This patch updates the topology interface structure alignment and also updates the Sample interleaving defines Signed-off-by: Hardik T Shah Signed-off-by: Omair M Abdullah Signed-off-by: Jeeja KP Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.h | 7 +-- sound/soc/intel/skylake/skl-tplg-interface.h | 69 +++++++++++++++++++++------- 2 files changed, 54 insertions(+), 22 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 3b63450c6d5e..4b0a59898676 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -58,12 +58,6 @@ enum skl_bitdepth { SKL_DEPTH_INVALID }; -enum skl_interleaving { - /* [s1_ch1...s1_chN,...,sM_ch1...sM_chN] */ - SKL_INTERLEAVING_PER_CHANNEL = 0, - /* [s1_ch1...sM_ch1,...,s1_chN...sM_chN] */ - SKL_INTERLEAVING_PER_SAMPLE = 1, -}; enum skl_s_freq { SKL_FS_8000 = 8000, @@ -253,6 +247,7 @@ enum skl_module_state { struct skl_module_cfg { struct skl_module_inst_id id; + u8 domain; bool homogenous_inputs; bool homogenous_outputs; struct skl_module_fmt in_fmt[MODULE_MAX_IN_PINS]; diff --git a/sound/soc/intel/skylake/skl-tplg-interface.h b/sound/soc/intel/skylake/skl-tplg-interface.h index 7bd9af7ee15c..aeb8f251675a 100644 --- a/sound/soc/intel/skylake/skl-tplg-interface.h +++ b/sound/soc/intel/skylake/skl-tplg-interface.h @@ -72,6 +72,7 @@ enum skl_ch_cfg { SKL_CH_CFG_DUAL_MONO = 9, SKL_CH_CFG_I2S_DUAL_STEREO_0 = 10, SKL_CH_CFG_I2S_DUAL_STEREO_1 = 11, + SKL_CH_CFG_4_CHANNEL = 12, SKL_CH_CFG_INVALID }; @@ -110,6 +111,25 @@ enum skl_dev_type { SKL_DEVICE_NONE }; +/** + * enum skl_interleaving - interleaving style + * + * @SKL_INTERLEAVING_PER_CHANNEL: [s1_ch1...s1_chN,...,sM_ch1...sM_chN] + * @SKL_INTERLEAVING_PER_SAMPLE: [s1_ch1...sM_ch1,...,s1_chN...sM_chN] + */ +enum skl_interleaving { + SKL_INTERLEAVING_PER_CHANNEL = 0, + SKL_INTERLEAVING_PER_SAMPLE = 1, +}; + +enum skl_sample_type { + SKL_SAMPLE_TYPE_INT_MSB = 0, + SKL_SAMPLE_TYPE_INT_LSB = 1, + SKL_SAMPLE_TYPE_INT_SIGNED = 2, + SKL_SAMPLE_TYPE_INT_UNSIGNED = 3, + SKL_SAMPLE_TYPE_FLOAT = 4 +}; + enum module_pin_type { /* All pins of the module takes same PCM inputs or outputs * e.g. mixout @@ -138,6 +158,9 @@ struct skl_dfw_module_fmt { } __packed; struct skl_dfw_module_caps { + u32 set_params:1; + u32 rsvd:31; + u32 param_id; u32 caps_size; u32 caps[HDA_SST_CFG_MAX]; }; @@ -145,30 +168,41 @@ struct skl_dfw_module_caps { struct skl_dfw_pipe { u8 pipe_id; u8 pipe_priority; - u16 conn_type; - u32 memory_pages; + u16 conn_type:4; + u16 rsvd:4; + u16 memory_pages:8; } __packed; struct skl_dfw_module { u16 module_id; u16 instance_id; u32 max_mcps; - u8 core_id; - u8 max_in_queue; - u8 max_out_queue; - u8 is_loadable; - u8 conn_type; - u8 dev_type; - u8 hw_conn_type; - u8 time_slot; + u32 mem_pages; u32 obs; u32 ibs; - u32 params_fixup; - u32 converter; - u32 module_type; u32 vbus_id; - u8 is_dynamic_in_pin; - u8 is_dynamic_out_pin; + + u32 max_in_queue:8; + u32 max_out_queue:8; + u32 time_slot:8; + u32 core_id:4; + u32 rsvd1:4; + + u32 module_type:8; + u32 conn_type:4; + u32 dev_type:4; + u32 hw_conn_type:4; + u32 rsvd2:12; + + u32 params_fixup:8; + u32 converter:8; + u32 input_pin_type:1; + u32 output_pin_type:1; + u32 is_dynamic_in_pin:1; + u32 is_dynamic_out_pin:1; + u32 is_loadable:1; + u32 rsvd3:11; + struct skl_dfw_pipe pipe; struct skl_dfw_module_fmt in_fmt[MAX_IN_QUEUE]; struct skl_dfw_module_fmt out_fmt[MAX_OUT_QUEUE]; @@ -178,8 +212,11 @@ struct skl_dfw_module { } __packed; struct skl_dfw_algo_data { + u32 set_params:1; + u32 rsvd:31; + u32 param_id; u32 max; - char *params; + char params[0]; } __packed; #endif -- cgit v1.2.3 From 65aecfa884d5436dede4c4bdfbc33e4ea8026cad Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Tue, 27 Oct 2015 09:22:57 +0900 Subject: ASoC: Intel: Skylake: Add support for module GUIDs The DSP FW specifies loadable modules using GUIDs so add support to specify the GUIDs from topology Signed-off-by: Hardik T Shah Signed-off-by: Omair M Abdullah Signed-off-by: Jeeja KP Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 4 ++++ sound/soc/intel/skylake/skl-topology.h | 1 + sound/soc/intel/skylake/skl-tplg-interface.h | 3 +++ 3 files changed, 8 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 37e5c4fc0f10..3c5f06235889 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1164,6 +1164,10 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, mconfig->time_slot = dfw_config->time_slot; mconfig->formats_config.caps_size = dfw_config->caps.caps_size; + if (dfw_config->is_loadable) + memcpy(mconfig->guid, dfw_config->uuid, + ARRAY_SIZE(dfw_config->uuid)); + mconfig->m_in_pin = devm_kzalloc(bus->dev, (mconfig->max_in_queue) * sizeof(*mconfig->m_in_pin), GFP_KERNEL); diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 4b0a59898676..57cb7b8dd269 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -246,6 +246,7 @@ enum skl_module_state { }; struct skl_module_cfg { + char guid[SKL_UUID_STR_SZ]; struct skl_module_inst_id id; u8 domain; bool homogenous_inputs; diff --git a/sound/soc/intel/skylake/skl-tplg-interface.h b/sound/soc/intel/skylake/skl-tplg-interface.h index aeb8f251675a..20c068754d08 100644 --- a/sound/soc/intel/skylake/skl-tplg-interface.h +++ b/sound/soc/intel/skylake/skl-tplg-interface.h @@ -32,6 +32,7 @@ #define MAX_IN_QUEUE 8 #define MAX_OUT_QUEUE 8 +#define SKL_UUID_STR_SZ 40 /* Event types goes here */ /* Reserve event type 0 for no event handlers */ enum skl_event_types { @@ -174,6 +175,8 @@ struct skl_dfw_pipe { } __packed; struct skl_dfw_module { + char uuid[SKL_UUID_STR_SZ]; + u16 module_id; u16 instance_id; u32 max_mcps; -- cgit v1.2.3 From 16882d24b3f8c402caf56326aa7bf0448d70d8e6 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Tue, 27 Oct 2015 09:22:58 +0900 Subject: ASoC: Intel: Skylake: Ignore rate check for DMIC link DMIC NHLT entry is sample rate agnostic, so ignore the rate checks for DMIC type Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-nhlt.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index 3ff22eba5875..6e4b21cdb1bd 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -55,7 +55,7 @@ void skl_nhlt_free(void *addr) static struct nhlt_specific_cfg *skl_get_specific_cfg( struct device *dev, struct nhlt_fmt *fmt, - u8 no_ch, u32 rate, u16 bps) + u8 no_ch, u32 rate, u16 bps, u8 linktype) { struct nhlt_specific_cfg *sp_config; struct wav_fmt *wfmt; @@ -68,11 +68,17 @@ static struct nhlt_specific_cfg *skl_get_specific_cfg( wfmt = &fmt_config->fmt_ext.fmt; dev_dbg(dev, "ch=%d fmt=%d s_rate=%d\n", wfmt->channels, wfmt->bits_per_sample, wfmt->samples_per_sec); - if (wfmt->channels == no_ch && wfmt->samples_per_sec == rate && - wfmt->bits_per_sample == bps) { + if (wfmt->channels == no_ch && wfmt->bits_per_sample == bps) { + /* + * if link type is dmic ignore rate check as the blob is + * generic for all rates + */ sp_config = &fmt_config->config; + if (linktype == NHLT_LINK_DMIC) + return sp_config; - return sp_config; + if (wfmt->samples_per_sec == rate) + return sp_config; } fmt_config = (struct nhlt_fmt_cfg *)(fmt_config->config.caps + @@ -128,7 +134,8 @@ struct nhlt_specific_cfg if (skl_check_ep_match(dev, epnt, instance, link_type, dirn)) { fmt = (struct nhlt_fmt *)(epnt->config.caps + epnt->config.size); - sp_config = skl_get_specific_cfg(dev, fmt, num_ch, s_rate, bps); + sp_config = skl_get_specific_cfg(dev, fmt, num_ch, + s_rate, bps, link_type); if (sp_config) return sp_config; } -- cgit v1.2.3 From 3e81f1a3c702641227cc59c0dd7a2a5bec741e0f Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Tue, 27 Oct 2015 09:22:59 +0900 Subject: ASoC: Intel: Skylake: Fix to remove channel_map calculation Widget FW topology private data already has the information on the channel map, ch_cfg and interleaving. This patch removes the calculation of channel_map in driver and reads the value directly from widget private data. Signed-off-by: Jeeja KP Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-messages.c | 98 ++-------------------------------- 1 file changed, 5 insertions(+), 93 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 07d3bf4a8bdd..bfde60bb8119 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -182,94 +182,6 @@ enum skl_bitdepth skl_get_bit_depth(int params) } } -static u32 skl_create_channel_map(enum skl_ch_cfg ch_cfg) -{ - u32 config; - - switch (ch_cfg) { - case SKL_CH_CFG_MONO: - config = (0xFFFFFFF0 | SKL_CHANNEL_LEFT); - break; - - case SKL_CH_CFG_STEREO: - config = (0xFFFFFF00 | SKL_CHANNEL_LEFT - | (SKL_CHANNEL_RIGHT << 4)); - break; - - case SKL_CH_CFG_2_1: - config = (0xFFFFF000 | SKL_CHANNEL_LEFT - | (SKL_CHANNEL_RIGHT << 4) - | (SKL_CHANNEL_LFE << 8)); - break; - - case SKL_CH_CFG_3_0: - config = (0xFFFFF000 | SKL_CHANNEL_LEFT - | (SKL_CHANNEL_CENTER << 4) - | (SKL_CHANNEL_RIGHT << 8)); - break; - - case SKL_CH_CFG_3_1: - config = (0xFFFF0000 | SKL_CHANNEL_LEFT - | (SKL_CHANNEL_CENTER << 4) - | (SKL_CHANNEL_RIGHT << 8) - | (SKL_CHANNEL_LFE << 12)); - break; - - case SKL_CH_CFG_QUATRO: - config = (0xFFFF0000 | SKL_CHANNEL_LEFT - | (SKL_CHANNEL_RIGHT << 4) - | (SKL_CHANNEL_LEFT_SURROUND << 8) - | (SKL_CHANNEL_RIGHT_SURROUND << 12)); - break; - - case SKL_CH_CFG_4_0: - config = (0xFFFF0000 | SKL_CHANNEL_LEFT - | (SKL_CHANNEL_CENTER << 4) - | (SKL_CHANNEL_RIGHT << 8) - | (SKL_CHANNEL_CENTER_SURROUND << 12)); - break; - - case SKL_CH_CFG_5_0: - config = (0xFFF00000 | SKL_CHANNEL_LEFT - | (SKL_CHANNEL_CENTER << 4) - | (SKL_CHANNEL_RIGHT << 8) - | (SKL_CHANNEL_LEFT_SURROUND << 12) - | (SKL_CHANNEL_RIGHT_SURROUND << 16)); - break; - - case SKL_CH_CFG_5_1: - config = (0xFF000000 | SKL_CHANNEL_CENTER - | (SKL_CHANNEL_LEFT << 4) - | (SKL_CHANNEL_RIGHT << 8) - | (SKL_CHANNEL_LEFT_SURROUND << 12) - | (SKL_CHANNEL_RIGHT_SURROUND << 16) - | (SKL_CHANNEL_LFE << 20)); - break; - - case SKL_CH_CFG_DUAL_MONO: - config = (0xFFFFFF00 | SKL_CHANNEL_LEFT - | (SKL_CHANNEL_LEFT << 4)); - break; - - case SKL_CH_CFG_I2S_DUAL_STEREO_0: - config = (0xFFFFFF00 | SKL_CHANNEL_LEFT - | (SKL_CHANNEL_RIGHT << 4)); - break; - - case SKL_CH_CFG_I2S_DUAL_STEREO_1: - config = (0xFFFF00FF | (SKL_CHANNEL_LEFT << 8) - | (SKL_CHANNEL_RIGHT << 12)); - break; - - default: - config = 0xFFFFFFFF; - break; - - } - - return config; -} - /* * Each module in DSP expects a base module configuration, which consists of * PCM format information, which we calculate in driver and resource values @@ -293,10 +205,9 @@ static void skl_set_base_module_format(struct skl_sst *ctx, format->bit_depth, format->valid_bit_depth, format->ch_cfg); - base_cfg->audio_fmt.channel_map = skl_create_channel_map( - base_cfg->audio_fmt.ch_cfg); + base_cfg->audio_fmt.channel_map = format->ch_map; - base_cfg->audio_fmt.interleaving = SKL_INTERLEAVING_PER_CHANNEL; + base_cfg->audio_fmt.interleaving = format->interleaving_style; base_cfg->cps = mconfig->mcps; base_cfg->ibs = mconfig->ibs; @@ -407,8 +318,9 @@ static void skl_setup_out_format(struct skl_sst *ctx, out_fmt->valid_bit_depth = format->valid_bit_depth; out_fmt->ch_cfg = format->ch_cfg; - out_fmt->channel_map = skl_create_channel_map(out_fmt->ch_cfg); - out_fmt->interleaving = SKL_INTERLEAVING_PER_CHANNEL; + out_fmt->channel_map = format->ch_map; + out_fmt->interleaving = format->interleaving_style; + out_fmt->sample_type = format->sample_type; dev_dbg(ctx->dev, "copier out format chan=%d fre=%d bitdepth=%d\n", out_fmt->number_of_channels, format->s_freq, format->bit_depth); -- cgit v1.2.3 From 61722f447243d4d8f249a9359ffc5a21c1587f36 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Tue, 27 Oct 2015 09:23:00 +0900 Subject: ASoC: Intel: Skylake: Fix PM behaviour The driver runtime behaviour is fine but in suspend, we missed setting the DSP to suspend and also missed resuming DSP on resume. Fix this by having common SKL suspend and resume routines which power up/down links, suspend/resume DSP and other common routines, and call these routines from both runtime as well as system PM handlers Signed-off-by: Jayachandran B Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl.c | 60 +++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 25 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 211ef6e2fa21..9b94a8cdf9bd 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -129,6 +129,37 @@ static int skl_acquire_irq(struct hdac_ext_bus *ebus, int do_disconnect) return 0; } +#ifdef CONFIG_PM +static int _skl_suspend(struct hdac_ext_bus *ebus) +{ + struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = ebus_to_hbus(ebus); + int ret; + + snd_hdac_ext_bus_link_power_down_all(ebus); + + ret = skl_suspend_dsp(skl); + if (ret < 0) + return ret; + + snd_hdac_bus_stop_chip(bus); + snd_hdac_bus_enter_link_reset(bus); + + return 0; +} + +static int _skl_resume(struct hdac_ext_bus *ebus) +{ + struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = ebus_to_hbus(ebus); + + skl_init_pci(skl); + snd_hdac_bus_init_chip(bus, true); + + return skl_resume_dsp(skl); +} +#endif + #ifdef CONFIG_PM_SLEEP /* * power management @@ -137,26 +168,16 @@ static int skl_suspend(struct device *dev) { struct pci_dev *pci = to_pci_dev(dev); struct hdac_ext_bus *ebus = pci_get_drvdata(pci); - struct hdac_bus *bus = ebus_to_hbus(ebus); - - snd_hdac_bus_stop_chip(bus); - snd_hdac_bus_enter_link_reset(bus); - return 0; + return _skl_suspend(ebus); } static int skl_resume(struct device *dev) { struct pci_dev *pci = to_pci_dev(dev); struct hdac_ext_bus *ebus = pci_get_drvdata(pci); - struct hdac_bus *bus = ebus_to_hbus(ebus); - struct skl *hda = ebus_to_skl(ebus); - - skl_init_pci(hda); - snd_hdac_bus_init_chip(bus, 1); - - return 0; + return _skl_resume(ebus); } #endif /* CONFIG_PM_SLEEP */ @@ -166,24 +187,13 @@ static int skl_runtime_suspend(struct device *dev) struct pci_dev *pci = to_pci_dev(dev); struct hdac_ext_bus *ebus = pci_get_drvdata(pci); struct hdac_bus *bus = ebus_to_hbus(ebus); - struct skl *skl = ebus_to_skl(ebus); - int ret; dev_dbg(bus->dev, "in %s\n", __func__); /* enable controller wake up event */ snd_hdac_chip_updatew(bus, WAKEEN, 0, STATESTS_INT_MASK); - snd_hdac_ext_bus_link_power_down_all(ebus); - - ret = skl_suspend_dsp(skl); - if (ret < 0) - return ret; - - snd_hdac_bus_stop_chip(bus); - snd_hdac_bus_enter_link_reset(bus); - - return 0; + return _skl_suspend(ebus); } static int skl_runtime_resume(struct device *dev) @@ -204,7 +214,7 @@ static int skl_runtime_resume(struct device *dev) /* disable controller Wake Up event */ snd_hdac_chip_updatew(bus, WAKEEN, STATESTS_INT_MASK, 0); - return skl_resume_dsp(skl); + return _skl_resume(ebus); } #endif /* CONFIG_PM */ -- cgit v1.2.3 From 677165f76de2c785d4874d69be10dc21a5236bfb Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Thu, 29 Oct 2015 12:31:34 +0900 Subject: ASoC: Intel: Skylake: Fix the SSP0 Fmt fixup to 24 bit SSP0 FMT uses 24 bits so fix to the value to 24 bits Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/boards/skl_rt286.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c index a73a431bd8b7..e6af48491229 100644 --- a/sound/soc/intel/boards/skl_rt286.c +++ b/sound/soc/intel/boards/skl_rt286.c @@ -112,12 +112,15 @@ static int skylake_ssp0_fixup(struct snd_soc_pcm_runtime *rtd, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); /* The output is 48KHz, stereo, 16bits */ rate->min = rate->max = 48000; channels->min = channels->max = 2; - params_set_format(params, SNDRV_PCM_FORMAT_S16_LE); + /* set SSP0 to 24 bit */ + snd_mask_none(fmt); + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); return 0; } -- cgit v1.2.3 From aaec7e9f789eff57f620f38a96d0118b2a7d71c3 Mon Sep 17 00:00:00 2001 From: Vincent Stehlé Date: Thu, 29 Oct 2015 23:04:41 +0100 Subject: ASoC: Intel: Skylake: fix typo in sizeof MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The size of the pointer to a data structure to send is erroneously passed to sst_ipc_tx_message_wait() as its tx_bytes argument. It should be given the size of the pointed skl_ipc_dxstate_info structure instead. Coincidentally, both the pointer and the structure have the same size of 8 bytes on a 64 bit machine, which "masks" the issue. Compiling for 32 bit reveals the issue more clearly. Fix the typo for correctness, and to make the code robust to future evolutions of the skl_ipc_dxstate_info structure size. This fixes the following coccicheck error: sound/soc/intel/skylake/skl-sst-ipc.c:641:8-14: ERROR: application of sizeof to pointer Signed-off-by: Vincent Stehlé Cc: Subhransu S. Prusty Cc: Jeeja KP Cc: Vinod Koul Cc: Mark Brown Cc: trivial@kernel.org Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-sst-ipc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 3345ea0d4414..95679c02c6ee 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -650,7 +650,7 @@ int skl_ipc_set_dx(struct sst_generic_ipc *ipc, u8 instance_id, dev_dbg(ipc->dev, "In %s primary =%x ext=%x\n", __func__, header.primary, header.extension); ret = sst_ipc_tx_message_wait(ipc, *ipc_header, - dx, sizeof(dx), NULL, 0); + dx, sizeof(*dx), NULL, 0); if (ret < 0) { dev_err(ipc->dev, "ipc: set dx failed, err %d\n", ret); return ret; -- cgit v1.2.3 From b4fe965f4e949d0d965561801de89e90b673b65a Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Fri, 30 Oct 2015 20:34:19 +0530 Subject: ASoC: Intel: Skylake: Fix to cleanup if skl_sst_dsp_init fails This patch fixes the below warning reported by Dan by invoking skl_sst_dsp_cleanup() in cleanup path on error and not bailing out sound/soc/intel/skylake/skl-sst.c:270 skl_sst_dsp_init() info: ignoring unreachable code. Reported-by: Dan Carpenter Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-sst.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c index 3b83dc99f1d4..5c5a244942ef 100644 --- a/sound/soc/intel/skylake/skl-sst.c +++ b/sound/soc/intel/skylake/skl-sst.c @@ -259,15 +259,16 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, ret = sst->fw_ops.load_fw(sst); if (ret < 0) { dev_err(dev, "Load base fw failed : %d", ret); - return ret; + goto cleanup; } if (dsp) *dsp = skl; - return 0; + return ret; - skl_ipc_free(&skl->ipc); +cleanup: + skl_sst_dsp_cleanup(dev, skl); return ret; } EXPORT_SYMBOL_GPL(skl_sst_dsp_init); -- cgit v1.2.3 From c7b2a44410a1029f1cee4ad0b86588c9a0f83a6c Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Fri, 30 Oct 2015 20:34:20 +0530 Subject: ASoC: Intel: Skylake: Fix substream dereference before check Smatch warns that we dereferenced substream before check, so fix this by initializing ebus after the check sound/soc/intel/skylake/skl-pcm.c:802 skl_get_position() warn: variable dereferenced before check 'substream->runtime' Reported by: Dan Carpenter Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-pcm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 2517ec576ffc..e652d58bd9a9 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -807,7 +807,7 @@ static unsigned int skl_get_position(struct hdac_ext_stream *hstream, { struct hdac_stream *hstr = hdac_stream(hstream); struct snd_pcm_substream *substream = hstr->substream; - struct hdac_ext_bus *ebus = get_bus_ctx(substream); + struct hdac_ext_bus *ebus; unsigned int pos; int delay; @@ -818,6 +818,7 @@ static unsigned int skl_get_position(struct hdac_ext_stream *hstream, pos = 0; if (substream->runtime) { + ebus = get_bus_ctx(substream); delay = skl_get_delay_from_lpib(ebus, hstream, pos) + codec_delay; substream->runtime->delay += delay; -- cgit v1.2.3 From 7ae3cb15590ea768323b5e5a6be1769f19e91044 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 5 Nov 2015 21:34:10 +0530 Subject: ASoC: Intel: Skylake: Fix resource cleanup on teardown MCPS free was being done from PGA context which will free up MCPS for only last modules in a pipe and not the rest causing MCPS leak and eventual audio loss due to no "free" MCPS. This needs to be freed for every module while cleaning up the modules, so move the check to skl_tplg_mixer_dapm_post_pmd_event() Signed-off-by: Mohan Krishna Velaga Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 3c5f06235889..2b6ee22b5ea2 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -640,6 +640,7 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w, list_for_each_entry(w_module, &s_pipe->w_list, node) { dst_module = w_module->w->priv; + skl_tplg_free_pipe_mcps(skl, dst_module); if (src_module == NULL) { src_module = dst_module; continue; @@ -673,7 +674,6 @@ static int skl_tplg_pga_dapm_post_pmd_event(struct snd_soc_dapm_widget *w, src_mconfig = w->priv; - skl_tplg_free_pipe_mcps(skl, src_mconfig); /* Stop the pipe since this is a mixin module */ ret = skl_stop_pipe(ctx, src_mconfig->pipe); if (ret) -- cgit v1.2.3 From 95f098014815b330838b1173d3d7bcea3b481242 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 5 Nov 2015 21:34:11 +0530 Subject: ASoC: Intel: Move apci find machine routines This code to find the machine is common for all drivers so move it to a separate file and header for use in other drivers Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/common/Makefile | 3 +-- sound/soc/intel/common/sst-acpi.c | 33 +------------------------ sound/soc/intel/common/sst-acpi.h | 28 +++++++++++++++++++++ sound/soc/intel/common/sst-match-acpi.c | 43 +++++++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 34 deletions(-) create mode 100644 sound/soc/intel/common/sst-acpi.h create mode 100644 sound/soc/intel/common/sst-match-acpi.c (limited to 'sound') diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index d9105584c51f..658edce16761 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -1,5 +1,5 @@ snd-soc-sst-dsp-objs := sst-dsp.o -snd-soc-sst-acpi-objs := sst-acpi.o +snd-soc-sst-acpi-objs := sst-acpi.o sst-match-acpi.o snd-soc-sst-ipc-objs := sst-ipc.o ifneq ($(CONFIG_DW_DMAC_CORE),) @@ -8,4 +8,3 @@ endif obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o - diff --git a/sound/soc/intel/common/sst-acpi.c b/sound/soc/intel/common/sst-acpi.c index 67b6d3d52f57..94a43e6fcf88 100644 --- a/sound/soc/intel/common/sst-acpi.c +++ b/sound/soc/intel/common/sst-acpi.c @@ -21,21 +21,12 @@ #include #include "sst-dsp.h" +#include "sst-acpi.h" #define SST_LPT_DSP_DMA_ADDR_OFFSET 0x0F0000 #define SST_WPT_DSP_DMA_ADDR_OFFSET 0x0FE000 #define SST_LPT_DSP_DMA_SIZE (1024 - 1) -/* Descriptor for SST ASoC machine driver */ -struct sst_acpi_mach { - /* ACPI ID for the matching machine driver. Audio codec for instance */ - const u8 id[ACPI_ID_LEN]; - /* machine driver name */ - const char *drv_name; - /* firmware file name */ - const char *fw_filename; -}; - /* Descriptor for setting up SST platform data */ struct sst_acpi_desc { const char *drv_name; @@ -88,28 +79,6 @@ static void sst_acpi_fw_cb(const struct firmware *fw, void *context) return; } -static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level, - void *context, void **ret) -{ - *(bool *)context = true; - return AE_OK; -} - -static struct sst_acpi_mach *sst_acpi_find_machine( - struct sst_acpi_mach *machines) -{ - struct sst_acpi_mach *mach; - bool found = false; - - for (mach = machines; mach->id[0]; mach++) - if (ACPI_SUCCESS(acpi_get_devices(mach->id, - sst_acpi_mach_match, - &found, NULL)) && found) - return mach; - - return NULL; -} - static int sst_acpi_probe(struct platform_device *pdev) { const struct acpi_device_id *id; diff --git a/sound/soc/intel/common/sst-acpi.h b/sound/soc/intel/common/sst-acpi.h new file mode 100644 index 000000000000..1dc059590ead --- /dev/null +++ b/sound/soc/intel/common/sst-acpi.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2013-15, Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include + +/* acpi match */ +struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines); + +/* Descriptor for SST ASoC machine driver */ +struct sst_acpi_mach { + /* ACPI ID for the matching machine driver. Audio codec for instance */ + const u8 id[ACPI_ID_LEN]; + /* machine driver name */ + const char *drv_name; + /* firmware file name */ + const char *fw_filename; +}; diff --git a/sound/soc/intel/common/sst-match-acpi.c b/sound/soc/intel/common/sst-match-acpi.c new file mode 100644 index 000000000000..dd077e116d25 --- /dev/null +++ b/sound/soc/intel/common/sst-match-acpi.c @@ -0,0 +1,43 @@ +/* + * sst_match_apci.c - SST (LPE) match for ACPI enumeration. + * + * Copyright (c) 2013-15, Intel Corporation. + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ +#include +#include +#include +#include + +#include "sst-acpi.h" + +static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level, + void *context, void **ret) +{ + *(bool *)context = true; + return AE_OK; +} + +struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines) +{ + struct sst_acpi_mach *mach; + bool found = false; + + for (mach = machines; mach->id[0]; mach++) + if (ACPI_SUCCESS(acpi_get_devices(mach->id, + sst_acpi_mach_match, + &found, NULL)) && found) + return mach; + + return NULL; +} +EXPORT_SYMBOL_GPL(sst_acpi_find_machine); -- cgit v1.2.3 From 12cc291b0b58503b3b0e629ac605218df1851ce1 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 5 Nov 2015 21:34:12 +0530 Subject: ASoC: Intel: Atom: move atom driver to common acpi match This patch moves the atom driver to use the common acpi match functions. Since atom driver has few more information in machine table, these are appended to table and set to NULL for common driver Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/Kconfig | 1 + sound/soc/intel/atom/sst/sst_acpi.c | 67 +++++++++++-------------------------- sound/soc/intel/common/sst-acpi.c | 8 ++--- sound/soc/intel/common/sst-acpi.h | 5 +++ 4 files changed, 29 insertions(+), 52 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 7b778ab85f8b..13a762172b5d 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -24,6 +24,7 @@ config SND_SST_IPC_PCI config SND_SST_IPC_ACPI tristate select SND_SST_IPC + select SND_SOC_INTEL_SST depends on ACPI config SND_SOC_INTEL_SST diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c index bb19b5801466..f3d109eb3800 100644 --- a/sound/soc/intel/atom/sst/sst_acpi.c +++ b/sound/soc/intel/atom/sst/sst_acpi.c @@ -40,18 +40,9 @@ #include #include "../sst-mfld-platform.h" #include "../../common/sst-dsp.h" +#include "../../common/sst-acpi.h" #include "sst.h" -struct sst_machines { - char *codec_id; - char board[32]; - char machine[32]; - void (*machine_quirk)(void); - char firmware[FW_NAME_SIZE]; - struct sst_platform_info *pdata; - -}; - /* LPE viewpoint addresses */ #define SST_BYT_IRAM_PHY_START 0xff2c0000 #define SST_BYT_IRAM_PHY_END 0xff2d4000 @@ -223,37 +214,16 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx) return 0; } -static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level, - void *context, void **ret) -{ - *(bool *)context = true; - return AE_OK; -} - -static struct sst_machines *sst_acpi_find_machine( - struct sst_machines *machines) -{ - struct sst_machines *mach; - bool found = false; - - for (mach = machines; mach->codec_id; mach++) - if (ACPI_SUCCESS(acpi_get_devices(mach->codec_id, - sst_acpi_mach_match, - &found, NULL)) && found) - return mach; - - return NULL; -} - static int sst_acpi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; int ret = 0; struct intel_sst_drv *ctx; const struct acpi_device_id *id; - struct sst_machines *mach; + struct sst_acpi_mach *mach; struct platform_device *mdev; struct platform_device *plat_dev; + struct sst_platform_info *pdata; unsigned int dev_id; id = acpi_match_device(dev->driver->acpi_match_table, dev); @@ -261,12 +231,13 @@ static int sst_acpi_probe(struct platform_device *pdev) return -ENODEV; dev_dbg(dev, "for %s", id->id); - mach = (struct sst_machines *)id->driver_data; + mach = (struct sst_acpi_mach *)id->driver_data; mach = sst_acpi_find_machine(mach); if (mach == NULL) { dev_err(dev, "No matching machine driver found\n"); return -ENODEV; } + pdata = mach->pdata; ret = kstrtouint(id->id, 16, &dev_id); if (ret < 0) { @@ -276,16 +247,16 @@ static int sst_acpi_probe(struct platform_device *pdev) dev_dbg(dev, "ACPI device id: %x\n", dev_id); - plat_dev = platform_device_register_data(dev, mach->pdata->platform, -1, NULL, 0); + plat_dev = platform_device_register_data(dev, pdata->platform, -1, NULL, 0); if (IS_ERR(plat_dev)) { - dev_err(dev, "Failed to create machine device: %s\n", mach->pdata->platform); + dev_err(dev, "Failed to create machine device: %s\n", pdata->platform); return PTR_ERR(plat_dev); } /* Create platform device for sst machine driver */ - mdev = platform_device_register_data(dev, mach->machine, -1, NULL, 0); + mdev = platform_device_register_data(dev, mach->drv_name, -1, NULL, 0); if (IS_ERR(mdev)) { - dev_err(dev, "Failed to create machine device: %s\n", mach->machine); + dev_err(dev, "Failed to create machine device: %s\n", mach->drv_name); return PTR_ERR(mdev); } @@ -294,8 +265,8 @@ static int sst_acpi_probe(struct platform_device *pdev) return ret; /* Fill sst platform data */ - ctx->pdata = mach->pdata; - strcpy(ctx->firmware_name, mach->firmware); + ctx->pdata = pdata; + strcpy(ctx->firmware_name, mach->fw_filename); ret = sst_platform_get_resources(ctx); if (ret) @@ -342,22 +313,22 @@ static int sst_acpi_remove(struct platform_device *pdev) return 0; } -static struct sst_machines sst_acpi_bytcr[] = { - {"10EC5640", "T100", "bytt100_rt5640", NULL, "intel/fw_sst_0f28.bin", +static struct sst_acpi_mach sst_acpi_bytcr[] = { + {"10EC5640", "bytt100_rt5640", "intel/fw_sst_0f28.bin", "T100", NULL, &byt_rvp_platform_data }, {}, }; /* Cherryview-based platforms: CherryTrail and Braswell */ -static struct sst_machines sst_acpi_chv[] = { - {"10EC5670", "cht-bsw", "cht-bsw-rt5672", NULL, "intel/fw_sst_22a8.bin", +static struct sst_acpi_mach sst_acpi_chv[] = { + {"10EC5670", "cht-bsw-rt5672", "intel/fw_sst_22a8.bin", "cht-bsw", NULL, + &chv_platform_data }, + {"10EC5645", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL, &chv_platform_data }, - {"10EC5645", "cht-bsw", "cht-bsw-rt5645", NULL, "intel/fw_sst_22a8.bin", + {"10EC5650", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL, &chv_platform_data }, - {"10EC5650", "cht-bsw", "cht-bsw-rt5645", NULL, "intel/fw_sst_22a8.bin", + {"193C9890", "cht-bsw-max98090", "intel/fw_sst_22a8.bin", "cht-bsw", NULL, &chv_platform_data }, - {"193C9890", "cht-bsw", "cht-bsw-max98090", NULL, - "intel/fw_sst_22a8.bin", &chv_platform_data }, {}, }; diff --git a/sound/soc/intel/common/sst-acpi.c b/sound/soc/intel/common/sst-acpi.c index 94a43e6fcf88..7a85c576dad3 100644 --- a/sound/soc/intel/common/sst-acpi.c +++ b/sound/soc/intel/common/sst-acpi.c @@ -180,7 +180,7 @@ static int sst_acpi_remove(struct platform_device *pdev) } static struct sst_acpi_mach haswell_machines[] = { - { "INT33CA", "haswell-audio", "intel/IntcSST1.bin" }, + { "INT33CA", "haswell-audio", "intel/IntcSST1.bin", NULL, NULL, NULL }, {} }; @@ -198,7 +198,7 @@ static struct sst_acpi_desc sst_acpi_haswell_desc = { }; static struct sst_acpi_mach broadwell_machines[] = { - { "INT343A", "broadwell-audio", "intel/IntcSST2.bin" }, + { "INT343A", "broadwell-audio", "intel/IntcSST2.bin", NULL, NULL, NULL }, {} }; @@ -216,8 +216,8 @@ static struct sst_acpi_desc sst_acpi_broadwell_desc = { }; static struct sst_acpi_mach baytrail_machines[] = { - { "10EC5640", "byt-rt5640", "intel/fw_sst_0f28.bin-48kHz_i2s_master" }, - { "193C9890", "byt-max98090", "intel/fw_sst_0f28.bin-48kHz_i2s_master" }, + { "10EC5640", "byt-rt5640", "intel/fw_sst_0f28.bin-48kHz_i2s_master", NULL, NULL, NULL }, + { "193C9890", "byt-max98090", "intel/fw_sst_0f28.bin-48kHz_i2s_master", NULL, NULL, NULL }, {} }; diff --git a/sound/soc/intel/common/sst-acpi.h b/sound/soc/intel/common/sst-acpi.h index 1dc059590ead..3ee3b7ab5d03 100644 --- a/sound/soc/intel/common/sst-acpi.h +++ b/sound/soc/intel/common/sst-acpi.h @@ -25,4 +25,9 @@ struct sst_acpi_mach { const char *drv_name; /* firmware file name */ const char *fw_filename; + + /* board name */ + const char *board; + void (*machine_quirk)(void); + void *pdata; }; -- cgit v1.2.3 From cc18c5fdcdcf06f75ff196dedfcde823a6556d7d Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 5 Nov 2015 21:34:13 +0530 Subject: ASoC: Intel: Skylake: Fix skl machine driver creation Now that we have common match code in place, update the SKL driver to use the common match routines for driver entry creation for UEFI BIOS systems Signed-off-by: Jeeja KP Signed-off-by: Omair M Abdullah Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl.c | 55 +++++++++++++++++++++++++++++++++++++++++-- sound/soc/intel/skylake/skl.h | 1 + 2 files changed, 54 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 9b94a8cdf9bd..59336cbc10dd 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -26,6 +26,7 @@ #include #include #include +#include "../common/sst-acpi.h" #include "skl.h" /* @@ -251,6 +252,42 @@ static int skl_free(struct hdac_ext_bus *ebus) return 0; } +static int skl_machine_device_register(struct skl *skl, void *driver_data) +{ + struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); + struct platform_device *pdev; + struct sst_acpi_mach *mach = driver_data; + int ret; + + mach = sst_acpi_find_machine(mach); + if (mach == NULL) { + dev_err(bus->dev, "No matching machine driver found\n"); + return -ENODEV; + } + + pdev = platform_device_alloc(mach->drv_name, -1); + if (pdev == NULL) { + dev_err(bus->dev, "platform device alloc failed\n"); + return -EIO; + } + + ret = platform_device_add(pdev); + if (ret) { + dev_err(bus->dev, "failed to add machine device\n"); + platform_device_put(pdev); + return -EIO; + } + skl->i2s_dev = pdev; + + return 0; +} + +static void skl_machine_device_unregister(struct skl *skl) +{ + if (skl->i2s_dev) + platform_device_unregister(skl->i2s_dev); +} + static int skl_dmic_device_register(struct skl *skl) { struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); @@ -479,10 +516,15 @@ static int skl_probe(struct pci_dev *pci, /* check if dsp is there */ if (ebus->ppcap) { + err = skl_machine_device_register(skl, + (void *)pci_id->driver_data); + if (err < 0) + goto out_free; + err = skl_init_dsp(skl); if (err < 0) { dev_dbg(bus->dev, "error failed to register dsp\n"); - goto out_free; + goto out_mach_free; } } if (ebus->mlcap) @@ -517,6 +559,8 @@ out_dmic_free: skl_dmic_device_unregister(skl); out_dsp_free: skl_free_dsp(skl); +out_mach_free: + skl_machine_device_unregister(skl); out_free: skl->init_failed = 1; skl_free(ebus); @@ -534,15 +578,22 @@ static void skl_remove(struct pci_dev *pci) pci_dev_put(pci); skl_platform_unregister(&pci->dev); skl_free_dsp(skl); + skl_machine_device_unregister(skl); skl_dmic_device_unregister(skl); skl_free(ebus); dev_set_drvdata(&pci->dev, NULL); } +static struct sst_acpi_mach sst_skl_devdata[] = { + { "INT343A", "skl_alc286s_i2s", "intel/dsp_fw_release.bin", NULL, NULL, NULL }, + {} +}; + /* PCI IDs */ static const struct pci_device_id skl_ids[] = { /* Sunrise Point-LP */ - { PCI_DEVICE(0x8086, 0x9d70), 0}, + { PCI_DEVICE(0x8086, 0x9d70), + .driver_data = (unsigned long)&sst_skl_devdata}, { 0, } }; MODULE_DEVICE_TABLE(pci, skl_ids); diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index f803ebb10605..9b1beed26f0f 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -61,6 +61,7 @@ struct skl { unsigned int init_failed:1; /* delayed init failed */ struct platform_device *dmic_dev; + struct platform_device *i2s_dev; void *nhlt; /* nhlt ptr */ struct skl_sst *skl_sst; /* sst skl ctx */ -- cgit v1.2.3 From 40c3ac46a49da3b01b1802eb4c4ff08626f48546 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 5 Nov 2015 21:34:14 +0530 Subject: ASoC: Intel: add fw name to common dsp context In order to pass the fw name to IPC driver for loading fw, we need to add a memeber to store the fw name Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/common/sst-dsp-priv.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h index 2151652d37b7..4452cda28874 100644 --- a/sound/soc/intel/common/sst-dsp-priv.h +++ b/sound/soc/intel/common/sst-dsp-priv.h @@ -308,6 +308,8 @@ struct sst_dsp { /* SKL data */ + const char *fw_name; + /* To allocate CL dma buffers */ struct skl_dsp_loader_ops dsp_ops; struct skl_dsp_fw_ops fw_ops; -- cgit v1.2.3 From aecf6fd878eba5182665cccb943205be4c9a0337 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 5 Nov 2015 21:34:15 +0530 Subject: ASoC: Intel: Skylake: Use the fw name from ACPI mach table The firmware name is hard coded which doesnt allow to load different platforms for various platforms so get this name from available machine table and pass it to dsp context for loading Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-messages.c | 2 +- sound/soc/intel/skylake/skl-sst-dsp.h | 3 ++- sound/soc/intel/skylake/skl-sst.c | 5 +++-- sound/soc/intel/skylake/skl.c | 1 + sound/soc/intel/skylake/skl.h | 2 ++ 5 files changed, 9 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index bfde60bb8119..d71b58322cc7 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -96,7 +96,7 @@ int skl_init_dsp(struct skl *skl) } ret = skl_sst_dsp_init(bus->dev, mmio_base, irq, - loader_ops, &skl->skl_sst); + skl->fw_name, loader_ops, &skl->skl_sst); if (ret < 0) return ret; diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index 6bfcef449bdc..f2a69d9e56b3 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -139,7 +139,8 @@ void skl_dsp_free(struct sst_dsp *dsp); int skl_dsp_boot(struct sst_dsp *ctx); int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, - struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp); + const char *fw_name, struct skl_dsp_loader_ops dsp_ops, + struct skl_sst **dsp); void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx); #endif /*__SKL_SST_DSP_H__*/ diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c index 5c5a244942ef..0c5039f2bd09 100644 --- a/sound/soc/intel/skylake/skl-sst.c +++ b/sound/soc/intel/skylake/skl-sst.c @@ -77,7 +77,7 @@ static int skl_load_base_firmware(struct sst_dsp *ctx) init_waitqueue_head(&skl->boot_wait); if (ctx->fw == NULL) { - ret = request_firmware(&ctx->fw, "dsp_fw_release.bin", ctx->dev); + ret = request_firmware(&ctx->fw, ctx->fw_name, ctx->dev); if (ret < 0) { dev_err(ctx->dev, "Request firmware failed %d\n", ret); skl_dsp_disable_core(ctx); @@ -223,7 +223,7 @@ static struct sst_dsp_device skl_dev = { }; int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, - struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp) + const char *fw_name, struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp) { struct skl_sst *skl; struct sst_dsp *sst; @@ -244,6 +244,7 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, sst = skl->dsp; + sst->fw_name = fw_name; sst->addr.lpe = mmio_base; sst->addr.shim = mmio_base; sst_dsp_mailbox_init(sst, (SKL_ADSP_SRAM0_BASE + SKL_ADSP_W0_STAT_SZ), diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 59336cbc10dd..390f839d6168 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -264,6 +264,7 @@ static int skl_machine_device_register(struct skl *skl, void *driver_data) dev_err(bus->dev, "No matching machine driver found\n"); return -ENODEV; } + skl->fw_name = mach->fw_filename; pdev = platform_device_alloc(mach->drv_name, -1); if (pdev == NULL) { diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 9b1beed26f0f..774c29cf84dc 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -68,6 +68,8 @@ struct skl { struct skl_dsp_resource resource; struct list_head ppl_list; + + const char *fw_name; }; #define skl_to_ebus(s) (&(s)->ebus) -- cgit v1.2.3 From 2458c37779ddb91b4109949d86f5a5e193ba415b Mon Sep 17 00:00:00 2001 From: Caesar Wang Date: Fri, 6 Nov 2015 19:38:14 +0800 Subject: ASoC: rockchip: i2s: change bclk and lrck according to sample rates This patch sets the dividers autonomously. when i2s works on master mode, and sample rates changed. We need to change bclk and lrck at the same time for cpu internal side. As the input source clock to the module is MCLK_I2S, and by the divider of the module, the clock generator generates SCLK and LRCK to transmitter and receiver. Signed-off-by: Caesar Wang Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_i2s.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'sound') diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index 58ee64594f07..ce880f3bccc7 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -41,6 +41,7 @@ struct rk_i2s_dev { */ bool tx_start; bool rx_start; + bool is_master_mode; }; static int i2s_runtime_suspend(struct device *dev) @@ -174,9 +175,11 @@ static int rockchip_i2s_set_fmt(struct snd_soc_dai *cpu_dai, case SND_SOC_DAIFMT_CBS_CFS: /* Set source clock in Master mode */ val = I2S_CKR_MSS_MASTER; + i2s->is_master_mode = true; break; case SND_SOC_DAIFMT_CBM_CFM: val = I2S_CKR_MSS_SLAVE; + i2s->is_master_mode = false; break; default: return -EINVAL; @@ -228,6 +231,26 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream, struct rk_i2s_dev *i2s = to_info(dai); struct snd_soc_pcm_runtime *rtd = substream->private_data; unsigned int val = 0; + unsigned int mclk_rate, bclk_rate, div_bclk, div_lrck; + + if (i2s->is_master_mode) { + mclk_rate = clk_get_rate(i2s->mclk); + bclk_rate = 2 * 32 * params_rate(params); + if (bclk_rate && mclk_rate % bclk_rate) + return -EINVAL; + + div_bclk = mclk_rate / bclk_rate; + div_lrck = bclk_rate / params_rate(params); + regmap_update_bits(i2s->regmap, I2S_CKR, + I2S_CKR_MDIV_MASK, + I2S_CKR_MDIV(div_bclk)); + + regmap_update_bits(i2s->regmap, I2S_CKR, + I2S_CKR_TSD_MASK | + I2S_CKR_RSD_MASK, + I2S_CKR_TSD(div_lrck) | + I2S_CKR_RSD(div_lrck)); + } switch (params_format(params)) { case SNDRV_PCM_FORMAT_S8: -- cgit v1.2.3 From a4ebd38042cf479a6917f09fbb1cf1f093dcf60d Mon Sep 17 00:00:00 2001 From: Caesar Wang Date: Fri, 6 Nov 2015 19:38:15 +0800 Subject: ASoC: rockchip-max98090: Allow more sample rates The MAX98090 audio codec support sample rates from 8 to 96 kHz as the dai claim. Signed-off-by: Caesar Wang Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_max98090.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'sound') diff --git a/sound/soc/rockchip/rockchip_max98090.c b/sound/soc/rockchip/rockchip_max98090.c index 26567b10393a..543610282cdb 100644 --- a/sound/soc/rockchip/rockchip_max98090.c +++ b/sound/soc/rockchip/rockchip_max98090.c @@ -80,11 +80,17 @@ static int rk_aif1_hw_params(struct snd_pcm_substream *substream, switch (params_rate(params)) { case 8000: case 16000: + case 24000: + case 32000: case 48000: + case 64000: case 96000: mclk = 12288000; break; + case 11025: + case 22050: case 44100: + case 88200: mclk = 11289600; break; default: -- cgit v1.2.3 From 3a3b070da98e43037e35b9ad02eb0fff1a57e318 Mon Sep 17 00:00:00 2001 From: Caesar Wang Date: Fri, 6 Nov 2015 19:38:16 +0800 Subject: ASoC: rockchip-rt5645: Allow more sample rates The RT5645 audio codec support sample rates from 8 to 96 kHz as the dai claim. Signed-off-by: Caesar Wang Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_rt5645.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'sound') diff --git a/sound/soc/rockchip/rockchip_rt5645.c b/sound/soc/rockchip/rockchip_rt5645.c index 68c62e4c2316..440a8026346a 100644 --- a/sound/soc/rockchip/rockchip_rt5645.c +++ b/sound/soc/rockchip/rockchip_rt5645.c @@ -79,11 +79,17 @@ static int rk_aif1_hw_params(struct snd_pcm_substream *substream, switch (params_rate(params)) { case 8000: case 16000: + case 24000: + case 32000: case 48000: + case 64000: case 96000: mclk = 12288000; break; + case 11025: + case 22050: case 44100: + case 88200: mclk = 11289600; break; default: -- cgit v1.2.3 From 4bbda49cc40f6c2e5cc3a5dd22cded1d217e074d Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 21 Oct 2015 16:18:19 +0800 Subject: ASoC: rt298: fix remove unnedded clk setting The bit is no longer present. So remove it. Signed-off-by: Bard Liao Signed-off-by: Mark Brown --- sound/soc/codecs/rt298.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index b3f795c60749..30c6de62ae6c 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -854,8 +854,6 @@ static int rt298_set_dai_sysclk(struct snd_soc_dai *dai, } else { snd_soc_update_bits(codec, RT298_I2S_CTRL2, 0x0100, 0x0100); - snd_soc_update_bits(codec, - RT298_PLL_CTRL, 0x4, 0x4); snd_soc_update_bits(codec, RT298_PLL_CTRL1, 0x20, 0x0); } -- cgit v1.2.3 From cdab0d4ecc1a890aece7102c2074bf73175b9935 Mon Sep 17 00:00:00 2001 From: Anatol Pomozov Date: Thu, 29 Oct 2015 15:31:59 -0700 Subject: ASoC: rt5677: use 'active low' logic for reset pin According to the datasheet RESET is active low pin, i.e. system goes to reset state when pin signal is low. The previous implementeation was assuming the pin is configured as 'active high' in DTS. Changle the gpio handling code and DTS configuration to 'active low'. Signed-off-by: Anatol Pomozov Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/rt5677.txt | 2 +- sound/soc/codecs/rt5677.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/Documentation/devicetree/bindings/sound/rt5677.txt b/Documentation/devicetree/bindings/sound/rt5677.txt index f07078997f87..1b3c13d206ff 100644 --- a/Documentation/devicetree/bindings/sound/rt5677.txt +++ b/Documentation/devicetree/bindings/sound/rt5677.txt @@ -18,7 +18,7 @@ Required properties: Optional properties: - realtek,pow-ldo2-gpio : The GPIO that controls the CODEC's POW_LDO2 pin. -- realtek,reset-gpio : The GPIO that controls the CODEC's RESET pin. +- realtek,reset-gpio : The GPIO that controls the CODEC's RESET pin. Active low. - realtek,in1-differential - realtek,in2-differential diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index b4cd7e3bf5f8..f73fd125e49c 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -4766,7 +4766,7 @@ static int rt5677_remove(struct snd_soc_codec *codec) regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec); gpiod_set_value_cansleep(rt5677->pow_ldo2, 0); - gpiod_set_value_cansleep(rt5677->reset_pin, 0); + gpiod_set_value_cansleep(rt5677->reset_pin, 1); return 0; } @@ -4781,7 +4781,7 @@ static int rt5677_suspend(struct snd_soc_codec *codec) regcache_mark_dirty(rt5677->regmap); gpiod_set_value_cansleep(rt5677->pow_ldo2, 0); - gpiod_set_value_cansleep(rt5677->reset_pin, 0); + gpiod_set_value_cansleep(rt5677->reset_pin, 1); } return 0; @@ -4793,7 +4793,7 @@ static int rt5677_resume(struct snd_soc_codec *codec) if (!rt5677->dsp_vad_en) { gpiod_set_value_cansleep(rt5677->pow_ldo2, 1); - gpiod_set_value_cansleep(rt5677->reset_pin, 1); + gpiod_set_value_cansleep(rt5677->reset_pin, 0); if (rt5677->pow_ldo2 || rt5677->reset_pin) msleep(10); @@ -5138,7 +5138,7 @@ static int rt5677_i2c_probe(struct i2c_client *i2c, return ret; } rt5677->reset_pin = devm_gpiod_get_optional(&i2c->dev, - "realtek,reset", GPIOD_OUT_HIGH); + "realtek,reset", GPIOD_OUT_LOW); if (IS_ERR(rt5677->reset_pin)) { ret = PTR_ERR(rt5677->reset_pin); dev_err(&i2c->dev, "Failed to request RESET: %d\n", ret); -- cgit v1.2.3 From 700dadfefc3df1f63dfbae7cb42fda147f4c074c Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sun, 1 Nov 2015 20:23:39 +0100 Subject: ASoC: wm9713: convert to regmap Convert the Wolfson WM9713 to regmap API. This will leverage all the regmap functions (debug, registers update, etc ...). As a bonus, this will pave the path to gpio chip introduction, and devicetree support. This was tested on the mioa701 board, pxa27x based, in PCM playback, and through suspend/resume. Signed-off-by: Robert Jarzmik Reviewed-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 1 + sound/soc/codecs/wm9713.c | 174 ++++++++++++++++++++++++++++------------------ 2 files changed, 108 insertions(+), 67 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index cfdafc4c11ea..a62b91989ac7 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -891,6 +891,7 @@ config SND_SOC_WM9712 config SND_SOC_WM9713 tristate + select REGMAP_AC97 # Amp config SND_SOC_LM4857 diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index 4083a5130cbd..22985c42764c 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -39,33 +40,15 @@ struct wm9713_priv { struct mutex lock; }; -static unsigned int ac97_read(struct snd_soc_codec *codec, - unsigned int reg); -static int ac97_write(struct snd_soc_codec *codec, - unsigned int reg, unsigned int val); - -/* - * WM9713 register cache - * Reg 0x3c bit 15 is used by touch driver. - */ -static const u16 wm9713_reg[] = { - 0x6174, 0x8080, 0x8080, 0x8080, - 0xc880, 0xe808, 0xe808, 0x0808, - 0x00da, 0x8000, 0xd600, 0xaaa0, - 0xaaa0, 0xaaa0, 0x0000, 0x0000, - 0x0f0f, 0x0040, 0x0000, 0x7f00, - 0x0405, 0x0410, 0xbb80, 0xbb80, - 0x0000, 0xbb80, 0x0000, 0x4523, - 0x0000, 0x2000, 0x7eff, 0xffff, - 0x0000, 0x0000, 0x0080, 0x0000, - 0x0000, 0x0000, 0xfffe, 0xffff, - 0x0000, 0x0000, 0x0000, 0xfffe, - 0x4000, 0x0000, 0x0000, 0x0000, - 0xb032, 0x3e00, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0006, - 0x0001, 0x0000, 0x574d, 0x4c13, -}; +static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg) +{ + return snd_soc_read(codec, reg); +} +static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int val) +{ + return snd_soc_write(codec, reg, val); +} #define HPL_MIXER 0 #define HPR_MIXER 1 @@ -674,39 +657,97 @@ static const struct snd_soc_dapm_route wm9713_audio_map[] = { {"Capture Mono Mux", "Right", "Right Capture Source"}, }; -static unsigned int ac97_read(struct snd_soc_codec *codec, - unsigned int reg) +static bool wm9713_readable_reg(struct device *dev, unsigned int reg) { - struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); - u16 *cache = codec->reg_cache; - - if (reg == AC97_RESET || reg == AC97_GPIO_STATUS || - reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 || - reg == AC97_CD) - return soc_ac97_ops->read(wm9713->ac97, reg); - else { - reg = reg >> 1; - - if (reg >= (ARRAY_SIZE(wm9713_reg))) - return -EIO; - - return cache[reg]; + switch (reg) { + case AC97_RESET ... AC97_PCM_SURR_DAC_RATE: + case AC97_PCM_LR_ADC_RATE: + case AC97_CENTER_LFE_MASTER: + case AC97_SPDIF ... AC97_LINE1_LEVEL: + case AC97_GPIO_CFG ... 0x5c: + case AC97_CODEC_CLASS_REV ... AC97_PCI_SID: + case 0x74 ... AC97_VENDOR_ID2: + return true; + default: + return false; } } -static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int val) +static bool wm9713_writeable_reg(struct device *dev, unsigned int reg) { - struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); + switch (reg) { + case AC97_VENDOR_ID1: + case AC97_VENDOR_ID2: + return false; + default: + return wm9713_readable_reg(dev, reg); + } +} - u16 *cache = codec->reg_cache; - soc_ac97_ops->write(wm9713->ac97, reg, val); - reg = reg >> 1; - if (reg < (ARRAY_SIZE(wm9713_reg))) - cache[reg] = val; +static const struct reg_default wm9713_reg_defaults[] = { + { 0x02, 0x8080 }, /* Speaker Output Volume */ + { 0x04, 0x8080 }, /* Headphone Output Volume */ + { 0x06, 0x8080 }, /* Out3/OUT4 Volume */ + { 0x08, 0xc880 }, /* Mono Volume */ + { 0x0a, 0xe808 }, /* LINEIN Volume */ + { 0x0c, 0xe808 }, /* DAC PGA Volume */ + { 0x0e, 0x0808 }, /* MIC PGA Volume */ + { 0x10, 0x00da }, /* MIC Routing Control */ + { 0x12, 0x8000 }, /* Record PGA Volume */ + { 0x14, 0xd600 }, /* Record Routing */ + { 0x16, 0xaaa0 }, /* PCBEEP Volume */ + { 0x18, 0xaaa0 }, /* VxDAC Volume */ + { 0x1a, 0xaaa0 }, /* AUXDAC Volume */ + { 0x1c, 0x0000 }, /* Output PGA Mux */ + { 0x1e, 0x0000 }, /* DAC 3D control */ + { 0x20, 0x0f0f }, /* DAC Tone Control*/ + { 0x22, 0x0040 }, /* MIC Input Select & Bias */ + { 0x24, 0x0000 }, /* Output Volume Mapping & Jack */ + { 0x26, 0x7f00 }, /* Powerdown Ctrl/Stat*/ + { 0x28, 0x0405 }, /* Extended Audio ID */ + { 0x2a, 0x0410 }, /* Extended Audio Start/Ctrl */ + { 0x2c, 0xbb80 }, /* Audio DACs Sample Rate */ + { 0x2e, 0xbb80 }, /* AUXDAC Sample Rate */ + { 0x32, 0xbb80 }, /* Audio ADCs Sample Rate */ + { 0x36, 0x4523 }, /* PCM codec control */ + { 0x3a, 0x2000 }, /* SPDIF control */ + { 0x3c, 0xfdff }, /* Powerdown 1 */ + { 0x3e, 0xffff }, /* Powerdown 2 */ + { 0x40, 0x0000 }, /* General Purpose */ + { 0x42, 0x0000 }, /* Fast Power-Up Control */ + { 0x44, 0x0080 }, /* MCLK/PLL Control */ + { 0x46, 0x0000 }, /* MCLK/PLL Control */ + { 0x4c, 0xfffe }, /* GPIO Pin Configuration */ + { 0x4e, 0xffff }, /* GPIO Pin Polarity / Type */ + { 0x50, 0x0000 }, /* GPIO Pin Sticky */ + { 0x52, 0x0000 }, /* GPIO Pin Wake-Up */ + /* GPIO Pin Status */ + { 0x56, 0xfffe }, /* GPIO Pin Sharing */ + { 0x58, 0x4000 }, /* GPIO PullUp/PullDown */ + { 0x5a, 0x0000 }, /* Additional Functions 1 */ + { 0x5c, 0x0000 }, /* Additional Functions 2 */ + { 0x60, 0xb032 }, /* ALC Control */ + { 0x62, 0x3e00 }, /* ALC / Noise Gate Control */ + { 0x64, 0x0000 }, /* AUXDAC input control */ + { 0x74, 0x0000 }, /* Digitiser Reg 1 */ + { 0x76, 0x0006 }, /* Digitiser Reg 2 */ + { 0x78, 0x0001 }, /* Digitiser Reg 3 */ + { 0x7a, 0x0000 }, /* Digitiser Read Back */ +}; - return 0; -} +static const struct regmap_config wm9713_regmap_config = { + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .max_register = 0x7e, + .cache_type = REGCACHE_RBTREE, + + .reg_defaults = wm9713_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm9713_reg_defaults), + .volatile_reg = regmap_ac97_default_volatile, + .readable_reg = wm9713_readable_reg, + .writeable_reg = wm9713_writeable_reg, +}; /* PLL divisors */ struct _pll_div { @@ -1173,8 +1214,7 @@ static int wm9713_soc_suspend(struct snd_soc_codec *codec) static int wm9713_soc_resume(struct snd_soc_codec *codec) { struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); - int i, ret; - u16 *cache = codec->reg_cache; + int ret; ret = snd_ac97_reset(wm9713->ac97, true, WM9713_VENDOR_ID, WM9713_VENDOR_ID_MASK); @@ -1189,12 +1229,8 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec) /* only synchronise the codec if warm reset failed */ if (ret == 0) { - for (i = 2; i < ARRAY_SIZE(wm9713_reg) << 1; i += 2) { - if (i == AC97_POWERDOWN || i == AC97_EXTENDED_MID || - i == AC97_EXTENDED_MSTATUS || i > 0x66) - continue; - soc_ac97_ops->write(wm9713->ac97, i, cache[i>>1]); - } + regcache_mark_dirty(codec->component.regmap); + snd_soc_cache_sync(codec); } return ret; @@ -1203,6 +1239,7 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec) static int wm9713_soc_probe(struct snd_soc_codec *codec) { struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); + struct regmap *regmap; int reg; wm9713->ac97 = snd_soc_new_ac97_codec(codec, WM9713_VENDOR_ID, @@ -1210,6 +1247,14 @@ static int wm9713_soc_probe(struct snd_soc_codec *codec) if (IS_ERR(wm9713->ac97)) return PTR_ERR(wm9713->ac97); + regmap = devm_regmap_init_ac97(wm9713->ac97, &wm9713_regmap_config); + if (IS_ERR(regmap)) { + snd_soc_free_ac97_codec(wm9713->ac97); + return PTR_ERR(regmap); + } + + snd_soc_codec_init_regmap(codec, regmap); + /* unmute the adc - move to kcontrol */ reg = ac97_read(codec, AC97_CD) & 0x7fff; ac97_write(codec, AC97_CD, reg); @@ -1221,6 +1266,7 @@ static int wm9713_soc_remove(struct snd_soc_codec *codec) { struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); + snd_soc_codec_exit_regmap(codec); snd_soc_free_ac97_codec(wm9713->ac97); return 0; } @@ -1230,13 +1276,7 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9713 = { .remove = wm9713_soc_remove, .suspend = wm9713_soc_suspend, .resume = wm9713_soc_resume, - .read = ac97_read, - .write = ac97_write, .set_bias_level = wm9713_set_bias_level, - .reg_cache_size = ARRAY_SIZE(wm9713_reg), - .reg_word_size = sizeof(u16), - .reg_cache_step = 2, - .reg_cache_default = wm9713_reg, .controls = wm9713_snd_ac97_controls, .num_controls = ARRAY_SIZE(wm9713_snd_ac97_controls), -- cgit v1.2.3 From fa1a51f3cd9849fcfb811c7cd62d6e2028ad4ea9 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sun, 1 Nov 2015 20:23:40 +0100 Subject: ASoC: wm9713: use snd_soc_*() calls to update ac97 registers Convert wm9713 to use the more modern registers manipulation functions, such as snd_soc_read(), snd_soc_write() and snd_soc_update_bits(). Signed-off-by: Robert Jarzmik Reviewed-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm9713.c | 140 +++++++++++++++++----------------------------- 1 file changed, 52 insertions(+), 88 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index 22985c42764c..79e143625ac3 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -40,16 +40,6 @@ struct wm9713_priv { struct mutex lock; }; -static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg) -{ - return snd_soc_read(codec, reg); -} -static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int val) -{ - return snd_soc_write(codec, reg, val); -} - #define HPL_MIXER 0 #define HPR_MIXER 1 @@ -203,18 +193,15 @@ static int wm9713_voice_shutdown(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); - u16 status, rate; if (WARN_ON(event != SND_SOC_DAPM_PRE_PMD)) return -EINVAL; /* Gracefully shut down the voice interface. */ - status = ac97_read(codec, AC97_EXTENDED_MID) | 0x1000; - rate = ac97_read(codec, AC97_HANDSET_RATE) & 0xF0FF; - ac97_write(codec, AC97_HANDSET_RATE, rate | 0x0200); + snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0f00, 0x0200); schedule_timeout_interruptible(msecs_to_jiffies(1)); - ac97_write(codec, AC97_HANDSET_RATE, rate | 0x0F00); - ac97_write(codec, AC97_EXTENDED_MID, status); + snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0f00, 0x0f00); + snd_soc_update_bits(codec, AC97_EXTENDED_MID, 0x1000, 0x1000); return 0; } @@ -834,10 +821,8 @@ static int wm9713_set_pll(struct snd_soc_codec *codec, /* turn PLL off ? */ if (freq_in == 0) { /* disable PLL power and select ext source */ - reg = ac97_read(codec, AC97_HANDSET_RATE); - ac97_write(codec, AC97_HANDSET_RATE, reg | 0x0080); - reg = ac97_read(codec, AC97_EXTENDED_MID); - ac97_write(codec, AC97_EXTENDED_MID, reg | 0x0200); + snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0080, 0x0080); + snd_soc_update_bits(codec, AC97_EXTENDED_MID, 0x0200, 0x0200); wm9713->pll_in = 0; return 0; } @@ -847,7 +832,7 @@ static int wm9713_set_pll(struct snd_soc_codec *codec, if (pll_div.k == 0) { reg = (pll_div.n << 12) | (pll_div.lf << 11) | (pll_div.divsel << 9) | (pll_div.divctl << 8); - ac97_write(codec, AC97_LINE1_LEVEL, reg); + snd_soc_write(codec, AC97_LINE1_LEVEL, reg); } else { /* write the fractional k to the reg 0x46 pages */ reg2 = (pll_div.n << 12) | (pll_div.lf << 11) | (1 << 10) | @@ -855,33 +840,31 @@ static int wm9713_set_pll(struct snd_soc_codec *codec, /* K [21:20] */ reg = reg2 | (0x5 << 4) | (pll_div.k >> 20); - ac97_write(codec, AC97_LINE1_LEVEL, reg); + snd_soc_write(codec, AC97_LINE1_LEVEL, reg); /* K [19:16] */ reg = reg2 | (0x4 << 4) | ((pll_div.k >> 16) & 0xf); - ac97_write(codec, AC97_LINE1_LEVEL, reg); + snd_soc_write(codec, AC97_LINE1_LEVEL, reg); /* K [15:12] */ reg = reg2 | (0x3 << 4) | ((pll_div.k >> 12) & 0xf); - ac97_write(codec, AC97_LINE1_LEVEL, reg); + snd_soc_write(codec, AC97_LINE1_LEVEL, reg); /* K [11:8] */ reg = reg2 | (0x2 << 4) | ((pll_div.k >> 8) & 0xf); - ac97_write(codec, AC97_LINE1_LEVEL, reg); + snd_soc_write(codec, AC97_LINE1_LEVEL, reg); /* K [7:4] */ reg = reg2 | (0x1 << 4) | ((pll_div.k >> 4) & 0xf); - ac97_write(codec, AC97_LINE1_LEVEL, reg); + snd_soc_write(codec, AC97_LINE1_LEVEL, reg); reg = reg2 | (0x0 << 4) | (pll_div.k & 0xf); /* K [3:0] */ - ac97_write(codec, AC97_LINE1_LEVEL, reg); + snd_soc_write(codec, AC97_LINE1_LEVEL, reg); } /* turn PLL on and select as source */ - reg = ac97_read(codec, AC97_EXTENDED_MID); - ac97_write(codec, AC97_EXTENDED_MID, reg & 0xfdff); - reg = ac97_read(codec, AC97_HANDSET_RATE); - ac97_write(codec, AC97_HANDSET_RATE, reg & 0xff7f); + snd_soc_update_bits(codec, AC97_EXTENDED_MID, 0x0200, 0x0000); + snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0080, 0x0000); wm9713->pll_in = freq_in; /* wait 10ms AC97 link frames for the link to stabilise */ @@ -904,10 +887,10 @@ static int wm9713_set_dai_tristate(struct snd_soc_dai *codec_dai, int tristate) { struct snd_soc_codec *codec = codec_dai->codec; - u16 reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0x9fff; if (tristate) - ac97_write(codec, AC97_CENTER_LFE_MASTER, reg); + snd_soc_update_bits(codec, AC97_CENTER_LFE_MASTER, + 0x6000, 0x0000); return 0; } @@ -920,36 +903,30 @@ static int wm9713_set_dai_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div) { struct snd_soc_codec *codec = codec_dai->codec; - u16 reg; switch (div_id) { case WM9713_PCMCLK_DIV: - reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xf0ff; - ac97_write(codec, AC97_HANDSET_RATE, reg | div); + snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0f00, div); break; case WM9713_CLKA_MULT: - reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xfffd; - ac97_write(codec, AC97_HANDSET_RATE, reg | div); + snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0002, div); break; case WM9713_CLKB_MULT: - reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xfffb; - ac97_write(codec, AC97_HANDSET_RATE, reg | div); + snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0004, div); break; case WM9713_HIFI_DIV: - reg = ac97_read(codec, AC97_HANDSET_RATE) & 0x8fff; - ac97_write(codec, AC97_HANDSET_RATE, reg | div); + snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x7000, div); break; case WM9713_PCMBCLK_DIV: - reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0xf1ff; - ac97_write(codec, AC97_CENTER_LFE_MASTER, reg | div); + snd_soc_update_bits(codec, AC97_CENTER_LFE_MASTER, 0x0e00, div); break; case WM9713_PCMCLK_PLL_DIV: - reg = ac97_read(codec, AC97_LINE1_LEVEL) & 0xff80; - ac97_write(codec, AC97_LINE1_LEVEL, reg | 0x60 | div); + snd_soc_update_bits(codec, AC97_LINE1_LEVEL, + 0x007f, div | 0x60); break; case WM9713_HIFI_PLL_DIV: - reg = ac97_read(codec, AC97_LINE1_LEVEL) & 0xff80; - ac97_write(codec, AC97_LINE1_LEVEL, reg | 0x70 | div); + snd_soc_update_bits(codec, AC97_LINE1_LEVEL, + 0x007f, div | 0x70); break; default: return -EINVAL; @@ -962,7 +939,7 @@ static int wm9713_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { struct snd_soc_codec *codec = codec_dai->codec; - u16 gpio = ac97_read(codec, AC97_GPIO_CFG) & 0xffc5; + u16 gpio = snd_soc_read(codec, AC97_GPIO_CFG) & 0xffc5; u16 reg = 0x8000; /* clock masters */ @@ -1015,8 +992,8 @@ static int wm9713_set_dai_fmt(struct snd_soc_dai *codec_dai, break; } - ac97_write(codec, AC97_GPIO_CFG, gpio); - ac97_write(codec, AC97_CENTER_LFE_MASTER, reg); + snd_soc_write(codec, AC97_GPIO_CFG, gpio); + snd_soc_write(codec, AC97_CENTER_LFE_MASTER, reg); return 0; } @@ -1025,24 +1002,24 @@ static int wm9713_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; - u16 reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0xfff3; + /* enable PCM interface in master mode */ switch (params_width(params)) { case 16: break; case 20: - reg |= 0x0004; + snd_soc_update_bits(codec, AC97_CENTER_LFE_MASTER, + 0x000c, 0x0004); break; case 24: - reg |= 0x0008; + snd_soc_update_bits(codec, AC97_CENTER_LFE_MASTER, + 0x000c, 0x0008); break; case 32: - reg |= 0x000c; + snd_soc_update_bits(codec, AC97_CENTER_LFE_MASTER, + 0x000c, 0x000c); break; } - - /* enable PCM interface in master mode */ - ac97_write(codec, AC97_CENTER_LFE_MASTER, reg); return 0; } @@ -1052,17 +1029,15 @@ static int ac97_hifi_prepare(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = dai->codec; struct snd_pcm_runtime *runtime = substream->runtime; int reg; - u16 vra; - vra = ac97_read(codec, AC97_EXTENDED_STATUS); - ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1); + snd_soc_update_bits(codec, AC97_EXTENDED_STATUS, 0x0001, 0x0001); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) reg = AC97_PCM_FRONT_DAC_RATE; else reg = AC97_PCM_LR_ADC_RATE; - return ac97_write(codec, reg, runtime->rate); + return snd_soc_write(codec, reg, runtime->rate); } static int ac97_aux_prepare(struct snd_pcm_substream *substream, @@ -1070,17 +1045,14 @@ static int ac97_aux_prepare(struct snd_pcm_substream *substream, { struct snd_soc_codec *codec = dai->codec; struct snd_pcm_runtime *runtime = substream->runtime; - u16 vra, xsle; - vra = ac97_read(codec, AC97_EXTENDED_STATUS); - ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1); - xsle = ac97_read(codec, AC97_PCI_SID); - ac97_write(codec, AC97_PCI_SID, xsle | 0x8000); + snd_soc_update_bits(codec, AC97_EXTENDED_STATUS, 0x0001, 0x0001); + snd_soc_update_bits(codec, AC97_PCI_SID, 0x8000, 0x8000); if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) return -ENODEV; - return ac97_write(codec, AC97_PCM_SURR_DAC_RATE, runtime->rate); + return snd_soc_write(codec, AC97_PCM_SURR_DAC_RATE, runtime->rate); } #define WM9713_RATES (SNDRV_PCM_RATE_8000 | \ @@ -1169,27 +1141,23 @@ static struct snd_soc_dai_driver wm9713_dai[] = { static int wm9713_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { - u16 reg; - switch (level) { case SND_SOC_BIAS_ON: /* enable thermal shutdown */ - reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x1bff; - ac97_write(codec, AC97_EXTENDED_MID, reg); + snd_soc_update_bits(codec, AC97_EXTENDED_MID, 0xe400, 0x0000); break; case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: /* enable master bias and vmid */ - reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x3bff; - ac97_write(codec, AC97_EXTENDED_MID, reg); - ac97_write(codec, AC97_POWERDOWN, 0x0000); + snd_soc_update_bits(codec, AC97_EXTENDED_MID, 0xc400, 0x0000); + snd_soc_write(codec, AC97_POWERDOWN, 0x0000); break; case SND_SOC_BIAS_OFF: /* disable everything including AC link */ - ac97_write(codec, AC97_EXTENDED_MID, 0xffff); - ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff); - ac97_write(codec, AC97_POWERDOWN, 0xffff); + snd_soc_write(codec, AC97_EXTENDED_MID, 0xffff); + snd_soc_write(codec, AC97_EXTENDED_MSTATUS, 0xffff); + snd_soc_write(codec, AC97_POWERDOWN, 0xffff); break; } return 0; @@ -1197,16 +1165,14 @@ static int wm9713_set_bias_level(struct snd_soc_codec *codec, static int wm9713_soc_suspend(struct snd_soc_codec *codec) { - u16 reg; - /* Disable everything except touchpanel - that will be handled * by the touch driver and left disabled if touch is not in * use. */ - reg = ac97_read(codec, AC97_EXTENDED_MID); - ac97_write(codec, AC97_EXTENDED_MID, reg | 0x7fff); - ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff); - ac97_write(codec, AC97_POWERDOWN, 0x6f00); - ac97_write(codec, AC97_POWERDOWN, 0xffff); + snd_soc_update_bits(codec, AC97_EXTENDED_MID, 0x7fff, + 0x7fff); + snd_soc_write(codec, AC97_EXTENDED_MSTATUS, 0xffff); + snd_soc_write(codec, AC97_POWERDOWN, 0x6f00); + snd_soc_write(codec, AC97_POWERDOWN, 0xffff); return 0; } @@ -1240,7 +1206,6 @@ static int wm9713_soc_probe(struct snd_soc_codec *codec) { struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); struct regmap *regmap; - int reg; wm9713->ac97 = snd_soc_new_ac97_codec(codec, WM9713_VENDOR_ID, WM9713_VENDOR_ID_MASK); @@ -1256,8 +1221,7 @@ static int wm9713_soc_probe(struct snd_soc_codec *codec) snd_soc_codec_init_regmap(codec, regmap); /* unmute the adc - move to kcontrol */ - reg = ac97_read(codec, AC97_CD) & 0x7fff; - ac97_write(codec, AC97_CD, reg); + snd_soc_update_bits(codec, AC97_CD, 0x7fff, 0x0000); return 0; } -- cgit v1.2.3 From 6e5b143c1d86d75a6d18b9f2cbde3aaebae87423 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Tue, 10 Nov 2015 19:35:18 +0800 Subject: ASoC: rt5645: Use the mod_delayed_work instead of the queue_delayed_work and cancel_delayed_work_sync Signed-off-by: Oder Chiou Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 672fafd8314a..4e81181c00c4 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -572,14 +572,12 @@ static int rt5645_spk_put_volsw(struct snd_kcontrol *kcontrol, struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); int ret; - cancel_delayed_work_sync(&rt5645->rcclock_work); - regmap_update_bits(rt5645->regmap, RT5645_MICBIAS, RT5645_PWR_CLK25M_MASK, RT5645_PWR_CLK25M_PU); ret = snd_soc_put_volsw(kcontrol, ucontrol); - queue_delayed_work(system_power_efficient_wq, &rt5645->rcclock_work, + mod_delayed_work(system_power_efficient_wq, &rt5645->rcclock_work, msecs_to_jiffies(200)); return ret; -- cgit v1.2.3 From 7336dcefac4d8f94fa205a668138a6462841acc4 Mon Sep 17 00:00:00 2001 From: John Lin Date: Mon, 16 Nov 2015 14:41:07 +0800 Subject: ASoC: rl6231: fix range of DMIC clock The maximum DMIC clock rate is 3.072 MHz for most DMIC. And it will get better performance in higher clock rate. If we set maximum to 3 MHz in driver, we will get a clock rate which is not even close to 3 MHz. For example, if DMIC clock source is 24.576 MHz, the DMIC clock will be about 1.5 MHz in current code. But it will be 3.072 MHz with this patch. Signed-off-by: John Lin Signed-off-by: Mark Brown --- sound/soc/codecs/rl6231.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/rl6231.c b/sound/soc/codecs/rl6231.c index 18b42925314e..1dc68ab08a17 100644 --- a/sound/soc/codecs/rl6231.c +++ b/sound/soc/codecs/rl6231.c @@ -82,8 +82,8 @@ int rl6231_calc_dmic_clk(int rate) for (i = 0; i < ARRAY_SIZE(div); i++) { if ((div[i] % 3) == 0) continue; - /* find divider that gives DMIC frequency below 3MHz */ - if (3000000 * div[i] >= rate) + /* find divider that gives DMIC frequency below 3.072MHz */ + if (3072000 * div[i] >= rate) return i; } -- cgit v1.2.3 From 91ed37e45c485533997e8a7c1efd2ca39b441b60 Mon Sep 17 00:00:00 2001 From: John Lin Date: Mon, 16 Nov 2015 13:55:35 +0800 Subject: ASoC: rt5645: Increase the delay time to imporve the HP pop noise Unmuting headphone has pop noise in particular hardware design. So we extend the delay time in headphone unmuting sequence to avoid pop. Signed-off-by: John Lin Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 672fafd8314a..fa8b5dfa673e 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -1519,7 +1519,7 @@ static void hp_amp_power(struct snd_soc_codec *codec, int on) regmap_write(rt5645->regmap, RT5645_PR_BASE + RT5645_MAMP_INT_REG2, 0xfc00); snd_soc_write(codec, RT5645_DEPOP_M2, 0x1140); - msleep(40); + msleep(70); rt5645->hp_on = true; } else { /* depop parameters */ -- cgit v1.2.3 From a3af0c65836e714fa71dcaa0a81f6db83a212faa Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 16 Nov 2015 04:51:21 +0000 Subject: ASoC: ak4613: add single-end optional property for IN/OUT pins ak4613 IN/OUT pin can be selected as differential/single-end. Default is differential, because it is register default settings. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/ak4613.txt | 10 ++++++++ sound/soc/codecs/ak4613.c | 29 ++++++++++++++++++++++ 2 files changed, 39 insertions(+) (limited to 'sound') diff --git a/Documentation/devicetree/bindings/sound/ak4613.txt b/Documentation/devicetree/bindings/sound/ak4613.txt index 15a919522b42..3cf63e7f8e77 100644 --- a/Documentation/devicetree/bindings/sound/ak4613.txt +++ b/Documentation/devicetree/bindings/sound/ak4613.txt @@ -7,6 +7,16 @@ Required properties: - compatible : "asahi-kasei,ak4613" - reg : The chip select number on the I2C bus +Optional properties: +- ak4613,in1-single-end : Boolean. Indicate input / output pins are single-ended. +- ak4613,in2-single-end rather than differential. +- ak4613,out1-single-end +- ak4613,out2-single-end +- ak4613,out3-single-end +- ak4613,out4-single-end +- ak4613,out5-single-end +- ak4613,out6-single-end + Example: &i2c { diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c index 07a266460ec3..394c10ff049e 100644 --- a/sound/soc/codecs/ak4613.c +++ b/sound/soc/codecs/ak4613.c @@ -79,6 +79,8 @@ struct ak4613_priv { unsigned int fmt; u8 fmt_ctrl; + u8 oc; + u8 ic; int cnt; }; @@ -343,6 +345,9 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream, snd_soc_update_bits(codec, CTRL1, FMT_MASK, fmt_ctrl); snd_soc_write(codec, CTRL2, ctrl2); + snd_soc_write(codec, ICTRL, priv->ic); + snd_soc_write(codec, OCTRL, priv->oc); + hw_params_end: if (ret < 0) dev_warn(dev, "unsupported data width/format combination\n"); @@ -431,6 +436,28 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4613 = { .num_dapm_routes = ARRAY_SIZE(ak4613_intercon), }; +static void ak4613_parse_of(struct ak4613_priv *priv, + struct device *dev) +{ + struct device_node *np = dev->of_node; + char prop[32]; + int i; + + /* Input 1 - 2 */ + for (i = 0; i < 2; i++) { + snprintf(prop, sizeof(prop), "ak4613,in%d-single-end", i + 1); + if (!of_get_property(np, prop, NULL)) + priv->ic |= 1 << i; + } + + /* Output 1 - 6 */ + for (i = 0; i < 6; i++) { + snprintf(prop, sizeof(prop), "ak4613,out%d-single-end", i + 1); + if (!of_get_property(np, prop, NULL)) + priv->oc |= 1 << i; + } +} + static int ak4613_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -458,6 +485,8 @@ static int ak4613_i2c_probe(struct i2c_client *i2c, if (!priv) return -ENOMEM; + ak4613_parse_of(priv, dev); + priv->fmt_ctrl = NO_FMT; priv->cnt = 0; -- cgit v1.2.3 From 35299f1779dbdcb61af4305904963b5bc9276eb9 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 16 Nov 2015 06:01:29 +0000 Subject: ASoC: ak4613: tidyup CTRL1 value selection method Current CTRL1 selection method didn't care about simultaneous playback / capture. This patch tidyup it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/codecs/ak4613.c | 90 ++++++++++++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 36 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c index 394c10ff049e..dab127603ff6 100644 --- a/sound/soc/codecs/ak4613.c +++ b/sound/soc/codecs/ak4613.c @@ -74,16 +74,6 @@ #define DFS_DOUBLE_SPEED (1 << 2) #define DFS_QUAD_SPEED (2 << 2) -struct ak4613_priv { - struct mutex lock; - - unsigned int fmt; - u8 fmt_ctrl; - u8 oc; - u8 ic; - int cnt; -}; - struct ak4613_formats { unsigned int width; unsigned int fmt; @@ -94,6 +84,16 @@ struct ak4613_interface { struct ak4613_formats playback; }; +struct ak4613_priv { + struct mutex lock; + const struct ak4613_interface *iface; + + unsigned int fmt; + u8 oc; + u8 ic; + int cnt; +}; + /* * Playback Volume * @@ -128,7 +128,7 @@ static const struct reg_default ak4613_reg[] = { { 0x14, 0x00 }, { 0x15, 0x00 }, { 0x16, 0x00 }, }; -#define AUDIO_IFACE_IDX_TO_VAL(i) (i << 3) +#define AUDIO_IFACE_TO_VAL(fmts) ((fmts - ak4613_iface) << 3) #define AUDIO_IFACE(b, fmt) { b, SND_SOC_DAIFMT_##fmt } static const struct ak4613_interface ak4613_iface[] = { /* capture */ /* playback */ @@ -242,7 +242,7 @@ static void ak4613_dai_shutdown(struct snd_pcm_substream *substream, priv->cnt = 0; } if (!priv->cnt) - priv->fmt_ctrl = NO_FMT; + priv->iface = NULL; mutex_unlock(&priv->lock); } @@ -267,13 +267,35 @@ static int ak4613_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } +static bool ak4613_dai_fmt_matching(const struct ak4613_interface *iface, + int is_play, + unsigned int fmt, unsigned int width) +{ + const struct ak4613_formats *fmts; + + fmts = (is_play) ? &iface->playback : &iface->capture; + + if (fmts->fmt != fmt) + return false; + + if (fmt == SND_SOC_DAIFMT_RIGHT_J) { + if (fmts->width != width) + return false; + } else { + if (fmts->width < width) + return false; + } + + return true; +} + static int ak4613_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec); - const struct ak4613_formats *fmts; + const struct ak4613_interface *iface; struct device *dev = codec->dev; unsigned int width = params_width(params); unsigned int fmt = priv->fmt; @@ -307,33 +329,27 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream, * It doesn't support TDM at this point */ fmt_ctrl = NO_FMT; - for (i = 0; i < ARRAY_SIZE(ak4613_iface); i++) { - fmts = (is_play) ? &ak4613_iface[i].playback : - &ak4613_iface[i].capture; - - if (fmts->fmt != fmt) - continue; + ret = -EINVAL; + iface = NULL; - if (fmt == SND_SOC_DAIFMT_RIGHT_J) { - if (fmts->width != width) - continue; - } else { - if (fmts->width < width) + mutex_lock(&priv->lock); + if (priv->iface) { + if (ak4613_dai_fmt_matching(priv->iface, is_play, fmt, width)) + iface = priv->iface; + } else { + for (i = ARRAY_SIZE(ak4613_iface); i >= 0; i--) { + if (!ak4613_dai_fmt_matching(ak4613_iface + i, + is_play, + fmt, width)) continue; + iface = ak4613_iface + i; + break; } - - fmt_ctrl = AUDIO_IFACE_IDX_TO_VAL(i); - break; } - ret = -EINVAL; - if (fmt_ctrl == NO_FMT) - goto hw_params_end; - - mutex_lock(&priv->lock); - if ((priv->fmt_ctrl == NO_FMT) || - (priv->fmt_ctrl == fmt_ctrl)) { - priv->fmt_ctrl = fmt_ctrl; + if ((priv->iface == NULL) || + (priv->iface == iface)) { + priv->iface = iface; priv->cnt++; ret = 0; } @@ -342,6 +358,8 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream, if (ret < 0) goto hw_params_end; + fmt_ctrl = AUDIO_IFACE_TO_VAL(iface); + snd_soc_update_bits(codec, CTRL1, FMT_MASK, fmt_ctrl); snd_soc_write(codec, CTRL2, ctrl2); @@ -487,7 +505,7 @@ static int ak4613_i2c_probe(struct i2c_client *i2c, ak4613_parse_of(priv, dev); - priv->fmt_ctrl = NO_FMT; + priv->iface = NULL; priv->cnt = 0; mutex_init(&priv->lock); -- cgit v1.2.3 From 0e18d457b31e98e68f6918e41c85ad3b736c4789 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 13 Nov 2015 18:15:56 +0100 Subject: ASoC: fix rockchip 64-bit build warning The rk_spdif_probe uses the device match data as a token to identify a particular device, but accidentally casts a pointer to 'int', which is not portable, as gcc points out in this warning on arm64: rockchip_spdif.c: In function 'rk_spdif_probe': rockchip_spdif.c:283:6: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast] This changes the logic to compare two pointer values instead, using the same cast that was used for initializing the value in the first place. Signed-off-by: Arnd Bergmann Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_spdif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c index a38a3029062c..ac72ff5055bb 100644 --- a/sound/soc/rockchip/rockchip_spdif.c +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -280,7 +280,7 @@ static int rk_spdif_probe(struct platform_device *pdev) int ret; match = of_match_node(rk_spdif_match, np); - if ((int) match->data == RK_SPDIF_RK3288) { + if (match->data == (void *)RK_SPDIF_RK3288) { struct regmap *grf; grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); -- cgit v1.2.3 From e9f96bc53c1b959859599cb30ce6fd4fbb4448c2 Mon Sep 17 00:00:00 2001 From: Sachin Pandhare Date: Tue, 10 Nov 2015 23:38:02 +0530 Subject: ASoC: wm8962: correct addresses for HPF_C_0/1 From datasheet: R17408 (4400h) HPF_C_1 R17409 (4401h) HPF_C_0 17048 -> 17408 (0x4400) 17049 -> 17409 (0x4401) Signed-off-by: Sachin Pandhare Acked-by: Charles Keepax Signed-off-by: Mark Brown Cc: stable@vger.kernel.org --- sound/soc/codecs/wm8962.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 39ebd7bf4f53..a7e79784fc16 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -365,8 +365,8 @@ static const struct reg_default wm8962_reg[] = { { 16924, 0x0059 }, /* R16924 - HDBASS_PG_1 */ { 16925, 0x999A }, /* R16925 - HDBASS_PG_0 */ - { 17048, 0x0083 }, /* R17408 - HPF_C_1 */ - { 17049, 0x98AD }, /* R17409 - HPF_C_0 */ + { 17408, 0x0083 }, /* R17408 - HPF_C_1 */ + { 17409, 0x98AD }, /* R17409 - HPF_C_0 */ { 17920, 0x007F }, /* R17920 - ADCL_RETUNE_C1_1 */ { 17921, 0xFFFF }, /* R17921 - ADCL_RETUNE_C1_0 */ -- cgit v1.2.3 From 49abc6cd58734803fb1428c3dfbb68fbc6ddb68c Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 10 Nov 2015 16:54:54 +0800 Subject: ASoC: rt5645: Separate regmap for rt5645 and rt5650 rt5645.c support both rt5645 and rt5650 codec. And the default value of registers are not identical. So we use different regmap for the two codecs. Signed-off-by: Bard Liao Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 209 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 200 insertions(+), 9 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 4e81181c00c4..11cabf8e7551 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -226,6 +226,163 @@ static const struct reg_default rt5645_reg[] = { { 0xff, 0x6308 }, }; +static const struct reg_default rt5650_reg[] = { + { 0x00, 0x0000 }, + { 0x01, 0xc8c8 }, + { 0x02, 0xc8c8 }, + { 0x03, 0xc8c8 }, + { 0x0a, 0x0002 }, + { 0x0b, 0x2827 }, + { 0x0c, 0xe000 }, + { 0x0d, 0x0000 }, + { 0x0e, 0x0000 }, + { 0x0f, 0x0808 }, + { 0x14, 0x3333 }, + { 0x16, 0x4b00 }, + { 0x18, 0x018b }, + { 0x19, 0xafaf }, + { 0x1a, 0xafaf }, + { 0x1b, 0x0001 }, + { 0x1c, 0x2f2f }, + { 0x1d, 0x2f2f }, + { 0x1e, 0x0000 }, + { 0x20, 0x0000 }, + { 0x27, 0x7060 }, + { 0x28, 0x7070 }, + { 0x29, 0x8080 }, + { 0x2a, 0x5656 }, + { 0x2b, 0x5454 }, + { 0x2c, 0xaaa0 }, + { 0x2d, 0x0000 }, + { 0x2f, 0x1002 }, + { 0x31, 0x5000 }, + { 0x32, 0x0000 }, + { 0x33, 0x0000 }, + { 0x34, 0x0000 }, + { 0x35, 0x0000 }, + { 0x3b, 0x0000 }, + { 0x3c, 0x007f }, + { 0x3d, 0x0000 }, + { 0x3e, 0x007f }, + { 0x3f, 0x0000 }, + { 0x40, 0x001f }, + { 0x41, 0x0000 }, + { 0x42, 0x001f }, + { 0x45, 0x6000 }, + { 0x46, 0x003e }, + { 0x47, 0x003e }, + { 0x48, 0xf807 }, + { 0x4a, 0x0004 }, + { 0x4d, 0x0000 }, + { 0x4e, 0x0000 }, + { 0x4f, 0x01ff }, + { 0x50, 0x0000 }, + { 0x51, 0x0000 }, + { 0x52, 0x01ff }, + { 0x53, 0xf000 }, + { 0x56, 0x0111 }, + { 0x57, 0x0064 }, + { 0x58, 0xef0e }, + { 0x59, 0xf0f0 }, + { 0x5a, 0xef0e }, + { 0x5b, 0xf0f0 }, + { 0x5c, 0xef0e }, + { 0x5d, 0xf0f0 }, + { 0x5e, 0xf000 }, + { 0x5f, 0x0000 }, + { 0x61, 0x0300 }, + { 0x62, 0x0000 }, + { 0x63, 0x00c2 }, + { 0x64, 0x0000 }, + { 0x65, 0x0000 }, + { 0x66, 0x0000 }, + { 0x6a, 0x0000 }, + { 0x6c, 0x0aaa }, + { 0x70, 0x8000 }, + { 0x71, 0x8000 }, + { 0x72, 0x8000 }, + { 0x73, 0x7770 }, + { 0x74, 0x3e00 }, + { 0x75, 0x2409 }, + { 0x76, 0x000a }, + { 0x77, 0x0c00 }, + { 0x78, 0x0000 }, + { 0x79, 0x0123 }, + { 0x7a, 0x0123 }, + { 0x80, 0x0000 }, + { 0x81, 0x0000 }, + { 0x82, 0x0000 }, + { 0x83, 0x0000 }, + { 0x84, 0x0000 }, + { 0x85, 0x0000 }, + { 0x8a, 0x0000 }, + { 0x8e, 0x0004 }, + { 0x8f, 0x1100 }, + { 0x90, 0x0646 }, + { 0x91, 0x0c06 }, + { 0x93, 0x0000 }, + { 0x94, 0x0200 }, + { 0x95, 0x0000 }, + { 0x9a, 0x2184 }, + { 0x9b, 0x010a }, + { 0x9c, 0x0aea }, + { 0x9d, 0x000c }, + { 0x9e, 0x0400 }, + { 0xa0, 0xa0a8 }, + { 0xa1, 0x0059 }, + { 0xa2, 0x0001 }, + { 0xae, 0x6000 }, + { 0xaf, 0x0000 }, + { 0xb0, 0x6000 }, + { 0xb1, 0x0000 }, + { 0xb2, 0x0000 }, + { 0xb3, 0x001f }, + { 0xb4, 0x020c }, + { 0xb5, 0x1f00 }, + { 0xb6, 0x0000 }, + { 0xbb, 0x0000 }, + { 0xbc, 0x0000 }, + { 0xbd, 0x0000 }, + { 0xbe, 0x0000 }, + { 0xbf, 0x3100 }, + { 0xc0, 0x0000 }, + { 0xc1, 0x0000 }, + { 0xc2, 0x0000 }, + { 0xc3, 0x2000 }, + { 0xcd, 0x0000 }, + { 0xce, 0x0000 }, + { 0xcf, 0x1813 }, + { 0xd0, 0x0690 }, + { 0xd1, 0x1c17 }, + { 0xd3, 0xb320 }, + { 0xd4, 0x0000 }, + { 0xd6, 0x0400 }, + { 0xd9, 0x0809 }, + { 0xda, 0x0000 }, + { 0xdb, 0x0003 }, + { 0xdc, 0x0049 }, + { 0xdd, 0x001b }, + { 0xdf, 0x0008 }, + { 0xe0, 0x4000 }, + { 0xe6, 0x8000 }, + { 0xe7, 0x0200 }, + { 0xec, 0xb300 }, + { 0xed, 0x0000 }, + { 0xf0, 0x001f }, + { 0xf1, 0x020c }, + { 0xf2, 0x1f00 }, + { 0xf3, 0x0000 }, + { 0xf4, 0x4000 }, + { 0xf8, 0x0000 }, + { 0xf9, 0x0000 }, + { 0xfa, 0x2060 }, + { 0xfb, 0x4040 }, + { 0xfc, 0x0000 }, + { 0xfd, 0x0002 }, + { 0xfe, 0x10ec }, + { 0xff, 0x6308 }, +}; + struct rt5645_eq_param_s { unsigned short reg; unsigned short val; @@ -3316,6 +3473,31 @@ static const struct regmap_config rt5645_regmap = { .num_ranges = ARRAY_SIZE(rt5645_ranges), }; +static const struct regmap_config rt5650_regmap = { + .reg_bits = 8, + .val_bits = 16, + .use_single_rw = true, + .max_register = RT5645_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5645_ranges) * + RT5645_PR_SPACING), + .volatile_reg = rt5645_volatile_register, + .readable_reg = rt5645_readable_register, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = rt5650_reg, + .num_reg_defaults = ARRAY_SIZE(rt5650_reg), + .ranges = rt5645_ranges, + .num_ranges = ARRAY_SIZE(rt5645_ranges), +}; + +static const struct regmap_config temp_regmap = { + .name="nocache", + .reg_bits = 8, + .val_bits = 16, + .use_single_rw = true, + .max_register = RT5645_VENDOR_ID2 + 1, + .cache_type = REGCACHE_NONE, +}; + static const struct i2c_device_id rt5645_i2c_id[] = { { "rt5645", 0 }, { "rt5650", 0 }, @@ -3426,6 +3608,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, struct rt5645_priv *rt5645; int ret, i; unsigned int val; + struct regmap *regmap; rt5645 = devm_kzalloc(&i2c->dev, sizeof(struct rt5645_priv), GFP_KERNEL); @@ -3451,14 +3634,6 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, return PTR_ERR(rt5645->gpiod_hp_det); } - rt5645->regmap = devm_regmap_init_i2c(i2c, &rt5645_regmap); - if (IS_ERR(rt5645->regmap)) { - ret = PTR_ERR(rt5645->regmap); - dev_err(&i2c->dev, "Failed to allocate register map: %d\n", - ret); - return ret; - } - for (i = 0; i < ARRAY_SIZE(rt5645->supplies); i++) rt5645->supplies[i].supply = rt5645_supply_names[i]; @@ -3477,13 +3652,22 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, return ret; } - regmap_read(rt5645->regmap, RT5645_VENDOR_ID2, &val); + regmap = devm_regmap_init_i2c(i2c, &temp_regmap); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(&i2c->dev, "Failed to allocate temp register map: %d\n", + ret); + return ret; + } + regmap_read(regmap, RT5645_VENDOR_ID2, &val); switch (val) { case RT5645_DEVICE_ID: + rt5645->regmap = devm_regmap_init_i2c(i2c, &rt5645_regmap); rt5645->codec_type = CODEC_TYPE_RT5645; break; case RT5650_DEVICE_ID: + rt5645->regmap = devm_regmap_init_i2c(i2c, &rt5650_regmap); rt5645->codec_type = CODEC_TYPE_RT5650; break; default: @@ -3494,6 +3678,13 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, goto err_enable; } + if (IS_ERR(rt5645->regmap)) { + ret = PTR_ERR(rt5645->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + regmap_write(rt5645->regmap, RT5645_RESET, 0); ret = regmap_register_patch(rt5645->regmap, init_list, -- cgit v1.2.3 From c4f9374ddc461ed76be30f4d354a6d1ecb94dfa5 Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Tue, 10 Nov 2015 15:32:07 +0800 Subject: ASoC: rockchip: i2s: compatible with different chips there maybe more than one i2s module inside chip, and these i2s modules have different channels features. for example: there are 3 i2s in rk3066, one support 8 channels playback and 2 channels capture, but the others only support 2 channels playback and 2 channels capture. in order to compatible with these various chips, we add playback and capture property to specify these values. there are default channels configuration in driver: 8 channels playback and 2 channels capture. if not add property, we use the default values. Signed-off-by: Sugar Zhang Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_i2s.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index ce880f3bccc7..83b1b9c9e017 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -474,6 +474,7 @@ static int rockchip_i2s_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; struct rk_i2s_dev *i2s; + struct snd_soc_dai_driver *soc_dai; struct resource *res; void __iomem *regs; int ret; @@ -534,17 +535,26 @@ static int rockchip_i2s_probe(struct platform_device *pdev) goto err_pm_disable; } - /* refine capture channels */ + soc_dai = devm_kzalloc(&pdev->dev, + sizeof(*soc_dai), GFP_KERNEL); + if (!soc_dai) + return -ENOMEM; + + memcpy(soc_dai, &rockchip_i2s_dai, sizeof(*soc_dai)); + if (!of_property_read_u32(node, "rockchip,playback-channels", &val)) { + if (val >= 2 && val <= 8) + soc_dai->playback.channels_max = val; + } + if (!of_property_read_u32(node, "rockchip,capture-channels", &val)) { if (val >= 2 && val <= 8) - rockchip_i2s_dai.capture.channels_max = val; - else - rockchip_i2s_dai.capture.channels_max = 2; + soc_dai->capture.channels_max = val; } ret = devm_snd_soc_register_component(&pdev->dev, &rockchip_i2s_component, - &rockchip_i2s_dai, 1); + soc_dai, 1); + if (ret) { dev_err(&pdev->dev, "Could not register DAI\n"); goto err_suspend; -- cgit v1.2.3 From 0580bcc91d0aee7367c001955234d71b0b337b41 Mon Sep 17 00:00:00 2001 From: John Lin Date: Wed, 11 Nov 2015 15:25:28 +0800 Subject: ASoC: rt5645: Add struct dmi_system_id "Google Edgar" for Chrome OS Add platform specific data for Edgar project. Signed-off-by: John Lin Signed-off-by: Mark Brown --- sound/soc/codecs/rt5645.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index fa8b5dfa673e..647b594ad04e 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -3378,6 +3378,13 @@ static const struct dmi_system_id dmi_platform_intel_braswell[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Reks"), }, }, + { + .ident = "Google Edgar", + .callback = strago_quirk_cb, + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "Edgar"), + }, + }, { } }; -- cgit v1.2.3 From 93189ea425498339b13e5f74d254070d4a2b7d37 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Wed, 11 Nov 2015 00:18:52 +0100 Subject: ASoC: Intel: constify sst_block_ops structures The sst_block_ops structure is never modified, and is thus declared as const. Done with the help of Coccinelle. Signed-off-by: Julia Lawall Acked-by: Jie Yang Signed-off-by: Mark Brown --- sound/soc/intel/common/sst-dsp-priv.h | 6 +++--- sound/soc/intel/common/sst-firmware.c | 4 ++-- sound/soc/intel/haswell/sst-haswell-dsp.c | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h index 4452cda28874..81aa1ed64201 100644 --- a/sound/soc/intel/common/sst-dsp-priv.h +++ b/sound/soc/intel/common/sst-dsp-priv.h @@ -243,7 +243,7 @@ struct sst_mem_block { u32 size; /* block size */ u32 index; /* block index 0..N */ enum sst_mem_type type; /* block memory type IRAM/DRAM */ - struct sst_block_ops *ops; /* block operations, if any */ + const struct sst_block_ops *ops;/* block operations, if any */ /* block status */ u32 bytes_used; /* bytes in use by modules */ @@ -378,8 +378,8 @@ void sst_block_free_scratch(struct sst_dsp *dsp); /* Register the DSPs memory blocks - would be nice to read from ACPI */ struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset, - u32 size, enum sst_mem_type type, struct sst_block_ops *ops, u32 index, - void *private); + u32 size, enum sst_mem_type type, const struct sst_block_ops *ops, + u32 index, void *private); void sst_mem_block_unregister_all(struct sst_dsp *dsp); /* Create/Free DMA resources */ diff --git a/sound/soc/intel/common/sst-firmware.c b/sound/soc/intel/common/sst-firmware.c index 1636a1eeb002..bee04a9707d8 100644 --- a/sound/soc/intel/common/sst-firmware.c +++ b/sound/soc/intel/common/sst-firmware.c @@ -1014,8 +1014,8 @@ EXPORT_SYMBOL_GPL(sst_module_runtime_restore); /* register a DSP memory block for use with FW based modules */ struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset, - u32 size, enum sst_mem_type type, struct sst_block_ops *ops, u32 index, - void *private) + u32 size, enum sst_mem_type type, const struct sst_block_ops *ops, + u32 index, void *private) { struct sst_mem_block *block; diff --git a/sound/soc/intel/haswell/sst-haswell-dsp.c b/sound/soc/intel/haswell/sst-haswell-dsp.c index 7f94920c8a4d..b2bec36d074c 100644 --- a/sound/soc/intel/haswell/sst-haswell-dsp.c +++ b/sound/soc/intel/haswell/sst-haswell-dsp.c @@ -607,7 +607,7 @@ static int hsw_block_disable(struct sst_mem_block *block) return 0; } -static struct sst_block_ops sst_hsw_ops = { +static const struct sst_block_ops sst_hsw_ops = { .enable = hsw_block_enable, .disable = hsw_block_disable, }; -- cgit v1.2.3 From 4454a8378be5809c2b830531bb4c4712b5e46bef Mon Sep 17 00:00:00 2001 From: Yong Zhi Date: Mon, 9 Nov 2015 12:56:00 -0800 Subject: ASoC: nau8825: add pm function This patch adds pm function and fixes following issues 1.i2c timeout after resume, after resume we saw interrupt handler is called prior to i2c controller is resumed.This causes i2c timeout 2.no audio after resume Signed-off-by: Fang, Yang A Signed-off-by: Yong Zhi Signed-off-by: Mark Brown --- sound/soc/codecs/nau8825.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index 7fc7b4e3f444..c1b87c5800b1 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -1271,6 +1271,36 @@ static int nau8825_i2c_remove(struct i2c_client *client) return 0; } +#ifdef CONFIG_PM_SLEEP +static int nau8825_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct nau8825 *nau8825 = dev_get_drvdata(dev); + + disable_irq(client->irq); + regcache_cache_only(nau8825->regmap, true); + regcache_mark_dirty(nau8825->regmap); + + return 0; +} + +static int nau8825_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct nau8825 *nau8825 = dev_get_drvdata(dev); + + regcache_cache_only(nau8825->regmap, false); + regcache_sync(nau8825->regmap); + enable_irq(client->irq); + + return 0; +} +#endif + +static const struct dev_pm_ops nau8825_pm = { + SET_SYSTEM_SLEEP_PM_OPS(nau8825_suspend, nau8825_resume) +}; + static const struct i2c_device_id nau8825_i2c_ids[] = { { "nau8825", 0 }, { } @@ -1297,6 +1327,7 @@ static struct i2c_driver nau8825_driver = { .name = "nau8825", .of_match_table = of_match_ptr(nau8825_of_ids), .acpi_match_table = ACPI_PTR(nau8825_acpi_match), + .pm = &nau8825_pm, }, .probe = nau8825_i2c_probe, .remove = nau8825_i2c_remove, -- cgit v1.2.3 From d647f0b70ce2b4aeb443639dc92b2d859da697a7 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 13 Nov 2015 19:40:07 -0200 Subject: [media] include/media: move driver interface headers to a separate dir Let's not mix headers used by the core with those headers that are needed by some driver-specific interface header. The headers used on drivers were manually moved using: mkdir include/media/drv-intf/ git mv include/media/cx2341x.h include/media/cx25840.h \ include/media/exynos-fimc.h include/media/msp3400.h \ include/media/s3c_camif.h include/media/saa7146.h \ include/media/saa7146_vv.h include/media/sh_mobile_ceu.h \ include/media/sh_mobile_csi2.h include/media/sh_vou.h \ include/media/si476x.h include/media/soc_mediabus.h \ include/media/tea575x.h include/media/drv-intf/ And the references for those headers were corrected using: MAIN_DIR="media/" PREV_DIR="media/" DIRS="drv-intf/" echo "Checking affected files" >&2 for i in $DIRS; do for j in $(find include/$MAIN_DIR/$i -type f -name '*.h'); do n=`basename $j` git grep -l $n done done|sort|uniq >files && ( echo "Handling files..." >&2; echo "for i in \$(cat files|grep -v Documentation); do cat \$i | \\"; ( cd include/$MAIN_DIR; for j in $DIRS; do for i in $(ls $j); do echo "perl -ne 's,(include [\\\"\\<])$PREV_DIR($i)([\\\"\\>]),\1$MAIN_DIR$j\2\3,; print \$_' |\\"; done; done; echo "cat > a && mv a \$i; done"; ); echo "Handling documentation..." >&2; echo "for i in MAINTAINERS \$(cat files); do cat \$i | \\"; ( cd include/$MAIN_DIR; for j in $DIRS; do for i in $(ls $j); do echo " perl -ne 's,include/$PREV_DIR($i)\b,include/$MAIN_DIR$j\1,; print \$_' |\\"; done; done; echo "cat > a && mv a \$i; done" ); ) >script && . ./script Signed-off-by: Mauro Carvalho Chehab Acked-by: Arnd Bergmann --- MAINTAINERS | 4 +- arch/sh/boards/mach-ap325rxa/setup.c | 2 +- arch/sh/boards/mach-ecovec24/setup.c | 4 +- arch/sh/boards/mach-kfr2r09/setup.c | 2 +- arch/sh/boards/mach-migor/setup.c | 2 +- arch/sh/boards/mach-se/7724/setup.c | 4 +- drivers/media/common/cx2341x.c | 2 +- drivers/media/common/saa7146/saa7146_core.c | 2 +- drivers/media/common/saa7146/saa7146_fops.c | 2 +- drivers/media/common/saa7146/saa7146_hlp.c | 2 +- drivers/media/common/saa7146/saa7146_i2c.c | 2 +- drivers/media/common/saa7146/saa7146_vbi.c | 2 +- drivers/media/common/saa7146/saa7146_video.c | 2 +- drivers/media/i2c/cx25840/cx25840-audio.c | 2 +- drivers/media/i2c/cx25840/cx25840-core.c | 2 +- drivers/media/i2c/cx25840/cx25840-firmware.c | 2 +- drivers/media/i2c/cx25840/cx25840-ir.c | 2 +- drivers/media/i2c/cx25840/cx25840-vbi.c | 2 +- drivers/media/i2c/m5mols/m5mols_capture.c | 2 +- drivers/media/i2c/msp3400-driver.c | 2 +- drivers/media/i2c/msp3400-driver.h | 2 +- drivers/media/i2c/msp3400-kthreads.c | 2 +- drivers/media/i2c/soc_camera/mt9m001.c | 2 +- drivers/media/i2c/soc_camera/mt9v022.c | 2 +- drivers/media/pci/bt8xx/bttv-driver.c | 2 +- drivers/media/pci/bt8xx/bttvp.h | 2 +- drivers/media/pci/cx18/cx23418.h | 2 +- drivers/media/pci/cx23885/cx23885-417.c | 2 +- drivers/media/pci/cx23885/cx23885-cards.c | 2 +- drivers/media/pci/cx23885/cx23885-video.c | 2 +- drivers/media/pci/cx23885/cx23885.h | 2 +- drivers/media/pci/cx88/cx88-blackbird.c | 2 +- drivers/media/pci/cx88/cx88.h | 2 +- drivers/media/pci/ivtv/ivtv-cards.c | 4 +- drivers/media/pci/ivtv/ivtv-driver.h | 2 +- drivers/media/pci/ivtv/ivtv-i2c.c | 2 +- drivers/media/pci/ivtv/ivtv-routing.c | 2 +- drivers/media/pci/saa7146/hexium_gemini.c | 2 +- drivers/media/pci/saa7146/hexium_orion.c | 2 +- drivers/media/pci/saa7146/mxb.c | 2 +- drivers/media/pci/ttpci/av7110.h | 2 +- drivers/media/pci/ttpci/budget-av.c | 2 +- drivers/media/pci/ttpci/budget.h | 2 +- drivers/media/platform/exynos4-is/common.c | 2 +- drivers/media/platform/exynos4-is/fimc-core.h | 2 +- drivers/media/platform/exynos4-is/fimc-isp-video.c | 2 +- drivers/media/platform/exynos4-is/fimc-isp.h | 2 +- drivers/media/platform/exynos4-is/fimc-lite-reg.c | 2 +- drivers/media/platform/exynos4-is/fimc-lite.c | 2 +- drivers/media/platform/exynos4-is/fimc-lite.h | 2 +- drivers/media/platform/exynos4-is/fimc-reg.c | 2 +- drivers/media/platform/exynos4-is/media-dev.c | 2 +- drivers/media/platform/exynos4-is/media-dev.h | 2 +- drivers/media/platform/exynos4-is/mipi-csis.c | 2 +- drivers/media/platform/s3c-camif/camif-core.h | 2 +- drivers/media/platform/s3c-camif/camif-regs.h | 2 +- drivers/media/platform/sh_vou.c | 2 +- drivers/media/platform/soc_camera/atmel-isi.c | 2 +- drivers/media/platform/soc_camera/mx2_camera.c | 2 +- drivers/media/platform/soc_camera/mx3_camera.c | 2 +- drivers/media/platform/soc_camera/omap1_camera.c | 2 +- drivers/media/platform/soc_camera/pxa_camera.c | 2 +- drivers/media/platform/soc_camera/rcar_vin.c | 2 +- .../platform/soc_camera/sh_mobile_ceu_camera.c | 6 +- drivers/media/platform/soc_camera/sh_mobile_csi2.c | 6 +- drivers/media/platform/soc_camera/soc_camera.c | 2 +- drivers/media/platform/soc_camera/soc_mediabus.c | 2 +- drivers/media/radio/radio-maxiradio.c | 2 +- drivers/media/radio/radio-sf16fmr2.c | 2 +- drivers/media/radio/radio-shark.c | 2 +- drivers/media/radio/radio-si476x.c | 2 +- drivers/media/radio/tea575x.c | 2 +- drivers/media/usb/cx231xx/cx231xx-417.c | 2 +- drivers/media/usb/cx231xx/cx231xx-cards.c | 2 +- drivers/media/usb/cx231xx/cx231xx-vbi.c | 2 +- drivers/media/usb/cx231xx/cx231xx-video.c | 2 +- drivers/media/usb/cx231xx/cx231xx.h | 2 +- drivers/media/usb/em28xx/em28xx-cards.c | 2 +- drivers/media/usb/em28xx/em28xx-video.c | 2 +- drivers/media/usb/pvrusb2/pvrusb2-audio.c | 2 +- drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c | 2 +- drivers/media/usb/pvrusb2/pvrusb2-hdw-internal.h | 2 +- include/media/cx2341x.h | 295 ------------- include/media/cx25840.h | 188 -------- include/media/drv-intf/cx2341x.h | 295 +++++++++++++ include/media/drv-intf/cx25840.h | 188 ++++++++ include/media/drv-intf/exynos-fimc.h | 161 +++++++ include/media/drv-intf/msp3400.h | 225 ++++++++++ include/media/drv-intf/s3c_camif.h | 45 ++ include/media/drv-intf/saa7146.h | 471 +++++++++++++++++++++ include/media/drv-intf/saa7146_vv.h | 265 ++++++++++++ include/media/drv-intf/sh_mobile_ceu.h | 29 ++ include/media/drv-intf/sh_mobile_csi2.h | 48 +++ include/media/drv-intf/sh_vou.h | 33 ++ include/media/drv-intf/si476x.h | 37 ++ include/media/drv-intf/soc_mediabus.h | 112 +++++ include/media/drv-intf/tea575x.h | 84 ++++ include/media/exynos-fimc.h | 161 ------- include/media/msp3400.h | 226 ---------- include/media/s3c_camif.h | 45 -- include/media/saa7146.h | 471 --------------------- include/media/saa7146_vv.h | 265 ------------ include/media/sh_mobile_ceu.h | 29 -- include/media/sh_mobile_csi2.h | 48 --- include/media/sh_vou.h | 33 -- include/media/si476x.h | 37 -- include/media/soc_mediabus.h | 112 ----- include/media/tea575x.h | 84 ---- include/uapi/linux/v4l2-controls.h | 6 +- sound/pci/es1968.c | 2 +- sound/pci/fm801.c | 2 +- 111 files changed, 2089 insertions(+), 2088 deletions(-) delete mode 100644 include/media/cx2341x.h delete mode 100644 include/media/cx25840.h create mode 100644 include/media/drv-intf/cx2341x.h create mode 100644 include/media/drv-intf/cx25840.h create mode 100644 include/media/drv-intf/exynos-fimc.h create mode 100644 include/media/drv-intf/msp3400.h create mode 100644 include/media/drv-intf/s3c_camif.h create mode 100644 include/media/drv-intf/saa7146.h create mode 100644 include/media/drv-intf/saa7146_vv.h create mode 100644 include/media/drv-intf/sh_mobile_ceu.h create mode 100644 include/media/drv-intf/sh_mobile_csi2.h create mode 100644 include/media/drv-intf/sh_vou.h create mode 100644 include/media/drv-intf/si476x.h create mode 100644 include/media/drv-intf/soc_mediabus.h create mode 100644 include/media/drv-intf/tea575x.h delete mode 100644 include/media/exynos-fimc.h delete mode 100644 include/media/msp3400.h delete mode 100644 include/media/s3c_camif.h delete mode 100644 include/media/saa7146.h delete mode 100644 include/media/saa7146_vv.h delete mode 100644 include/media/sh_mobile_ceu.h delete mode 100644 include/media/sh_mobile_csi2.h delete mode 100644 include/media/sh_vou.h delete mode 100644 include/media/si476x.h delete mode 100644 include/media/soc_mediabus.h delete mode 100644 include/media/tea575x.h (limited to 'sound') diff --git a/MAINTAINERS b/MAINTAINERS index a8e3f478d869..dc1787719c2a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9225,7 +9225,7 @@ L: linux-media@vger.kernel.org L: linux-samsung-soc@vger.kernel.org (moderated for non-subscribers) S: Maintained F: drivers/media/platform/s3c-camif/ -F: include/media/s3c_camif.h +F: include/media/drv-intf/s3c_camif.h SAMSUNG S5C73M3 CAMERA DRIVER M: Kyungmin Park @@ -9686,7 +9686,7 @@ SH_VOU V4L2 OUTPUT DRIVER L: linux-media@vger.kernel.org S: Orphan F: drivers/media/platform/sh_vou.c -F: include/media/sh_vou.h +F: include/media/drv-intf/sh_vou.h SIMPLE FIRMWARE INTERFACE (SFI) M: Len Brown diff --git a/arch/sh/boards/mach-ap325rxa/setup.c b/arch/sh/boards/mach-ap325rxa/setup.c index 62b045c6d289..d642a77d1913 100644 --- a/arch/sh/boards/mach-ap325rxa/setup.c +++ b/arch/sh/boards/mach-ap325rxa/setup.c @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include