From: Derek Chen Date: Thu, 5 Oct 2017 22:48:45 +0000 (-0400) Subject: ASoC: msm: qdsp6v2: enable DMA channel control X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=3404b6eb56712a1515e94ee0114944b7e2c48a2a;p=sagit-ice-cold%2Fkernel_xiaomi_msm8998.git ASoC: msm: qdsp6v2: enable DMA channel control Enable AFE driver to request and release LPASS DMA channel indices. CRs-fixed: 2126719 Signed-off-by: Derek Chen Change-Id: Ic40bbf9aa7170d2c91cef45ddca7a86ccd27c4b0 --- diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h index 0393c8869b8f..ee65bdae9971 100644 --- a/include/sound/apr_audio-v2.h +++ b/include/sound/apr_audio-v2.h @@ -3512,6 +3512,263 @@ struct afe_param_id_set_topology_cfg { u32 topology_id; } __packed; +/* + * This command is used by client to request the LPASS resources. + * Currently this command supports only LPAIF DMA resources. + * Allocated resources will be in control of remote client until + * they get released. + * + * If all the requested resources are available then response status in + * AFE_CMDRSP_REQUEST_LPASS_RESOURCES payload will + * be updated with ADSP_EOK, otherwise it will be ADSP_EFAILED. + * + * This command is variable payload size command, and size depends + * on the type of resource requested. + * + * For example, if client requests AFE_LPAIF_DMA_RESOURCE_ID + * resources, afe_cmd_request_lpass_resources structure will + * be followed with the afe_cmd_request_lpass_dma_resources + * structure. + * + * AFE_CMDRSP_REQUEST_LPASS_RESOURCES is the response for + * this command, which returns the allocated resources. + * + * @apr_hdr_fields + * Opcode -- AFE_CMD_REQUEST_LPASS_RESOURCES + * + * @return + * #AFE_CMDRSP_REQUEST_LPASS_RESOURCES + */ +#define AFE_CMD_REQUEST_LPASS_RESOURCES 0x00010109 + +/* Macro for requesting LPAIF DMA resources */ +#define AFE_LPAIF_DMA_RESOURCE_ID 0x00000001 + +struct afe_cmd_request_lpass_resources { + /* + * LPASS Resource ID + * @values: + * - AFE_LPAIF_DMA_RESOURCE_ID + */ + u32 resource_id; +} __packed; + +/* + * AFE_CMD_REQUEST_LPASS_RESOURCES uses this structure when + * client is requesting LPAIF DMA resources. + * + * Number of read DMA channels and write DMA channels varies from chipset to + * chipset. HLOS needs to make sure that when it requests LPASS DMA + * resources, it should not impact the concurrencies which + * are mandatory for a given chipset. + */ + +/* Macro for AFE LPAIF default DMA data type */ +#define AFE_LPAIF_DEFAULT_DMA_TYPE 0x0 + +struct afe_cmd_request_lpass_dma_resources { + /* + * LPASS DMA Type + * @values: + * - AFE_LPAIF_DEFAULT_DMA_TYPE + */ + u8 dma_type; + /* + * Number of read DMA channels required + * @values: >=0 + * - 0 indicates channels are not requested + */ + u8 num_read_dma_channels; + /* + * Number of write DMA channels required + * @values: >=0 + * - 0 indicates channels are not requested + */ + u8 num_write_dma_channels; + /* + * Reserved field for 4 byte alignment + * @values: 0 + */ + u8 reserved; +} __packed; + +struct afe_request_lpass_dma_resources_command { + struct apr_hdr hdr; + struct afe_cmd_request_lpass_resources resources; + struct afe_cmd_request_lpass_dma_resources dma_resources; +} __packed; + +/* + * This is the response for the command AFE_CMD_REQUEST_LPASS_RESOURCES. + * Payload of this command is variable. + * + * Resources allocated successfully or not, are determined by the "status" + * in the payload. If status is ADSP_EOK, then resources are + * allocated successfully and allocated resource information + * follows. + * + * For example, if the response resource id is AFE_LPAIF_DMA_RESOURCE_ID, + * afe_cmdrsp_request_lpass_dma_resources structure will + * follow after afe_cmdrsp_request_lpass_resources. + * + * If status is ADSP_EFAILED, this indicates requested resources + * are not allocated successfully. In this case the payload following + * this structure is invalid. + * @apr_hdr_fields + * Opcode -- AFE_CMDRSP_REQUEST_LPASS_RESOURCES +*/ +#define AFE_CMDRSP_REQUEST_LPASS_RESOURCES 0x0001010A + +struct afe_cmdrsp_request_lpass_resources { + /* + * ADSP_EOK if all requested resources are allocated. + * ADSP_EFAILED if resource allocation is failed. + */ + u32 status; + /* + * Returned LPASS DMA resource ID + * @values: + * - AFE_LPAIF_DMA_RESOURCE_ID + */ + u32 resource_id; +} __packed; + +/* + * This command will be sent as a payload for + * AFE_CMDRSP_REQUEST_LPASS_RESOURCES, when the LPAIF DMA resources + * were requested. Payload of this command is variable, which + * follows after the afe_cmdrsp_request_lpass_dma_resources structure. + * The size in bytes following this structure is sum of + * num_read_dma_channels and num_write_dma_channels. + * + * If the resource allocation is successful, then the payload contains + * the valid DMA channel indices. + * + * For example, if number of requested DMA read channels is 2, and they + * are successfully allocated, the variable payload contains + * valid DMA channel index values in first two bytes array. + * + * In the failure case this payload can be ignored, and all the values will be + * initialized with zeros. + * + * An example payload of the command response is below: + * + * + * read DMA index value for each byte. + * write DMA index value for each byte. + * padded zeros, if sum of num_read_dma_channels and num_write_dma_channels + * are not multiples of 4. +*/ + +struct afe_cmdrsp_request_lpass_dma_resources { + /* + * LPASS DMA Type + * @values: + * - AFE_LPAIF_DEFAULT_DMA_TYPE + */ + u8 dma_type; + /* + * Returned number of read DMA channels allocated + * @values: >=0 + */ + u8 num_read_dma_channels; + /* + * Returned number of write DMA channels allocated + * @values: >=0 + */ + u8 num_write_dma_channels; + /* + * Reserved field for 4 byte alignment + * @values: 0 + */ + u8 reserved; +} __packed; + +/* + * This command is for releasing resources which are allocated as + * part of AFE_CMD_REQUEST_LPASS_RESOURCES. + * + * Payload of this command is variable, which follows + * after the afe_cmd_release_lpass_resources structure. + * + * If release resource is AFE_LPAIF_DMA_RESOURCE_ID + * afe_cmd_release_lpass_dma_resources structure will be + * followed after afe_cmd_release_lpass_resources. + * + * + * @apr_hdr_fields + * Opcode -- AFE_CMD_RELEASE_LPASS_RESOURCES + + * @return + * #APRv2 IBASIC RSP Result +*/ +#define AFE_CMD_RELEASE_LPASS_RESOURCES 0x0001010B + +struct afe_cmd_release_lpass_resources { + /* + * LPASS DMA resource ID + * @values: + * - AFE_LPAIF_DMA_RESOURCE_ID + */ + u32 resource_id; +} __packed; + +/* + * This payload to be appended as part of AFE_CMD_RELEASE_LPASS_RESOURCES + * when resource id AFE_LPAIF_DMA_RESOURCE_ID is used. + * + * Payload of this command is variable, which will be followed after the + * afe_cmd_release_lpass_dma_resources structure. + * The variable payload's size in bytes is sum of + * num_read_dma_channels and num_write_dma_channels. + * Variable payload data contains the valid DMA channel indices which are + * allocated as part of AFE_CMD_REQUEST_LPASS_RESOURCES. + * + * For example, if number of DMA read channels released are 2, + * the variable payload contains valid DMA channel + * index values in first two bytes of variable payload. + * Client needs to fill the same DMA channel indices were returned + * as part of AFE_CMD_RELEASE_LPASS_RESOURCES, otherwise + * ADSP will return the error. + * + * An example payload of the release command is below: + * + * + * read DMA index value for each byte. + * write DMA index value for each byte. +*/ + +struct afe_cmd_release_lpass_dma_resources { + /* + * LPASS DMA Type + * @values: + * - AFE_LPAIF_DEFAULT_DMA_TYPE + */ + u8 dma_type; + /* + * Number of read DMA channels to be released + * @values: >=0 + * - 0 indicates channels are not released + */ + u8 num_read_dma_channels; + /* + * Number of write DMA channels to be released + * @values: >=0 + * - 0 indicates channels are not released + */ + u8 num_write_dma_channels; + /* + * Reserved field for 4 byte alignment + * @values: 0 + */ + u8 reserved; +} __packed; + +struct afe_release_lpass_dma_resources_command { + struct apr_hdr hdr; + struct afe_cmd_release_lpass_resources resources; + struct afe_cmd_release_lpass_dma_resources dma_resources; +} __packed; /* * Generic encoder module ID. diff --git a/include/sound/q6afe-v2.h b/include/sound/q6afe-v2.h index 5031e62beb17..9a7eb29b11a6 100644 --- a/include/sound/q6afe-v2.h +++ b/include/sound/q6afe-v2.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 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 @@ -42,6 +42,9 @@ #define AFE_CLK_VERSION_V1 1 #define AFE_CLK_VERSION_V2 2 +#define AFE_MAX_RDDMA 10 +#define AFE_MAX_WRDMA 10 + typedef int (*routing_cb)(int port); enum { @@ -369,4 +372,9 @@ void afe_set_routing_callback(routing_cb); int afe_get_av_dev_drift(struct afe_param_id_dev_timing_stats *timing_stats, u16 port); int afe_get_svc_version(uint32_t service_id); +int afe_request_dma_resources(uint8_t dma_type, uint8_t num_read_dma_channels, + uint8_t num_write_dma_channels); +int afe_get_dma_idx(bool **ret_rddma_idx, + bool **ret_wrdma_idx); +int afe_release_all_dma_resources(void); #endif /* __Q6AFE_V2_H__ */ diff --git a/sound/soc/msm/qdsp6v2/q6afe.c b/sound/soc/msm/qdsp6v2/q6afe.c index 352ea9257832..a52dcb6d6219 100644 --- a/sound/soc/msm/qdsp6v2/q6afe.c +++ b/sound/soc/msm/qdsp6v2/q6afe.c @@ -124,6 +124,10 @@ struct afe_ctl { int set_custom_topology; int dev_acdb_id[AFE_MAX_PORTS]; routing_cb rt_cb; + int num_alloced_rddma; + bool alloced_rddma[AFE_MAX_RDDMA]; + int num_alloced_wrdma; + bool alloced_wrdma[AFE_MAX_WRDMA]; }; static atomic_t afe_ports_mad_type[SLIMBUS_PORT_LAST - SLIMBUS_0_RX]; @@ -349,6 +353,99 @@ static int32_t sp_make_afe_callback(uint32_t opcode, uint32_t *payload, return 0; } +static int32_t afe_lpass_resources_callback(struct apr_client_data *data) +{ + uint8_t *payload = data->payload; + struct afe_cmdrsp_request_lpass_resources *resources = + (struct afe_cmdrsp_request_lpass_resources *) payload; + struct afe_cmdrsp_request_lpass_dma_resources *dma_resources = NULL; + uint8_t *dma_channels_id_payload = NULL; + + if (!payload || (data->token >= AFE_MAX_PORTS)) { + pr_err("%s: Error: size %d payload %pK token %d\n", + __func__, data->payload_size, + payload, data->token); + atomic_set(&this_afe.status, ADSP_EBADPARAM); + return -EINVAL; + } + + if (resources->status != 0) { + pr_debug("%s: Error: Requesting LPASS resources ret %d\n", + __func__, resources->status); + atomic_set(&this_afe.status, ADSP_EBADPARAM); + return -EINVAL; + } + + if (resources->resource_id == AFE_LPAIF_DMA_RESOURCE_ID) { + int i; + + payload += sizeof( + struct afe_cmdrsp_request_lpass_resources); + dma_resources = (struct + afe_cmdrsp_request_lpass_dma_resources *) + payload; + + pr_debug("%s: DMA Type allocated = %d\n", + __func__, + dma_resources->dma_type); + + if (dma_resources->num_read_dma_channels > AFE_MAX_RDDMA) { + pr_err("%s: Allocated Read DMA %d exceeds max %d\n", + __func__, + dma_resources->num_read_dma_channels, + AFE_MAX_RDDMA); + dma_resources->num_read_dma_channels = AFE_MAX_RDDMA; + } + + if (dma_resources->num_write_dma_channels > AFE_MAX_WRDMA) { + pr_err("%s: Allocated Write DMA %d exceeds max %d\n", + __func__, + dma_resources->num_write_dma_channels, + AFE_MAX_WRDMA); + dma_resources->num_write_dma_channels = AFE_MAX_WRDMA; + } + + this_afe.num_alloced_rddma = + dma_resources->num_read_dma_channels; + this_afe.num_alloced_wrdma = + dma_resources->num_write_dma_channels; + + pr_debug("%s: Number of allocated Read DMA channels= %d\n", + __func__, + dma_resources->num_read_dma_channels); + pr_debug("%s: Number of allocated Write DMA channels= %d\n", + __func__, + dma_resources->num_write_dma_channels); + + payload += sizeof( + struct afe_cmdrsp_request_lpass_dma_resources); + dma_channels_id_payload = payload; + + for (i = 0; i < this_afe.num_alloced_rddma; i++) { + pr_debug("%s: Read DMA Index %d allocated\n", + __func__, *dma_channels_id_payload); + this_afe.alloced_rddma + [*dma_channels_id_payload] = 1; + dma_channels_id_payload++; + } + + for (i = 0; i < this_afe.num_alloced_wrdma; i++) { + pr_debug("%s: Write DMA Index %d allocated\n", + __func__, *dma_channels_id_payload); + this_afe.alloced_wrdma + [*dma_channels_id_payload] = 1; + dma_channels_id_payload++; + } + } else { + pr_err("%s: Error: Unknown resource ID %d", + __func__, resources->resource_id); + atomic_set(&this_afe.status, ADSP_EBADPARAM); + return -EINVAL; + } + + return 0; +} + static int32_t afe_callback(struct apr_client_data *data, void *priv) { if (!data) { @@ -429,6 +526,15 @@ static int32_t afe_callback(struct apr_client_data *data, void *priv) return -EINVAL; } wake_up(&this_afe.wait[data->token]); + } else if (data->opcode == AFE_CMDRSP_REQUEST_LPASS_RESOURCES) { + uint32_t ret = 0; + + ret = afe_lpass_resources_callback(data); + atomic_set(&this_afe.state, 0); + wake_up(&this_afe.wait[data->token]); + if (!ret) { + return ret; + } } else if (data->payload_size) { uint32_t *payload; uint16_t port_id = 0; @@ -459,6 +565,7 @@ static int32_t afe_callback(struct apr_client_data *data, void *priv) case AFE_PORTS_CMD_DTMF_CTL: case AFE_SVC_CMD_SET_PARAM: case AFE_SVC_CMD_SET_PARAM_V2: + case AFE_CMD_REQUEST_LPASS_RESOURCES: atomic_set(&this_afe.state, 0); wake_up(&this_afe.wait[data->token]); break; @@ -498,6 +605,18 @@ static int32_t afe_callback(struct apr_client_data *data, void *priv) atomic_set(&this_afe.state, payload[1]); wake_up(&this_afe.wait[data->token]); break; + case AFE_CMD_RELEASE_LPASS_RESOURCES: + memset(&this_afe.alloced_rddma[0], + 0, + AFE_MAX_RDDMA); + memset(&this_afe.alloced_wrdma[0], + 0, + AFE_MAX_WRDMA); + this_afe.num_alloced_rddma = 0; + this_afe.num_alloced_wrdma = 0; + atomic_set(&this_afe.state, 0); + wake_up(&this_afe.wait[data->token]); + break; default: pr_err("%s: Unknown cmd 0x%x\n", __func__, payload[0]); @@ -6528,6 +6647,173 @@ done: return result; } +int afe_request_dma_resources(uint8_t dma_type, uint8_t num_read_dma_channels, + uint8_t num_write_dma_channels) +{ + int result = 0; + struct afe_request_lpass_dma_resources_command config; + + pr_debug("%s:\n", __func__); + + if (dma_type != AFE_LPAIF_DEFAULT_DMA_TYPE) { + pr_err("%s: DMA type %d is invalid\n", + __func__, + dma_type); + goto done; + } + + if ((num_read_dma_channels == 0) && + (num_write_dma_channels == 0)) { + pr_err("%s: DMA channels to allocate are 0\n", + __func__); + goto done; + } + + if (num_read_dma_channels > AFE_MAX_RDDMA) { + pr_err("%s: Read DMA channels %d to allocate are > %d\n", + __func__, + num_read_dma_channels, + AFE_MAX_RDDMA); + goto done; + } + + if (num_write_dma_channels > AFE_MAX_WRDMA) { + pr_err("%s: Write DMA channels %d to allocate are > %d\n", + __func__, + num_write_dma_channels, + AFE_MAX_WRDMA); + goto done; + } + + result = afe_q6_interface_prepare(); + if (result != 0) { + pr_err("%s: Q6 interface prepare failed %d\n", + __func__, result); + goto done; + } + + memset(&config, 0, sizeof(config)); + config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + config.hdr.pkt_size = sizeof(config); + config.hdr.src_port = 0; + config.hdr.dest_port = 0; + config.hdr.token = IDX_GLOBAL_CFG; + config.hdr.opcode = AFE_CMD_REQUEST_LPASS_RESOURCES; + config.resources.resource_id = AFE_LPAIF_DMA_RESOURCE_ID; + /* Only AFE_LPAIF_DEFAULT_DMA_TYPE dma type is supported */ + config.dma_resources.dma_type = dma_type; + config.dma_resources.num_read_dma_channels = num_read_dma_channels; + config.dma_resources.num_write_dma_channels = num_write_dma_channels; + + result = afe_apr_send_pkt(&config, &this_afe.wait[IDX_GLOBAL_CFG]); + if (result) + pr_err("%s: AFE_CMD_REQUEST_LPASS_RESOURCES failed %d\n", + __func__, result); + +done: + return result; +} +EXPORT_SYMBOL(afe_request_dma_resources); + +int afe_get_dma_idx(bool **ret_rddma_idx, + bool **ret_wrdma_idx) +{ + int ret = 0; + + if (!ret_rddma_idx || !ret_wrdma_idx) { + pr_err("%s: invalid return pointers.", __func__); + ret = -EINVAL; + goto done; + } + + *ret_rddma_idx = &this_afe.alloced_rddma[0]; + *ret_wrdma_idx = &this_afe.alloced_wrdma[0]; + +done: + return ret; +} +EXPORT_SYMBOL(afe_get_dma_idx); + +int afe_release_all_dma_resources(void) +{ + int result = 0; + int i, total_size; + struct afe_release_lpass_dma_resources_command *config; + uint8_t *payload; + + pr_debug("%s:\n", __func__); + + if ((this_afe.num_alloced_rddma == 0) && + (this_afe.num_alloced_wrdma == 0)) { + pr_err("%s: DMA channels to release is 0", + __func__); + goto done; + } + + result = afe_q6_interface_prepare(); + if (result != 0) { + pr_err("%s: Q6 interface prepare failed %d\n", + __func__, result); + goto done; + } + + total_size = sizeof(struct afe_release_lpass_dma_resources_command) + + sizeof(uint8_t) * + (this_afe.num_alloced_rddma + this_afe.num_alloced_wrdma); + + config = kzalloc(total_size, GFP_KERNEL); + if (!config) { + result = -ENOMEM; + goto done; + } + + memset(config, 0, total_size); + payload = (uint8_t *) config + + sizeof(struct afe_release_lpass_dma_resources_command); + + config->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + config->hdr.pkt_size = total_size; + config->hdr.src_port = 0; + config->hdr.dest_port = 0; + config->hdr.token = IDX_GLOBAL_CFG; + config->hdr.opcode = AFE_CMD_RELEASE_LPASS_RESOURCES; + config->resources.resource_id = AFE_LPAIF_DMA_RESOURCE_ID; + /* Only AFE_LPAIF_DEFAULT_DMA_TYPE dma type is supported */ + config->dma_resources.dma_type = AFE_LPAIF_DEFAULT_DMA_TYPE; + config->dma_resources.num_read_dma_channels = + this_afe.num_alloced_rddma; + config->dma_resources.num_write_dma_channels = + this_afe.num_alloced_wrdma; + + for (i = 0; i < AFE_MAX_RDDMA; i++) { + if (this_afe.alloced_rddma[i]) { + *payload = i; + payload++; + } + } + + for (i = 0; i < AFE_MAX_WRDMA; i++) { + if (this_afe.alloced_wrdma[i]) { + *payload = i; + payload++; + } + } + + result = afe_apr_send_pkt(config, &this_afe.wait[IDX_GLOBAL_CFG]); + if (result) + pr_err("%s: AFE_CMD_RELEASE_LPASS_RESOURCES failed %d\n", + __func__, result); + + kfree(config); +done: + return result; +} +EXPORT_SYMBOL(afe_release_all_dma_resources); + static int __init afe_init(void) { int i = 0, ret;