diff options
| author | Linux Build Service Account <lnxbuild@localhost> | 2017-02-15 06:11:35 -0800 |
|---|---|---|
| committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2017-02-15 06:11:35 -0800 |
| commit | 7a2596d325bbbecf6bf9f85021d316e64389e30c (patch) | |
| tree | 668a7f43663314fe8f9521dfa542e75f0d797b26 | |
| parent | 6078b8e5891055d908b5a8dac1c7d6eeca210b9a (diff) | |
| parent | bb52835a61710c10edb686728abd15af4f7ceb57 (diff) | |
Merge "ASoC: msm: qdspv2: Add mixer controls for drift query"
| -rw-r--r-- | include/sound/apr_audio-v2.h | 102 | ||||
| -rw-r--r-- | include/sound/q6afe-v2.h | 2 | ||||
| -rw-r--r-- | sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c | 63 | ||||
| -rw-r--r-- | sound/soc/msm/qdsp6v2/q6afe.c | 134 |
4 files changed, 294 insertions, 7 deletions
diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h index 3d3a2022bc04..ff0ee57daf78 100644 --- a/include/sound/apr_audio-v2.h +++ b/include/sound/apr_audio-v2.h @@ -9992,6 +9992,108 @@ struct afe_port_group_create { union afe_port_group_config data; } __packed; +/* ID of the parameter used by #AFE_MODULE_AUDIO_DEV_INTERFACE to specify + * the timing statistics of the corresponding device interface. + * Client can periodically query for the device time statistics to help adjust + * the PLL based on the drift value. The get param command must be sent to + * AFE port ID corresponding to device interface + + * This parameter ID supports following get param commands: + * #AFE_PORT_CMD_GET_PARAM_V2 and + * #AFE_PORT_CMD_GET_PARAM_V3. + */ +#define AFE_PARAM_ID_DEV_TIMING_STATS 0x000102AD + +/* Version information used to handle future additions to AFE device + * interface timing statistics (for backward compatibility). + */ +#define AFE_API_VERSION_DEV_TIMING_STATS 0x1 + +/* Enumeration for specifying a sink(Rx) device */ +#define AFE_SINK_DEVICE 0x0 + +/* Enumeration for specifying a source(Tx) device */ +#define AFE_SOURCE_DEVICE 0x1 + +/* Enumeration for specifying the drift reference is of type AV Timer */ +#define AFE_REF_TIMER_TYPE_AVTIMER 0x0 + +/* Message payload structure for the + * AFE_PARAM_ID_DEV_TIMING_STATS parameter. + */ +struct afe_param_id_dev_timing_stats { + /* Minor version used to track the version of device interface timing + * statistics. Currently, the supported version is 1. + * @values #AFE_API_VERSION_DEV_TIMING_STATS + */ + u32 minor_version; + + /* Indicates the device interface direction as either + * source (Tx) or sink (Rx). + * @values + * #AFE_SINK_DEVICE + * #AFE_SOURCE_DEVICE + */ + u16 device_direction; + + /* Reference timer for drift accumulation and time stamp information. + * @values + * #AFE_REF_TIMER_TYPE_AVTIMER @tablebulletend + */ + u16 reference_timer; + + /* + * Flag to indicate if resync is required on the client side for + * drift correction. Flag is set to TRUE for the first get_param + * response after device interface starts. This flag value can be + * used by client to identify if device interface restart has + * happened and if any re-sync is required at their end for drift + * correction. + * @values + * 0: FALSE (Resync not required) + * 1: TRUE (Resync required) @tablebulletend + */ + u32 resync_flag; + + /* Accumulated drift value in microseconds. This value is updated + * every 100th ms. + * Positive drift value indicates AV timer is running faster than device + * Negative drift value indicates AV timer is running slower than device + * @values Any valid int32 number + */ + s32 acc_drift_value; + + /* Lower 32 bits of the 64-bit absolute timestamp of reference + * timer in microseconds. + + * This timestamp corresponds to the time when the drift values + * are accumlated for every 100th ms. + * @values Any valid uint32 number + */ + u32 ref_timer_abs_ts_lsw; + + /* Upper 32 bits of the 64-bit absolute timestamp of reference + * timer in microseconds. + * This timestamp corresponds to the time when the drift values + * are accumlated for every 100th ms. + * @values Any valid uint32 number + */ + u32 ref_timer_abs_ts_msw; +} __packed; + +struct afe_av_dev_drift_get_param { + struct apr_hdr hdr; + struct afe_port_cmd_get_param_v2 get_param; + struct afe_port_param_data_v2 pdata; + struct afe_param_id_dev_timing_stats timing_stats; +} __packed; + +struct afe_av_dev_drift_get_param_resp { + uint32_t status; + struct afe_port_param_data_v2 pdata; + struct afe_param_id_dev_timing_stats timing_stats; +} __packed; + /* Command for Matrix or Stream Router */ #define ASM_SESSION_CMD_SET_MTMX_STRTR_PARAMS_V2 0x00010DCE /* Module for AVSYNC */ diff --git a/include/sound/q6afe-v2.h b/include/sound/q6afe-v2.h index e4033e712804..02d772514222 100644 --- a/include/sound/q6afe-v2.h +++ b/include/sound/q6afe-v2.h @@ -366,4 +366,6 @@ int afe_send_custom_tdm_header_cfg( int afe_tdm_port_start(u16 port_id, struct afe_tdm_port_config *tdm_port, u32 rate); void afe_set_routing_callback(routing_cb); +int afe_get_av_dev_drift(struct afe_param_id_dev_timing_stats *timing_stats, + u16 port); #endif /* __Q6AFE_V2_H__ */ 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 69a9e14c47de..46e2f7109b5a 100644 --- a/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The 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 @@ -124,6 +124,45 @@ static const struct soc_enum hdmi_config_enum[] = { SOC_ENUM_SINGLE_EXT(2, hdmi_format), }; +static int msm_dai_q6_ext_disp_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_ext_disp_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_hdmi_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. 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); + + ret = -EINVAL; + goto done; + } + + memcpy(ucontrol->value.bytes.data, (void *)&timing_stats, + sizeof(struct afe_param_id_dev_timing_stats)); +done: + return ret; +} + static const struct snd_kcontrol_new hdmi_config_controls[] = { SOC_ENUM_EXT("HDMI RX Format", hdmi_config_enum[0], msm_dai_q6_ext_disp_format_get, @@ -132,6 +171,13 @@ static const struct snd_kcontrol_new hdmi_config_controls[] = { HDMI_RX_CA_MAX, 0, 1, msm_dai_q6_ext_disp_ca_get, msm_dai_q6_ext_disp_ca_put), + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "HDMI RX Drift", + .info = msm_dai_q6_ext_disp_drift_info, + .get = msm_dai_q6_ext_disp_drift_get, + }, }; static const struct snd_kcontrol_new display_port_config_controls[] = { @@ -142,6 +188,13 @@ static const struct snd_kcontrol_new display_port_config_controls[] = { HDMI_RX_CA_MAX, 0, 1, msm_dai_q6_ext_disp_ca_get, msm_dai_q6_ext_disp_ca_put), + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "DISPLAY Port RX Drift", + .info = msm_dai_q6_ext_disp_drift_info, + .get = msm_dai_q6_ext_disp_drift_get, + }, }; /* Current implementation assumes hw_param is called once @@ -299,6 +352,10 @@ static int msm_dai_q6_hdmi_dai_probe(struct snd_soc_dai *dai) kcontrol = &hdmi_config_controls[1]; rc = snd_ctl_add(dai->component->card->snd_card, snd_ctl_new1(kcontrol, dai_data)); + + kcontrol = &hdmi_config_controls[2]; + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(kcontrol, dai)); } else if (dai->driver->id == DISPLAY_PORT_RX) { kcontrol = &display_port_config_controls[0]; rc = snd_ctl_add(dai->component->card->snd_card, @@ -307,6 +364,10 @@ static int msm_dai_q6_hdmi_dai_probe(struct snd_soc_dai *dai) kcontrol = &display_port_config_controls[1]; rc = snd_ctl_add(dai->component->card->snd_card, snd_ctl_new1(kcontrol, dai_data)); + + kcontrol = &display_port_config_controls[2]; + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(kcontrol, dai)); } else { dev_err(dai->dev, "%s: Invalid id:%d\n", __func__, dai->driver->id); diff --git a/sound/soc/msm/qdsp6v2/q6afe.c b/sound/soc/msm/qdsp6v2/q6afe.c index be5437a186ea..c52d9425c315 100644 --- a/sound/soc/msm/qdsp6v2/q6afe.c +++ b/sound/soc/msm/qdsp6v2/q6afe.c @@ -113,6 +113,7 @@ struct afe_ctl { struct audio_cal_info_sp_ex_vi_ftm_cfg ex_ftm_cfg; struct afe_sp_th_vi_get_param_resp th_vi_resp; struct afe_sp_ex_vi_get_param_resp ex_vi_resp; + struct afe_av_dev_drift_get_param_resp av_dev_drift_resp; int vi_tx_port; int vi_rx_port; uint32_t afe_sample_rates[AFE_MAX_PORTS]; @@ -189,6 +190,38 @@ static void afe_callback_debug_print(struct apr_client_data *data) __func__, data->opcode, data->payload_size); } +static void av_dev_drift_afe_cb_handler(uint32_t *payload, + uint32_t payload_size) +{ + u32 param_id; + struct afe_av_dev_drift_get_param_resp *resp = + (struct afe_av_dev_drift_get_param_resp *) payload; + + if (!(&(resp->pdata))) { + pr_err("%s: Error: resp pdata is NULL\n", __func__); + return; + } + + param_id = resp->pdata.param_id; + if (param_id == AFE_PARAM_ID_DEV_TIMING_STATS) { + if (payload_size < sizeof(this_afe.av_dev_drift_resp)) { + pr_err("%s: Error: received size %d, resp size %zu\n", + __func__, payload_size, + sizeof(this_afe.av_dev_drift_resp)); + return; + } + memcpy(&this_afe.av_dev_drift_resp, payload, + sizeof(this_afe.av_dev_drift_resp)); + if (!this_afe.av_dev_drift_resp.status) { + atomic_set(&this_afe.state, 0); + } else { + pr_debug("%s: av_dev_drift_resp status: %d", __func__, + this_afe.av_dev_drift_resp.status); + atomic_set(&this_afe.state, -1); + } + } +} + static int32_t sp_make_afe_callback(uint32_t *payload, uint32_t payload_size) { u32 param_id; @@ -309,10 +342,7 @@ static int32_t afe_callback(struct apr_client_data *data, void *priv) } afe_callback_debug_print(data); if (data->opcode == AFE_PORT_CMDRSP_GET_PARAM_V2) { - u8 *payload = data->payload; - - if (rtac_make_afe_callback(data->payload, data->payload_size)) - return 0; + uint32_t *payload = data->payload; if (!payload || (data->token >= AFE_MAX_PORTS)) { pr_err("%s: Error: size %d payload %pK token %d\n", @@ -320,9 +350,19 @@ static int32_t afe_callback(struct apr_client_data *data, void *priv) payload, data->token); return -EINVAL; } - if (sp_make_afe_callback(data->payload, data->payload_size)) - return -EINVAL; + if (payload[2] == AFE_PARAM_ID_DEV_TIMING_STATS) { + av_dev_drift_afe_cb_handler(data->payload, + data->payload_size); + } else { + if (rtac_make_afe_callback(data->payload, + data->payload_size)) + return 0; + + if (sp_make_afe_callback(data->payload, + data->payload_size)) + return -EINVAL; + } wake_up(&this_afe.wait[data->token]); } else if (data->payload_size) { uint32_t *payload; @@ -6163,6 +6203,88 @@ done: return ret; } +int afe_get_av_dev_drift(struct afe_param_id_dev_timing_stats *timing_stats, + u16 port) +{ + int ret = -EINVAL; + int index = 0; + struct afe_av_dev_drift_get_param av_dev_drift; + + if (!timing_stats) { + pr_err("%s: Invalid params\n", __func__); + goto exit; + } + + ret = q6audio_validate_port(port); + if (ret < 0) { + pr_err("%s: invalid port 0x%x ret %d\n", __func__, port, ret); + ret = -EINVAL; + goto exit; + } + + index = q6audio_get_port_index(port); + if (index < 0 || index >= AFE_MAX_PORTS) { + pr_err("%s: Invalid AFE port index[%d]\n", + __func__, index); + ret = -EINVAL; + goto exit; + } + + memset(&av_dev_drift, 0, sizeof(struct afe_av_dev_drift_get_param)); + + av_dev_drift.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + av_dev_drift.hdr.pkt_size = sizeof(av_dev_drift); + av_dev_drift.hdr.src_port = 0; + av_dev_drift.hdr.dest_port = 0; + av_dev_drift.hdr.token = index; + av_dev_drift.hdr.opcode = AFE_PORT_CMD_GET_PARAM_V2; + av_dev_drift.get_param.mem_map_handle = 0; + av_dev_drift.get_param.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + av_dev_drift.get_param.param_id = AFE_PARAM_ID_DEV_TIMING_STATS; + av_dev_drift.get_param.payload_address_lsw = 0; + av_dev_drift.get_param.payload_address_msw = 0; + av_dev_drift.get_param.payload_size = sizeof(av_dev_drift) + - sizeof(av_dev_drift.get_param) - sizeof(av_dev_drift.hdr); + av_dev_drift.get_param.port_id = q6audio_get_port_id(port); + av_dev_drift.pdata.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + av_dev_drift.pdata.param_id = AFE_PARAM_ID_DEV_TIMING_STATS; + av_dev_drift.pdata.param_size = sizeof(av_dev_drift.timing_stats); + atomic_set(&this_afe.status, 0); + atomic_set(&this_afe.state, 1); + ret = apr_send_pkt(this_afe.apr, (uint32_t *)&av_dev_drift); + if (ret < 0) { + pr_err("%s: get param port 0x%x param id[0x%x] failed %d\n", + __func__, port, av_dev_drift.get_param.param_id, ret); + goto exit; + } + + ret = wait_event_timeout(this_afe.wait[index], + (atomic_read(&this_afe.state) == 0), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout\n", __func__); + ret = -EINVAL; + goto exit; + } + + if (atomic_read(&this_afe.status) > 0) { + pr_err("%s: config cmd failed [%s]\n", + __func__, adsp_err_get_err_str( + atomic_read(&this_afe.status))); + ret = adsp_err_get_lnx_err_code( + atomic_read(&this_afe.status)); + goto exit; + } + + memcpy(timing_stats, &this_afe.av_dev_drift_resp.timing_stats, + sizeof(this_afe.av_dev_drift_resp.timing_stats)); + ret = 0; +exit: + return ret; +} + int afe_spk_prot_get_calib_data(struct afe_spkr_prot_get_vi_calib *calib_resp) { int ret = -EINVAL; |
