diff options
Diffstat (limited to 'sound/soc/codecs/pcm512x.c')
| -rw-r--r-- | sound/soc/codecs/pcm512x.c | 226 |
1 files changed, 146 insertions, 80 deletions
diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index 4cbef9affffd..a70e8ea166dc 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -48,6 +48,7 @@ struct pcm512x_priv { int mute; struct mutex mutex; unsigned int bclk_ratio; + int force_pll_on; }; /* @@ -116,6 +117,8 @@ static const struct reg_default pcm512x_reg_defaults[] = { { PCM512x_FS_SPEED_MODE, 0x00 }, { PCM512x_IDAC_1, 0x01 }, { PCM512x_IDAC_2, 0x00 }, + { PCM512x_I2S_1, 0x02 }, + { PCM512x_I2S_2, 0x00 }, }; static bool pcm512x_readable(struct device *dev, unsigned int reg) @@ -221,7 +224,7 @@ static bool pcm512x_volatile(struct device *dev, unsigned int reg) static int pcm512x_overclock_pll_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); ucontrol->value.integer.value[0] = pcm512x->overclock_pll; @@ -231,10 +234,11 @@ static int pcm512x_overclock_pll_get(struct snd_kcontrol *kcontrol, static int pcm512x_overclock_pll_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol); struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); - switch (snd_soc_component_get_bias_level(component)) { + switch (snd_soc_dapm_get_bias_level(dapm)) { case SND_SOC_BIAS_OFF: case SND_SOC_BIAS_STANDBY: break; @@ -249,7 +253,7 @@ static int pcm512x_overclock_pll_put(struct snd_kcontrol *kcontrol, static int pcm512x_overclock_dsp_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); ucontrol->value.integer.value[0] = pcm512x->overclock_dsp; @@ -259,10 +263,11 @@ static int pcm512x_overclock_dsp_get(struct snd_kcontrol *kcontrol, static int pcm512x_overclock_dsp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol); struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); - switch (snd_soc_component_get_bias_level(component)) { + switch (snd_soc_dapm_get_bias_level(dapm)) { case SND_SOC_BIAS_OFF: case SND_SOC_BIAS_STANDBY: break; @@ -277,7 +282,7 @@ static int pcm512x_overclock_dsp_put(struct snd_kcontrol *kcontrol, static int pcm512x_overclock_dac_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); ucontrol->value.integer.value[0] = pcm512x->overclock_dac; @@ -287,10 +292,11 @@ static int pcm512x_overclock_dac_get(struct snd_kcontrol *kcontrol, static int pcm512x_overclock_dac_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol); struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); - switch (snd_soc_component_get_bias_level(component)) { + switch (snd_soc_dapm_get_bias_level(dapm)) { case SND_SOC_BIAS_OFF: case SND_SOC_BIAS_STANDBY: break; @@ -390,7 +396,7 @@ static int pcm512x_update_mute(struct pcm512x_priv *pcm512x) static int pcm512x_digital_playback_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); mutex_lock(&pcm512x->mutex); @@ -404,7 +410,7 @@ static int pcm512x_digital_playback_switch_get(struct snd_kcontrol *kcontrol, static int pcm512x_digital_playback_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); int ret, changed = 0; @@ -650,12 +656,12 @@ static int pcm512x_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_component *component = dai->component; struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); - switch (pcm512x->fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: - case SND_SOC_DAIFMT_CBM_CFS: + switch (pcm512x->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + case SND_SOC_DAIFMT_CBP_CFC: return pcm512x_dai_startup_master(substream, dai); - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: return pcm512x_dai_startup_slave(substream, dai); default: @@ -1168,8 +1174,6 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream, struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); int alen; int gpio; - int clock_output; - int master_mode; int ret; dev_dbg(component->dev, "hw_params %u Hz, %u channels\n", @@ -1195,19 +1199,15 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - switch (pcm512x->fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: - ret = regmap_update_bits(pcm512x->regmap, - PCM512x_BCLK_LRCLK_CFG, - PCM512x_BCKP - | PCM512x_BCKO | PCM512x_LRKO, - 0); - if (ret != 0) { - dev_err(component->dev, - "Failed to enable slave mode: %d\n", ret); - return ret; - } + ret = regmap_update_bits(pcm512x->regmap, PCM512x_I2S_1, + PCM512x_ALEN, alen); + if (ret != 0) { + dev_err(component->dev, "Failed to set frame size: %d\n", ret); + return ret; + } + if ((pcm512x->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) == + SND_SOC_DAIFMT_CBC_CFC) { ret = regmap_update_bits(pcm512x->regmap, PCM512x_ERROR_DETECT, PCM512x_DCAS, 0); if (ret != 0) { @@ -1216,24 +1216,7 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream, ret); return ret; } - return 0; - case SND_SOC_DAIFMT_CBM_CFM: - clock_output = PCM512x_BCKO | PCM512x_LRKO; - master_mode = PCM512x_RLRK | PCM512x_RBCK; - break; - case SND_SOC_DAIFMT_CBM_CFS: - clock_output = PCM512x_BCKO; - master_mode = PCM512x_RBCK; - break; - default: - return -EINVAL; - } - - ret = regmap_update_bits(pcm512x->regmap, PCM512x_I2S_1, - PCM512x_ALEN, alen); - if (ret != 0) { - dev_err(component->dev, "Failed to set frame size: %d\n", ret); - return ret; + goto skip_pll; } if (pcm512x->pll_out) { @@ -1279,10 +1262,34 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream, return ret; } - ret = regmap_update_bits(pcm512x->regmap, PCM512x_PLL_EN, - PCM512x_PLLE, 0); + if (!pcm512x->force_pll_on) { + ret = regmap_update_bits(pcm512x->regmap, + PCM512x_PLL_EN, PCM512x_PLLE, 0); + } else { + /* provide minimum PLL config for TAS575x clocking + * and leave PLL enabled + */ + ret = regmap_write(pcm512x->regmap, + PCM512x_PLL_COEFF_0, 0x01); + if (ret != 0) { + dev_err(component->dev, + "Failed to set pll coefficient: %d\n", ret); + return ret; + } + ret = regmap_write(pcm512x->regmap, + PCM512x_PLL_COEFF_1, 0x04); + if (ret != 0) { + dev_err(component->dev, + "Failed to set pll coefficient: %d\n", ret); + return ret; + } + ret = regmap_write(pcm512x->regmap, + PCM512x_PLL_EN, 0x01); + dev_dbg(component->dev, "Enabling PLL for TAS575x\n"); + } + if (ret != 0) { - dev_err(component->dev, "Failed to disable pll: %d\n", ret); + dev_err(component->dev, "Failed to set pll mode: %d\n", ret); return ret; } } @@ -1316,25 +1323,7 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream, dev_err(component->dev, "Failed to enable pll: %d\n", ret); return ret; } - } - - ret = regmap_update_bits(pcm512x->regmap, PCM512x_BCLK_LRCLK_CFG, - PCM512x_BCKP | PCM512x_BCKO | PCM512x_LRKO, - clock_output); - if (ret != 0) { - dev_err(component->dev, "Failed to enable clock output: %d\n", ret); - return ret; - } - - ret = regmap_update_bits(pcm512x->regmap, PCM512x_MASTER_MODE, - PCM512x_RLRK | PCM512x_RBCK, - master_mode); - if (ret != 0) { - dev_err(component->dev, "Failed to enable master mode: %d\n", ret); - return ret; - } - if (pcm512x->pll_out) { gpio = PCM512x_G1OE << (pcm512x->pll_out - 1); ret = regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_EN, gpio, gpio); @@ -1368,6 +1357,7 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream, return ret; } +skip_pll: return 0; } @@ -1375,6 +1365,80 @@ static int pcm512x_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct snd_soc_component *component = dai->component; struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); + int afmt; + int offset = 0; + int clock_output; + int provider_mode; + int ret; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + clock_output = 0; + provider_mode = 0; + break; + case SND_SOC_DAIFMT_CBP_CFP: + clock_output = PCM512x_BCKO | PCM512x_LRKO; + provider_mode = PCM512x_RLRK | PCM512x_RBCK; + break; + case SND_SOC_DAIFMT_CBP_CFC: + clock_output = PCM512x_BCKO; + provider_mode = PCM512x_RBCK; + break; + default: + return -EINVAL; + } + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_BCLK_LRCLK_CFG, + PCM512x_BCKP | PCM512x_BCKO | PCM512x_LRKO, + clock_output); + if (ret != 0) { + dev_err(component->dev, "Failed to enable clock output: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_MASTER_MODE, + PCM512x_RLRK | PCM512x_RBCK, + provider_mode); + if (ret != 0) { + dev_err(component->dev, "Failed to enable provider mode: %d\n", ret); + return ret; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + afmt = PCM512x_AFMT_I2S; + break; + case SND_SOC_DAIFMT_RIGHT_J: + afmt = PCM512x_AFMT_RTJ; + break; + case SND_SOC_DAIFMT_LEFT_J: + afmt = PCM512x_AFMT_LTJ; + break; + case SND_SOC_DAIFMT_DSP_A: + offset = 1; + fallthrough; + case SND_SOC_DAIFMT_DSP_B: + afmt = PCM512x_AFMT_DSP; + break; + default: + dev_err(component->dev, "unsupported DAI format: 0x%x\n", + pcm512x->fmt); + return -EINVAL; + } + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_I2S_1, + PCM512x_AFMT, afmt); + if (ret != 0) { + dev_err(component->dev, "Failed to set data format: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_I2S_2, + 0xFF, offset); + if (ret != 0) { + dev_err(component->dev, "Failed to set data offset: %d\n", ret); + return ret; + } pcm512x->fmt = fmt; @@ -1394,7 +1458,7 @@ static int pcm512x_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) return 0; } -static int pcm512x_digital_mute(struct snd_soc_dai *dai, int mute) +static int pcm512x_mute(struct snd_soc_dai *dai, int mute, int direction) { struct snd_soc_component *component = dai->component; struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); @@ -1445,8 +1509,9 @@ static const struct snd_soc_dai_ops pcm512x_dai_ops = { .startup = pcm512x_dai_startup, .hw_params = pcm512x_hw_params, .set_fmt = pcm512x_set_fmt, - .digital_mute = pcm512x_digital_mute, + .mute_stream = pcm512x_mute, .set_bclk_ratio = pcm512x_set_bclk_ratio, + .no_capture_mute = 1, }; static struct snd_soc_dai_driver pcm512x_dai = { @@ -1475,7 +1540,6 @@ static const struct snd_soc_component_driver pcm512x_component_driver = { .num_dapm_routes = ARRAY_SIZE(pcm512x_dapm_routes), .use_pmdown_time = 1, .endianness = 1, - .non_legacy_dai_naming = 1, }; static const struct regmap_range_cfg pcm512x_range = { @@ -1598,7 +1662,7 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap) if (val > 6) { dev_err(dev, "Invalid pll-in\n"); ret = -EINVAL; - goto err_clk; + goto err_pm; } pcm512x->pll_in = val; } @@ -1607,7 +1671,7 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap) if (val > 6) { dev_err(dev, "Invalid pll-out\n"); ret = -EINVAL; - goto err_clk; + goto err_pm; } pcm512x->pll_out = val; } @@ -1616,13 +1680,18 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap) dev_err(dev, "Error: both pll-in and pll-out, or none\n"); ret = -EINVAL; - goto err_clk; + goto err_pm; } if (pcm512x->pll_in && pcm512x->pll_in == pcm512x->pll_out) { dev_err(dev, "Error: pll-in == pll-out\n"); ret = -EINVAL; - goto err_clk; + goto err_pm; } + + if (!strcmp(np->name, "tas5756") || + !strcmp(np->name, "tas5754")) + pcm512x->force_pll_on = 1; + dev_dbg(dev, "Device ID: %s\n", np->name); } #endif @@ -1659,7 +1728,6 @@ void pcm512x_remove(struct device *dev) } EXPORT_SYMBOL_GPL(pcm512x_remove); -#ifdef CONFIG_PM static int pcm512x_suspend(struct device *dev) { struct pcm512x_priv *pcm512x = dev_get_drvdata(dev); @@ -1721,12 +1789,10 @@ static int pcm512x_resume(struct device *dev) return 0; } -#endif -const struct dev_pm_ops pcm512x_pm_ops = { - SET_RUNTIME_PM_OPS(pcm512x_suspend, pcm512x_resume, NULL) +EXPORT_GPL_DEV_PM_OPS(pcm512x_pm_ops) = { + RUNTIME_PM_OPS(pcm512x_suspend, pcm512x_resume, NULL) }; -EXPORT_SYMBOL_GPL(pcm512x_pm_ops); MODULE_DESCRIPTION("ASoC PCM512x codec driver"); MODULE_AUTHOR("Mark Brown <broonie@kernel.org>"); |
