audio: hal: Volume control support for transcode loopback
Changes to enable volume control for DSP HW
transcode loopback use case type.
CRs-Fixed: 2092579
Change-Id: I48b9020ec1f8137495ddf84151445f423d01a1ff
diff --git a/hal/audio_extn/hw_loopback.c b/hal/audio_extn/hw_loopback.c
index 64f5ae6..9938c04 100644
--- a/hal/audio_extn/hw_loopback.c
+++ b/hal/audio_extn/hw_loopback.c
@@ -41,7 +41,10 @@
#define PATCH_HANDLE_INVALID 0xFFFF
#define MAX_SOURCE_PORTS_PER_PATCH 1
#define MAX_SINK_PORTS_PER_PATCH 1
+#define HW_LOOPBACK_RX_VOLUME "Trans Loopback RX Volume"
+#define HW_LOOPBACK_RX_UNITY_GAIN 0x2000
+#include <math.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
@@ -131,6 +134,34 @@
}
}
+/* Set loopback volume : for mute implementation */
+static int hw_loopback_set_volume(struct audio_device *adev, int value)
+{
+ int32_t ret = 0;
+ struct mixer_ctl *ctl;
+ char mixer_ctl_name[MAX_LENGTH_MIXER_CONTROL_IN_INT];
+ snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
+ "Transcode Loopback Rx Volume");
+
+ ALOGD("%s: (%d)\n", __func__, value);
+
+ ALOGD("%s: Setting HW loopback volume to %d \n", __func__, value);
+ 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, value) < 0) {
+ ALOGE("%s: Couldn't set HW Loopback Volume: [%d]", __func__, value);
+ return -EINVAL;
+ }
+
+ ALOGV("%s: exit", __func__);
+ return ret;
+}
+
/* Initialize patch database */
int init_patch_database(patch_db_t* patch_db)
{
@@ -662,8 +693,7 @@
struct audio_port *port_in)
{
int status = 0, n=0, patch_num=-1;
- loopback_patch_t *active_loopback_patch = NULL;
- port_info_t *port_info = NULL;
+ port_info_t port_info;
struct audio_port_config *port_out=NULL;
ALOGV("%s %d", __func__, __LINE__);
@@ -675,10 +705,10 @@
pthread_mutex_lock(&audio_loopback_mod->lock);
- port_info->id = port_in->id;
- port_info->role = port_in->role; /* sink or source */
- port_info->type = port_in->type; /* device, mix ... */
- port_out = get_port_from_patch_db(port_info, &audio_loopback_mod->patch_db,
+ port_info.id = port_in->id;
+ port_info.role = port_in->role; /* sink or source */
+ port_info.type = port_in->type; /* device, mix ... */
+ port_out = get_port_from_patch_db(&port_info, &audio_loopback_mod->patch_db,
&patch_num);
if (port_out == NULL) {
ALOGE("%s, Unable to find a valid matching port in patch \
@@ -709,40 +739,64 @@
const struct audio_port_config *config)
{
int status = 0, n=0, patch_num=-1;
- loopback_patch_t *active_loopback_patch = NULL;
- port_info_t *port_info = NULL;
+ port_info_t port_info;
struct audio_port_config *port_out=NULL;
+ struct audio_device *adev = audio_loopback_mod->adev;
+ int loopback_gain = HW_LOOPBACK_RX_UNITY_GAIN;
+
ALOGV("%s %d", __func__, __LINE__);
if ((audio_loopback_mod == NULL) || (dev == NULL)) {
ALOGE("%s, Invalid device", __func__);
- status = -1;
+ status = -EINVAL;
return status;
}
pthread_mutex_lock(&audio_loopback_mod->lock);
- port_info->id = config->id;
- port_info->role = config->role; /* sink or source */
- port_info->type = config->type; /* device, mix */
- port_out = get_port_from_patch_db(port_info, &audio_loopback_mod->patch_db
+ port_info.id = config->id;
+ port_info.role = config->role; /* sink or source */
+ port_info.type = config->type; /* device, mix */
+ port_out = get_port_from_patch_db(&port_info, &audio_loopback_mod->patch_db
, &patch_num);
if (port_out == NULL) {
ALOGE("%s, Unable to find a valid matching port in patch \
database,exiting", __func__);
- status = -1;
- return status;
+ status = -EINVAL;
+ goto exit_set_port_config;
}
- port_out->config_mask = config->config_mask;
- port_out->channel_mask = config->channel_mask;
- port_out->format = config->format;
- port_out->gain = config->gain;
- port_out->sample_rate = config->sample_rate;
+ port_out->config_mask |= config->config_mask;
+ if(config->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK)
+ port_out->channel_mask = config->channel_mask;
+ if(config->config_mask & AUDIO_PORT_CONFIG_FORMAT)
+ port_out->format = config->format;
+ if(config->config_mask & AUDIO_PORT_CONFIG_GAIN)
+ port_out->gain = config->gain;
+ if(config->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE)
+ port_out->sample_rate = config->sample_rate;
+
+ /* Convert gain in millibels to ratio and convert to Q13 */
+ loopback_gain = pow(10, (float)((float)port_out->gain.values[0]/2000)) *
+ (1 << 13);
+ ALOGV("%s, Port config gain_in_mbells: %d, gain_in_q13 : %d", __func__,
+ port_out->gain.values[0], loopback_gain);
+ if((port_out->config_mask & AUDIO_PORT_CONFIG_GAIN) &&
+ port_out->gain.mode == AUDIO_GAIN_MODE_JOINT ) {
+ status = hw_loopback_set_volume(adev, loopback_gain);
+ if (status) {
+ ALOGE("%s, Error setting loopback gain config: status %d",
+ __func__, status);
+ }
+ } else {
+ ALOGE("%s, Unsupported port config ,exiting", __func__);
+ status = -EINVAL;
+ }
/* Currently, port config is not used for anything,
need to restart session */
+exit_set_port_config:
pthread_mutex_unlock(&audio_loopback_mod->lock);
return status;
}
diff --git a/qahw_api/test/trans_loopback_test.c b/qahw_api/test/trans_loopback_test.c
index 0ecbdcb..eb9530e 100644
--- a/qahw_api/test/trans_loopback_test.c
+++ b/qahw_api/test/trans_loopback_test.c
@@ -45,6 +45,7 @@
#include <sys/socket.h>
#include <sys/types.h>
#include <utils/Log.h>
+#include <math.h>
#include <cutils/list.h>
#include "qahw_api.h"
@@ -87,6 +88,7 @@
FILE * log_file = NULL;
volatile bool stop_loopback = false;
+static float loopback_gain = 1.0;
const char *log_filename = NULL;
#define TRANSCODE_LOOPBACK_SOURCE_PORT_ID 0x4C00
@@ -394,6 +396,23 @@
&transcode_loopback_config->sink_config,
&transcode_loopback_config->patch_handle);
fprintf(log_file,"\nCreate patch returned %d\n",rc);
+ if(!rc) {
+ struct audio_port_config sink_gain_config;
+ /* Convert loopback gain to millibels */
+ int loopback_gain_in_millibels = 2000 * log10(loopback_gain);
+ sink_gain_config.gain.index = 0;
+ sink_gain_config.gain.mode = AUDIO_GAIN_MODE_JOINT;
+ sink_gain_config.gain.channel_mask = 1;
+ sink_gain_config.gain.values[0] = loopback_gain_in_millibels;
+ sink_gain_config.id = transcode_loopback_config->sink_config.id;
+ sink_gain_config.role = transcode_loopback_config->sink_config.role;
+ sink_gain_config.type = transcode_loopback_config->sink_config.type;
+ sink_gain_config.config_mask = AUDIO_PORT_CONFIG_GAIN;
+
+ (void)qahw_set_audio_port_config(transcode_loopback_config->hal_handle,
+ &sink_gain_config);
+ }
+
return rc;
}
@@ -494,7 +513,7 @@
j++;
}
- if ((dev_path != NULL) && (switch_name != NULL))
+ if ((dev_path != NULL) && (switch_name != NULL))
fprintf(log_file,"devpath = %s, switch_name = %s \n",dev_path, switch_name);
if((DEV_NODE_CHECK(tlb_hdmi_in_audio_dev_path, dev_path) == 0) || (DEV_NODE_CHECK(tlb_hdmi_in_audio_sample_rate_dev_path, dev_path) == 0)
@@ -543,7 +562,7 @@
int main(int argc, char *argv[]) {
int status = 0;
- uint32_t play_duration_in_seconds = 600,play_duration_elapsed_msec = 0,play_duration_in_msec = 0, sink_device = 2;
+ uint32_t play_duration_in_seconds = 600,play_duration_elapsed_msec = 0,play_duration_in_msec = 0, sink_device = 2, volume_in_millibels = 0;
source_port_type_t source_port_type = SOURCE_PORT_NONE;
log_file = stdout;
transcode_loopback_config_t *transcode_loopback_config = NULL;
@@ -553,6 +572,7 @@
/* These options set a flag. */
{"sink-device", required_argument, 0, 'd'},
{"play-duration", required_argument, 0, 'p'},
+ {"play-volume", required_argument, 0, 'v'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
};
@@ -562,7 +582,7 @@
while ((opt = getopt_long(argc,
argv,
- "-d:p:h",
+ "-d:p:v:h",
long_options,
&option_index)) != -1) {
@@ -575,6 +595,9 @@
case 'p':
play_duration_in_seconds = atoi(optarg);
break;
+ case 'v':
+ loopback_gain = atof(optarg);
+ break;
case 'h':
default :
usage();
@@ -584,9 +607,9 @@
}
fprintf(log_file,"\nTranscode loopback test begin\n");
- if (play_duration_in_seconds < 0 | play_duration_in_seconds > 3600) {
+ if (play_duration_in_seconds < 0 | play_duration_in_seconds > 360000) {
fprintf(log_file,
- "\nPlayback duration %d invalid or unsupported(range : 1 to 3600, defaulting to 600 seconds )\n",
+ "\nPlayback duration %d invalid or unsupported(range : 1 to 360000, defaulting to 600 seconds )\n",
play_duration_in_seconds);
play_duration_in_seconds = 600;
}
@@ -654,8 +677,8 @@
void usage()
{
- fprintf(log_file,"\nUsage : trans_loopback_test -p <duration_in_seconds> -d <sink_device_id>\n");
- fprintf(log_file,"\nExample to play for 1 minute on speaker device: trans_loopback_test -p 60 -d 2\n");
+ fprintf(log_file,"\nUsage : trans_loopback_test -p <duration_in_seconds> -d <sink_device_id> -v <loopback_volume(range 0 to 4.0)>\n");
+ fprintf(log_file,"\nExample to play for 1 minute on speaker device with volume unity: trans_loopback_test -p 60 -d 2 -v 1.0\n");
fprintf(log_file,"\nExample to play for 5 minutes on headphone device: trans_loopback_test -p 300 -d 8\n");
fprintf(log_file,"\nHelp : trans_loopback_test -h\n");
}