diff --git a/src/audio/copier/copier.c b/src/audio/copier/copier.c index 29a3943905a5..7c8d53726d20 100644 --- a/src/audio/copier/copier.c +++ b/src/audio/copier/copier.c @@ -661,6 +661,7 @@ static int copier_set_sink_fmt(struct comp_dev *dev, const void *data, const struct ipc4_copier_config_set_sink_format *sink_fmt = data; struct processing_module *mod = comp_mod(dev); struct copier_data *cd = module_get_private_data(mod); + uint32_t chmap; if (max_data_size < sizeof(*sink_fmt)) { comp_err(dev, "error: max_data_size %d should be bigger than %d", max_data_size, @@ -686,9 +687,15 @@ static int copier_set_sink_fmt(struct comp_dev *dev, const void *data, } cd->out_fmt[sink_fmt->sink_id] = sink_fmt->sink_fmt; + + if (cd->endpoint_num > 0 && dev->ipc_config.type == SOF_COMP_DAI) + chmap = cd->dd[0]->chmap; + else + chmap = DUMMY_CHMAP; + cd->converter[sink_fmt->sink_id] = get_converter_func(&sink_fmt->source_fmt, &sink_fmt->sink_fmt, ipc4_gtw_none, - ipc4_bidirection, DUMMY_CHMAP); + ipc4_bidirection, chmap); return 0; } @@ -728,6 +735,82 @@ static int set_attenuation(struct comp_dev *dev, uint32_t data_offset, const cha return 0; } +static int set_chmap(struct comp_dev *dev, const void *data, size_t data_size) +{ + const struct ipc4_copier_config_channel_map *chmap_cfg = data; + struct processing_module *mod = comp_mod(dev); + struct copier_data *cd = module_get_private_data(mod); + enum ipc4_direction_type dir; + struct ipc4_audio_format in_fmt = cd->config.base.audio_fmt; + struct ipc4_audio_format out_fmt = cd->config.out_fmt; + pcm_converter_func process; + pcm_converter_func converters[IPC4_COPIER_MODULE_OUTPUT_PINS_COUNT]; + int i; + uint32_t irq_flags; + + if (data_size < sizeof(*chmap_cfg)) { + comp_err(dev, "Wrong payload size: %d", data_size); + return -EINVAL; + } + + if (cd->endpoint_num == 0 || dev->ipc_config.type != SOF_COMP_DAI) { + comp_err(dev, "Only DAI gateway supports changing chmap"); + return -EINVAL; + } + + comp_info(dev, "New chmap requested: %x", chmap_cfg->channel_map); + + if (!cd->dd[0]->dma_buffer) { + /* DMA buffer not yet created. Remember the chmap, it will be used + * later in .params() handler. + * + * The assignment should be atomic as LL thread can preempt this IPC thread. + */ + cd->dd[0]->chmap = chmap_cfg->channel_map; + return 0; + } + + copier_dai_adjust_params(cd, &in_fmt, &out_fmt); + + dir = (cd->direction == SOF_IPC_STREAM_PLAYBACK) ? + ipc4_playback : ipc4_capture; + + process = get_converter_func(&in_fmt, &out_fmt, cd->gtw_type, dir, chmap_cfg->channel_map); + + if (!process) { + comp_err(dev, "No gtw converter func found!"); + return -EINVAL; + } + + /* Channel map is same for all sinks. However, as sinks allowed to have different + * sample formats, get new convert/remap function for each sink. + */ + for (i = 0; i < IPC4_COPIER_MODULE_OUTPUT_PINS_COUNT; i++) { + if (cd->converter[i]) { + converters[i] = get_converter_func(&in_fmt, &cd->out_fmt[i], + ipc4_gtw_none, ipc4_bidirection, + chmap_cfg->channel_map); + /* Do not report an error if converter not found as sinks could be + * bound/unbound on a fly and out_fmt[i] may contain obsolete data. + */ + } else { + converters[i] = NULL; + } + } + + /* Atomically update chmap, process and converters */ + irq_local_disable(irq_flags); + + cd->dd[0]->chmap = chmap_cfg->channel_map; + cd->dd[0]->process = process; + for (i = 0; i < IPC4_COPIER_MODULE_OUTPUT_PINS_COUNT; i++) + cd->converter[i] = converters[i]; + + irq_local_enable(irq_flags); + + return 0; +} + static int copier_set_configuration(struct processing_module *mod, uint32_t config_id, enum module_cfg_fragment_position pos, @@ -745,6 +828,8 @@ static int copier_set_configuration(struct processing_module *mod, return copier_set_sink_fmt(dev, fragment, fragment_size); case IPC4_COPIER_MODULE_CFG_ATTENUATION: return set_attenuation(dev, fragment_size, (const char *)fragment); + case IPC4_COPIER_MODULE_CFG_PARAM_CHANNEL_MAP: + return set_chmap(dev, fragment, fragment_size); default: return -EINVAL; } diff --git a/src/audio/copier/copier.h b/src/audio/copier/copier.h index 3f210e4d630e..41bca7afc287 100644 --- a/src/audio/copier/copier.h +++ b/src/audio/copier/copier.h @@ -180,7 +180,12 @@ enum ipc4_copier_module_config_params { * uint32_t. Config is only allowed when output pin is set up for 32bit and * source is connected to Gateway */ - IPC4_COPIER_MODULE_CFG_ATTENUATION = 6 + IPC4_COPIER_MODULE_CFG_ATTENUATION = 6, + /* Use LARGE_CONFIG_SET to setup new channel map, which allows to map channels + * from gateway buffer to copier with any order. + * Same mapping will be applied for all copier sinks. + */ + IPC4_COPIER_MODULE_CFG_PARAM_CHANNEL_MAP = 7 }; struct ipc4_copier_config_timestamp_init_data { @@ -201,6 +206,13 @@ struct ipc4_copier_config_set_sink_format { struct ipc4_audio_format sink_fmt; } __attribute__((packed, aligned(4))); +struct ipc4_copier_config_channel_map { + /* Each half-byte of the channel map is an index of the DMA stream channel that + * should be copied to the position determined by this half-byte index. + */ + uint32_t channel_map; +} __attribute__((packed, aligned(4))); + #define IPC4_COPIER_DATA_SEGMENT_DISABLE (0 << 0) #define IPC4_COPIER_DATA_SEGMENT_ENABLE (1 << 0) #define IPC4_COPIER_DATA_SEGMENT_RESTART (1 << 1) diff --git a/src/audio/copier/copier_dai.c b/src/audio/copier/copier_dai.c index 0228f095f9be..80866f6933c1 100644 --- a/src/audio/copier/copier_dai.c +++ b/src/audio/copier/copier_dai.c @@ -164,6 +164,7 @@ static int copier_dai_init(struct comp_dev *dev, { struct processing_module *mod = comp_mod(dev); struct copier_data *cd = module_get_private_data(mod); + uint32_t chmap; struct dai_data *dd; int ret; @@ -178,6 +179,7 @@ static int copier_dai_init(struct comp_dev *dev, config->frame_fmt = out_frame_fmt; pipeline->sink_comp = dev; cd->bsource_buffer = true; + chmap = copier->base.audio_fmt.ch_map; } else { enum sof_ipc_frame in_frame_fmt, in_valid_fmt; @@ -187,6 +189,7 @@ static int copier_dai_init(struct comp_dev *dev, copier->base.audio_fmt.s_type); config->frame_fmt = in_frame_fmt; pipeline->source_comp = dev; + chmap = copier->out_fmt.ch_map; } /* save the channel map and count for ALH multi-gateway */ @@ -205,6 +208,8 @@ static int copier_dai_init(struct comp_dev *dev, if (ret < 0) goto free_dd; + dd->chmap = chmap; + pipeline->sched_id = config->id; cd->dd[index] = dd; @@ -449,9 +454,9 @@ static int copy_single_channel_c32(const struct audio_stream *src, return 0; } -static void copier_dai_adjust_params(const struct copier_data *cd, - struct ipc4_audio_format *in_fmt, - struct ipc4_audio_format *out_fmt) +void copier_dai_adjust_params(const struct copier_data *cd, + struct ipc4_audio_format *in_fmt, + struct ipc4_audio_format *out_fmt) { struct comp_buffer *dma_buf; int dma_buf_channels; @@ -519,7 +524,7 @@ int copier_dai_params(struct copier_data *cd, struct comp_dev *dev, ipc4_playback : ipc4_capture; cd->dd[0]->process = - get_converter_func(&in_fmt, &out_fmt, cd->gtw_type, dir, DUMMY_CHMAP); + get_converter_func(&in_fmt, &out_fmt, cd->gtw_type, dir, cd->dd[0]->chmap); return ret; } diff --git a/src/audio/copier/dai_copier.h b/src/audio/copier/dai_copier.h index 4694b48ff1f2..a03970288b1b 100644 --- a/src/audio/copier/dai_copier.h +++ b/src/audio/copier/dai_copier.h @@ -83,6 +83,10 @@ void copier_dai_free(struct copier_data *cd); int copier_dai_prepare(struct comp_dev *dev, struct copier_data *cd); +void copier_dai_adjust_params(const struct copier_data *cd, + struct ipc4_audio_format *in_fmt, + struct ipc4_audio_format *out_fmt); + int copier_dai_params(struct copier_data *cd, struct comp_dev *dev, struct sof_ipc_stream_params *params, int dai_index); diff --git a/src/audio/dai-zephyr.c b/src/audio/dai-zephyr.c index c4ad228e4b40..c289b517e8d2 100644 --- a/src/audio/dai-zephyr.c +++ b/src/audio/dai-zephyr.c @@ -269,7 +269,7 @@ dai_dma_cb(struct dai_data *dd, struct comp_dev *dev, uint32_t bytes, if (dev->direction == SOF_IPC_STREAM_PLAYBACK) { ret = dma_buffer_copy_to(dd->local_buffer, dd->dma_buffer, - dd->process, bytes, DUMMY_CHMAP); + dd->process, bytes, dd->chmap); } else { audio_stream_invalidate(&dd->dma_buffer->stream, bytes); /* @@ -277,7 +277,7 @@ dai_dma_cb(struct dai_data *dd, struct comp_dev *dev, uint32_t bytes, * so no need to check the return value of dma_buffer_copy_from_no_consume(). */ ret = dma_buffer_copy_from_no_consume(dd->dma_buffer, dd->local_buffer, - dd->process, bytes, DUMMY_CHMAP); + dd->process, bytes, dd->chmap); #if CONFIG_IPC_MAJOR_4 struct list_item *sink_list; /* Skip in case of endpoint DAI devices created by the copier */ @@ -318,7 +318,7 @@ dai_dma_cb(struct dai_data *dd, struct comp_dev *dev, uint32_t bytes, sink->hw_params_configured) ret = dma_buffer_copy_from_no_consume(dd->dma_buffer, sink, converter[j], - bytes, DUMMY_CHMAP); + bytes, dd->chmap); } } #endif @@ -468,6 +468,8 @@ static struct comp_dev *dai_new(const struct comp_driver *drv, if (ret < 0) goto error; + dd->chmap = DUMMY_CHMAP; + dev->state = COMP_STATE_READY; return dev; diff --git a/src/include/sof/lib/dai-zephyr.h b/src/include/sof/lib/dai-zephyr.h index e909bfd40e05..b7f2f0f5c6f1 100644 --- a/src/include/sof/lib/dai-zephyr.h +++ b/src/include/sof/lib/dai-zephyr.h @@ -129,6 +129,7 @@ struct dai_data { int xrun; /* true if we are doing xrun recovery */ pcm_converter_func process; /* processing function */ + uint32_t chmap; channel_copy_func channel_copy; /* channel copy func used by multi-endpoint * gateway to mux/demux stream from/to multiple