Add function to constrain rates

Merge series from Chancel Liu <chancel.liu@nxp.com>:

Platforms like i.MX93/91 only have one audio PLL. Some sample rates are
not supported. If the PLL source is used for 8kHz series rates, then
11kHz series rates can't be supported. Add common function to constrain
rates according to different clock sources.

In ASoC drivers switch to this new function.
This commit is contained in:
Mark Brown
2024-12-10 12:37:21 +00:00
6 changed files with 93 additions and 20 deletions

View File

@@ -35,6 +35,15 @@
#define MICFIL_AUDIO_PLL2 1
#define MICFIL_CLK_EXT3 2
static const unsigned int fsl_micfil_rates[] = {
8000, 11025, 16000, 22050, 32000, 44100, 48000,
};
static const struct snd_pcm_hw_constraint_list fsl_micfil_rate_constraints = {
.count = ARRAY_SIZE(fsl_micfil_rates),
.list = fsl_micfil_rates,
};
enum quality {
QUALITY_HIGH,
QUALITY_MEDIUM,
@@ -486,29 +495,12 @@ static int fsl_micfil_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai);
unsigned int rates[MICFIL_NUM_RATES] = {8000, 11025, 16000, 22050, 32000, 44100, 48000};
int i, j, k = 0;
u64 clk_rate;
if (!micfil) {
dev_err(dai->dev, "micfil dai priv_data not set\n");
return -EINVAL;
}
micfil->constraint_rates.list = micfil->constraint_rates_list;
micfil->constraint_rates.count = 0;
for (j = 0; j < MICFIL_NUM_RATES; j++) {
for (i = 0; i < MICFIL_CLK_SRC_NUM; i++) {
clk_rate = clk_get_rate(micfil->clk_src[i]);
if (clk_rate != 0 && do_div(clk_rate, rates[j]) == 0) {
micfil->constraint_rates_list[k++] = rates[j];
micfil->constraint_rates.count++;
break;
}
}
}
if (micfil->constraint_rates.count > 0)
snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
@@ -1239,6 +1231,13 @@ static int fsl_micfil_probe(struct platform_device *pdev)
if (IS_ERR(micfil->clk_src[MICFIL_CLK_EXT3]))
micfil->clk_src[MICFIL_CLK_EXT3] = NULL;
fsl_asoc_constrain_rates(&micfil->constraint_rates,
&fsl_micfil_rate_constraints,
micfil->clk_src[MICFIL_AUDIO_PLL1],
micfil->clk_src[MICFIL_AUDIO_PLL2],
micfil->clk_src[MICFIL_CLK_EXT3],
micfil->constraint_rates_list);
/* init regmap */
regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(regs))

View File

@@ -885,7 +885,7 @@ static int fsl_sai_startup(struct snd_pcm_substream *substream,
sai->dma_params_rx.maxburst);
ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, &fsl_sai_rate_constraints);
SNDRV_PCM_HW_PARAM_RATE, &sai->constraint_rates);
return ret;
}
@@ -1442,6 +1442,11 @@ static int fsl_sai_probe(struct platform_device *pdev)
fsl_asoc_get_pll_clocks(&pdev->dev, &sai->pll8k_clk,
&sai->pll11k_clk);
fsl_asoc_constrain_rates(&sai->constraint_rates,
&fsl_sai_rate_constraints,
sai->pll8k_clk, sai->pll11k_clk, NULL,
sai->constraint_rates_list);
/* Use Multi FIFO mode depending on the support from SDMA script */
ret = of_property_read_u32_array(np, "dmas", dmas, 4);
if (!sai->soc_data->use_edma && !ret && dmas[2] == IMX_DMATYPE_MULTI_SAI)

View File

@@ -9,6 +9,7 @@
#include <linux/dma/imx-dma.h>
#include <sound/dmaengine_pcm.h>
#define FAL_SAI_NUM_RATES 20
#define FSL_SAI_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE |\
@@ -309,6 +310,8 @@ struct fsl_sai {
struct pinctrl *pinctrl;
struct pinctrl_state *pins_state;
struct sdma_peripheral_config audio_config[2];
struct snd_pcm_hw_constraint_list constraint_rates;
unsigned int constraint_rates_list[FAL_SAI_NUM_RATES];
};
#define TX 1

View File

@@ -152,6 +152,51 @@ void fsl_asoc_reparent_pll_clocks(struct device *dev, struct clk *clk,
}
EXPORT_SYMBOL(fsl_asoc_reparent_pll_clocks);
/**
* fsl_asoc_constrain_rates - constrain rates according to clocks
*
* @target_constr: target constraint
* @original_constr: original constraint
* @pll8k_clk: PLL clock pointer for 8kHz
* @pll11k_clk: PLL clock pointer for 11kHz
* @ext_clk: External clock pointer
* @target_rates: target rates array
*
* This function constrain rates according to clocks
*/
void fsl_asoc_constrain_rates(struct snd_pcm_hw_constraint_list *target_constr,
const struct snd_pcm_hw_constraint_list *original_constr,
struct clk *pll8k_clk, struct clk *pll11k_clk,
struct clk *ext_clk, int *target_rates)
{
int i, j, k = 0;
u64 clk_rate[3];
*target_constr = *original_constr;
if (pll8k_clk || pll11k_clk || ext_clk) {
target_constr->list = target_rates;
target_constr->count = 0;
for (i = 0; i < original_constr->count; i++) {
clk_rate[0] = clk_get_rate(pll8k_clk);
clk_rate[1] = clk_get_rate(pll11k_clk);
clk_rate[2] = clk_get_rate(ext_clk);
for (j = 0; j < 3; j++) {
if (clk_rate[j] != 0 &&
do_div(clk_rate[j], original_constr->list[i]) == 0) {
target_rates[k++] = original_constr->list[i];
target_constr->count++;
break;
}
}
}
/* protection for if there is no proper rate found*/
if (!target_constr->count)
*target_constr = *original_constr;
}
}
EXPORT_SYMBOL(fsl_asoc_constrain_rates);
MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
MODULE_DESCRIPTION("Freescale ASoC utility code");
MODULE_LICENSE("GPL v2");

View File

@@ -26,4 +26,9 @@ void fsl_asoc_get_pll_clocks(struct device *dev, struct clk **pll8k_clk,
void fsl_asoc_reparent_pll_clocks(struct device *dev, struct clk *clk,
struct clk *pll8k_clk,
struct clk *pll11k_clk, u64 ratio);
void fsl_asoc_constrain_rates(struct snd_pcm_hw_constraint_list *target_constr,
const struct snd_pcm_hw_constraint_list *original_constr,
struct clk *pll8k_clk, struct clk *pll11k_clk,
struct clk *ext_clk, int *target_rates);
#endif /* _FSL_UTILS_H */

View File

@@ -19,6 +19,7 @@
#include "imx-pcm.h"
#define FSL_XCVR_CAPDS_SIZE 256
#define SPDIF_NUM_RATES 7
enum fsl_xcvr_pll_verison {
PLL_MX8MP,
@@ -57,6 +58,8 @@ struct fsl_xcvr {
u8 cap_ds[FSL_XCVR_CAPDS_SIZE];
struct work_struct work_rst;
spinlock_t lock; /* Protect hw_reset and trigger */
struct snd_pcm_hw_constraint_list spdif_constr_rates;
u32 spdif_constr_rates_list[SPDIF_NUM_RATES];
};
static const struct fsl_xcvr_pll_conf {
@@ -640,8 +643,12 @@ static int fsl_xcvr_startup(struct snd_pcm_substream *substream,
switch (xcvr->mode) {
case FSL_XCVR_MODE_SPDIF:
case FSL_XCVR_MODE_ARC:
ret = fsl_xcvr_constr(substream, &fsl_xcvr_spdif_channels_constr,
&fsl_xcvr_spdif_rates_constr);
if (xcvr->soc_data->spdif_only && tx)
ret = fsl_xcvr_constr(substream, &fsl_xcvr_spdif_channels_constr,
&xcvr->spdif_constr_rates);
else
ret = fsl_xcvr_constr(substream, &fsl_xcvr_spdif_channels_constr,
&fsl_xcvr_spdif_rates_constr);
break;
case FSL_XCVR_MODE_EARC:
ret = fsl_xcvr_constr(substream, &fsl_xcvr_earc_channels_constr,
@@ -1546,6 +1553,15 @@ static int fsl_xcvr_probe(struct platform_device *pdev)
fsl_asoc_get_pll_clocks(dev, &xcvr->pll8k_clk,
&xcvr->pll11k_clk);
if (xcvr->soc_data->spdif_only) {
if (!(xcvr->pll8k_clk || xcvr->pll11k_clk))
xcvr->pll8k_clk = xcvr->phy_clk;
fsl_asoc_constrain_rates(&xcvr->spdif_constr_rates,
&fsl_xcvr_spdif_rates_constr,
xcvr->pll8k_clk, xcvr->pll11k_clk, NULL,
xcvr->spdif_constr_rates_list);
}
xcvr->ram_addr = devm_platform_ioremap_resource_byname(pdev, "ram");
if (IS_ERR(xcvr->ram_addr))
return PTR_ERR(xcvr->ram_addr);