ASoC: msm: qdsp6v2: Add support for DSP clock recovery

ASM driver changes to apply DSP clock recovery.

Clients can issue clock recovery command with a positive value
to advance the clock or a negative value to delay the clock.

CRs-Fixed: 2036899
Change-Id: If2f10368afafe203ebdea219edcc2227cd381801
Signed-off-by: Manish Dewangan <manish@codeaurora.org>
This commit is contained in:
Manish Dewangan 2017-04-17 14:59:23 +05:30 committed by Gerrit - the friendly Code Review server
parent c01ba1a5f9
commit 761f20e31b
3 changed files with 173 additions and 0 deletions

View file

@ -10320,10 +10320,33 @@ struct asm_session_mtmx_strtr_param_clk_rec_t {
u32 flags;
} __packed;
/* Parameter used by #ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC to
* realize smoother adjustment of audio session clock for a specified session.
* The desired audio session clock adjustment(in micro seconds) is specified
* using the command #ASM_SESSION_CMD_ADJUST_SESSION_CLOCK_V2.
* Delaying/Advancing the session clock would be implemented by inserting
* interpolated/dropping audio samples in the playback path respectively.
* Also, this parameter has to be configured before the Audio Session is put
* to RUN state to avoid cold start latency/glitches in the playback.
*/
#define ASM_SESSION_MTMX_PARAM_ADJUST_SESSION_TIME_CTL 0x00013217
struct asm_session_mtmx_param_adjust_session_time_ctl_t {
/* Specifies whether the module is enabled or not
* @values
* 0 -- disabled
* 1 -- enabled
*/
u32 enable;
};
union asm_session_mtmx_strtr_param_config {
struct asm_session_mtmx_strtr_param_window_v2_t window_param;
struct asm_session_mtmx_strtr_param_render_mode_t render_param;
struct asm_session_mtmx_strtr_param_clk_rec_t clk_rec_param;
struct asm_session_mtmx_param_adjust_session_time_ctl_t adj_time_param;
} __packed;
struct asm_mtmx_strtr_params {

View file

@ -655,6 +655,10 @@ int q6asm_send_mtmx_strtr_render_mode(struct audio_client *ac,
int q6asm_send_mtmx_strtr_clk_rec_mode(struct audio_client *ac,
uint32_t clk_rec_mode);
/* Enable adjust session clock in DSP */
int q6asm_send_mtmx_strtr_enable_adjust_session_clock(struct audio_client *ac,
bool enable);
/* Retrieve the current DSP path delay */
int q6asm_get_path_delay(struct audio_client *ac);
@ -662,4 +666,8 @@ int q6asm_get_path_delay(struct audio_client *ac);
uint8_t q6asm_get_buf_index_from_token(uint32_t token);
uint8_t q6asm_get_stream_id_from_token(uint32_t token);
/* Adjust session clock in DSP */
int q6asm_adjust_session_clock(struct audio_client *ac,
uint32_t adjust_time_lsw,
uint32_t adjust_time_msw);
#endif /* __Q6_ASM_H__ */

View file

@ -2059,6 +2059,12 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
(void *)pp_event_package, ac->priv);
kfree(pp_event_package);
return 0;
case ASM_SESSION_CMDRSP_ADJUST_SESSION_CLOCK_V2:
pr_debug("%s: ASM_SESSION_CMDRSP_ADJUST_SESSION_CLOCK_V2 sesion %d status 0x%x msw %u lsw %u\n",
__func__, ac->session, payload[0], payload[2],
payload[1]);
wake_up(&ac->cmd_wait);
break;
case ASM_SESSION_CMDRSP_GET_PATH_DELAY_V2:
pr_debug("%s: ASM_SESSION_CMDRSP_GET_PATH_DELAY_V2 session %d status 0x%x msw %u lsw %u\n",
__func__, ac->session, payload[0], payload[2],
@ -8277,6 +8283,80 @@ exit:
return rc;
}
int q6asm_send_mtmx_strtr_enable_adjust_session_clock(struct audio_client *ac,
bool enable)
{
struct asm_mtmx_strtr_params matrix;
struct asm_session_mtmx_param_adjust_session_time_ctl_t adjust_time;
int sz = 0;
int rc = 0;
pr_debug("%s: adjust session enable %d\n", __func__, enable);
if (!ac) {
pr_err("%s: audio client handle is NULL\n", __func__);
rc = -EINVAL;
goto exit;
}
if (ac->apr == NULL) {
pr_err("%s: ac->apr is NULL\n", __func__);
rc = -EINVAL;
goto exit;
}
adjust_time.enable = enable;
memset(&matrix, 0, sizeof(struct asm_mtmx_strtr_params));
sz = sizeof(struct asm_mtmx_strtr_params);
q6asm_add_hdr(ac, &matrix.hdr, sz, TRUE);
atomic_set(&ac->cmd_state, -1);
matrix.hdr.opcode = ASM_SESSION_CMD_SET_MTMX_STRTR_PARAMS_V2;
matrix.param.data_payload_addr_lsw = 0;
matrix.param.data_payload_addr_msw = 0;
matrix.param.mem_map_handle = 0;
matrix.param.data_payload_size =
sizeof(struct asm_stream_param_data_v2) +
sizeof(struct asm_session_mtmx_param_adjust_session_time_ctl_t);
matrix.param.direction = 0; /* RX */
matrix.data.module_id = ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC;
matrix.data.param_id = ASM_SESSION_MTMX_PARAM_ADJUST_SESSION_TIME_CTL;
matrix.data.param_size =
sizeof(struct asm_session_mtmx_param_adjust_session_time_ctl_t);
matrix.data.reserved = 0;
matrix.config.adj_time_param.enable = adjust_time.enable;
rc = apr_send_pkt(ac->apr, (uint32_t *) &matrix);
if (rc < 0) {
pr_err("%s: enable adjust session failed failed paramid [0x%x]\n",
__func__, matrix.data.param_id);
rc = -EINVAL;
goto exit;
}
rc = wait_event_timeout(ac->cmd_wait,
(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
if (!rc) {
pr_err("%s: enable adjust session failed failed paramid [0x%x]\n",
__func__, matrix.data.param_id);
rc = -ETIMEDOUT;
goto exit;
}
if (atomic_read(&ac->cmd_state) > 0) {
pr_err("%s: DSP returned error[%s]\n",
__func__, adsp_err_get_err_str(
atomic_read(&ac->cmd_state)));
rc = adsp_err_get_lnx_err_code(
atomic_read(&ac->cmd_state));
goto exit;
}
rc = 0;
exit:
return rc;
}
static int __q6asm_cmd(struct audio_client *ac, int cmd, uint32_t stream_id)
{
struct apr_hdr hdr;
@ -8666,6 +8746,68 @@ fail_cmd:
return -EINVAL;
}
int q6asm_adjust_session_clock(struct audio_client *ac,
uint32_t adjust_time_lsw,
uint32_t adjust_time_msw)
{
int rc = 0;
int sz = 0;
struct asm_session_cmd_adjust_session_clock_v2 adjust_clock;
pr_debug("%s: adjust_time_lsw is %x, adjust_time_msw is %x\n", __func__,
adjust_time_lsw, adjust_time_msw);
if (!ac) {
pr_err("%s: audio client handle is NULL\n", __func__);
rc = -EINVAL;
goto fail_cmd;
}
if (ac->apr == NULL) {
pr_err("%s: ac->apr is NULL", __func__);
rc = -EINVAL;
goto fail_cmd;
}
sz = sizeof(struct asm_session_cmd_adjust_session_clock_v2);
q6asm_add_hdr(ac, &adjust_clock.hdr, sz, TRUE);
atomic_set(&ac->cmd_state, -1);
adjust_clock.hdr.opcode = ASM_SESSION_CMD_ADJUST_SESSION_CLOCK_V2;
adjust_clock.adjustime_lsw = adjust_time_lsw;
adjust_clock.adjustime_msw = adjust_time_msw;
rc = apr_send_pkt(ac->apr, (uint32_t *) &adjust_clock);
if (rc < 0) {
pr_err("%s: adjust_clock send failed paramid [0x%x]\n",
__func__, adjust_clock.hdr.opcode);
rc = -EINVAL;
goto fail_cmd;
}
rc = wait_event_timeout(ac->cmd_wait,
(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
if (!rc) {
pr_err("%s: timeout, adjust_clock paramid[0x%x]\n",
__func__, adjust_clock.hdr.opcode);
rc = -ETIMEDOUT;
goto fail_cmd;
}
if (atomic_read(&ac->cmd_state) > 0) {
pr_err("%s: DSP returned error[%s]\n",
__func__, adsp_err_get_err_str(
atomic_read(&ac->cmd_state)));
rc = adsp_err_get_lnx_err_code(
atomic_read(&ac->cmd_state));
goto fail_cmd;
}
rc = 0;
fail_cmd:
return rc;
}
/*
* q6asm_get_path_delay() - get the path delay for an audio session
* @ac: audio client handle