/* We should at least have a slot for a valid interface */ if (!tx_slots && !rx_slots) {
dev_err(dai->dev, "interface has no slot\n"); return -EINVAL;
}
iface->slots = slots;
switch (slot_width) { case 0:
slot_width = 32;
fallthrough; case 32:
fmt |= SNDRV_PCM_FMTBIT_S32_LE;
fallthrough; case 24:
fmt |= SNDRV_PCM_FMTBIT_S24_LE;
fmt |= SNDRV_PCM_FMTBIT_S20_LE;
fallthrough; case 16:
fmt |= SNDRV_PCM_FMTBIT_S16_LE;
fallthrough; case 8:
fmt |= SNDRV_PCM_FMTBIT_S8; break; default:
dev_err(dai->dev, "unsupported slot width: %d\n", slot_width); return -EINVAL;
}
iface->slot_width = slot_width;
/* Amend the dai driver and let dpcm merge do its job */ if (tx) {
tx->mask = tx_mask;
dai->driver->playback.channels_max = tx_slots;
dai->driver->playback.formats = fmt;
}
staticint axg_tdm_iface_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsignedint freq, int dir)
{ struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); int ret = -ENOTSUPP;
if (dir == SND_SOC_CLOCK_OUT && clk_id == 0) { if (!iface->mclk) {
dev_warn(dai->dev, "master clock not provided\n");
} else {
ret = clk_set_rate(iface->mclk, freq); if (!ret)
iface->mclk_rate = freq;
}
}
switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { case SND_SOC_DAIFMT_BP_FP: if (!iface->mclk) {
dev_err(dai->dev, "cpu clock master: mclk missing\n"); return -ENODEV;
} break;
case SND_SOC_DAIFMT_BC_FC: break;
case SND_SOC_DAIFMT_BP_FC: case SND_SOC_DAIFMT_BC_FP:
dev_err(dai->dev, "only BP_FP and BC_FC are supported\n");
fallthrough; default: return -EINVAL;
}
if (!axg_tdm_slots_total(ts->mask)) {
dev_err(dai->dev, "interface has not slots\n"); return -EINVAL;
}
if (snd_soc_component_active(dai->component)) { /* Apply component wide rate symmetry */
ret = snd_pcm_hw_constraint_single(substream->runtime,
SNDRV_PCM_HW_PARAM_RATE,
iface->rate);
} else { /* Limit rate according to the slot number and width */ unsignedint max_rate =
MAX_SCLK / (iface->slots * iface->slot_width);
ret = snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_RATE,
0, max_rate);
}
if (ret < 0)
dev_err(dai->dev, "can't set iface rate constraint\n"); else
ret = 0;
/* Save rate and sample_bits for component symmetry */
iface->rate = params_rate(params);
/* Make sure this interface can cope with the stream */ if (axg_tdm_slots_total(ts->mask) < channels) {
dev_err(dai->dev, "not enough slots for channels\n"); return -EINVAL;
}
if (iface->slot_width < width) {
dev_err(dai->dev, "incompatible slots width for stream\n"); return -EINVAL;
}
/* Save the parameter for tdmout/tdmin widgets */
ts->physical_width = params_physical_width(params);
ts->width = params_width(params);
ts->channels = params_channels(params);
ret = clk_set_rate(iface->lrclk, params_rate(params)); if (ret) {
dev_err(dai->dev, "setting sample clock failed: %d\n", ret); return ret;
}
switch (iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: case SND_SOC_DAIFMT_LEFT_J: case SND_SOC_DAIFMT_RIGHT_J: /* 50% duty cycle ratio */
ratio_num = 1; break;
case SND_SOC_DAIFMT_DSP_A: case SND_SOC_DAIFMT_DSP_B: /* * A zero duty cycle ratio will result in setting the mininum * ratio possible which, for this clock, is 1 cycle of the * parent bclk clock high and the rest low, This is exactly * what we want here.
*/
ratio_num = 0; break;
default: return -EINVAL;
}
ret = clk_set_duty_cycle(iface->lrclk, ratio_num, 2); if (ret) {
dev_err(dai->dev, "setting sample clock duty cycle failed: %d\n", ret); return ret;
}
/* Set sample clock inversion */
ret = clk_set_phase(iface->lrclk,
axg_tdm_lrclk_invert(iface->fmt) ? 180 : 0); if (ret) {
dev_err(dai->dev, "setting sample clock phase failed: %d\n", ret); return ret;
}
if (!iface->mclk_rate) { /* If no specific mclk is requested, default to bit clock * 2 */
clk_set_rate(iface->mclk, 2 * srate);
} else { /* Check if we can actually get the bit clock from mclk */ if (iface->mclk_rate % srate) {
dev_err(dai->dev, "can't derive sclk %lu from mclk %lu\n",
srate, iface->mclk_rate); return -EINVAL;
}
}
ret = clk_set_rate(iface->sclk, srate); if (ret) {
dev_err(dai->dev, "setting bit clock failed: %d\n", ret); return ret;
}
/* Set the bit clock inversion */
ret = clk_set_phase(iface->sclk,
axg_tdm_sclk_invert(iface->fmt) ? 0 : 180); if (ret) {
dev_err(dai->dev, "setting bit clock phase failed: %d\n", ret); return ret;
}
switch (iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: case SND_SOC_DAIFMT_LEFT_J: case SND_SOC_DAIFMT_RIGHT_J: if (iface->slots > 2) {
dev_err(dai->dev, "bad slot number for format: %d\n",
iface->slots); return -EINVAL;
} break;
case SND_SOC_DAIFMT_DSP_A: case SND_SOC_DAIFMT_DSP_B: break;
default:
dev_err(dai->dev, "unsupported dai format\n"); return -EINVAL;
}
ret = axg_tdm_iface_set_stream(substream, params, dai); if (ret) return ret;
if ((iface->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) ==
SND_SOC_DAIFMT_BP_FP) {
ret = axg_tdm_iface_set_sclk(dai, params); if (ret) return ret;
ret = axg_tdm_iface_set_lrclk(dai, params); if (ret) return ret;
}
ret = axg_tdm_stream_set_cont_clocks(ts, iface->fmt); if (ret)
dev_err(dai->dev, "failed to apply continuous clock setting\n");
switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
axg_tdm_stream_start(ts); break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_STOP:
axg_tdm_stream_stop(ts); break; default: return -EINVAL;
}
return 0;
}
staticint axg_tdm_iface_remove_dai(struct snd_soc_dai *dai)
{ int stream;
/* * Duplicate dai driver: depending on the slot masks configuration * We'll change the number of channel provided by DAI stream, so dpcm * channel merge can be done properly
*/
dai_drv = devm_kmemdup_array(dev, axg_tdm_iface_dai_drv, ARRAY_SIZE(axg_tdm_iface_dai_drv), sizeof(axg_tdm_iface_dai_drv[0]), GFP_KERNEL); if (!dai_drv) return -ENOMEM;
/* Bit clock provided on the pad */
iface->sclk = devm_clk_get(dev, "sclk"); if (IS_ERR(iface->sclk)) return dev_err_probe(dev, PTR_ERR(iface->sclk), "failed to get sclk\n");
/* Sample clock provided on the pad */
iface->lrclk = devm_clk_get(dev, "lrclk"); if (IS_ERR(iface->lrclk)) return dev_err_probe(dev, PTR_ERR(iface->lrclk), "failed to get lrclk\n");
/* * mclk maybe be missing when the cpu dai is in slave mode and * the codec does not require it to provide a master clock. * At this point, ignore the error if mclk is missing. We'll * throw an error if the cpu dai is master and mclk is missing
*/
iface->mclk = devm_clk_get_optional(dev, "mclk"); if (IS_ERR(iface->mclk)) return dev_err_probe(dev, PTR_ERR(iface->mclk), "failed to get mclk\n");
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.