[RFC PATCH v2 0/6] ASoC: fsl: add memory to memory function for ASRC
This function is base on the accelerator implementation for compress API: https://patchwork.kernel.org/project/alsa-devel/patch/20240731083843.59911-1...
Audio signal processing also has the requirement for memory to memory similar as Video.
This asrc memory to memory (memory ->asrc->memory) case is a non real time use case.
User fills the input buffer to the asrc module, after conversion, then asrc sends back the output buffer to user. So it is not a traditional ALSA playback and capture case.
Because we had implemented the "memory -> asrc ->i2s device-> codec" use case in ALSA. Now the "memory->asrc->memory" needs to reuse the code in asrc driver, so the patch 1 and patch 2 is for refining the code to make it can be shared by the "memory->asrc->memory" driver.
Other change is to add memory to memory support for two kinds of i.MX ASRC modules.
changes in v2: - Remove the changes in compress API - drop the SNDRV_COMPRESS_SRC_RATIO_MOD - drop the SND_AUDIOCODEC_SRC and struct snd_dec_src - define private metadata key value ASRC_OUTPUT_FORMAT/ASRC_OUTPUT_RATE/ASRC_RATIO_MOD
Shengjiu Wang (6): ALSA: compress: reserve space in snd_compr_metadata.key for private usage ASoC: fsl_asrc: define functions for memory to memory usage ASoC: fsl_easrc: define functions for memory to memory usage ASoC: fsl_asrc_m2m: Add memory to memory function ASoC: fsl_asrc: register m2m platform device ASoC: fsl_easrc: register m2m platform device
include/uapi/sound/compress_offload.h | 2 +- sound/soc/fsl/Kconfig | 1 + sound/soc/fsl/Makefile | 2 +- sound/soc/fsl/fsl_asrc.c | 176 +++++- sound/soc/fsl/fsl_asrc.h | 2 + sound/soc/fsl/fsl_asrc_common.h | 68 +++ sound/soc/fsl/fsl_asrc_m2m.c | 791 ++++++++++++++++++++++++++ sound/soc/fsl/fsl_easrc.c | 259 ++++++++- sound/soc/fsl/fsl_easrc.h | 4 + 9 files changed, 1297 insertions(+), 8 deletions(-) create mode 100644 sound/soc/fsl/fsl_asrc_m2m.c
Reserve the key value which is larger than 0x80000000 for driver private usage. Driver may define its own key values which are not public in ALSA header
Signed-off-by: Shengjiu Wang shengjiu.wang@nxp.com --- include/uapi/sound/compress_offload.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/uapi/sound/compress_offload.h b/include/uapi/sound/compress_offload.h index 98772b0cbcb7..7e9190538df2 100644 --- a/include/uapi/sound/compress_offload.h +++ b/include/uapi/sound/compress_offload.h @@ -120,7 +120,7 @@ enum sndrv_compress_encoder {
/** * struct snd_compr_metadata - compressed stream metadata - * @key: key id + * @key: key id, value larger than 0x80000000 reserved for driver private usage * @value: key value */ struct snd_compr_metadata {
ASRC can be used on memory to memory case, define several functions for m2m usage.
m2m_prepare: prepare for the start step m2m_start: the start step m2m_unprepare: unprepare for stop step, optional m2m_stop: stop step m2m_check_format: check format is supported or not m2m_calc_out_len: calculate output length according to input length m2m_get_maxburst: burst size for dma m2m_pair_suspend: suspend function of pair, optional. m2m_pair_resume: resume function of pair get_output_fifo_size: get remaining data size in FIFO
Signed-off-by: Shengjiu Wang shengjiu.wang@nxp.com --- sound/soc/fsl/fsl_asrc.c | 139 ++++++++++++++++++++++++++++++++ sound/soc/fsl/fsl_asrc.h | 2 + sound/soc/fsl/fsl_asrc_common.h | 59 ++++++++++++++ 3 files changed, 200 insertions(+)
diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index b793263291dc..01e3af5b1bea 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -1063,6 +1063,136 @@ static int fsl_asrc_get_fifo_addr(u8 dir, enum asrc_pair_index index) return REG_ASRDx(dir, index); }
+/* Get sample numbers in FIFO */ +static unsigned int fsl_asrc_get_output_fifo_size(struct fsl_asrc_pair *pair) +{ + struct fsl_asrc *asrc = pair->asrc; + enum asrc_pair_index index = pair->index; + u32 val; + + regmap_read(asrc->regmap, REG_ASRFST(index), &val); + + val &= ASRFSTi_OUTPUT_FIFO_MASK; + + return val >> ASRFSTi_OUTPUT_FIFO_SHIFT; +} + +static int fsl_asrc_m2m_prepare(struct fsl_asrc_pair *pair) +{ + struct fsl_asrc_pair_priv *pair_priv = pair->private; + struct fsl_asrc *asrc = pair->asrc; + struct device *dev = &asrc->pdev->dev; + struct asrc_config config; + int ret; + + /* fill config */ + config.pair = pair->index; + config.channel_num = pair->channels; + config.input_sample_rate = pair->rate[IN]; + config.output_sample_rate = pair->rate[OUT]; + config.input_format = pair->sample_format[IN]; + config.output_format = pair->sample_format[OUT]; + config.inclk = INCLK_NONE; + config.outclk = OUTCLK_ASRCK1_CLK; + + pair_priv->config = &config; + ret = fsl_asrc_config_pair(pair, true); + if (ret) { + dev_err(dev, "failed to config pair: %d\n", ret); + return ret; + } + + pair->first_convert = 1; + + return 0; +} + +static int fsl_asrc_m2m_start(struct fsl_asrc_pair *pair) +{ + if (pair->first_convert) { + fsl_asrc_start_pair(pair); + pair->first_convert = 0; + } + /* + * Clear DMA request during the stall state of ASRC: + * During STALL state, the remaining in input fifo would never be + * smaller than the input threshold while the output fifo would not + * be bigger than output one. Thus the DMA request would be cleared. + */ + fsl_asrc_set_watermarks(pair, ASRC_FIFO_THRESHOLD_MIN, + ASRC_FIFO_THRESHOLD_MAX); + + /* Update the real input threshold to raise DMA request */ + fsl_asrc_set_watermarks(pair, ASRC_M2M_INPUTFIFO_WML, + ASRC_M2M_OUTPUTFIFO_WML); + + return 0; +} + +static int fsl_asrc_m2m_stop(struct fsl_asrc_pair *pair) +{ + if (!pair->first_convert) { + fsl_asrc_stop_pair(pair); + pair->first_convert = 1; + } + + return 0; +} + +/* calculate capture data length according to output data length and sample rate */ +static int fsl_asrc_m2m_calc_out_len(struct fsl_asrc_pair *pair, int input_buffer_length) +{ + unsigned int in_width, out_width; + unsigned int channels = pair->channels; + unsigned int in_samples, out_samples; + unsigned int out_length; + + in_width = snd_pcm_format_physical_width(pair->sample_format[IN]) / 8; + out_width = snd_pcm_format_physical_width(pair->sample_format[OUT]) / 8; + + in_samples = input_buffer_length / in_width / channels; + out_samples = pair->rate[OUT] * in_samples / pair->rate[IN]; + out_length = (out_samples - ASRC_OUTPUT_LAST_SAMPLE) * out_width * channels; + + return out_length; +} + +static int fsl_asrc_m2m_get_maxburst(u8 dir, struct fsl_asrc_pair *pair) +{ + struct fsl_asrc *asrc = pair->asrc; + struct fsl_asrc_priv *asrc_priv = asrc->private; + int wml = (dir == IN) ? ASRC_M2M_INPUTFIFO_WML : ASRC_M2M_OUTPUTFIFO_WML; + + if (!asrc_priv->soc->use_edma) + return wml * pair->channels; + else + return 1; +} + +static int fsl_asrc_m2m_get_cap(struct fsl_asrc_m2m_cap *cap) +{ + cap->fmt_in = FSL_ASRC_FORMATS; + cap->fmt_out = FSL_ASRC_FORMATS | SNDRV_PCM_FMTBIT_S8; + cap->rate_in = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_5512; + cap->rate_out = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_5512; + cap->chan_min = 1; + cap->chan_max = 10; + + return 0; +} + +static int fsl_asrc_m2m_pair_resume(struct fsl_asrc_pair *pair) +{ + struct fsl_asrc *asrc = pair->asrc; + int i; + + for (i = 0; i < pair->channels * 4; i++) + regmap_write(asrc->regmap, REG_ASRDI(pair->index), 0); + + pair->first_convert = 1; + return 0; +} + static int fsl_asrc_runtime_resume(struct device *dev); static int fsl_asrc_runtime_suspend(struct device *dev);
@@ -1147,6 +1277,15 @@ static int fsl_asrc_probe(struct platform_device *pdev) asrc->get_fifo_addr = fsl_asrc_get_fifo_addr; asrc->pair_priv_size = sizeof(struct fsl_asrc_pair_priv);
+ asrc->m2m_prepare = fsl_asrc_m2m_prepare; + asrc->m2m_start = fsl_asrc_m2m_start; + asrc->m2m_stop = fsl_asrc_m2m_stop; + asrc->get_output_fifo_size = fsl_asrc_get_output_fifo_size; + asrc->m2m_calc_out_len = fsl_asrc_m2m_calc_out_len; + asrc->m2m_get_maxburst = fsl_asrc_m2m_get_maxburst; + asrc->m2m_pair_resume = fsl_asrc_m2m_pair_resume; + asrc->m2m_get_cap = fsl_asrc_m2m_get_cap; + if (of_device_is_compatible(np, "fsl,imx35-asrc")) { asrc_priv->clk_map[IN] = input_clk_map_imx35; asrc_priv->clk_map[OUT] = output_clk_map_imx35; diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h index 86d2422ad606..1c492eb237f5 100644 --- a/sound/soc/fsl/fsl_asrc.h +++ b/sound/soc/fsl/fsl_asrc.h @@ -12,6 +12,8 @@
#include "fsl_asrc_common.h"
+#define ASRC_M2M_INPUTFIFO_WML 0x4 +#define ASRC_M2M_OUTPUTFIFO_WML 0x2 #define ASRC_DMA_BUFFER_NUM 2 #define ASRC_INPUTFIFO_THRESHOLD 32 #define ASRC_OUTPUTFIFO_THRESHOLD 32 diff --git a/sound/soc/fsl/fsl_asrc_common.h b/sound/soc/fsl/fsl_asrc_common.h index 7e1c13ca37f1..91dad736a969 100644 --- a/sound/soc/fsl/fsl_asrc_common.h +++ b/sound/soc/fsl/fsl_asrc_common.h @@ -21,6 +21,24 @@ enum asrc_pair_index {
#define PAIR_CTX_NUM 0x4
+/** + * struct fsl_asrc_m2m_cap - capability data + * @fmt_in: input sample format + * @fmt_out: output sample format + * @chan_min: minimum channel number + * @chan_max: maximum channel number + * @rate_in: minimum rate + * @rate_out: maximum rete + */ +struct fsl_asrc_m2m_cap { + u64 fmt_in; + u64 fmt_out; + int chan_min; + int chan_max; + int rate_in; + int rate_out; +}; + /** * fsl_asrc_pair: ASRC Pair common data * @@ -34,6 +52,13 @@ enum asrc_pair_index { * @pos: hardware pointer position * @req_dma_chan: flag to release dev_to_dev chan * @private: pair private area + * @complete: dma task complete + * @sample_format: format of m2m + * @rate: rate of m2m + * @buf_len: buffer length of m2m + * @dma_buffer: buffer pointers + * @first_convert: start of conversion + * @ratio_mod: ratio modification */ struct fsl_asrc_pair { struct fsl_asrc *asrc; @@ -49,6 +74,15 @@ struct fsl_asrc_pair { bool req_dma_chan;
void *private; + + /* used for m2m */ + struct completion complete[2]; + snd_pcm_format_t sample_format[2]; + unsigned int rate[2]; + unsigned int buf_len[2]; + struct snd_dma_buffer dma_buffer[2]; + unsigned int first_convert; + unsigned int ratio_mod; };
/** @@ -72,6 +106,17 @@ struct fsl_asrc_pair { * @request_pair: function pointer * @release_pair: function pointer * @get_fifo_addr: function pointer + * @m2m_get_cap: function pointer + * @m2m_prepare: function pointer + * @m2m_start: function pointer + * @m2m_unprepare: function pointer + * @m2m_stop: function pointer + * @m2m_calc_out_len: function pointer + * @m2m_get_maxburst: function pointer + * @m2m_pair_suspend: function pointer + * @m2m_pair_resume: function pointer + * @m2m_set_ratio_mod: function pointer + * @get_output_fifo_size: function pointer * @pair_priv_size: size of pair private struct. * @private: private data structure */ @@ -97,6 +142,20 @@ struct fsl_asrc { int (*request_pair)(int channels, struct fsl_asrc_pair *pair); void (*release_pair)(struct fsl_asrc_pair *pair); int (*get_fifo_addr)(u8 dir, enum asrc_pair_index index); + int (*m2m_get_cap)(struct fsl_asrc_m2m_cap *cap); + + int (*m2m_prepare)(struct fsl_asrc_pair *pair); + int (*m2m_start)(struct fsl_asrc_pair *pair); + int (*m2m_unprepare)(struct fsl_asrc_pair *pair); + int (*m2m_stop)(struct fsl_asrc_pair *pair); + + int (*m2m_calc_out_len)(struct fsl_asrc_pair *pair, int input_buffer_length); + int (*m2m_get_maxburst)(u8 dir, struct fsl_asrc_pair *pair); + int (*m2m_pair_suspend)(struct fsl_asrc_pair *pair); + int (*m2m_pair_resume)(struct fsl_asrc_pair *pair); + int (*m2m_set_ratio_mod)(struct fsl_asrc_pair *pair, int val); + + unsigned int (*get_output_fifo_size)(struct fsl_asrc_pair *pair); size_t pair_priv_size;
void *private;
ASRC can be used on memory to memory case, define several functions for m2m usage and export them as function pointer.
Signed-off-by: Shengjiu Wang shengjiu.wang@nxp.com --- sound/soc/fsl/fsl_easrc.c | 226 ++++++++++++++++++++++++++++++++++++++ sound/soc/fsl/fsl_easrc.h | 4 + 2 files changed, 230 insertions(+)
diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c index 962f30912091..959a8e2dd716 100644 --- a/sound/soc/fsl/fsl_easrc.c +++ b/sound/soc/fsl/fsl_easrc.c @@ -1861,6 +1861,222 @@ static int fsl_easrc_get_fifo_addr(u8 dir, enum asrc_pair_index index) return REG_EASRC_FIFO(dir, index); }
+/* Get sample numbers in FIFO */ +static unsigned int fsl_easrc_get_output_fifo_size(struct fsl_asrc_pair *pair) +{ + struct fsl_asrc *asrc = pair->asrc; + enum asrc_pair_index index = pair->index; + u32 val; + + regmap_read(asrc->regmap, REG_EASRC_SFS(index), &val); + val &= EASRC_SFS_NSGO_MASK; + + return val >> EASRC_SFS_NSGO_SHIFT; +} + +static int fsl_easrc_m2m_prepare(struct fsl_asrc_pair *pair) +{ + struct fsl_easrc_ctx_priv *ctx_priv = pair->private; + struct fsl_asrc *asrc = pair->asrc; + struct device *dev = &asrc->pdev->dev; + int ret; + + ctx_priv->in_params.sample_rate = pair->rate[IN]; + ctx_priv->in_params.sample_format = pair->sample_format[IN]; + ctx_priv->out_params.sample_rate = pair->rate[OUT]; + ctx_priv->out_params.sample_format = pair->sample_format[OUT]; + + ctx_priv->in_params.fifo_wtmk = FSL_EASRC_INPUTFIFO_WML; + ctx_priv->out_params.fifo_wtmk = FSL_EASRC_OUTPUTFIFO_WML; + /* Fill the right half of the re-sampler with zeros */ + ctx_priv->rs_init_mode = 0x2; + /* Zero fill the right half of the prefilter */ + ctx_priv->pf_init_mode = 0x2; + + ret = fsl_easrc_set_ctx_format(pair, + &ctx_priv->in_params.sample_format, + &ctx_priv->out_params.sample_format); + if (ret) { + dev_err(dev, "failed to set context format: %d\n", ret); + return ret; + } + + ret = fsl_easrc_config_context(asrc, pair->index); + if (ret) { + dev_err(dev, "failed to config context %d\n", ret); + return ret; + } + + ctx_priv->in_params.iterations = 1; + ctx_priv->in_params.group_len = pair->channels; + ctx_priv->in_params.access_len = pair->channels; + ctx_priv->out_params.iterations = 1; + ctx_priv->out_params.group_len = pair->channels; + ctx_priv->out_params.access_len = pair->channels; + + ret = fsl_easrc_set_ctx_organziation(pair); + if (ret) { + dev_err(dev, "failed to set fifo organization\n"); + return ret; + } + + /* The context start flag */ + pair->first_convert = 1; + return 0; +} + +static int fsl_easrc_m2m_start(struct fsl_asrc_pair *pair) +{ + /* start context once */ + if (pair->first_convert) { + fsl_easrc_start_context(pair); + pair->first_convert = 0; + } + + return 0; +} + +static int fsl_easrc_m2m_stop(struct fsl_asrc_pair *pair) +{ + /* Stop pair/context */ + if (!pair->first_convert) { + fsl_easrc_stop_context(pair); + pair->first_convert = 1; + } + + return 0; +} + +/* calculate capture data length according to output data length and sample rate */ +static int fsl_easrc_m2m_calc_out_len(struct fsl_asrc_pair *pair, int input_buffer_length) +{ + struct fsl_asrc *easrc = pair->asrc; + struct fsl_easrc_priv *easrc_priv = easrc->private; + struct fsl_easrc_ctx_priv *ctx_priv = pair->private; + unsigned int in_rate = ctx_priv->in_params.norm_rate; + unsigned int out_rate = ctx_priv->out_params.norm_rate; + unsigned int channels = pair->channels; + unsigned int in_samples, out_samples; + unsigned int in_width, out_width; + unsigned int out_length; + unsigned int frac_bits; + u64 val1, val2; + + switch (easrc_priv->rs_num_taps) { + case EASRC_RS_32_TAPS: + /* integer bits = 5; */ + frac_bits = 39; + break; + case EASRC_RS_64_TAPS: + /* integer bits = 6; */ + frac_bits = 38; + break; + case EASRC_RS_128_TAPS: + /* integer bits = 7; */ + frac_bits = 37; + break; + default: + return -EINVAL; + } + + val1 = (u64)in_rate << frac_bits; + do_div(val1, out_rate); + val1 += (s64)ctx_priv->ratio_mod << (frac_bits - 31); + + in_width = snd_pcm_format_physical_width(ctx_priv->in_params.sample_format) / 8; + out_width = snd_pcm_format_physical_width(ctx_priv->out_params.sample_format) / 8; + + ctx_priv->in_filled_len += input_buffer_length; + if (ctx_priv->in_filled_len <= ctx_priv->in_filled_sample * in_width * channels) { + out_length = 0; + } else { + in_samples = ctx_priv->in_filled_len / (in_width * channels) - + ctx_priv->in_filled_sample; + + /* right shift 12 bit to make ratio in 32bit space */ + val2 = (u64)in_samples << (frac_bits - 12); + val1 = val1 >> 12; + do_div(val2, val1); + out_samples = val2; + + out_length = out_samples * out_width * channels; + ctx_priv->in_filled_len = ctx_priv->in_filled_sample * in_width * channels; + } + + return out_length; +} + +static int fsl_easrc_m2m_get_maxburst(u8 dir, struct fsl_asrc_pair *pair) +{ + struct fsl_easrc_ctx_priv *ctx_priv = pair->private; + + if (dir == IN) + return ctx_priv->in_params.fifo_wtmk * pair->channels; + else + return ctx_priv->out_params.fifo_wtmk * pair->channels; +} + +static int fsl_easrc_m2m_pair_suspend(struct fsl_asrc_pair *pair) +{ + fsl_easrc_stop_context(pair); + + return 0; +} + +static int fsl_easrc_m2m_pair_resume(struct fsl_asrc_pair *pair) +{ + struct fsl_easrc_ctx_priv *ctx_priv = pair->private; + + pair->first_convert = 1; + ctx_priv->in_filled_len = 0; + + return 0; +} + +/* val is Q31 */ +static int fsl_easrc_m2m_set_ratio_mod(struct fsl_asrc_pair *pair, int val) +{ + struct fsl_easrc_ctx_priv *ctx_priv = pair->private; + struct fsl_asrc *easrc = pair->asrc; + struct fsl_easrc_priv *easrc_priv = easrc->private; + unsigned int frac_bits; + + ctx_priv->ratio_mod += val; + + switch (easrc_priv->rs_num_taps) { + case EASRC_RS_32_TAPS: + /* integer bits = 5; */ + frac_bits = 39; + break; + case EASRC_RS_64_TAPS: + /* integer bits = 6; */ + frac_bits = 38; + break; + case EASRC_RS_128_TAPS: + /* integer bits = 7; */ + frac_bits = 37; + break; + default: + return -EINVAL; + } + + val <<= (frac_bits - 31); + regmap_write(easrc->regmap, REG_EASRC_RUC(pair->index), EASRC_RSUC_RS_RM(val)); + + return 0; +} + +static int fsl_easrc_m2m_get_cap(struct fsl_asrc_m2m_cap *cap) +{ + cap->fmt_in = FSL_EASRC_FORMATS; + cap->fmt_out = FSL_EASRC_FORMATS | SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE; + cap->rate_in = SNDRV_PCM_RATE_8000_768000; + cap->rate_out = SNDRV_PCM_RATE_8000_768000; + cap->chan_min = 1; + cap->chan_max = 32; + return 0; +} + static const struct of_device_id fsl_easrc_dt_ids[] = { { .compatible = "fsl,imx8mn-easrc",}, {} @@ -1926,6 +2142,16 @@ static int fsl_easrc_probe(struct platform_device *pdev) easrc->release_pair = fsl_easrc_release_context; easrc->get_fifo_addr = fsl_easrc_get_fifo_addr; easrc->pair_priv_size = sizeof(struct fsl_easrc_ctx_priv); + easrc->m2m_prepare = fsl_easrc_m2m_prepare; + easrc->m2m_start = fsl_easrc_m2m_start; + easrc->m2m_stop = fsl_easrc_m2m_stop; + easrc->get_output_fifo_size = fsl_easrc_get_output_fifo_size; + easrc->m2m_calc_out_len = fsl_easrc_m2m_calc_out_len; + easrc->m2m_get_maxburst = fsl_easrc_m2m_get_maxburst; + easrc->m2m_pair_suspend = fsl_easrc_m2m_pair_suspend; + easrc->m2m_pair_resume = fsl_easrc_m2m_pair_resume; + easrc->m2m_set_ratio_mod = fsl_easrc_m2m_set_ratio_mod; + easrc->m2m_get_cap = fsl_easrc_m2m_get_cap;
easrc_priv->rs_num_taps = EASRC_RS_32_TAPS; easrc_priv->const_coeff = 0x3FF0000000000000; diff --git a/sound/soc/fsl/fsl_easrc.h b/sound/soc/fsl/fsl_easrc.h index 7c70dac52713..c9f770862662 100644 --- a/sound/soc/fsl/fsl_easrc.h +++ b/sound/soc/fsl/fsl_easrc.h @@ -601,6 +601,8 @@ struct fsl_easrc_slot { * @out_missed_sample: sample missed in output * @st1_addexp: exponent added for stage1 * @st2_addexp: exponent added for stage2 + * @ratio_mod: update ratio + * @in_filled_len: input filled length */ struct fsl_easrc_ctx_priv { struct fsl_easrc_io_params in_params; @@ -618,6 +620,8 @@ struct fsl_easrc_ctx_priv { int out_missed_sample; int st1_addexp; int st2_addexp; + int ratio_mod; + unsigned int in_filled_len; };
/**
Implement the ASRC memory to memory function using the compress framework, user can use this function with compress ioctl interface.
Define below private metadata key value for output format, output rate and ratio modifier configuration. ASRC_OUTPUT_FORMAT 0x80000001 ASRC_OUTPUT_RATE 0x80000002 ASRC_RATIO_MOD 0x80000003
This feature can be shared by ASRC and EASRC drivers
Signed-off-by: Shengjiu Wang shengjiu.wang@nxp.com --- sound/soc/fsl/Kconfig | 1 + sound/soc/fsl/Makefile | 2 +- sound/soc/fsl/fsl_asrc_common.h | 9 + sound/soc/fsl/fsl_asrc_m2m.c | 791 ++++++++++++++++++++++++++++++++ 4 files changed, 802 insertions(+), 1 deletion(-) create mode 100644 sound/soc/fsl/fsl_asrc_m2m.c
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index e283751abfef..bff9c6bda344 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -8,6 +8,7 @@ config SND_SOC_FSL_ASRC depends on HAS_DMA select REGMAP_MMIO select SND_SOC_GENERIC_DMAENGINE_PCM + select SND_COMPRESS_ACCEL help Say Y if you want to add Asynchronous Sample Rate Converter (ASRC) support for the Freescale CPUs. diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index ad97244b5cc3..d656a9ab54e3 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -10,7 +10,7 @@ obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o # Freescale SSI/DMA/SAI/SPDIF Support snd-soc-fsl-audmix-y := fsl_audmix.o snd-soc-fsl-asoc-card-y := fsl-asoc-card.o -snd-soc-fsl-asrc-y := fsl_asrc.o fsl_asrc_dma.o +snd-soc-fsl-asrc-y := fsl_asrc.o fsl_asrc_dma.o fsl_asrc_m2m.o snd-soc-fsl-lpc3xxx-y := lpc3xxx-pcm.o lpc3xxx-i2s.o snd-soc-fsl-sai-y := fsl_sai.o snd-soc-fsl-ssi-y := fsl_ssi.o diff --git a/sound/soc/fsl/fsl_asrc_common.h b/sound/soc/fsl/fsl_asrc_common.h index 91dad736a969..63b4bee370e3 100644 --- a/sound/soc/fsl/fsl_asrc_common.h +++ b/sound/soc/fsl/fsl_asrc_common.h @@ -58,6 +58,7 @@ struct fsl_asrc_m2m_cap { * @buf_len: buffer length of m2m * @dma_buffer: buffer pointers * @first_convert: start of conversion + * @ratio_mod_flag: flag for new ratio modifier * @ratio_mod: ratio modification */ struct fsl_asrc_pair { @@ -82,6 +83,7 @@ struct fsl_asrc_pair { unsigned int buf_len[2]; struct snd_dma_buffer dma_buffer[2]; unsigned int first_convert; + bool ratio_mod_flag; unsigned int ratio_mod; };
@@ -96,6 +98,7 @@ struct fsl_asrc_pair { * @mem_clk: clock source to access register * @ipg_clk: clock source to drive peripheral * @spba_clk: SPBA clock (optional, depending on SoC design) + * @card: compress sound card * @lock: spin lock for resource protection * @pair: pair pointers * @channel_avail: non-occupied channel numbers @@ -129,6 +132,7 @@ struct fsl_asrc { struct clk *mem_clk; struct clk *ipg_clk; struct clk *spba_clk; + struct snd_card *card; spinlock_t lock; /* spin lock for resource protection */
struct fsl_asrc_pair *pair[PAIR_CTX_NUM]; @@ -164,4 +168,9 @@ struct fsl_asrc { #define DRV_NAME "fsl-asrc-dai" extern struct snd_soc_component_driver fsl_asrc_component;
+int fsl_asrc_m2m_init(struct fsl_asrc *asrc); +void fsl_asrc_m2m_exit(struct fsl_asrc *asrc); +int fsl_asrc_m2m_resume(struct fsl_asrc *asrc); +int fsl_asrc_m2m_suspend(struct fsl_asrc *asrc); + #endif /* _FSL_ASRC_COMMON_H */ diff --git a/sound/soc/fsl/fsl_asrc_m2m.c b/sound/soc/fsl/fsl_asrc_m2m.c new file mode 100644 index 000000000000..df09b3cce232 --- /dev/null +++ b/sound/soc/fsl/fsl_asrc_m2m.c @@ -0,0 +1,791 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (C) 2014-2016 Freescale Semiconductor, Inc. +// Copyright (C) 2019-2024 NXP +// +// Freescale ASRC Memory to Memory (M2M) driver + +#include <linux/dma/imx-dma.h> +#include <linux/dma-buf.h> +#include <linux/dma-mapping.h> +#include <linux/pm_runtime.h> +#include <sound/asound.h> +#include <sound/dmaengine_pcm.h> +#include <sound/initval.h> + +#include "fsl_asrc_common.h" + +#define DIR_STR(dir) (dir) == IN ? "in" : "out" + +#define ASRC_xPUT_DMA_CALLBACK(dir) \ + (((dir) == IN) ? asrc_input_dma_callback \ + : asrc_output_dma_callback) + +/* Maximum output and capture buffer size */ +#define ASRC_M2M_BUFFER_SIZE (512 * 1024) + +/* Maximum output and capture period size */ +#define ASRC_M2M_PERIOD_SIZE (48 * 1024) + +#define ASRC_OUTPUT_FORMAT 0x80000001 +#define ASRC_OUTPUT_RATE 0x80000002 +#define ASRC_RATIO_MOD 0x80000003 + +/* dma complete callback */ +static void asrc_input_dma_callback(void *data) +{ + struct fsl_asrc_pair *pair = (struct fsl_asrc_pair *)data; + + complete(&pair->complete[IN]); +} + +/* dma complete callback */ +static void asrc_output_dma_callback(void *data) +{ + struct fsl_asrc_pair *pair = (struct fsl_asrc_pair *)data; + + complete(&pair->complete[OUT]); +} + +/** + *asrc_read_last_fifo: read all the remaining data from FIFO + *@pair: Structure pointer of fsl_asrc_pair + *@dma_vaddr: virtual address of capture buffer + *@length: payload length of capture buffer + */ +static void asrc_read_last_fifo(struct fsl_asrc_pair *pair, void *dma_vaddr, u32 *length) +{ + struct fsl_asrc *asrc = pair->asrc; + enum asrc_pair_index index = pair->index; + u32 i, reg, size, t_size = 0, width; + u32 *reg32 = NULL; + u16 *reg16 = NULL; + u8 *reg24 = NULL; + + width = snd_pcm_format_physical_width(pair->sample_format[OUT]); + if (width == 32) + reg32 = dma_vaddr + *length; + else if (width == 16) + reg16 = dma_vaddr + *length; + else + reg24 = dma_vaddr + *length; +retry: + size = asrc->get_output_fifo_size(pair); + if (size + *length > ASRC_M2M_BUFFER_SIZE) + goto end; + + for (i = 0; i < size * pair->channels; i++) { + regmap_read(asrc->regmap, asrc->get_fifo_addr(OUT, index), ®); + if (reg32) { + *reg32++ = reg; + } else if (reg16) { + *reg16++ = (u16)reg; + } else { + *reg24++ = (u8)reg; + *reg24++ = (u8)(reg >> 8); + *reg24++ = (u8)(reg >> 16); + } + } + t_size += size; + + /* In case there is data left in FIFO */ + if (size) + goto retry; +end: + /* Update payload length */ + if (reg32) + *length += t_size * pair->channels * 4; + else if (reg16) + *length += t_size * pair->channels * 2; + else + *length += t_size * pair->channels * 3; +} + +/* config dma channel */ +static int asrc_dmaconfig(struct fsl_asrc_pair *pair, + struct dma_chan *chan, + u32 dma_addr, dma_addr_t buf_addr, u32 buf_len, + int dir, int width) +{ + struct fsl_asrc *asrc = pair->asrc; + struct device *dev = &asrc->pdev->dev; + struct dma_slave_config slave_config; + enum dma_slave_buswidth buswidth; + unsigned int sg_len, max_period_size; + struct scatterlist *sg; + int ret, i; + + switch (width) { + case 8: + buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE; + break; + case 16: + buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; + break; + case 24: + buswidth = DMA_SLAVE_BUSWIDTH_3_BYTES; + break; + case 32: + buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES; + break; + default: + dev_err(dev, "invalid word width\n"); + return -EINVAL; + } + + memset(&slave_config, 0, sizeof(slave_config)); + if (dir == IN) { + slave_config.direction = DMA_MEM_TO_DEV; + slave_config.dst_addr = dma_addr; + slave_config.dst_addr_width = buswidth; + slave_config.dst_maxburst = asrc->m2m_get_maxburst(IN, pair); + } else { + slave_config.direction = DMA_DEV_TO_MEM; + slave_config.src_addr = dma_addr; + slave_config.src_addr_width = buswidth; + slave_config.src_maxburst = asrc->m2m_get_maxburst(OUT, pair); + } + + ret = dmaengine_slave_config(chan, &slave_config); + if (ret) { + dev_err(dev, "failed to config dmaengine for %s task: %d\n", + DIR_STR(dir), ret); + return -EINVAL; + } + + max_period_size = rounddown(ASRC_M2M_PERIOD_SIZE, width * pair->channels / 8); + /* scatter gather mode */ + sg_len = buf_len / max_period_size; + if (buf_len % max_period_size) + sg_len += 1; + + sg = kmalloc_array(sg_len, sizeof(*sg), GFP_KERNEL); + if (!sg) + return -ENOMEM; + + sg_init_table(sg, sg_len); + for (i = 0; i < (sg_len - 1); i++) { + sg_dma_address(&sg[i]) = buf_addr + i * max_period_size; + sg_dma_len(&sg[i]) = max_period_size; + } + sg_dma_address(&sg[i]) = buf_addr + i * max_period_size; + sg_dma_len(&sg[i]) = buf_len - i * max_period_size; + + pair->desc[dir] = dmaengine_prep_slave_sg(chan, sg, sg_len, + slave_config.direction, + DMA_PREP_INTERRUPT); + kfree(sg); + if (!pair->desc[dir]) { + dev_err(dev, "failed to prepare dmaengine for %s task\n", DIR_STR(dir)); + return -EINVAL; + } + + pair->desc[dir]->callback = ASRC_xPUT_DMA_CALLBACK(dir); + pair->desc[dir]->callback_param = pair; + + return 0; +} + +/* main function of converter */ +static void asrc_m2m_device_run(struct fsl_asrc_pair *pair, struct snd_compr_task_runtime *task) +{ + struct fsl_asrc *asrc = pair->asrc; + struct device *dev = &asrc->pdev->dev; + enum asrc_pair_index index = pair->index; + struct snd_dma_buffer *src_buf, *dst_buf; + unsigned int in_buf_len; + unsigned int out_dma_len; + unsigned int width; + u32 fifo_addr; + int ret; + + /* set ratio mod */ + if (asrc->m2m_set_ratio_mod) { + if (pair->ratio_mod_flag) { + asrc->m2m_set_ratio_mod(pair, pair->ratio_mod); + pair->ratio_mod_flag = false; + } + } + + src_buf = &pair->dma_buffer[IN]; + dst_buf = &pair->dma_buffer[OUT]; + + width = snd_pcm_format_physical_width(pair->sample_format[IN]); + fifo_addr = asrc->paddr + asrc->get_fifo_addr(IN, index); + + in_buf_len = task->input_size; + + if (in_buf_len < width * pair->channels / 8 || + in_buf_len > ASRC_M2M_BUFFER_SIZE || + in_buf_len % (width * pair->channels / 8)) { + dev_err(dev, "out buffer size is error: [%d]\n", in_buf_len); + goto end; + } + + /* dma config for output dma channel */ + ret = asrc_dmaconfig(pair, + pair->dma_chan[IN], + fifo_addr, + src_buf->addr, + in_buf_len, IN, width); + if (ret) { + dev_err(dev, "out dma config error\n"); + goto end; + } + + width = snd_pcm_format_physical_width(pair->sample_format[OUT]); + fifo_addr = asrc->paddr + asrc->get_fifo_addr(OUT, index); + out_dma_len = asrc->m2m_calc_out_len(pair, in_buf_len); + if (out_dma_len > 0 && out_dma_len <= ASRC_M2M_BUFFER_SIZE) { + /* dma config for capture dma channel */ + ret = asrc_dmaconfig(pair, + pair->dma_chan[OUT], + fifo_addr, + dst_buf->addr, + out_dma_len, OUT, width); + if (ret) { + dev_err(dev, "cap dma config error\n"); + goto end; + } + } else if (out_dma_len > ASRC_M2M_BUFFER_SIZE) { + dev_err(dev, "cap buffer size error\n"); + goto end; + } + + reinit_completion(&pair->complete[IN]); + reinit_completion(&pair->complete[OUT]); + + /* Submit DMA request */ + dmaengine_submit(pair->desc[IN]); + dma_async_issue_pending(pair->desc[IN]->chan); + if (out_dma_len > 0) { + dmaengine_submit(pair->desc[OUT]); + dma_async_issue_pending(pair->desc[OUT]->chan); + } + + asrc->m2m_start(pair); + + if (!wait_for_completion_interruptible_timeout(&pair->complete[IN], 10 * HZ)) { + dev_err(dev, "out DMA task timeout\n"); + goto end; + } + + if (out_dma_len > 0) { + if (!wait_for_completion_interruptible_timeout(&pair->complete[OUT], 10 * HZ)) { + dev_err(dev, "cap DMA task timeout\n"); + goto end; + } + } + + /* read the last words from FIFO */ + asrc_read_last_fifo(pair, dst_buf->area, &out_dma_len); + /* update payload length for capture */ + task->output_size = out_dma_len; +end: + return; +} + +static int fsl_asrc_m2m_comp_open(struct snd_compr_stream *stream) +{ + struct fsl_asrc *asrc = stream->private_data; + struct snd_compr_runtime *runtime = stream->runtime; + struct device *dev = &asrc->pdev->dev; + struct fsl_asrc_pair *pair; + int size, ret; + + pair = kzalloc(sizeof(*pair) + asrc->pair_priv_size, GFP_KERNEL); + if (!pair) + return -ENOMEM; + + pair->private = (void *)pair + sizeof(struct fsl_asrc_pair); + pair->asrc = asrc; + + init_completion(&pair->complete[IN]); + init_completion(&pair->complete[OUT]); + + runtime->private_data = pair; + + size = ASRC_M2M_BUFFER_SIZE; + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size, &pair->dma_buffer[IN]); + if (ret) + goto error_alloc_in_buf; + + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size, &pair->dma_buffer[OUT]); + if (ret) + goto error_alloc_out_buf; + + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "Failed to power up asrc\n"); + goto err_pm_runtime; + } + + return 0; + +err_pm_runtime: + snd_dma_free_pages(&pair->dma_buffer[OUT]); +error_alloc_out_buf: + snd_dma_free_pages(&pair->dma_buffer[IN]); +error_alloc_in_buf: + kfree(pair); + return ret; +} + +static int fsl_asrc_m2m_comp_release(struct snd_compr_stream *stream) +{ + struct fsl_asrc *asrc = stream->private_data; + struct snd_compr_runtime *runtime = stream->runtime; + struct fsl_asrc_pair *pair = runtime->private_data; + struct device *dev = &asrc->pdev->dev; + + pm_runtime_put_sync(dev); + + snd_dma_free_pages(&pair->dma_buffer[IN]); + snd_dma_free_pages(&pair->dma_buffer[OUT]); + + kfree(runtime->private_data); + + return 0; +} + +static int fsl_asrc_m2m_comp_set_params(struct snd_compr_stream *stream, + struct snd_compr_params *params) +{ + struct fsl_asrc *asrc = stream->private_data; + struct snd_compr_runtime *runtime = stream->runtime; + struct fsl_asrc_pair *pair = runtime->private_data; + struct fsl_asrc_m2m_cap cap; + int ret; + + ret = asrc->m2m_get_cap(&cap); + if (ret) + return -EINVAL; + + if (pcm_format_to_bits(params->codec.format) & cap.fmt_in) + pair->sample_format[IN] = params->codec.format; + else + return -EINVAL; + +// if (pcm_format_to_bits(params->codec.options.src.format_out) & cap.fmt_out) +// pair->sample_format[OUT] = params->codec.options.src.format_out; +// else +// return -EINVAL; + + if (snd_pcm_rate_to_rate_bit(params->codec.sample_rate) & cap.rate_in) + pair->rate[IN] = params->codec.sample_rate; + else + return -EINVAL; +// if (snd_pcm_rate_to_rate_bit(params->codec.options.src.rate_out) & cap.rate_out) +// pair->rate[OUT] = params->codec.options.src.rate_out; +// else +// return -EINVAL; + + if (params->codec.ch_in != params->codec.ch_out || + params->codec.ch_in < cap.chan_min || + params->codec.ch_in > cap.chan_max) + return -EINVAL; + + pair->channels = params->codec.ch_in; + pair->buf_len[IN] = params->buffer.fragment_size; + pair->buf_len[OUT] = params->buffer.fragment_size; + + return 0; +} + +static int fsl_asrc_m2m_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) +{ + struct snd_dma_buffer *dmab = dmabuf->priv; + + return snd_dma_buffer_mmap(dmab, vma); +} + +static struct sg_table *fsl_asrc_m2m_map_dma_buf(struct dma_buf_attachment *attachment, + enum dma_data_direction direction) +{ + struct snd_dma_buffer *dmab = attachment->dmabuf->priv; + struct sg_table *sgt; + + sgt = kmalloc(sizeof(*sgt), GFP_KERNEL); + if (!sgt) + return NULL; + + if (dma_get_sgtable(attachment->dev, sgt, dmab->area, dmab->addr, dmab->bytes) < 0) + goto free; + + if (dma_map_sgtable(attachment->dev, sgt, direction, 0)) + goto free; + + return sgt; + +free: + sg_free_table(sgt); + kfree(sgt); + return NULL; +} + +static void fsl_asrc_m2m_unmap_dma_buf(struct dma_buf_attachment *attachment, + struct sg_table *table, + enum dma_data_direction direction) +{ + dma_unmap_sgtable(attachment->dev, table, direction, 0); +} + +static void fsl_asrc_m2m_release(struct dma_buf *dmabuf) +{ + /* buffer is released by fsl_asrc_m2m_comp_release() */ +} + +static const struct dma_buf_ops fsl_asrc_m2m_dma_buf_ops = { + .mmap = fsl_asrc_m2m_mmap, + .map_dma_buf = fsl_asrc_m2m_map_dma_buf, + .unmap_dma_buf = fsl_asrc_m2m_unmap_dma_buf, + .release = fsl_asrc_m2m_release, +}; + +static int fsl_asrc_m2m_comp_task_create(struct snd_compr_stream *stream, + struct snd_compr_task_runtime *task) +{ + DEFINE_DMA_BUF_EXPORT_INFO(exp_info_in); + DEFINE_DMA_BUF_EXPORT_INFO(exp_info_out); + struct fsl_asrc *asrc = stream->private_data; + struct snd_compr_runtime *runtime = stream->runtime; + struct fsl_asrc_pair *pair = runtime->private_data; + struct device *dev = &asrc->pdev->dev; + int ret; + + exp_info_in.ops = &fsl_asrc_m2m_dma_buf_ops; + exp_info_in.size = ASRC_M2M_BUFFER_SIZE; + exp_info_in.flags = O_RDWR; + exp_info_in.priv = &pair->dma_buffer[IN]; + task->input = dma_buf_export(&exp_info_in); + if (IS_ERR(task->input)) { + ret = PTR_ERR(task->input); + return ret; + } + + exp_info_out.ops = &fsl_asrc_m2m_dma_buf_ops; + exp_info_out.size = ASRC_M2M_BUFFER_SIZE; + exp_info_out.flags = O_RDWR; + exp_info_out.priv = &pair->dma_buffer[OUT]; + task->output = dma_buf_export(&exp_info_out); + if (IS_ERR(task->output)) { + ret = PTR_ERR(task->output); + return ret; + } + + /* Request asrc pair/context */ + ret = asrc->request_pair(pair->channels, pair); + if (ret) { + dev_err(dev, "failed to request pair: %d\n", ret); + goto err_request_pair; + } + + ret = asrc->m2m_prepare(pair); + if (ret) { + dev_err(dev, "failed to start pair part one: %d\n", ret); + goto err_start_part_one; + } + + /* Request dma channels */ + pair->dma_chan[IN] = asrc->get_dma_channel(pair, IN); + if (!pair->dma_chan[IN]) { + dev_err(dev, "[ctx%d] failed to get input DMA channel\n", pair->index); + ret = -EBUSY; + goto err_dma_channel_in; + } + + pair->dma_chan[OUT] = asrc->get_dma_channel(pair, OUT); + if (!pair->dma_chan[OUT]) { + dev_err(dev, "[ctx%d] failed to get output DMA channel\n", pair->index); + ret = -EBUSY; + goto err_dma_channel_out; + } + + return 0; + +err_dma_channel_out: + dma_release_channel(pair->dma_chan[IN]); +err_dma_channel_in: + if (asrc->m2m_unprepare) + asrc->m2m_unprepare(pair); +err_start_part_one: + asrc->release_pair(pair); +err_request_pair: + return ret; +} + +static int fsl_asrc_m2m_comp_task_start(struct snd_compr_stream *stream, + struct snd_compr_task_runtime *task) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct fsl_asrc_pair *pair = runtime->private_data; + + asrc_m2m_device_run(pair, task); + + return 0; +} + +static int fsl_asrc_m2m_comp_task_stop(struct snd_compr_stream *stream, + struct snd_compr_task_runtime *task) +{ + return 0; +} + +static int fsl_asrc_m2m_comp_task_free(struct snd_compr_stream *stream, + struct snd_compr_task_runtime *task) +{ + struct fsl_asrc *asrc = stream->private_data; + struct snd_compr_runtime *runtime = stream->runtime; + struct fsl_asrc_pair *pair = runtime->private_data; + + /* Stop & release pair/context */ + if (asrc->m2m_stop) + asrc->m2m_stop(pair); + + if (asrc->m2m_unprepare) + asrc->m2m_unprepare(pair); + asrc->release_pair(pair); + + /* Release dma channel */ + if (pair->dma_chan[IN]) + dma_release_channel(pair->dma_chan[IN]); + if (pair->dma_chan[OUT]) + dma_release_channel(pair->dma_chan[OUT]); + + return 0; +} + +static int fsl_asrc_m2m_get_caps(struct snd_compr_stream *cstream, + struct snd_compr_caps *caps) +{ + caps->num_codecs = 1; + caps->min_fragment_size = 4096; + caps->max_fragment_size = 4096; + caps->min_fragments = 1; + caps->max_fragments = 1; + caps->codecs[0] = SND_AUDIOCODEC_PCM; + + return 0; +} + +static int fsl_asrc_m2m_fill_codec_caps(struct fsl_asrc *asrc, + struct snd_compr_codec_caps *codec) +{ + struct fsl_asrc_m2m_cap cap; + __u32 rates[MAX_NUM_BITRATES]; + snd_pcm_format_t k; + int i = 0, j = 0; + int ret; + + ret = asrc->m2m_get_cap(&cap); + if (ret) + return -EINVAL; + + if (cap.rate_in & SNDRV_PCM_RATE_5512) + rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_5512); + if (cap.rate_in & SNDRV_PCM_RATE_8000) + rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_8000); + if (cap.rate_in & SNDRV_PCM_RATE_11025) + rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_11025); + if (cap.rate_in & SNDRV_PCM_RATE_16000) + rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_16000); + if (cap.rate_in & SNDRV_PCM_RATE_22050) + rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_22050); + if (cap.rate_in & SNDRV_PCM_RATE_32000) + rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_32000); + if (cap.rate_in & SNDRV_PCM_RATE_44100) + rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_44100); + if (cap.rate_in & SNDRV_PCM_RATE_48000) + rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_48000); + if (cap.rate_in & SNDRV_PCM_RATE_88200) + rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_88200); + if (cap.rate_in & SNDRV_PCM_RATE_96000) + rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_96000); + if (cap.rate_in & SNDRV_PCM_RATE_176400) + rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_176400); + if (cap.rate_in & SNDRV_PCM_RATE_192000) + rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_192000); + if (cap.rate_in & SNDRV_PCM_RATE_352800) + rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_352800); + if (cap.rate_in & SNDRV_PCM_RATE_384000) + rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_384000); + if (cap.rate_in & SNDRV_PCM_RATE_705600) + rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_705600); + if (cap.rate_in & SNDRV_PCM_RATE_768000) + rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_768000); + + pcm_for_each_format(k) { + if (pcm_format_to_bits(k) & cap.fmt_in) { + codec->descriptor[j].max_ch = cap.chan_max; + memcpy(codec->descriptor[j].sample_rates, rates, i * sizeof(__u32)); + codec->descriptor[j].num_sample_rates = i; + codec->descriptor[j].formats = k; + j++; + } + } + + codec->codec = SND_AUDIOCODEC_PCM; + codec->num_descriptors = j; + return 0; +} + +static int fsl_asrc_m2m_get_codec_caps(struct snd_compr_stream *stream, + struct snd_compr_codec_caps *codec) +{ + struct fsl_asrc *asrc = stream->private_data; + + return fsl_asrc_m2m_fill_codec_caps(asrc, codec); +} + +static int fsl_asrc_m2m_comp_set_metadata(struct snd_compr_stream *stream, + struct snd_compr_metadata *metadata) +{ + struct fsl_asrc *asrc = stream->private_data; + struct snd_compr_runtime *runtime = stream->runtime; + struct fsl_asrc_pair *pair = runtime->private_data; + struct fsl_asrc_m2m_cap cap; + int ret; + + ret = asrc->m2m_get_cap(&cap); + if (ret) + return -EINVAL; + + switch (metadata->key) { + case ASRC_OUTPUT_FORMAT: + if (pcm_format_to_bits(metadata->value[0]) & cap.fmt_out) + pair->sample_format[OUT] = metadata->value[0]; + else + return -EINVAL; + break; + case ASRC_OUTPUT_RATE: + if (snd_pcm_rate_to_rate_bit(metadata->value[0]) & cap.rate_out) + pair->rate[OUT] = metadata->value[0]; + else + return -EINVAL; + break; + case ASRC_RATIO_MOD: + if (pair->ratio_mod_flag) + return -EINVAL; + pair->ratio_mod = metadata->value[0]; + pair->ratio_mod_flag = true; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static struct snd_compr_ops fsl_asrc_m2m_compr_ops = { + .open = fsl_asrc_m2m_comp_open, + .free = fsl_asrc_m2m_comp_release, + .set_params = fsl_asrc_m2m_comp_set_params, + .set_metadata = fsl_asrc_m2m_comp_set_metadata, + .get_caps = fsl_asrc_m2m_get_caps, + .get_codec_caps = fsl_asrc_m2m_get_codec_caps, + .task_create = fsl_asrc_m2m_comp_task_create, + .task_start = fsl_asrc_m2m_comp_task_start, + .task_stop = fsl_asrc_m2m_comp_task_stop, + .task_free = fsl_asrc_m2m_comp_task_free, +}; + +int fsl_asrc_m2m_suspend(struct fsl_asrc *asrc) +{ + struct fsl_asrc_pair *pair; + int i; + + for (i = 0; i < PAIR_CTX_NUM; i++) { + pair = asrc->pair[i]; + if (!pair) + continue; + if (!completion_done(&pair->complete[IN])) { + if (pair->dma_chan[IN]) + dmaengine_terminate_all(pair->dma_chan[IN]); + asrc_input_dma_callback((void *)pair); + } + if (!completion_done(&pair->complete[OUT])) { + if (pair->dma_chan[OUT]) + dmaengine_terminate_all(pair->dma_chan[OUT]); + asrc_output_dma_callback((void *)pair); + } + + if (asrc->m2m_pair_suspend) + asrc->m2m_pair_suspend(pair); + } + + return 0; +} +EXPORT_SYMBOL_GPL(fsl_asrc_m2m_suspend); + +int fsl_asrc_m2m_resume(struct fsl_asrc *asrc) +{ + struct fsl_asrc_pair *pair; + int i; + + for (i = 0; i < PAIR_CTX_NUM; i++) { + pair = asrc->pair[i]; + if (!pair) + continue; + if (asrc->m2m_pair_resume) + asrc->m2m_pair_resume(pair); + } + + return 0; +} +EXPORT_SYMBOL_GPL(fsl_asrc_m2m_resume); + +int fsl_asrc_m2m_init(struct fsl_asrc *asrc) +{ + struct device *dev = &asrc->pdev->dev; + struct snd_card *card; + struct snd_compr *compr; + int ret; + + ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, + THIS_MODULE, 0, &card); + if (ret < 0) + return ret; + + strscpy(card->driver, "fsl-asrc-m2m", sizeof(card->driver)); + strscpy(card->shortname, "ASRC-M2M", sizeof(card->shortname)); + strscpy(card->longname, "ASRC-M2M", sizeof(card->shortname)); + + asrc->card = card; + + compr = devm_kzalloc(dev, sizeof(*compr), GFP_KERNEL); + if (!compr) { + ret = -ENOMEM; + goto err; + } + + compr->ops = &fsl_asrc_m2m_compr_ops; + compr->private_data = asrc; + + ret = snd_compress_new(card, 0, SND_COMPRESS_ACCEL, "ASRC M2M", compr); + if (ret < 0) + goto err; + + ret = snd_card_register(card); + if (ret < 0) + goto err; + + return 0; +err: + snd_card_free(card); + return ret; +} +EXPORT_SYMBOL_GPL(fsl_asrc_m2m_init); + +void fsl_asrc_m2m_exit(struct fsl_asrc *asrc) +{ + struct snd_card *card = asrc->card; + + snd_card_free(card); +} +EXPORT_SYMBOL_GPL(fsl_asrc_m2m_exit); + +MODULE_IMPORT_NS(DMA_BUF); +MODULE_AUTHOR("Shengjiu Wang Shengjiu.Wang@nxp.com"); +MODULE_DESCRIPTION("Freescale ASRC M2M driver"); +MODULE_LICENSE("GPL");
On 8/16/24 12:42, Shengjiu Wang wrote:
Implement the ASRC memory to memory function using the compress framework, user can use this function with compress ioctl interface.
Define below private metadata key value for output format, output rate and ratio modifier configuration. ASRC_OUTPUT_FORMAT 0x80000001 ASRC_OUTPUT_RATE 0x80000002 ASRC_RATIO_MOD 0x80000003
Can the output format/rate change at run-time?
If no, then these parameters should be moved somewhere else - e.g. hw_params or something.
I am still not very clear on the expanding the SET_METADATA ioctl to deal with the ratio changes. This isn't linked to the control layer as suggested before, and there's no precedent of calling it multiple times during streaming.
I also wonder how it was tested since tinycompress does not support this?
+static int fsl_asrc_m2m_fill_codec_caps(struct fsl_asrc *asrc,
struct snd_compr_codec_caps *codec)
+{
- struct fsl_asrc_m2m_cap cap;
- __u32 rates[MAX_NUM_BITRATES];
- snd_pcm_format_t k;
- int i = 0, j = 0;
- int ret;
- ret = asrc->m2m_get_cap(&cap);
- if (ret)
return -EINVAL;
- if (cap.rate_in & SNDRV_PCM_RATE_5512)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_5512);
this doesn't sound compatible with the patch2 definitions?
cap->rate_in = SNDRV_PCM_RATE_8000_768000;
- if (cap.rate_in & SNDRV_PCM_RATE_8000)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_8000);
- if (cap.rate_in & SNDRV_PCM_RATE_11025)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_11025);
- if (cap.rate_in & SNDRV_PCM_RATE_16000)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_16000);
- if (cap.rate_in & SNDRV_PCM_RATE_22050)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_22050);
missing 24 kHz
- if (cap.rate_in & SNDRV_PCM_RATE_32000)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_32000);
- if (cap.rate_in & SNDRV_PCM_RATE_44100)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_44100);
- if (cap.rate_in & SNDRV_PCM_RATE_48000)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_48000);
missing 64kHz
- if (cap.rate_in & SNDRV_PCM_RATE_88200)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_88200);
- if (cap.rate_in & SNDRV_PCM_RATE_96000)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_96000);
- if (cap.rate_in & SNDRV_PCM_RATE_176400)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_176400);
- if (cap.rate_in & SNDRV_PCM_RATE_192000)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_192000);
- if (cap.rate_in & SNDRV_PCM_RATE_352800)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_352800);
- if (cap.rate_in & SNDRV_PCM_RATE_384000)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_384000);
- if (cap.rate_in & SNDRV_PCM_RATE_705600)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_705600);
- if (cap.rate_in & SNDRV_PCM_RATE_768000)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_768000);
- pcm_for_each_format(k) {
if (pcm_format_to_bits(k) & cap.fmt_in) {
codec->descriptor[j].max_ch = cap.chan_max;
memcpy(codec->descriptor[j].sample_rates, rates, i * sizeof(__u32));
codec->descriptor[j].num_sample_rates = i;
codec->descriptor[j].formats = k;
j++;
}
- }
- codec->codec = SND_AUDIOCODEC_PCM;
- codec->num_descriptors = j;
- return 0;
On Mon, Aug 19, 2024 at 3:42 PM Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com wrote:
On 8/16/24 12:42, Shengjiu Wang wrote:
Implement the ASRC memory to memory function using the compress framework, user can use this function with compress ioctl interface.
Define below private metadata key value for output format, output rate and ratio modifier configuration. ASRC_OUTPUT_FORMAT 0x80000001 ASRC_OUTPUT_RATE 0x80000002 ASRC_RATIO_MOD 0x80000003
Can the output format/rate change at run-time?
Seldom I think.
If no, then these parameters should be moved somewhere else - e.g. hw_params or something.
This means I will do some changes in compress_params.h, add output format and output rate definition, follow Jaroslav's example right?
I am still not very clear on the expanding the SET_METADATA ioctl to deal with the ratio changes. This isn't linked to the control layer as suggested before, and there's no precedent of calling it multiple times during streaming.
Which control layer? if you means the snd_kcontrol_new? it is bound with sound card, but in my case, I need to the control bind with the snd_compr_stream to support multi streams/instances.
I also wonder how it was tested since tinycompress does not support this?
I wrote a unit test to test these ASRC M2M functions.
+static int fsl_asrc_m2m_fill_codec_caps(struct fsl_asrc *asrc,
struct snd_compr_codec_caps *codec)
+{
struct fsl_asrc_m2m_cap cap;
__u32 rates[MAX_NUM_BITRATES];
snd_pcm_format_t k;
int i = 0, j = 0;
int ret;
ret = asrc->m2m_get_cap(&cap);
if (ret)
return -EINVAL;
if (cap.rate_in & SNDRV_PCM_RATE_5512)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_5512);
this doesn't sound compatible with the patch2 definitions?
cap->rate_in = SNDRV_PCM_RATE_8000_768000;
This ASRC M2M driver is designed for two kinds of hw ASRC modules.
one cap is : cap->rate_in = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_5512; another is : cap->rate_in = SNDRV_PCM_RATE_8000_768000; they are in patch2 and patch3
if (cap.rate_in & SNDRV_PCM_RATE_8000)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_8000);
if (cap.rate_in & SNDRV_PCM_RATE_11025)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_11025);
if (cap.rate_in & SNDRV_PCM_RATE_16000)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_16000);
if (cap.rate_in & SNDRV_PCM_RATE_22050)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_22050);
missing 24 kHz
There is no SNDRV_PCM_RATE_24000 in ALSA.
if (cap.rate_in & SNDRV_PCM_RATE_32000)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_32000);
if (cap.rate_in & SNDRV_PCM_RATE_44100)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_44100);
if (cap.rate_in & SNDRV_PCM_RATE_48000)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_48000);
missing 64kHz
Yes, will add it.
Best regards Shengjiu Wang
if (cap.rate_in & SNDRV_PCM_RATE_88200)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_88200);
if (cap.rate_in & SNDRV_PCM_RATE_96000)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_96000);
if (cap.rate_in & SNDRV_PCM_RATE_176400)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_176400);
if (cap.rate_in & SNDRV_PCM_RATE_192000)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_192000);
if (cap.rate_in & SNDRV_PCM_RATE_352800)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_352800);
if (cap.rate_in & SNDRV_PCM_RATE_384000)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_384000);
if (cap.rate_in & SNDRV_PCM_RATE_705600)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_705600);
if (cap.rate_in & SNDRV_PCM_RATE_768000)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_768000);
pcm_for_each_format(k) {
if (pcm_format_to_bits(k) & cap.fmt_in) {
codec->descriptor[j].max_ch = cap.chan_max;
memcpy(codec->descriptor[j].sample_rates, rates, i * sizeof(__u32));
codec->descriptor[j].num_sample_rates = i;
codec->descriptor[j].formats = k;
j++;
}
}
codec->codec = SND_AUDIOCODEC_PCM;
codec->num_descriptors = j;
return 0;
On 8/20/24 04:53, Shengjiu Wang wrote:
On Mon, Aug 19, 2024 at 3:42 PM Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com wrote:
On 8/16/24 12:42, Shengjiu Wang wrote:
Implement the ASRC memory to memory function using the compress framework, user can use this function with compress ioctl interface.
Define below private metadata key value for output format, output rate and ratio modifier configuration. ASRC_OUTPUT_FORMAT 0x80000001 ASRC_OUTPUT_RATE 0x80000002 ASRC_RATIO_MOD 0x80000003
Can the output format/rate change at run-time?
Seldom I think.
If no, then these parameters should be moved somewhere else - e.g. hw_params or something.
This means I will do some changes in compress_params.h, add output format and output rate definition, follow Jaroslav's example right?
yes, having parameters for the PCM case would make sense.
I am still not very clear on the expanding the SET_METADATA ioctl to deal with the ratio changes. This isn't linked to the control layer as suggested before, and there's no precedent of calling it multiple times during streaming.
Which control layer? if you means the snd_kcontrol_new? it is bound with sound card, but in my case, I need to the control bind with the snd_compr_stream to support multi streams/instances.
I can only quote Jaroslav's previous answer:
" This argument is not valid. The controls are bound to the card, but the element identifiers have already iface (interface), device and subdevice numbers. We are using controls for PCM devices for example. The binding is straight.
Just add SNDRV_CTL_ELEM_IFACE_COMPRESS define and specify the compress device number in the 'struct snd_ctl_elem_id'. "
I also wonder how it was tested since tinycompress does not support this?
I wrote a unit test to test these ASRC M2M functions.
This should be shared IMHO, usually when we add/extend a new interface it's best to have a userspace test program that can be used by others.
+static int fsl_asrc_m2m_fill_codec_caps(struct fsl_asrc *asrc,
struct snd_compr_codec_caps *codec)
+{
struct fsl_asrc_m2m_cap cap;
__u32 rates[MAX_NUM_BITRATES];
snd_pcm_format_t k;
int i = 0, j = 0;
int ret;
ret = asrc->m2m_get_cap(&cap);
if (ret)
return -EINVAL;
if (cap.rate_in & SNDRV_PCM_RATE_5512)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_5512);
this doesn't sound compatible with the patch2 definitions?
cap->rate_in = SNDRV_PCM_RATE_8000_768000;
This ASRC M2M driver is designed for two kinds of hw ASRC modules.
one cap is : cap->rate_in = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_5512; another is : cap->rate_in = SNDRV_PCM_RATE_8000_768000; they are in patch2 and patch3
if (cap.rate_in & SNDRV_PCM_RATE_8000)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_8000);
if (cap.rate_in & SNDRV_PCM_RATE_11025)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_11025);
if (cap.rate_in & SNDRV_PCM_RATE_16000)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_16000);
if (cap.rate_in & SNDRV_PCM_RATE_22050)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_22050);
missing 24 kHz
There is no SNDRV_PCM_RATE_24000 in ALSA.
Right, but that doesn't mean 24kHz cannot be supported. We use constraints in those cases. see quote from Takashi found with a 2s Google search
https://mailman.alsa-project.org/pipermail/alsa-devel/2013-November/069356.h...
" CONTINUOUS means that any rate between the specified min and max is fine, if no min or max is specified any rate is fine. KNOT means there are rates supported other than the standard rates defines by ALSA, but the other rates are enumerable. You'd typically specify them by explicitly listing them all and use a list constraint or you'd use one of the ratio constraints. "
if (cap.rate_in & SNDRV_PCM_RATE_32000)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_32000);
if (cap.rate_in & SNDRV_PCM_RATE_44100)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_44100);
if (cap.rate_in & SNDRV_PCM_RATE_48000)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_48000);
missing 64kHz
Yes, will add it.
On Tue, Aug 20, 2024 at 2:59 PM Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com wrote:
On 8/20/24 04:53, Shengjiu Wang wrote:
On Mon, Aug 19, 2024 at 3:42 PM Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com wrote:
On 8/16/24 12:42, Shengjiu Wang wrote:
Implement the ASRC memory to memory function using the compress framework, user can use this function with compress ioctl interface.
Define below private metadata key value for output format, output rate and ratio modifier configuration. ASRC_OUTPUT_FORMAT 0x80000001 ASRC_OUTPUT_RATE 0x80000002 ASRC_RATIO_MOD 0x80000003
Can the output format/rate change at run-time?
Seldom I think.
If no, then these parameters should be moved somewhere else - e.g. hw_params or something.
This means I will do some changes in compress_params.h, add output format and output rate definition, follow Jaroslav's example right?
yes, having parameters for the PCM case would make sense.
I am still not very clear on the expanding the SET_METADATA ioctl to deal with the ratio changes. This isn't linked to the control layer as suggested before, and there's no precedent of calling it multiple times during streaming.
Which control layer? if you means the snd_kcontrol_new? it is bound with sound card, but in my case, I need to the control bind with the snd_compr_stream to support multi streams/instances.
I can only quote Jaroslav's previous answer:
" This argument is not valid. The controls are bound to the card, but the element identifiers have already iface (interface), device and subdevice numbers. We are using controls for PCM devices for example. The binding is straight.
Just add SNDRV_CTL_ELEM_IFACE_COMPRESS define and specify the compress device number in the 'struct snd_ctl_elem_id'. "
I don't think it is doable, or at least I don't know how to do it.
My case is just one card/one device/one subdevice. can't use it to distinguish multi streams.
I also wonder how it was tested since tinycompress does not support this?
I wrote a unit test to test these ASRC M2M functions.
This should be shared IMHO, usually when we add/extend a new interface it's best to have a userspace test program that can be used by others.
After Jaroslav updates the tinycompress, I can update this example to it.
+static int fsl_asrc_m2m_fill_codec_caps(struct fsl_asrc *asrc,
struct snd_compr_codec_caps *codec)
+{
struct fsl_asrc_m2m_cap cap;
__u32 rates[MAX_NUM_BITRATES];
snd_pcm_format_t k;
int i = 0, j = 0;
int ret;
ret = asrc->m2m_get_cap(&cap);
if (ret)
return -EINVAL;
if (cap.rate_in & SNDRV_PCM_RATE_5512)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_5512);
this doesn't sound compatible with the patch2 definitions?
cap->rate_in = SNDRV_PCM_RATE_8000_768000;
This ASRC M2M driver is designed for two kinds of hw ASRC modules.
one cap is : cap->rate_in = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_5512; another is : cap->rate_in = SNDRV_PCM_RATE_8000_768000; they are in patch2 and patch3
if (cap.rate_in & SNDRV_PCM_RATE_8000)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_8000);
if (cap.rate_in & SNDRV_PCM_RATE_11025)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_11025);
if (cap.rate_in & SNDRV_PCM_RATE_16000)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_16000);
if (cap.rate_in & SNDRV_PCM_RATE_22050)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_22050);
missing 24 kHz
There is no SNDRV_PCM_RATE_24000 in ALSA.
Right, but that doesn't mean 24kHz cannot be supported. We use constraints in those cases. see quote from Takashi found with a 2s Google search
https://mailman.alsa-project.org/pipermail/alsa-devel/2013-November/069356.h...
" CONTINUOUS means that any rate between the specified min and max is fine, if no min or max is specified any rate is fine. KNOT means there are rates supported other than the standard rates defines by ALSA, but the other rates are enumerable. You'd typically specify them by explicitly listing them all and use a list constraint or you'd use one of the ratio constraints. "
if (cap.rate_in & SNDRV_PCM_RATE_32000)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_32000);
if (cap.rate_in & SNDRV_PCM_RATE_44100)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_44100);
if (cap.rate_in & SNDRV_PCM_RATE_48000)
rates[i++] = snd_pcm_rate_bit_to_rate(SNDRV_PCM_RATE_48000);
missing 64kHz
Yes, will add it.
On 20. 08. 24 9:37, Shengjiu Wang wrote:
On Tue, Aug 20, 2024 at 2:59 PM Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com wrote:
On 8/20/24 04:53, Shengjiu Wang wrote:
On Mon, Aug 19, 2024 at 3:42 PM Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com wrote:
On 8/16/24 12:42, Shengjiu Wang wrote:
Implement the ASRC memory to memory function using the compress framework, user can use this function with compress ioctl interface.
Define below private metadata key value for output format, output rate and ratio modifier configuration. ASRC_OUTPUT_FORMAT 0x80000001 ASRC_OUTPUT_RATE 0x80000002 ASRC_RATIO_MOD 0x80000003
Can the output format/rate change at run-time?
Seldom I think.
If no, then these parameters should be moved somewhere else - e.g. hw_params or something.
This means I will do some changes in compress_params.h, add output format and output rate definition, follow Jaroslav's example right?
yes, having parameters for the PCM case would make sense.
I am still not very clear on the expanding the SET_METADATA ioctl to deal with the ratio changes. This isn't linked to the control layer as suggested before, and there's no precedent of calling it multiple times during streaming.
Which control layer? if you means the snd_kcontrol_new? it is bound with sound card, but in my case, I need to the control bind with the snd_compr_stream to support multi streams/instances.
I can only quote Jaroslav's previous answer:
" This argument is not valid. The controls are bound to the card, but the element identifiers have already iface (interface), device and subdevice numbers. We are using controls for PCM devices for example. The binding is straight.
Just add SNDRV_CTL_ELEM_IFACE_COMPRESS define and specify the compress device number in the 'struct snd_ctl_elem_id'. "
I don't think it is doable, or at least I don't know how to do it.
My case is just one card/one device/one subdevice. can't use it to distinguish multi streams.
I already wrote that the compress core code should be extended to support subdevices like other ALSA APIs. I'll work on it. For now, just add support for one converter.
Jaroslav
On Tue, Aug 20, 2024 at 3:42 PM Jaroslav Kysela perex@perex.cz wrote:
On 20. 08. 24 9:37, Shengjiu Wang wrote:
On Tue, Aug 20, 2024 at 2:59 PM Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com wrote:
On 8/20/24 04:53, Shengjiu Wang wrote:
On Mon, Aug 19, 2024 at 3:42 PM Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com wrote:
On 8/16/24 12:42, Shengjiu Wang wrote:
Implement the ASRC memory to memory function using the compress framework, user can use this function with compress ioctl interface.
Define below private metadata key value for output format, output rate and ratio modifier configuration. ASRC_OUTPUT_FORMAT 0x80000001 ASRC_OUTPUT_RATE 0x80000002 ASRC_RATIO_MOD 0x80000003
Can the output format/rate change at run-time?
Seldom I think.
If no, then these parameters should be moved somewhere else - e.g. hw_params or something.
This means I will do some changes in compress_params.h, add output format and output rate definition, follow Jaroslav's example right?
yes, having parameters for the PCM case would make sense.
I am still not very clear on the expanding the SET_METADATA ioctl to deal with the ratio changes. This isn't linked to the control layer as suggested before, and there's no precedent of calling it multiple times during streaming.
Which control layer? if you means the snd_kcontrol_new? it is bound with sound card, but in my case, I need to the control bind with the snd_compr_stream to support multi streams/instances.
I can only quote Jaroslav's previous answer:
" This argument is not valid. The controls are bound to the card, but the element identifiers have already iface (interface), device and subdevice numbers. We are using controls for PCM devices for example. The binding is straight.
Just add SNDRV_CTL_ELEM_IFACE_COMPRESS define and specify the compress device number in the 'struct snd_ctl_elem_id'. "
I don't think it is doable, or at least I don't know how to do it.
My case is just one card/one device/one subdevice. can't use it to distinguish multi streams.
I already wrote that the compress core code should be extended to support subdevices like other ALSA APIs. I'll work on it. For now, just add support for one converter.
Thanks.
What does this subdevices mean? Is it equal to the compress streams?
When I call snd_compr_ops.open(), it means to create an instance, the instance is created at runtime (call open()), not created when the sound card is created.
Best regards Shengjiu Wang
Jaroslav
-- Jaroslav Kysela perex@perex.cz Linux Sound Maintainer; ALSA Project; Red Hat, Inc.
On 8/20/24 09:42, Jaroslav Kysela wrote:
On 20. 08. 24 9:37, Shengjiu Wang wrote:
On Tue, Aug 20, 2024 at 2:59 PM Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com wrote:
On 8/20/24 04:53, Shengjiu Wang wrote:
On Mon, Aug 19, 2024 at 3:42 PM Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com wrote:
On 8/16/24 12:42, Shengjiu Wang wrote:
Implement the ASRC memory to memory function using the compress framework, user can use this function with compress ioctl interface.
Define below private metadata key value for output format, output rate and ratio modifier configuration. ASRC_OUTPUT_FORMAT 0x80000001 ASRC_OUTPUT_RATE 0x80000002 ASRC_RATIO_MOD 0x80000003
Can the output format/rate change at run-time?
Seldom I think.
If no, then these parameters should be moved somewhere else - e.g. hw_params or something.
This means I will do some changes in compress_params.h, add output format and output rate definition, follow Jaroslav's example right?
yes, having parameters for the PCM case would make sense.
I am still not very clear on the expanding the SET_METADATA ioctl to deal with the ratio changes. This isn't linked to the control layer as suggested before, and there's no precedent of calling it multiple times during streaming.
Which control layer? if you means the snd_kcontrol_new? it is bound with sound card, but in my case, I need to the control bind with the snd_compr_stream to support multi streams/instances.
I can only quote Jaroslav's previous answer:
" This argument is not valid. The controls are bound to the card, but the element identifiers have already iface (interface), device and subdevice numbers. We are using controls for PCM devices for example. The binding is straight.
Just add SNDRV_CTL_ELEM_IFACE_COMPRESS define and specify the compress device number in the 'struct snd_ctl_elem_id'. "
I don't think it is doable, or at least I don't know how to do it.
My case is just one card/one device/one subdevice. can't use it to distinguish multi streams.
I already wrote that the compress core code should be extended to support subdevices like other ALSA APIs. I'll work on it. For now, just add support for one converter.
I am not sure I get the benefits of subdevices in this context.
Can we not use different devices already, one per hardware ASRC instance?
Put differently, what would be the difference between a card with N compressed devices or a card with 1 compressed device and N subdevices?
Register m2m platform device, that user can use M2M feature.
Signed-off-by: Shengjiu Wang shengjiu.wang@nxp.com --- sound/soc/fsl/fsl_asrc.c | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-)
diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index 01e3af5b1bea..5d88abd905a5 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -1381,6 +1381,12 @@ static int fsl_asrc_probe(struct platform_device *pdev) goto err_pm_get_sync; }
+ ret = fsl_asrc_m2m_init(asrc); + if (ret) { + dev_err(&pdev->dev, "failed to init m2m device %d\n", ret); + return ret; + } + return 0;
err_pm_get_sync: @@ -1393,6 +1399,10 @@ static int fsl_asrc_probe(struct platform_device *pdev)
static void fsl_asrc_remove(struct platform_device *pdev) { + struct fsl_asrc *asrc = dev_get_drvdata(&pdev->dev); + + fsl_asrc_m2m_exit(asrc); + pm_runtime_disable(&pdev->dev); if (!pm_runtime_status_suspended(&pdev->dev)) fsl_asrc_runtime_suspend(&pdev->dev); @@ -1494,10 +1504,29 @@ static int fsl_asrc_runtime_suspend(struct device *dev) return 0; }
+static int fsl_asrc_suspend(struct device *dev) +{ + struct fsl_asrc *asrc = dev_get_drvdata(dev); + int ret; + + fsl_asrc_m2m_suspend(asrc); + ret = pm_runtime_force_suspend(dev); + return ret; +} + +static int fsl_asrc_resume(struct device *dev) +{ + struct fsl_asrc *asrc = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_resume(dev); + fsl_asrc_m2m_resume(asrc); + return ret; +} + static const struct dev_pm_ops fsl_asrc_pm = { - SET_RUNTIME_PM_OPS(fsl_asrc_runtime_suspend, fsl_asrc_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + RUNTIME_PM_OPS(fsl_asrc_runtime_suspend, fsl_asrc_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(fsl_asrc_suspend, fsl_asrc_resume) };
static const struct fsl_asrc_soc_data fsl_asrc_imx35_data = { @@ -1535,7 +1564,7 @@ static struct platform_driver fsl_asrc_driver = { .driver = { .name = "fsl-asrc", .of_match_table = fsl_asrc_ids, - .pm = &fsl_asrc_pm, + .pm = pm_ptr(&fsl_asrc_pm), }, }; module_platform_driver(fsl_asrc_driver);
Register m2m platform device,that user can use M2M feature.
Signed-off-by: Shengjiu Wang shengjiu.wang@nxp.com --- sound/soc/fsl/fsl_easrc.c | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-)
diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c index 959a8e2dd716..98adbae082fa 100644 --- a/sound/soc/fsl/fsl_easrc.c +++ b/sound/soc/fsl/fsl_easrc.c @@ -2202,6 +2202,12 @@ static int fsl_easrc_probe(struct platform_device *pdev) goto err_pm_disable; }
+ ret = fsl_asrc_m2m_init(easrc); + if (ret) { + dev_err(&pdev->dev, "failed to init m2m device %d\n", ret); + return ret; + } + return 0;
err_pm_disable: @@ -2211,6 +2217,10 @@ static int fsl_easrc_probe(struct platform_device *pdev)
static void fsl_easrc_remove(struct platform_device *pdev) { + struct fsl_asrc *easrc = dev_get_drvdata(&pdev->dev); + + fsl_asrc_m2m_exit(easrc); + pm_runtime_disable(&pdev->dev); }
@@ -2311,10 +2321,29 @@ static int fsl_easrc_runtime_resume(struct device *dev) return ret; }
+static int fsl_easrc_suspend(struct device *dev) +{ + struct fsl_asrc *easrc = dev_get_drvdata(dev); + int ret; + + fsl_asrc_m2m_suspend(easrc); + ret = pm_runtime_force_suspend(dev); + return ret; +} + +static int fsl_easrc_resume(struct device *dev) +{ + struct fsl_asrc *easrc = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_resume(dev); + fsl_asrc_m2m_resume(easrc); + return ret; +} + static const struct dev_pm_ops fsl_easrc_pm_ops = { RUNTIME_PM_OPS(fsl_easrc_runtime_suspend, fsl_easrc_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + SYSTEM_SLEEP_PM_OPS(fsl_easrc_suspend, fsl_easrc_resume) };
static struct platform_driver fsl_easrc_driver = {
participants (4)
-
Jaroslav Kysela
-
Pierre-Louis Bossart
-
Shengjiu Wang
-
Shengjiu Wang