// SPDX-License-Identifier: GPL-2.0-or-later /* * Codec driver for ST STA350 2.1-channel high-efficiency digital audio system * * Copyright: 2014 Raumfeld GmbH * Author: Sven Brandau <info@brandau.biz> * * based on code from: * Raumfeld GmbH * Johannes Stezenbach <js@sig21.net> * Wolfson Microelectronics PLC. * Mark Brown <broonie@opensource.wolfsonmicro.com> * Freescale Semiconductor, Inc. * Timur Tabi <timur@freescale.com>
*/
/* * byte array controls for setting biquad, mixer, scaling coefficients; * for biquads all five coefficients need to be set in one go, * mixer and pre/postscale coefs can be set individually; * each coef is 24bit, the bytes are ordered in the same way * as given in the STA350 data sheet (big endian; b1, b2, a1, a2, b0)
*/
staticint sta350_coefficient_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct sta350_priv *sta350 = snd_soc_component_get_drvdata(component); int numcoef = kcontrol->private_value >> 16; int index = kcontrol->private_value & 0xffff; unsignedint cfud, val; int i, ret = 0;
mutex_lock(&sta350->coeff_lock);
/* preserve reserved bits in STA350_CFUD */
regmap_read(sta350->regmap, STA350_CFUD, &cfud);
cfud &= 0xf0; /* * chip documentation does not say if the bits are self clearing, * so do it explicitly
*/
regmap_write(sta350->regmap, STA350_CFUD, cfud);
for (i = 0; i < 3 * numcoef; i++) {
regmap_read(sta350->regmap, STA350_B1CF1 + i, &val);
ucontrol->value.bytes.data[i] = val;
}
exit_unlock:
mutex_unlock(&sta350->coeff_lock);
return ret;
}
staticint sta350_coefficient_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); struct sta350_priv *sta350 = snd_soc_component_get_drvdata(component); int numcoef = kcontrol->private_value >> 16; int index = kcontrol->private_value & 0xffff; unsignedint cfud; int i;
/* preserve reserved bits in STA350_CFUD */
regmap_read(sta350->regmap, STA350_CFUD, &cfud);
cfud &= 0xf0; /* * chip documentation does not say if the bits are self clearing, * so do it explicitly
*/
regmap_write(sta350->regmap, STA350_CFUD, cfud);
regmap_write(sta350->regmap, STA350_CFADDR2, index); for (i = 0; i < numcoef && (index + i < STA350_COEF_COUNT); i++)
sta350->coef_shadow[index + i] =
(ucontrol->value.bytes.data[3 * i] << 16)
| (ucontrol->value.bytes.data[3 * i + 1] << 8)
| (ucontrol->value.bytes.data[3 * i + 2]); for (i = 0; i < 3 * numcoef; i++)
regmap_write(sta350->regmap, STA350_B1CF1 + i,
ucontrol->value.bytes.data[i]); if (numcoef == 1)
regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x01); elseif (numcoef == 5)
regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x02); else return -EINVAL;
/** * sta350_set_dai_sysclk - configure MCLK * @codec_dai: the codec DAI * @clk_id: the clock ID (ignored) * @freq: the MCLK input frequency * @dir: the clock direction (ignored) * * The value of MCLK is used to determine which sample rates are supported * by the STA350, based on the mcs_ratio_table. * * This function must be called by the machine driver's 'startup' function, * otherwise the list of supported sample rates will not be available in * time for ALSA.
*/ staticint sta350_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, unsignedint freq, int dir)
{ struct snd_soc_component *component = codec_dai->component; struct sta350_priv *sta350 = snd_soc_component_get_drvdata(component);
/** * sta350_set_dai_fmt - configure the codec for the selected audio format * @codec_dai: the codec DAI * @fmt: a SND_SOC_DAIFMT_x value indicating the data format * * This function takes a bitmask of SND_SOC_DAIFMT_x bits and programs the * codec accordingly.
*/ staticint sta350_set_dai_fmt(struct snd_soc_dai *codec_dai, unsignedint fmt)
{ struct snd_soc_component *component = codec_dai->component; struct sta350_priv *sta350 = snd_soc_component_get_drvdata(component); unsignedint confb = 0;
/** * sta350_hw_params - program the STA350 with the given hardware parameters. * @substream: the audio stream * @params: the hardware parameters to set * @dai: the SOC DAI (ignored) * * This function programs the hardware with the values provided. * Specifically, the sample rate and the data format.
*/ staticint sta350_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{ struct snd_soc_component *component = dai->component; struct sta350_priv *sta350 = snd_soc_component_get_drvdata(component); int i, mcs = -EINVAL, ir = -EINVAL; unsignedint confa, confb; unsignedint rate, ratio; int ret;
if (!sta350->mclk) {
dev_err(component->dev, "sta350->mclk is unset. Unable to determine ratio\n"); return -EIO;
}
switch (params_width(params)) { case 24:
dev_dbg(component->dev, "24bit\n");
fallthrough; case 32:
dev_dbg(component->dev, "24bit or 32bit\n"); switch (sta350->format) { case SND_SOC_DAIFMT_I2S:
confb |= 0x0; break; case SND_SOC_DAIFMT_LEFT_J:
confb |= 0x1; break; case SND_SOC_DAIFMT_RIGHT_J:
confb |= 0x2; break;
}
break; case 20:
dev_dbg(component->dev, "20bit\n"); switch (sta350->format) { case SND_SOC_DAIFMT_I2S:
confb |= 0x4; break; case SND_SOC_DAIFMT_LEFT_J:
confb |= 0x5; break; case SND_SOC_DAIFMT_RIGHT_J:
confb |= 0x6; break;
}
break; case 18:
dev_dbg(component->dev, "18bit\n"); switch (sta350->format) { case SND_SOC_DAIFMT_I2S:
confb |= 0x8; break; case SND_SOC_DAIFMT_LEFT_J:
confb |= 0x9; break; case SND_SOC_DAIFMT_RIGHT_J:
confb |= 0xa; break;
}
break; case 16:
dev_dbg(component->dev, "16bit\n"); switch (sta350->format) { case SND_SOC_DAIFMT_I2S:
confb |= 0x0; break; case SND_SOC_DAIFMT_LEFT_J:
confb |= 0xd; break; case SND_SOC_DAIFMT_RIGHT_J:
confb |= 0xe; break;
}
break; default: return -EINVAL;
}
ret = regmap_update_bits(sta350->regmap, STA350_CONFA,
STA350_CONFA_MCS_MASK | STA350_CONFA_IR_MASK,
confa); if (ret < 0) return ret;
ret = regmap_update_bits(sta350->regmap, STA350_CONFB,
STA350_CONFB_SAI_MASK | STA350_CONFB_SAIFB,
confb); if (ret < 0) return ret;
return 0;
}
staticint sta350_startup_sequence(struct sta350_priv *sta350)
{ if (sta350->gpiod_power_down)
gpiod_set_value(sta350->gpiod_power_down, 1);
if (sta350->gpiod_nreset) {
gpiod_set_value(sta350->gpiod_nreset, 0);
mdelay(1);
gpiod_set_value(sta350->gpiod_nreset, 1);
mdelay(1);
}
return 0;
}
/** * sta350_set_bias_level - DAPM callback * @component: the component device * @level: DAPM power level * * This is called by ALSA to put the component into low power mode * or to wake it up. If the component is powered off completely * all registers must be restored after power on.
*/ staticint sta350_set_bias_level(struct snd_soc_component *component, enum snd_soc_bias_level level)
{ struct sta350_priv *sta350 = snd_soc_component_get_drvdata(component); int ret;
case SND_SOC_BIAS_PREPARE: /* Full power on */
regmap_update_bits(sta350->regmap, STA350_CONFF,
STA350_CONFF_PWDN | STA350_CONFF_EAPD,
STA350_CONFF_PWDN | STA350_CONFF_EAPD); break;
case SND_SOC_BIAS_STANDBY: if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
ret = regulator_bulk_enable(
ARRAY_SIZE(sta350->supplies),
sta350->supplies); if (ret < 0) {
dev_err(component->dev, "Failed to enable supplies: %d\n",
ret); return ret;
}
sta350_startup_sequence(sta350);
sta350_cache_sync(component);
}
/* Power down */
regmap_update_bits(sta350->regmap, STA350_CONFF,
STA350_CONFF_PWDN | STA350_CONFF_EAPD,
0);
break;
case SND_SOC_BIAS_OFF: /* The chip runs through the power down sequence for us */
regmap_update_bits(sta350->regmap, STA350_CONFF,
STA350_CONFF_PWDN | STA350_CONFF_EAPD, 0);
/* power down: low */ if (sta350->gpiod_power_down)
gpiod_set_value(sta350->gpiod_power_down, 0);
if (sta350->gpiod_nreset)
gpiod_set_value(sta350->gpiod_nreset, 0);
/* initialize coefficient shadow RAM with reset values */ for (i = 4; i <= 49; i += 5)
sta350->coef_shadow[i] = 0x400000; for (i = 50; i <= 54; i++)
sta350->coef_shadow[i] = 0x7fffff;
sta350->coef_shadow[55] = 0x5a9df7;
sta350->coef_shadow[56] = 0x7fffff;
sta350->coef_shadow[59] = 0x7fffff;
sta350->coef_shadow[60] = 0x400000;
sta350->coef_shadow[61] = 0x400000;
snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY); /* Bias level configuration will have done an extra enable */
regulator_bulk_disable(ARRAY_SIZE(sta350->supplies), sta350->supplies);
pdata->ffx_power_output_mode = STA350_FFX_PM_VARIABLE_DROP_COMP; if (!of_property_read_string(np, "st,ffx-power-output-mode",
&ffx_power_mode)) { int i, mode = -EINVAL;
for (i = 0; i < ARRAY_SIZE(sta350_ffx_modes); i++) if (!strcasecmp(ffx_power_mode, sta350_ffx_modes[i]))
mode = i;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.