ASoC: msm: qdsp6v2: Add support for speaker swap

Add mixer control to swap the speaker channels.
Use MFC module to update the channel mapping.

When playback is started with speaker device and
device orientation changes, swapping L/R channel
mixer control command is sent to DSP with the MFC
module.

CRs-Fixed: 1026248
Change-Id: I1d80ecc7f1e3cfb8f8cdf71c384e92a07023be5c
Signed-off-by: kunleiz <kunleiz@codeaurora.org>
This commit is contained in:
Aditya Bavanari 2016-10-19 19:44:02 +05:30 committed by Gerrit - the friendly Code Review server
parent 6ee87610ac
commit 4d4eafb1e2
3 changed files with 196 additions and 0 deletions

View file

@ -164,4 +164,6 @@ int adm_get_sound_focus(int port_id, int copp_idx,
struct sound_focus_param *soundFocusData); struct sound_focus_param *soundFocusData);
int adm_get_source_tracking(int port_id, int copp_idx, int adm_get_source_tracking(int port_id, int copp_idx,
struct source_tracking_param *sourceTrackingData); 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__ */ #endif /* __Q6_ADM_V2_H__ */

View file

@ -80,6 +80,7 @@ static uint32_t voc_session_id = ALL_SESSION_VSID;
static int msm_route_ext_ec_ref; static int msm_route_ext_ec_ref;
static bool is_custom_stereo_on; static bool is_custom_stereo_on;
static bool is_ds2_on; static bool is_ds2_on;
static bool swap_ch;
enum { enum {
MADNONE, 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 = { static struct snd_pcm_ops msm_routing_pcm_ops = {
.hw_params = msm_pcm_routing_hw_params, .hw_params = msm_pcm_routing_hw_params,
.close = msm_pcm_routing_close, .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, snd_soc_add_platform_controls(platform, aptx_dec_license_controls,
ARRAY_SIZE(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; return 0;
} }

View file

@ -4288,6 +4288,136 @@ end:
return ret; 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, int adm_set_sound_focus(int port_id, int copp_idx,
struct sound_focus_param soundFocusData) struct sound_focus_param soundFocusData)
{ {