[PATCH 00/14] Add support for compress offload and gapless playback.
Add support for compress offload and gapless playback in audioreach platform drivers.
Mohammad Rafi Shaik (14): ALSA: compress: Update compress set params for gapless playback ASoC: qcom: SC7280: audioreach: Add sc7280 hardware param fixup callback ASoC: q6dsp: audioreach: Add placeholder decoder for compress playback ASoC: q6dsp: audioreach: Add support for compress offload commands ASoC: q6dsp: audioreach: Add support to set compress params ASoC: q6dsp: audioreach: Add support for sending real module ID to ADSP ASoC: q6dsp: q6apm-dai: Add async compress write support ASoC: q6dsp: q6apm-dai: Add open/free compress DAI callbacks ASoC: q6dsp: q6apm-dai: Add compress DAI and codec caps get callbacks ASoC: q6dsp: q6apm-dai: Add trigger/pointer compress DAI callbacks ASoC: q6dsp: q6apm-dai: Add compress set params and metadata DAI callbacks ASoC: q6dsp: q6apm-dai: Add mmap and copy compress DAI callbacks ASoC: qdsp6: audioreach: Add MP3, AAC and FLAC compress format support ASoC: q6dsp: audioreach: Add gapless feature support
sound/core/compress_offload.c | 12 +- sound/soc/qcom/qdsp6/audioreach.c | 299 +++++++++++++++++-- sound/soc/qcom/qdsp6/audioreach.h | 56 ++++ sound/soc/qcom/qdsp6/q6apm-dai.c | 464 ++++++++++++++++++++++++++++++ sound/soc/qcom/qdsp6/q6apm.c | 117 ++++++++ sound/soc/qcom/qdsp6/q6apm.h | 8 + sound/soc/qcom/sc7280.c | 21 +- 7 files changed, 950 insertions(+), 27 deletions(-)
Update compress set params for supporting next track settings during gapless playback. Update the runtime state to setup state only if it's in open state. Allow parameter change only when stream has been opened and running state.
Signed-off-by: Mohammad Rafi Shaik quic_mohs@quicinc.com Co-developed-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org Signed-off-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org --- sound/core/compress_offload.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index 243acad89fd3..9b951d76c120 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -589,7 +589,8 @@ snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg) struct snd_compr_params *params; int retval;
- if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) { + if (stream->runtime->state == SNDRV_PCM_STATE_OPEN || + stream->runtime->state == SNDRV_PCM_STATE_RUNNING) { /* * we should allow parameter change only when stream has been * opened not in other cases @@ -612,10 +613,13 @@ snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg) if (retval) goto out;
- stream->metadata_set = false; - stream->next_track = false; + if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) { + stream->metadata_set = false; + stream->next_track = false; + + stream->runtime->state = SNDRV_PCM_STATE_SETUP; + }
- stream->runtime->state = SNDRV_PCM_STATE_SETUP; } else { return -EPERM; }
Add support to set backend params such as sampling rate and number of channels using backend params fixup callback. Also remove hardware params constraints setting.
Signed-off-by: Mohammad Rafi Shaik quic_mohs@quicinc.com Co-developed-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org Signed-off-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org --- sound/soc/qcom/sc7280.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-)
diff --git a/sound/soc/qcom/sc7280.c b/sound/soc/qcom/sc7280.c index da7469a6a267..aaa95fe63d83 100644 --- a/sound/soc/qcom/sc7280.c +++ b/sound/soc/qcom/sc7280.c @@ -14,6 +14,7 @@ #include <sound/soc.h> #include <sound/rt5682s.h> #include <linux/soundwire/sdw.h> +#include <sound/pcm_params.h>
#include "../codecs/rt5682.h" #include "../codecs/rt5682s.h" @@ -24,6 +25,7 @@ #define DEFAULT_MCLK_RATE 19200000 #define RT5682_PLL_FREQ (48000 * 512) #define MI2S_BCLK_RATE 1536000 +#define DEFAULT_SAMPLE_RATE_48K 48000
struct sc7280_snd_data { struct snd_soc_card card; @@ -188,7 +190,6 @@ static int sc7280_init(struct snd_soc_pcm_runtime *rtd) static int sc7280_snd_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai; const struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); @@ -196,8 +197,6 @@ static int sc7280_snd_hw_params(struct snd_pcm_substream *substream, struct sdw_stream_runtime *sruntime; int i;
- snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2); - snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE, 48000, 48000);
switch (cpu_dai->id) { case LPASS_CDC_DMA_TX3: @@ -358,6 +357,20 @@ static const struct snd_soc_dapm_widget sc7280_snd_widgets[] = { SND_SOC_DAPM_MIC("Headset Mic", NULL), };
+static int sc7280_snd_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + rate->min = rate->max = DEFAULT_SAMPLE_RATE_48K; + channels->min = channels->max = 2; + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE); + + return 0; +} + static int sc7280_snd_platform_probe(struct platform_device *pdev) { struct snd_soc_card *card; @@ -387,6 +400,8 @@ static int sc7280_snd_platform_probe(struct platform_device *pdev) for_each_card_prelinks(card, i, link) { link->init = sc7280_init; link->ops = &sc7280_ops; + if (link->no_pcm == 1) + link->be_hw_params_fixup = sc7280_snd_be_hw_params_fixup; }
return devm_snd_soc_register_card(dev, card);
On Wed, Feb 01, 2023 at 07:19:35PM +0530, Mohammad Rafi Shaik wrote:
+#define DEFAULT_SAMPLE_RATE_48K 48000
Why are we bothering with a define here given that the define also encodes the value and it's only used in once place?
for_each_card_prelinks(card, i, link) { link->init = sc7280_init; link->ops = &sc7280_ops;
if (link->no_pcm == 1)
link->be_hw_params_fixup = sc7280_snd_be_hw_params_fixup;
We only set the fixup in the case where there's no PCM but we removed the constraint in all cases - isn't the constraint needed otherwise?
On 2/1/2023 8:10 PM, Mark Brown wrote:
On Wed, Feb 01, 2023 at 07:19:35PM +0530, Mohammad Rafi Shaik wrote:
+#define DEFAULT_SAMPLE_RATE_48K 48000
Why are we bothering with a define here given that the define also encodes the value and it's only used in once place?
okay, will remove it.
for_each_card_prelinks(card, i, link) { link->init = sc7280_init; link->ops = &sc7280_ops;
if (link->no_pcm == 1)
link->be_hw_params_fixup = sc7280_snd_be_hw_params_fixup;
We only set the fixup in the case where there's no PCM but we removed the constraint in all cases - isn't the constraint needed otherwise?
okay, will add conditional check for constraint and will only do if no_pcm is zero.
Add placeholder decoder graph module for compressed playback feature.
Signed-off-by: Mohammad Rafi Shaik quic_mohs@quicinc.com Co-developed-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org Signed-off-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org --- sound/soc/qcom/qdsp6/audioreach.c | 2 ++ sound/soc/qcom/qdsp6/audioreach.h | 39 +++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+)
diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c index 1e0c918eb576..6d3d2a04ffe8 100644 --- a/sound/soc/qcom/qdsp6/audioreach.c +++ b/sound/soc/qcom/qdsp6/audioreach.c @@ -1120,6 +1120,8 @@ int audioreach_set_media_format(struct q6apm_graph *graph, struct audioreach_mod case MODULE_ID_PCM_DEC: case MODULE_ID_PCM_ENC: case MODULE_ID_PCM_CNV: + case MODULE_ID_PLACEHOLDER_DECODER: + case MODULE_ID_PLACEHOLDER_ENCODER: rc = audioreach_pcm_set_media_format(graph, module, cfg); break; case MODULE_ID_I2S_SOURCE: diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h index 1d1d47d47d40..b78fd9bc8eb3 100644 --- a/sound/soc/qcom/qdsp6/audioreach.h +++ b/sound/soc/qcom/qdsp6/audioreach.h @@ -15,6 +15,8 @@ struct q6apm_graph; #define MODULE_ID_PCM_CNV 0x07001003 #define MODULE_ID_PCM_ENC 0x07001004 #define MODULE_ID_PCM_DEC 0x07001005 +#define MODULE_ID_PLACEHOLDER_ENCODER 0x07001008 +#define MODULE_ID_PLACEHOLDER_DECODER 0x07001009 #define MODULE_ID_SAL 0x07001010 #define MODULE_ID_MFC 0x07001015 #define MODULE_ID_CODEC_DMA_SINK 0x07001023 @@ -22,6 +24,10 @@ struct q6apm_graph; #define MODULE_ID_I2S_SINK 0x0700100A #define MODULE_ID_I2S_SOURCE 0x0700100B #define MODULE_ID_DATA_LOGGING 0x0700101A +#define MODULE_ID_AAC_DEC 0x0700101F +#define MODULE_ID_FLAC_DEC 0x0700102F +#define MODULE_ID_MP3_DECODE 0x0700103B +#define MODULE_ID_GAPLESS 0x0700104D
#define APM_CMD_GET_SPF_STATE 0x01001021 #define APM_CMD_RSP_GET_SPF_STATE 0x02001007 @@ -142,12 +148,15 @@ struct param_id_enc_bitrate_param { } __packed;
#define DATA_FORMAT_FIXED_POINT 1 +#define DATA_FORMAT_GENERIC_COMPRESSED 5 +#define DATA_FORMAT_RAW_COMPRESSED 6 #define PCM_LSB_ALIGNED 1 #define PCM_MSB_ALIGNED 2 #define PCM_LITTLE_ENDIAN 1 #define PCM_BIT_ENDIAN 2
#define MEDIA_FMT_ID_PCM 0x09001000 +#define MEDIA_FMT_ID_MP3 0x09001009 #define PCM_CHANNEL_L 1 #define PCM_CHANNEL_R 2 #define SAMPLE_RATE_48K 48000 @@ -225,6 +234,28 @@ struct apm_media_format { uint32_t payload_size; } __packed;
+#define MEDIA_FMT_ID_FLAC 0x09001004 + +struct payload_media_fmt_flac_t { + uint16_t num_channels; + uint16_t sample_size; + uint16_t min_blk_size; + uint16_t max_blk_size; + uint32_t sample_rate; + uint32_t min_frame_size; + uint32_t max_frame_size; +} __packed; + +#define MEDIA_FMT_ID_AAC 0x09001001 + +struct payload_media_fmt_aac_t { + uint16_t aac_fmt_flag; + uint16_t audio_obj_type; + uint16_t num_channels; + uint16_t total_size_of_PCE_bits; + uint32_t sample_rate; +} __packed; + #define DATA_CMD_WR_SH_MEM_EP_EOS 0x04001002 #define WR_SH_MEM_EP_EOS_POLICY_LAST 1 #define WR_SH_MEM_EP_EOS_POLICY_EACH 2 @@ -598,6 +629,13 @@ struct param_id_vol_ctrl_master_gain { } __packed;
+ +#define PARAM_ID_REAL_MODULE_ID 0x0800100B + +struct param_id_placeholder_real_module_id { + uint32_t real_module_id; +} __packed; + /* Graph */ struct audioreach_connection { /* Connections */ @@ -704,6 +742,7 @@ struct audioreach_module_config { u16 active_channels_mask; u32 sd_line_mask; int fmt; + struct snd_codec codec; u8 channel_map[AR_PCM_MAX_NUM_CHANNEL]; };
Add functions to send commands to the ADSP for supporting compressed offload playback. This includes functions to enable module ID, to remove trailing and initial silence. Also add functionality to send 32 bit parameter to ADSP.
Signed-off-by: Mohammad Rafi Shaik quic_mohs@quicinc.com Co-developed-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org Signed-off-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org --- sound/soc/qcom/qdsp6/audioreach.c | 56 +++++++++++++++++++++++++++++++ sound/soc/qcom/qdsp6/audioreach.h | 7 ++++ sound/soc/qcom/qdsp6/q6apm.c | 39 +++++++++++++++++++++ sound/soc/qcom/qdsp6/q6apm.h | 3 ++ 4 files changed, 105 insertions(+)
diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c index 6d3d2a04ffe8..a11bab69a676 100644 --- a/sound/soc/qcom/qdsp6/audioreach.c +++ b/sound/soc/qcom/qdsp6/audioreach.c @@ -1258,3 +1258,59 @@ int audioreach_shared_memory_send_eos(struct q6apm_graph *graph) return rc; } EXPORT_SYMBOL_GPL(audioreach_shared_memory_send_eos); + +int audioreach_send_u32_param(struct q6apm *apm, struct audioreach_module *module, + uint32_t param_id, uint32_t param_val) +{ + struct apm_module_param_data *param_data; + struct gpr_pkt *pkt; + uint32_t *param; + int rc, payload_size; + void *p; + + payload_size = sizeof(uint32_t) + APM_MODULE_PARAM_DATA_SIZE; + p = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); + if (IS_ERR(p)) + return -ENOMEM; + + pkt = p; + p = p + GPR_HDR_SIZE + APM_CMD_HDR_SIZE; + + param_data = p; + param_data->module_instance_id = module->instance_id; + param_data->error_code = 0; + param_data->param_id = param_id; + param_data->param_size = payload_size - APM_MODULE_PARAM_DATA_SIZE; + + p = p + APM_MODULE_PARAM_DATA_SIZE; + param = p; + *param = param_val; + rc = q6apm_send_cmd_sync(apm, pkt, 0); + + kfree(pkt); + + return rc; +} +EXPORT_SYMBOL_GPL(audioreach_send_u32_param); + +int audioreach_remove_trailing_silence(struct q6apm *apm, struct audioreach_module *module, + uint32_t trailing_samples) +{ + return audioreach_send_u32_param(apm, module, PARAM_ID_REMOVE_TRAILING_SILENCE, + trailing_samples); +} +EXPORT_SYMBOL_GPL(audioreach_remove_trailing_silence); + +int audioreach_remove_initial_silence(struct q6apm *apm, struct audioreach_module *module, + uint32_t initial_samples) +{ + return audioreach_send_u32_param(apm, module, PARAM_ID_REMOVE_INITIAL_SILENCE, + initial_samples); +} +EXPORT_SYMBOL_GPL(audioreach_remove_initial_silence); + +int audioreach_enable_module(struct q6apm *apm, struct audioreach_module *module, bool en) +{ + return audioreach_send_u32_param(apm, module, PARAM_ID_MODULE_ENABLE, en); +} +EXPORT_SYMBOL_GPL(audioreach_enable_module); diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h index b78fd9bc8eb3..76dea97773cc 100644 --- a/sound/soc/qcom/qdsp6/audioreach.h +++ b/sound/soc/qcom/qdsp6/audioreach.h @@ -629,6 +629,8 @@ struct param_id_vol_ctrl_master_gain { } __packed;
+#define PARAM_ID_REMOVE_INITIAL_SILENCE 0x0800114B +#define PARAM_ID_REMOVE_TRAILING_SILENCE 0x0800115D
#define PARAM_ID_REAL_MODULE_ID 0x0800100B
@@ -779,4 +781,9 @@ int audioreach_set_media_format(struct q6apm_graph *graph, int audioreach_shared_memory_send_eos(struct q6apm_graph *graph); int audioreach_gain_set_vol_ctrl(struct q6apm *apm, struct audioreach_module *module, int vol); +int audioreach_enable_module(struct q6apm *apm, struct audioreach_module *module, bool en); +int audioreach_remove_initial_silence(struct q6apm *apm, struct audioreach_module *module, + uint32_t initial_samples); +int audioreach_remove_trailing_silence(struct q6apm *apm, struct audioreach_module *module, + uint32_t trailing_samples); #endif /* __AUDIOREACH_H__ */ diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index 8a7dfd27d3c5..78c1a7c13348 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -297,6 +297,45 @@ int q6apm_unmap_memory_regions(struct q6apm_graph *graph, unsigned int dir) } EXPORT_SYMBOL_GPL(q6apm_unmap_memory_regions);
+int q6apm_remove_initial_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples) +{ + struct q6apm *apm = dev_get_drvdata(dev->parent); + struct audioreach_module *module; + + module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER); + if (!module) + return -ENODEV; + + return audioreach_remove_initial_silence(apm, module, samples); +} +EXPORT_SYMBOL_GPL(q6apm_remove_initial_silence); + +int q6apm_remove_trailing_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples) +{ + struct q6apm *apm = dev_get_drvdata(dev->parent); + struct audioreach_module *module; + + module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER); + if (!module) + return -ENODEV; + + return audioreach_remove_trailing_silence(apm, module, samples); +} +EXPORT_SYMBOL_GPL(q6apm_remove_trailing_silence); + +int q6apm_enable_compress_module(struct device *dev, struct q6apm_graph *graph, bool en) +{ + struct q6apm *apm = dev_get_drvdata(dev->parent); + struct audioreach_module *module; + + module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER); + if (!module) + return -ENODEV; + + return audioreach_enable_module(apm, module, en); +} +EXPORT_SYMBOL_GPL(q6apm_enable_compress_module); + int q6apm_graph_media_format_pcm(struct q6apm_graph *graph, struct audioreach_module_config *cfg) { struct audioreach_graph_info *info = graph->info; diff --git a/sound/soc/qcom/qdsp6/q6apm.h b/sound/soc/qcom/qdsp6/q6apm.h index 7005be9b63e3..08b64f78c750 100644 --- a/sound/soc/qcom/qdsp6/q6apm.h +++ b/sound/soc/qcom/qdsp6/q6apm.h @@ -147,4 +147,7 @@ int q6apm_graph_get_rx_shmem_module_iid(struct q6apm_graph *graph);
bool q6apm_is_adsp_ready(void);
+int q6apm_enable_compress_module(struct device *dev, struct q6apm_graph *graph, bool en); +int q6apm_remove_initial_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples); +int q6apm_remove_trailing_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples); #endif /* __APM_GRAPH_ */
Hi Mohammad,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on broonie-sound/for-next] [also build test WARNING on next-20230201] [cannot apply to tiwai-sound/for-next tiwai-sound/for-linus linus/master v6.2-rc6] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Mohammad-Rafi-Shaik/ALSA-comp... base: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next patch link: https://lore.kernel.org/r/20230201134947.1638197-5-quic_mohs%40quicinc.com patch subject: [PATCH 04/14] ASoC: q6dsp: audioreach: Add support for compress offload commands config: m68k-allyesconfig (https://download.01.org/0day-ci/archive/20230201/202302012309.BtyJn8FN-lkp@i...) compiler: m68k-linux-gcc (GCC) 12.1.0 reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/intel-lab-lkp/linux/commit/6a2982489303bcf32b927da80e4baf... git remote add linux-review https://github.com/intel-lab-lkp/linux git fetch --no-tags linux-review Mohammad-Rafi-Shaik/ALSA-compress-Update-compress-set-params-for-gapless-playback/20230201-215622 git checkout 6a2982489303bcf32b927da80e4baffae58437e0 # save the config file mkdir build_dir && cp config build_dir/.config COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=m68k olddefconfig COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=m68k SHELL=/bin/bash sound/soc/qcom/qdsp6/
If you fix the issue, kindly add following tag where applicable | Reported-by: kernel test robot lkp@intel.com
All warnings (new ones prefixed by >>):
sound/soc/qcom/qdsp6/audioreach.c:1262:5: warning: no previous prototype for 'audioreach_send_u32_param' [-Wmissing-prototypes]
1262 | int audioreach_send_u32_param(struct q6apm *apm, struct audioreach_module *module, | ^~~~~~~~~~~~~~~~~~~~~~~~~
vim +/audioreach_send_u32_param +1262 sound/soc/qcom/qdsp6/audioreach.c
1261
1262 int audioreach_send_u32_param(struct q6apm *apm, struct audioreach_module *module,
1263 uint32_t param_id, uint32_t param_val) 1264 { 1265 struct apm_module_param_data *param_data; 1266 struct gpr_pkt *pkt; 1267 uint32_t *param; 1268 int rc, payload_size; 1269 void *p; 1270 1271 payload_size = sizeof(uint32_t) + APM_MODULE_PARAM_DATA_SIZE; 1272 p = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); 1273 if (IS_ERR(p)) 1274 return -ENOMEM; 1275 1276 pkt = p; 1277 p = p + GPR_HDR_SIZE + APM_CMD_HDR_SIZE; 1278 1279 param_data = p; 1280 param_data->module_instance_id = module->instance_id; 1281 param_data->error_code = 0; 1282 param_data->param_id = param_id; 1283 param_data->param_size = payload_size - APM_MODULE_PARAM_DATA_SIZE; 1284 1285 p = p + APM_MODULE_PARAM_DATA_SIZE; 1286 param = p; 1287 *param = param_val; 1288 rc = q6apm_send_cmd_sync(apm, pkt, 0); 1289 1290 kfree(pkt); 1291 1292 return rc; 1293 } 1294 EXPORT_SYMBOL_GPL(audioreach_send_u32_param); 1295
Add function for setting compress params to set the next track parameters during gapless playback.
Signed-off-by: Mohammad Rafi Shaik quic_mohs@quicinc.com Co-developed-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org Signed-off-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org --- sound/soc/qcom/qdsp6/audioreach.c | 51 +++++++++++++++++++++++++++++++ sound/soc/qcom/qdsp6/audioreach.h | 1 + sound/soc/qcom/qdsp6/q6apm-dai.c | 1 + 3 files changed, 53 insertions(+)
diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c index a11bab69a676..a87df09d187f 100644 --- a/sound/soc/qcom/qdsp6/audioreach.c +++ b/sound/soc/qcom/qdsp6/audioreach.c @@ -1314,3 +1314,54 @@ int audioreach_enable_module(struct q6apm *apm, struct audioreach_module *module return audioreach_send_u32_param(apm, module, PARAM_ID_MODULE_ENABLE, en); } EXPORT_SYMBOL_GPL(audioreach_enable_module); + +int audioreach_compr_set_param(struct q6apm_graph *graph, struct audioreach_module_config *mcfg) +{ + struct media_format *header; + struct gpr_pkt *pkt; + struct payload_media_fmt_pcm *cfg; + uint32_t num_channels = mcfg->num_channels; + int iid, payload_size, rc; + void *p; + + payload_size = sizeof(struct apm_sh_module_media_fmt_cmd); + + iid = q6apm_graph_get_rx_shmem_module_iid(graph); + pkt = audioreach_alloc_cmd_pkt(payload_size, DATA_CMD_WR_SH_MEM_EP_MEDIA_FORMAT, + 0, graph->port->id, iid); + + if (IS_ERR(pkt)) + return -ENOMEM; + + p = (void *)pkt + GPR_HDR_SIZE; + header = p; + + if (mcfg->fmt == SND_AUDIOCODEC_PCM) { + header->data_format = DATA_FORMAT_FIXED_POINT; + header->fmt_id = MEDIA_FMT_ID_PCM; + header->payload_size = sizeof(*cfg); + + p = p + sizeof(*header); + cfg = p; + cfg->sample_rate = mcfg->sample_rate; + cfg->bit_width = mcfg->bit_width; + cfg->alignment = PCM_LSB_ALIGNED; + cfg->bits_per_sample = mcfg->bit_width; + cfg->q_factor = mcfg->bit_width - 1; + cfg->endianness = PCM_LITTLE_ENDIAN; + cfg->num_channels = mcfg->num_channels; + + if (mcfg->num_channels == 1) + cfg->channel_mapping[0] = PCM_CHANNEL_L; + else if (num_channels == 2) { + cfg->channel_mapping[0] = PCM_CHANNEL_L; + cfg->channel_mapping[1] = PCM_CHANNEL_R; + } + } + + rc = gpr_send_port_pkt(graph->port, pkt); + kfree(pkt); + + return rc; +} +EXPORT_SYMBOL_GPL(audioreach_compr_set_param); diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h index 76dea97773cc..4c4bdff45cf1 100644 --- a/sound/soc/qcom/qdsp6/audioreach.h +++ b/sound/soc/qcom/qdsp6/audioreach.h @@ -786,4 +786,5 @@ int audioreach_remove_initial_silence(struct q6apm *apm, struct audioreach_modul uint32_t initial_samples); int audioreach_remove_trailing_silence(struct q6apm *apm, struct audioreach_module *module, uint32_t trailing_samples); +int audioreach_compr_set_param(struct q6apm_graph *graph, struct audioreach_module_config *mcfg); #endif /* __AUDIOREACH_H__ */ diff --git a/sound/soc/qcom/qdsp6/q6apm-dai.c b/sound/soc/qcom/qdsp6/q6apm-dai.c index ee59ef36b85a..8f5d744b3365 100644 --- a/sound/soc/qcom/qdsp6/q6apm-dai.c +++ b/sound/soc/qcom/qdsp6/q6apm-dai.c @@ -146,6 +146,7 @@ static int q6apm_dai_prepare(struct snd_soc_component *component, cfg.sample_rate = runtime->rate; cfg.num_channels = runtime->channels; cfg.bit_width = prtd->bits_per_sample; + cfg.fmt = SND_AUDIOCODEC_PCM;
if (prtd->state) { /* clear the previous setup if any */
Add support for sending the placeholder real module ID to ADSP for enabling compressed playback.
Signed-off-by: Mohammad Rafi Shaik quic_mohs@quicinc.com Co-developed-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org Signed-off-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org --- sound/soc/qcom/qdsp6/audioreach.c | 6 ++++++ sound/soc/qcom/qdsp6/audioreach.h | 1 + sound/soc/qcom/qdsp6/q6apm.c | 29 +++++++++++++++++++++++++++++ sound/soc/qcom/qdsp6/q6apm.h | 1 + 4 files changed, 37 insertions(+)
diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c index a87df09d187f..e84ccbacc0f7 100644 --- a/sound/soc/qcom/qdsp6/audioreach.c +++ b/sound/soc/qcom/qdsp6/audioreach.c @@ -1315,6 +1315,12 @@ int audioreach_enable_module(struct q6apm *apm, struct audioreach_module *module } EXPORT_SYMBOL_GPL(audioreach_enable_module);
+int audioreach_set_real_module_id(struct q6apm *apm, struct audioreach_module *module, uint32_t id) +{ + return audioreach_send_u32_param(apm, module, PARAM_ID_REAL_MODULE_ID, id); +} +EXPORT_SYMBOL_GPL(audioreach_set_real_module_id); + int audioreach_compr_set_param(struct q6apm_graph *graph, struct audioreach_module_config *mcfg) { struct media_format *header; diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h index 4c4bdff45cf1..0faaf75115fd 100644 --- a/sound/soc/qcom/qdsp6/audioreach.h +++ b/sound/soc/qcom/qdsp6/audioreach.h @@ -787,4 +787,5 @@ int audioreach_remove_initial_silence(struct q6apm *apm, struct audioreach_modul int audioreach_remove_trailing_silence(struct q6apm *apm, struct audioreach_module *module, uint32_t trailing_samples); int audioreach_compr_set_param(struct q6apm_graph *graph, struct audioreach_module_config *mcfg); +int audioreach_set_real_module_id(struct q6apm *apm, struct audioreach_module *module, uint32_t id); #endif /* __AUDIOREACH_H__ */ diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index 78c1a7c13348..811d86bdc092 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -336,6 +336,35 @@ int q6apm_enable_compress_module(struct device *dev, struct q6apm_graph *graph, } EXPORT_SYMBOL_GPL(q6apm_enable_compress_module);
+int q6apm_set_real_module_id(struct device *dev, struct q6apm_graph *graph, + uint32_t codec_id) +{ + struct q6apm *apm = dev_get_drvdata(dev->parent); + struct audioreach_module *module; + uint32_t module_id; + + module = q6apm_find_module_by_mid(graph, MODULE_ID_PLACEHOLDER_DECODER); + if (!module) + return -ENODEV; + + switch (codec_id) { + case SND_AUDIOCODEC_MP3: + module_id = MODULE_ID_MP3_DECODE; + break; + case SND_AUDIOCODEC_AAC: + module_id = MODULE_ID_AAC_DEC; + break; + case SND_AUDIOCODEC_FLAC: + module_id = MODULE_ID_FLAC_DEC; + break; + default: + return -EINVAL; + } + + return audioreach_set_real_module_id(apm, module, module_id); +} +EXPORT_SYMBOL_GPL(q6apm_set_real_module_id); + int q6apm_graph_media_format_pcm(struct q6apm_graph *graph, struct audioreach_module_config *cfg) { struct audioreach_graph_info *info = graph->info; diff --git a/sound/soc/qcom/qdsp6/q6apm.h b/sound/soc/qcom/qdsp6/q6apm.h index 08b64f78c750..87d67faf5f1a 100644 --- a/sound/soc/qcom/qdsp6/q6apm.h +++ b/sound/soc/qcom/qdsp6/q6apm.h @@ -150,4 +150,5 @@ bool q6apm_is_adsp_ready(void); int q6apm_enable_compress_module(struct device *dev, struct q6apm_graph *graph, bool en); int q6apm_remove_initial_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples); int q6apm_remove_trailing_silence(struct device *dev, struct q6apm_graph *graph, uint32_t samples); +int q6apm_set_real_module_id(struct device *dev, struct q6apm_graph *graph, uint32_t codec_id); #endif /* __APM_GRAPH_ */
Add async compress write API to send the compressed audio data packet to ADSP.
Signed-off-by: Mohammad Rafi Shaik quic_mohs@quicinc.com Co-developed-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org Signed-off-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org --- sound/soc/qcom/qdsp6/audioreach.c | 38 +++++++++++++++++++++++++ sound/soc/qcom/qdsp6/audioreach.h | 2 ++ sound/soc/qcom/qdsp6/q6apm-dai.c | 7 +++++ sound/soc/qcom/qdsp6/q6apm.c | 46 +++++++++++++++++++++++++++++++ sound/soc/qcom/qdsp6/q6apm.h | 3 ++ 5 files changed, 96 insertions(+)
diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c index e84ccbacc0f7..7c45c36e9156 100644 --- a/sound/soc/qcom/qdsp6/audioreach.c +++ b/sound/soc/qcom/qdsp6/audioreach.c @@ -240,6 +240,44 @@ void *audioreach_alloc_pkt(int payload_size, uint32_t opcode, uint32_t token, } EXPORT_SYMBOL_GPL(audioreach_alloc_pkt);
+static void __audioreach_update_pkt(struct gpr_pkt *pkt, int payload_size, uint32_t opcode, + uint32_t token, uint32_t src_port, uint32_t dest_port, + bool has_cmd_hdr) +{ + int pkt_size = GPR_HDR_SIZE + payload_size; + void *p; + + if (has_cmd_hdr) + pkt_size += APM_CMD_HDR_SIZE; + + p = pkt; + pkt->hdr.version = GPR_PKT_VER; + pkt->hdr.hdr_size = GPR_PKT_HEADER_WORD_SIZE; + pkt->hdr.pkt_size = pkt_size; + pkt->hdr.dest_port = dest_port; + pkt->hdr.src_port = src_port; + + pkt->hdr.dest_domain = GPR_DOMAIN_ID_ADSP; + pkt->hdr.src_domain = GPR_DOMAIN_ID_APPS; + pkt->hdr.token = token; + pkt->hdr.opcode = opcode; + + if (has_cmd_hdr) { + struct apm_cmd_header *cmd_header; + + p = p + GPR_HDR_SIZE; + cmd_header = p; + cmd_header->payload_size = payload_size; + } +} + +void audioreach_update_pkt(struct gpr_pkt *pkt, int payload_size, uint32_t opcode, uint32_t token, + uint32_t src_port, uint32_t dest_port) +{ + __audioreach_update_pkt(pkt, payload_size, opcode, token, src_port, dest_port, false); +} +EXPORT_SYMBOL_GPL(audioreach_update_pkt); + void *audioreach_alloc_apm_pkt(int pkt_size, uint32_t opcode, uint32_t token, uint32_t src_port) { return __audioreach_alloc_pkt(pkt_size, opcode, token, src_port, APM_MODULE_INSTANCE_ID, diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h index 0faaf75115fd..044994ca4811 100644 --- a/sound/soc/qcom/qdsp6/audioreach.h +++ b/sound/soc/qcom/qdsp6/audioreach.h @@ -788,4 +788,6 @@ int audioreach_remove_trailing_silence(struct q6apm *apm, struct audioreach_modu uint32_t trailing_samples); int audioreach_compr_set_param(struct q6apm_graph *graph, struct audioreach_module_config *mcfg); int audioreach_set_real_module_id(struct q6apm *apm, struct audioreach_module *module, uint32_t id); +void audioreach_update_pkt(struct gpr_pkt *pkt, int payload_size, uint32_t opcode, + uint32_t token, uint32_t src_port, uint32_t dest_port); #endif /* __AUDIOREACH_H__ */ diff --git a/sound/soc/qcom/qdsp6/q6apm-dai.c b/sound/soc/qcom/qdsp6/q6apm-dai.c index 8f5d744b3365..e621e31294a1 100644 --- a/sound/soc/qcom/qdsp6/q6apm-dai.c +++ b/sound/soc/qcom/qdsp6/q6apm-dai.c @@ -38,8 +38,10 @@ enum stream_state { struct q6apm_dai_rtd { struct snd_pcm_substream *substream; struct snd_compr_stream *cstream; + struct snd_codec codec; struct snd_compr_params codec_param; struct snd_dma_buffer dma_buffer; + spinlock_t lock; phys_addr_t phys; unsigned int pcm_size; unsigned int pcm_count; @@ -51,8 +53,13 @@ struct q6apm_dai_rtd { uint16_t bits_per_sample; uint16_t source; /* Encoding source bit mask */ uint16_t session_id; + bool next_track; enum stream_state state; struct q6apm_graph *graph; + uint32_t initial_samples_drop; + uint32_t trailing_samples_drop; + uint32_t next_track_stream_id; + bool notify_on_drain; };
struct q6apm_dai_data { diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index 811d86bdc092..1a6c7108bae0 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -25,6 +25,8 @@ struct apm_graph_mgmt_cmd { uint32_t sub_graph_id_list[]; } __packed;
+struct gpr_pkt *pkt; + #define APM_GRAPH_MGMT_PSIZE(p, n) ALIGN(struct_size(p, sub_graph_id_list, n), 8)
struct q6apm *g_apm; @@ -457,6 +459,45 @@ int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts, } EXPORT_SYMBOL_GPL(q6apm_write_async);
+int q6apm_write_async_compr(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts, + uint32_t lsw_ts, uint32_t wflags) +{ + struct apm_data_cmd_wr_sh_mem_ep_data_buffer_v2 *write_buffer; + struct audio_buffer *ab; + + int rc, iid; + + iid = q6apm_graph_get_rx_shmem_module_iid(graph); + + audioreach_update_pkt(pkt, sizeof(*write_buffer), DATA_CMD_WR_SH_MEM_EP_DATA_BUFFER_V2, + graph->rx_data.dsp_buf | (len << APM_WRITE_TOKEN_LEN_SHIFT), + graph->port->id, iid); + + write_buffer = (void *)pkt + GPR_HDR_SIZE; + + ab = &graph->rx_data.buf[graph->rx_data.dsp_buf]; + + write_buffer->buf_addr_lsw = lower_32_bits(ab->phys); + write_buffer->buf_addr_msw = upper_32_bits(ab->phys); + write_buffer->buf_size = len; + write_buffer->timestamp_lsw = lsw_ts; + write_buffer->timestamp_msw = msw_ts; + write_buffer->mem_map_handle = graph->rx_data.mem_map_handle; + write_buffer->flags = wflags; + + graph->rx_data.dsp_buf++; + + if (graph->rx_data.dsp_buf >= graph->rx_data.num_periods) + graph->rx_data.dsp_buf = 0; + + rc = gpr_send_port_pkt(graph->port, pkt); + + memset(pkt, 0, sizeof(write_buffer) + GPR_HDR_SIZE); + + return rc; +} +EXPORT_SYMBOL_GPL(q6apm_write_async_compr); + int q6apm_read(struct q6apm_graph *graph) { struct data_cmd_rd_sh_mem_ep_data_buffer_v2 *read_buffer; @@ -724,6 +765,11 @@ static int apm_probe(gpr_device_t *gdev)
dev_set_drvdata(dev, apm);
+ pkt = devm_kzalloc(dev, sizeof(struct apm_data_cmd_wr_sh_mem_ep_data_buffer_v2) + + GPR_HDR_SIZE, GFP_KERNEL); + if (!pkt) + return -ENOMEM; + mutex_init(&apm->lock); apm->dev = dev; apm->gdev = gdev; diff --git a/sound/soc/qcom/qdsp6/q6apm.h b/sound/soc/qcom/qdsp6/q6apm.h index 87d67faf5f1a..630c2bca0f06 100644 --- a/sound/soc/qcom/qdsp6/q6apm.h +++ b/sound/soc/qcom/qdsp6/q6apm.h @@ -45,6 +45,7 @@ #define APM_WRITE_TOKEN_LEN_SHIFT 16
#define APM_MAX_SESSIONS 8 +#define APM_LAST_BUFFER_FLAG BIT(30)
struct q6apm { struct device *dev; @@ -128,6 +129,8 @@ int q6apm_send_eos_nowait(struct q6apm_graph *graph); int q6apm_read(struct q6apm_graph *graph); int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts, uint32_t lsw_ts, uint32_t wflags); +int q6apm_write_async_compr(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts, + uint32_t lsw_ts, uint32_t wflags);
/* Memory Map related */ int q6apm_map_memory_regions(struct q6apm_graph *graph,
Add q6apm open and free compress DAI callbacks to support compress offload playback. Include compress event handler callback also.
Signed-off-by: Mohammad Rafi Shaik quic_mohs@quicinc.com Co-developed-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org Signed-off-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org --- sound/soc/qcom/qdsp6/q6apm-dai.c | 135 +++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+)
diff --git a/sound/soc/qcom/qdsp6/q6apm-dai.c b/sound/soc/qcom/qdsp6/q6apm-dai.c index e621e31294a1..fd134c268189 100644 --- a/sound/soc/qcom/qdsp6/q6apm-dai.c +++ b/sound/soc/qcom/qdsp6/q6apm-dai.c @@ -27,6 +27,8 @@ #define CAPTURE_MIN_PERIOD_SIZE 320 #define BUFFER_BYTES_MAX (PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE) #define BUFFER_BYTES_MIN (PLAYBACK_MIN_NUM_PERIODS * PLAYBACK_MIN_PERIOD_SIZE) +#define COMPR_PLAYBACK_MAX_FRAGMENT_SIZE (128 * 1024) +#define COMPR_PLAYBACK_MAX_NUM_FRAGMENTS (16 * 4) #define SID_MASK_DEFAULT 0xF
enum stream_state { @@ -130,6 +132,69 @@ static void event_handler(uint32_t opcode, uint32_t token, uint32_t *payload, vo } }
+void event_handler_compr(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + struct q6apm_dai_rtd *prtd = priv; + struct snd_compr_stream *substream = prtd->cstream; + unsigned long flags; + uint32_t wflags = 0; + uint64_t avail; + uint32_t bytes_written, bytes_to_write; + bool is_last_buffer = false; + + switch (opcode) { + case APM_CLIENT_EVENT_CMD_EOS_DONE: + spin_lock_irqsave(&prtd->lock, flags); + if (prtd->notify_on_drain) { + snd_compr_drain_notify(prtd->cstream); + prtd->notify_on_drain = false; + } else { + prtd->state = Q6APM_STREAM_STOPPED; + } + spin_unlock_irqrestore(&prtd->lock, flags); + break; + case APM_CLIENT_EVENT_DATA_WRITE_DONE: + spin_lock_irqsave(&prtd->lock, flags); + bytes_written = token >> APM_WRITE_TOKEN_LEN_SHIFT; + prtd->copied_total += bytes_written; + snd_compr_fragment_elapsed(substream); + + if (prtd->state != Q6APM_STREAM_RUNNING) { + spin_unlock_irqrestore(&prtd->lock, flags); + break; + } + + avail = prtd->bytes_received - prtd->bytes_sent; + + if (avail > prtd->pcm_count) { + bytes_to_write = prtd->pcm_count; + } else { + if (substream->partial_drain || prtd->notify_on_drain) + is_last_buffer = true; + bytes_to_write = avail; + } + + if (bytes_to_write) { + if (substream->partial_drain && is_last_buffer) + wflags |= APM_LAST_BUFFER_FLAG; + + q6apm_write_async_compr(prtd->graph, + bytes_to_write, 0, 0, wflags); + + prtd->bytes_sent += bytes_to_write; + + if (prtd->notify_on_drain && is_last_buffer) + audioreach_shared_memory_send_eos(prtd->graph); + } + + spin_unlock_irqrestore(&prtd->lock, flags); + break; + default: + break; + } +} + static int q6apm_dai_prepare(struct snd_soc_component *component, struct snd_pcm_substream *substream) { @@ -378,6 +443,75 @@ static int q6apm_dai_pcm_new(struct snd_soc_component *component, struct snd_soc return snd_pcm_set_fixed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, component->dev, size); }
+static int q6apm_dai_compr_open(struct snd_soc_component *component, + struct snd_compr_stream *stream) +{ + struct snd_soc_pcm_runtime *rtd = stream->private_data; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct snd_compr_runtime *runtime = stream->runtime; + struct q6apm_dai_rtd *prtd; + struct q6apm_dai_data *pdata; + struct device *dev = component->dev; + int ret, size; + int graph_id; + + graph_id = cpu_dai->driver->id; + pdata = snd_soc_component_get_drvdata(component); + if (!pdata) + return -EINVAL; + + prtd = kzalloc(sizeof(*prtd), GFP_KERNEL); + if (prtd == NULL) + return -ENOMEM; + + prtd->cstream = stream; + prtd->graph = q6apm_graph_open(dev, (q6apm_cb)event_handler_compr, prtd, graph_id); + if (IS_ERR(prtd->graph)) { + ret = PTR_ERR(prtd->graph); + kfree(prtd); + return ret; + } + + runtime->private_data = prtd; + runtime->dma_bytes = BUFFER_BYTES_MAX; + size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE * COMPR_PLAYBACK_MAX_NUM_FRAGMENTS; + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size, &prtd->dma_buffer); + if (ret) + return ret; + + if (pdata->sid < 0) + prtd->phys = prtd->dma_buffer.addr; + else + prtd->phys = prtd->dma_buffer.addr | (pdata->sid << 32); + + snd_compr_set_runtime_buffer(stream, &prtd->dma_buffer); + spin_lock_init(&prtd->lock); + + q6apm_enable_compress_module(dev, prtd->graph, true); + return 0; +} + +static int q6apm_dai_compr_free(struct snd_soc_component *component, + struct snd_compr_stream *stream) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct q6apm_dai_rtd *prtd = runtime->private_data; + + q6apm_graph_stop(prtd->graph); + q6apm_unmap_memory_regions(prtd->graph, SNDRV_PCM_STREAM_PLAYBACK); + q6apm_graph_close(prtd->graph); + snd_dma_free_pages(&prtd->dma_buffer); + prtd->graph = NULL; + kfree(prtd); + runtime->private_data = NULL; + + return 0; +} +static const struct snd_compress_ops q6apm_dai_compress_ops = { + .open = q6apm_dai_compr_open, + .free = q6apm_dai_compr_free, +}; + static const struct snd_soc_component_driver q6apm_fe_dai_component = { .name = DRV_NAME, .open = q6apm_dai_open, @@ -387,6 +521,7 @@ static const struct snd_soc_component_driver q6apm_fe_dai_component = { .hw_params = q6apm_dai_hw_params, .pointer = q6apm_dai_pointer, .trigger = q6apm_dai_trigger, + .compress_ops = &q6apm_dai_compress_ops, };
static int q6apm_dai_probe(struct platform_device *pdev)
Hi Mohammad,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on broonie-sound/for-next] [also build test WARNING on next-20230201] [cannot apply to tiwai-sound/for-next tiwai-sound/for-linus linus/master v6.2-rc6] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Mohammad-Rafi-Shaik/ALSA-comp... base: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next patch link: https://lore.kernel.org/r/20230201134947.1638197-9-quic_mohs%40quicinc.com patch subject: [PATCH 08/14] ASoC: q6dsp: q6apm-dai: Add open/free compress DAI callbacks config: m68k-allyesconfig (https://download.01.org/0day-ci/archive/20230201/202302012337.pC5Q3lLy-lkp@i...) compiler: m68k-linux-gcc (GCC) 12.1.0 reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/intel-lab-lkp/linux/commit/78a6016e006a8e405679fd335940ee... git remote add linux-review https://github.com/intel-lab-lkp/linux git fetch --no-tags linux-review Mohammad-Rafi-Shaik/ALSA-compress-Update-compress-set-params-for-gapless-playback/20230201-215622 git checkout 78a6016e006a8e405679fd335940ee710416c43f # save the config file mkdir build_dir && cp config build_dir/.config COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=m68k olddefconfig COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=m68k SHELL=/bin/bash sound/soc/qcom/qdsp6/
If you fix the issue, kindly add following tag where applicable | Reported-by: kernel test robot lkp@intel.com
All warnings (new ones prefixed by >>):
sound/soc/qcom/qdsp6/q6apm-dai.c:135:6: warning: no previous prototype for 'event_handler_compr' [-Wmissing-prototypes]
135 | void event_handler_compr(uint32_t opcode, uint32_t token, | ^~~~~~~~~~~~~~~~~~~
vim +/event_handler_compr +135 sound/soc/qcom/qdsp6/q6apm-dai.c
134
135 void event_handler_compr(uint32_t opcode, uint32_t token,
136 uint32_t *payload, void *priv) 137 { 138 struct q6apm_dai_rtd *prtd = priv; 139 struct snd_compr_stream *substream = prtd->cstream; 140 unsigned long flags; 141 uint32_t wflags = 0; 142 uint64_t avail; 143 uint32_t bytes_written, bytes_to_write; 144 bool is_last_buffer = false; 145 146 switch (opcode) { 147 case APM_CLIENT_EVENT_CMD_EOS_DONE: 148 spin_lock_irqsave(&prtd->lock, flags); 149 if (prtd->notify_on_drain) { 150 snd_compr_drain_notify(prtd->cstream); 151 prtd->notify_on_drain = false; 152 } else { 153 prtd->state = Q6APM_STREAM_STOPPED; 154 } 155 spin_unlock_irqrestore(&prtd->lock, flags); 156 break; 157 case APM_CLIENT_EVENT_DATA_WRITE_DONE: 158 spin_lock_irqsave(&prtd->lock, flags); 159 bytes_written = token >> APM_WRITE_TOKEN_LEN_SHIFT; 160 prtd->copied_total += bytes_written; 161 snd_compr_fragment_elapsed(substream); 162 163 if (prtd->state != Q6APM_STREAM_RUNNING) { 164 spin_unlock_irqrestore(&prtd->lock, flags); 165 break; 166 } 167 168 avail = prtd->bytes_received - prtd->bytes_sent; 169 170 if (avail > prtd->pcm_count) { 171 bytes_to_write = prtd->pcm_count; 172 } else { 173 if (substream->partial_drain || prtd->notify_on_drain) 174 is_last_buffer = true; 175 bytes_to_write = avail; 176 } 177 178 if (bytes_to_write) { 179 if (substream->partial_drain && is_last_buffer) 180 wflags |= APM_LAST_BUFFER_FLAG; 181 182 q6apm_write_async_compr(prtd->graph, 183 bytes_to_write, 0, 0, wflags); 184 185 prtd->bytes_sent += bytes_to_write; 186 187 if (prtd->notify_on_drain && is_last_buffer) 188 audioreach_shared_memory_send_eos(prtd->graph); 189 } 190 191 spin_unlock_irqrestore(&prtd->lock, flags); 192 break; 193 default: 194 break; 195 } 196 } 197
Add q6apm get compress DAI capabilities and codec capabilities callbacks to support compress offload playback.
Signed-off-by: Mohammad Rafi Shaik quic_mohs@quicinc.com Co-developed-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org Signed-off-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org --- sound/soc/qcom/qdsp6/q6apm-dai.c | 51 ++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+)
diff --git a/sound/soc/qcom/qdsp6/q6apm-dai.c b/sound/soc/qcom/qdsp6/q6apm-dai.c index fd134c268189..54e1aca61e4c 100644 --- a/sound/soc/qcom/qdsp6/q6apm-dai.c +++ b/sound/soc/qcom/qdsp6/q6apm-dai.c @@ -29,8 +29,25 @@ #define BUFFER_BYTES_MIN (PLAYBACK_MIN_NUM_PERIODS * PLAYBACK_MIN_PERIOD_SIZE) #define COMPR_PLAYBACK_MAX_FRAGMENT_SIZE (128 * 1024) #define COMPR_PLAYBACK_MAX_NUM_FRAGMENTS (16 * 4) +#define COMPR_PLAYBACK_MIN_FRAGMENT_SIZE (8 * 1024) +#define COMPR_PLAYBACK_MIN_NUM_FRAGMENTS (4) #define SID_MASK_DEFAULT 0xF
+static const struct snd_compr_codec_caps q6apm_compr_caps = { + .num_descriptors = 1, + .descriptor[0].max_ch = 2, + .descriptor[0].sample_rates = { 8000, 11025, 12000, 16000, 22050, + 24000, 32000, 44100, 48000, 88200, + 96000, 176400, 192000 }, + .descriptor[0].num_sample_rates = 13, + .descriptor[0].bit_rate[0] = 320, + .descriptor[0].bit_rate[1] = 128, + .descriptor[0].num_bitrates = 2, + .descriptor[0].profiles = 0, + .descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO, + .descriptor[0].formats = 0, +}; + enum stream_state { Q6APM_STREAM_IDLE = 0, Q6APM_STREAM_STOPPED, @@ -507,9 +524,43 @@ static int q6apm_dai_compr_free(struct snd_soc_component *component,
return 0; } + +static int q6apm_dai_compr_get_caps(struct snd_soc_component *component, + struct snd_compr_stream *stream, + struct snd_compr_caps *caps) +{ + caps->direction = SND_COMPRESS_PLAYBACK; + caps->min_fragment_size = COMPR_PLAYBACK_MIN_FRAGMENT_SIZE; + caps->max_fragment_size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE; + caps->min_fragments = COMPR_PLAYBACK_MIN_NUM_FRAGMENTS; + caps->max_fragments = COMPR_PLAYBACK_MAX_NUM_FRAGMENTS; + caps->num_codecs = 3; + caps->codecs[0] = SND_AUDIOCODEC_MP3; + caps->codecs[1] = SND_AUDIOCODEC_AAC; + caps->codecs[2] = SND_AUDIOCODEC_FLAC; + + return 0; +} + +static int q6apm_dai_compr_get_codec_caps(struct snd_soc_component *component, + struct snd_compr_stream *stream, + struct snd_compr_codec_caps *codec) +{ + switch (codec->codec) { + case SND_AUDIOCODEC_MP3: + *codec = q6apm_compr_caps; + break; + default: + break; + } + + return 0; +} static const struct snd_compress_ops q6apm_dai_compress_ops = { .open = q6apm_dai_compr_open, .free = q6apm_dai_compr_free, + .get_caps = q6apm_dai_compr_get_caps, + .get_codec_caps = q6apm_dai_compr_get_codec_caps, };
static const struct snd_soc_component_driver q6apm_fe_dai_component = {
Add q6apm trigger and pointer compress DAI callbacks to support compress offload playback.
Signed-off-by: Mohammad Rafi Shaik quic_mohs@quicinc.com Co-developed-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org Signed-off-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org --- sound/soc/qcom/qdsp6/q6apm-dai.c | 68 ++++++++++++++++++++++++++++++++ sound/soc/qcom/qdsp6/q6apm.h | 1 + 2 files changed, 69 insertions(+)
diff --git a/sound/soc/qcom/qdsp6/q6apm-dai.c b/sound/soc/qcom/qdsp6/q6apm-dai.c index 54e1aca61e4c..f43b60742e2f 100644 --- a/sound/soc/qcom/qdsp6/q6apm-dai.c +++ b/sound/soc/qcom/qdsp6/q6apm-dai.c @@ -556,11 +556,79 @@ static int q6apm_dai_compr_get_codec_caps(struct snd_soc_component *component,
return 0; } + +static int q6apm_dai_compr_pointer(struct snd_soc_component *component, + struct snd_compr_stream *stream, + struct snd_compr_tstamp *tstamp) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct q6apm_dai_rtd *prtd = runtime->private_data; + unsigned long flags; + + spin_lock_irqsave(&prtd->lock, flags); + tstamp->copied_total = prtd->copied_total; + tstamp->byte_offset = prtd->copied_total % prtd->pcm_size; + spin_unlock_irqrestore(&prtd->lock, flags); + + return 0; +} + +int q6apm_dai_compr_trigger(struct snd_soc_component *component, + struct snd_compr_stream *stream, int cmd) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct q6apm_dai_rtd *prtd = runtime->private_data; + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ret = q6apm_write_async_compr(prtd->graph, prtd->pcm_count, 0, 0, NO_TIMESTAMP); + break; + case SNDRV_PCM_TRIGGER_STOP: + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + break; + case SND_COMPR_TRIGGER_NEXT_TRACK: + prtd->next_track = true; + prtd->next_track_stream_id = (prtd->graph->id == 1 ? 2 : 1); + break; + case SND_COMPR_TRIGGER_DRAIN: + case SND_COMPR_TRIGGER_PARTIAL_DRAIN: + prtd->notify_on_drain = true; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +int q6apm_dai_compr_ack(struct snd_soc_component *component, struct snd_compr_stream *stream, + size_t count) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct q6apm_dai_rtd *prtd = runtime->private_data; + unsigned long flags; + + spin_lock_irqsave(&prtd->lock, flags); + prtd->bytes_received += count; + spin_unlock_irqrestore(&prtd->lock, flags); + + return count; +} + static const struct snd_compress_ops q6apm_dai_compress_ops = { .open = q6apm_dai_compr_open, .free = q6apm_dai_compr_free, .get_caps = q6apm_dai_compr_get_caps, .get_codec_caps = q6apm_dai_compr_get_codec_caps, + .pointer = q6apm_dai_compr_pointer, + .trigger = q6apm_dai_compr_trigger, + .ack = q6apm_dai_compr_ack, };
static const struct snd_soc_component_driver q6apm_fe_dai_component = { diff --git a/sound/soc/qcom/qdsp6/q6apm.h b/sound/soc/qcom/qdsp6/q6apm.h index 630c2bca0f06..e0247e6e4fd2 100644 --- a/sound/soc/qcom/qdsp6/q6apm.h +++ b/sound/soc/qcom/qdsp6/q6apm.h @@ -46,6 +46,7 @@
#define APM_MAX_SESSIONS 8 #define APM_LAST_BUFFER_FLAG BIT(30) +#define NO_TIMESTAMP 0xFF00
struct q6apm { struct device *dev;
Hi Mohammad,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on broonie-sound/for-next] [also build test WARNING on next-20230201] [cannot apply to tiwai-sound/for-next tiwai-sound/for-linus linus/master v6.2-rc6] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Mohammad-Rafi-Shaik/ALSA-comp... base: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next patch link: https://lore.kernel.org/r/20230201134947.1638197-11-quic_mohs%40quicinc.com patch subject: [PATCH 10/14] ASoC: q6dsp: q6apm-dai: Add trigger/pointer compress DAI callbacks config: m68k-allyesconfig (https://download.01.org/0day-ci/archive/20230202/202302020014.J221iZHe-lkp@i...) compiler: m68k-linux-gcc (GCC) 12.1.0 reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/intel-lab-lkp/linux/commit/2b44c079fb2d53ef9e13fc7d7b257f... git remote add linux-review https://github.com/intel-lab-lkp/linux git fetch --no-tags linux-review Mohammad-Rafi-Shaik/ALSA-compress-Update-compress-set-params-for-gapless-playback/20230201-215622 git checkout 2b44c079fb2d53ef9e13fc7d7b257fd4c6f4b56a # save the config file mkdir build_dir && cp config build_dir/.config COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=m68k olddefconfig COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=m68k SHELL=/bin/bash sound/soc/
If you fix the issue, kindly add following tag where applicable | Reported-by: kernel test robot lkp@intel.com
All warnings (new ones prefixed by >>):
sound/soc/qcom/qdsp6/q6apm-dai.c:152:6: warning: no previous prototype for 'event_handler_compr' [-Wmissing-prototypes] 152 | void event_handler_compr(uint32_t opcode, uint32_t token, | ^~~~~~~~~~~~~~~~~~~
sound/soc/qcom/qdsp6/q6apm-dai.c:576:5: warning: no previous prototype for 'q6apm_dai_compr_trigger' [-Wmissing-prototypes]
576 | int q6apm_dai_compr_trigger(struct snd_soc_component *component, | ^~~~~~~~~~~~~~~~~~~~~~~
sound/soc/qcom/qdsp6/q6apm-dai.c:610:5: warning: no previous prototype for 'q6apm_dai_compr_ack' [-Wmissing-prototypes]
610 | int q6apm_dai_compr_ack(struct snd_soc_component *component, struct snd_compr_stream *stream, | ^~~~~~~~~~~~~~~~~~~
vim +/q6apm_dai_compr_trigger +576 sound/soc/qcom/qdsp6/q6apm-dai.c
575
576 int q6apm_dai_compr_trigger(struct snd_soc_component *component,
577 struct snd_compr_stream *stream, int cmd) 578 { 579 struct snd_compr_runtime *runtime = stream->runtime; 580 struct q6apm_dai_rtd *prtd = runtime->private_data; 581 int ret = 0; 582 583 switch (cmd) { 584 case SNDRV_PCM_TRIGGER_START: 585 case SNDRV_PCM_TRIGGER_RESUME: 586 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 587 ret = q6apm_write_async_compr(prtd->graph, prtd->pcm_count, 0, 0, NO_TIMESTAMP); 588 break; 589 case SNDRV_PCM_TRIGGER_STOP: 590 break; 591 case SNDRV_PCM_TRIGGER_SUSPEND: 592 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 593 break; 594 case SND_COMPR_TRIGGER_NEXT_TRACK: 595 prtd->next_track = true; 596 prtd->next_track_stream_id = (prtd->graph->id == 1 ? 2 : 1); 597 break; 598 case SND_COMPR_TRIGGER_DRAIN: 599 case SND_COMPR_TRIGGER_PARTIAL_DRAIN: 600 prtd->notify_on_drain = true; 601 break; 602 default: 603 ret = -EINVAL; 604 break; 605 } 606 607 return ret; 608 } 609
610 int q6apm_dai_compr_ack(struct snd_soc_component *component, struct snd_compr_stream *stream,
611 size_t count) 612 { 613 struct snd_compr_runtime *runtime = stream->runtime; 614 struct q6apm_dai_rtd *prtd = runtime->private_data; 615 unsigned long flags; 616 617 spin_lock_irqsave(&prtd->lock, flags); 618 prtd->bytes_received += count; 619 spin_unlock_irqrestore(&prtd->lock, flags); 620 621 return count; 622 } 623
Add q6apm compress DAI callbacks for setting params and metadata to support compress offload playback.
Signed-off-by: Mohammad Rafi Shaik quic_mohs@quicinc.com Co-developed-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org Signed-off-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org --- sound/soc/qcom/qdsp6/q6apm-dai.c | 121 +++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+)
diff --git a/sound/soc/qcom/qdsp6/q6apm-dai.c b/sound/soc/qcom/qdsp6/q6apm-dai.c index f43b60742e2f..8ee14822362b 100644 --- a/sound/soc/qcom/qdsp6/q6apm-dai.c +++ b/sound/soc/qcom/qdsp6/q6apm-dai.c @@ -621,6 +621,125 @@ int q6apm_dai_compr_ack(struct snd_soc_component *component, struct snd_compr_st return count; }
+static int __q6apm_dai_compr_set_codec_params(struct snd_soc_component *component, + struct snd_compr_stream *stream, + struct snd_codec *codec, + int stream_id) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct q6apm_dai_rtd *prtd = runtime->private_data; + struct device *dev = component->dev; + union snd_codec_options *codec_options; + + codec_options = &(prtd->codec.options); + + memcpy(&prtd->codec, codec, sizeof(*codec)); + q6apm_set_real_module_id(dev, prtd->graph, codec->id); + + return 0; +} + +static int q6apm_dai_compr_set_params(struct snd_soc_component *component, + struct snd_compr_stream *stream, + struct snd_compr_params *params) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct q6apm_dai_rtd *prtd = runtime->private_data; + struct q6apm_dai_data *pdata; + struct audioreach_module_config cfg; + struct snd_codec *codec = ¶ms->codec; + int dir = stream->direction; + int ret; + + pdata = snd_soc_component_get_drvdata(component); + if (!pdata) + return -EINVAL; + + prtd->periods = runtime->fragments; + prtd->pcm_count = runtime->fragment_size; + prtd->pcm_size = runtime->fragments * runtime->fragment_size; + prtd->bits_per_sample = 16; + + prtd->pos = 0; + + if (prtd->next_track != true) { + ret = __q6apm_dai_compr_set_codec_params(component, stream, + ¶ms->codec, 0); + if (ret) + return ret; + + cfg.direction = dir; + cfg.sample_rate = codec->sample_rate; + cfg.num_channels = 2; + cfg.bit_width = prtd->bits_per_sample; + cfg.fmt = codec->id; + memcpy(&cfg.codec, codec, sizeof(*codec)); + + ret = q6apm_graph_media_format_shmem(prtd->graph, &cfg); + if (ret < 0) + return ret; + + ret = q6apm_graph_media_format_pcm(prtd->graph, &cfg); + if (ret) + return ret; + + ret = q6apm_map_memory_regions(prtd->graph, SNDRV_PCM_STREAM_PLAYBACK, + prtd->phys, (prtd->pcm_size / prtd->periods), + prtd->periods); + if (ret < 0) + return -ENOMEM; + + ret = q6apm_graph_prepare(prtd->graph); + if (ret) + return ret; + ret = q6apm_graph_start(prtd->graph); + if (ret) + return ret; + + } else { + cfg.direction = dir; + cfg.sample_rate = codec->sample_rate; + cfg.num_channels = 2; + cfg.bit_width = prtd->bits_per_sample; + cfg.fmt = codec->id; + memcpy(&cfg.codec, codec, sizeof(*codec)); + + ret = audioreach_compr_set_param(prtd->graph, &cfg); + if (ret < 0) + return ret; + } + prtd->state = Q6APM_STREAM_RUNNING; + + return 0; +} + +static int q6apm_dai_compr_set_metadata(struct snd_soc_component *component, + struct snd_compr_stream *stream, + struct snd_compr_metadata *metadata) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct q6apm_dai_rtd *prtd = runtime->private_data; + int ret = 0; + + switch (metadata->key) { + case SNDRV_COMPRESS_ENCODER_PADDING: + prtd->trailing_samples_drop = metadata->value[0]; + q6apm_remove_trailing_silence(component->dev, prtd->graph, + prtd->trailing_samples_drop); + break; + case SNDRV_COMPRESS_ENCODER_DELAY: + prtd->initial_samples_drop = metadata->value[0]; + q6apm_remove_initial_silence(component->dev, prtd->graph, + prtd->initial_samples_drop); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + static const struct snd_compress_ops q6apm_dai_compress_ops = { .open = q6apm_dai_compr_open, .free = q6apm_dai_compr_free, @@ -629,6 +748,8 @@ static const struct snd_compress_ops q6apm_dai_compress_ops = { .pointer = q6apm_dai_compr_pointer, .trigger = q6apm_dai_compr_trigger, .ack = q6apm_dai_compr_ack, + .set_params = q6apm_dai_compr_set_params, + .set_metadata = q6apm_dai_compr_set_metadata, };
static const struct snd_soc_component_driver q6apm_fe_dai_component = {
Hi Mohammad,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on broonie-sound/for-next] [also build test WARNING on next-20230201] [cannot apply to tiwai-sound/for-next tiwai-sound/for-linus linus/master v6.2-rc6] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Mohammad-Rafi-Shaik/ALSA-comp... base: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next patch link: https://lore.kernel.org/r/20230201134947.1638197-12-quic_mohs%40quicinc.com patch subject: [PATCH 11/14] ASoC: q6dsp: q6apm-dai: Add compress set params and metadata DAI callbacks config: m68k-allyesconfig (https://download.01.org/0day-ci/archive/20230201/202302012348.LL8vhyj4-lkp@i...) compiler: m68k-linux-gcc (GCC) 12.1.0 reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/intel-lab-lkp/linux/commit/f6204693a956c267d6cdfd17f5c27d... git remote add linux-review https://github.com/intel-lab-lkp/linux git fetch --no-tags linux-review Mohammad-Rafi-Shaik/ALSA-compress-Update-compress-set-params-for-gapless-playback/20230201-215622 git checkout f6204693a956c267d6cdfd17f5c27da0f4594ca3 # save the config file mkdir build_dir && cp config build_dir/.config COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=m68k olddefconfig COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=m68k SHELL=/bin/bash sound/soc/qcom/qdsp6/
If you fix the issue, kindly add following tag where applicable | Reported-by: kernel test robot lkp@intel.com
All warnings (new ones prefixed by >>):
sound/soc/qcom/qdsp6/q6apm-dai.c:152:6: warning: no previous prototype for 'event_handler_compr' [-Wmissing-prototypes] 152 | void event_handler_compr(uint32_t opcode, uint32_t token, | ^~~~~~~~~~~~~~~~~~~ sound/soc/qcom/qdsp6/q6apm-dai.c:576:5: warning: no previous prototype for 'q6apm_dai_compr_trigger' [-Wmissing-prototypes] 576 | int q6apm_dai_compr_trigger(struct snd_soc_component *component, | ^~~~~~~~~~~~~~~~~~~~~~~ sound/soc/qcom/qdsp6/q6apm-dai.c:610:5: warning: no previous prototype for 'q6apm_dai_compr_ack' [-Wmissing-prototypes] 610 | int q6apm_dai_compr_ack(struct snd_soc_component *component, struct snd_compr_stream *stream, | ^~~~~~~~~~~~~~~~~~~ sound/soc/qcom/qdsp6/q6apm-dai.c: In function '__q6apm_dai_compr_set_codec_params':
sound/soc/qcom/qdsp6/q6apm-dai.c:632:34: warning: variable 'codec_options' set but not used [-Wunused-but-set-variable]
632 | union snd_codec_options *codec_options; | ^~~~~~~~~~~~~
vim +/codec_options +632 sound/soc/qcom/qdsp6/q6apm-dai.c
623 624 static int __q6apm_dai_compr_set_codec_params(struct snd_soc_component *component, 625 struct snd_compr_stream *stream, 626 struct snd_codec *codec, 627 int stream_id) 628 { 629 struct snd_compr_runtime *runtime = stream->runtime; 630 struct q6apm_dai_rtd *prtd = runtime->private_data; 631 struct device *dev = component->dev;
632 union snd_codec_options *codec_options;
633 634 codec_options = &(prtd->codec.options); 635 636 memcpy(&prtd->codec, codec, sizeof(*codec)); 637 q6apm_set_real_module_id(dev, prtd->graph, codec->id); 638 639 return 0; 640 } 641
Add q6apm mmap and copy compress DAI callbacks to support compress offload playback.
Signed-off-by: Mohammad Rafi Shaik quic_mohs@quicinc.com Co-developed-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org Signed-off-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org --- sound/soc/qcom/qdsp6/q6apm-dai.c | 81 ++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+)
diff --git a/sound/soc/qcom/qdsp6/q6apm-dai.c b/sound/soc/qcom/qdsp6/q6apm-dai.c index 8ee14822362b..2c0aa2f4caaf 100644 --- a/sound/soc/qcom/qdsp6/q6apm-dai.c +++ b/sound/soc/qcom/qdsp6/q6apm-dai.c @@ -740,6 +740,85 @@ static int q6apm_dai_compr_set_metadata(struct snd_soc_component *component, return ret; }
+static int q6apm_dai_compr_mmap(struct snd_soc_component *component, + struct snd_compr_stream *stream, + struct vm_area_struct *vma) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct q6apm_dai_rtd *prtd = runtime->private_data; + struct device *dev = component->dev; + + return dma_mmap_coherent(dev, vma, prtd->dma_buffer.area, prtd->dma_buffer.addr, + prtd->dma_buffer.bytes); +} + +static int q6apm_compr_copy(struct snd_soc_component *component, + struct snd_compr_stream *stream, char __user *buf, + size_t count) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct q6apm_dai_rtd *prtd = runtime->private_data; + void *dstn; + unsigned long flags; + size_t copy; + u32 wflags = 0; + u32 app_pointer; + u32 bytes_received; + uint32_t bytes_to_write; + int avail, bytes_in_flight = 0; + + bytes_received = prtd->bytes_received; + + /** + * Make sure that next track data pointer is aligned at 32 bit boundary + * This is a Mandatory requirement from DSP data buffers alignment + */ + if (prtd->next_track) + bytes_received = ALIGN(prtd->bytes_received, prtd->pcm_count); + + app_pointer = bytes_received/prtd->pcm_size; + app_pointer = bytes_received - (app_pointer * prtd->pcm_size); + dstn = prtd->dma_buffer.area + app_pointer; + + if (count < prtd->pcm_size - app_pointer) { + if (copy_from_user(dstn, buf, count)) + return -EFAULT; + } else { + copy = prtd->pcm_size - app_pointer; + if (copy_from_user(dstn, buf, copy)) + return -EFAULT; + if (copy_from_user(prtd->dma_buffer.area, buf + copy, count - copy)) + return -EFAULT; + } + + spin_lock_irqsave(&prtd->lock, flags); + bytes_in_flight = prtd->bytes_received - prtd->copied_total; + + if (prtd->next_track) { + prtd->next_track = false; + prtd->copied_total = ALIGN(prtd->copied_total, prtd->pcm_count); + prtd->bytes_sent = ALIGN(prtd->bytes_sent, prtd->pcm_count); + } + + prtd->bytes_received = bytes_received + count; + + /* Kick off the data to dsp if its starving!! */ + if (prtd->state == Q6APM_STREAM_RUNNING && (bytes_in_flight == 0)) { + bytes_to_write = prtd->pcm_count; + avail = prtd->bytes_received - prtd->bytes_sent; + + if (avail < prtd->pcm_count) + bytes_to_write = avail; + + q6apm_write_async_compr(prtd->graph, bytes_to_write, 0, 0, wflags); + prtd->bytes_sent += bytes_to_write; + } + + spin_unlock_irqrestore(&prtd->lock, flags); + + return count; +} + static const struct snd_compress_ops q6apm_dai_compress_ops = { .open = q6apm_dai_compr_open, .free = q6apm_dai_compr_free, @@ -750,6 +829,8 @@ static const struct snd_compress_ops q6apm_dai_compress_ops = { .ack = q6apm_dai_compr_ack, .set_params = q6apm_dai_compr_set_params, .set_metadata = q6apm_dai_compr_set_metadata, + .mmap = q6apm_dai_compr_mmap, + .copy = q6apm_compr_copy, };
static const struct snd_soc_component_driver q6apm_fe_dai_component = {
Add support for handling compressed formats such as MP3, AAC and FLAC.
Signed-off-by: Mohammad Rafi Shaik quic_mohs@quicinc.com Co-developed-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org Signed-off-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org --- sound/soc/qcom/qdsp6/audioreach.c | 106 ++++++++++++++++++++++++------ 1 file changed, 86 insertions(+), 20 deletions(-)
diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c index 7c45c36e9156..250ed828c7d3 100644 --- a/sound/soc/qcom/qdsp6/audioreach.c +++ b/sound/soc/qcom/qdsp6/audioreach.c @@ -852,6 +852,68 @@ static int audioreach_mfc_set_media_format(struct q6apm_graph *graph, return rc; }
+static int audioreach_set_compr_media_format(struct media_format *media_fmt_hdr, + void *p, struct audioreach_module_config *mcfg) +{ + struct payload_media_fmt_aac_t *aac_cfg; + struct payload_media_fmt_pcm *mp3_cfg; + struct payload_media_fmt_flac_t *flac_cfg; + int ret = 0; + + switch (mcfg->fmt) { + case SND_AUDIOCODEC_MP3: + media_fmt_hdr->data_format = DATA_FORMAT_RAW_COMPRESSED; + media_fmt_hdr->fmt_id = MEDIA_FMT_ID_MP3; + media_fmt_hdr->payload_size = 0; + p = p + sizeof(*media_fmt_hdr); + mp3_cfg = p; + mp3_cfg->sample_rate = mcfg->sample_rate; + mp3_cfg->bit_width = mcfg->bit_width; + mp3_cfg->alignment = PCM_LSB_ALIGNED; + mp3_cfg->bits_per_sample = mcfg->bit_width; + mp3_cfg->q_factor = mcfg->bit_width - 1; + mp3_cfg->endianness = PCM_LITTLE_ENDIAN; + mp3_cfg->num_channels = mcfg->num_channels; + + if (mcfg->num_channels == 1) { + mp3_cfg->channel_mapping[0] = PCM_CHANNEL_L; + } else if (mcfg->num_channels == 2) { + mp3_cfg->channel_mapping[0] = PCM_CHANNEL_L; + mp3_cfg->channel_mapping[1] = PCM_CHANNEL_R; + } + break; + case SND_AUDIOCODEC_AAC: + media_fmt_hdr->data_format = DATA_FORMAT_RAW_COMPRESSED; + media_fmt_hdr->fmt_id = MEDIA_FMT_ID_AAC; + media_fmt_hdr->payload_size = sizeof(struct payload_media_fmt_aac_t); + p = p + sizeof(*media_fmt_hdr); + aac_cfg = p; + aac_cfg->aac_fmt_flag = 0; + aac_cfg->audio_obj_type = 5; + aac_cfg->num_channels = mcfg->num_channels; + aac_cfg->total_size_of_PCE_bits = 0; + aac_cfg->sample_rate = mcfg->sample_rate; + break; + case SND_AUDIOCODEC_FLAC: + media_fmt_hdr->data_format = DATA_FORMAT_RAW_COMPRESSED; + media_fmt_hdr->fmt_id = MEDIA_FMT_ID_FLAC; + media_fmt_hdr->payload_size = sizeof(struct payload_media_fmt_flac_t); + p = p + sizeof(*media_fmt_hdr); + flac_cfg = p; + flac_cfg->sample_size = mcfg->codec.options.flac_d.sample_size; + flac_cfg->num_channels = mcfg->num_channels; + flac_cfg->min_blk_size = mcfg->codec.options.flac_d.min_blk_size; + flac_cfg->max_blk_size = mcfg->codec.options.flac_d.max_blk_size; + flac_cfg->sample_rate = mcfg->sample_rate; + flac_cfg->min_frame_size = mcfg->codec.options.flac_d.min_frame_size; + flac_cfg->max_frame_size = mcfg->codec.options.flac_d.max_frame_size; + break; + default: + return -EINVAL; + } + return ret; +} + static int audioreach_i2s_set_media_format(struct q6apm_graph *graph, struct audioreach_module *module, struct audioreach_module_config *cfg) @@ -1055,26 +1117,29 @@ static int audioreach_shmem_set_media_format(struct q6apm_graph *graph, p = p + APM_MODULE_PARAM_DATA_SIZE;
header = p; - header->data_format = DATA_FORMAT_FIXED_POINT; - header->fmt_id = MEDIA_FMT_ID_PCM; - header->payload_size = payload_size - sizeof(*header); + if (mcfg->fmt == SND_AUDIOCODEC_PCM) { + header->data_format = DATA_FORMAT_FIXED_POINT; + header->fmt_id = MEDIA_FMT_ID_PCM; + header->payload_size = payload_size - sizeof(*header);
- p = p + sizeof(*header); - cfg = p; - cfg->sample_rate = mcfg->sample_rate; - cfg->bit_width = mcfg->bit_width; - cfg->alignment = PCM_LSB_ALIGNED; - cfg->bits_per_sample = mcfg->bit_width; - cfg->q_factor = mcfg->bit_width - 1; - cfg->endianness = PCM_LITTLE_ENDIAN; - cfg->num_channels = mcfg->num_channels; - - if (mcfg->num_channels == 1) { - cfg->channel_mapping[0] = PCM_CHANNEL_L; - } else if (num_channels == 2) { - cfg->channel_mapping[0] = PCM_CHANNEL_L; - cfg->channel_mapping[1] = PCM_CHANNEL_R; - } + p = p + sizeof(*header); + cfg = p; + cfg->sample_rate = mcfg->sample_rate; + cfg->bit_width = mcfg->bit_width; + cfg->alignment = PCM_LSB_ALIGNED; + cfg->bits_per_sample = mcfg->bit_width; + cfg->q_factor = mcfg->bit_width - 1; + cfg->endianness = PCM_LITTLE_ENDIAN; + cfg->num_channels = mcfg->num_channels; + + if (mcfg->num_channels == 1) + cfg->channel_mapping[0] = PCM_CHANNEL_L; + else if (num_channels == 2) { + cfg->channel_mapping[0] = PCM_CHANNEL_L; + cfg->channel_mapping[1] = PCM_CHANNEL_R; + } + } else + audioreach_set_compr_media_format(header, p, mcfg);
rc = audioreach_graph_send_cmd_sync(graph, pkt, 0);
@@ -1401,7 +1466,8 @@ int audioreach_compr_set_param(struct q6apm_graph *graph, struct audioreach_modu cfg->channel_mapping[0] = PCM_CHANNEL_L; cfg->channel_mapping[1] = PCM_CHANNEL_R; } - } + } else + audioreach_set_compr_media_format(header, p, mcfg);
rc = gpr_send_port_pkt(graph->port, pkt); kfree(pkt);
Add support for setting EOS delay command and receive the EOS response from ADSP, for seamless compress offload playback feature.
Signed-off-by: Mohammad Rafi Shaik quic_mohs@quicinc.com Co-developed-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org Signed-off-by: Srinivas Kandagatla srinivas.kandagatla@linaro.org --- sound/soc/qcom/qdsp6/audioreach.c | 40 +++++++++++++++++++++++++++++++ sound/soc/qcom/qdsp6/audioreach.h | 6 +++++ sound/soc/qcom/qdsp6/q6apm.c | 3 +++ 3 files changed, 49 insertions(+)
diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c index 250ed828c7d3..71ff705e27cb 100644 --- a/sound/soc/qcom/qdsp6/audioreach.c +++ b/sound/soc/qcom/qdsp6/audioreach.c @@ -852,6 +852,43 @@ static int audioreach_mfc_set_media_format(struct q6apm_graph *graph, return rc; }
+static int audioreach_gapless_set_media_format(struct q6apm_graph *graph, + struct audioreach_module *module, + struct audioreach_module_config *cfg) +{ + struct apm_module_param_data *param_data; + struct param_id_gapless_early_eos_delay_t *media_format; + int payload_size; + struct gpr_pkt *pkt; + int rc; + void *p; + + payload_size = sizeof(struct param_id_gapless_early_eos_delay_t) + + APM_MODULE_PARAM_DATA_SIZE; + + pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0); + if (IS_ERR(pkt)) + return PTR_ERR(pkt); + + p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE; + + param_data = p; + param_data->module_instance_id = module->instance_id; + param_data->error_code = 0; + param_data->param_id = PARAM_ID_EARLY_EOS_DELAY; + param_data->param_size = sizeof(struct param_id_gapless_early_eos_delay_t); + p = p + APM_MODULE_PARAM_DATA_SIZE; + media_format = p; + + media_format->early_eos_delay_ms = EARLY_EOS_DELAY_MS; + + rc = q6apm_send_cmd_sync(graph->apm, pkt, 0); + + kfree(pkt); + + return rc; +} + static int audioreach_set_compr_media_format(struct media_format *media_fmt_hdr, void *p, struct audioreach_module_config *mcfg) { @@ -1249,6 +1286,9 @@ int audioreach_set_media_format(struct q6apm_graph *graph, struct audioreach_mod case MODULE_ID_MFC: rc = audioreach_mfc_set_media_format(graph, module, cfg); break; + case MODULE_ID_GAPLESS: + rc = audioreach_gapless_set_media_format(graph, module, cfg); + break; default: rc = 0; } diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h index 044994ca4811..a9d4f70ad3e0 100644 --- a/sound/soc/qcom/qdsp6/audioreach.h +++ b/sound/soc/qcom/qdsp6/audioreach.h @@ -543,6 +543,8 @@ struct param_id_sal_limiter_enable { } __packed;
#define PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT 0x08001024 +#define PARAM_ID_EARLY_EOS_DELAY 0x0800114C +#define EARLY_EOS_DELAY_MS 150
struct param_id_mfc_media_format { uint32_t sample_rate; @@ -551,6 +553,10 @@ struct param_id_mfc_media_format { uint16_t channel_mapping[]; } __packed;
+struct param_id_gapless_early_eos_delay_t { + uint32_t early_eos_delay_ms; +} __packed; + struct media_format { uint32_t data_format; uint32_t fmt_id; diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index 1a6c7108bae0..37ded6e0f4ea 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -605,6 +605,9 @@ static int graph_callback(struct gpr_resp_pkt *data, void *priv, int op) } break; case DATA_CMD_WR_SH_MEM_EP_EOS_RENDERED: + client_event = APM_CLIENT_EVENT_CMD_EOS_DONE; + if (graph->cb) + graph->cb(client_event, hdr->token, data->payload, graph->priv); break; case GPR_BASIC_RSP_RESULT: switch (result->opcode) {
participants (2)
-
kernel test robot
-
Mark Brown
-
Mohammad Rafi Shaik