hal: add audio notification acknowledge feature
- Modify sysfs node to allow audio hal to acknowledge
every HDMI connect/disconnect event.
- This makes audio hal and HDMI driver to be in sync
with respect to notifications and avoid any fast
plug-in/out notification issues.
Change-Id: I6aead358967c3e978f4f599de66c9884b28bf7e9
diff --git a/hal/audio_extn/audio_extn.c b/hal/audio_extn/audio_extn.c
index e7822b3..265f2c0 100644
--- a/hal/audio_extn/audio_extn.c
+++ b/hal/audio_extn/audio_extn.c
@@ -42,6 +42,7 @@
#include <stdlib.h>
#include <errno.h>
#include <dlfcn.h>
+#include <fcntl.h>
#include <cutils/properties.h>
#include <cutils/log.h>
@@ -82,6 +83,124 @@
#define AUDIO_PARAMETER_OFFLOAD_NUM_ACTIVE "offload_num_active"
#define AUDIO_PARAMETER_HPX "HPX"
+/*
+* update sysfs node hdmi_audio_cb to enable notification acknowledge feature
+* bit(5) set to 1 to enable this feature
+* bit(4) set to 1 to enable acknowledgement
+* this is done only once at the first connect event
+*
+* bit(0) set to 1 when HDMI cable is connected
+* bit(0) set to 0 when HDMI cable is disconnected
+* this is done when device switch happens by setting audioparamter
+*/
+
+#define HDMI_PLUG_STATUS_NOTIFY_ENABLE 0x30
+
+static ssize_t update_sysfs_node(const char *path, const char *data, size_t len)
+{
+ ssize_t err = 0;
+ int fd = -1;
+
+ err = access(path, W_OK);
+ if (!err) {
+ fd = open(path, O_WRONLY);
+ errno = 0;
+ err = write(fd, data, len);
+ if (err < 0) {
+ err = -errno;
+ }
+ close(fd);
+ } else {
+ ALOGE("%s: Failed to access path: %s error: %s",
+ __FUNCTION__, path, strerror(errno));
+ err = -errno;
+ }
+
+ return err;
+}
+
+static int get_hdmi_sysfs_node_index()
+{
+ static int node_index = -1;
+ char fbvalue[80] = {0};
+ char fbpath[80] = {0};
+ int i = 0;
+ FILE *hdmi_fp = NULL;
+
+ if(node_index >= 0) {
+ //hdmi sysfs node will not change so we just need to get the index once.
+ ALOGV("HDMI sysfs node is at fb%d", node_index);
+ return node_index;
+ }
+
+ for(i = 0; i < 3; i++) {
+ snprintf(fbpath, sizeof(fbpath),
+ "/sys/class/graphics/fb%d/msm_fb_type", i);
+ hdmi_fp = fopen(fbpath, "r");
+ if(hdmi_fp) {
+ fread(fbvalue, sizeof(char), 80, hdmi_fp);
+ if(strncmp(fbvalue, "dtv panel", strlen("dtv panel")) == 0) {
+ node_index = i;
+ ALOGV("HDMI is at fb%d",i);
+ fclose(hdmi_fp);
+ return node_index;
+ }
+ fclose(hdmi_fp);
+ } else {
+ ALOGE("Failed to open fb node %d",i);
+ }
+ }
+
+ return -1;
+}
+
+static int update_hdmi_sysfs_node(int node_value)
+{
+ char hdmi_ack_path[80] = {0};
+ char hdmi_ack_value[3] = {0};
+ int index, ret = -1;
+
+ index = get_hdmi_sysfs_node_index();
+
+ if (index >= 0) {
+ snprintf(hdmi_ack_value, sizeof(hdmi_ack_value), "%d", node_value);
+ snprintf(hdmi_ack_path, sizeof(hdmi_ack_path),
+ "/sys/class/graphics/fb%d/hdmi_audio_cb", index);
+
+ ret = update_sysfs_node(hdmi_ack_path, hdmi_ack_value,
+ sizeof(hdmi_ack_value));
+
+ ALOGI("update hdmi_audio_cb at fb[%d] to:[%d] %s",
+ index, node_value, (ret >= 0) ? "success":"fail");
+ }
+
+ return ret;
+}
+
+static void check_and_set_hdmi_connection_status(struct str_parms *parms)
+{
+ char value[32] = {0};
+ static bool is_hdmi_sysfs_node_init = false;
+
+ if (str_parms_get_str(parms, "connect", value, sizeof(value)) >= 0
+ && (atoi(value) & AUDIO_DEVICE_OUT_HDMI)) {
+ //params = "connect=1024" for HDMI connection.
+ if (is_hdmi_sysfs_node_init == false) {
+ is_hdmi_sysfs_node_init = true;
+ update_hdmi_sysfs_node(HDMI_PLUG_STATUS_NOTIFY_ENABLE);
+ }
+ update_hdmi_sysfs_node(1);
+ } else if(str_parms_get_str(parms, "disconnect", value, sizeof(value)) >= 0
+ && (atoi(value) & AUDIO_DEVICE_OUT_HDMI)){
+ //params = "disconnect=1024" for HDMI disconnection.
+ update_hdmi_sysfs_node(0);
+ } else {
+ // handle hdmi devices only
+ return;
+ }
+}
+
+
#ifndef FM_POWER_OPT
#define audio_extn_fm_set_parameters(adev, parms) (0)
#else
@@ -574,6 +693,7 @@
audio_extn_hpx_set_parameters(adev, parms);
audio_extn_pm_set_parameters(parms);
audio_extn_source_track_set_parameters(adev, parms);
+ check_and_set_hdmi_connection_status(parms);
if (adev->offload_effects_set_parameters != NULL)
adev->offload_effects_set_parameters(parms);
}