Merge "hal: Add speaker swap feature"
diff --git a/configs/sdm660/audio_platform_info.xml b/configs/sdm660/audio_platform_info.xml
index 313c59b..b5aa892 100644
--- a/configs/sdm660/audio_platform_info.xml
+++ b/configs/sdm660/audio_platform_info.xml
@@ -76,6 +76,7 @@
</config_params>
<acdb_ids>
<device name="SND_DEVICE_OUT_SPEAKER" acdb_id="15"/>
+ <device name="SND_DEVICE_OUT_SPEAKER_REVERSE" acdb_id="15"/>
<device name="SND_DEVICE_OUT_SPEAKER_PROTECTED" acdb_id="124"/>
<device name="SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED" acdb_id="101"/>
<device name="SND_DEVICE_OUT_SPEAKER_PROTECTED_VBAT" acdb_id="124"/>
diff --git a/configs/sdm845/audio_platform_info.xml b/configs/sdm845/audio_platform_info.xml
index 6a903f3..19ea80c 100644
--- a/configs/sdm845/audio_platform_info.xml
+++ b/configs/sdm845/audio_platform_info.xml
@@ -27,6 +27,7 @@
<audio_platform_info>
<acdb_ids>
<device name="SND_DEVICE_OUT_SPEAKER" acdb_id="15"/>
+ <device name="SND_DEVICE_OUT_SPEAKER_REVERSE" acdb_id="15"/>
<device name="SND_DEVICE_OUT_SPEAKER_PROTECTED" acdb_id="124"/>
<device name="SND_DEVICE_IN_VOICE_REC_QMIC_FLUENCE" acdb_id="131"/>
<device name="SND_DEVICE_IN_VOICE_REC_TMIC" acdb_id="131"/>
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index d1367a7..6468e7e 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -1123,6 +1123,10 @@
if (platform_can_enable_spkr_prot_on_device(snd_device) &&
audio_extn_spkr_prot_is_enabled()) {
audio_extn_spkr_prot_stop_processing(snd_device);
+
+ // when speaker device is disabled, reset swap.
+ // will be renabled on usecase start
+ platform_set_swap_channels(adev, false);
} else if (platform_split_snd_device(adev->platform,
snd_device,
&num_devices,
@@ -3105,6 +3109,13 @@
ALOGE("%s: audio_extn_ip_hdlr_intf_open failed %d",__func__, ret);
}
+ // consider a scenario where on pause lower layers are tear down.
+ // so on resume, swap mixer control need to be sent only when
+ // backend is active, hence rather than sending from enable device
+ // sending it from start of streamtream
+
+ platform_set_swap_channels(adev, true);
+
return ret;
error_open:
audio_extn_perf_lock_release(&adev->perf_lock_handle);
@@ -3631,8 +3642,13 @@
select_devices(adev, out->usecase);
out->devices = new_dev;
}
- if (!same_dev)
+
+ if (!same_dev) {
+ // on device switch force swap, lower functions will make sure
+ // to check if swap is allowed or not.
+ platform_set_swap_channels(adev, true);
audio_extn_perf_lock_release(&adev->perf_lock_handle);
+ }
if ((out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) &&
out->a2dp_compress_mute &&
(!(out->devices & AUDIO_DEVICE_OUT_ALL_A2DP) || audio_extn_a2dp_is_ready())) {
@@ -6167,19 +6183,10 @@
status = -EINVAL;
}
if (status == 0) {
- if (adev->speaker_lr_swap != reverse_speakers) {
- adev->speaker_lr_swap = reverse_speakers;
- // only update the selected device if there is active pcm playback
- struct audio_usecase *usecase;
- struct listnode *node;
- list_for_each(node, &adev->usecase_list) {
- usecase = node_to_item(node, struct audio_usecase, list);
- if (usecase->type == PCM_PLAYBACK) {
- select_devices(adev, usecase->id);
- break;
- }
- }
- }
+ // check and set swap
+ // - check if orientation changed and speaker active
+ // - set rotation and cache the rotation value
+ platform_check_and_set_swap_lr_channels(adev, reverse_speakers);
}
}
diff --git a/hal/msm8916/platform.c b/hal/msm8916/platform.c
index d5f1b86..45b02e3 100644
--- a/hal/msm8916/platform.c
+++ b/hal/msm8916/platform.c
@@ -251,6 +251,7 @@
bool external_spk_1;
bool external_spk_2;
bool external_mic;
+ bool speaker_lr_swap;
int fluence_type;
char fluence_cap[PROPERTY_VALUE_MAX];
int fluence_mode;
@@ -7327,6 +7328,7 @@
bool ret = false;
if (snd_device == SND_DEVICE_OUT_SPEAKER ||
+ snd_device == SND_DEVICE_OUT_SPEAKER_REVERSE ||
snd_device == SND_DEVICE_OUT_SPEAKER_WSA ||
snd_device == SND_DEVICE_OUT_SPEAKER_VBAT ||
snd_device == SND_DEVICE_OUT_VOICE_SPEAKER_VBAT ||
@@ -7648,6 +7650,151 @@
return my_data->max_mic_count;
}
+#define DEFAULT_NOMINAL_SPEAKER_GAIN 20
+int ramp_speaker_gain(struct audio_device *adev, bool ramp_up, int target_ramp_up_gain) {
+ // backup_gain: gain to try to set in case of an error during ramp
+ int start_gain, end_gain, step, backup_gain, i;
+ bool error = false;
+ const char *mixer_ctl_name_gain_left = "Left Speaker Gain";
+ const char *mixer_ctl_name_gain_right = "Right Speaker Gain";
+ struct mixer_ctl *ctl_left = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name_gain_left);
+ struct mixer_ctl *ctl_right = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name_gain_right);
+ if (!ctl_left || !ctl_right) {
+ ALOGE("%s: Could not get ctl for mixer cmd - %s or %s, not applying speaker gain ramp",
+ __func__, mixer_ctl_name_gain_left, mixer_ctl_name_gain_right);
+ return -EINVAL;
+ } else if ((mixer_ctl_get_num_values(ctl_left) != 1)
+ || (mixer_ctl_get_num_values(ctl_right) != 1)) {
+ ALOGE("%s: Unexpected num values for mixer cmd - %s or %s, not applying speaker gain ramp",
+ __func__, mixer_ctl_name_gain_left, mixer_ctl_name_gain_right);
+ return -EINVAL;
+ }
+ if (ramp_up) {
+ start_gain = 0;
+ end_gain = target_ramp_up_gain > 0 ? target_ramp_up_gain : DEFAULT_NOMINAL_SPEAKER_GAIN;
+ step = +1;
+ backup_gain = end_gain;
+ } else {
+ // using same gain on left and right
+ const int left_gain = mixer_ctl_get_value(ctl_left, 0);
+ start_gain = left_gain > 0 ? left_gain : DEFAULT_NOMINAL_SPEAKER_GAIN;
+ end_gain = 0;
+ step = -1;
+ backup_gain = start_gain;
+ }
+ for (i = start_gain ; i != (end_gain + step) ; i += step) {
+ if (mixer_ctl_set_value(ctl_left, 0, i)) {
+ ALOGE("%s: error setting %s to %d during gain ramp",
+ __func__, mixer_ctl_name_gain_left, i);
+ error = true;
+ break;
+ }
+ if (mixer_ctl_set_value(ctl_right, 0, i)) {
+ ALOGE("%s: error setting %s to %d during gain ramp",
+ __func__, mixer_ctl_name_gain_right, i);
+ error = true;
+ break;
+ }
+ usleep(1000);
+ }
+ if (error) {
+ // an error occured during the ramp, let's still try to go back to a safe volume
+ if (mixer_ctl_set_value(ctl_left, 0, backup_gain)) {
+ ALOGE("%s: error restoring left gain to %d", __func__, backup_gain);
+ }
+ if (mixer_ctl_set_value(ctl_right, 0, backup_gain)) {
+ ALOGE("%s: error restoring right gain to %d", __func__, backup_gain);
+ }
+ }
+ return start_gain;
+}
+
+int platform_set_swap_mixer(struct audio_device *adev, bool swap_channels)
+{
+ const char *mixer_ctl_name = "Swap channel";
+ struct mixer_ctl *ctl;
+ const char *mixer_path;
+ struct platform_data *my_data = (struct platform_data *)adev->platform;
+
+ // forced to set to swap, but device not rotated ... ignore set
+ if (swap_channels && !my_data->speaker_lr_swap)
+ return 0;
+
+ ALOGV("%s:", __func__);
+
+ if (swap_channels)
+ mixer_path = platform_get_snd_device_name(SND_DEVICE_OUT_SPEAKER_REVERSE);
+ else
+ mixer_path = platform_get_snd_device_name(SND_DEVICE_OUT_SPEAKER);
+
+ audio_route_apply_and_update_path(adev->audio_route, mixer_path);
+
+ ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
+ if (!ctl) {
+ ALOGE("%s: Could not get ctl for mixer cmd - %s",__func__, mixer_ctl_name);
+ return -EINVAL;
+ }
+
+ if (mixer_ctl_set_value(ctl, 0, swap_channels) < 0) {
+ ALOGE("%s: Could not set reverse cotrol %d",__func__, swap_channels);
+ return -EINVAL;
+ }
+
+ ALOGV("platfor_force_swap_channel :: Channel orientation ( %s ) ",
+ swap_channels?"R --> L":"L --> R");
+
+ return 0;
+}
+
+int platform_check_and_set_swap_lr_channels(struct audio_device *adev, bool swap_channels)
+{
+ // only update if there is active pcm playback on speaker
+ struct platform_data *my_data = (struct platform_data *)adev->platform;
+
+ my_data->speaker_lr_swap = swap_channels;
+
+ return platform_set_swap_channels(adev, swap_channels);
+}
+
+int platform_set_swap_channels(struct audio_device *adev, bool swap_channels)
+{
+ // only update if there is active pcm playback on speaker
+ struct audio_usecase *usecase;
+ struct listnode *node;
+
+ // do not swap channels in audio modes with concurrent capture and playback
+ // as this may break the echo reference
+ if ((adev->mode == AUDIO_MODE_IN_COMMUNICATION) || (adev->mode == AUDIO_MODE_IN_CALL)) {
+ ALOGV("%s: will not swap due to audio mode %d", __func__, adev->mode);
+ return 0;
+ }
+
+ list_for_each(node, &adev->usecase_list) {
+ usecase = node_to_item(node, struct audio_usecase, list);
+ if (usecase->type == PCM_PLAYBACK &&
+ usecase->stream.out->devices & AUDIO_DEVICE_OUT_SPEAKER) {
+ /*
+ * If acdb tuning is different for SPEAKER_REVERSE, it is must
+ * to perform device switch to disable the current backend to
+ * enable it with new acdb data.
+ */
+ if (acdb_device_table[SND_DEVICE_OUT_SPEAKER] !=
+ acdb_device_table[SND_DEVICE_OUT_SPEAKER_REVERSE]) {
+ const int initial_skpr_gain = ramp_speaker_gain(adev, false /*ramp_up*/, -1);
+ select_devices(adev, usecase->id);
+ if (initial_skpr_gain != -EINVAL)
+ ramp_speaker_gain(adev, true /*ramp_up*/, initial_skpr_gain);
+
+ } else {
+ platform_set_swap_mixer(adev, swap_channels);
+ }
+ break;
+ }
+ }
+
+ return 0;
+}
+
bool platform_add_gain_level_mapping(struct amp_db_and_gain_table *tbl_entry __unused)
{
return false;
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index 15158ec..64dde86 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -224,6 +224,7 @@
bool external_spk_1;
bool external_spk_2;
bool external_mic;
+ bool speaker_lr_swap;
int fluence_type;
int fluence_mode;
char fluence_cap[PROPERTY_VALUE_MAX];
@@ -6992,6 +6993,7 @@
bool ret = false;
if (snd_device == SND_DEVICE_OUT_SPEAKER ||
+ snd_device == SND_DEVICE_OUT_SPEAKER_REVERSE ||
snd_device == SND_DEVICE_OUT_SPEAKER_VBAT ||
snd_device == SND_DEVICE_OUT_VOICE_SPEAKER_VBAT ||
snd_device == SND_DEVICE_OUT_VOICE_SPEAKER_2_VBAT ||
@@ -7347,6 +7349,151 @@
return my_data->max_mic_count;
}
+#define DEFAULT_NOMINAL_SPEAKER_GAIN 20
+int ramp_speaker_gain(struct audio_device *adev, bool ramp_up, int target_ramp_up_gain) {
+ // backup_gain: gain to try to set in case of an error during ramp
+ int start_gain, end_gain, step, backup_gain, i;
+ bool error = false;
+ const char *mixer_ctl_name_gain_left = "Left Speaker Gain";
+ const char *mixer_ctl_name_gain_right = "Right Speaker Gain";
+ struct mixer_ctl *ctl_left = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name_gain_left);
+ struct mixer_ctl *ctl_right = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name_gain_right);
+ if (!ctl_left || !ctl_right) {
+ ALOGE("%s: Could not get ctl for mixer cmd - %s or %s, not applying speaker gain ramp",
+ __func__, mixer_ctl_name_gain_left, mixer_ctl_name_gain_right);
+ return -EINVAL;
+ } else if ((mixer_ctl_get_num_values(ctl_left) != 1)
+ || (mixer_ctl_get_num_values(ctl_right) != 1)) {
+ ALOGE("%s: Unexpected num values for mixer cmd - %s or %s, not applying speaker gain ramp",
+ __func__, mixer_ctl_name_gain_left, mixer_ctl_name_gain_right);
+ return -EINVAL;
+ }
+ if (ramp_up) {
+ start_gain = 0;
+ end_gain = target_ramp_up_gain > 0 ? target_ramp_up_gain : DEFAULT_NOMINAL_SPEAKER_GAIN;
+ step = +1;
+ backup_gain = end_gain;
+ } else {
+ // using same gain on left and right
+ const int left_gain = mixer_ctl_get_value(ctl_left, 0);
+ start_gain = left_gain > 0 ? left_gain : DEFAULT_NOMINAL_SPEAKER_GAIN;
+ end_gain = 0;
+ step = -1;
+ backup_gain = start_gain;
+ }
+ for (i = start_gain ; i != (end_gain + step) ; i += step) {
+ if (mixer_ctl_set_value(ctl_left, 0, i)) {
+ ALOGE("%s: error setting %s to %d during gain ramp",
+ __func__, mixer_ctl_name_gain_left, i);
+ error = true;
+ break;
+ }
+ if (mixer_ctl_set_value(ctl_right, 0, i)) {
+ ALOGE("%s: error setting %s to %d during gain ramp",
+ __func__, mixer_ctl_name_gain_right, i);
+ error = true;
+ break;
+ }
+ usleep(1000);
+ }
+ if (error) {
+ // an error occured during the ramp, let's still try to go back to a safe volume
+ if (mixer_ctl_set_value(ctl_left, 0, backup_gain)) {
+ ALOGE("%s: error restoring left gain to %d", __func__, backup_gain);
+ }
+ if (mixer_ctl_set_value(ctl_right, 0, backup_gain)) {
+ ALOGE("%s: error restoring right gain to %d", __func__, backup_gain);
+ }
+ }
+ return start_gain;
+}
+
+int platform_set_swap_mixer(struct audio_device *adev, bool swap_channels)
+{
+ const char *mixer_ctl_name = "Swap channel";
+ struct mixer_ctl *ctl;
+ const char *mixer_path;
+ struct platform_data *my_data = (struct platform_data *)adev->platform;
+
+ // forced to set to swap, but device not rotated ... ignore set
+ if (swap_channels && !my_data->speaker_lr_swap)
+ return 0;
+
+ ALOGV("%s:", __func__);
+
+ if (swap_channels)
+ mixer_path = platform_get_snd_device_name(SND_DEVICE_OUT_SPEAKER_REVERSE);
+ else
+ mixer_path = platform_get_snd_device_name(SND_DEVICE_OUT_SPEAKER);
+
+ audio_route_apply_and_update_path(adev->audio_route, mixer_path);
+
+ ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
+ if (!ctl) {
+ ALOGE("%s: Could not get ctl for mixer cmd - %s",__func__, mixer_ctl_name);
+ return -EINVAL;
+ }
+
+ if (mixer_ctl_set_value(ctl, 0, swap_channels) < 0) {
+ ALOGE("%s: Could not set reverse cotrol %d",__func__, swap_channels);
+ return -EINVAL;
+ }
+
+ ALOGV("platfor_force_swap_channel :: Channel orientation ( %s ) ",
+ swap_channels?"R --> L":"L --> R");
+
+ return 0;
+}
+
+int platform_check_and_set_swap_lr_channels(struct audio_device *adev, bool swap_channels)
+{
+ // only update if there is active pcm playback on speaker
+ struct platform_data *my_data = (struct platform_data *)adev->platform;
+
+ my_data->speaker_lr_swap = swap_channels;
+
+ return platform_set_swap_channels(adev, swap_channels);
+}
+
+int platform_set_swap_channels(struct audio_device *adev, bool swap_channels)
+{
+ // only update if there is active pcm playback on speaker
+ struct audio_usecase *usecase;
+ struct listnode *node;
+
+ // do not swap channels in audio modes with concurrent capture and playback
+ // as this may break the echo reference
+ if ((adev->mode == AUDIO_MODE_IN_COMMUNICATION) || (adev->mode == AUDIO_MODE_IN_CALL)) {
+ ALOGV("%s: will not swap due to audio mode %d", __func__, adev->mode);
+ return 0;
+ }
+
+ list_for_each(node, &adev->usecase_list) {
+ usecase = node_to_item(node, struct audio_usecase, list);
+ if (usecase->type == PCM_PLAYBACK &&
+ usecase->stream.out->devices & AUDIO_DEVICE_OUT_SPEAKER) {
+ /*
+ * If acdb tuning is different for SPEAKER_REVERSE, it is must
+ * to perform device switch to disable the current backend to
+ * enable it with new acdb data.
+ */
+ if (acdb_device_table[SND_DEVICE_OUT_SPEAKER] !=
+ acdb_device_table[SND_DEVICE_OUT_SPEAKER_REVERSE]) {
+ const int initial_skpr_gain = ramp_speaker_gain(adev, false /*ramp_up*/, -1);
+ select_devices(adev, usecase->id);
+ if (initial_skpr_gain != -EINVAL)
+ ramp_speaker_gain(adev, true /*ramp_up*/, initial_skpr_gain);
+
+ } else {
+ platform_set_swap_mixer(adev, swap_channels);
+ }
+ break;
+ }
+ }
+
+ return 0;
+}
+
static struct amp_db_and_gain_table tbl_mapping[MAX_VOLUME_CAL_STEPS];
static int num_gain_tbl_entry = 0;
diff --git a/hal/platform_api.h b/hal/platform_api.h
index cce7b2d..4407d2c 100644
--- a/hal/platform_api.h
+++ b/hal/platform_api.h
@@ -178,6 +178,8 @@
int platform_get_usecase_index(const char * usecase);
int platform_set_usecase_pcm_id(audio_usecase_t usecase, int32_t type, int32_t pcm_id);
void platform_set_echo_reference(struct audio_device *adev, bool enable, audio_devices_t out_device);
+int platform_check_and_set_swap_lr_channels(struct audio_device *adev, bool swap_channels);
+int platform_set_swap_channels(struct audio_device *adev, bool swap_channels);
void platform_get_device_to_be_id_map(int **be_id_map, int *length);
int platform_set_channel_allocation(void *platform, int channel_alloc);