audio: HAL to support for peripheral manager

- Peripheral manager takes care of modem/adsp image loading/unloading

- Add support to register with PM and call subsystem_put to unload
  modem/adsp image to transfer the control to PM

Change-Id: I6fccff8821f737790f040751aaa03584006a23a1
diff --git a/hal/Android.mk b/hal/Android.mk
index 0dff2ae..a9c4114 100644
--- a/hal/Android.mk
+++ b/hal/Android.mk
@@ -186,6 +186,13 @@
     LOCAL_CFLAGS += -DAUXPCM_BT_ENABLED
 endif
 
+ifeq ($(strip $(AUDIO_FEATURE_ENABLED_PM_SUPPORT)),true)
+    LOCAL_CFLAGS += -DPM_SUPPORT_ENABLED
+    LOCAL_SRC_FILES += audio_extn/pm.c
+    LOCAL_C_INCLUDES += $(TARGET_OUT_HEADERS)/libperipheralclient/inc
+    LOCAL_SHARED_LIBRARIES += libperipheral_client
+endif
+
 LOCAL_COPY_HEADERS_TO   := mm-audio
 LOCAL_COPY_HEADERS      := audio_extn/audio_defs.h
 
diff --git a/hal/audio_extn/audio_extn.c b/hal/audio_extn/audio_extn.c
index 5ab7018..4ac3156 100644
--- a/hal/audio_extn/audio_extn.c
+++ b/hal/audio_extn/audio_extn.c
@@ -462,6 +462,7 @@
    audio_extn_hfp_set_parameters(adev, parms);
    audio_extn_ddp_set_parameters(adev, parms);
    audio_extn_customstereo_set_parameters(adev, parms);
+   audio_extn_pm_set_parameters(parms);
 }
 
 void audio_extn_get_parameters(const struct audio_device *adev,
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index dd6063f..5754db4 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -300,6 +300,16 @@
 int audio_extn_dev_arbi_release(snd_device_t snd_device);
 #endif
 
+#ifndef PM_SUPPORT_ENABLED
+#define audio_extn_pm_set_parameters(params) (0)
+#define audio_extn_pm_vote(void) (0)
+#define audio_extn_pm_unvote(void) (0)
+#else
+void audio_extn_pm_set_parameters(struct str_parms *parms);
+int audio_extn_pm_vote (void);
+void audio_extn_pm_unvote(void);
+#endif
+
 void audio_extn_utils_update_streams_output_cfg_list(void *platform,
                                   struct mixer *mixer,
                                   struct listnode *streams_output_cfg_list);
diff --git a/hal/audio_extn/pm.c b/hal/audio_extn/pm.c
new file mode 100644
index 0000000..7b76f60
--- /dev/null
+++ b/hal/audio_extn/pm.c
@@ -0,0 +1,149 @@
+/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#define LOG_TAG "audio_hw_pm"
+/*#define LOG_NDEBUG 0*/
+
+#include "pm.h"
+#include <cutils/log.h>
+
+static s_audio_subsys audio_ss;
+
+int audio_extn_pm_vote(void)
+{
+    int err, intfd, ret;
+    FILE *fd;
+    enum pm_event subsys_state;
+    char halPropVal[PROPERTY_VALUE_MAX];
+    bool prop_unload_image = false;
+    bool pm_reg = false;
+    bool pm_supp = false;
+
+    platform_get_subsys_image_name((char *)&audio_ss.img_name);
+    ALOGD("%s: register with peripheral manager for %s",__func__, audio_ss.img_name);
+    ret = pm_client_register(audio_extn_pm_event_notifier,
+                      &audio_ss,
+                      audio_ss.img_name,
+                      PM_CLIENT_NAME,
+                      &subsys_state,
+                      &audio_ss.pm_handle);
+    if (ret == PM_RET_SUCCESS) {
+        pm_reg = true;
+        pm_supp = true;
+        ALOGV("%s: registered with peripheral manager for %s",
+                  __func__, audio_ss.img_name);
+    } else if (ret == PM_RET_UNSUPPORTED) {
+        pm_reg = true;
+        pm_supp = false;
+        ALOGV("%s: peripheral mgr unsupported for %s",
+              __func__, audio_ss.img_name);
+        return ret;
+    } else {
+       return ret;
+    }
+    if (pm_supp == true &&
+       pm_reg == true) {
+       ALOGD("%s: Voting for subsystem power up", __func__);
+       pm_client_connect(audio_ss.pm_handle);
+
+       if (property_get("sys.audio.init", halPropVal, NULL)) {
+           prop_unload_image = !(strncmp("false", halPropVal, sizeof("false")));
+       }
+       /*
+        * adsp-loader loads modem/adsp image at boot up to play boot tone,
+        * before peripheral manager service is up. Once PM is up, vote to PM
+        * and unload the image to give control to PM to load/unload image
+        */
+       if (prop_unload_image) {
+           intfd = open(BOOT_IMG_SYSFS_PATH, O_WRONLY);
+           if (intfd == -1) {
+               ALOGE("failed to open fd in write mode, %d", errno);
+           } else {
+               ALOGD("%s: write to sysfs to unload image", __func__);
+               err = write(intfd, UNLOAD_IMAGE, 1);
+               close(intfd);
+               property_set("sys.audio.init", "true");
+          }
+       }
+    }
+    return 0;
+}
+
+void audio_extn_pm_unvote(void)
+{
+    ALOGD("%s", __func__);
+    if (audio_ss.pm_handle) {
+        pm_client_disconnect(audio_ss.pm_handle);
+        pm_client_unregister(audio_ss.pm_handle);
+    }
+}
+
+void audio_extn_pm_set_parameters(struct str_parms *parms)
+{
+    int ret;
+    char value[32];
+
+    ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_DEV_SHUTDOWN, value, sizeof(value));
+    if (ret >= 0) {
+        if (strstr(value, "true")) {
+            ALOGD("Device shutdown notification received, unregister with PM");
+            audio_extn_pm_unvote();
+        }
+    }
+}
+
+void audio_extn_pm_event_notifier(void *client_data, enum pm_event event)
+{
+    pm_client_event_acknowledge(audio_ss.pm_handle, event);
+
+    /* Closing and re-opening of session is done based on snd card status given
+     * by AudioDaemon during SS offline/online (legacy code). Just return for now.
+     */
+    switch (event) {
+    case EVENT_PERIPH_GOING_OFFLINE:
+        ALOGV("%s: %s is going offline", __func__, audio_ss.img_name);
+    break;
+
+    case EVENT_PERIPH_IS_OFFLINE:
+        ALOGV("%s: %s is offline", __func__, audio_ss.img_name);
+    break;
+
+    case EVENT_PERIPH_GOING_ONLINE:
+        ALOGV("%s: %s is going online", __func__, audio_ss.img_name);
+    break;
+
+    case EVENT_PERIPH_IS_ONLINE:
+        ALOGV("%s: %s is online", __func__, audio_ss.img_name);
+    break;
+
+    default:
+        ALOGV("%s: invalid event received from PM", __func__);
+    break;
+    }
+}
diff --git a/hal/audio_extn/pm.h b/hal/audio_extn/pm.h
new file mode 100644
index 0000000..daa376e
--- /dev/null
+++ b/hal/audio_extn/pm.h
@@ -0,0 +1,68 @@
+/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef AUDIO_EXTN_PM_H
+#define AUDIO_EXTN_PM_H
+
+#include <errno.h>
+#include <math.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <pm-service.h>
+#include "audio_hw.h"
+#include <platform.h>
+#include <cutils/properties.h>
+#include <cutils/log.h>
+
+
+/* Client name to be registered with PM */
+#define PM_CLIENT_NAME "audio"
+/* Command to sysfs to unload image */
+#define UNLOAD_IMAGE "0"
+#define MAX_NAME_LEN 32
+#define BOOT_IMG_SYSFS_PATH "/sys/kernel/boot_adsp/boot"
+
+typedef struct {
+    //MAX_NAME_LEN defined in mdm_detect.h
+    char img_name[MAX_NAME_LEN];
+    //this handle is used by peripheral mgr
+    void *pm_handle;
+}s_audio_subsys;
+
+/* Vote to peripheral manager for required subsystem */
+int audio_extn_pm_vote (void);
+
+/* Unvote to peripheral manager */
+void audio_extn_pm_unvote (void);
+
+/* Get subsytem status notification from PM */
+void audio_extn_pm_event_notifier (void *client_data, enum pm_event event);
+
+#endif // AUDIO_EXTN_PM_H
diff --git a/hal/msm8916/platform.c b/hal/msm8916/platform.c
index c0641ee..2e27530 100644
--- a/hal/msm8916/platform.c
+++ b/hal/msm8916/platform.c
@@ -959,6 +959,7 @@
         if (cvd_version)
             free(cvd_version);
     }
+    audio_extn_pm_vote();
 
 acdb_init_fail:
     /* Initialize ACDB ID's */
@@ -2485,3 +2486,8 @@
     return -ENOSYS;
 }
 
+int platform_get_subsys_image_name(char *buf)
+{
+    strlcpy(buf, PLATFORM_IMAGE_NAME, sizeof(PLATFORM_IMAGE_NAME));
+    return 0;
+}
diff --git a/hal/msm8916/platform.h b/hal/msm8916/platform.h
index 61987c6..d82bd1c 100644
--- a/hal/msm8916/platform.h
+++ b/hal/msm8916/platform.h
@@ -32,6 +32,8 @@
     FLUENCE_BROADSIDE = 0x2,
 };
 
+#define PLATFORM_IMAGE_NAME "modem"
+
 /*
  * Below are the devices for which is back end is same, SLIMBUS_0_RX.
  * All these devices are handled by the internal HW codec. We can
@@ -250,4 +252,6 @@
     stop_record_t stop_record;
 };
 
+int platform_get_subsys_image_name (char *buf);
+
 #endif // QCOM_AUDIO_PLATFORM_H