if (pll_id < 0 || chip->num_plls <= pll_id) {
dev_err(dev, "PLL(%d) is not supported\n", pll_id); returnfalse;
}
return chip->plls[pll_id].enable;
}
/** * find_volume - find volume supported HW port by HW port number * @chip: the AIO chip pointer * @oport_hw: HW port number, one of AUD_HW_XXXX * * Find AIO device from device list by HW port number. Volume feature is * available only in Output and PCM ports, this limitation comes from HW * specifications. * * Return: The pointer of AIO substream if successful, otherwise NULL on error.
*/ staticstruct uniphier_aio_sub *find_volume(struct uniphier_aio_chip *chip, int oport_hw)
{ int i;
for (i = 0; i < chip->num_aios; i++) { struct uniphier_aio_sub *sub = &chip->aios[i].sub[0];
if (!sub->swm) continue;
if (sub->swm->oport.hw == oport_hw) return sub;
}
return NULL;
}
staticbool match_spec(conststruct uniphier_aio_spec *spec, constchar *name, int dir)
{ if (dir == SNDRV_PCM_STREAM_PLAYBACK &&
spec->swm.dir != PORT_DIR_OUTPUT) { returnfalse;
}
if (spec->name && strcmp(spec->name, name) == 0) returntrue;
if (spec->gname && strcmp(spec->gname, name) == 0) returntrue;
returnfalse;
}
/** * find_spec - find HW specification info by name * @aio: the AIO device pointer * @name: name of device * @direction: the direction of substream, SNDRV_PCM_STREAM_* * * Find hardware specification information from list by device name. This * information is used for telling the difference of SoCs to driver. * * Specification list is array of 'struct uniphier_aio_spec' which is defined * in each drivers (see: aio-i2s.c). * * Return: The pointer of hardware specification of AIO if successful, * otherwise NULL on error.
*/ staticconststruct uniphier_aio_spec *find_spec(struct uniphier_aio *aio, constchar *name, int direction)
{ conststruct uniphier_aio_chip_spec *chip_spec = aio->chip->chip_spec; int i;
for (i = 0; i < chip_spec->num_specs; i++) { conststruct uniphier_aio_spec *spec = &chip_spec->specs[i];
if (match_spec(spec, name, direction)) return spec;
}
return NULL;
}
/** * find_divider - find clock divider by frequency * @aio: the AIO device pointer * @pll_id: PLL ID, should be AUD_PLL_XX * @freq: required frequency * * Find suitable clock divider by frequency. * * Return: The ID of PLL if successful, otherwise negative error value.
*/ staticint find_divider(struct uniphier_aio *aio, int pll_id, unsignedint freq)
{ struct uniphier_aio_pll *pll; staticconstint mul[] = { 1, 1, 1, 2, }; staticconstint div[] = { 2, 3, 1, 3, }; int i;
if (!is_valid_pll(aio->chip, pll_id)) return -EINVAL;
pll = &aio->chip->plls[pll_id]; for (i = 0; i < ARRAY_SIZE(mul); i++) if (pll->freq * mul[i] / div[i] == freq) return i;
return -ENOTSUPP;
}
staticint uniphier_aio_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsignedint freq, int dir)
{ struct uniphier_aio *aio = uniphier_priv(dai); struct device *dev = &aio->chip->pdev->dev; bool pll_auto = false; int pll_id, div_id;
switch (clk_id) { case AUD_CLK_IO: return -ENOTSUPP; case AUD_CLK_A1:
pll_id = AUD_PLL_A1; break; case AUD_CLK_F1:
pll_id = AUD_PLL_F1; break; case AUD_CLK_A2:
pll_id = AUD_PLL_A2; break; case AUD_CLK_F2:
pll_id = AUD_PLL_F2; break; case AUD_CLK_A:
pll_id = AUD_PLL_A1;
pll_auto = true; break; case AUD_CLK_F:
pll_id = AUD_PLL_F1;
pll_auto = true; break; case AUD_CLK_APLL:
pll_id = AUD_PLL_APLL; break; case AUD_CLK_RX0:
pll_id = AUD_PLL_RX0; break; case AUD_CLK_USB0:
pll_id = AUD_PLL_USB0; break; case AUD_CLK_HSC0:
pll_id = AUD_PLL_HSC0; break; default:
dev_err(dev, "Sysclk(%d) is not supported\n", clk_id); return -EINVAL;
}
if (pll_auto) { for (pll_id = 0; pll_id < aio->chip->num_plls; pll_id++) {
div_id = find_divider(aio, pll_id, freq); if (div_id >= 0) {
aio->plldiv = div_id; break;
}
} if (pll_id == aio->chip->num_plls) {
dev_err(dev, "Sysclk frequency is not supported(%d)\n",
freq); return -EINVAL;
}
}
switch (params_rate(params)) { case 48000: case 32000: case 24000:
freq = 12288000; break; case 44100: case 22050:
freq = 11289600; break; default:
dev_err(dev, "Rate is not supported(%d)\n",
params_rate(params)); return -EINVAL;
}
ret = snd_soc_dai_set_sysclk(dai, AUD_CLK_A,
freq, SND_SOC_CLOCK_OUT); if (ret) return ret;
ret = aio_port_set_param(sub, sub->pass_through, &sub->params); if (ret) return ret;
ret = aio_src_set_param(sub, &sub->params); if (ret) return ret;
aio_port_set_enable(sub, 1);
ret = aio_if_set_param(sub, sub->pass_through); if (ret) return ret;
if (sub->swm->type == PORT_TYPE_CONV) {
ret = aio_srcif_set_param(sub); if (ret) return ret;
ret = aio_srcch_set_param(sub); if (ret) return ret;
aio_srcch_set_enable(sub, 1);
}
err_out_reset: if (!aio->chip->num_wup_aios)
reset_control_assert(aio->chip->rst);
err_out_clock: if (!aio->chip->num_wup_aios)
clk_disable_unprepare(aio->chip->clk);
return ret;
}
staticint uniphier_aio_resume(struct snd_soc_component *component)
{ struct snd_soc_dai *dai; int ret = 0;
for_each_component_dais(component, dai)
ret |= uniphier_aio_dai_resume(dai); return ret;
}
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.