diff options
Diffstat (limited to 'sound/soc/fsl/fsl_asrc.c')
-rw-r--r-- | sound/soc/fsl/fsl_asrc.c | 239 |
1 files changed, 183 insertions, 56 deletions
diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index cbbf6257f08a..0dcebc24c312 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -41,26 +41,65 @@ static struct snd_pcm_hw_constraint_list fsl_asrc_rate_constraints = { * The following tables map the relationship between asrc_inclk/asrc_outclk in * fsl_asrc.h and the registers of ASRCSR */ -static unsigned char input_clk_map_imx35[] = { +static unsigned char input_clk_map_imx35[ASRC_CLK_MAP_LEN] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, }; -static unsigned char output_clk_map_imx35[] = { +static unsigned char output_clk_map_imx35[ASRC_CLK_MAP_LEN] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, }; /* i.MX53 uses the same map for input and output */ -static unsigned char input_clk_map_imx53[] = { +static unsigned char input_clk_map_imx53[ASRC_CLK_MAP_LEN] = { /* 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf */ 0x0, 0x1, 0x2, 0x7, 0x4, 0x5, 0x6, 0x3, 0x8, 0x9, 0xa, 0xb, 0xc, 0xf, 0xe, 0xd, + 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, + 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, }; -static unsigned char output_clk_map_imx53[] = { +static unsigned char output_clk_map_imx53[ASRC_CLK_MAP_LEN] = { /* 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf */ 0x8, 0x9, 0xa, 0x7, 0xc, 0x5, 0x6, 0xb, 0x0, 0x1, 0x2, 0x3, 0x4, 0xf, 0xe, 0xd, + 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, + 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, }; -static unsigned char *clk_map[2]; +/** + * i.MX8QM/i.MX8QXP uses the same map for input and output. + * clk_map_imx8qm[0] is for i.MX8QM asrc0 + * clk_map_imx8qm[1] is for i.MX8QM asrc1 + * clk_map_imx8qxp[0] is for i.MX8QXP asrc0 + * clk_map_imx8qxp[1] is for i.MX8QXP asrc1 + */ +static unsigned char clk_map_imx8qm[2][ASRC_CLK_MAP_LEN] = { + { + 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, + 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, + }, + { + 0xf, 0xf, 0xf, 0xf, 0xf, 0x7, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0, + 0x0, 0x1, 0x2, 0x3, 0xb, 0xc, 0xf, 0xf, 0xd, 0xe, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, + 0x4, 0x5, 0x6, 0xf, 0x8, 0x9, 0xa, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, + }, +}; + +static unsigned char clk_map_imx8qxp[2][ASRC_CLK_MAP_LEN] = { + { + 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0xf, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xf, 0xf, + 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, + }, + { + 0xf, 0xf, 0xf, 0xf, 0xf, 0x7, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0, + 0x0, 0x1, 0x2, 0x3, 0x7, 0x8, 0xf, 0xf, 0x9, 0xa, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, + 0xf, 0xf, 0x6, 0xf, 0xf, 0xf, 0xa, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, + }, +}; /** * Select the pre-processing and post-processing options @@ -115,7 +154,7 @@ static void fsl_asrc_sel_proc(int inrate, int outrate, * within range [ANCA, ANCA+ANCB-1], depends on the channels of pair A * while pair A and pair C are comparatively independent. */ -static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair) +int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair) { enum asrc_pair_index index = ASRC_INVALID_PAIR; struct fsl_asrc *asrc_priv = pair->asrc_priv; @@ -158,7 +197,7 @@ static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair) * * It clears the resource from asrc_priv and releases the occupied channels. */ -static void fsl_asrc_release_pair(struct fsl_asrc_pair *pair) +void fsl_asrc_release_pair(struct fsl_asrc_pair *pair) { struct fsl_asrc *asrc_priv = pair->asrc_priv; enum asrc_pair_index index = pair->index; @@ -259,14 +298,24 @@ static int fsl_asrc_set_ideal_ratio(struct fsl_asrc_pair *pair, * It configures those ASRC registers according to a configuration instance * of struct asrc_config which includes in/output sample rate, width, channel * and clock settings. + * + * Note: + * The ideal ratio configuration can work with a flexible clock rate setting. + * Using IDEAL_RATIO_RATE gives a faster converting speed but overloads ASRC. + * For a regular audio playback, the clock rate should not be slower than an + * clock rate aligning with the output sample rate; For a use case requiring + * faster conversion, set use_ideal_rate to have the faster speed. */ -static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair) +static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate) { struct asrc_config *config = pair->config; struct fsl_asrc *asrc_priv = pair->asrc_priv; enum asrc_pair_index index = pair->index; + enum asrc_word_width input_word_width; + enum asrc_word_width output_word_width; u32 inrate, outrate, indiv, outdiv; - u32 clk_index[2], div[2]; + u32 clk_index[2], div[2], rem[2]; + u64 clk_rate; int in, out, channels; int pre_proc, post_proc; struct clk *clk; @@ -283,9 +332,32 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair) return -EINVAL; } - /* Validate output width */ - if (config->output_word_width == ASRC_WIDTH_8_BIT) { - pair_err("does not support 8bit width output\n"); + switch (snd_pcm_format_width(config->input_format)) { + case 8: + input_word_width = ASRC_WIDTH_8_BIT; + break; + case 16: + input_word_width = ASRC_WIDTH_16_BIT; + break; + case 24: + input_word_width = ASRC_WIDTH_24_BIT; + break; + default: + pair_err("does not support this input format, %d\n", + config->input_format); + return -EINVAL; + } + + switch (snd_pcm_format_width(config->output_format)) { + case 16: + output_word_width = ASRC_WIDTH_16_BIT; + break; + case 24: + output_word_width = ASRC_WIDTH_24_BIT; + break; + default: + pair_err("does not support this output format, %d\n", + config->output_format); return -EINVAL; } @@ -320,43 +392,58 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair) } /* Validate input and output clock sources */ - clk_index[IN] = clk_map[IN][config->inclk]; - clk_index[OUT] = clk_map[OUT][config->outclk]; + clk_index[IN] = asrc_priv->clk_map[IN][config->inclk]; + clk_index[OUT] = asrc_priv->clk_map[OUT][config->outclk]; /* We only have output clock for ideal ratio mode */ clk = asrc_priv->asrck_clk[clk_index[ideal ? OUT : IN]]; - div[IN] = clk_get_rate(clk) / inrate; - if (div[IN] == 0) { + clk_rate = clk_get_rate(clk); + rem[IN] = do_div(clk_rate, inrate); + div[IN] = (u32)clk_rate; + + /* + * The divider range is [1, 1024], defined by the hardware. For non- + * ideal ratio configuration, clock rate has to be strictly aligned + * with the sample rate. For ideal ratio configuration, clock rates + * only result in different converting speeds. So remainder does not + * matter, as long as we keep the divider within its valid range. + */ + if (div[IN] == 0 || (!ideal && (div[IN] > 1024 || rem[IN] != 0))) { pair_err("failed to support input sample rate %dHz by asrck_%x\n", inrate, clk_index[ideal ? OUT : IN]); return -EINVAL; } - clk = asrc_priv->asrck_clk[clk_index[OUT]]; + div[IN] = min_t(u32, 1024, div[IN]); - /* Use fixed output rate for Ideal Ratio mode (INCLK_NONE) */ - if (ideal) - div[OUT] = clk_get_rate(clk) / IDEAL_RATIO_RATE; + clk = asrc_priv->asrck_clk[clk_index[OUT]]; + clk_rate = clk_get_rate(clk); + if (ideal && use_ideal_rate) + rem[OUT] = do_div(clk_rate, IDEAL_RATIO_RATE); else - div[OUT] = clk_get_rate(clk) / outrate; + rem[OUT] = do_div(clk_rate, outrate); + div[OUT] = clk_rate; - if (div[OUT] == 0) { + /* Output divider has the same limitation as the input one */ + if (div[OUT] == 0 || (!ideal && (div[OUT] > 1024 || rem[OUT] != 0))) { pair_err("failed to support output sample rate %dHz by asrck_%x\n", outrate, clk_index[OUT]); return -EINVAL; } + div[OUT] = min_t(u32, 1024, div[OUT]); + /* Set the channel number */ channels = config->channel_num; - if (asrc_priv->channel_bits < 4) + if (asrc_priv->soc->channel_bits < 4) channels /= 2; /* Update channels for current pair */ regmap_update_bits(asrc_priv->regmap, REG_ASRCNCR, - ASRCNCR_ANCi_MASK(index, asrc_priv->channel_bits), - ASRCNCR_ANCi(index, channels, asrc_priv->channel_bits)); + ASRCNCR_ANCi_MASK(index, asrc_priv->soc->channel_bits), + ASRCNCR_ANCi(index, channels, asrc_priv->soc->channel_bits)); /* Default setting: Automatic selection for processing mode */ regmap_update_bits(asrc_priv->regmap, REG_ASRCTR, @@ -383,8 +470,8 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair) /* Implement word_width configurations */ regmap_update_bits(asrc_priv->regmap, REG_ASRMCR1(index), ASRMCR1i_OW16_MASK | ASRMCR1i_IWD_MASK, - ASRMCR1i_OW16(config->output_word_width) | - ASRMCR1i_IWD(config->input_word_width)); + ASRMCR1i_OW16(output_word_width) | + ASRMCR1i_IWD(input_word_width)); /* Enable BUFFER STALL */ regmap_update_bits(asrc_priv->regmap, REG_ASRMCR(index), @@ -483,7 +570,7 @@ static int fsl_asrc_dai_startup(struct snd_pcm_substream *substream, struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(dai); /* Odd channel number is not valid for older ASRC (channel_bits==3) */ - if (asrc_priv->channel_bits == 3) + if (asrc_priv->soc->channel_bits == 3) snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 2); @@ -497,13 +584,13 @@ static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(dai); - int width = params_width(params); struct snd_pcm_runtime *runtime = substream->runtime; struct fsl_asrc_pair *pair = runtime->private_data; unsigned int channels = params_channels(params); unsigned int rate = params_rate(params); struct asrc_config config; - int word_width, ret; + snd_pcm_format_t format; + int ret; ret = fsl_asrc_request_pair(channels, pair); if (ret) { @@ -513,15 +600,10 @@ static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream, pair->config = &config; - if (width == 16) - width = ASRC_WIDTH_16_BIT; - else - width = ASRC_WIDTH_24_BIT; - if (asrc_priv->asrc_width == 16) - word_width = ASRC_WIDTH_16_BIT; + format = SNDRV_PCM_FORMAT_S16_LE; else - word_width = ASRC_WIDTH_24_BIT; + format = SNDRV_PCM_FORMAT_S24_LE; config.pair = pair->index; config.channel_num = channels; @@ -529,18 +611,18 @@ static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream, config.outclk = OUTCLK_ASRCK1_CLK; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - config.input_word_width = width; - config.output_word_width = word_width; + config.input_format = params_format(params); + config.output_format = format; config.input_sample_rate = rate; config.output_sample_rate = asrc_priv->asrc_rate; } else { - config.input_word_width = word_width; - config.output_word_width = width; + config.input_format = format; + config.output_format = params_format(params); config.input_sample_rate = asrc_priv->asrc_rate; config.output_sample_rate = rate; } - ret = fsl_asrc_config_pair(pair); + ret = fsl_asrc_config_pair(pair, false); if (ret) { dev_err(dai->dev, "fail to config asrc pair\n"); return ret; @@ -604,7 +686,7 @@ static int fsl_asrc_dai_probe(struct snd_soc_dai *dai) #define FSL_ASRC_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_S16_LE | \ - SNDRV_PCM_FMTBIT_S20_3LE) + SNDRV_PCM_FMTBIT_S24_3LE) static struct snd_soc_dai_driver fsl_asrc_dai = { .probe = fsl_asrc_dai_probe, @@ -615,7 +697,8 @@ static struct snd_soc_dai_driver fsl_asrc_dai = { .rate_min = 5512, .rate_max = 192000, .rates = SNDRV_PCM_RATE_KNOT, - .formats = FSL_ASRC_FORMATS, + .formats = FSL_ASRC_FORMATS | + SNDRV_PCM_FMTBIT_S8, }, .capture = { .stream_name = "ASRC-Capture", @@ -861,6 +944,7 @@ static int fsl_asrc_probe(struct platform_device *pdev) struct resource *res; void __iomem *regs; int irq, ret, i; + u32 map_idx; char tmp[16]; asrc_priv = devm_kzalloc(&pdev->dev, sizeof(*asrc_priv), GFP_KERNEL); @@ -885,10 +969,8 @@ static int fsl_asrc_probe(struct platform_device *pdev) } irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "no irq for node %s\n", pdev->name); + if (irq < 0) return irq; - } ret = devm_request_irq(&pdev->dev, irq, fsl_asrc_isr, 0, dev_name(&pdev->dev), asrc_priv); @@ -922,14 +1004,37 @@ static int fsl_asrc_probe(struct platform_device *pdev) } } + asrc_priv->soc = of_device_get_match_data(&pdev->dev); + if (!asrc_priv->soc) { + dev_err(&pdev->dev, "failed to get soc data\n"); + return -ENODEV; + } + if (of_device_is_compatible(np, "fsl,imx35-asrc")) { - asrc_priv->channel_bits = 3; - clk_map[IN] = input_clk_map_imx35; - clk_map[OUT] = output_clk_map_imx35; - } else { - asrc_priv->channel_bits = 4; - clk_map[IN] = input_clk_map_imx53; - clk_map[OUT] = output_clk_map_imx53; + asrc_priv->clk_map[IN] = input_clk_map_imx35; + asrc_priv->clk_map[OUT] = output_clk_map_imx35; + } else if (of_device_is_compatible(np, "fsl,imx53-asrc")) { + asrc_priv->clk_map[IN] = input_clk_map_imx53; + asrc_priv->clk_map[OUT] = output_clk_map_imx53; + } else if (of_device_is_compatible(np, "fsl,imx8qm-asrc") || + of_device_is_compatible(np, "fsl,imx8qxp-asrc")) { + ret = of_property_read_u32(np, "fsl,asrc-clk-map", &map_idx); + if (ret) { + dev_err(&pdev->dev, "failed to get clk map index\n"); + return ret; + } + + if (map_idx > 1) { + dev_err(&pdev->dev, "unsupported clk map index\n"); + return -EINVAL; + } + if (of_device_is_compatible(np, "fsl,imx8qm-asrc")) { + asrc_priv->clk_map[IN] = clk_map_imx8qm[map_idx]; + asrc_priv->clk_map[OUT] = clk_map_imx8qm[map_idx]; + } else { + asrc_priv->clk_map[IN] = clk_map_imx8qxp[map_idx]; + asrc_priv->clk_map[OUT] = clk_map_imx8qxp[map_idx]; + } } ret = fsl_asrc_init(asrc_priv); @@ -1071,9 +1176,31 @@ static const struct dev_pm_ops fsl_asrc_pm = { SET_SYSTEM_SLEEP_PM_OPS(fsl_asrc_suspend, fsl_asrc_resume) }; +static const struct fsl_asrc_soc_data fsl_asrc_imx35_data = { + .use_edma = false, + .channel_bits = 3, +}; + +static const struct fsl_asrc_soc_data fsl_asrc_imx53_data = { + .use_edma = false, + .channel_bits = 4, +}; + +static const struct fsl_asrc_soc_data fsl_asrc_imx8qm_data = { + .use_edma = true, + .channel_bits = 4, +}; + +static const struct fsl_asrc_soc_data fsl_asrc_imx8qxp_data = { + .use_edma = true, + .channel_bits = 4, +}; + static const struct of_device_id fsl_asrc_ids[] = { - { .compatible = "fsl,imx35-asrc", }, - { .compatible = "fsl,imx53-asrc", }, + { .compatible = "fsl,imx35-asrc", .data = &fsl_asrc_imx35_data }, + { .compatible = "fsl,imx53-asrc", .data = &fsl_asrc_imx53_data }, + { .compatible = "fsl,imx8qm-asrc", .data = &fsl_asrc_imx8qm_data }, + { .compatible = "fsl,imx8qxp-asrc", .data = &fsl_asrc_imx8qxp_data }, {} }; MODULE_DEVICE_TABLE(of, fsl_asrc_ids); |