audio: Add HiFi Filter support

- Enable HiFi filter for 48kHz, 44.1kHz Music playback

Change-Id: I87a87b0d719ac4ba82b3d16331711b85459a3edb
Signed-off-by: Ramlal Karra <rkarra@codeaurora.org>
diff --git a/hal/audio_extn/audio_extn.c b/hal/audio_extn/audio_extn.c
index 8512a4e..76feecb 100644
--- a/hal/audio_extn/audio_extn.c
+++ b/hal/audio_extn/audio_extn.c
@@ -192,6 +192,7 @@
 static bool audio_extn_battery_listener_enabled = false;
 static bool audio_extn_maxx_audio_enabled = false;
 static bool audio_extn_audiozoom_enabled = false;
+static bool audio_extn_hifi_filter_enabled = false;
 
 #define AUDIO_PARAMETER_KEY_AANC_NOISE_LEVEL "aanc_noise_level"
 #define AUDIO_PARAMETER_KEY_ANC        "anc_enabled"
@@ -5198,6 +5199,87 @@
 }
 // END: BATTERY_LISTENER ================================================================
 
+// START: HiFi Filter Feature ============================================================
+void audio_extn_enable_hifi_filter(struct audio_device *adev, bool value)
+{
+    const char *mixer_ctl_name = "HiFi Filter";
+    struct mixer_ctl *ctl = NULL;
+    ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
+    if (!ctl) {
+        ALOGE("%s: Could not get ctl for mixer cmd - %s, using default control",
+              __func__, mixer_ctl_name);
+        return;
+    } else {
+        mixer_ctl_set_value(ctl, 0, value);
+        ALOGD("%s: mixer_value set %d", __func__, value);
+    }
+    return;
+}
+
+void audio_extn_hifi_filter_set_params(struct str_parms *parms,
+                                        char *value, int len)
+{
+    int ret = 0;
+    ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_HIFI_AUDIO_FILTER, value,len);
+    if (ret >= 0) {
+        if (value && !strncmp(value, "true", sizeof("true")))
+            audio_extn_hifi_filter_enabled = true;
+        else
+            audio_extn_hifi_filter_enabled = false;
+        str_parms_del(parms, AUDIO_PARAMETER_KEY_HIFI_AUDIO_FILTER);
+    }
+}
+
+bool audio_extn_hifi_check_usecase_params(int sample_rate, int usecase)
+{
+    if (sample_rate != 48000 && sample_rate != 44100)
+        return false;
+    if (usecase == USECASE_AUDIO_PLAYBACK_LOW_LATENCY || usecase == USECASE_AUDIO_PLAYBACK_ULL)
+        return false;
+    return true;
+}
+
+bool audio_extn_is_hifi_filter_enabled(struct audio_device* adev, struct stream_out *out,
+                                   snd_device_t snd_device, char *codec_variant,
+                                   int channels, int usecase_init)
+{
+    int na_mode = platform_get_native_support();
+    struct audio_usecase *uc = NULL;
+    struct listnode *node = NULL;
+    bool hifi_active = false;
+
+    if (audio_extn_hifi_filter_enabled) {
+        /*Restricting the feature for Tavil and WCD9375 codecs only*/
+        if ((strstr(codec_variant, "WCD9385") || strstr(codec_variant, "WCD9375"))
+            && (na_mode == NATIVE_AUDIO_MODE_MULTIPLE_MIX_IN_DSP) && channels <=2) {
+            /*Upsampling 8 time should be restricited to headphones playback only */
+            if (snd_device == SND_DEVICE_OUT_HEADPHONES
+                || snd_device == SND_DEVICE_OUT_HEADPHONES_44_1
+                || snd_device == SND_DEVICE_OUT_HEADPHONES_HIFI_FILTER
+                || usecase_init) {
+                if (audio_extn_hifi_check_usecase_params(out->sample_rate,
+                    out->usecase) && !usecase_init)
+                    return true;
+
+                list_for_each(node, &adev->usecase_list) {
+                    /* checking if hifi_filter is already active to set */
+                    /* concurrent playback sessions with hifi_filter enabled*/
+                    uc = node_to_item(node, struct audio_usecase, list);
+                    struct stream_out *curr_out = (struct stream_out*) uc->stream.out;
+                    if (uc->type == PCM_PLAYBACK && curr_out
+                        && audio_extn_hifi_check_usecase_params(
+                        curr_out->sample_rate, curr_out->usecase) &&
+                        (curr_out->channel_mask == AUDIO_CHANNEL_OUT_STEREO ||
+                        curr_out->channel_mask == AUDIO_CHANNEL_OUT_MONO))
+                            hifi_active = true;
+                }
+            }
+        }
+    }
+    return hifi_active;
+}
+// END: HiFi Filter Feature ==============================================================
+
 // START: AUDIOZOOM_FEATURE =====================================================================
 #ifdef __LP64__
 #define AUDIOZOOM_LIB_PATH "/vendor/lib64/libaudiozoom.so"
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index 91204a7..6ae496e 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -117,6 +117,8 @@
 #define MAX_LENGTH_MIXER_CONTROL_IN_INT                  (128)
 #define HW_INFO_ARRAY_MAX_SIZE 32
 
+#define AUDIO_PARAMETER_KEY_HIFI_AUDIO_FILTER "hifi_filter"
+
 struct snd_card_split {
     char device[HW_INFO_ARRAY_MAX_SIZE];
     char snd_card[HW_INFO_ARRAY_MAX_SIZE];
@@ -262,6 +264,14 @@
 int32_t audio_extn_get_afe_proxy_channel_count();
 //END: AFE_PROXY_FEATURE
 
+//START: HIFI FILTER
+void audio_extn_enable_hifi_filter(struct audio_device *adev, bool value);
+void audio_extn_hifi_filter_set_params(struct str_parms *parms, char *value, int len);
+bool audio_extn_is_hifi_filter_enabled(struct audio_device* adev,struct stream_out *out,
+                                   snd_device_t snd_device, char *codec_variant,
+                                   int channels, int usecase_init);
+//END: HIFI FILTER
+
 /// ---- USB feature ---------------------------------------------------------------
 void audio_extn_usb_init(void *adev);
 void audio_extn_usb_deinit();
diff --git a/hal/msm8974/hw_info.c b/hal/msm8974/hw_info.c
index 26fae75..3bb6c12 100644
--- a/hal/msm8974/hw_info.c
+++ b/hal/msm8974/hw_info.c
@@ -64,7 +64,8 @@
     SND_DEVICE_OUT_SPEAKER_AND_LINE,
     SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_EXTERNAL_1,
     SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_EXTERNAL_2,
-    SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET
+    SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET,
+    SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_HIFI_FILTER
 };
 
 static const snd_device_t taiko_fluid_variant_devices[] = {
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index fb65f88..e9da416 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -505,6 +505,8 @@
     [SND_DEVICE_OUT_SPEAKER_SAFE] = "speaker-safe",
     [SND_DEVICE_OUT_HEADPHONES] = "headphones",
     [SND_DEVICE_OUT_HEADPHONES_DSD] = "headphones-dsd",
+    [SND_DEVICE_OUT_HEADPHONES_HIFI_FILTER] = "headphones-hifi-filter",
+    [SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_HIFI_FILTER] = "speaker-and-headphones-hifi-filter",
     [SND_DEVICE_OUT_HEADPHONES_44_1] = "headphones-44.1",
     [SND_DEVICE_OUT_LINE] = "line",
     [SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES] = "speaker-and-headphones",
@@ -842,6 +844,8 @@
     [SND_DEVICE_OUT_TRANSMISSION_FM] = 0,
     [SND_DEVICE_OUT_ANC_HEADSET] = 26,
     [SND_DEVICE_OUT_ANC_FB_HEADSET] = 27,
+    [SND_DEVICE_OUT_HEADPHONES_HIFI_FILTER] = 188,
+    [SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_HIFI_FILTER] = 188,
     [SND_DEVICE_OUT_VOICE_ANC_HEADSET] = 26,
     [SND_DEVICE_OUT_VOICE_ANC_FB_HEADSET] = 27,
     [SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET] = 26,
@@ -1012,6 +1016,8 @@
     {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_SAFE)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_HEADPHONES)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_HEADPHONES_DSD)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_HEADPHONES_HIFI_FILTER)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_HIFI_FILTER)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_HEADPHONES_44_1)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_LINE)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES)},
@@ -2154,6 +2160,9 @@
     backend_tag_table[SND_DEVICE_IN_CAPTURE_FM] = strdup("capture-fm");
     backend_tag_table[SND_DEVICE_OUT_TRANSMISSION_FM] = strdup("transmission-fm");
     backend_tag_table[SND_DEVICE_OUT_HEADPHONES_DSD] = strdup("headphones-dsd");
+    backend_tag_table[SND_DEVICE_OUT_HEADPHONES_HIFI_FILTER] = strdup("headphones-hifi-filter");
+    backend_tag_table[SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_HIFI_FILTER] =
+        strdup("speaker-and-headphones-hifi-filter");
     backend_tag_table[SND_DEVICE_OUT_HEADPHONES_44_1] = strdup("headphones-44.1");
     backend_tag_table[SND_DEVICE_OUT_VOICE_SPEAKER_VBAT] = strdup("voice-speaker-vbat");
     backend_tag_table[SND_DEVICE_OUT_VOICE_SPEAKER_2_VBAT] = strdup("voice-speaker-2-vbat");
@@ -4926,6 +4935,9 @@
                 if (strncmp(backend_tag_table[snd_device], "headphones-44.1",
                             sizeof("headphones-44.1")) == 0)
                         port = HEADPHONE_44_1_BACKEND;
+                else if (strncmp(backend_tag_table[snd_device], "headphones-hifi-filter",
+                            sizeof("headphones-hifi-filter")) == 0)
+                        port = HEADPHONE_BACKEND;
                 else if (strncmp(backend_tag_table[snd_device], "headphones-dsd",
                             sizeof("headphones-dsd")) == 0)
                         port = DSD_NATIVE_BACKEND;
@@ -5578,6 +5590,12 @@
         new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER;
         new_snd_devices[1] = SND_DEVICE_OUT_USB_HEADSET;
         ret = 0;
+    } else if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_HIFI_FILTER &&
+               !platform_check_backends_match(SND_DEVICE_OUT_SPEAKER, SND_DEVICE_OUT_HEADPHONES_HIFI_FILTER)) {
+        *num_devices = 2;
+        new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER;
+        new_snd_devices[1] = SND_DEVICE_OUT_HEADPHONES_HIFI_FILTER;
+        ret = 0;
     } else if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_BT_SCO &&
                !platform_check_backends_match(SND_DEVICE_OUT_SPEAKER, SND_DEVICE_OUT_BT_SCO)) {
         *num_devices = 2;
@@ -5876,6 +5894,9 @@
                 snd_device = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_EXTERNAL_2;
             else if (is_active_voice_call)
                 snd_device = SND_DEVICE_OUT_VOICE_SPEAKER_AND_VOICE_HEADPHONES;
+            else if  (audio_extn_is_hifi_filter_enabled(adev, out, snd_device,
+               my_data->codec_variant, channel_count, 1))
+                snd_device = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_HIFI_FILTER;
             else
                 snd_device = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES;
         } else if (devices == (AUDIO_DEVICE_OUT_LINE |
@@ -5899,6 +5920,9 @@
                 snd_device = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_EXTERNAL_1;
             else if (my_data->external_spk_2)
                 snd_device = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_EXTERNAL_2;
+            else if  (audio_extn_is_hifi_filter_enabled(adev, out, snd_device,
+               my_data->codec_variant, channel_count, 1))
+                snd_device = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_HIFI_FILTER;
             else {
                 if (is_active_voice_call)
                     snd_device = SND_DEVICE_OUT_VOICE_SPEAKER_AND_VOICE_HEADPHONES;
@@ -6168,6 +6192,11 @@
                 snd_device = SND_DEVICE_OUT_HEADPHONES_44_1;
         } else if (out->format == AUDIO_FORMAT_DSD) {
                 snd_device = SND_DEVICE_OUT_HEADPHONES_DSD;
+        } else if (audio_extn_is_hifi_filter_enabled(adev, out, snd_device,
+             my_data->codec_variant, channel_count, 1)) {
+                snd_device = SND_DEVICE_OUT_HEADPHONES_HIFI_FILTER;
+        } else if (devices & SND_DEVICE_OUT_HEADPHONES_HIFI_FILTER) {
+                snd_device = SND_DEVICE_OUT_HEADPHONES_HIFI_FILTER;
         } else if (devices & AUDIO_DEVICE_OUT_LINE) {
                 snd_device = SND_DEVICE_OUT_LINE;
         } else
@@ -7402,7 +7431,7 @@
     ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_TRUE_32_BIT,
                             value,len);
     if (ret >= 0) {
-        if (value && !strncmp(value, "true", sizeof("src")))
+        if (value && !strncmp(value, "true", sizeof("true")))
             supports_true_32_bit = true;
         else
             supports_true_32_bit = false;
@@ -7752,6 +7781,7 @@
     audio_extn_hfp_set_parameters(my_data->adev, parms);
     perf_lock_set_params(platform, parms, value, len);
     true_32_bit_set_params(parms, value, len);
+    audio_extn_hifi_filter_set_params(parms, value, len);
     platform_spkr_device_set_params(platform, parms, value, len);
 done:
     ALOGV("%s: exit with code(%d)", __func__, ret);
@@ -8916,6 +8946,12 @@
                         if (channels < out_channels)
                             channels = out_channels;
                 }
+                if ((snd_device == SND_DEVICE_OUT_HEADPHONES_HIFI_FILTER) &&
+                    (usecase->id==USECASE_AUDIO_PLAYBACK_LOW_LATENCY ||
+                      usecase->id == USECASE_AUDIO_PLAYBACK_ULL)) {
+                       sample_rate = my_data->current_backend_cfg[backend_idx].sample_rate;
+                       bit_width = my_data->current_backend_cfg[backend_idx].bit_width;
+                }
             }
         }
     }
@@ -9015,6 +9051,37 @@
         }
     }
 
+    if (backend_idx != platform_get_voice_call_backend(adev)
+        && usecase->type == PCM_PLAYBACK) {
+        struct stream_out *out = (struct stream_out*) usecase->stream.out;
+        if(audio_extn_is_hifi_filter_enabled(adev, out, snd_device,
+            my_data->codec_variant, channels, 0))  {
+            switch (sample_rate) {
+                case 48000:
+                    audio_extn_enable_hifi_filter(adev, true);
+                    if (audio_is_true_native_stream_active(adev))
+                        sample_rate = 352800;
+                    else
+                        sample_rate = 384000;
+                    bit_width = 32;
+                    break;
+                case 44100:
+                    audio_extn_enable_hifi_filter(adev, true);
+                    sample_rate = 352800;
+                    bit_width = 32;
+                    break;
+                default:
+                    audio_extn_enable_hifi_filter(adev, false);
+            }
+        }
+        if (snd_device != SND_DEVICE_OUT_HEADPHONES_HIFI_FILTER)
+            audio_extn_enable_hifi_filter(adev, false);
+        ALOGD("%s:becf: updated afe: bitwidth %d, samplerate %d channels %d,"
+            "backend_idx %d usecase = %d device (%s)", __func__, bit_width,
+            sample_rate, channels, backend_idx, usecase->id,
+            platform_get_snd_device_name(snd_device));
+    }
+
     /*
      * Handset and speaker may have diffrent backend. Check if the device is speaker or handset,
      * and these devices are restricited to 48kHz.
@@ -9137,7 +9204,7 @@
     }
 
     if (snd_device == SND_DEVICE_OUT_HEADPHONES || snd_device ==
-        SND_DEVICE_OUT_HEADPHONES_44_1) {
+        SND_DEVICE_OUT_HEADPHONES_44_1 || snd_device == SND_DEVICE_OUT_HEADPHONES_HIFI_FILTER) {
         if (sample_rate > 48000 ||
             (bit_width >= 24 && (sample_rate == 48000  || sample_rate == 44100))) {
             ALOGI("%s: apply HPH HQ mode\n", __func__);
diff --git a/hal/msm8974/platform.h b/hal/msm8974/platform.h
index 2a570ce..4dcddc0 100644
--- a/hal/msm8974/platform.h
+++ b/hal/msm8974/platform.h
@@ -92,8 +92,10 @@
     SND_DEVICE_OUT_LINE,
     SND_DEVICE_OUT_HEADPHONES,
     SND_DEVICE_OUT_HEADPHONES_DSD,
+    SND_DEVICE_OUT_HEADPHONES_HIFI_FILTER,
     SND_DEVICE_OUT_HEADPHONES_44_1,
     SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES,
+    SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_HIFI_FILTER,
     SND_DEVICE_OUT_SPEAKER_SAFE_AND_HEADPHONES,
     SND_DEVICE_OUT_SPEAKER_AND_LINE,
     SND_DEVICE_OUT_SPEAKER_SAFE_AND_LINE,