ASoC: msm: qdsp6v2: Add timestamp support for lsm detection event

Add support to parse LSM_SESSION_EVENT_DETECTION_STATUS_V3
in cases where event status requires timestamp corresponding
to detection.

Framework mode config is set to timestamp mode in cases where
LSM_SESSION_EVENT_DETECTION_STATUS_V3 is required.

Change-Id: Id0da3b24d55ac56ff6b61372ede9c63f50b2f4d4
Signed-off-by: Chaithanya Krishna Bacharaju <chaithan@codeaurora.org>
Signed-off-by: Revathi Uddaraju <revathiu@codeaurora.org>
This commit is contained in:
Chaithanya Krishna Bacharaju 2016-07-21 10:08:32 +05:30 committed by Revathi Uddaraju
parent 6df96093b3
commit 1f5681eec2
4 changed files with 316 additions and 19 deletions

View file

@ -9046,6 +9046,7 @@ struct asm_aptx_dec_fmt_blk_v2 {
#define LSM_SESSION_EVENT_DETECTION_STATUS_V2 (0x00012B01)
#define LSM_DATA_EVENT_READ_DONE (0x00012B02)
#define LSM_DATA_EVENT_STATUS (0x00012B03)
#define LSM_SESSION_EVENT_DETECTION_STATUS_V3 (0x00012B04)
#define LSM_MODULE_ID_VOICE_WAKEUP (0x00012C00)
#define LSM_PARAM_ID_ENDPOINT_DETECT_THRESHOLD (0x00012C01)
@ -9062,6 +9063,7 @@ struct asm_aptx_dec_fmt_blk_v2 {
#define LSM_PARAM_ID_SWMAD_MODEL (0x00012C19)
#define LSM_PARAM_ID_SWMAD_ENABLE (0x00012C1A)
#define LSM_PARAM_ID_POLLING_ENABLE (0x00012C1B)
#define LSM_PARAM_ID_FWK_MODE_CONFIG (0x00012C27)
/* HW MAD specific */
#define AFE_MODULE_HW_MAD (0x00010230)

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2016, Linux Foundation. All rights reserved.
* Copyright (c) 2013-2017, Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@ -86,6 +86,7 @@ struct lsm_client {
int session_state;
bool poll_enable;
int perf_mode;
uint32_t event_mode;
};
struct lsm_stream_cmd_open_tx {
@ -146,6 +147,12 @@ struct lsm_param_poll_enable {
uint32_t polling_enable;
} __packed;
struct lsm_param_fwk_mode_cfg {
struct lsm_param_payload_common common;
uint32_t minor_version;
uint32_t mode;
} __packed;
/*
* This param cannot be sent in this format.
* The actual number of confidence level values
@ -272,6 +279,12 @@ struct lsm_cmd_read_done {
uint32_t flags;
} __packed;
struct lsm_cmd_set_fwk_mode_cfg {
struct apr_hdr msg_hdr;
struct lsm_set_params_hdr params_hdr;
struct lsm_param_fwk_mode_cfg fwk_mode_cfg;
} __packed;
struct lsm_client *q6lsm_client_alloc(lsm_app_cb cb, void *priv);
void q6lsm_client_free(struct lsm_client *client);
int q6lsm_open(struct lsm_client *client, uint16_t app_id);
@ -301,4 +314,5 @@ void q6lsm_sm_set_param_data(struct lsm_client *client,
struct lsm_params_info *p_info,
size_t *offset);
int q6lsm_set_port_connected(struct lsm_client *client);
int q6lsm_set_fwk_mode_cfg(struct lsm_client *client, uint32_t event_mode);
#endif /* __Q6LSM_H__ */

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
* Copyright (c) 2013-2017, Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@ -76,7 +76,7 @@ static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
struct lsm_priv {
struct snd_pcm_substream *substream;
struct lsm_client *lsm_client;
struct snd_lsm_event_status *event_status;
struct snd_lsm_event_status_v3 *event_status;
spinlock_t event_lock;
wait_queue_head_t event_wait;
unsigned long event_avail;
@ -201,6 +201,8 @@ static void lsm_event_handler(uint32_t opcode, uint32_t token,
uint16_t status = 0;
uint16_t payload_size = 0;
uint16_t index = 0;
uint32_t event_ts_lsw = 0;
uint32_t event_ts_msw = 0;
if (!substream || !substream->private_data) {
pr_err("%s: Invalid %s\n", __func__,
@ -274,24 +276,44 @@ static void lsm_event_handler(uint32_t opcode, uint32_t token,
"%s: event detect status = %d payload size = %d\n",
__func__, status , payload_size);
break;
case LSM_SESSION_EVENT_DETECTION_STATUS_V3:
event_ts_lsw = ((uint32_t *)payload)[0];
event_ts_msw = ((uint32_t *)payload)[1];
status = (uint16_t)((uint8_t *)payload)[8];
payload_size = (uint16_t)((uint8_t *)payload)[9];
index = 10;
dev_dbg(rtd->dev,
"%s: ts_msw = %u, ts_lsw = %u, event detect status = %d payload size = %d\n",
__func__, event_ts_msw, event_ts_lsw, status,
payload_size);
break;
default:
break;
}
if (opcode == LSM_SESSION_EVENT_DETECTION_STATUS ||
opcode == LSM_SESSION_EVENT_DETECTION_STATUS_V2) {
opcode == LSM_SESSION_EVENT_DETECTION_STATUS_V2 ||
opcode == LSM_SESSION_EVENT_DETECTION_STATUS_V3) {
spin_lock_irqsave(&prtd->event_lock, flags);
prtd->event_status = krealloc(prtd->event_status,
sizeof(struct snd_lsm_event_status) +
sizeof(struct snd_lsm_event_status_v3) +
payload_size, GFP_ATOMIC);
if (!prtd->event_status) {
dev_err(rtd->dev, "%s: no memory for event status\n",
__func__);
return;
}
/*
* event status timestamp will be non-zero and valid if
* opcode is LSM_SESSION_EVENT_DETECTION_STATUS_V3
*/
prtd->event_status->timestamp_lsw = event_ts_lsw;
prtd->event_status->timestamp_msw = event_ts_msw;
prtd->event_status->status = status;
prtd->event_status->payload_size = payload_size;
if (likely(prtd->event_status)) {
memcpy(prtd->event_status->payload,
&((uint8_t *)payload)[index],
@ -766,10 +788,8 @@ static int msm_lsm_ioctl_shared(struct snd_pcm_substream *substream,
struct snd_lsm_session_data session_data;
int rc = 0;
int xchg = 0;
u32 size = 0;
struct snd_pcm_runtime *runtime;
struct lsm_priv *prtd;
struct snd_lsm_event_status *user = arg;
struct snd_lsm_detection_params det_params;
uint8_t *confidence_level = NULL;
@ -926,6 +946,10 @@ static int msm_lsm_ioctl_shared(struct snd_pcm_substream *substream,
break;
case SNDRV_LSM_EVENT_STATUS:
case SNDRV_LSM_EVENT_STATUS_V3: {
uint32_t ts_lsw, ts_msw;
uint16_t status = 0, payload_size = 0;
dev_dbg(rtd->dev, "%s: Get event status\n", __func__);
atomic_set(&prtd->event_wait_stop, 0);
rc = wait_event_freezable(prtd->event_wait,
@ -938,9 +962,12 @@ static int msm_lsm_ioctl_shared(struct snd_pcm_substream *substream,
dev_dbg(rtd->dev, "%s: New event available %ld\n",
__func__, prtd->event_avail);
spin_lock_irqsave(&prtd->event_lock, flags);
if (prtd->event_status) {
size = sizeof(*(prtd->event_status)) +
prtd->event_status->payload_size;
payload_size = prtd->event_status->payload_size;
ts_lsw = prtd->event_status->timestamp_lsw;
ts_msw = prtd->event_status->timestamp_msw;
status = prtd->event_status->status;
spin_unlock_irqrestore(&prtd->event_lock,
flags);
} else {
@ -952,15 +979,43 @@ static int msm_lsm_ioctl_shared(struct snd_pcm_substream *substream,
__func__);
break;
}
if (user->payload_size <
prtd->event_status->payload_size) {
dev_dbg(rtd->dev,
"%s: provided %d bytes isn't enough, needs %d bytes\n",
__func__, user->payload_size,
prtd->event_status->payload_size);
rc = -ENOMEM;
if (cmd == SNDRV_LSM_EVENT_STATUS) {
struct snd_lsm_event_status *user = arg;
if (user->payload_size < payload_size) {
dev_dbg(rtd->dev,
"%s: provided %d bytes isn't enough, needs %d bytes\n",
__func__, user->payload_size,
payload_size);
rc = -ENOMEM;
} else {
user->status = status;
user->payload_size = payload_size;
memcpy(user->payload,
prtd->event_status->payload,
payload_size);
}
} else {
memcpy(user, prtd->event_status, size);
struct snd_lsm_event_status_v3 *user_v3 = arg;
if (user_v3->payload_size < payload_size) {
dev_dbg(rtd->dev,
"%s: provided %d bytes isn't enough, needs %d bytes\n",
__func__, user_v3->payload_size,
payload_size);
rc = -ENOMEM;
} else {
user_v3->timestamp_lsw = ts_lsw;
user_v3->timestamp_msw = ts_msw;
user_v3->status = status;
user_v3->payload_size = payload_size;
memcpy(user_v3->payload,
prtd->event_status->payload,
payload_size);
}
}
if (!rc) {
if (prtd->lsm_client->lab_enable
&& !prtd->lsm_client->lab_started
&& prtd->event_status->status ==
@ -985,6 +1040,7 @@ static int msm_lsm_ioctl_shared(struct snd_pcm_substream *substream,
rc = 0;
}
break;
}
case SNDRV_LSM_ABORT_EVENT:
dev_dbg(rtd->dev, "%s: Aborting event status wait\n",
@ -1097,6 +1153,37 @@ static int msm_lsm_ioctl_shared(struct snd_pcm_substream *substream,
rc = q6lsm_set_port_connected(prtd->lsm_client);
break;
case SNDRV_LSM_SET_FWK_MODE_CONFIG: {
u32 *mode = NULL;
if (!arg) {
dev_err(rtd->dev,
"%s: Invalid param arg for ioctl %s session %d\n",
__func__, "SNDRV_LSM_SET_FWK_MODE_CONFIG",
prtd->lsm_client->session);
rc = -EINVAL;
break;
}
mode = (u32 *)arg;
if (prtd->lsm_client->event_mode == *mode) {
dev_dbg(rtd->dev,
"%s: mode for %d already set to %d\n",
__func__, prtd->lsm_client->session, *mode);
rc = 0;
} else {
dev_dbg(rtd->dev, "%s: Event mode = %d\n",
__func__, *mode);
rc = q6lsm_set_fwk_mode_cfg(prtd->lsm_client, *mode);
if (!rc)
prtd->lsm_client->event_mode = *mode;
else
dev_err(rtd->dev,
"%s: set event mode failed %d\n",
__func__, rc);
}
break;
}
default:
dev_dbg(rtd->dev,
"%s: Falling into default snd_lib_ioctl cmd 0x%x\n",
@ -1115,6 +1202,21 @@ static int msm_lsm_ioctl_shared(struct snd_pcm_substream *substream,
return rc;
}
#ifdef CONFIG_COMPAT
struct snd_lsm_event_status32 {
u16 status;
u16 payload_size;
u8 payload[0];
};
struct snd_lsm_event_status_v3_32 {
u32 timestamp_lsw;
u32 timestamp_msw;
u16 status;
u16 payload_size;
u8 payload[0];
};
struct snd_lsm_sound_model_v2_32 {
compat_uptr_t data;
compat_uptr_t confidence_level;
@ -1152,6 +1254,8 @@ enum {
_IOW('U', 0x0A, struct snd_lsm_detection_params_32),
SNDRV_LSM_SET_MODULE_PARAMS_32 =
_IOW('U', 0x0B, struct snd_lsm_module_params_32),
SNDRV_LSM_EVENT_STATUS_V3_32 =
_IOW('U', 0x0F, struct snd_lsm_event_status_v3_32),
};
static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream,
@ -1240,6 +1344,73 @@ static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream,
break;
}
case SNDRV_LSM_EVENT_STATUS_V3_32: {
struct snd_lsm_event_status_v3_32 userarg32, *user32 = NULL;
struct snd_lsm_event_status_v3 *user = NULL;
if (copy_from_user(&userarg32, arg, sizeof(userarg32))) {
dev_err(rtd->dev, "%s: err copyuser ioctl %s\n",
__func__, "SNDRV_LSM_EVENT_STATUS_V3_32");
return -EFAULT;
}
if (userarg32.payload_size >
LISTEN_MAX_STATUS_PAYLOAD_SIZE) {
pr_err("%s: payload_size %d is invalid, max allowed = %d\n",
__func__, userarg32.payload_size,
LISTEN_MAX_STATUS_PAYLOAD_SIZE);
return -EINVAL;
}
size = sizeof(*user) + userarg32.payload_size;
user = kmalloc(size, GFP_KERNEL);
if (!user) {
dev_err(rtd->dev,
"%s: Allocation failed event status size %d\n",
__func__, size);
return -EFAULT;
}
cmd = SNDRV_LSM_EVENT_STATUS_V3;
user->payload_size = userarg32.payload_size;
err = msm_lsm_ioctl_shared(substream, cmd, user);
/* Update size with actual payload size */
size = sizeof(userarg32) + user->payload_size;
if (!err && !access_ok(VERIFY_WRITE, arg, size)) {
dev_err(rtd->dev,
"%s: write verify failed size %d\n",
__func__, size);
err = -EFAULT;
}
if (!err) {
user32 = kmalloc(size, GFP_KERNEL);
if (!user32) {
dev_err(rtd->dev,
"%s: Allocation event user status size %d\n",
__func__, size);
err = -EFAULT;
} else {
user32->timestamp_lsw = user->timestamp_lsw;
user32->timestamp_msw = user->timestamp_msw;
user32->status = user->status;
user32->payload_size = user->payload_size;
memcpy(user32->payload,
user->payload, user32->payload_size);
}
}
if (!err && (copy_to_user(arg, user32, size))) {
dev_err(rtd->dev, "%s: failed to copy payload %d",
__func__, size);
err = -EFAULT;
}
kfree(user);
kfree(user32);
if (err)
dev_err(rtd->dev, "%s: lsmevent failed %d",
__func__, err);
break;
}
case SNDRV_LSM_REG_SND_MODEL_V2_32: {
struct snd_lsm_sound_model_v2_32 snd_modelv232;
struct snd_lsm_sound_model_v2 snd_modelv2;
@ -1635,6 +1806,67 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream,
"%s: lsmevent failed %d", __func__, err);
return err;
}
case SNDRV_LSM_EVENT_STATUS_V3: {
struct snd_lsm_event_status_v3 *user = NULL;
struct snd_lsm_event_status_v3 userarg;
dev_dbg(rtd->dev,
"%s: SNDRV_LSM_EVENT_STATUS_V3\n", __func__);
if (!arg) {
dev_err(rtd->dev,
"%s: Invalid params event_status_v3\n",
__func__);
return -EINVAL;
}
if (copy_from_user(&userarg, arg, sizeof(userarg))) {
dev_err(rtd->dev,
"%s: err copyuser event_status_v3\n",
__func__);
return -EFAULT;
}
if (userarg.payload_size >
LISTEN_MAX_STATUS_PAYLOAD_SIZE) {
pr_err("%s: payload_size %d is invalid, max allowed = %d\n",
__func__, userarg.payload_size,
LISTEN_MAX_STATUS_PAYLOAD_SIZE);
return -EINVAL;
}
size = sizeof(struct snd_lsm_event_status_v3) +
userarg.payload_size;
user = kmalloc(size, GFP_KERNEL);
if (!user) {
dev_err(rtd->dev,
"%s: Allocation failed event status size %d\n",
__func__, size);
return -EFAULT;
}
user->payload_size = userarg.payload_size;
err = msm_lsm_ioctl_shared(substream, cmd, user);
/* Update size with actual payload size */
size = sizeof(*user) + user->payload_size;
if (!err && !access_ok(VERIFY_WRITE, arg, size)) {
dev_err(rtd->dev,
"%s: write verify failed size %d\n",
__func__, size);
err = -EFAULT;
}
if (!err && (copy_to_user(arg, user, size))) {
dev_err(rtd->dev,
"%s: failed to copy payload %d",
__func__, size);
err = -EFAULT;
}
kfree(user);
if (err)
dev_err(rtd->dev,
"%s: lsm_event_v3 failed %d", __func__, err);
break;
}
default:
err = msm_lsm_ioctl_shared(substream, cmd, arg);
break;
@ -1705,6 +1937,7 @@ static int msm_lsm_open(struct snd_pcm_substream *substream)
prtd->lsm_client->session_state = IDLE;
prtd->lsm_client->poll_enable = true;
prtd->lsm_client->perf_mode = 0;
prtd->lsm_client->event_mode = LSM_EVENT_NON_TIME_STAMP_MODE;
return 0;
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2016, Linux Foundation. All rights reserved.
* Copyright (c) 2013-2017, Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@ -849,6 +849,54 @@ static int q6lsm_send_param_polling_enable(struct lsm_client *client,
return rc;
}
int q6lsm_set_fwk_mode_cfg(struct lsm_client *client,
uint32_t event_mode)
{
int rc = 0;
struct lsm_cmd_set_fwk_mode_cfg cmd;
struct lsm_module_param_ids fwk_mode_cfg_ids;
struct apr_hdr *msg_hdr;
struct lsm_param_fwk_mode_cfg *fwk_mode_cfg;
u32 data_payload_size, param_size, set_param_opcode;
if (client->use_topology) {
set_param_opcode = LSM_SESSION_CMD_SET_PARAMS_V2;
fwk_mode_cfg_ids.module_id = LSM_MODULE_ID_FRAMEWORK;
fwk_mode_cfg_ids.param_id = LSM_PARAM_ID_FWK_MODE_CONFIG;
} else {
pr_debug("%s: Ignore sending event mode\n", __func__);
return rc;
}
msg_hdr = &cmd.msg_hdr;
q6lsm_add_hdr(client, msg_hdr,
sizeof(struct lsm_cmd_set_fwk_mode_cfg), true);
msg_hdr->opcode = set_param_opcode;
data_payload_size = sizeof(struct lsm_cmd_set_fwk_mode_cfg) -
sizeof(struct apr_hdr) -
sizeof(struct lsm_set_params_hdr);
q6lsm_set_param_hdr_info(&cmd.params_hdr,
data_payload_size, 0, 0, 0);
fwk_mode_cfg = &cmd.fwk_mode_cfg;
param_size = (sizeof(struct lsm_param_fwk_mode_cfg) -
sizeof(fwk_mode_cfg->common));
q6lsm_set_param_common(&fwk_mode_cfg->common,
&fwk_mode_cfg_ids, param_size,
set_param_opcode);
fwk_mode_cfg->minor_version = QLSM_PARAM_ID_MINOR_VERSION;
fwk_mode_cfg->mode = event_mode;
pr_debug("%s: mode = %d\n", __func__, fwk_mode_cfg->mode);
rc = q6lsm_apr_send_pkt(client, client->apr,
&cmd, true, NULL);
if (rc)
pr_err("%s: Failed set_params opcode 0x%x, rc %d\n",
__func__, msg_hdr->opcode, rc);
return rc;
}
int q6lsm_set_data(struct lsm_client *client,
enum lsm_detection_mode mode,
bool detectfailure)