diff options
Diffstat (limited to 'sound/soc/fsl/fsl_asrc.c')
-rw-r--r-- | sound/soc/fsl/fsl_asrc.c | 472 |
1 files changed, 386 insertions, 86 deletions
diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index 88a438f6c2de..bc3fe4a3fc84 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -1,7 +1,8 @@ /* * Freescale ASRC ALSA SoC Digital Audio Interface (DAI) driver * - * Copyright (C) 2014 Freescale Semiconductor, Inc. + * Copyright (C) 2014-2016 Freescale Semiconductor, Inc. + * Copyright 2017 NXP * * Author: Nicolin Chen <nicoleotsuka@gmail.com> * @@ -21,67 +22,86 @@ #include <sound/pcm_params.h> #include "fsl_asrc.h" +#include "imx-pcm.h" #define IDEAL_RATIO_DECIMAL_DEPTH 26 #define pair_err(fmt, ...) \ dev_err(&asrc_priv->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__) +#define pair_warn(fmt, ...) \ + dev_warn(&asrc_priv->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__) + #define pair_dbg(fmt, ...) \ dev_dbg(&asrc_priv->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__) -/* Sample rates are aligned with that defined in pcm.h file */ -static const u8 process_option[][12][2] = { - /* 8kHz 11.025kHz 16kHz 22.05kHz 32kHz 44.1kHz 48kHz 64kHz 88.2kHz 96kHz 176kHz 192kHz */ - {{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 5512Hz */ - {{0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 8kHz */ - {{0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 11025Hz */ - {{1, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 16kHz */ - {{1, 2}, {1, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 22050Hz */ - {{1, 2}, {2, 1}, {2, 1}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0},}, /* 32kHz */ - {{2, 2}, {2, 2}, {2, 1}, {2, 1}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 44.1kHz */ - {{2, 2}, {2, 2}, {2, 1}, {2, 1}, {0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 48kHz */ - {{2, 2}, {2, 2}, {2, 2}, {2, 1}, {1, 2}, {0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0},}, /* 64kHz */ - {{2, 2}, {2, 2}, {2, 2}, {2, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 88.2kHz */ - {{2, 2}, {2, 2}, {2, 2}, {2, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 96kHz */ - {{2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 176kHz */ - {{2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 192kHz */ -}; - /* Corresponding to process_option */ -static int supported_input_rate[] = { - 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, 88200, - 96000, 176400, 192000, +static unsigned int supported_asrc_rate[] = { + 5512, 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, + 64000, 88200, 96000, 128000, 176400, 192000, }; -static int supported_asrc_rate[] = { - 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000, +static struct snd_pcm_hw_constraint_list fsl_asrc_rate_constraints = { + .count = ARRAY_SIZE(supported_asrc_rate), + .list = supported_asrc_rate, }; /** * The following tables map the relationship between asrc_inclk/asrc_outclk in * fsl_asrc.h and the registers of ASRCSR */ +#define CLK_MAP_NUM 48 static unsigned char input_clk_map_imx35[] = { 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[] = { 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[] = { /* 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[] = { /* 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, +}; + +/* i.MX8 uses the same map for input and output */ +static unsigned char input_clk_map_imx8_0[] = { + 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, }; -static unsigned char *clk_map[2]; +static unsigned char output_clk_map_imx8_0[] = { + 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, +}; + +static unsigned char input_clk_map_imx8_1[] = { + 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 output_clk_map_imx8_1[] = { + 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, +}; /** * Request ASRC pair @@ -90,7 +110,7 @@ static unsigned char *clk_map[2]; * 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; @@ -113,7 +133,8 @@ static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair) if (index == ASRC_INVALID_PAIR) { dev_err(dev, "all pairs are busy now\n"); ret = -EBUSY; - } else if (asrc_priv->channel_avail < channels) { + } else if (asrc_priv->channel_avail < channels || + (asrc_priv->channel_bits < 4 && channels % 2 != 0)) { dev_err(dev, "can't afford required channels: %d\n", channels); ret = -EINVAL; } else { @@ -128,12 +149,53 @@ static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair) return ret; } +static int proc_autosel(int Fsin, int Fsout, int *pre_proc, int *post_proc) +{ + bool det_out_op2_cond; + bool det_out_op0_cond; + det_out_op2_cond = (((Fsin * 15 > Fsout * 16) & (Fsout < 56000)) | + ((Fsin > 56000) & (Fsout < 56000))); + det_out_op0_cond = (Fsin * 23 < Fsout * 8); + + /* + * Not supported case: Tsout>16.125*Tsin, and Tsout>8.125*Tsin. + */ + if (Fsin * 8 > 129 * Fsout) + *pre_proc = 5; + else if (Fsin * 8 > 65 * Fsout) + *pre_proc = 4; + else if (Fsin * 8 > 33 * Fsout) + *pre_proc = 2; + else if (Fsin * 8 > 15 * Fsout) { + if (Fsin > 152000) + *pre_proc = 2; + else + *pre_proc = 1; + } else if (Fsin < 76000) + *pre_proc = 0; + else if (Fsin > 152000) + *pre_proc = 2; + else + *pre_proc = 1; + + if (det_out_op2_cond) + *post_proc = 2; + else if (det_out_op0_cond) + *post_proc = 0; + else + *post_proc = 1; + + if (*pre_proc == 4 || *pre_proc == 5) + return -EINVAL; + return 0; +} + /** * Release ASRC 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; @@ -235,7 +297,7 @@ static int fsl_asrc_set_ideal_ratio(struct fsl_asrc_pair *pair, * of struct asrc_config which includes in/output sample rate, width, channel * and clock settings. */ -static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair) +static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool p2p_in, bool p2p_out) { struct asrc_config *config = pair->config; struct fsl_asrc *asrc_priv = pair->asrc_priv; @@ -243,8 +305,10 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair) u32 inrate, outrate, indiv, outdiv; u32 clk_index[2], div[2]; int in, out, channels; + int pre_proc, post_proc; struct clk *clk; bool ideal; + int ret; if (!config) { pair_err("invalid pair config\n"); @@ -268,11 +332,11 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair) ideal = config->inclk == INCLK_NONE; /* Validate input and output sample rates */ - for (in = 0; in < ARRAY_SIZE(supported_input_rate); in++) - if (inrate == supported_input_rate[in]) + for (in = 0; in < ARRAY_SIZE(supported_asrc_rate); in++) + if (inrate == supported_asrc_rate[in]) break; - if (in == ARRAY_SIZE(supported_input_rate)) { + if (in == ARRAY_SIZE(supported_asrc_rate)) { pair_err("unsupported input sample rate: %dHz\n", inrate); return -EINVAL; } @@ -294,8 +358,8 @@ 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]]; @@ -309,11 +373,17 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair) clk = asrc_priv->asrck_clk[clk_index[OUT]]; - /* Use fixed output rate for Ideal Ratio mode (INCLK_NONE) */ - if (ideal) - div[OUT] = clk_get_rate(clk) / IDEAL_RATIO_RATE; - else + /* + * When P2P mode, output rate should align with the out samplerate. + * if set too high output rate, there will be lots of Overload. + * When M2M mode, output rate should also need to align with the out + * samplerate, but M2M must use less time to achieve good performance. + */ + if (p2p_out || p2p_in) div[OUT] = clk_get_rate(clk) / outrate; + else + div[OUT] = clk_get_rate(clk) / IDEAL_RATIO_RATE; + if (div[OUT] == 0) { pair_err("failed to support output sample rate %dHz by asrck_%x\n", @@ -321,6 +391,17 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair) return -EINVAL; } + if (div[IN] > 1024 && div[OUT] > 1024) { + pair_warn("both divider (%d, %d) are larger than threshold\n", + div[IN], div[OUT]); + } + + if (div[IN] > 1024) + div[IN] = 1024; + + if (div[OUT] > 1024) + div[OUT] = 1024; + /* Set the channel number */ channels = config->channel_num; @@ -335,8 +416,11 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair) /* Default setting: Automatic selection for processing mode */ regmap_update_bits(asrc_priv->regmap, REG_ASRCTR, ASRCTR_ATSi_MASK(index), ASRCTR_ATS(index)); + + /* Default setting: use internal measured ratio */ regmap_update_bits(asrc_priv->regmap, REG_ASRCTR, - ASRCTR_USRi_MASK(index), 0); + ASRCTR_USRi_MASK(index) | ASRCTR_IDRi_MASK(index), + ASRCTR_USR(index)); /* Set the input and output clock sources */ regmap_update_bits(asrc_priv->regmap, REG_ASRCSR, @@ -381,11 +465,17 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair) ASRCTR_IDRi_MASK(index) | ASRCTR_USRi_MASK(index), ASRCTR_IDR(index) | ASRCTR_USR(index)); + ret = proc_autosel(inrate, outrate, &pre_proc, &post_proc); + if (ret) { + pair_err("No supported pre-processing options\n"); + return ret; + } + /* Apply configurations for pre- and post-processing */ regmap_update_bits(asrc_priv->regmap, REG_ASRCFG, ASRCFG_PREMODi_MASK(index) | ASRCFG_POSTMODi_MASK(index), - ASRCFG_PREMOD(index, process_option[in][out][0]) | - ASRCFG_POSTMOD(index, process_option[in][out][1])); + ASRCFG_PREMOD(index, pre_proc) | + ASRCFG_POSTMOD(index, post_proc)); return fsl_asrc_set_ideal_ratio(pair, inrate, outrate); } @@ -449,6 +539,61 @@ struct dma_chan *fsl_asrc_get_dma_channel(struct fsl_asrc_pair *pair, bool dir) } EXPORT_SYMBOL_GPL(fsl_asrc_get_dma_channel); +static int fsl_asrc_select_clk(struct fsl_asrc *asrc_priv, + struct fsl_asrc_pair *pair, + int in_rate, + int out_rate) +{ + struct asrc_config *config = pair->config; + int clk_rate; + int clk_index; + int i = 0, j = 0; + int rate[2]; + int select_clk[2]; + bool clk_sel[2]; + + rate[0] = in_rate; + rate[1] = out_rate; + + /*select proper clock for asrc p2p mode*/ + for (j = 0; j < 2; j++) { + for (i = 0; i < CLK_MAP_NUM; i++) { + clk_index = asrc_priv->clk_map[j][i]; + clk_rate = clk_get_rate(asrc_priv->asrck_clk[clk_index]); + if (clk_rate != 0 && (clk_rate / rate[j]) <= 1024 && + (clk_rate % rate[j]) == 0) + break; + } + + if (i == CLK_MAP_NUM) { + select_clk[j] = OUTCLK_ASRCK1_CLK; + clk_sel[j] = false; + } else { + select_clk[j] = i; + clk_sel[j] = true; + } + } + + if (clk_sel[0] != true || clk_sel[1] != true) + select_clk[IN] = INCLK_NONE; + + config->inclk = select_clk[IN]; + config->outclk = select_clk[OUT]; + + /* + * FIXME: workaroud for 176400/192000 with 8 channel input case + * the output sample rate is 48kHz. + * with ideal ratio mode, the asrc seems has performance issue + * that the output sound is not correct. so switch to non-ideal + * ratio mode + */ + if (config->channel_num >= 8 && config->input_sample_rate >= 176400 + && config->inclk == INCLK_NONE) + config->inclk = INCLK_ASRCK1_CLK; + + return 0; +} + static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -468,6 +613,7 @@ static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream, return ret; } + pair->pair_streams |= BIT(substream->stream); pair->config = &config; if (width == 16) @@ -482,25 +628,46 @@ static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream, config.pair = pair->index; config.channel_num = channels; - config.inclk = INCLK_NONE; - 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_sample_rate = rate; config.output_sample_rate = asrc_priv->asrc_rate; + + ret = fsl_asrc_select_clk(asrc_priv, pair, + config.input_sample_rate, + config.output_sample_rate); + if (ret) { + dev_err(dai->dev, "fail to select clock\n"); + return ret; + } + + ret = fsl_asrc_config_pair(pair, false, true); + if (ret) { + dev_err(dai->dev, "fail to config asrc pair\n"); + return ret; + } + } else { config.input_word_width = word_width; config.output_word_width = width; config.input_sample_rate = asrc_priv->asrc_rate; config.output_sample_rate = rate; - } - ret = fsl_asrc_config_pair(pair); - if (ret) { - dev_err(dai->dev, "fail to config asrc pair\n"); - return ret; + ret = fsl_asrc_select_clk(asrc_priv, pair, + config.input_sample_rate, + config.output_sample_rate); + if (ret) { + dev_err(dai->dev, "fail to select clock\n"); + return ret; + } + + ret = fsl_asrc_config_pair(pair, true, false); + if (ret) { + dev_err(dai->dev, "fail to config asrc pair\n"); + return ret; + } } return 0; @@ -512,8 +679,10 @@ static int fsl_asrc_dai_hw_free(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; struct fsl_asrc_pair *pair = runtime->private_data; - if (pair) + if (pair && (pair->pair_streams & BIT(substream->stream))) { fsl_asrc_release_pair(pair); + pair->pair_streams &= ~BIT(substream->stream); + } return 0; } @@ -529,6 +698,8 @@ static int fsl_asrc_dai_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: fsl_asrc_start_pair(pair); + /* Output enough data to content the DMA burstsize of BE */ + mdelay(1); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: @@ -542,7 +713,28 @@ static int fsl_asrc_dai_trigger(struct snd_pcm_substream *substream, int cmd, return 0; } +static int fsl_asrc_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(cpu_dai); + + asrc_priv->substream[substream->stream] = substream; + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &fsl_asrc_rate_constraints); +} + +static void fsl_asrc_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(cpu_dai); + + asrc_priv->substream[substream->stream] = NULL; +} + static struct snd_soc_dai_ops fsl_asrc_dai_ops = { + .startup = fsl_asrc_dai_startup, + .shutdown = fsl_asrc_dai_shutdown, .hw_params = fsl_asrc_dai_hw_params, .hw_free = fsl_asrc_dai_hw_free, .trigger = fsl_asrc_dai_trigger, @@ -561,7 +753,7 @@ static int fsl_asrc_dai_probe(struct snd_soc_dai *dai) #define FSL_ASRC_RATES SNDRV_PCM_RATE_8000_192000 #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, @@ -569,14 +761,18 @@ static struct snd_soc_dai_driver fsl_asrc_dai = { .stream_name = "ASRC-Playback", .channels_min = 1, .channels_max = 10, - .rates = FSL_ASRC_RATES, + .rate_min = 5512, + .rate_max = 192000, + .rates = SNDRV_PCM_RATE_KNOT, .formats = FSL_ASRC_FORMATS, }, .capture = { .stream_name = "ASRC-Capture", .channels_min = 1, .channels_max = 10, - .rates = FSL_ASRC_RATES, + .rate_min = 5512, + .rate_max = 192000, + .rates = SNDRV_PCM_RATE_KNOT, .formats = FSL_ASRC_FORMATS, }, .ops = &fsl_asrc_dai_ops, @@ -729,11 +925,71 @@ static const struct regmap_config fsl_asrc_regmap_config = { .cache_type = REGCACHE_FLAT, }; +#include "fsl_asrc_m2m.c" + +static bool fsl_asrc_check_xrun(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_dmaengine_dai_dma_data *dma_params_be = NULL; + struct snd_pcm_substream *be_substream; + struct snd_soc_dpcm *dpcm; + int ret = 0; + + /* find the be for this fe stream */ + list_for_each_entry(dpcm, &rtd->dpcm[substream->stream].be_clients, list_be) { + struct snd_soc_pcm_runtime *be = dpcm->be; + struct snd_soc_dai *dai = be->cpu_dai; + + if (dpcm->fe != rtd) + continue; + + be_substream = snd_soc_dpcm_get_substream(be, substream->stream); + dma_params_be = snd_soc_dai_get_dma_data(dai, be_substream); + if (dma_params_be->check_xrun && dma_params_be->check_xrun(be_substream)) + ret = 1; + } + + return ret; +} + +static void fsl_asrc_reset(struct snd_pcm_substream *substream, bool stop) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(cpu_dai); + struct snd_dmaengine_dai_dma_data *dma_params_be = NULL; + struct snd_soc_dpcm *dpcm; + struct snd_pcm_substream *be_substream; + unsigned long flags = 0; + + if (stop) + imx_stop_lock_pcm_streams(asrc_priv->substream, 2, &flags); + + /* find the be for this fe stream */ + list_for_each_entry(dpcm, &rtd->dpcm[substream->stream].be_clients, list_be) { + struct snd_soc_pcm_runtime *be = dpcm->be; + struct snd_soc_dai *dai = be->cpu_dai; + + if (dpcm->fe != rtd) + continue; + + be_substream = snd_soc_dpcm_get_substream(be, substream->stream); + dma_params_be = snd_soc_dai_get_dma_data(dai, be_substream); + dma_params_be->device_reset(be_substream, 0); + break; + } + + if (stop) + imx_start_unlock_pcm_streams(asrc_priv->substream, 2, &flags); +} + /** * Initialize ASRC registers with a default configurations */ static int fsl_asrc_init(struct fsl_asrc *asrc_priv) { + unsigned long ipg_rate; + /* Halt ASRC internal FP when input FIFO needs data for pair A, B, C */ regmap_write(asrc_priv->regmap, REG_ASRCTR, ASRCTR_ASRCEN); @@ -751,11 +1007,12 @@ static int fsl_asrc_init(struct fsl_asrc *asrc_priv) regmap_update_bits(asrc_priv->regmap, REG_ASRTFR1, ASRTFR1_TF_BASE_MASK, ASRTFR1_TF_BASE(0xfc)); - /* Set the processing clock for 76KHz to 133M */ - regmap_write(asrc_priv->regmap, REG_ASR76K, 0x06D6); - - /* Set the processing clock for 56KHz to 133M */ - return regmap_write(asrc_priv->regmap, REG_ASR56K, 0x0947); + ipg_rate = clk_get_rate(asrc_priv->ipg_clk); + /* Set the period of the 76KHz and 56KHz sampling clocks based on + * the ASRC processing clock. + */ + regmap_write(asrc_priv->regmap, REG_ASR76K, ipg_rate / 76000); + return regmap_write(asrc_priv->regmap, REG_ASR56K, ipg_rate / 56000); } /** @@ -881,12 +1138,32 @@ static int fsl_asrc_probe(struct platform_device *pdev) 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 { + strncpy(asrc_priv->name, "mxc_asrc", + sizeof(asrc_priv->name) - 1); + asrc_priv->clk_map[IN] = input_clk_map_imx35; + asrc_priv->clk_map[OUT] = output_clk_map_imx35; + asrc_priv->dma_type = DMA_SDMA; + } else if (of_device_is_compatible(np, "fsl,imx53-asrc")) { + asrc_priv->channel_bits = 4; + strncpy(asrc_priv->name, "mxc_asrc", + sizeof(asrc_priv->name) - 1); + asrc_priv->clk_map[IN] = input_clk_map_imx53; + asrc_priv->clk_map[OUT] = output_clk_map_imx53; + asrc_priv->dma_type = DMA_SDMA; + } else if (of_device_is_compatible(np, "fsl,imx8qm-asrc0")) { + asrc_priv->channel_bits = 4; + strncpy(asrc_priv->name, "mxc_asrc", + sizeof(asrc_priv->name) - 1); + asrc_priv->clk_map[IN] = input_clk_map_imx8_0; + asrc_priv->clk_map[OUT] = output_clk_map_imx8_0; + asrc_priv->dma_type = DMA_EDMA; + } else if (of_device_is_compatible(np, "fsl,imx8qm-asrc1")) { asrc_priv->channel_bits = 4; - clk_map[IN] = input_clk_map_imx53; - clk_map[OUT] = output_clk_map_imx53; + strncpy(asrc_priv->name, "mxc_asrc1", + sizeof(asrc_priv->name) - 1); + asrc_priv->clk_map[IN] = input_clk_map_imx8_1; + asrc_priv->clk_map[OUT] = output_clk_map_imx8_1; + asrc_priv->dma_type = DMA_EDMA; } ret = fsl_asrc_init(asrc_priv); @@ -911,6 +1188,11 @@ static int fsl_asrc_probe(struct platform_device *pdev) return ret; } + asrc_priv->dma_params_tx.check_xrun = fsl_asrc_check_xrun; + asrc_priv->dma_params_rx.check_xrun = fsl_asrc_check_xrun; + asrc_priv->dma_params_tx.device_reset = fsl_asrc_reset; + asrc_priv->dma_params_rx.device_reset = fsl_asrc_reset; + if (asrc_priv->asrc_width != 16 && asrc_priv->asrc_width != 24) { dev_warn(&pdev->dev, "unsupported width, switching to 24bit\n"); asrc_priv->asrc_width = 24; @@ -920,6 +1202,8 @@ static int fsl_asrc_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); spin_lock_init(&asrc_priv->lock); + regcache_cache_only(asrc_priv->regmap, true); + ret = devm_snd_soc_register_component(&pdev->dev, &fsl_asrc_component, &fsl_asrc_dai, 1); if (ret) { @@ -933,6 +1217,12 @@ static int fsl_asrc_probe(struct platform_device *pdev) return ret; } + ret = fsl_asrc_m2m_init(asrc_priv); + if (ret) { + dev_err(&pdev->dev, "failed to init m2m device %d\n", ret); + return ret; + } + return 0; } @@ -941,6 +1231,7 @@ static int fsl_asrc_runtime_resume(struct device *dev) { struct fsl_asrc *asrc_priv = dev_get_drvdata(dev); int i, ret; + u32 asrctr; ret = clk_prepare_enable(asrc_priv->mem_clk); if (ret) @@ -959,6 +1250,24 @@ static int fsl_asrc_runtime_resume(struct device *dev) goto disable_asrck_clk; } + /* Stop all pairs provisionally */ + regmap_read(asrc_priv->regmap, REG_ASRCTR, &asrctr); + regmap_update_bits(asrc_priv->regmap, REG_ASRCTR, + ASRCTR_ASRCEi_ALL_MASK, 0); + + /* Restore all registers */ + regcache_cache_only(asrc_priv->regmap, false); + regcache_mark_dirty(asrc_priv->regmap); + regcache_sync(asrc_priv->regmap); + + regmap_update_bits(asrc_priv->regmap, REG_ASRCFG, + ASRCFG_NDPRi_ALL_MASK | ASRCFG_POSTMODi_ALL_MASK | + ASRCFG_PREMODi_ALL_MASK, asrc_priv->regcache_cfg); + + /* Restart enabled pairs */ + regmap_update_bits(asrc_priv->regmap, REG_ASRCTR, + ASRCTR_ASRCEi_ALL_MASK, asrctr); + return 0; disable_asrck_clk: @@ -978,6 +1287,11 @@ static int fsl_asrc_runtime_suspend(struct device *dev) struct fsl_asrc *asrc_priv = dev_get_drvdata(dev); int i; + regmap_read(asrc_priv->regmap, REG_ASRCFG, + &asrc_priv->regcache_cfg); + + regcache_cache_only(asrc_priv->regmap, true); + for (i = 0; i < ASRC_CLK_MAX_NUM; i++) clk_disable_unprepare(asrc_priv->asrck_clk[i]); if (!IS_ERR(asrc_priv->spba_clk)) @@ -993,39 +1307,22 @@ static int fsl_asrc_runtime_suspend(struct device *dev) static int fsl_asrc_suspend(struct device *dev) { struct fsl_asrc *asrc_priv = dev_get_drvdata(dev); + int ret; - regmap_read(asrc_priv->regmap, REG_ASRCFG, - &asrc_priv->regcache_cfg); + fsl_asrc_m2m_suspend(asrc_priv); - regcache_cache_only(asrc_priv->regmap, true); - regcache_mark_dirty(asrc_priv->regmap); + ret = pm_runtime_force_suspend(dev); - return 0; + return ret; } static int fsl_asrc_resume(struct device *dev) { - struct fsl_asrc *asrc_priv = dev_get_drvdata(dev); - u32 asrctr; - - /* Stop all pairs provisionally */ - regmap_read(asrc_priv->regmap, REG_ASRCTR, &asrctr); - regmap_update_bits(asrc_priv->regmap, REG_ASRCTR, - ASRCTR_ASRCEi_ALL_MASK, 0); + int ret; - /* Restore all registers */ - regcache_cache_only(asrc_priv->regmap, false); - regcache_sync(asrc_priv->regmap); + ret = pm_runtime_force_resume(dev); - regmap_update_bits(asrc_priv->regmap, REG_ASRCFG, - ASRCFG_NDPRi_ALL_MASK | ASRCFG_POSTMODi_ALL_MASK | - ASRCFG_PREMODi_ALL_MASK, asrc_priv->regcache_cfg); - - /* Restart enabled pairs */ - regmap_update_bits(asrc_priv->regmap, REG_ASRCTR, - ASRCTR_ASRCEi_ALL_MASK, asrctr); - - return 0; + return ret; } #endif /* CONFIG_PM_SLEEP */ @@ -1037,12 +1334,15 @@ static const struct dev_pm_ops fsl_asrc_pm = { static const struct of_device_id fsl_asrc_ids[] = { { .compatible = "fsl,imx35-asrc", }, { .compatible = "fsl,imx53-asrc", }, + { .compatible = "fsl,imx8qm-asrc0", }, + { .compatible = "fsl,imx8qm-asrc1", }, {} }; MODULE_DEVICE_TABLE(of, fsl_asrc_ids); static struct platform_driver fsl_asrc_driver = { .probe = fsl_asrc_probe, + .remove = fsl_asrc_m2m_remove, .driver = { .name = "fsl-asrc", .of_match_table = fsl_asrc_ids, |