diff options
-rw-r--r-- | include/sound/q6adm-v2.h | 2 | ||||
-rw-r--r-- | sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c | 64 | ||||
-rw-r--r-- | sound/soc/msm/qdsp6v2/q6adm.c | 130 |
3 files changed, 196 insertions, 0 deletions
diff --git a/include/sound/q6adm-v2.h b/include/sound/q6adm-v2.h index 42d048f24e12..f3443b79ba51 100644 --- a/include/sound/q6adm-v2.h +++ b/include/sound/q6adm-v2.h @@ -164,4 +164,6 @@ int adm_get_sound_focus(int port_id, int copp_idx, struct sound_focus_param *soundFocusData); int adm_get_source_tracking(int port_id, int copp_idx, struct source_tracking_param *sourceTrackingData); +int adm_swap_speaker_channels(int port_id, int copp_idx, int sample_rate, + bool spk_swap); #endif /* __Q6_ADM_V2_H__ */ diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c index 08c78f91920e..f461f360d88d 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c @@ -80,6 +80,7 @@ static uint32_t voc_session_id = ALL_SESSION_VSID; static int msm_route_ext_ec_ref; static bool is_custom_stereo_on; static bool is_ds2_on; +static bool swap_ch; enum { MADNONE, @@ -14567,6 +14568,67 @@ static const struct snd_kcontrol_new }, }; +static int msm_routing_stereo_channel_reverse_control_get( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = swap_ch; + pr_debug("%s: Swap channel value: %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_routing_stereo_channel_reverse_control_put( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int i, idx, be_index, port_id; + int ret = 0; + unsigned long copp; + + pr_debug("%s Swap channel value:%ld\n", __func__, + ucontrol->value.integer.value[0]); + + swap_ch = ucontrol->value.integer.value[0]; + + mutex_lock(&routing_lock); + for (be_index = 0; be_index < MSM_BACKEND_DAI_MAX; be_index++) { + port_id = msm_bedais[be_index].port_id; + if (!msm_bedais[be_index].active) + continue; + + for_each_set_bit(i, &msm_bedais[be_index].fe_sessions[0], + MSM_FRONTEND_DAI_MM_SIZE) { + copp = session_copp_map[i][SESSION_TYPE_RX][be_index]; + for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) { + if (!test_bit(idx, &copp)) + continue; + + pr_debug("%s: swap channel control of portid:%d, coppid:%d\n", + __func__, port_id, idx); + ret = adm_swap_speaker_channels( + port_id, idx, + msm_bedais[be_index].sample_rate, + swap_ch); + if (ret) { + pr_err("%s:Swap_channel failed, err=%d\n", + __func__, ret); + goto done; + } + } + } + } +done: + mutex_unlock(&routing_lock); + return ret; +} + +static const struct snd_kcontrol_new stereo_channel_reverse_control[] = { + SOC_SINGLE_EXT("Swap channel", SND_SOC_NOPM, 0, + 1, 0, msm_routing_stereo_channel_reverse_control_get, + msm_routing_stereo_channel_reverse_control_put), +}; + static struct snd_pcm_ops msm_routing_pcm_ops = { .hw_params = msm_pcm_routing_hw_params, .close = msm_pcm_routing_close, @@ -14632,6 +14694,8 @@ static int msm_routing_probe(struct snd_soc_platform *platform) snd_soc_add_platform_controls(platform, aptx_dec_license_controls, ARRAY_SIZE(aptx_dec_license_controls)); + snd_soc_add_platform_controls(platform, stereo_channel_reverse_control, + ARRAY_SIZE(stereo_channel_reverse_control)); return 0; } diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c index 2da7e6dc11e7..f026e82a73cc 100644 --- a/sound/soc/msm/qdsp6v2/q6adm.c +++ b/sound/soc/msm/qdsp6v2/q6adm.c @@ -4288,6 +4288,136 @@ end: return ret; } +/** + * adm_swap_speaker_channels + * + * Receives port_id, copp_idx, sample rate, spk_swap and + * send MFC command to swap speaker channel. + * Return zero on success. On failure returns nonzero. + * + * port_id - Passed value, port_id for which channels swap is wanted + * copp_idx - Passed value, copp_idx for which channels swap is wanted + * sample_rate - Passed value, sample rate used by app type config + * spk_swap - Passed value, spk_swap for check if swap flag is set +*/ +int adm_swap_speaker_channels(int port_id, int copp_idx, + int sample_rate, bool spk_swap) +{ + struct audproc_mfc_output_media_fmt mfc_cfg; + uint16_t num_channels; + int port_idx; + int ret = 0; + + pr_debug("%s: Enter, port_id %d, copp_idx %d\n", + __func__, port_id, copp_idx); + port_id = q6audio_convert_virtual_to_portid(port_id); + port_idx = adm_validate_and_get_port_index(port_id); + if (port_idx < 0 || port_idx >= AFE_MAX_PORTS) { + pr_err("%s: Invalid port_id %#x\n", __func__, port_id); + ret = -EINVAL; + goto done; + } + + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx); + ret = -EINVAL; + goto done; + } + + num_channels = atomic_read( + &this_adm.copp.channels[port_idx][copp_idx]); + if (num_channels != 2) { + pr_debug("%s: Invalid number of channels: %d\n", + __func__, num_channels); + ret = -EINVAL; + goto done; + } + + memset(&mfc_cfg, 0, sizeof(mfc_cfg)); + mfc_cfg.params.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + mfc_cfg.params.hdr.pkt_size = + sizeof(mfc_cfg); + mfc_cfg.params.hdr.src_svc = APR_SVC_ADM; + mfc_cfg.params.hdr.src_domain = APR_DOMAIN_APPS; + mfc_cfg.params.hdr.src_port = port_id; + mfc_cfg.params.hdr.dest_svc = APR_SVC_ADM; + mfc_cfg.params.hdr.dest_domain = APR_DOMAIN_ADSP; + mfc_cfg.params.hdr.dest_port = + atomic_read(&this_adm.copp.id[port_idx][copp_idx]); + mfc_cfg.params.hdr.token = port_idx << 16 | copp_idx; + mfc_cfg.params.hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5; + mfc_cfg.params.payload_addr_lsw = 0; + mfc_cfg.params.payload_addr_msw = 0; + mfc_cfg.params.mem_map_handle = 0; + mfc_cfg.params.payload_size = sizeof(mfc_cfg) - + sizeof(mfc_cfg.params); + mfc_cfg.data.module_id = AUDPROC_MODULE_ID_MFC; + mfc_cfg.data.param_id = AUDPROC_PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT; + mfc_cfg.data.param_size = mfc_cfg.params.payload_size - + sizeof(mfc_cfg.data); + mfc_cfg.data.reserved = 0; + mfc_cfg.sampling_rate = sample_rate; + mfc_cfg.bits_per_sample = + atomic_read(&this_adm.copp.bit_width[port_idx][copp_idx]); + mfc_cfg.num_channels = num_channels; + + /* Currently applying speaker swap for only 2 channel use case */ + if (spk_swap) { + mfc_cfg.channel_type[0] = + (uint16_t) PCM_CHANNEL_FR; + mfc_cfg.channel_type[1] = + (uint16_t) PCM_CHANNEL_FL; + } else { + mfc_cfg.channel_type[0] = + (uint16_t) PCM_CHANNEL_FL; + mfc_cfg.channel_type[1] = + (uint16_t) PCM_CHANNEL_FR; + } + + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1); + pr_debug("%s: mfc config: port_idx %d copp_idx %d copp SR %d copp BW %d copp chan %d\n", + __func__, port_idx, copp_idx, mfc_cfg.sampling_rate, + mfc_cfg.bits_per_sample, mfc_cfg.num_channels); + + ret = apr_send_pkt(this_adm.apr, (uint32_t *)&mfc_cfg); + if (ret < 0) { + pr_err("%s: port_id: for[0x%x] failed %d\n", + __func__, port_id, ret); + goto done; + } + /* Wait for the callback with copp id */ + ret = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx], + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]) >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: mfc_cfg Set params timed out for port_id: for [0x%x]\n", + __func__, port_id); + ret = -ETIMEDOUT; + goto done; + } + + if (atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx]))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_adm.copp.stat + [port_idx][copp_idx])); + goto done; + } + + pr_debug("%s: mfc_cfg Set params returned success", __func__); + ret = 0; + +done: + return ret; +} +EXPORT_SYMBOL(adm_swap_speaker_channels); + int adm_set_sound_focus(int port_id, int copp_idx, struct sound_focus_param soundFocusData) { |