OSDN Git Service

ASoC: msm: qdsp6v2: enable DMA channel control
authorDerek Chen <chenche@codeaurora.org>
Thu, 5 Oct 2017 22:48:45 +0000 (18:48 -0400)
committerGerrit - the friendly Code Review server <code-review@localhost>
Fri, 27 Apr 2018 20:54:38 +0000 (13:54 -0700)
Enable AFE driver to request and release
LPASS DMA channel indices.

CRs-fixed: 2126719
Signed-off-by: Derek Chen <chenche@codeaurora.org>
Change-Id: Ic40bbf9aa7170d2c91cef45ddca7a86ccd27c4b0

include/sound/apr_audio-v2.h
include/sound/q6afe-v2.h
sound/soc/msm/qdsp6v2/q6afe.c

index 0393c88..ee65bda 100644 (file)
@@ -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:
+ * <struct afe_cmdrsp_request_lpass_resources>
+ * <struct afe_cmdrsp_request_lpass_dma_resources>
+ * 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:
+ * <struct afe_cmd_release_lpass_resources>
+ * <struct afe_cmd_release_lpass_dma_resources>
+ * 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.
index 5031e62..9a7eb29 100644 (file)
@@ -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__ */
index 352ea92..a52dcb6 100644 (file)
@@ -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;