OSDN Git Service

ASoC: msm: qdsp6v2: Add support for speaker swap
authorAditya Bavanari <abavanar@codeaurora.org>
Wed, 19 Oct 2016 14:14:02 +0000 (19:44 +0530)
committerGerrit - the friendly Code Review server <code-review@localhost>
Fri, 21 Apr 2017 05:24:12 +0000 (22:24 -0700)
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>
include/sound/q6adm-v2.h
sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
sound/soc/msm/qdsp6v2/q6adm.c

index 42d048f..f3443b7 100644 (file)
@@ -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__ */
index 08c78f9..f461f36 100644 (file)
@@ -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;
 }
 
index 2da7e6d..f026e82 100644 (file)
@@ -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)
 {