diff --git a/app/boards/intel_adsp_ace30_ptl.conf b/app/boards/intel_adsp_ace30_ptl.conf index 62106d05e02e..c39bde0240d8 100644 --- a/app/boards/intel_adsp_ace30_ptl.conf +++ b/app/boards/intel_adsp_ace30_ptl.conf @@ -70,6 +70,8 @@ CONFIG_INTEL_ADSP_TIMER=y CONFIG_MM_DRV_INTEL_ADSP_TLB_REMAP_UNUSED_RAM=y CONFIG_SYS_CLOCK_TICKS_PER_SEC=12000 +CONFIG_MICROPHONE_PRIVACY=y + # Zephyr / power settings CONFIG_ADSP_IMR_CONTEXT_SAVE=y CONFIG_PM=y diff --git a/posix/include/sof/lib/dma.h b/posix/include/sof/lib/dma.h index ffae21574916..f41b16b38e1e 100644 --- a/posix/include/sof/lib/dma.h +++ b/posix/include/sof/lib/dma.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -550,7 +551,8 @@ int dma_buffer_copy_to(struct comp_buffer __sparse_cache *source, * conversion function. DMA buffer consume should be performed after the data has been copied * to all sinks. */ -int stream_copy_from_no_consume(struct comp_buffer __sparse_cache *source, +int stream_copy_from_no_consume(struct comp_dev *dev, + struct comp_buffer __sparse_cache *source, struct comp_buffer __sparse_cache *sink, dma_process_func process, uint32_t source_bytes, uint32_t chmap); diff --git a/src/audio/CMakeLists.txt b/src/audio/CMakeLists.txt index bd43559aaa80..81d9154093a0 100644 --- a/src/audio/CMakeLists.txt +++ b/src/audio/CMakeLists.txt @@ -103,6 +103,9 @@ if(NOT CONFIG_COMP_MODULE_SHARED_LIBRARY_BUILD) if(CONFIG_COMP_VOLUME) add_subdirectory(volume) endif() + if(CONFIG_MICROPHONE_PRIVACY) + add_subdirectory(mic_privacy_manager) + endif() subdirs(pipeline) add_subdirectory(google) if(CONFIG_COMP_CHAIN_DMA) diff --git a/src/audio/Kconfig b/src/audio/Kconfig index d0be938077df..abe4bc0912e6 100644 --- a/src/audio/Kconfig +++ b/src/audio/Kconfig @@ -72,6 +72,16 @@ config IPC4_GATEWAY host and DSP without using DMA: via memory window (audio payload) and IPC4 messages (set/get/flush commands). +config MICROPHONE_PRIVACY + bool "microphone privacy" + default y + depends on IPC_MAJOR_4 + help + Audio privacy feature allows end user to directly control if user space + applications receive actual data from input devices (microphones). + The control is bypassing application level settings or operating system + controls (like audio endpoint volume). + rsource "up_down_mixer/Kconfig" config COMP_BLOB diff --git a/src/audio/base_fw.c b/src/audio/base_fw.c index ea495a433a0e..7a98bcbec01f 100644 --- a/src/audio/base_fw.c +++ b/src/audio/base_fw.c @@ -25,6 +25,7 @@ #include #include #include +#include /* FIXME: * Builds for some platforms like tgl fail because their defines related to memory windows are * already defined somewhere else. Remove this ifdef after it's cleaned up @@ -49,6 +50,7 @@ static int basefw_config(uint32_t *data_offset, char *data) uint16_t version[4] = {SOF_MAJOR, SOF_MINOR, SOF_MICRO, SOF_BUILD}; struct sof_tlv *tuple = (struct sof_tlv *)data; struct ipc4_scheduler_config sche_cfg; + struct privacy_capabilities priv_caps; uint32_t plat_data_offset = 0; uint32_t log_bytes_size = 0; @@ -122,6 +124,16 @@ static int basefw_config(uint32_t *data_offset, char *data) tuple = tlv_next(tuple); + + priv_caps.privacy_version = 1; + priv_caps.capabilities_length = 1; + priv_caps.capabilities[0] = mic_privacy_get_policy_register(); + + tlv_value_set(tuple, IPC4_PRIVACY_CAPS_HW_CFG, sizeof(priv_caps), &priv_caps); + + tuple = tlv_next(tuple); + + /* add platform specific tuples */ basefw_vendor_fw_config(&plat_data_offset, (char *)tuple); @@ -262,6 +274,29 @@ static int basefw_resource_allocation_request(bool first_block, } } +static int basefw_mic_priv_state_changed(bool first_block, + bool last_block, + uint32_t data_offset_or_size, + const char *data) +{ + tr_info(&basefw_comp_tr, "basefw_mic_priv_state_changed, status = %d", *data); + + uint32_t mic_disable_status = (uint32_t)(*data); + struct mic_privacy_settings settings = fill_mic_priv_settings(mic_disable_status); + propagate_privacy_settings(&settings); + + return 0; +} + +static int basefw_set_mic_priv_policy(bool first_block, + bool last_block, + uint32_t data_offset_or_size, + const char *data) +{ + tr_info(&basefw_comp_tr, "basefw_set_mic_priv_policy"); + return 0; +} + static int basefw_power_state_info_get(uint32_t *data_offset, char *data) { #if CONFIG_KCPS_DYNAMIC_CLOCK_CONTROL @@ -643,6 +678,11 @@ static int basefw_set_large_config(struct comp_dev *dev, case IPC4_RESOURCE_ALLOCATION_REQUEST: return basefw_resource_allocation_request(first_block, last_block, data_offset, data); + case SNDW_MIC_PRIVACY_HW_MANAGED_STATE_CHANGE: + return basefw_mic_priv_state_changed(first_block, last_block, data_offset, + data); + case IPC4_SET_MIC_PRIVACY_FW_MANAGED_POLICY_MASK: + return basefw_set_mic_priv_policy(first_block, last_block, data_offset, data); default: break; } diff --git a/src/audio/copier/copier.c b/src/audio/copier/copier.c index 0fe324cb78f3..452af0352cfd 100644 --- a/src/audio/copier/copier.c +++ b/src/audio/copier/copier.c @@ -39,6 +39,7 @@ #include "host_copier.h" #include "dai_copier.h" #include "ipcgtw_copier.h" +#include #if CONFIG_ZEPHYR_NATIVE_DRIVERS #include @@ -51,6 +52,8 @@ SOF_DEFINE_REG_UUID(copier); DECLARE_TR_CTX(copier_comp_tr, SOF_UUID(copier_uuid), LOG_LEVEL_INFO); +static int mic_privacy_configure(struct comp_dev *dev, struct copier_data *cd); + static int copier_init(struct processing_module *mod) { union ipc4_connector_node_id node_id; @@ -131,6 +134,15 @@ static int copier_init(struct processing_module *mod) comp_err(dev, "unable to create host"); goto error; } + + if (cd->direction == SOF_IPC_STREAM_CAPTURE && node_id.f.dma_type == ipc4_hda_host_output_class) { + ret = mic_privacy_configure(dev, cd); + if (ret < 0) { + comp_err(dev, "unable to configure mic privacy"); + goto error; + } + } + break; case ipc4_hda_link_output_class: case ipc4_hda_link_input_class: @@ -144,6 +156,14 @@ static int copier_init(struct processing_module *mod) comp_err(dev, "unable to create dai"); goto error; } + + if (cd->direction == SOF_IPC_STREAM_CAPTURE) { + ret = mic_privacy_configure(dev, cd); + if (ret < 0) { + comp_err(dev, "unable to configure mic privacy"); + goto error; + } + } break; #if CONFIG_IPC4_GATEWAY case ipc4_ipc_output_class: @@ -198,6 +218,8 @@ static int copier_free(struct processing_module *mod) default: break; } + if(cd->mic_priv) + rfree(cd->mic_priv); if (cd) rfree(cd->gtw_cfg); @@ -1098,6 +1120,62 @@ static int copier_unbind(struct processing_module *mod, void *data) return 0; } +static void mic_privacy_event(void *arg, enum notify_id type, void *data) +{ + LOG_INF("mic_privacy_event"); + struct mic_privacy_data *mic_priv_data = arg; + struct mic_privacy_settings *mic_privacy_settings = data; + + LOG_INF("mic_privacy_event, arg = %p, data = %p", arg, data); + + if (type == NOTIFIER_ID_MIC_PRIVACY_STATE_CHANGE) { + + LOG_INF("NOTIFIER_ID_MIC_PRIVACY_STATE_CHANGE, max ramp time = %d, ", mic_privacy_settings->max_ramp_time); + LOG_INF("mic_privacy_event, state1 = %d, state2 = %d ", mic_privacy_settings->mic_privacy_state, mic_priv_data->mic_privacy_state); + + if (mic_privacy_settings->mic_privacy_state == UNMUTED) { + if (mic_priv_data->mic_privacy_state == MUTED) { + mic_priv_data->mic_privacy_state = FADE_IN; + LOG_INF("mic_privacy_event switch to FADE_IN"); + } + } else { + //In case when mute would be triggered before copier instantiation. + if (mic_priv_data->mic_privacy_state != MUTED) { + mic_priv_data->mic_privacy_state = FADE_OUT; + LOG_INF("mic_privacy_event switch to FADE_OUT"); + } + } + mic_priv_data->max_ramp_time_in_ms = (mic_privacy_settings->max_ramp_time * 1000) / ADSP_RTC_FREQUENCY; + LOG_INF("max_ramp_time_in_ms= %d, audio_freq = %d, max_ramp_time = %d", mic_priv_data->max_ramp_time_in_ms, mic_priv_data->audio_freq, mic_privacy_settings->max_ramp_time); + + } +} + +static int mic_privacy_configure(struct comp_dev *dev, struct copier_data *cd) +{ + int ret; + struct mic_privacy_data* mic_priv_data; + mic_priv_data = rzalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM, sizeof(struct mic_privacy_data)); + if (!mic_priv_data) { + rfree(mic_priv_data); + return -ENOMEM; + } + + mic_priv_data->audio_freq = cd->config.base.audio_fmt.sampling_frequency; + + uint32_t zeroing_wait_time = (get_dma_zeroing_wait_time() *1000) / ADSP_RTC_FREQUENCY; + + ret = copier_gain_set_params(dev, &mic_priv_data->mic_priv_gain_params, zeroing_wait_time, SOF_DAI_INTEL_NONE); + if(ret != 0) + return ret; + + cd->mic_priv = mic_priv_data; + + ret = notifier_register(cd->mic_priv, NULL, NOTIFIER_ID_MIC_PRIVACY_STATE_CHANGE, + mic_privacy_event, 0); + return ret; +} + static struct module_endpoint_ops copier_endpoint_ops = { .get_total_data_processed = copier_get_processed_data, .position = copier_position, diff --git a/src/audio/copier/copier.h b/src/audio/copier/copier.h index 43c125134f07..76f90f0c7dae 100644 --- a/src/audio/copier/copier.h +++ b/src/audio/copier/copier.h @@ -32,6 +32,8 @@ #include #include #include +#include +#include static const uint32_t INVALID_QUEUE_ID = 0xFFFFFFFF; @@ -270,6 +272,7 @@ struct copier_data { uint32_t channels[IPC4_ALH_MAX_NUMBER_OF_GTW]; uint32_t chan_map[IPC4_ALH_MAX_NUMBER_OF_GTW]; struct ipcgtw_data *ipcgtw_data; + struct mic_privacy_data *mic_priv; }; int apply_attenuation(struct comp_dev *dev, struct copier_data *cd, @@ -291,4 +294,6 @@ enum sof_ipc_stream_direction void copier_update_params(struct copier_data *cd, struct comp_dev *dev, struct sof_ipc_stream_params *params); + +void mic_privacy_event(void *arg, enum notify_id type, void *data); #endif diff --git a/src/audio/copier/copier_dai.c b/src/audio/copier/copier_dai.c index 587a133ebd75..fcfd45bb59c1 100644 --- a/src/audio/copier/copier_dai.c +++ b/src/audio/copier/copier_dai.c @@ -229,7 +229,8 @@ static int copier_dai_init(struct comp_dev *dev, } cd->dd[index]->gain_data = gain_data; - ret = copier_gain_set_params(dev, cd->dd[index]); + ret = copier_gain_set_params(dev, cd->dd[index]->gain_data, GAIN_DEFAULT_FADE_PERIOD, + cd->dd[index]->dai->type); if (ret < 0) { comp_err(dev, "Failed to set gain params!"); goto gain_free; diff --git a/src/audio/copier/copier_gain.c b/src/audio/copier/copier_gain.c index f7e2f24fdb8d..2321325fde19 100644 --- a/src/audio/copier/copier_gain.c +++ b/src/audio/copier/copier_gain.c @@ -15,20 +15,22 @@ LOG_MODULE_DECLARE(copier, CONFIG_SOF_LOG_LEVEL); -int copier_gain_set_params(struct comp_dev *dev, struct dai_data *dd) +int copier_gain_set_params(struct comp_dev *dev, + struct copier_gain_params *gain_params, + uint32_t fade_period, + enum sof_ipc_dai_type dai_type) { struct processing_module *mod = comp_mod(dev); struct copier_data *cd = module_get_private_data(mod); struct ipc4_base_module_cfg *ipc4_cfg = &cd->config.base; uint32_t sampling_freq = ipc4_cfg->audio_fmt.sampling_frequency; uint32_t frames = sampling_freq / dev->pipeline->period; - uint32_t fade_period = GAIN_DEFAULT_FADE_PERIOD; int ret; /* Set basic gain parameters */ - copier_gain_set_basic_params(dev, dd, ipc4_cfg); + copier_gain_set_basic_params(dev, gain_params, ipc4_cfg); - switch (dd->dai->type) { + switch (dai_type) { case SOF_DAI_INTEL_DMIC: { struct dmic_config_data *dmic_cfg = cd->gtw_cfg; @@ -43,18 +45,18 @@ int copier_gain_set_params(struct comp_dev *dev, struct dai_data *dd) /* Get fade period from DMIC blob */ fade_period = dmic_glb_cfg->ext_global_cfg.fade_in_period; /* Convert and assign silence and fade length values */ - dd->gain_data->silence_sg_length = + gain_params->silence_sg_length = frames * dmic_glb_cfg->ext_global_cfg.silence_period; - dd->gain_data->fade_sg_length = frames * fade_period; + gain_params->fade_sg_length = frames * fade_period; } break; default: - comp_info(dev, "Apply default fade period for dai type %d", dd->dai->type); + comp_info(dev, "Apply default fade period for dai type %d", dai_type); break; } /* Set fade parameters */ - ret = copier_gain_set_fade_params(dev, dd, ipc4_cfg, fade_period, frames); + ret = copier_gain_set_fade_params(dev, gain_params, ipc4_cfg, fade_period, frames); if (ret) comp_err(dev, "Failed to set fade params"); @@ -150,7 +152,10 @@ int copier_gain_dma_control(union ipc4_connector_node_id node, const char *confi break; } - ret = copier_set_gain(dev, cd->dd[0], gain_data); + struct ipc4_copier_module_cfg *copier_cfg = cd->dd[0]->dai_spec_config; + const int channels = copier_cfg->base.audio_fmt.channels_count; + + ret = copier_set_gain(dev, cd->dd[0]->gain_data, gain_data, channels); if (ret) comp_err(dev, "Gain DMA control: failed to set gain"); return ret; @@ -159,12 +164,9 @@ int copier_gain_dma_control(union ipc4_connector_node_id node, const char *confi return -ENODEV; } -int copier_set_gain(struct comp_dev *dev, struct dai_data *dd, - struct gain_dma_control_data *gain_data) +int copier_set_gain(struct comp_dev *dev, struct copier_gain_params *gain_params, + struct gain_dma_control_data *gain_data, int channels) { - struct copier_gain_params *gain_params = dd->gain_data; - struct ipc4_copier_module_cfg *copier_cfg = dd->dai_spec_config; - const int channels = copier_cfg->base.audio_fmt.channels_count; uint16_t static_gain[MAX_GAIN_COEFFS_CNT]; int ret; diff --git a/src/audio/copier/copier_gain.h b/src/audio/copier/copier_gain.h index 077a47f468c3..c7cda43d30ff 100644 --- a/src/audio/copier/copier_gain.h +++ b/src/audio/copier/copier_gain.h @@ -117,10 +117,13 @@ struct gain_dma_control_data { * the given device and DAI data. * * @param dev The pointer to the component device structure. - * @param dd The pointer to the DAI data structure. + * @param gain_params The pointer to gain params structure. + * @param fade_period The fade period in milliseconds. + * @param dai_type DAI type * @return 0 on success, negative error code on failure. */ -int copier_gain_set_params(struct comp_dev *dev, struct dai_data *dd); +int copier_gain_set_params(struct comp_dev *dev, struct copier_gain_params *gain_params, uint32_t fade_period, + enum sof_ipc_dai_type dai_type); /** * @brief Sets the basic gain parameters. @@ -129,10 +132,10 @@ int copier_gain_set_params(struct comp_dev *dev, struct dai_data *dd); * by the given device and DAI data. * * @param dev The pointer to the component device structure. - * @param dd The pointer to the DAI data structure. + * @param gain_params The pointer to gain params structure. * @param ipc4_cfg The pointer to the IPC4 base module config. */ -void copier_gain_set_basic_params(struct comp_dev *dev, struct dai_data *dd, +void copier_gain_set_basic_params(struct comp_dev *dev, struct copier_gain_params *gain_params, struct ipc4_base_module_cfg *ipc4_cfg); /** @@ -142,13 +145,13 @@ void copier_gain_set_basic_params(struct comp_dev *dev, struct dai_data *dd, * by the given device and DAI data. * * @param dev The pointer to the component device structure. - * @param dd The pointer to the DAI data structure. + * @param gain_params The pointer to gain params structure. * @param ipc4_cfg The pointer to the IPC4 base module config. * @param fade_period The fade period in milliseconds. * @param frames The number of frames to fade. * @return 0 on success, negative error code on failure. */ -int copier_gain_set_fade_params(struct comp_dev *dev, struct dai_data *dd, +int copier_gain_set_fade_params(struct comp_dev *dev, struct copier_gain_params *gain_params, struct ipc4_base_module_cfg *ipc4_cfg, uint32_t fade_period, uint32_t frames); @@ -209,12 +212,13 @@ enum copier_gain_state copier_gain_eval_state(struct copier_gain_params *gain_pa * Sets/modify gain for a copier module in runtime. * * @param dev The copier device structure. - * @param dd The DAI data structure. + * @param gain_params The pointer to the copier_gain_params structure. * @param gain_data The gain control data structure. + * @param channels Number of audio channels. * @return 0 on success, otherwise a negative error code. */ -int copier_set_gain(struct comp_dev *dev, struct dai_data *dd, - struct gain_dma_control_data *gain_data); +int copier_set_gain(struct comp_dev *dev, struct copier_gain_params *gain_params, + struct gain_dma_control_data *gain_data, int channels); /** * Checks for unity gain mode. diff --git a/src/audio/copier/copier_generic.c b/src/audio/copier/copier_generic.c index a4fc8ca6c9a1..2c14d7f9c4db 100644 --- a/src/audio/copier/copier_generic.c +++ b/src/audio/copier/copier_generic.c @@ -60,10 +60,9 @@ int apply_attenuation(struct comp_dev *dev, struct copier_data *cd, } } -void copier_gain_set_basic_params(struct comp_dev *dev, struct dai_data *dd, +void copier_gain_set_basic_params(struct comp_dev *dev, struct copier_gain_params *gain_params, struct ipc4_base_module_cfg *ipc4_cfg) { - struct copier_gain_params *gain_params = dd->gain_data; gain_params->channels_count = ipc4_cfg->audio_fmt.channels_count; @@ -71,11 +70,10 @@ void copier_gain_set_basic_params(struct comp_dev *dev, struct dai_data *dd, gain_params->gain_coeffs[i] = UNITY_GAIN_GENERIC; } -int copier_gain_set_fade_params(struct comp_dev *dev, struct dai_data *dd, +int copier_gain_set_fade_params(struct comp_dev *dev, struct copier_gain_params *gain_params, struct ipc4_base_module_cfg *ipc4_cfg, uint32_t fade_period, uint32_t frames) { - struct copier_gain_params *gain_params = dd->gain_data; uint16_t step_i64_to_i16; if (fade_period == GAIN_DEFAULT_FADE_PERIOD) { @@ -88,6 +86,8 @@ int copier_gain_set_fade_params(struct comp_dev *dev, struct dai_data *dd, /* Special case for GAIN_ZERO_TRANS_MS to support zero fade-in transition time */ gain_params->fade_sg_length = 0; return 0; + } else { + gain_params->fade_sg_length = frames * fade_period; } /* High precision step for fade-in calculation, keeps accurate precision */ diff --git a/src/audio/copier/copier_hifi.c b/src/audio/copier/copier_hifi.c index 0d0d454f2c41..131b36ea8a7a 100644 --- a/src/audio/copier/copier_hifi.c +++ b/src/audio/copier/copier_hifi.c @@ -75,10 +75,9 @@ int apply_attenuation(struct comp_dev *dev, struct copier_data *cd, } } -void copier_gain_set_basic_params(struct comp_dev *dev, struct dai_data *dd, +void copier_gain_set_basic_params(struct comp_dev *dev, struct copier_gain_params *gain_params, struct ipc4_base_module_cfg *ipc4_cfg) { - struct copier_gain_params *gain_params = dd->gain_data; /* Set default gain coefficients */ for (int i = 0; i < ARRAY_SIZE(gain_params->gain_coeffs); ++i) @@ -91,11 +90,10 @@ void copier_gain_set_basic_params(struct comp_dev *dev, struct dai_data *dd, gain_params->channels_count = ipc4_cfg->audio_fmt.channels_count; } -int copier_gain_set_fade_params(struct comp_dev *dev, struct dai_data *dd, +int copier_gain_set_fade_params(struct comp_dev *dev, struct copier_gain_params *gain_params, struct ipc4_base_module_cfg *ipc4_cfg, uint32_t fade_period, uint32_t frames) { - struct copier_gain_params *gain_params = dd->gain_data; uint16_t init_gain[MAX_GAIN_COEFFS_CNT]; uint16_t step_i64_to_i16; ae_f16 step_f16; @@ -113,7 +111,9 @@ int copier_gain_set_fade_params(struct comp_dev *dev, struct dai_data *dd, /* Special case for GAIN_ZERO_TRANS_MS to support zero fade in transition time */ gain_params->fade_sg_length = 0; return 0; - } + } else { + gain_params->fade_sg_length = frames * fade_period; + } /* High precision step for fade-in calculation, keeps accurate precision */ gain_params->step_i64 = INT64_MAX / gain_params->fade_sg_length; diff --git a/src/audio/copier/copier_host.c b/src/audio/copier/copier_host.c index b61af44bfe0e..8f324e3ea288 100644 --- a/src/audio/copier/copier_host.c +++ b/src/audio/copier/copier_host.c @@ -271,6 +271,10 @@ void copier_host_dma_cb(struct comp_dev *dev, size_t bytes) buffer_stream_writeback(cd->hd->local_buffer, bytes); } + + if(cd->mic_priv) + mic_privacy_process(dev, cd->mic_priv, cd->hd->local_buffer, bytes); + } static void copier_notifier_cb(void *arg, enum notify_id type, void *data) diff --git a/src/audio/dai-zephyr.c b/src/audio/dai-zephyr.c index c43d2f87332f..c4f60b68714b 100644 --- a/src/audio/dai-zephyr.c +++ b/src/audio/dai-zephyr.c @@ -305,7 +305,7 @@ dai_dma_cb(struct dai_data *dd, struct comp_dev *dev, uint32_t bytes, if (sink_dev && sink_dev->state == COMP_STATE_ACTIVE && audio_buffer_hw_params_configured(&sink->audio_buffer)) { - ret = stream_copy_from_no_consume(dd->local_buffer, sink, + ret = stream_copy_from_no_consume(dev, dd->local_buffer, sink, converter[j], bytes, dd->chmap); } } @@ -318,8 +318,9 @@ dai_dma_cb(struct dai_data *dd, struct comp_dev *dev, uint32_t bytes, * The PCM converter functions used during DMA buffer copy can never fail, * so no need to check the return value of stream_copy_from_no_consume(). */ - ret = stream_copy_from_no_consume(dd->dma_buffer, dd->local_buffer, - dd->process, bytes, dd->chmap); + + ret = stream_copy_from_no_consume(dev, dd->dma_buffer, dd->local_buffer, + dd->process, bytes, dd->chmap); #if CONFIG_IPC_MAJOR_4 /* Apply gain to the local buffer */ if (dd->ipc_config.apply_gain) { @@ -362,12 +363,12 @@ dai_dma_cb(struct dai_data *dd, struct comp_dev *dev, uint32_t bytes, ret = -EINVAL; continue; } - if (sink_dev && sink_dev->state == COMP_STATE_ACTIVE && - audio_buffer_hw_params_configured(&sink->audio_buffer)) - ret = stream_copy_from_no_consume(dd->dma_buffer, - sink, converter[j], - bytes, dd->chmap); + audio_buffer_hw_params_configured(&sink->audio_buffer)) + ret = stream_copy_from_no_consume(dev, dd->dma_buffer, + sink, converter[j], + bytes, dd->chmap); + } } #endif diff --git a/src/audio/mic_privacy_manager/CMakeLists.txt b/src/audio/mic_privacy_manager/CMakeLists.txt new file mode 100644 index 000000000000..ee22713403b8 --- /dev/null +++ b/src/audio/mic_privacy_manager/CMakeLists.txt @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: BSD-3-Clause + +add_local_sources(sof mic_privacy_manager.c) diff --git a/src/audio/mic_privacy_manager/mic_privacy_manager.c b/src/audio/mic_privacy_manager/mic_privacy_manager.c new file mode 100644 index 000000000000..2db681e08ac3 --- /dev/null +++ b/src/audio/mic_privacy_manager/mic_privacy_manager.c @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2023 Intel Corporation. All rights reserved. + +#include "sof/audio/mic_privacy_manager.h" + +#include +#include +#include +#include +#include +#include + +const struct device *mic_priv_dev; + +static struct mic_privacy_api_funcs *mic_privacy_api; +static enum mic_privacy_policy mic_privacy_policy; + +#define LOG_DOMAIN mic_priv + +LOG_MODULE_REGISTER(LOG_DOMAIN); + +#define MAX_INT64 (0x7FFFFFFFFFFFFFFFULL) + + +int mic_privacy_manager_init(void) +{ + mic_priv_dev = DEVICE_DT_GET(DT_NODELABEL(mic_privacy)); + + if (mic_priv_dev == NULL) + return -EINVAL; + + mic_privacy_api = (struct mic_privacy_api_funcs *) mic_priv_dev->api; + + mic_privacy_policy = mic_privacy_api->get_policy(); + + if (mic_privacy_policy == FW_MANAGED) + { + LOG_INF("mic_privacy FW_MANAGED"); + + mic_privacy_api->set_fw_managed_mode(true); + } + + //Enable interrupts + if (mic_privacy_policy == HW_MANAGED) { + LOG_INF("mic_privacy init HW_MANAGED"); + enable_dmic_irq(true); + } + + else { + enable_fw_managed_irq(true); + } + + return 0; +} + +int mic_privacy_manager_get_policy(void) +{ + LOG_INF("mic_privacy_manager_get_policy "); + + mic_privacy_api = (struct mic_privacy_api_funcs *) mic_priv_dev->api; + + return mic_privacy_api->get_policy(); +} + +uint32_t mic_privacy_get_policy_register(void) +{ + if (mic_priv_dev == NULL) + return -EINVAL; + mic_privacy_api = (struct mic_privacy_api_funcs *) mic_priv_dev->api; + + return mic_privacy_api->get_privacy_policy_register_raw_value(); +} + +void enable_fw_managed_irq(bool enable_irq) +{ + LOG_INF("enable_fw_managed_irq %d", enable_irq); + + if (enable_irq) + mic_privacy_api->enable_fw_managed_irq(true, handle_fw_managed_interrupt); + else + mic_privacy_api->enable_fw_managed_irq(false, NULL); +} + +void handle_dmic_interrupt(void *const self, int a, int b) +{ + LOG_INF("mic_privacy handle_dmic_interrupt"); + + if (mic_privacy_api->get_dmic_irq_status() ) + { + uint32_t mic_disable_status = mic_privacy_api->get_dmic_mic_disable_status(); + struct mic_privacy_settings settings = fill_mic_priv_settings(mic_disable_status); + propagate_privacy_settings(&settings); + mic_privacy_api->clear_dmic_irq_status(); + } +} + + +void enable_dmic_irq(bool enable_irq) +{ + if (enable_irq) + mic_privacy_api->enable_dmic_irq(true, handle_dmic_interrupt); + else + mic_privacy_api->enable_dmic_irq(false, NULL); +} + + +void handle_fw_managed_interrupt(void * const dev) +{ + LOG_INF("handle_fw_managed_interrupt"); + uint32_t mic_disable_status = mic_privacy_api->get_fw_managed_mic_disable_status(); + + + struct mic_privacy_settings mic_privacy_settings = + fill_mic_priv_settings(mic_disable_status); + + propagate_privacy_settings(&mic_privacy_settings); + + + if (mic_disable_status) + mic_privacy_api->set_fw_mic_disable_status(true); + else + mic_privacy_api->set_fw_mic_disable_status(false); + + mic_privacy_api->clear_fw_managed_irq(); +} + +void propagate_privacy_settings(struct mic_privacy_settings *mic_privacy_settings) +{ + LOG_INF("propagate_privacy_settings"); + notifier_event(mic_priv_dev, NOTIFIER_ID_MIC_PRIVACY_STATE_CHANGE, + NOTIFIER_TARGET_CORE_ALL_MASK, + mic_privacy_settings, sizeof(struct mic_privacy_settings)); +} + +uint32_t get_dma_zeroing_wait_time(void) +{ + return (uint32_t)mic_privacy_api->get_dma_data_zeroing_wait_time(); +} + + +uint32_t get_privacy_mask(void) +{ + if (mic_privacy_policy == HW_MANAGED) + { + return mic_privacy_api->get_dma_data_zeroing_link_select(); + } + //hardcoded for FW_MANAGED + return 0xFFFFFFFF; +} + +struct mic_privacy_settings fill_mic_priv_settings(uint32_t mic_disable_status) +{ + LOG_INF("fill_mic_priv_settings"); + + struct mic_privacy_settings mic_privacy_settings = {}; + + mic_privacy_settings.mic_privacy_mode = mic_privacy_policy; + mic_privacy_settings.mic_privacy_state = mic_disable_status; + mic_privacy_settings.privacy_mask_bits.value = get_privacy_mask(); + mic_privacy_settings.max_ramp_time = get_dma_zeroing_wait_time(); + + LOG_INF("mic_privacy_mode = %d, mic_disable_status = %d, privacy_mask = 0x%x, max_ramp_time_in_ms = %d", + mic_privacy_settings.mic_privacy_mode, + mic_privacy_settings.mic_privacy_state, + mic_privacy_settings.privacy_mask_bits.value, + mic_privacy_settings.max_ramp_time ); + + return mic_privacy_settings; +} + +void set_gtw_mic_state(struct mic_privacy_data *mic_priv_data, uint32_t mic_disable_status) +{ + + if (mic_privacy_policy == HW_MANAGED) { + if (mic_disable_status != 0) { + mic_priv_data->mic_privacy_state = MUTED; + } else { + mic_priv_data->mic_privacy_state = UNMUTED; + } + } else if (mic_privacy_policy == FW_MANAGED) { + LOG_INF("set_gtw_mic_state FW_MANAGED, mic_disable_status = %d", + mic_disable_status); + + if (mic_disable_status != 0) { + LOG_INF("set_gtw_mic_state MUTED"); + mic_priv_data->mic_privacy_state = MUTED; + mic_priv_data->dma_data_zeroing = true; + mic_privacy_api->set_fw_mic_disable_status(true); + } else { + LOG_INF("set_gtw_mic_state UNMUTED"); + mic_priv_data->mic_privacy_state = UNMUTED; + mic_priv_data->dma_data_zeroing = false; + mic_privacy_api->set_fw_mic_disable_status(false); + } + } +} + + +void update_gtw_mic_state(struct mic_privacy_data *mic_priv_data, uint32_t hw_mic_disable_status) +{ + + switch (mic_privacy_policy) { + case HW_MANAGED: + { + LOG_INF("update_gtw_mic_state HW_MANAGED"); + set_gtw_mic_state(mic_priv_data, hw_mic_disable_status); + break; + } + case FW_MANAGED: + { + LOG_INF("update_gtw_mic_state FW_MANAGED"); + set_gtw_mic_state(mic_priv_data, + mic_privacy_api->get_fw_managed_mic_disable_status()); + break; + } + default: + { + break; + } + } +} + + +void mic_privacy_process(struct comp_dev *dev, struct mic_privacy_data *mic_priv, struct comp_buffer *buffer, uint32_t copy_samples) +{ + uint32_t sg_size_in_bytes; + sg_size_in_bytes = audio_stream_frame_bytes(&buffer->stream); + uint32_t one_ms_in_bytes = sg_size_in_bytes * (buffer->stream.runtime_stream_params.rate / 1000); + uint32_t copy_bytes = copy_samples * audio_stream_sample_bytes(&buffer->stream); + + if (mic_priv->mic_privacy_state == FADE_IN) { + if (mic_priv->fade_in_out_bytes == 0) { + //start addition + mic_priv->mic_priv_gain_params.fade_in_sg_count = 0; + mic_priv->mic_priv_gain_params.gain_env = 0; + + } + mic_priv->fade_in_out_bytes += copy_bytes; + if (mic_priv->fade_in_out_bytes > one_ms_in_bytes * mic_priv->max_ramp_time_in_ms) { + mic_priv->mic_privacy_state = UNMUTED; + mic_priv->fade_in_out_bytes = 0; + } + + if (mic_priv->max_ramp_time_in_ms > 0) { + copier_gain_input(dev, buffer, &mic_priv->mic_priv_gain_params, GAIN_ADD, copy_bytes); + } + } + else if (mic_priv->mic_privacy_state == FADE_OUT){ + if (mic_priv->fade_in_out_bytes == 0) { + //start subtraction + mic_priv->mic_priv_gain_params.fade_in_sg_count = 0; + mic_priv->mic_priv_gain_params.gain_env = MAX_INT64; + } + mic_priv->fade_in_out_bytes += copy_bytes; + if (mic_priv->fade_in_out_bytes > one_ms_in_bytes * mic_priv->max_ramp_time_in_ms) { + mic_priv->mic_privacy_state = MUTED; + mic_priv->fade_in_out_bytes = 0; + buffer_zero(buffer); + } + if (mic_priv->max_ramp_time_in_ms > 0) { + copier_gain_input(dev, buffer, &mic_priv->mic_priv_gain_params, GAIN_SUBTRACT, copy_bytes); + } + } + else if (mic_priv->mic_privacy_state == MUTED) { + buffer_zero(buffer); + } + else + { + LOG_ERR("mic_privacy_process invalid state 0x%x", mic_priv->mic_privacy_state); + } + +} + + diff --git a/src/include/ipc4/base_fw.h b/src/include/ipc4/base_fw.h index ab98fad44160..15d2b89fce3a 100644 --- a/src/include/ipc4/base_fw.h +++ b/src/include/ipc4/base_fw.h @@ -288,6 +288,14 @@ enum ipc4_basefw_params { /* Use LARGE_CONFIG_SET to change SDW ownership */ IPC4_SDW_OWNERSHIP = 31, + + /*! + * This command is used by SW to notify FW for changing state of Mic Privacy + * for SoundWire Gateways + */ + SNDW_MIC_PRIVACY_HW_MANAGED_STATE_CHANGE = 35, + + IPC4_SET_MIC_PRIVACY_FW_MANAGED_POLICY_MASK =36, }; enum ipc4_fw_config_params { @@ -399,7 +407,9 @@ enum ipc4_hw_config_params { /* Size of a single memory bank (EBB) in bytes */ IPC4_EBB_SIZE_BYTES_HW_CFG = 9, /* UAOL capabilities */ - IPC4_UAOL_CAPS_HW_CFG = 10 + IPC4_UAOL_CAPS_HW_CFG = 10, + /* Mic privacy capabilities */ + IPC4_PRIVACY_CAPS_HW_CFG = 11 }; enum ipc4_memory_type { diff --git a/src/include/sof/audio/mic_privacy_manager.h b/src/include/sof/audio/mic_privacy_manager.h new file mode 100644 index 000000000000..5d9064cde6bc --- /dev/null +++ b/src/include/sof/audio/mic_privacy_manager.h @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2023 Intel Corporation. All rights reserved. + +#ifndef ADSP_FW_MIC_PRIVACY_MANAGER_H +#define ADSP_FW_MIC_PRIVACY_MANAGER_H + +#include +#include +#include +#include +#include "../audio/copier/copier_gain.h" + +#define ADSP_RTC_FREQUENCY 32768 + + +struct mic_privacy_data { + enum ipc4_sampling_frequency audio_freq; + uint32_t mic_privacy_state; + bool dma_data_zeroing; + uint32_t fade_in_out_bytes; + uint32_t max_ramp_time_in_ms; + + struct copier_gain_params mic_priv_gain_params; +}; + + +struct mic_privacy_settings { + enum mic_privacy_policy mic_privacy_mode; + /* 0-Mic Unmute, 1-Mic Mute */ + uint32_t mic_privacy_state; + uint32_t max_ramp_time; + union privacy_mask privacy_mask_bits; + +}; + +struct privacy_capabilities { + uint32_t privacy_version; + uint32_t capabilities_length; + uint32_t capabilities[1]; +}; + +//describes gain direction +enum mic_priv_gain_direction +{ + ADDITION = 0, + SUBTRACTION = 1, +}; + +enum mic_privacy_state { + UNMUTED = 0, + FADE_IN = 1, + FADE_OUT = 2, + MUTED = 3, +}; + +int mic_privacy_manager_init(void); + +int mic_privacy_manager_get_policy(void); + +uint32_t mic_privacy_get_policy_register(void); + +void enable_fw_managed_irq(bool enable_irq); + +void enable_dmic_irq(bool enable_irq); + +void handle_dmic_interrupt(void * const self, int a, int b); + +void handle_fw_managed_interrupt(void * const dev); + +void propagate_privacy_settings(struct mic_privacy_settings *mic_privacy_settings); + +uint32_t get_dma_zeroing_wait_time(void); + +uint32_t get_privacy_mask(void); + +struct mic_privacy_settings fill_mic_priv_settings(uint32_t mic_disable_status); + +void set_gtw_mic_state(struct mic_privacy_data *mic_priv_data, uint32_t mic_disable_status); + +void update_gtw_mic_state(struct mic_privacy_data *mic_priv_data, uint32_t hw_mic_disable_status); + +void mic_privacy_process(struct comp_dev *dev, struct mic_privacy_data *mic_priv, struct comp_buffer *buffer, uint32_t copy_samples); + +void mic_priv_gain_input(uint8_t *buff, uint32_t buff_size, uint32_t mic_priv_state, + const struct ipc4_audio_format *in_fmt); + +#endif /* ADSP_FW_MIC_PRIVACY_MANAGER_H */ diff --git a/src/include/sof/lib/notifier.h b/src/include/sof/lib/notifier.h index abf893281f70..060906655cb8 100644 --- a/src/include/sof/lib/notifier.h +++ b/src/include/sof/lib/notifier.h @@ -34,6 +34,7 @@ enum notify_id { NOTIFIER_ID_LL_POST_RUN, /* NULL */ NOTIFIER_ID_DMA_IRQ, /* struct dma_chan_data * */ NOTIFIER_ID_DAI_TRIGGER, /* struct dai_group * */ + NOTIFIER_ID_MIC_PRIVACY_STATE_CHANGE, /* struct mic_privacy_settings * */ NOTIFIER_ID_COUNT }; diff --git a/src/lib/dma.c b/src/lib/dma.c index 552c487cb296..29f455b17467 100644 --- a/src/lib/dma.c +++ b/src/lib/dma.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include #include @@ -20,6 +22,8 @@ #include #include #include +#include +#include "../audio/copier/copier.h" LOG_MODULE_REGISTER(dma, CONFIG_SOF_LOG_LEVEL); @@ -344,7 +348,7 @@ int dma_buffer_copy_from(struct comp_buffer *source, * cannot be used with Host DMA. Fortunately, remapping conversion is specifically * designed for use with IPC4 DAI gateway's device posture feature. IPC4 DAI gateway * does not have the same size alignment limitations. Therefore, frame-based calculations - * are employed only when necessary —- in the case of IPC4 DAI gateway with remapping + * are employed only when necessary —- in the case of IPC4 DAI gateway with remapping * conversion that modifies number of channels. */ if (source_channels == sink_channels) { @@ -408,7 +412,7 @@ int dma_buffer_copy_to(struct comp_buffer *source, * cannot be used with Host DMA. Fortunately, remapping conversion is specifically * designed for use with IPC4 DAI gateway's device posture feature. IPC4 DAI gateway * does not have the same size alignment limitations. Therefore, frame-based calculations - * are employed only when necessary —- in the case of IPC4 DAI gateway with remapping + * are employed only when necessary —- in the case of IPC4 DAI gateway with remapping * conversion that modifies number of channels. */ if (source_channels == sink_channels) { @@ -453,8 +457,10 @@ int dma_buffer_copy_to(struct comp_buffer *source, return ret; } -int stream_copy_from_no_consume(struct comp_buffer *source, struct comp_buffer *sink, - dma_process_func process, uint32_t source_bytes, uint32_t chmap) + +int stream_copy_from_no_consume(struct comp_dev *dev, struct comp_buffer *source, + struct comp_buffer *sink, + dma_process_func process, uint32_t source_bytes, uint32_t chmap) { int source_channels = audio_stream_get_channels(&source->stream); int sink_channels = audio_stream_get_channels(&sink->stream); @@ -471,7 +477,7 @@ int stream_copy_from_no_consume(struct comp_buffer *source, struct comp_buffer * * cannot be used with Host DMA. Fortunately, remapping conversion is specifically * designed for use with IPC4 DAI gateway's device posture feature. IPC4 DAI gateway * does not have the same size alignment limitations. Therefore, frame-based calculations - * are employed only when necessary —- in the case of IPC4 DAI gateway with remapping + * are employed only when necessary —- in the case of IPC4 DAI gateway with remapping * conversion that modifies number of channels. */ if (source_channels == sink_channels) { @@ -501,6 +507,13 @@ int stream_copy_from_no_consume(struct comp_buffer *source, struct comp_buffer * /* process data */ ret = process(istream, 0, &sink->stream, 0, source_samples, chmap); + + struct processing_module *mod = comp_mod(dev); + struct copier_data *cd = module_get_private_data(mod); + + if(cd->mic_priv) + mic_privacy_process(dev, cd->mic_priv, sink, source_samples); + buffer_stream_writeback(sink, sink_bytes); comp_update_buffer_produce(sink, sink_bytes); diff --git a/src/platform/intel/ace/platform.c b/src/platform/intel/ace/platform.c index 9e88861d9a97..002f3219668b 100644 --- a/src/platform/intel/ace/platform.c +++ b/src/platform/intel/ace/platform.c @@ -26,6 +26,7 @@ #include #include #include +#include "sof/audio/mic_privacy_manager.h" #include #include @@ -137,6 +138,12 @@ int platform_init(struct sof *sof) watchdog_init(); + /* Init mic privacy manager */ + ret = mic_privacy_manager_init(); + if (ret < 0) + return ret; + + /* show heap status */ heap_trace_all(1); diff --git a/xtos/include/sof/lib/dma.h b/xtos/include/sof/lib/dma.h index 7cfd6cecdc2b..7c785d19becf 100644 --- a/xtos/include/sof/lib/dma.h +++ b/xtos/include/sof/lib/dma.h @@ -544,10 +544,10 @@ int dma_buffer_copy_from(struct comp_buffer *source, * conversion function. DMA buffer consume should be performed after the data has been copied * to all sinks. */ -int stream_copy_from_no_consume(struct comp_buffer *source, - struct comp_buffer *sink, - dma_process_func process, - uint32_t source_bytes, uint32_t chmap); + +int stream_copy_from_no_consume(struct comp_dev *dev, struct comp_buffer *source, + struct comp_buffer *sink, + dma_process_func process, uint32_t source_bytes, uint32_t chmap); /* copies data to DMA buffer using provided processing function */ int dma_buffer_copy_to(struct comp_buffer *source, diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt index e3e6534bcb88..1a670a415d50 100644 --- a/zephyr/CMakeLists.txt +++ b/zephyr/CMakeLists.txt @@ -1189,6 +1189,10 @@ zephyr_library_sources_ifdef(CONFIG_SHELL sof_shell.c ) +zephyr_library_sources_ifdef(CONFIG_MICROPHONE_PRIVACY + ${SOF_AUDIO_PATH}/mic_privacy_manager/mic_privacy_manager.c +) + zephyr_library_link_libraries(SOF) target_link_libraries(SOF INTERFACE zephyr_interface) diff --git a/zephyr/include/sof/lib/dma.h b/zephyr/include/sof/lib/dma.h index e5c68f9b31c9..9031d6f263d6 100644 --- a/zephyr/include/sof/lib/dma.h +++ b/zephyr/include/sof/lib/dma.h @@ -316,7 +316,8 @@ int dma_buffer_copy_from(struct comp_buffer *source, * conversion function. DMA buffer consume should be performed after the data has been copied * to all sinks. */ -int stream_copy_from_no_consume(struct comp_buffer *source, +int stream_copy_from_no_consume(struct comp_dev *dev, + struct comp_buffer *source, struct comp_buffer *sink, dma_process_func process, uint32_t source_bytes, uint32_t chmap);