From ed70f3a264e9f746eaf17c96ccc4c9b7eda742dc Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 4 Jun 2014 10:11:06 +0100 Subject: [PATCH 01/16] ASoC: arizona: Implement TDM support for Arizona devices Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- include/linux/mfd/arizona/core.h | 3 ++ sound/soc/codecs/arizona.c | 78 +++++++++++++++++++++++++++++--- 2 files changed, 75 insertions(+), 6 deletions(-) diff --git a/include/linux/mfd/arizona/core.h b/include/linux/mfd/arizona/core.h index 5cf8b91ce996..11783b511b9a 100644 --- a/include/linux/mfd/arizona/core.h +++ b/include/linux/mfd/arizona/core.h @@ -110,6 +110,9 @@ struct arizona { int clk32k_ref; struct snd_soc_dapm_context *dapm; + + int tdm_width[ARIZONA_MAX_AIF]; + int tdm_slots[ARIZONA_MAX_AIF]; }; int arizona_clk32k_enable(struct arizona *arizona); diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 29e198f57d4c..e77f61c387f7 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -1185,7 +1185,10 @@ static int arizona_hw_params(struct snd_pcm_substream *substream, int base = dai->driver->base; const int *rates; int i, ret, val; + int channels = params_channels(params); int chan_limit = arizona->pdata.max_channels_clocked[dai->id - 1]; + int tdm_width = arizona->tdm_width[dai->id - 1]; + int tdm_slots = arizona->tdm_slots[dai->id - 1]; int bclk, lrclk, wl, frame, bclk_target; if (params_rate(params) % 8000) @@ -1193,18 +1196,27 @@ static int arizona_hw_params(struct snd_pcm_substream *substream, else rates = &arizona_48k_bclk_rates[0]; - bclk_target = snd_soc_params_to_bclk(params); - if (chan_limit && chan_limit < params_channels(params)) { + if (tdm_slots) { + arizona_aif_dbg(dai, "Configuring for %d %d bit TDM slots\n", + tdm_slots, tdm_width); + bclk_target = tdm_slots * tdm_width * params_rate(params); + channels = tdm_slots; + } else { + bclk_target = snd_soc_params_to_bclk(params); + } + + if (chan_limit && chan_limit < channels) { arizona_aif_dbg(dai, "Limiting to %d channels\n", chan_limit); - bclk_target /= params_channels(params); + bclk_target /= channels; bclk_target *= chan_limit; } - /* Force stereo for I2S mode */ + /* Force multiple of 2 channels for I2S mode */ val = snd_soc_read(codec, base + ARIZONA_AIF_FORMAT); - if (params_channels(params) == 1 && (val & ARIZONA_AIF1_FMT_MASK)) { + if ((channels & 1) && (val & ARIZONA_AIF1_FMT_MASK)) { arizona_aif_dbg(dai, "Forcing stereo mode\n"); - bclk_target *= 2; + bclk_target /= channels; + bclk_target *= channels + 1; } for (i = 0; i < ARRAY_SIZE(arizona_44k1_bclk_rates); i++) { @@ -1324,9 +1336,63 @@ static int arizona_set_tristate(struct snd_soc_dai *dai, int tristate) ARIZONA_AIF1_TRI, reg); } +static void arizona_set_channels_to_mask(struct snd_soc_dai *dai, + unsigned int base, + int channels, unsigned int mask) +{ + struct snd_soc_codec *codec = dai->codec; + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = priv->arizona; + int slot, i; + + for (i = 0; i < channels; ++i) { + slot = ffs(mask) - 1; + if (slot < 0) + return; + + regmap_write(arizona->regmap, base + i, slot); + + mask &= ~(1 << slot); + } + + if (mask) + arizona_aif_warn(dai, "Too many channels in TDM mask\n"); +} + +static int arizona_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_codec *codec = dai->codec; + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = priv->arizona; + int base = dai->driver->base; + int rx_max_chan = dai->driver->playback.channels_max; + int tx_max_chan = dai->driver->capture.channels_max; + + /* Only support TDM for the physical AIFs */ + if (dai->id > ARIZONA_MAX_AIF) + return -ENOTSUPP; + + if (slots == 0) { + tx_mask = (1 << tx_max_chan) - 1; + rx_mask = (1 << rx_max_chan) - 1; + } + + arizona_set_channels_to_mask(dai, base + ARIZONA_AIF_FRAME_CTRL_3, + tx_max_chan, tx_mask); + arizona_set_channels_to_mask(dai, base + ARIZONA_AIF_FRAME_CTRL_11, + rx_max_chan, rx_mask); + + arizona->tdm_width[dai->id - 1] = slot_width; + arizona->tdm_slots[dai->id - 1] = slots; + + return 0; +} + const struct snd_soc_dai_ops arizona_dai_ops = { .startup = arizona_startup, .set_fmt = arizona_set_fmt, + .set_tdm_slot = arizona_set_tdm_slot, .hw_params = arizona_hw_params, .set_sysclk = arizona_dai_set_sysclk, .set_tristate = arizona_set_tristate, From cc9e92431ee9c7fe974266e0e6533a1a68e45539 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 6 Jun 2014 14:14:05 +0100 Subject: [PATCH 02/16] ASoC: wm5102: Add controls to allow shaping of ultrasonic response Add controls to allow custom shaping of the ultrasonic response. This custom shaping can be turned on/off at runtime, although, it should be noted that settings will not affect a currently open audio stream, they will be applied when the next audio stream is started. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- include/linux/mfd/arizona/core.h | 3 ++ sound/soc/codecs/arizona.c | 34 ++++++++++++++++++ sound/soc/codecs/wm5102.c | 62 ++++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+) diff --git a/include/linux/mfd/arizona/core.h b/include/linux/mfd/arizona/core.h index 11783b511b9a..55926517d50b 100644 --- a/include/linux/mfd/arizona/core.h +++ b/include/linux/mfd/arizona/core.h @@ -113,6 +113,9 @@ struct arizona { int tdm_width[ARIZONA_MAX_AIF]; int tdm_slots[ARIZONA_MAX_AIF]; + + uint16_t dac_comp_coeff; + uint8_t dac_comp_enabled; }; int arizona_clk32k_enable(struct arizona *arizona); diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index e77f61c387f7..41b56ee6ff51 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -1127,6 +1127,31 @@ static int arizona_startup(struct snd_pcm_substream *substream, constraint); } +static void arizona_wm5102_set_dac_comp(struct snd_soc_codec *codec, + unsigned int rate) +{ + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = priv->arizona; + struct reg_default dac_comp[] = { + { 0x80, 0x3 }, + { ARIZONA_DAC_COMP_1, 0 }, + { ARIZONA_DAC_COMP_2, 0 }, + { 0x80, 0x0 }, + }; + + mutex_lock(&codec->mutex); + + dac_comp[1].def = arizona->dac_comp_coeff; + if (rate >= 176400) + dac_comp[2].def = arizona->dac_comp_enabled; + + mutex_unlock(&codec->mutex); + + regmap_multi_reg_write(arizona->regmap, + dac_comp, + ARRAY_SIZE(dac_comp)); +} + static int arizona_hw_params_rate(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -1153,6 +1178,15 @@ static int arizona_hw_params_rate(struct snd_pcm_substream *substream, switch (dai_priv->clk) { case ARIZONA_CLK_SYSCLK: + switch (priv->arizona->type) { + case WM5102: + arizona_wm5102_set_dac_comp(codec, + params_rate(params)); + break; + default: + break; + } + snd_soc_update_bits(codec, ARIZONA_SAMPLE_RATE_1, ARIZONA_SAMPLE_RATE_1_MASK, sr_val); if (base) diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index dcf1d12cfef8..7bf2397fc25a 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -612,6 +612,62 @@ static int wm5102_sysclk_ev(struct snd_soc_dapm_widget *w, return 0; } +static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct arizona *arizona = dev_get_drvdata(codec->dev->parent); + uint16_t data; + + mutex_lock(&codec->mutex); + data = cpu_to_be16(arizona->dac_comp_coeff); + memcpy(ucontrol->value.bytes.data, &data, sizeof(data)); + mutex_unlock(&codec->mutex); + + return 0; +} + +static int wm5102_out_comp_coeff_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct arizona *arizona = dev_get_drvdata(codec->dev->parent); + + mutex_lock(&codec->mutex); + memcpy(&arizona->dac_comp_coeff, ucontrol->value.bytes.data, + sizeof(arizona->dac_comp_coeff)); + arizona->dac_comp_coeff = be16_to_cpu(arizona->dac_comp_coeff); + mutex_unlock(&codec->mutex); + + return 0; +} + +static int wm5102_out_comp_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct arizona *arizona = dev_get_drvdata(codec->dev->parent); + + mutex_lock(&codec->mutex); + ucontrol->value.integer.value[0] = arizona->dac_comp_enabled; + mutex_unlock(&codec->mutex); + + return 0; +} + +static int wm5102_out_comp_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct arizona *arizona = dev_get_drvdata(codec->dev->parent); + + mutex_lock(&codec->mutex); + arizona->dac_comp_enabled = ucontrol->value.integer.value[0]; + mutex_unlock(&codec->mutex); + + return 0; +} + static const char *wm5102_osr_text[] = { "Low power", "Normal", "High performance", }; @@ -843,6 +899,12 @@ 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), +SND_SOC_BYTES_EXT("Output Compensation Coefficient", 2, + wm5102_out_comp_coeff_get, wm5102_out_comp_coeff_put), + +SOC_SINGLE_EXT("Output Compensation Switch", 0, 0, 1, 0, + wm5102_out_comp_switch_get, wm5102_out_comp_switch_put), + WM5102_NG_SRC("HPOUT1L", ARIZONA_NOISE_GATE_SELECT_1L), WM5102_NG_SRC("HPOUT1R", ARIZONA_NOISE_GATE_SELECT_1R), WM5102_NG_SRC("HPOUT2L", ARIZONA_NOISE_GATE_SELECT_2L), From 5c3fc7a79aae474670096d73da21aec9f0750abb Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 11 Jun 2014 12:50:58 +0100 Subject: [PATCH 03/16] ASoC: wm5102: Convert snd_kcontrol_chip to snd_soc_kcontrol_codec Controls for shaping the ultrasonic frequency response were introduced in this commit: commit 720630c002ffc7b0fa2ed5b3f4bfb36fd8f87ca6 ASoC: wm5102: Add controls to allow shaping of ultrasonic response However, they mistakenly used snd_kcontrol_chip instead of snd_soc_kcontrol_codec, which has replaced it now the framework is moving to componentisation. This patch fixes this. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm5102.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index 7bf2397fc25a..58913580cc5a 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -615,7 +615,7 @@ static int wm5102_sysclk_ev(struct snd_soc_dapm_widget *w, static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct arizona *arizona = dev_get_drvdata(codec->dev->parent); uint16_t data; @@ -630,7 +630,7 @@ static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol, static int wm5102_out_comp_coeff_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct arizona *arizona = dev_get_drvdata(codec->dev->parent); mutex_lock(&codec->mutex); @@ -645,7 +645,7 @@ static int wm5102_out_comp_coeff_put(struct snd_kcontrol *kcontrol, static int wm5102_out_comp_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct arizona *arizona = dev_get_drvdata(codec->dev->parent); mutex_lock(&codec->mutex); @@ -658,7 +658,7 @@ static int wm5102_out_comp_switch_get(struct snd_kcontrol *kcontrol, static int wm5102_out_comp_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct arizona *arizona = dev_get_drvdata(codec->dev->parent); mutex_lock(&codec->mutex); From fb668e735bef560291429d0694a5020695062cbb Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 27 Mar 2014 21:42:14 +0100 Subject: [PATCH 04/16] ASoC: ak5386: add regulator consumer support The chip has two power supplies, VA and VDD. Enable them both as long as the codec is in use. Signed-off-by: Daniel Mack Signed-off-by: Mark Brown --- sound/soc/codecs/ak5386.c | 50 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/sound/soc/codecs/ak5386.c b/sound/soc/codecs/ak5386.c index 72e953b2cb41..a30be5ce3fa4 100644 --- a/sound/soc/codecs/ak5386.c +++ b/sound/soc/codecs/ak5386.c @@ -14,12 +14,18 @@ #include #include #include +#include #include #include #include +static const char const *supply_names[] = { + "va", "vd" +}; + struct ak5386_priv { int reset_gpio; + struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)]; }; static const struct snd_soc_dapm_widget ak5386_dapm_widgets[] = { @@ -32,7 +38,42 @@ static const struct snd_soc_dapm_route ak5386_dapm_routes[] = { { "Capture", NULL, "AINR" }, }; +static int ak5386_soc_probe(struct snd_soc_codec *codec) +{ + struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec); + return regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies); +} + +static int ak5386_soc_remove(struct snd_soc_codec *codec) +{ + struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec); + regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies); + return 0; +} + +#ifdef CONFIG_PM +static int ak5386_soc_suspend(struct snd_soc_codec *codec) +{ + struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec); + regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies); + return 0; +} + +static int ak5386_soc_resume(struct snd_soc_codec *codec) +{ + struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec); + return regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies); +} +#else +#define ak5386_soc_suspend NULL +#define ak5386_soc_resume NULL +#endif /* CONFIG_PM */ + static struct snd_soc_codec_driver soc_codec_ak5386 = { + .probe = ak5386_soc_probe, + .remove = ak5386_soc_remove, + .suspend = ak5386_soc_suspend, + .resume = ak5386_soc_resume, .dapm_widgets = ak5386_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(ak5386_dapm_widgets), .dapm_routes = ak5386_dapm_routes, @@ -122,6 +163,7 @@ static int ak5386_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct ak5386_priv *priv; + int ret, i; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -130,6 +172,14 @@ static int ak5386_probe(struct platform_device *pdev) priv->reset_gpio = -EINVAL; dev_set_drvdata(dev, priv); + for (i = 0; i < ARRAY_SIZE(supply_names); i++) + priv->supplies[i].supply = supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(priv->supplies), + priv->supplies); + if (ret < 0) + return ret; + if (of_match_device(of_match_ptr(ak5386_dt_ids), dev)) priv->reset_gpio = of_get_named_gpio(dev->of_node, "reset-gpio", 0); From 2ad76541026ba5c6c93b5a5a6f1418aa89fa5f34 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Fri, 28 Mar 2014 19:05:04 +0100 Subject: [PATCH 05/16] ASoC: ak5386: Add regulators to documentation and fix sparse warning Document the newly added regulators to the DT binding document. Also, "static const char const *x" is not identical to "static const char * const x", which sparse now complains about. Fix it. Signed-off-by: Daniel Mack Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/ak5386.txt | 4 ++++ sound/soc/codecs/ak5386.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/sound/ak5386.txt b/Documentation/devicetree/bindings/sound/ak5386.txt index dc3914fe6ce8..ec3df3abba0c 100644 --- a/Documentation/devicetree/bindings/sound/ak5386.txt +++ b/Documentation/devicetree/bindings/sound/ak5386.txt @@ -10,10 +10,14 @@ Optional properties: - reset-gpio : a GPIO spec for the reset/power down pin. If specified, it will be deasserted at probe time. + - va-supply : a regulator spec, providing 5.0V + - vd-supply : a regulator spec, providing 3.3V Example: spdif: ak5386@0 { compatible = "asahi-kasei,ak5386"; reset-gpio = <&gpio0 23>; + va-supply = <&vdd_5v0_reg>; + vd-supply = <&vdd_3v3_reg>; }; diff --git a/sound/soc/codecs/ak5386.c b/sound/soc/codecs/ak5386.c index a30be5ce3fa4..8107a1cac876 100644 --- a/sound/soc/codecs/ak5386.c +++ b/sound/soc/codecs/ak5386.c @@ -19,7 +19,7 @@ #include #include -static const char const *supply_names[] = { +static const char * const supply_names[] = { "va", "vd" }; From e73a2571988870372f7f2b6072a98f49e414be66 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 19 Jun 2014 07:50:01 +0200 Subject: [PATCH 06/16] ASoC: wm5100/wm8903/wm8996: Replace open-coded snd_soc_dapm_to_codec() We now have a generic helper function to cast from a DAPM context to a CODEC. Make use of it in the places which previously open-coded it. Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm5100.c | 3 +-- sound/soc/codecs/wm8903.c | 3 +-- sound/soc/codecs/wm8996.c | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index eca983fad891..42a3ff3a1b68 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -735,8 +735,7 @@ WM5100_MIXER_CONTROLS("LHPF4", WM5100_HPLP4MIX_INPUT_1_SOURCE), static void wm5100_seq_notifier(struct snd_soc_dapm_context *dapm, enum snd_soc_dapm_type event, int subseq) { - struct snd_soc_codec *codec = container_of(dapm, - struct snd_soc_codec, dapm); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec); u16 val, expect, i; diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index b0084a127d18..2116e79085ec 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -281,8 +281,7 @@ static int wm8903_dcs_event(struct snd_soc_dapm_widget *w, static void wm8903_seq_notifier(struct snd_soc_dapm_context *dapm, enum snd_soc_dapm_type event, int subseq) { - struct snd_soc_codec *codec = container_of(dapm, - struct snd_soc_codec, dapm); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); int dcs_mode = WM8903_DCS_MODE_WRITE_STOP; int i, val; diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index c6cbb3b8ace9..8f4d2cddb962 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c @@ -690,8 +690,7 @@ static void wait_for_dc_servo(struct snd_soc_codec *codec, u16 mask) static void wm8996_seq_notifier(struct snd_soc_dapm_context *dapm, enum snd_soc_dapm_type event, int subseq) { - struct snd_soc_codec *codec = container_of(dapm, - struct snd_soc_codec, dapm); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec); u16 val, mask; From 4714bc015de147c2da5248762b2772c6dbf160eb Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 9 Jul 2014 17:41:43 +0100 Subject: [PATCH 07/16] ASoC: arizona: Do not test ratio zero as it is not a valid setting Zero is not a valid FRATIO for the FLL, as such we shouldn't test it whilst refining the FRATIO. This patch does just that. Reported-by: Ryo Tsutsui Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/arizona.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 41b56ee6ff51..b03974eb541e 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -1585,7 +1585,7 @@ static int arizona_calc_fratio(struct arizona_fll *fll, } } - for (ratio = init_ratio - 1; ratio >= 0; ratio--) { + for (ratio = init_ratio - 1; ratio > 0; ratio--) { if (ARIZONA_FLL_VCO_CORNER / (fll->vco_mult * ratio) < Fref) break; From 29fee829408117624efcd478f6f228d77039b969 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 9 Jul 2014 17:41:44 +0100 Subject: [PATCH 08/16] ASoC: arizona: Correct checking of FLL ratio limitations The check to ensure the Fref frequency is within the bounds for the current ratio, was placed in the wrong loop. The initial configuration will always be valid and the loop lowering the ratio will only reinforce this validity. The check should be on the loop increasing the ratio. This could on occasion cause an invalid ratio/Fref combination to be selected. Reported-by: Ryo Tsutsui Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/arizona.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index b03974eb541e..4c4727f0837c 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -1578,6 +1578,10 @@ static int arizona_calc_fratio(struct arizona_fll *fll, while (div <= ARIZONA_FLL_MAX_REFDIV) { for (ratio = init_ratio; ratio <= ARIZONA_FLL_MAX_FRATIO; ratio++) { + if (ARIZONA_FLL_VCO_CORNER / (fll->vco_mult * ratio) < + Fref) + break; + if (target % (ratio * Fref)) { cfg->refdiv = refdiv; cfg->fratio = ratio - 1; @@ -1586,10 +1590,6 @@ static int arizona_calc_fratio(struct arizona_fll *fll, } for (ratio = init_ratio - 1; ratio > 0; ratio--) { - if (ARIZONA_FLL_VCO_CORNER / (fll->vco_mult * ratio) < - Fref) - break; - if (target % (ratio * Fref)) { cfg->refdiv = refdiv; cfg->fratio = ratio - 1; From 35a730a0790b2a6535e0b4a964c6a8b2d27fa019 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 9 Jul 2014 17:41:45 +0100 Subject: [PATCH 09/16] ASoC: arizona: Correct relationship between VCO corner and Fref When configuring the FLL we must ensure that the reference clock passed to the FLL is under a certain limit. This limit was specified incorrectly in the current code, this patch corrects this. Although the error will only be encountered in some edge cases. Reported-by: Ryo Tsutsui Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/arizona.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 4c4727f0837c..6084af76f337 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -1578,8 +1578,8 @@ static int arizona_calc_fratio(struct arizona_fll *fll, while (div <= ARIZONA_FLL_MAX_REFDIV) { for (ratio = init_ratio; ratio <= ARIZONA_FLL_MAX_FRATIO; ratio++) { - if (ARIZONA_FLL_VCO_CORNER / (fll->vco_mult * ratio) < - Fref) + if ((ARIZONA_FLL_VCO_CORNER / 2) / + (fll->vco_mult * ratio) < Fref) break; if (target % (ratio * Fref)) { From 613124ce644c38857ef5780a96e6d400d6b11abd Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 9 Jul 2014 17:41:46 +0100 Subject: [PATCH 10/16] ASoC: arizona: Coding standards, remove unneeded brackets Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/arizona.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 6084af76f337..0a417e8bcadd 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -1868,11 +1868,10 @@ int arizona_set_fll(struct arizona_fll *fll, int source, fll->sync_freq = Fref; fll->fout = Fout; - if (Fout) { + if (Fout) arizona_enable_fll(fll); - } else { + else arizona_disable_fll(fll); - } return 0; } From c393aca94f26e90290cd861743efbfbcd97c0316 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 9 Jul 2014 17:41:47 +0100 Subject: [PATCH 11/16] ASoC: arizona: Correct return value of arizona_is_enabled_fll arizona_is_enabled_fll currently returns a bool, but can throw an error. The error will be basically ignored and we will treat the FLL as already on. This patch changes the return to be an int and adds error code to propagate the error up to the callback. Reported-by: Anil Kumar Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/arizona.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 0a417e8bcadd..a6f09bb0a2e9 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -1716,7 +1716,7 @@ static void arizona_apply_fll(struct arizona *arizona, unsigned int base, ARIZONA_FLL1_CTRL_UPD | cfg->n); } -static bool arizona_is_enabled_fll(struct arizona_fll *fll) +static int arizona_is_enabled_fll(struct arizona_fll *fll) { struct arizona *arizona = fll->arizona; unsigned int reg; @@ -1732,13 +1732,17 @@ static bool arizona_is_enabled_fll(struct arizona_fll *fll) return reg & ARIZONA_FLL1_ENA; } -static void arizona_enable_fll(struct arizona_fll *fll) +static int arizona_enable_fll(struct arizona_fll *fll) { struct arizona *arizona = fll->arizona; int ret; bool use_sync = false; + int already_enabled = arizona_is_enabled_fll(fll); struct arizona_fll_cfg cfg; + if (already_enabled < 0) + return already_enabled; + /* * If we have both REFCLK and SYNCCLK then enable both, * otherwise apply the SYNCCLK settings to REFCLK. @@ -1766,7 +1770,7 @@ static void arizona_enable_fll(struct arizona_fll *fll) ARIZONA_FLL1_SYNC_ENA, 0); } else { arizona_fll_err(fll, "No clocks provided\n"); - return; + return -EINVAL; } /* @@ -1781,7 +1785,7 @@ static void arizona_enable_fll(struct arizona_fll *fll) ARIZONA_FLL1_SYNC_BW, ARIZONA_FLL1_SYNC_BW); - if (!arizona_is_enabled_fll(fll)) + if (!already_enabled) pm_runtime_get(arizona->dev); /* Clear any pending completions */ @@ -1800,6 +1804,8 @@ static void arizona_enable_fll(struct arizona_fll *fll) msecs_to_jiffies(250)); if (ret == 0) arizona_fll_warn(fll, "Timed out waiting for lock\n"); + + return 0; } static void arizona_disable_fll(struct arizona_fll *fll) @@ -1821,7 +1827,7 @@ static void arizona_disable_fll(struct arizona_fll *fll) int arizona_set_fll_refclk(struct arizona_fll *fll, int source, unsigned int Fref, unsigned int Fout) { - int ret; + int ret = 0; if (fll->ref_src == source && fll->ref_freq == Fref) return 0; @@ -1836,17 +1842,17 @@ int arizona_set_fll_refclk(struct arizona_fll *fll, int source, fll->ref_freq = Fref; if (fll->fout && Fref > 0) { - arizona_enable_fll(fll); + ret = arizona_enable_fll(fll); } - return 0; + return ret; } EXPORT_SYMBOL_GPL(arizona_set_fll_refclk); int arizona_set_fll(struct arizona_fll *fll, int source, unsigned int Fref, unsigned int Fout) { - int ret; + int ret = 0; if (fll->sync_src == source && fll->sync_freq == Fref && fll->fout == Fout) @@ -1869,11 +1875,11 @@ int arizona_set_fll(struct arizona_fll *fll, int source, fll->fout = Fout; if (Fout) - arizona_enable_fll(fll); + ret = arizona_enable_fll(fll); else arizona_disable_fll(fll); - return 0; + return ret; } EXPORT_SYMBOL_GPL(arizona_set_fll); From 5e39a50bf8c644f2ef348037d3e3efdc03fa65b2 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 9 Jul 2014 17:41:48 +0100 Subject: [PATCH 12/16] ASoC: arizona: FLL freerun only required whilst disabling The FLL freerun is only required whilst we disable the FLL not the entire time the FLL is disabled. This patch moves the FLL freerun disable from the enable sequence to the disable sequence. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/arizona.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index a6f09bb0a2e9..7c362cb36b21 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -1791,8 +1791,6 @@ static int arizona_enable_fll(struct arizona_fll *fll) /* Clear any pending completions */ try_wait_for_completion(&fll->ok); - regmap_update_bits_async(arizona->regmap, fll->base + 1, - ARIZONA_FLL1_FREERUN, 0); regmap_update_bits_async(arizona->regmap, fll->base + 1, ARIZONA_FLL1_ENA, ARIZONA_FLL1_ENA); if (use_sync) @@ -1819,6 +1817,8 @@ static void arizona_disable_fll(struct arizona_fll *fll) ARIZONA_FLL1_ENA, 0, &change); regmap_update_bits(arizona->regmap, fll->base + 0x11, ARIZONA_FLL1_SYNC_ENA, 0); + regmap_update_bits_async(arizona->regmap, fll->base + 1, + ARIZONA_FLL1_FREERUN, 0); if (change) pm_runtime_put_autosuspend(arizona->dev); From c8badda8eea6e9d142c8794519f6a57aa3ea3123 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 9 Jul 2014 17:41:49 +0100 Subject: [PATCH 13/16] ASoC: arizona: Update handling for input change on an active FLL Currently, the driver places no restrictions on changes that can be applied to an active FLL. However, it is only possible to change the input for an active FLL, to change the output the FLL should be stopped and then recofigured. This patch disallows changes in output frequency and adds some additional handling to ensure the output remains consistent across an input transition. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/arizona.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 7c362cb36b21..be3657ac52a4 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -1500,6 +1500,12 @@ static int arizona_validate_fll(struct arizona_fll *fll, { unsigned int Fvco_min; + if (fll->fout && Fout != fll->fout) { + arizona_fll_err(fll, + "Can't change output on active FLL\n"); + return -EINVAL; + } + if (Fref / ARIZONA_FLL_MAX_REFDIV > ARIZONA_FLL_MAX_FREF) { arizona_fll_err(fll, "Can't scale %dMHz in to <=13.5MHz\n", @@ -1743,6 +1749,15 @@ static int arizona_enable_fll(struct arizona_fll *fll) if (already_enabled < 0) return already_enabled; + if (already_enabled) { + /* Facilitate smooth refclk across the transition */ + regmap_update_bits_async(fll->arizona->regmap, fll->base + 0x7, + ARIZONA_FLL1_GAIN_MASK, 0); + regmap_update_bits_async(fll->arizona->regmap, fll->base + 1, + ARIZONA_FLL1_FREERUN, + ARIZONA_FLL1_FREERUN); + } + /* * If we have both REFCLK and SYNCCLK then enable both, * otherwise apply the SYNCCLK settings to REFCLK. @@ -1798,6 +1813,10 @@ static int arizona_enable_fll(struct arizona_fll *fll) ARIZONA_FLL1_SYNC_ENA, ARIZONA_FLL1_SYNC_ENA); + if (already_enabled) + regmap_update_bits_async(arizona->regmap, fll->base + 1, + ARIZONA_FLL1_FREERUN, 0); + ret = wait_for_completion_timeout(&fll->ok, msecs_to_jiffies(250)); if (ret == 0) From bedd4b1993ecb7228f8e5d6673e25d4482bd302b Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 22 Jul 2014 11:42:06 +0100 Subject: [PATCH 14/16] ASoC: arizona: Disable AIF TX/RX before configuring it Changes to the AIF configuration registers only take effect when the AIF is disabled. If the configuration is being changed from the previous setup, temporarily disable the AIF. Signed-off-by: Dimitris Papastamos Signed-off-by: Richard Fitzgerald Signed-off-by: Mark Brown --- sound/soc/codecs/arizona.c | 87 ++++++++++++++++++++++++++++++-------- 1 file changed, 69 insertions(+), 18 deletions(-) diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index be3657ac52a4..9a730689a0ac 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -1209,6 +1209,27 @@ static int arizona_hw_params_rate(struct snd_pcm_substream *substream, return 0; } +static bool arizona_aif_cfg_changed(struct snd_soc_codec *codec, + int base, int bclk, int lrclk, int frame) +{ + int val; + + val = snd_soc_read(codec, base + ARIZONA_AIF_BCLK_CTRL); + if (bclk != (val & ARIZONA_AIF1_BCLK_FREQ_MASK)) + return true; + + val = snd_soc_read(codec, base + ARIZONA_AIF_TX_BCLK_RATE); + if (lrclk != (val & ARIZONA_AIF1TX_BCPF_MASK)) + return true; + + val = snd_soc_read(codec, base + ARIZONA_AIF_FRAME_CTRL_1); + if (frame != (val & (ARIZONA_AIF1TX_WL_MASK | + ARIZONA_AIF1TX_SLOT_LEN_MASK))) + return true; + + return false; +} + static int arizona_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -1224,6 +1245,8 @@ static int arizona_hw_params(struct snd_pcm_substream *substream, int tdm_width = arizona->tdm_width[dai->id - 1]; int tdm_slots = arizona->tdm_slots[dai->id - 1]; int bclk, lrclk, wl, frame, bclk_target; + bool reconfig; + unsigned int aif_tx_state, aif_rx_state; if (params_rate(params) % 8000) rates = &arizona_44k1_bclk_rates[0]; @@ -1274,28 +1297,56 @@ static int arizona_hw_params(struct snd_pcm_substream *substream, wl = snd_pcm_format_width(params_format(params)); frame = wl << ARIZONA_AIF1TX_WL_SHIFT | wl; + reconfig = arizona_aif_cfg_changed(codec, base, bclk, lrclk, frame); + + if (reconfig) { + /* Save AIF TX/RX state */ + aif_tx_state = snd_soc_read(codec, + base + ARIZONA_AIF_TX_ENABLES); + aif_rx_state = snd_soc_read(codec, + base + ARIZONA_AIF_RX_ENABLES); + /* Disable AIF TX/RX before reconfiguring it */ + regmap_update_bits_async(arizona->regmap, + base + ARIZONA_AIF_TX_ENABLES, 0xff, 0x0); + regmap_update_bits(arizona->regmap, + base + ARIZONA_AIF_RX_ENABLES, 0xff, 0x0); + } + ret = arizona_hw_params_rate(substream, params, dai); if (ret != 0) - return ret; + goto restore_aif; - regmap_update_bits_async(arizona->regmap, - base + ARIZONA_AIF_BCLK_CTRL, - ARIZONA_AIF1_BCLK_FREQ_MASK, bclk); - regmap_update_bits_async(arizona->regmap, - base + ARIZONA_AIF_TX_BCLK_RATE, - ARIZONA_AIF1TX_BCPF_MASK, lrclk); - regmap_update_bits_async(arizona->regmap, - base + ARIZONA_AIF_RX_BCLK_RATE, - ARIZONA_AIF1RX_BCPF_MASK, lrclk); - regmap_update_bits_async(arizona->regmap, - base + ARIZONA_AIF_FRAME_CTRL_1, - ARIZONA_AIF1TX_WL_MASK | - ARIZONA_AIF1TX_SLOT_LEN_MASK, frame); - regmap_update_bits(arizona->regmap, base + ARIZONA_AIF_FRAME_CTRL_2, - ARIZONA_AIF1RX_WL_MASK | - ARIZONA_AIF1RX_SLOT_LEN_MASK, frame); + if (reconfig) { + regmap_update_bits_async(arizona->regmap, + base + ARIZONA_AIF_BCLK_CTRL, + ARIZONA_AIF1_BCLK_FREQ_MASK, bclk); + regmap_update_bits_async(arizona->regmap, + base + ARIZONA_AIF_TX_BCLK_RATE, + ARIZONA_AIF1TX_BCPF_MASK, lrclk); + regmap_update_bits_async(arizona->regmap, + base + ARIZONA_AIF_RX_BCLK_RATE, + ARIZONA_AIF1RX_BCPF_MASK, lrclk); + regmap_update_bits_async(arizona->regmap, + base + ARIZONA_AIF_FRAME_CTRL_1, + ARIZONA_AIF1TX_WL_MASK | + ARIZONA_AIF1TX_SLOT_LEN_MASK, frame); + regmap_update_bits(arizona->regmap, + base + ARIZONA_AIF_FRAME_CTRL_2, + ARIZONA_AIF1RX_WL_MASK | + ARIZONA_AIF1RX_SLOT_LEN_MASK, frame); + } - return 0; +restore_aif: + if (reconfig) { + /* Restore AIF TX/RX state */ + regmap_update_bits_async(arizona->regmap, + base + ARIZONA_AIF_TX_ENABLES, + 0xff, aif_tx_state); + regmap_update_bits(arizona->regmap, + base + ARIZONA_AIF_RX_ENABLES, + 0xff, aif_rx_state); + } + return ret; } static const char *arizona_dai_clk_str(int clk_id) From 27204ca8b981160c3b65cdc226c9070f76cbbd36 Mon Sep 17 00:00:00 2001 From: Kiran Padwal Date: Mon, 28 Jul 2014 11:41:04 +0530 Subject: [PATCH 15/16] ASoC: ak4642: Make of_device_id array const Make of_device_id array const, because all OF functions handle it as const Signed-off-by: Kiran Padwal Signed-off-by: Mark Brown --- sound/soc/codecs/ak4642.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 3ba4c0f11418..041712592e29 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -547,7 +547,7 @@ static const struct ak4642_drvdata ak4648_drvdata = { .extended_frequencies = 1, }; -static struct of_device_id ak4642_of_match[]; +static const struct of_device_id ak4642_of_match[]; static int ak4642_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -593,7 +593,7 @@ static int ak4642_i2c_remove(struct i2c_client *client) return 0; } -static struct of_device_id ak4642_of_match[] = { +static const struct of_device_id ak4642_of_match[] = { { .compatible = "asahi-kasei,ak4642", .data = &ak4642_drvdata}, { .compatible = "asahi-kasei,ak4643", .data = &ak4643_drvdata}, { .compatible = "asahi-kasei,ak4648", .data = &ak4648_drvdata}, From d8df26bb57d2a86365de46a5421b97417401e39a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 1 Aug 2014 17:19:00 +0100 Subject: [PATCH 16/16] ASoC: adau1977: Fix truncation warning on 64 bit architectures Negating ADAU1977_BLOCK_POWER_SAI_LDO_EN creates an unsigned long constant with all bits set which on 64 bit architectures needs to be truncated to an unsigned int, generating a warning. Add an explicit cast since we know this is OK. Signed-off-by: Mark Brown Acked-by: Lars-Peter Clausen --- sound/soc/codecs/adau1977.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/adau1977.c b/sound/soc/codecs/adau1977.c index fd55da7cb9d4..70ab35744aba 100644 --- a/sound/soc/codecs/adau1977.c +++ b/sound/soc/codecs/adau1977.c @@ -968,7 +968,7 @@ int adau1977_probe(struct device *dev, struct regmap *regmap, if (adau1977->dvdd_reg) power_off_mask = ~0; else - power_off_mask = ~ADAU1977_BLOCK_POWER_SAI_LDO_EN; + power_off_mask = (unsigned int)~ADAU1977_BLOCK_POWER_SAI_LDO_EN; ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_BLOCK_POWER_SAI, power_off_mask, 0x00);