[RFC PATCH 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.
Shengjiu Wang (6): ALSA: compress: add Sample Rate Converter codec support 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 + include/uapi/sound/compress_params.h | 9 +- 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 | 769 ++++++++++++++++++++++++++ sound/soc/fsl/fsl_easrc.c | 259 ++++++++- sound/soc/fsl/fsl_easrc.h | 4 + 10 files changed, 1284 insertions(+), 8 deletions(-) create mode 100644 sound/soc/fsl/fsl_asrc_m2m.c
Add Sample Rate Converter(SRC) codec support, define the output format and rate for SRC.
Signed-off-by: Shengjiu Wang shengjiu.wang@nxp.com --- include/uapi/sound/compress_offload.h | 2 ++ include/uapi/sound/compress_params.h | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/include/uapi/sound/compress_offload.h b/include/uapi/sound/compress_offload.h index 98772b0cbcb7..8b2b72f94e26 100644 --- a/include/uapi/sound/compress_offload.h +++ b/include/uapi/sound/compress_offload.h @@ -112,10 +112,12 @@ struct snd_compr_codec_caps { * end of the track * @SNDRV_COMPRESS_ENCODER_DELAY: no of samples inserted by the encoder at the * beginning of the track + * @SNDRV_COMPRESS_SRC_RATIO_MOD: Resampling Ratio Modifier for sample rate converter */ enum sndrv_compress_encoder { SNDRV_COMPRESS_ENCODER_PADDING = 1, SNDRV_COMPRESS_ENCODER_DELAY = 2, + SNDRV_COMPRESS_SRC_RATIO_MOD = 3, };
/** diff --git a/include/uapi/sound/compress_params.h b/include/uapi/sound/compress_params.h index ddc77322d571..0843773ea6b4 100644 --- a/include/uapi/sound/compress_params.h +++ b/include/uapi/sound/compress_params.h @@ -43,7 +43,8 @@ #define SND_AUDIOCODEC_BESPOKE ((__u32) 0x0000000E) #define SND_AUDIOCODEC_ALAC ((__u32) 0x0000000F) #define SND_AUDIOCODEC_APE ((__u32) 0x00000010) -#define SND_AUDIOCODEC_MAX SND_AUDIOCODEC_APE +#define SND_AUDIOCODEC_SRC ((__u32) 0x00000011) +#define SND_AUDIOCODEC_MAX SND_AUDIOCODEC_SRC
/* * Profile and modes are listed with bit masks. This allows for a @@ -324,6 +325,11 @@ struct snd_dec_ape { __u32 seek_table_present; } __attribute__((packed, aligned(4)));
+struct snd_dec_src { + __u32 format_out; + __u32 rate_out; +} __attribute__((packed, aligned(4))); + union snd_codec_options { struct snd_enc_wma wma; struct snd_enc_vorbis vorbis; @@ -334,6 +340,7 @@ union snd_codec_options { struct snd_dec_wma wma_d; struct snd_dec_alac alac_d; struct snd_dec_ape ape_d; + struct snd_dec_src src; } __attribute__((packed, aligned(4)));
/** struct snd_codec_desc - description of codec capabilities
On 8/6/24 12:26, Shengjiu Wang wrote:
Add Sample Rate Converter(SRC) codec support, define the output format and rate for SRC.
Signed-off-by: Shengjiu Wang shengjiu.wang@nxp.com
include/uapi/sound/compress_offload.h | 2 ++ include/uapi/sound/compress_params.h | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/include/uapi/sound/compress_offload.h b/include/uapi/sound/compress_offload.h index 98772b0cbcb7..8b2b72f94e26 100644 --- a/include/uapi/sound/compress_offload.h +++ b/include/uapi/sound/compress_offload.h @@ -112,10 +112,12 @@ struct snd_compr_codec_caps {
- end of the track
- @SNDRV_COMPRESS_ENCODER_DELAY: no of samples inserted by the encoder at the
- beginning of the track
*/
- @SNDRV_COMPRESS_SRC_RATIO_MOD: Resampling Ratio Modifier for sample rate converter
enum sndrv_compress_encoder { SNDRV_COMPRESS_ENCODER_PADDING = 1, SNDRV_COMPRESS_ENCODER_DELAY = 2,
- SNDRV_COMPRESS_SRC_RATIO_MOD = 3,
};
this sounds wrong to me. The sample rate converter is not an "encoder", and the properties for padding/delay are totally specific to an encoder function.
The other point is that I am not sure how standard this ratio_mod parameter is. This could be totally specific to a specific implementation, and another ASRC might have different parameters.
/** diff --git a/include/uapi/sound/compress_params.h b/include/uapi/sound/compress_params.h index ddc77322d571..0843773ea6b4 100644 --- a/include/uapi/sound/compress_params.h +++ b/include/uapi/sound/compress_params.h @@ -43,7 +43,8 @@ #define SND_AUDIOCODEC_BESPOKE ((__u32) 0x0000000E) #define SND_AUDIOCODEC_ALAC ((__u32) 0x0000000F) #define SND_AUDIOCODEC_APE ((__u32) 0x00000010) -#define SND_AUDIOCODEC_MAX SND_AUDIOCODEC_APE +#define SND_AUDIOCODEC_SRC ((__u32) 0x00000011) +#define SND_AUDIOCODEC_MAX SND_AUDIOCODEC_SRC
I am not sure this is wise to change such definitions?
/*
- Profile and modes are listed with bit masks. This allows for a
@@ -324,6 +325,11 @@ struct snd_dec_ape { __u32 seek_table_present; } __attribute__((packed, aligned(4)));
+struct snd_dec_src {
- __u32 format_out;
- __u32 rate_out;
+} __attribute__((packed, aligned(4)));
Again I am not sure how standard those parameters are, and even if they were if their representation is reusable.
And the compressed API has a good split between encoders and decoders, I am not sure how an SRC can be classified as either.
union snd_codec_options { struct snd_enc_wma wma; struct snd_enc_vorbis vorbis; @@ -334,6 +340,7 @@ union snd_codec_options { struct snd_dec_wma wma_d; struct snd_dec_alac alac_d; struct snd_dec_ape ape_d;
- struct snd_dec_src src;
} __attribute__((packed, aligned(4)));
/** struct snd_codec_desc - description of codec capabilities
All-
"The sample rate converter is not an encoder ..."
Indeed, an encoder creates a compressed bitstream from audio data (typically linear PCM samples), normally for transmission of some type. A sample rate converter generates audio data from audio data, and is normally applied prior to an encoder because it can only accept a limited range of sample rates.
-Jeff
Quoting Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com:
On 8/6/24 12:26, Shengjiu Wang wrote:
Add Sample Rate Converter(SRC) codec support, define the output format and rate for SRC.
Signed-off-by: Shengjiu Wang shengjiu.wang@nxp.com
include/uapi/sound/compress_offload.h | 2 ++ include/uapi/sound/compress_params.h | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/include/uapi/sound/compress_offload.h b/include/uapi/sound/compress_offload.h index 98772b0cbcb7..8b2b72f94e26 100644 --- a/include/uapi/sound/compress_offload.h +++ b/include/uapi/sound/compress_offload.h @@ -112,10 +112,12 @@ struct snd_compr_codec_caps {
- end of the track
- @SNDRV_COMPRESS_ENCODER_DELAY: no of samples inserted by the
encoder at the
- beginning of the track
- @SNDRV_COMPRESS_SRC_RATIO_MOD: Resampling Ratio Modifier for
sample rate converter */ enum sndrv_compress_encoder { SNDRV_COMPRESS_ENCODER_PADDING = 1, SNDRV_COMPRESS_ENCODER_DELAY = 2,
- SNDRV_COMPRESS_SRC_RATIO_MOD = 3,
};
this sounds wrong to me. The sample rate converter is not an "encoder", and the properties for padding/delay are totally specific to an encoder function.
The other point is that I am not sure how standard this ratio_mod parameter is. This could be totally specific to a specific implementation, and another ASRC might have different parameters.
/** diff --git a/include/uapi/sound/compress_params.h b/include/uapi/sound/compress_params.h index ddc77322d571..0843773ea6b4 100644 --- a/include/uapi/sound/compress_params.h +++ b/include/uapi/sound/compress_params.h @@ -43,7 +43,8 @@ #define SND_AUDIOCODEC_BESPOKE ((__u32) 0x0000000E) #define SND_AUDIOCODEC_ALAC ((__u32) 0x0000000F) #define SND_AUDIOCODEC_APE ((__u32) 0x00000010) -#define SND_AUDIOCODEC_MAX SND_AUDIOCODEC_APE +#define SND_AUDIOCODEC_SRC ((__u32) 0x00000011) +#define SND_AUDIOCODEC_MAX SND_AUDIOCODEC_SRC
I am not sure this is wise to change such definitions?
/*
- Profile and modes are listed with bit masks. This allows for a
@@ -324,6 +325,11 @@ struct snd_dec_ape { __u32 seek_table_present; } __attribute__((packed, aligned(4)));
+struct snd_dec_src {
- __u32 format_out;
- __u32 rate_out;
+} __attribute__((packed, aligned(4)));
Again I am not sure how standard those parameters are, and even if they were if their representation is reusable.
And the compressed API has a good split between encoders and decoders, I am not sure how an SRC can be classified as either.
union snd_codec_options { struct snd_enc_wma wma; struct snd_enc_vorbis vorbis; @@ -334,6 +340,7 @@ union snd_codec_options { struct snd_dec_wma wma_d; struct snd_dec_alac alac_d; struct snd_dec_ape ape_d;
- struct snd_dec_src src;
} __attribute__((packed, aligned(4)));
/** struct snd_codec_desc - description of codec capabilities
On Tue, Aug 6, 2024 at 7:25 PM Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com wrote:
On 8/6/24 12:26, Shengjiu Wang wrote:
Add Sample Rate Converter(SRC) codec support, define the output format and rate for SRC.
Signed-off-by: Shengjiu Wang shengjiu.wang@nxp.com
include/uapi/sound/compress_offload.h | 2 ++ include/uapi/sound/compress_params.h | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/include/uapi/sound/compress_offload.h b/include/uapi/sound/compress_offload.h index 98772b0cbcb7..8b2b72f94e26 100644 --- a/include/uapi/sound/compress_offload.h +++ b/include/uapi/sound/compress_offload.h @@ -112,10 +112,12 @@ struct snd_compr_codec_caps {
- end of the track
- @SNDRV_COMPRESS_ENCODER_DELAY: no of samples inserted by the encoder at the
- beginning of the track
*/
- @SNDRV_COMPRESS_SRC_RATIO_MOD: Resampling Ratio Modifier for sample rate converter
enum sndrv_compress_encoder { SNDRV_COMPRESS_ENCODER_PADDING = 1, SNDRV_COMPRESS_ENCODER_DELAY = 2,
SNDRV_COMPRESS_SRC_RATIO_MOD = 3,
};
this sounds wrong to me. The sample rate converter is not an "encoder", and the properties for padding/delay are totally specific to an encoder function.
There is only decoder and encoder definition for compress, I know it is difficult to add SRC to encoder or decoder classification, SRC is a Post Processing. I hope you can have a recommandation.
Thanks.
Best regards Shengjiu Wang
The other point is that I am not sure how standard this ratio_mod parameter is. This could be totally specific to a specific implementation, and another ASRC might have different parameters.
/** diff --git a/include/uapi/sound/compress_params.h b/include/uapi/sound/compress_params.h index ddc77322d571..0843773ea6b4 100644 --- a/include/uapi/sound/compress_params.h +++ b/include/uapi/sound/compress_params.h @@ -43,7 +43,8 @@ #define SND_AUDIOCODEC_BESPOKE ((__u32) 0x0000000E) #define SND_AUDIOCODEC_ALAC ((__u32) 0x0000000F) #define SND_AUDIOCODEC_APE ((__u32) 0x00000010) -#define SND_AUDIOCODEC_MAX SND_AUDIOCODEC_APE +#define SND_AUDIOCODEC_SRC ((__u32) 0x00000011) +#define SND_AUDIOCODEC_MAX SND_AUDIOCODEC_SRC
I am not sure this is wise to change such definitions?
/*
- Profile and modes are listed with bit masks. This allows for a
@@ -324,6 +325,11 @@ struct snd_dec_ape { __u32 seek_table_present; } __attribute__((packed, aligned(4)));
+struct snd_dec_src {
__u32 format_out;
__u32 rate_out;
+} __attribute__((packed, aligned(4)));
Again I am not sure how standard those parameters are, and even if they were if their representation is reusable.
And the compressed API has a good split between encoders and decoders, I am not sure how an SRC can be classified as either.
union snd_codec_options { struct snd_enc_wma wma; struct snd_enc_vorbis vorbis; @@ -334,6 +340,7 @@ union snd_codec_options { struct snd_dec_wma wma_d; struct snd_dec_alac alac_d; struct snd_dec_ape ape_d;
struct snd_dec_src src;
} __attribute__((packed, aligned(4)));
/** struct snd_codec_desc - description of codec capabilities
On 8/8/24 11:17, Shengjiu Wang wrote:
On Tue, Aug 6, 2024 at 7:25 PM Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com wrote:
On 8/6/24 12:26, Shengjiu Wang wrote:
Add Sample Rate Converter(SRC) codec support, define the output format and rate for SRC.
Signed-off-by: Shengjiu Wang shengjiu.wang@nxp.com
include/uapi/sound/compress_offload.h | 2 ++ include/uapi/sound/compress_params.h | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/include/uapi/sound/compress_offload.h b/include/uapi/sound/compress_offload.h index 98772b0cbcb7..8b2b72f94e26 100644 --- a/include/uapi/sound/compress_offload.h +++ b/include/uapi/sound/compress_offload.h @@ -112,10 +112,12 @@ struct snd_compr_codec_caps {
- end of the track
- @SNDRV_COMPRESS_ENCODER_DELAY: no of samples inserted by the encoder at the
- beginning of the track
*/
- @SNDRV_COMPRESS_SRC_RATIO_MOD: Resampling Ratio Modifier for sample rate converter
enum sndrv_compress_encoder { SNDRV_COMPRESS_ENCODER_PADDING = 1, SNDRV_COMPRESS_ENCODER_DELAY = 2,
SNDRV_COMPRESS_SRC_RATIO_MOD = 3,
};
this sounds wrong to me. The sample rate converter is not an "encoder", and the properties for padding/delay are totally specific to an encoder function.
There is only decoder and encoder definition for compress, I know it is difficult to add SRC to encoder or decoder classification, SRC is a Post Processing. I hope you can have a recommandation
I don't. I think we're blurring layers in a really odd way.
The main reason why the compress API was added is to remove the byte-to-time conversions. But that's clearly not useful for a post-processing of PCM data, where the bitrate is constant. It really feels like we're adding this memory-to-memory API to the compress framework because we don't have anything else available, not because it makes sense to do so.
Then there's the issue of parameters, we chose to only add parameters for standard encoders/decoders. Post-processing is highly specific and the parameter definitions varies from one implementation to another - and usually parameters are handled in an opaque way with binary controls. This is best handled with a UUID that needs to be known only to applications and low-level firmware/hardware, the kernel code should not have to be modified for each and every processing and to add new parameters. It just does not scale and it's unmaintainable.
At the very least if you really want to use this compress API, extend it to use a non-descript "UUID-defined" type and an opaque set of parameters with this UUID passed in a header.
On 08. 08. 24 13:27, Pierre-Louis Bossart wrote:
On 8/8/24 11:17, Shengjiu Wang wrote:
On Tue, Aug 6, 2024 at 7:25 PM Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com wrote:
On 8/6/24 12:26, Shengjiu Wang wrote:
Add Sample Rate Converter(SRC) codec support, define the output format and rate for SRC.
Signed-off-by: Shengjiu Wang shengjiu.wang@nxp.com
include/uapi/sound/compress_offload.h | 2 ++ include/uapi/sound/compress_params.h | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/include/uapi/sound/compress_offload.h b/include/uapi/sound/compress_offload.h index 98772b0cbcb7..8b2b72f94e26 100644 --- a/include/uapi/sound/compress_offload.h +++ b/include/uapi/sound/compress_offload.h @@ -112,10 +112,12 @@ struct snd_compr_codec_caps {
- end of the track
- @SNDRV_COMPRESS_ENCODER_DELAY: no of samples inserted by the encoder at the
- beginning of the track
*/ enum sndrv_compress_encoder { SNDRV_COMPRESS_ENCODER_PADDING = 1, SNDRV_COMPRESS_ENCODER_DELAY = 2,
- @SNDRV_COMPRESS_SRC_RATIO_MOD: Resampling Ratio Modifier for sample rate converter
};SNDRV_COMPRESS_SRC_RATIO_MOD = 3,
this sounds wrong to me. The sample rate converter is not an "encoder", and the properties for padding/delay are totally specific to an encoder function.
There is only decoder and encoder definition for compress, I know it is difficult to add SRC to encoder or decoder classification, SRC is a Post Processing. I hope you can have a recommandation
I don't. I think we're blurring layers in a really odd way.
The main reason why the compress API was added is to remove the byte-to-time conversions. But that's clearly not useful for a post-processing of PCM data, where the bitrate is constant. It really feels like we're adding this memory-to-memory API to the compress framework because we don't have anything else available, not because it makes sense to do so.
It makes sense to offload decoder/encoder tasks as batch processing for standard compress stream and return back result (PCM stream or encoded stream) to user space. So it makes sense to use the compress interface (parameters handshake) for it. Let's talk about the proper SRC extension.
For SRC and dynamic rate modification. I would just create an ALSA control for this. We are already using the "PCM Rate Shift 100000" control in the sound/drivers/aloop.c for this purpose (something like pitch in MIDI) for example. There is no requirement to add this function through metadata ioctls. As bonus, this control can be monitored with multiple tasks.
Then there's the issue of parameters, we chose to only add parameters for standard encoders/decoders. Post-processing is highly specific and the parameter definitions varies from one implementation to another - and usually parameters are handled in an opaque way with binary controls. This is best handled with a UUID that needs to be known only to applications and low-level firmware/hardware, the kernel code should not have to be modified for each and every processing and to add new parameters. It just does not scale and it's unmaintainable.
At the very least if you really want to use this compress API, extend it to use a non-descript "UUID-defined" type and an opaque set of parameters with this UUID passed in a header.
We don't need to use UUID-defined scheme for simple (A)SRC implementation. As I noted, the specific runtime controls may use existing ALSA control API.
Jaroslav
files changed, 10 insertions(+), 1 deletion(-)
diff --git a/include/uapi/sound/compress_offload.h b/include/uapi/ sound/compress_offload.h index 98772b0cbcb7..8b2b72f94e26 100644 --- a/include/uapi/sound/compress_offload.h +++ b/include/uapi/sound/compress_offload.h @@ -112,10 +112,12 @@ struct snd_compr_codec_caps { * end of the track * @SNDRV_COMPRESS_ENCODER_DELAY: no of samples inserted by the encoder at the * beginning of the track
- @SNDRV_COMPRESS_SRC_RATIO_MOD: Resampling Ratio Modifier for
sample rate converter */ enum sndrv_compress_encoder { SNDRV_COMPRESS_ENCODER_PADDING = 1, SNDRV_COMPRESS_ENCODER_DELAY = 2, + SNDRV_COMPRESS_SRC_RATIO_MOD = 3, };
this sounds wrong to me. The sample rate converter is not an "encoder", and the properties for padding/delay are totally specific to an encoder function.
There is only decoder and encoder definition for compress, I know it is difficult to add SRC to encoder or decoder classification, SRC is a Post Processing. I hope you can have a recommandation
I don't. I think we're blurring layers in a really odd way.
The main reason why the compress API was added is to remove the byte-to-time conversions. But that's clearly not useful for a post-processing of PCM data, where the bitrate is constant. It really feels like we're adding this memory-to-memory API to the compress framework because we don't have anything else available, not because it makes sense to do so.
It makes sense to offload decoder/encoder tasks as batch processing for standard compress stream and return back result (PCM stream or encoded stream) to user space. So it makes sense to use the compress interface (parameters handshake) for it. Let's talk about the proper SRC extension.
For SRC and dynamic rate modification. I would just create an ALSA control for this. We are already using the "PCM Rate Shift 100000" control in the sound/drivers/aloop.c for this purpose (something like pitch in MIDI) for example. There is no requirement to add this function through metadata ioctls. As bonus, this control can be monitored with multiple tasks.
this wouldn't work when the rate is estimated in firmware/hardware, which is precisely what the 'asynchronous' part of ASRC does.
Then there's the issue of parameters, we chose to only add parameters for standard encoders/decoders. Post-processing is highly specific and the parameter definitions varies from one implementation to another - and usually parameters are handled in an opaque way with binary controls. This is best handled with a UUID that needs to be known only to applications and low-level firmware/hardware, the kernel code should not have to be modified for each and every processing and to add new parameters. It just does not scale and it's unmaintainable.
At the very least if you really want to use this compress API, extend it to use a non-descript "UUID-defined" type and an opaque set of parameters with this UUID passed in a header.
We don't need to use UUID-defined scheme for simple (A)SRC implementation. As I noted, the specific runtime controls may use existing ALSA control API.
"Simple (A)SRC" is an oxymoron. There are multiple ways to define the performance, and how the drift estimator is handled. There's nothing simple if you look under the hood. The SOF implementation has for example those parameters:
uint32_t source_rate; /**< Define fixed source rate or */ /**< use 0 to indicate need to get */ /**< the rate from stream */ uint32_t sink_rate; /**< Define fixed sink rate or */ /**< use 0 to indicate need to get */ /**< the rate from stream */ uint32_t asynchronous_mode; /**< synchronous 0, asynchronous 1 */ /**< When 1 the ASRC tracks and */ /**< compensates for drift. */ uint32_t operation_mode; /**< push 0, pull 1, In push mode the */ /**< ASRC consumes a defined number */ /**< of frames at input, with varying */ /**< number of frames at output. */ /**< In pull mode the ASRC outputs */ /**< a defined number of frames while */ /**< number of input frames varies. */
They are clearly different from what is suggested above with a 'ratio-mod'.
Same if you have a 'simple EQ'. there are dozens of ways to implement the functionality with FIR, IIR or a combination of the two, and multiple bands.
The point is that you have to think upfront about a generic way to pass parameters. We didn't have to do it for encoders/decoders because we only catered to well-documented standard solutions only. By choosing to support PCM processing, a new can of worms is now open.
I repeat: please do not make the mistake of listing all processing with an enum and a new structure for parameters every time someone needs a specific transform in their pipeline. We made that mistake with SOF and had to backtrack rather quickly. The only way to scale is an identifier that is NOT included in the kernel code but is known to higher and lower-levels only.
On 08. 08. 24 14:19, Pierre-Louis Bossart wrote:
files changed, 10 insertions(+), 1 deletion(-)
diff --git a/include/uapi/sound/compress_offload.h b/include/uapi/ sound/compress_offload.h index 98772b0cbcb7..8b2b72f94e26 100644 --- a/include/uapi/sound/compress_offload.h +++ b/include/uapi/sound/compress_offload.h @@ -112,10 +112,12 @@ struct snd_compr_codec_caps { * end of the track * @SNDRV_COMPRESS_ENCODER_DELAY: no of samples inserted by the encoder at the * beginning of the track
- @SNDRV_COMPRESS_SRC_RATIO_MOD: Resampling Ratio Modifier for
sample rate converter */ enum sndrv_compress_encoder { SNDRV_COMPRESS_ENCODER_PADDING = 1, SNDRV_COMPRESS_ENCODER_DELAY = 2, + SNDRV_COMPRESS_SRC_RATIO_MOD = 3, };
this sounds wrong to me. The sample rate converter is not an "encoder", and the properties for padding/delay are totally specific to an encoder function.
There is only decoder and encoder definition for compress, I know it is difficult to add SRC to encoder or decoder classification, SRC is a Post Processing. I hope you can have a recommandation
I don't. I think we're blurring layers in a really odd way.
The main reason why the compress API was added is to remove the byte-to-time conversions. But that's clearly not useful for a post-processing of PCM data, where the bitrate is constant. It really feels like we're adding this memory-to-memory API to the compress framework because we don't have anything else available, not because it makes sense to do so.
It makes sense to offload decoder/encoder tasks as batch processing for standard compress stream and return back result (PCM stream or encoded stream) to user space. So it makes sense to use the compress interface (parameters handshake) for it. Let's talk about the proper SRC extension.
For SRC and dynamic rate modification. I would just create an ALSA control for this. We are already using the "PCM Rate Shift 100000" control in the sound/drivers/aloop.c for this purpose (something like pitch in MIDI) for example. There is no requirement to add this function through metadata ioctls. As bonus, this control can be monitored with multiple tasks.
this wouldn't work when the rate is estimated in firmware/hardware, which is precisely what the 'asynchronous' part of ASRC does.
Then there's the issue of parameters, we chose to only add parameters for standard encoders/decoders. Post-processing is highly specific and the parameter definitions varies from one implementation to another - and usually parameters are handled in an opaque way with binary controls. This is best handled with a UUID that needs to be known only to applications and low-level firmware/hardware, the kernel code should not have to be modified for each and every processing and to add new parameters. It just does not scale and it's unmaintainable.
At the very least if you really want to use this compress API, extend it to use a non-descript "UUID-defined" type and an opaque set of parameters with this UUID passed in a header.
We don't need to use UUID-defined scheme for simple (A)SRC implementation. As I noted, the specific runtime controls may use existing ALSA control API.
"Simple (A)SRC" is an oxymoron. There are multiple ways to define the performance, and how the drift estimator is handled. There's nothing simple if you look under the hood. The SOF implementation has for example those parameters:
uint32_t source_rate; /**< Define fixed source rate or */ /**< use 0 to indicate need to get */ /**< the rate from stream */ uint32_t sink_rate; /**< Define fixed sink rate or */ /**< use 0 to indicate need to get */ /**< the rate from stream */ uint32_t asynchronous_mode; /**< synchronous 0, asynchronous 1 */ /**< When 1 the ASRC tracks and */ /**< compensates for drift. */ uint32_t operation_mode; /**< push 0, pull 1, In push mode the */ /**< ASRC consumes a defined number */ /**< of frames at input, with varying */ /**< number of frames at output. */ /**< In pull mode the ASRC outputs */ /**< a defined number of frames while */ /**< number of input frames varies. */
They are clearly different from what is suggested above with a 'ratio-mod'.
I don't think so. The proposed (A)SRC for compress-accel is just one case for the above configs where the input is known and output is controlled by the requested rate. The I/O mechanism is abstracted enough in this case and the driver/hardware/firmware must follow it.
Same if you have a 'simple EQ'. there are dozens of ways to implement the functionality with FIR, IIR or a combination of the two, and multiple bands.
The point is that you have to think upfront about a generic way to pass parameters. We didn't have to do it for encoders/decoders because we only catered to well-documented standard solutions only. By choosing to support PCM processing, a new can of worms is now open.
I repeat: please do not make the mistake of listing all processing with an enum and a new structure for parameters every time someone needs a specific transform in their pipeline. We made that mistake with SOF and had to backtrack rather quickly. The only way to scale is an identifier that is NOT included in the kernel code but is known to higher and lower-levels only.
There are two ways - black box (UUID - as you suggested) - or well defined purpose (abstraction). For your example 'simple EQ', the parameters should be the band (frequency range) volume values. It's abstract and the real filters (resp. implementation) used behind may depend on the hardware/driver capabilities.
From my view, the really special cases may be handled as black box, but others like (A)SRC should follow some well-defined abstraction IMHO to not force user space to handle all special cases.
Jaroslav
Then there's the issue of parameters, we chose to only add parameters for standard encoders/decoders. Post-processing is highly specific and the parameter definitions varies from one implementation to another - and usually parameters are handled in an opaque way with binary controls. This is best handled with a UUID that needs to be known only to applications and low-level firmware/hardware, the kernel code should not have to be modified for each and every processing and to add new parameters. It just does not scale and it's unmaintainable.
At the very least if you really want to use this compress API, extend it to use a non-descript "UUID-defined" type and an opaque set of parameters with this UUID passed in a header.
We don't need to use UUID-defined scheme for simple (A)SRC implementation. As I noted, the specific runtime controls may use existing ALSA control API.
"Simple (A)SRC" is an oxymoron. There are multiple ways to define the performance, and how the drift estimator is handled. There's nothing simple if you look under the hood. The SOF implementation has for example those parameters:
uint32_t source_rate; /**< Define fixed source rate or */ /**< use 0 to indicate need to get */ /**< the rate from stream */ uint32_t sink_rate; /**< Define fixed sink rate or */ /**< use 0 to indicate need to get */ /**< the rate from stream */ uint32_t asynchronous_mode; /**< synchronous 0, asynchronous 1 */ /**< When 1 the ASRC tracks and */ /**< compensates for drift. */ uint32_t operation_mode; /**< push 0, pull 1, In push mode the */ /**< ASRC consumes a defined number */ /**< of frames at input, with varying */ /**< number of frames at output. */ /**< In pull mode the ASRC outputs */ /**< a defined number of frames while */ /**< number of input frames varies. */
They are clearly different from what is suggested above with a 'ratio- mod'.
I don't think so. The proposed (A)SRC for compress-accel is just one case for the above configs where the input is known and output is controlled by the requested rate. The I/O mechanism is abstracted enough in this case and the driver/hardware/firmware must follow it.
ASRC is usually added when the nominal rates are known but the clock sources differ and the drift needs to be estimated at run-time and the coefficients or interpolation modified dynamically
If the ratio is known exactly and there's no clock drift, then it's a different problem where the filter coefficients are constant.
Same if you have a 'simple EQ'. there are dozens of ways to implement the functionality with FIR, IIR or a combination of the two, and multiple bands.
The point is that you have to think upfront about a generic way to pass parameters. We didn't have to do it for encoders/decoders because we only catered to well-documented standard solutions only. By choosing to support PCM processing, a new can of worms is now open.
I repeat: please do not make the mistake of listing all processing with an enum and a new structure for parameters every time someone needs a specific transform in their pipeline. We made that mistake with SOF and had to backtrack rather quickly. The only way to scale is an identifier that is NOT included in the kernel code but is known to higher and lower-levels only.
There are two ways - black box (UUID - as you suggested) - or well defined purpose (abstraction). For your example 'simple EQ', the parameters should be the band (frequency range) volume values. It's abstract and the real filters (resp. implementation) used behind may depend on the hardware/driver capabilities.
Indeed there is a possibility that the parameters are high-level, but that would require firmware or hardware to be able to generate actual coefficients from those parameters. That usually requires some advanced math which isn't necessarily obvious to implement with fixed-point hardware.
From my view, the really special cases may be handled as black box, but others like (A)SRC should follow some well-defined abstraction IMHO to not force user space to handle all special cases.
I am not against the high-level abstractions, e.g. along the lines of what Android defined: https://developer.android.com/reference/android/media/audiofx/AudioEffect
That's not sufficient however, we also need to make sure there's an ability to provide pre-computed coefficients in an opaque manner for processing that doesn't fit in the well-defined cases. In practice there are very few 3rd party IP that fits in well-defined cases, everyone has secret-sauce parameters and options.
On Fri, Aug 9, 2024 at 3:25 PM Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com wrote:
Then there's the issue of parameters, we chose to only add parameters for standard encoders/decoders. Post-processing is highly specific and the parameter definitions varies from one implementation to another - and usually parameters are handled in an opaque way with binary controls. This is best handled with a UUID that needs to be known only to applications and low-level firmware/hardware, the kernel code should not have to be modified for each and every processing and to add new parameters. It just does not scale and it's unmaintainable.
At the very least if you really want to use this compress API, extend it to use a non-descript "UUID-defined" type and an opaque set of parameters with this UUID passed in a header.
We don't need to use UUID-defined scheme for simple (A)SRC implementation. As I noted, the specific runtime controls may use existing ALSA control API.
"Simple (A)SRC" is an oxymoron. There are multiple ways to define the performance, and how the drift estimator is handled. There's nothing simple if you look under the hood. The SOF implementation has for example those parameters:
uint32_t source_rate; /**< Define fixed source rate or */ /**< use 0 to indicate need to get */ /**< the rate from stream */ uint32_t sink_rate; /**< Define fixed sink rate or */ /**< use 0 to indicate need to get */ /**< the rate from stream */ uint32_t asynchronous_mode; /**< synchronous 0, asynchronous 1 */ /**< When 1 the ASRC tracks and */ /**< compensates for drift. */ uint32_t operation_mode; /**< push 0, pull 1, In push mode the */ /**< ASRC consumes a defined number */ /**< of frames at input, with varying */ /**< number of frames at output. */ /**< In pull mode the ASRC outputs */ /**< a defined number of frames while */ /**< number of input frames varies. */
They are clearly different from what is suggested above with a 'ratio- mod'.
I don't think so. The proposed (A)SRC for compress-accel is just one case for the above configs where the input is known and output is controlled by the requested rate. The I/O mechanism is abstracted enough in this case and the driver/hardware/firmware must follow it.
ASRC is usually added when the nominal rates are known but the clock sources differ and the drift needs to be estimated at run-time and the coefficients or interpolation modified dynamically
If the ratio is known exactly and there's no clock drift, then it's a different problem where the filter coefficients are constant.
Same if you have a 'simple EQ'. there are dozens of ways to implement the functionality with FIR, IIR or a combination of the two, and multiple bands.
The point is that you have to think upfront about a generic way to pass parameters. We didn't have to do it for encoders/decoders because we only catered to well-documented standard solutions only. By choosing to support PCM processing, a new can of worms is now open.
I repeat: please do not make the mistake of listing all processing with an enum and a new structure for parameters every time someone needs a specific transform in their pipeline. We made that mistake with SOF and had to backtrack rather quickly. The only way to scale is an identifier that is NOT included in the kernel code but is known to higher and lower-levels only.
There are two ways - black box (UUID - as you suggested) - or well defined purpose (abstraction). For your example 'simple EQ', the parameters should be the band (frequency range) volume values. It's abstract and the real filters (resp. implementation) used behind may depend on the hardware/driver capabilities.
Indeed there is a possibility that the parameters are high-level, but that would require firmware or hardware to be able to generate actual coefficients from those parameters. That usually requires some advanced math which isn't necessarily obvious to implement with fixed-point hardware.
From my view, the really special cases may be handled as black box, but others like (A)SRC should follow some well-defined abstraction IMHO to not force user space to handle all special cases.
I am not against the high-level abstractions, e.g. along the lines of what Android defined: https://developer.android.com/reference/android/media/audiofx/AudioEffect
That's not sufficient however, we also need to make sure there's an ability to provide pre-computed coefficients in an opaque manner for processing that doesn't fit in the well-defined cases. In practice there are very few 3rd party IP that fits in well-defined cases, everyone has secret-sauce parameters and options.
Appreciate the discussion.
Let me explain the reason for the change:
Why I use the metadata ioctl is because the ALSA controls are binding to the sound card. What I want is the controls can be bound to snd_compr_stream, because the ASRC compress sound card can support multi instances ( the ASRC can support multi conversion in parallel). The ALSA controls can't be used for this case, the only choice in current compress API is metadata ioctl. And metadata ioctl can be called many times which can meet the ratio modifier requirement (ratio may be drift on the fly)
And compress API uses codec as the unit for capability query and parameter setting, So I think need to define "SND_AUDIOCODEC_SRC' and 'struct snd_dec_src', for the 'snd_dec_src' just defined output format and output rate, channels definition just reuse the snd_codec.ch_in.
I understand your concern, but there seems no better option. If you have, please guide me. Thanks.
Best regards Shengjiu Wang
Why I use the metadata ioctl is because the ALSA controls are binding to the sound card. What I want is the controls can be bound to snd_compr_stream, because the ASRC compress sound card can support multi instances ( the ASRC can support multi conversion in parallel). The ALSA controls can't be used for this case, the only choice in current compress API is metadata ioctl.
I don't know if there is really a technical limitation for this, this is for Jaroslav to comment. I am not sure why it would be a problem to e.g. have a volume control prior to an encoder or after a decoder.
And metadata ioctl can be called many times which can meet the ratio modifier requirement (ratio may be drift on the fly)
Interesting, that's yet another way of handling the drift with userspace modifying the ratio dynamically. That's different to what I've seen before.
And compress API uses codec as the unit for capability query and parameter setting, So I think need to define "SND_AUDIOCODEC_SRC' and 'struct snd_dec_src', for the 'snd_dec_src' just defined output format and output rate, channels definition just reuse the snd_codec.ch_in.
The capability query is an interesting point as well, it's not clear how to expose to userspace what this specific implementation can do, while at the same time *requiring* userpace to update the ratio dynamically. For something like this to work, userspace needs to have pre-existing information on how the SRC works.
On 09. 08. 24 14:52, Pierre-Louis Bossart wrote:
And metadata ioctl can be called many times which can meet the ratio modifier requirement (ratio may be drift on the fly)
Interesting, that's yet another way of handling the drift with userspace modifying the ratio dynamically. That's different to what I've seen before.
Note that the "timing" is managed by the user space with this scheme.
And compress API uses codec as the unit for capability query and parameter setting, So I think need to define "SND_AUDIOCODEC_SRC' and 'struct snd_dec_src', for the 'snd_dec_src' just defined output format and output rate, channels definition just reuse the snd_codec.ch_in.
The capability query is an interesting point as well, it's not clear how to expose to userspace what this specific implementation can do, while at the same time *requiring* userpace to update the ratio dynamically. For something like this to work, userspace needs to have pre-existing information on how the SRC works.
Yes, it's about abstraction. The user space wants to push data, read data back converted to the target rate and eventually modify the drift using a control managing clocks using own way. We can eventually assume, that if this control does not exist, the drift cannot be controlled. Also, nice thing is that the control has min and max values (range), so driver can specify the drift range, too.
And again, look to "PCM Rate Shift 100000" control implementation in sound/drivers/aloop.c. It would be nice to have the base offset for the shift/drift/pitch value standardized.
Jaroslav
And metadata ioctl can be called many times which can meet the ratio modifier requirement (ratio may be drift on the fly)
Interesting, that's yet another way of handling the drift with userspace modifying the ratio dynamically. That's different to what I've seen before.
Note that the "timing" is managed by the user space with this scheme.
And compress API uses codec as the unit for capability query and parameter setting, So I think need to define "SND_AUDIOCODEC_SRC' and 'struct snd_dec_src', for the 'snd_dec_src' just defined output format and output rate, channels definition just reuse the snd_codec.ch_in.
The capability query is an interesting point as well, it's not clear how to expose to userspace what this specific implementation can do, while at the same time *requiring* userpace to update the ratio dynamically. For something like this to work, userspace needs to have pre-existing information on how the SRC works.
Yes, it's about abstraction. The user space wants to push data, read data back converted to the target rate and eventually modify the drift using a control managing clocks using own way. We can eventually assume, that if this control does not exist, the drift cannot be controlled. Also, nice thing is that the control has min and max values (range), so driver can specify the drift range, too.
This mode of operation would be fine, but if you add the SRC as part of the processing allowed in a compressed stream, it might be used in a 'regular' real-time pipeline and the arguments and implementation could be very different.
In other words, this SRC support looks like an extension of the compress API for a very narrow usage restricted to the memory-to-memory case only. I worry a bit about the next steps... Are there going to be other types of PCM processing like this, and if yes, how would we know if they are intended to be used for the 'regular' compress API or for the memory-to-memory scheme?
On Fri, Aug 9, 2024 at 10:01 PM Jaroslav Kysela perex@perex.cz wrote:
On 09. 08. 24 14:52, Pierre-Louis Bossart wrote:
And metadata ioctl can be called many times which can meet the ratio modifier requirement (ratio may be drift on the fly)
Interesting, that's yet another way of handling the drift with userspace modifying the ratio dynamically. That's different to what I've seen before.
Note that the "timing" is managed by the user space with this scheme.
And compress API uses codec as the unit for capability query and parameter setting, So I think need to define "SND_AUDIOCODEC_SRC' and 'struct snd_dec_src', for the 'snd_dec_src' just defined output format and output rate, channels definition just reuse the snd_codec.ch_in.
The capability query is an interesting point as well, it's not clear how to expose to userspace what this specific implementation can do, while at the same time *requiring* userpace to update the ratio dynamically. For something like this to work, userspace needs to have pre-existing information on how the SRC works.
Yes, it's about abstraction. The user space wants to push data, read data back converted to the target rate and eventually modify the drift using a control managing clocks using own way. We can eventually assume, that if this control does not exist, the drift cannot be controlled. Also, nice thing is that the control has min and max values (range), so driver can specify the drift range, too.
And again, look to "PCM Rate Shift 100000" control implementation in sound/drivers/aloop.c. It would be nice to have the base offset for the shift/drift/pitch value standardized.
Thanks.
But the ASRC driver I implemented is different, I just register one sound card, one device/subdevice. but the ASRC hardware support 4 instances together, so user can open the card device 4 times to create 4 instances then the controls can only bind with compress streams.
I think I can remove the 'SNDRV_COMPRESS_SRC_RATIO_MOD', Only define a private type for driver, which means only the ASRC driver and its user application know the type.
For the change in 'include/uapi/sound/compress_params.h", should I keep them, is there any other suggestion for them?
Best regards Shengjiu Wang
Jaroslav
-- Jaroslav Kysela perex@perex.cz Linux Sound Maintainer; ALSA Project; Red Hat, Inc.
On 12. 08. 24 12:24, Shengjiu Wang wrote:
On Fri, Aug 9, 2024 at 10:01 PM Jaroslav Kysela perex@perex.cz wrote:
On 09. 08. 24 14:52, Pierre-Louis Bossart wrote:
And metadata ioctl can be called many times which can meet the ratio modifier requirement (ratio may be drift on the fly)
Interesting, that's yet another way of handling the drift with userspace modifying the ratio dynamically. That's different to what I've seen before.
Note that the "timing" is managed by the user space with this scheme.
And compress API uses codec as the unit for capability query and parameter setting, So I think need to define "SND_AUDIOCODEC_SRC' and 'struct snd_dec_src', for the 'snd_dec_src' just defined output format and output rate, channels definition just reuse the snd_codec.ch_in.
The capability query is an interesting point as well, it's not clear how to expose to userspace what this specific implementation can do, while at the same time *requiring* userpace to update the ratio dynamically. For something like this to work, userspace needs to have pre-existing information on how the SRC works.
Yes, it's about abstraction. The user space wants to push data, read data back converted to the target rate and eventually modify the drift using a control managing clocks using own way. We can eventually assume, that if this control does not exist, the drift cannot be controlled. Also, nice thing is that the control has min and max values (range), so driver can specify the drift range, too.
And again, look to "PCM Rate Shift 100000" control implementation in sound/drivers/aloop.c. It would be nice to have the base offset for the shift/drift/pitch value standardized.
Thanks.
But the ASRC driver I implemented is different, I just register one sound card, one device/subdevice. but the ASRC hardware support 4 instances together, so user can open the card device 4 times to create 4 instances then the controls can only bind with compress streams.
It's just a reason to add the subdevice code for the compress offload layer like we have in other APIs for overall consistency. I'll try to work on this.
I think I can remove the 'SNDRV_COMPRESS_SRC_RATIO_MOD',
Yes.
Only define a private type for driver, which means only the ASRC driver and its user application know the type.
The control API should be used for this IMHO.
For the change in 'include/uapi/sound/compress_params.h", should I keep them, is there any other suggestion for them?
I don't see a problem there.
Jaroslav
On 8/12/24 15:31, Jaroslav Kysela wrote:
On 12. 08. 24 12:24, Shengjiu Wang wrote:
On Fri, Aug 9, 2024 at 10:01 PM Jaroslav Kysela perex@perex.cz wrote:
On 09. 08. 24 14:52, Pierre-Louis Bossart wrote:
And metadata ioctl can be called many times which can meet the ratio modifier requirement (ratio may be drift on the fly)
Interesting, that's yet another way of handling the drift with userspace modifying the ratio dynamically. That's different to what I've seen before.
Note that the "timing" is managed by the user space with this scheme.
And compress API uses codec as the unit for capability query and parameter setting, So I think need to define "SND_AUDIOCODEC_SRC' and 'struct snd_dec_src', for the 'snd_dec_src' just defined output format and output rate, channels definition just reuse the snd_codec.ch_in.
The capability query is an interesting point as well, it's not clear how to expose to userspace what this specific implementation can do, while at the same time *requiring* userpace to update the ratio dynamically. For something like this to work, userspace needs to have pre-existing information on how the SRC works.
Yes, it's about abstraction. The user space wants to push data, read data back converted to the target rate and eventually modify the drift using a control managing clocks using own way. We can eventually assume, that if this control does not exist, the drift cannot be controlled. Also, nice thing is that the control has min and max values (range), so driver can specify the drift range, too.
And again, look to "PCM Rate Shift 100000" control implementation in sound/drivers/aloop.c. It would be nice to have the base offset for the shift/drift/pitch value standardized.
Thanks.
But the ASRC driver I implemented is different, I just register one sound card, one device/subdevice. but the ASRC hardware support 4 instances together, so user can open the card device 4 times to create 4 instances then the controls can only bind with compress streams.
It's just a reason to add the subdevice code for the compress offload layer like we have in other APIs for overall consistency. I'll try to work on this.
I thought this was supported already? I remember there was a request to enable more than one compressed stream for enhanced cross-fade support with different formats? That isn't supported with the single-device + PARTIAL_DRAIN method.
Vinod?
I think I can remove the 'SNDRV_COMPRESS_SRC_RATIO_MOD',
Yes.
Only define a private type for driver, which means only the ASRC driver and its user application know the type.
The control API should be used for this IMHO.
Agree, this would be a 'clean' split where the compress API is used for the data parts and the control parts used otherwise to alter the ratio or whatever else is needed.
For the change in 'include/uapi/sound/compress_params.h", should I keep them, is there any other suggestion for them?
You can add the SRC type but if you use a control for the parameters you don't need to add anything for the encoder options, do you?
On Mon, Aug 12, 2024 at 9:44 PM Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com wrote:
On 8/12/24 15:31, Jaroslav Kysela wrote:
On 12. 08. 24 12:24, Shengjiu Wang wrote:
On Fri, Aug 9, 2024 at 10:01 PM Jaroslav Kysela perex@perex.cz wrote:
On 09. 08. 24 14:52, Pierre-Louis Bossart wrote:
And metadata ioctl can be called many times which can meet the ratio modifier requirement (ratio may be drift on the fly)
Interesting, that's yet another way of handling the drift with userspace modifying the ratio dynamically. That's different to what I've seen before.
Note that the "timing" is managed by the user space with this scheme.
And compress API uses codec as the unit for capability query and parameter setting, So I think need to define "SND_AUDIOCODEC_SRC' and 'struct snd_dec_src', for the 'snd_dec_src' just defined output format and output rate, channels definition just reuse the snd_codec.ch_in.
The capability query is an interesting point as well, it's not clear how to expose to userspace what this specific implementation can do, while at the same time *requiring* userpace to update the ratio dynamically. For something like this to work, userspace needs to have pre-existing information on how the SRC works.
Yes, it's about abstraction. The user space wants to push data, read data back converted to the target rate and eventually modify the drift using a control managing clocks using own way. We can eventually assume, that if this control does not exist, the drift cannot be controlled. Also, nice thing is that the control has min and max values (range), so driver can specify the drift range, too.
And again, look to "PCM Rate Shift 100000" control implementation in sound/drivers/aloop.c. It would be nice to have the base offset for the shift/drift/pitch value standardized.
Thanks.
But the ASRC driver I implemented is different, I just register one sound card, one device/subdevice. but the ASRC hardware support 4 instances together, so user can open the card device 4 times to create 4 instances then the controls can only bind with compress streams.
It's just a reason to add the subdevice code for the compress offload layer like we have in other APIs for overall consistency. I'll try to work on this.
I thought this was supported already? I remember there was a request to enable more than one compressed stream for enhanced cross-fade support with different formats? That isn't supported with the single-device + PARTIAL_DRAIN method.
Vinod?
I think I can remove the 'SNDRV_COMPRESS_SRC_RATIO_MOD',
Yes.
Only define a private type for driver, which means only the ASRC driver and its user application know the type.
The control API should be used for this IMHO.
Agree, this would be a 'clean' split where the compress API is used for the data parts and the control parts used otherwise to alter the ratio or whatever else is needed.
For the change in 'include/uapi/sound/compress_params.h", should I keep them, is there any other suggestion for them?
You can add the SRC type but if you use a control for the parameters you don't need to add anything for the encoder options, do you?
Yes, to go further, I think we can use SND_AUDIOCODEC_PCM, then the SRC type will be dropped.
But my understanding of the control means the .set_metadata() API, right? As I said, the output rate, output format, and ratio modifier are applied to the instances of ASRC, which is the snd_compr_stream in driver. so only the .set_metadata() API can be used for these purposes.
Best regards Shengjiu Wang
On 09. 08. 24 12:14, Shengjiu Wang wrote:
On Fri, Aug 9, 2024 at 3:25 PM Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com wrote:
Then there's the issue of parameters, we chose to only add parameters for standard encoders/decoders. Post-processing is highly specific and the parameter definitions varies from one implementation to another - and usually parameters are handled in an opaque way with binary controls. This is best handled with a UUID that needs to be known only to applications and low-level firmware/hardware, the kernel code should not have to be modified for each and every processing and to add new parameters. It just does not scale and it's unmaintainable.
At the very least if you really want to use this compress API, extend it to use a non-descript "UUID-defined" type and an opaque set of parameters with this UUID passed in a header.
We don't need to use UUID-defined scheme for simple (A)SRC implementation. As I noted, the specific runtime controls may use existing ALSA control API.
"Simple (A)SRC" is an oxymoron. There are multiple ways to define the performance, and how the drift estimator is handled. There's nothing simple if you look under the hood. The SOF implementation has for example those parameters:
uint32_t source_rate; /**< Define fixed source rate or */ /**< use 0 to indicate need to get */ /**< the rate from stream */ uint32_t sink_rate; /**< Define fixed sink rate or */ /**< use 0 to indicate need to get */ /**< the rate from stream */ uint32_t asynchronous_mode; /**< synchronous 0, asynchronous 1 */ /**< When 1 the ASRC tracks and */ /**< compensates for drift. */ uint32_t operation_mode; /**< push 0, pull 1, In push mode the */ /**< ASRC consumes a defined number */ /**< of frames at input, with varying */ /**< number of frames at output. */ /**< In pull mode the ASRC outputs */ /**< a defined number of frames while */ /**< number of input frames varies. */
They are clearly different from what is suggested above with a 'ratio- mod'.
I don't think so. The proposed (A)SRC for compress-accel is just one case for the above configs where the input is known and output is controlled by the requested rate. The I/O mechanism is abstracted enough in this case and the driver/hardware/firmware must follow it.
ASRC is usually added when the nominal rates are known but the clock sources differ and the drift needs to be estimated at run-time and the coefficients or interpolation modified dynamically
If the ratio is known exactly and there's no clock drift, then it's a different problem where the filter coefficients are constant.
Same if you have a 'simple EQ'. there are dozens of ways to implement the functionality with FIR, IIR or a combination of the two, and multiple bands.
The point is that you have to think upfront about a generic way to pass parameters. We didn't have to do it for encoders/decoders because we only catered to well-documented standard solutions only. By choosing to support PCM processing, a new can of worms is now open.
I repeat: please do not make the mistake of listing all processing with an enum and a new structure for parameters every time someone needs a specific transform in their pipeline. We made that mistake with SOF and had to backtrack rather quickly. The only way to scale is an identifier that is NOT included in the kernel code but is known to higher and lower-levels only.
There are two ways - black box (UUID - as you suggested) - or well defined purpose (abstraction). For your example 'simple EQ', the parameters should be the band (frequency range) volume values. It's abstract and the real filters (resp. implementation) used behind may depend on the hardware/driver capabilities.
Indeed there is a possibility that the parameters are high-level, but that would require firmware or hardware to be able to generate actual coefficients from those parameters. That usually requires some advanced math which isn't necessarily obvious to implement with fixed-point hardware.
From my view, the really special cases may be handled as black box, but others like (A)SRC should follow some well-defined abstraction IMHO to not force user space to handle all special cases.
I am not against the high-level abstractions, e.g. along the lines of what Android defined: https://developer.android.com/reference/android/media/audiofx/AudioEffect
That's not sufficient however, we also need to make sure there's an ability to provide pre-computed coefficients in an opaque manner for processing that doesn't fit in the well-defined cases. In practice there are very few 3rd party IP that fits in well-defined cases, everyone has secret-sauce parameters and options.
Appreciate the discussion.
Let me explain the reason for the change:
Why I use the metadata ioctl is because the ALSA controls are binding to the sound card. What I want is the controls can be bound to snd_compr_stream, because the ASRC compress sound card can support multi instances ( the ASRC can support multi conversion in parallel). The ALSA controls can't be used for this case, the only choice in current compress API is metadata ioctl. And metadata ioctl can be called many times which can meet the ratio modifier requirement (ratio may be drift on the fly)
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'.
Jaroslav
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.
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 | 769 ++++++++++++++++++++++++++++++++ 4 files changed, 780 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..7be5fb725afc --- /dev/null +++ b/sound/soc/fsl/fsl_asrc_m2m.c @@ -0,0 +1,769 @@ +// 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) + +/* 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_SRC; + + 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_SRC; + 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 snd_compr_runtime *runtime = stream->runtime; + struct fsl_asrc_pair *pair = runtime->private_data; + int ret = 0; + + switch (metadata->key) { + case SNDRV_COMPRESS_SRC_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");
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 (5)
-
Jaroslav Kysela
-
Jeff Brower
-
Pierre-Louis Bossart
-
Shengjiu Wang
-
Shengjiu Wang