diff options
| author | Linux Build Service Account <lnxbuild@quicinc.com> | 2017-05-10 23:03:35 -0700 |
|---|---|---|
| committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2017-05-10 23:03:35 -0700 |
| commit | 4cb291c99a103e4db4ab9d5c4f95f30427cc374d (patch) | |
| tree | 8baa89391caef0af5e3976118230c1c1c046603a | |
| parent | 3e3619c81e60e4ddb008f746ec45896c6e8a588d (diff) | |
| parent | c6911c81c2c0e06fefa57a73c2a76161b54070eb (diff) | |
Merge "ASoC: msm: qdspv2: Add mixer controls for drift query"
| -rw-r--r-- | include/sound/apr_audio-v2.h | 23 | ||||
| -rw-r--r-- | include/sound/q6asm-v2.h | 8 | ||||
| -rw-r--r-- | include/uapi/sound/compress_offload.h | 7 | ||||
| -rw-r--r-- | sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c | 41 | ||||
| -rw-r--r-- | sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c | 4 | ||||
| -rw-r--r-- | sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c | 74 | ||||
| -rw-r--r-- | sound/soc/msm/qdsp6v2/q6asm.c | 142 |
7 files changed, 297 insertions, 2 deletions
diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h index bcb53faaf678..021b81105c42 100644 --- a/include/sound/apr_audio-v2.h +++ b/include/sound/apr_audio-v2.h @@ -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 { diff --git a/include/sound/q6asm-v2.h b/include/sound/q6asm-v2.h index 41fcdeb50831..b2bb8d493839 100644 --- a/include/sound/q6asm-v2.h +++ b/include/sound/q6asm-v2.h @@ -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__ */ diff --git a/include/uapi/sound/compress_offload.h b/include/uapi/sound/compress_offload.h index 30481056cce1..866ec3d2af69 100644 --- a/include/uapi/sound/compress_offload.h +++ b/include/uapi/sound/compress_offload.h @@ -149,6 +149,8 @@ struct snd_compr_audio_info { * @SNDRV_COMPRESS_CLK_REC_MODE: clock recovery mode ( none or auto) * @SNDRV_COMPRESS_RENDER_WINDOW: render window * @SNDRV_COMPRESS_START_DELAY: start delay + * @SNDRV_COMPRESS_ENABLE_ADJUST_SESSION_CLOCK: enable dsp drift correction + * @SNDRV_COMPRESS_ADJUST_SESSION_CLOCK: set drift correction value */ enum sndrv_compress_encoder { SNDRV_COMPRESS_ENCODER_PADDING = 1, @@ -160,6 +162,8 @@ enum sndrv_compress_encoder { SNDRV_COMPRESS_CLK_REC_MODE = 7, SNDRV_COMPRESS_RENDER_WINDOW = 8, SNDRV_COMPRESS_START_DELAY = 9, + SNDRV_COMPRESS_ENABLE_ADJUST_SESSION_CLOCK = 10, + SNDRV_COMPRESS_ADJUST_SESSION_CLOCK = 11, }; #define SNDRV_COMPRESS_PATH_DELAY SNDRV_COMPRESS_PATH_DELAY @@ -167,6 +171,9 @@ enum sndrv_compress_encoder { #define SNDRV_COMPRESS_CLK_REC_MODE SNDRV_COMPRESS_CLK_REC_MODE #define SNDRV_COMPRESS_RENDER_WINDOW SNDRV_COMPRESS_RENDER_WINDOW #define SNDRV_COMPRESS_START_DELAY SNDRV_COMPRESS_START_DELAY +#define SNDRV_COMPRESS_ENABLE_ADJUST_SESSION_CLOCK \ + SNDRV_COMPRESS_ENABLE_ADJUST_SESSION_CLOCK +#define SNDRV_COMPRESS_ADJUST_SESSION_CLOCK SNDRV_COMPRESS_ADJUST_SESSION_CLOCK /** * struct snd_compr_metadata - compressed stream metadata diff --git a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c index 5bd3b6d7014e..a1cb18f53380 100644 --- a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c @@ -302,6 +302,39 @@ exit: return ret; } +static int msm_compr_enable_adjust_session_clock(struct audio_client *ac, + bool enable) +{ + int ret; + + pr_debug("%s, enable adjust_session %d\n", __func__, enable); + + ret = q6asm_send_mtmx_strtr_enable_adjust_session_clock(ac, enable); + if (ret) + pr_err("%s, adjust session clock can't be set error %d\n", + __func__, ret); + + return ret; +} + +static int msm_compr_adjust_session_clock(struct audio_client *ac, + uint32_t adjust_session_lsw, uint32_t adjust_session_msw) +{ + int ret; + + pr_debug("%s, adjust_session_time_msw 0x%x adjust_session_time_lsw 0x%x\n", + __func__, adjust_session_msw, adjust_session_lsw); + + ret = q6asm_adjust_session_clock(ac, + adjust_session_lsw, + adjust_session_msw); + if (ret) + pr_err("%s, adjust session clock can't be set error %d\n", + __func__, ret); + + return ret; +} + static int msm_compr_set_volume(struct snd_compr_stream *cstream, uint32_t volume_l, uint32_t volume_r) { @@ -2875,6 +2908,14 @@ static int msm_compr_set_metadata(struct snd_compr_stream *cstream, } else if (metadata->key == SNDRV_COMPRESS_START_DELAY) { prtd->start_delay_lsw = metadata->value[0]; prtd->start_delay_msw = metadata->value[1]; + } else if (metadata->key == + SNDRV_COMPRESS_ENABLE_ADJUST_SESSION_CLOCK) { + return msm_compr_enable_adjust_session_clock(ac, + metadata->value[0]); + } else if (metadata->key == SNDRV_COMPRESS_ADJUST_SESSION_CLOCK) { + return msm_compr_adjust_session_clock(ac, + metadata->value[0], + metadata->value[1]); } return 0; diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c index 26b7f3f26b26..45868f508a60 100644 --- a/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c @@ -174,7 +174,7 @@ static const struct snd_kcontrol_new hdmi_config_controls[] = { { .access = SNDRV_CTL_ELEM_ACCESS_READ, .iface = SNDRV_CTL_ELEM_IFACE_PCM, - .name = "HDMI RX Drift", + .name = "HDMI Drift", .info = msm_dai_q6_ext_disp_drift_info, .get = msm_dai_q6_ext_disp_drift_get, }, @@ -191,7 +191,7 @@ static const struct snd_kcontrol_new display_port_config_controls[] = { { .access = SNDRV_CTL_ELEM_ACCESS_READ, .iface = SNDRV_CTL_ELEM_IFACE_PCM, - .name = "DISPLAY Port RX Drift", + .name = "DISPLAY_PORT Drift", .info = msm_dai_q6_ext_disp_drift_info, .get = msm_dai_q6_ext_disp_drift_get, }, diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c index 201ca652c10b..9ed61288a3e4 100644 --- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c @@ -2321,6 +2321,44 @@ static const struct snd_kcontrol_new afe_enc_config_controls[] = { msm_dai_q6_afe_input_bit_format_put), }; +static int msm_dai_q6_slim_rx_drift_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = sizeof(struct afe_param_id_dev_timing_stats); + + return 0; +} + +static int msm_dai_q6_slim_rx_drift_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + struct afe_param_id_dev_timing_stats timing_stats; + struct snd_soc_dai *dai = kcontrol->private_data; + struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev); + + if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + pr_err("%s: afe port not started. dai_data->status_mask = %ld\n", + __func__, *dai_data->status_mask); + goto done; + } + + memset(&timing_stats, 0, sizeof(struct afe_param_id_dev_timing_stats)); + ret = afe_get_av_dev_drift(&timing_stats, dai->id); + if (ret) { + pr_err("%s: Error getting AFE Drift for port %d, err=%d\n", + __func__, dai->id, ret); + + goto done; + } + + memcpy(ucontrol->value.bytes.data, (void *)&timing_stats, + sizeof(struct afe_param_id_dev_timing_stats)); +done: + return ret; +} + static const char * const afe_cal_mode_text[] = { "CAL_MODE_DEFAULT", "CAL_MODE_NONE" }; @@ -2373,6 +2411,29 @@ static const struct snd_kcontrol_new usb_audio_cfg_controls[] = { msm_dai_q6_usb_audio_endian_cfg_put), }; +static const struct snd_kcontrol_new avd_drift_config_controls[] = { + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "SLIMBUS_0_RX DRIFT", + .info = msm_dai_q6_slim_rx_drift_info, + .get = msm_dai_q6_slim_rx_drift_get, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "SLIMBUS_6_RX DRIFT", + .info = msm_dai_q6_slim_rx_drift_info, + .get = msm_dai_q6_slim_rx_drift_get, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "SLIMBUS_7_RX DRIFT", + .info = msm_dai_q6_slim_rx_drift_info, + .get = msm_dai_q6_slim_rx_drift_get, + }, +}; static int msm_dai_q6_dai_probe(struct snd_soc_dai *dai) { struct msm_dai_q6_dai_data *dai_data; @@ -2422,6 +2483,9 @@ static int msm_dai_q6_dai_probe(struct snd_soc_dai *dai) rc = snd_ctl_add(dai->component->card->snd_card, snd_ctl_new1(&afe_enc_config_controls[2], dai_data)); + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&avd_drift_config_controls[2], + dai)); break; case RT_PROXY_DAI_001_RX: rc = snd_ctl_add(dai->component->card->snd_card, @@ -2449,6 +2513,16 @@ static int msm_dai_q6_dai_probe(struct snd_soc_dai *dai) snd_ctl_new1(&usb_audio_cfg_controls[3], dai_data)); break; + case SLIMBUS_0_RX: + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&avd_drift_config_controls[0], + dai)); + break; + case SLIMBUS_6_RX: + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&avd_drift_config_controls[1], + dai)); + break; } if (IS_ERR_VALUE(rc)) dev_err(dai->dev, "%s: err add config ctl, DAI = %s\n", diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c index 0cf386a3c2fc..d79074d9521b 100644 --- a/sound/soc/msm/qdsp6v2/q6asm.c +++ b/sound/soc/msm/qdsp6v2/q6asm.c @@ -2062,6 +2062,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], @@ -8039,6 +8045,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; @@ -8428,6 +8508,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 |
