blob: e7f8eff1f8a757fdde1fa3a0fb42a43e4fa9c804 [file] [log] [blame]
/* st_hw_lpma_extn.c
*
* This file contains the implementation of LPMA (Low Power Mic Access) feature
* functionality which provides WDSP buffers data access to non-SVA clients.
* SLPI (Sensors Low Power Island) is one such non-SVA client.
*
* Copyright (c) 2018-2019, 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 "sound_trigger_hw_lpma"
/* #define LOG_NDEBUG 0 */
#include <stdlib.h>
#include <dlfcn.h>
#include <errno.h>
#include <cutils/log.h>
#include <linux/wcd-spi-ac-params.h>
#include "gcs_api.h"
#include "sound_trigger_hw.h"
#include "sound_trigger_platform.h"
#include "st_common_defs.h"
#include "st_hw_session_gcs.h"
#include "st_hw_extn.h"
#include "st_graphite_api.h"
/*
* Notes:
* 1. Open ac (access control) driver only if usecase is started.
* Keeping it opened from bootup is unncessary overhead of ac driver,
* if usecase never gets started.
* 2. During audio concurrency or device switch either the graph is closed
* or device backend is disconnected, which means no data flow in WDSP.
* Inform SLPI of concurrency disabled/enabled so that it can stop/start
* reading the buffers. For SSR cases driver already knows to inform the
* SLPI.
*/
#define SPI_AC_DEV_NODE "/dev/wcd-spi-ac-client"
#define GCS_LIB "libgcs.so"
enum lpma_state {
LPMA_NOINIT,
LPMA_IDLE,
LPMA_SETUP,
LPMA_ACTIVE
};
enum conc_event_type {
CLIENT_STARTED = 0x01,
TRANSIT_TO_APE = 0x02,
WDSP_OFFLINE = 0x04,
SLPI_OFFLINE = 0x80
};
/* gcs functions loaded from dynamic library */
static int32_t(*gcs_open_fn)(uint32_t UID,
uint32_t DID, uint32_t *graph_handle);
static int32_t(*gcs_enable_fn)(uint32_t graph_handle,
void *non_persist_ucal,
uint32_t size_ucal);
static int32_t(*gcs_disable_fn)(uint32_t graph_handle);
static int32_t(*gcs_close_fn)(uint32_t graph_handle);
static int32_t(*gcs_enable_device_fn)(uint32_t graph_handle,
uint32_t UID, int8_t *payload, uint32_t payload_size);
static int32_t(*gcs_disable_device_fn)(uint32_t graph_handle);
static int32_t (*gcs_set_config_fn)(uint32_t graph_handle, void *payload,
uint32_t payload_size);
static int32_t (*gcs_get_config_fn)(uint32_t graph_handle, void *payload,
uint32_t payload_size, struct gcs_config_rsp *response);
typedef struct {
struct sound_trigger_device *stdev;
struct st_lpma_config *lpma_cfg;
enum lpma_state state;
enum conc_event_type conc_event;
void *gcs_lib_handle;
uint32_t graph_handle;
struct wcd_spi_ac_write_cmd *ac_cmd;
int32_t ac_fd;
uint32_t circbuf_addr[MAX_LPMA_BB_IDS * MAX_BRDIGE_BUF_PORTS];
st_device_t st_device;
char *st_device_name;
pthread_mutex_t lock;
} sthw_extn_lpma_data_t;
static sthw_extn_lpma_data_t lpma_data = {
.stdev = NULL,
.lpma_cfg = NULL,
.state = LPMA_NOINIT,
.conc_event = 0,
.gcs_lib_handle = NULL,
.graph_handle = 0,
.ac_cmd = NULL,
.ac_fd = -1,
.circbuf_addr = {0},
.st_device = ST_DEVICE_NONE,
.st_device_name = NULL,
.lock = PTHREAD_MUTEX_INITIALIZER,
};
static int lpma_get_bridge_bufs_addr()
{
struct graphite_cal_header req_cfg[MAX_LPMA_BB_IDS];
struct gcs_bridge_buf_params rsp_buf[MAX_LPMA_BB_IDS];
struct gcs_config_rsp gcs_rsp;
int status = 0, size = 0;
unsigned int i = 0;
/*
* Request bridge buffers address info. gcs_bridge_buf_params payload is
* already 4 byte aligned by definition.
*/
for (i = 0; i < lpma_data.lpma_cfg->num_bb_ids; i++) {
req_cfg[i].module_id = lpma_data.lpma_cfg->bb_params[i].module_id;
req_cfg[i].instance_id = lpma_data.lpma_cfg->bb_params[i].instance_id;
req_cfg[i].param_id = lpma_data.lpma_cfg->bb_params[i].param_id;
req_cfg[i].reserved = 0;
req_cfg[i].size = sizeof(struct gcs_bridge_buf_payload);
}
size = lpma_data.lpma_cfg->num_bb_ids * sizeof(struct graphite_cal_header);
gcs_rsp.rsp_buf = rsp_buf;
gcs_rsp.rsp_size_requested = lpma_data.lpma_cfg->num_bb_ids *
sizeof(struct gcs_bridge_buf_params);
ALOGD("%s: gcs_get_config req_pld_size %d, rsp_size_requested %d",
__func__, size, gcs_rsp.rsp_size_requested) ;
status = gcs_get_config_fn(lpma_data.graph_handle, &req_cfg, size, &gcs_rsp);
if (status) {
ALOGE("%s: gcs_get_config failed status %d", __func__, status);
return status;
}
if (gcs_rsp.rsp_size_returned != gcs_rsp.rsp_size_requested) {
ALOGE("%s: gcs_get_config returned[%d] != requested[%d]", __func__,
gcs_rsp.rsp_size_returned, gcs_rsp.rsp_size_requested);
return -EINVAL;
}
/* Extract and store the buffer addresses from payload received from gcs */
for (i = 0; i < lpma_data.lpma_cfg->num_bb_ids; i++) {
memcpy (&lpma_data.circbuf_addr[i * MAX_BRDIGE_BUF_PORTS],
rsp_buf[i].payload.addr_circbuf_info,
sizeof(rsp_buf[i].payload.addr_circbuf_info));
}
return 0;
}
static int lpma_send_bridge_bufs_addr()
{
int size = 0;
size = lpma_data.lpma_cfg->num_bb_ids *
MAX_BRDIGE_BUF_PORTS * sizeof(uint32_t);
memcpy(lpma_data.ac_cmd->payload, lpma_data.circbuf_addr, size);
lpma_data.ac_cmd->cmd_type = WCD_SPI_AC_CMD_BUF_DATA;
size += sizeof(struct wcd_spi_ac_write_cmd);
ALOGD("%s: write size %d", __func__, size);
if (write(lpma_data.ac_fd, lpma_data.ac_cmd, size) != size) {
ALOGE("%s: ac_cmd_circ_buf_data failed, %s, size %d", __func__,
strerror(errno), size);
return -errno;
}
return 0;
}
static int lpma_set_device(bool enable)
{
char st_device_name[DEVICE_NAME_MAX_SIZE] = { 0 };
int ref_cnt_idx = 0, ref_cnt = 0, status = 0;
st_device_t st_device;
audio_devices_t capture_device;
if (enable) {
capture_device = platform_stdev_get_capture_device(
lpma_data.stdev->platform);
st_device = platform_stdev_get_device(lpma_data.stdev->platform,
NULL, capture_device, ST_EXEC_MODE_CPE);
if (platform_stdev_get_device_name(lpma_data.stdev->platform,
ST_EXEC_MODE_CPE, st_device, st_device_name) < 0) {
ALOGE("%s: Invalid sound trigger device returned", __func__);
return -EINVAL;
}
pthread_mutex_lock(&lpma_data.stdev->ref_cnt_lock);
ref_cnt_idx = (ST_EXEC_MODE_CPE * ST_DEVICE_MAX) + st_device;
ref_cnt = ++(lpma_data.stdev->dev_ref_cnt[ref_cnt_idx]);
if (1 == ref_cnt) {
status = platform_stdev_send_calibration(lpma_data.stdev->platform,
capture_device, ST_EXEC_MODE_CPE, NULL,
ACDB_LSM_APP_TYPE_NO_TOPOLOGY, false, ST_DEVICE_CAL);
if (!status) {
ALOGD("%s: enable device (%x) = %s", __func__, st_device,
st_device_name);
audio_route_apply_and_update_path(lpma_data.stdev->audio_route,
st_device_name);
lpma_data.stdev->capture_device = capture_device;
} else {
ALOGE("%s: failed to send calibration %d", __func__, status);
--(lpma_data.stdev->dev_ref_cnt[ref_cnt_idx]);
}
}
pthread_mutex_unlock(&lpma_data.stdev->ref_cnt_lock);
lpma_data.st_device = st_device;
lpma_data.st_device_name = strdup(st_device_name);
} else {
if (!lpma_data.st_device_name) {
ALOGE("%s: Invalid sound trigger device name", __func__);
return -EINVAL;
}
ref_cnt_idx = (ST_EXEC_MODE_CPE * ST_DEVICE_MAX) + lpma_data.st_device;
pthread_mutex_lock(&lpma_data.stdev->ref_cnt_lock);
ref_cnt = lpma_data.stdev->dev_ref_cnt[ref_cnt_idx];
if (0 < ref_cnt) {
ref_cnt = --(lpma_data.stdev->dev_ref_cnt[ref_cnt_idx]);
} else {
ALOGV("%s: ref_cnt = %d", __func__, ref_cnt);
pthread_mutex_unlock(&lpma_data.stdev->ref_cnt_lock);
return status;
}
if (0 == ref_cnt) {
ALOGD("%s: disable device (%x) = %s", __func__, lpma_data.st_device,
lpma_data.st_device_name);
audio_route_reset_and_update_path(lpma_data.stdev->audio_route,
lpma_data.st_device_name);
}
pthread_mutex_unlock(&lpma_data.stdev->ref_cnt_lock);
free(lpma_data.st_device_name);
}
return status;
}
static int lpma_setup()
{
int status = 0, acdb_id = 0;
st_device_t st_device;
audio_devices_t capture_device;
ALOGV("%s: enter ", __func__);
if (lpma_data.state != LPMA_IDLE)
return 0;
status = st_hw_gcs_load_wdsp(true);
if (status) {
ALOGE("%s: wdsp image load failed %d", __func__, status);
return status;
}
lpma_data.ac_cmd = calloc(1, sizeof(struct wcd_spi_ac_write_cmd) +
sizeof(lpma_data.circbuf_addr));
if (!lpma_data.ac_cmd) {
ALOGE("%s: ac_cmd allocation failed", __func__);
status = -ENOMEM;
goto cleanup;
}
capture_device = platform_stdev_get_capture_device(
lpma_data.stdev->platform);
st_device = platform_stdev_get_device(lpma_data.stdev->platform,
NULL, capture_device, ST_EXEC_MODE_CPE);
acdb_id = platform_stdev_get_acdb_id(st_device, ST_EXEC_MODE_CPE);
if (0 > acdb_id) {
ALOGE("%s: Could not get ACDB ID for device %d", __func__,
st_device);
status = -EINVAL;
goto cleanup;
}
ALOGD("%s: gcs_open with uid %d, did %d", __func__,
lpma_data.lpma_cfg->uid, acdb_id);
status = gcs_open_fn(lpma_data.lpma_cfg->uid, acdb_id,
&lpma_data.graph_handle);
if (status) {
ALOGE("%s: gcs_open failed status %d", __func__, status);
goto cleanup;
}
lpma_data.state = LPMA_SETUP;
ALOGV("%s: exit ", __func__);
return 0;
cleanup:
if (lpma_data.ac_cmd) {
free(lpma_data.ac_cmd);
lpma_data.ac_cmd = NULL;
}
st_hw_gcs_load_wdsp(false);
ALOGV("%s: exit status %d", __func__, status);
return status;
}
static int lpma_teardown()
{
int status = 0;
ALOGV("%s: enter", __func__);
if (lpma_data.state != LPMA_SETUP)
return 0;
ALOGD("%s: gcs_close with handle %d", __func__, lpma_data.graph_handle);
status = gcs_close_fn(lpma_data.graph_handle);
if (status)
ALOGE("%s: gcs_close failed status %d", __func__, status);
if (lpma_data.ac_cmd) {
free(lpma_data.ac_cmd);
lpma_data.ac_cmd = NULL;
}
status = st_hw_gcs_load_wdsp(false);
if (status)
ALOGE("%s: wdsp image unload failed %d", __func__, status);
lpma_data.state = LPMA_IDLE;
ALOGV("%s: exit status %d", __func__, status);
return status;
}
static int lpma_start(bool conc)
{
int status = 0, size = 0;
char ac_dev_node[50];
ALOGV("%s: enter ", __func__);
if (lpma_data.state != LPMA_SETUP)
return 0;
status = lpma_set_device(true);
if (status)
return status;
ALOGD("%s: gcs_enable with handle %d", __func__, lpma_data.graph_handle);
status = gcs_enable_fn(lpma_data.graph_handle, NULL, 0);
if (status) {
ALOGE("%s: gcs_enable failed status %d", __func__, status);
goto cleanup1;
}
snprintf(ac_dev_node, sizeof(ac_dev_node), "/%s/%s",
"dev", WCD_SPI_AC_CLIENT_CDEV_NAME);
lpma_data.ac_fd = open(ac_dev_node, O_RDWR);
if (lpma_data.ac_fd < 0) {
ALOGE("%s: %s ", __func__, strerror(errno));
status = -errno;
goto cleanup2;
}
/* query buffer addresses from WDSP and send to access control driver */
status = lpma_get_bridge_bufs_addr();
if (!status)
status = lpma_send_bridge_bufs_addr();
if (status)
goto cleanup2;
if (conc) {
/* disable SLPI SPI access */
lpma_data.ac_cmd->cmd_type = WCD_SPI_AC_CMD_CONC_END;
size = sizeof(struct wcd_spi_ac_write_cmd);
if (write(lpma_data.ac_fd, lpma_data.ac_cmd, size) != size) {
ALOGE("%s: ac_cmd_conc_begin failed %s", __func__,
strerror(errno));
status = -errno;
goto cleanup2;
}
}
lpma_data.state = LPMA_ACTIVE;
ALOGV("%s: exit ", __func__);
return 0;
cleanup2:
if (lpma_data.ac_fd >= 0)
close(lpma_data.ac_fd);
gcs_disable_fn(lpma_data.graph_handle);
cleanup1:
lpma_set_device(false);
ALOGV("%s: exit status %d", __func__, status);
return status;
}
static int lpma_stop(bool conc)
{
int status = 0, rc = 0, size = 0;
ALOGV("%s: enter", __func__);
if (lpma_data.state != LPMA_ACTIVE)
return 0;
if (conc) {
/* disable SLPI SPI access */
lpma_data.ac_cmd->cmd_type = WCD_SPI_AC_CMD_CONC_BEGIN;
size = sizeof(struct wcd_spi_ac_write_cmd);
if (write(lpma_data.ac_fd, lpma_data.ac_cmd, size) != size) {
ALOGE("%s: ac_cmd_conc_begin failed %s", __func__,
strerror(errno));
return -errno;
}
}
/* Must be closed before DSP graph close, as part of gcs_disable, to
* disable SPI access to SLPI */
close(lpma_data.ac_fd);
ALOGD("%s: gcs_disable with handle %d", __func__, lpma_data.graph_handle);
status = gcs_disable_fn(lpma_data.graph_handle);
if (status) {
ALOGE("%s: gcs_disable failed status %d", __func__, status);
rc = status;
}
status = lpma_set_device(false);
if (status)
rc = status;
lpma_data.state = LPMA_SETUP;
ALOGV("%s: exit status %d", __func__, rc);
return rc;
}
static int lpma_handle_audio_concurrency()
{
int status = 0;
ALOGV("%s: enter", __func__);
/* Usecases:
* UC1: lpma is not yet active -> concurrency is active: ignore
* UC2: lpma is active -> concurrency is active: stop lpma
* UC3: lpma is already stopped -> another concurrency: ignore
*/
if (lpma_data.state == LPMA_IDLE)
return 0;
if ((lpma_data.state == LPMA_ACTIVE) &&
(lpma_data.stdev->tx_concurrency_active == 1))
status = lpma_stop(true);
else if ((lpma_data.state != LPMA_ACTIVE) &&
(lpma_data.stdev->tx_concurrency_active == 0))
status = lpma_start(true);
ALOGV("%s: exit status %d", __func__, status);
return status;
}
static int lpma_enable_device()
{
int status = 0, acdb_id = 0, size = 0;
ALOGV("%s: enter", __func__);
/*
* Device switch can initiate disable and enable device when lpma is not
* yet active due to usecase not started or audio concurrency. Ignore it.
* Note that we don't change state as disable/enable are internal
* sequential calls.
*/
if (lpma_data.state != LPMA_ACTIVE)
return 0;
status = lpma_set_device(true);
if (status)
return status;
acdb_id = platform_stdev_get_acdb_id(lpma_data.st_device, ST_EXEC_MODE_CPE);
if (0 > acdb_id) {
ALOGE("%s: Could not get ACDB ID for device %d", __func__,
lpma_data.st_device);
status = -EINVAL;
goto cleanup1;
}
ALOGD("%s: gcs_enable_device with handle %d, acdb_id %d",
__func__, lpma_data.graph_handle, acdb_id);
status = gcs_enable_device_fn(lpma_data.graph_handle, acdb_id, NULL, 0);
if (status) {
ALOGE("%s: gcs_enable_device failed status %d", __func__, status);
goto cleanup1;
}
/* enable SLPI SPI access */
lpma_data.ac_cmd->cmd_type = WCD_SPI_AC_CMD_CONC_END;
size = sizeof(struct wcd_spi_ac_write_cmd);
if (write(lpma_data.ac_fd, lpma_data.ac_cmd, size) != size) {
ALOGE("%s: ac_cmd_conc_end failed %s", __func__,
strerror(errno));
status = -errno;
goto cleanup2;
}
ALOGV("%s: exit", __func__);
return 0;
cleanup2:
gcs_disable_fn(lpma_data.graph_handle);
cleanup1:
lpma_set_device(false);
ALOGV("%s: exit status %d", __func__, status);
return status;
}
static int lpma_disable_device()
{
int status = 0, rc = 0, size = 0;
ALOGV("%s: enter", __func__);
/*
* Device switch can initiate disable and enable device when lpma is not
* yet active due to usecase not started or audio concurrency. Ignore it.
* Note that we don't change state as disable/enable are internal
* sequential calls.
*/
if (lpma_data.state != LPMA_ACTIVE)
return 0;
/* disable SLPI SPI access */
lpma_data.ac_cmd->cmd_type = WCD_SPI_AC_CMD_CONC_BEGIN;
size = sizeof(struct wcd_spi_ac_write_cmd);
if (write(lpma_data.ac_fd, lpma_data.ac_cmd, size) != size) {
ALOGE("%s: ac_cmd_conc_begin failed %s", __func__,
strerror(errno));
return -errno;
}
ALOGD("%s: gcs_disable_device with handle %d", __func__,
lpma_data.graph_handle);
rc = gcs_disable_device_fn(lpma_data.graph_handle);
if (rc) {
ALOGE("%s: gcs_disable_device failed status %d", __func__, status);
status = rc;
}
rc = lpma_set_device(false);
if (rc)
status = rc;
ALOGV("%s: exit status %d", __func__, status);
return status;
}
int sthw_extn_lpma_notify_event(enum sthw_extn_lpma_event_type event)
{
int status = 0, rc = 0;
ALOGV("%s: enter", __func__);
if (lpma_data.state == LPMA_NOINIT)
return -EINVAL;
pthread_mutex_lock(&lpma_data.lock);
switch (event) {
case LPMA_EVENT_START:
if (!(lpma_data.conc_event & (WDSP_OFFLINE | TRANSIT_TO_APE))) {
status = lpma_setup();
if (!status && !lpma_data.stdev->tx_concurrency_active)
status = lpma_start(false);
}
if (!status)
lpma_data.conc_event |= CLIENT_STARTED;
break;
case LPMA_EVENT_CPE_STATUS_ONLINE:
case LPMA_EVENT_TRANSIT_APE_TO_CPE:
if (lpma_data.conc_event & CLIENT_STARTED) {
status = lpma_setup();
if (!status && !lpma_data.stdev->tx_concurrency_active)
status = lpma_start(event == LPMA_EVENT_TRANSIT_APE_TO_CPE ?
true : false);
}
if (event == LPMA_EVENT_CPE_STATUS_ONLINE)
lpma_data.conc_event &= ~WDSP_OFFLINE;
else
lpma_data.conc_event &= ~TRANSIT_TO_APE;
break;
case LPMA_EVENT_STOP:
case LPMA_EVENT_CPE_STATUS_OFFLINE:
case LPMA_EVENT_TRANSIT_CPE_TO_APE:
rc = lpma_stop((event == LPMA_EVENT_TRANSIT_CPE_TO_APE) ? true : false);
if (rc)
status = rc;
rc = lpma_teardown();
if (rc)
status = rc;
if (event == LPMA_EVENT_STOP)
lpma_data.conc_event &= ~CLIENT_STARTED;
else if (event == LPMA_EVENT_CPE_STATUS_OFFLINE)
lpma_data.conc_event |= WDSP_OFFLINE;
else
lpma_data.conc_event |= TRANSIT_TO_APE;
break;
case LPMA_EVENT_AUDIO_CONCURRENCY:
status = lpma_handle_audio_concurrency();
break;
case LPMA_EVENT_ENABLE_DEVICE:
status = lpma_disable_device();
break;
case LPMA_EVENT_DISABLE_DEVICE:
status = lpma_enable_device();
break;
case LPMA_EVENT_SLPI_STATUS_OFFLINE:
lpma_data.conc_event |= SLPI_OFFLINE;
break;
case LPMA_EVENT_SLPI_STATUS_ONLINE:
/* resend bridge buffer addresses to access control driver */
if (lpma_data.state == LPMA_ACTIVE)
status = lpma_send_bridge_bufs_addr();
lpma_data.conc_event &= ~SLPI_OFFLINE;
break;
default:
ALOGW("%s: unhandled event %d", __func__, event);
status = -EINVAL;
}
pthread_mutex_unlock(&lpma_data.lock);
ALOGV("%s: exist status %d", __func__, status);
return status;
}
int sthw_extn_lpma_init(struct sound_trigger_device *stdev)
{
int status = 0;
ALOGV("%s: enter", __func__);
lpma_data.gcs_lib_handle = dlopen(GCS_LIB, RTLD_NOW);
if (!lpma_data.gcs_lib_handle) {
ALOGE("%s: %s", __func__, dlerror());
return -ENOENT;
}
DLSYM(lpma_data.gcs_lib_handle, gcs_open_fn, gcs_open, status);
if (status)
goto cleanup;
DLSYM(lpma_data.gcs_lib_handle, gcs_close_fn, gcs_close, status);
if (status)
goto cleanup;
DLSYM(lpma_data.gcs_lib_handle, gcs_enable_fn, gcs_enable, status);
if (status)
goto cleanup;
DLSYM(lpma_data.gcs_lib_handle, gcs_disable_fn, gcs_disable, status);
if (status)
goto cleanup;
DLSYM(lpma_data.gcs_lib_handle, gcs_enable_device_fn, gcs_enable_device,
status);
if (status)
goto cleanup;
DLSYM(lpma_data.gcs_lib_handle, gcs_disable_device_fn,
gcs_disable_device, status);
if (status)
goto cleanup;
DLSYM(lpma_data.gcs_lib_handle, gcs_set_config_fn, gcs_set_config,
status);
if (status)
goto cleanup;
DLSYM(lpma_data.gcs_lib_handle, gcs_get_config_fn, gcs_get_config,
status);
if (status)
goto cleanup;
/*
* No need to gcs_init() as it gets initialized during boot up from
* platform source
*/
/* Get lpma graph ids from platform config */
platform_stdev_get_lpma_config(stdev->platform, &lpma_data.lpma_cfg);
if (!lpma_data.lpma_cfg) {
ALOGE("%s: lpma graph platform info not present", __func__);
goto cleanup;
}
lpma_data.stdev = stdev;
lpma_data.state = LPMA_IDLE;
lpma_data.conc_event = 0;
ALOGD("%s: exit", __func__);
return 0;
cleanup:
dlclose(lpma_data.gcs_lib_handle);
lpma_data.gcs_lib_handle = NULL;
ALOGV("%s: exit status %d", __func__, status);
return status;
}
void sthw_extn_lpma_deinit()
{
ALOGV("%s: enter", __func__);
if (lpma_data.gcs_lib_handle) {
dlclose(lpma_data.gcs_lib_handle);
lpma_data.gcs_lib_handle = NULL;
}
lpma_data.state = LPMA_NOINIT;
ALOGD("%s: exit", __func__);
}