From 99139b80c1b3d73026ed8be2de42c52e2976ab64 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 27 Sep 2021 14:55:40 +0100 Subject: soc: qcom: apr: make code more reuseable APR and other packet routers like GPR are pretty much same and interact with other drivers in similar way. Ex: GPR ports can be considered as APR services, only difference is they are allocated dynamically. Other difference is packet layout, which should not matter with the apis abstracted. Apart from this the rest of the functionality is pretty much identical across APR and GPR. Make the apr code more reusable by abstracting it service level, rather than device level so that we do not need to write new drivers for other new packet routers like GPR. This patch is in preparation to add GPR support to this driver. Signed-off-by: Srinivas Kandagatla Reviewed-by: Pierre-Louis Bossart Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20210927135559.738-4-srinivas.kandagatla@linaro.org --- include/linux/soc/qcom/apr.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/soc/qcom/apr.h b/include/linux/soc/qcom/apr.h index 137f9f2ac4c3..7bca213a3f83 100644 --- a/include/linux/soc/qcom/apr.h +++ b/include/linux/soc/qcom/apr.h @@ -79,6 +79,15 @@ struct apr_resp_pkt { #define APR_SVC_MAJOR_VERSION(v) ((v >> 16) & 0xFF) #define APR_SVC_MINOR_VERSION(v) (v & 0xFF) +struct packet_router; +struct pkt_router_svc { + struct device *dev; + struct packet_router *pr; + spinlock_t lock; + int id; + void *priv; +}; + struct apr_device { struct device dev; uint16_t svc_id; @@ -86,11 +95,12 @@ struct apr_device { uint32_t version; char name[APR_NAME_SIZE]; const char *service_path; - spinlock_t lock; + struct pkt_router_svc svc; struct list_head node; }; #define to_apr_device(d) container_of(d, struct apr_device, dev) +#define svc_to_apr_device(d) container_of(d, struct apr_device, svc) struct apr_driver { int (*probe)(struct apr_device *sl); -- cgit v1.2.3 From 974c6faf7667e551d202712470ca210c14ca249d Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 27 Sep 2021 14:55:41 +0100 Subject: soc: dt-bindings: qcom: add gpr bindings Qualcomm Generic Packet router aka GPR is the IPC mechanism found in AudioReach next generation signal processing framework to perform command and response messages between various processors. GPR has concepts of static and dynamic port, all static services like APM (Audio Processing Manager), PRM (Proxy resource manager) have fixed port numbers where as dynamic services like graphs have dynamic port numbers which are allocated at runtime. All GPR packet messages will have source and destination domain and port along with opcode and payload. This support is added using existing APR driver to reuse most of the code. Signed-off-by: Srinivas Kandagatla Reviewed-by: Rob Herring Reviewed-by: Pierre-Louis Bossart Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20210927135559.738-5-srinivas.kandagatla@linaro.org --- .../devicetree/bindings/soc/qcom/qcom,apr.yaml | 41 ++++++++++++++++++---- include/dt-bindings/soc/qcom,gpr.h | 19 ++++++++++ 2 files changed, 53 insertions(+), 7 deletions(-) create mode 100644 include/dt-bindings/soc/qcom,gpr.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,apr.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,apr.yaml index bfe04fca8aa3..028c5d105adb 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,apr.yaml +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,apr.yaml @@ -4,20 +4,21 @@ $id: "http://devicetree.org/schemas/soc/qcom/qcom,apr.yaml#" $schema: "http://devicetree.org/meta-schemas/core.yaml#" -title: Qualcomm APR (Asynchronous Packet Router) binding +title: Qualcomm APR/GPR (Asynchronous/Generic Packet Router) binding maintainers: - Srinivas Kandagatla description: | - This binding describes the Qualcomm APR, APR is a IPC protocol for - communication between Application processor and QDSP. APR is mainly + This binding describes the Qualcomm APR/GPR, APR/GPR is a IPC protocol for + communication between Application processor and QDSP. APR/GPR is mainly used for audio/voice services on the QDSP. properties: compatible: enum: - qcom,apr-v2 + - qcom,gpr qcom,apr-domain: $ref: /schemas/types.yaml#/definitions/uint32 @@ -46,6 +47,10 @@ properties: 5 = Application processor Domain 6 = Modem2 Domain 7 = Application Processor2 Domain + Selects the processor domain for gpr + 1 = Modem Domain + 2 = Audio DSP Domain + 3 = Application Processor Domain '#address-cells': const: 1 @@ -53,12 +58,12 @@ properties: '#size-cells': const: 0 -#APR Services +#APR/GPR Services patternProperties: - "^service@[3-9a-d]$": + "^service@[1-9a-d]$": type: object description: - APR node's client devices use subnodes for desired static port services. + APR/GPR node's client devices use subnodes for desired static port services. properties: compatible: @@ -67,9 +72,11 @@ patternProperties: - qcom,q6asm - qcom,q6afe - qcom,q6adm + - qcom,q6apm + - qcom,q6prm reg: - minimum: 3 + minimum: 1 maximum: 13 description: APR Service ID @@ -84,6 +91,11 @@ patternProperties: 11 = Core voice processor. 12 = Ultrasound stream manager. 13 = Listen stream manager. + GPR Service ID + 1 = Audio Process Manager Service + 2 = Proxy Resource Manager Service. + 3 = AMDB Service. + 4 = Voice processing manager. qcom,protection-domain: $ref: /schemas/types.yaml#/definitions/string-array @@ -148,3 +160,18 @@ examples: qcom,protection-domain = "avs/audio", "msm/adsp/audio_pd"; }; }; + + - | + #include + gpr { + compatible = "qcom,gpr"; + qcom,domain = ; + #address-cells = <1>; + #size-cells = <0>; + + service@1 { + compatible = "qcom,q6apm"; + reg = ; + qcom,protection-domain = "avs/audio", "msm/adsp/audio_pd"; + }; + }; diff --git a/include/dt-bindings/soc/qcom,gpr.h b/include/dt-bindings/soc/qcom,gpr.h new file mode 100644 index 000000000000..3107da59319c --- /dev/null +++ b/include/dt-bindings/soc/qcom,gpr.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ + +#ifndef __DT_BINDINGS_QCOM_GPR_H +#define __DT_BINDINGS_QCOM_GPR_H + +/* DOMAINS */ + +#define GPR_DOMAIN_ID_MODEM 1 +#define GPR_DOMAIN_ID_ADSP 2 +#define GPR_DOMAIN_ID_APPS 3 + +/* Static Services */ + +#define GPR_APM_MODULE_IID 1 +#define GPR_PRM_MODULE_IID 2 +#define GPR_AMDB_MODULE_IID 3 +#define GPR_VCPM_MODULE_IID 4 + +#endif /* __DT_BINDINGS_QCOM_GPR_H */ -- cgit v1.2.3 From ec1471a898cca38af6b8956a83ebc1297214546f Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 27 Sep 2021 14:55:42 +0100 Subject: soc: qcom: apr: Add GPR support Qualcomm Generic Packet router aka GPR is the IPC mechanism found in AudioReach next generation signal processing framework to perform command and response messages between various processors. GPR has concepts of static and dynamic port, all static services like APM (Audio Processing Manager), PRM (Proxy resource manager) have fixed port numbers where as dynamic services like graphs have dynamic port numbers which are allocated at runtime. All GPR packet messages will have source and destination domain and port along with opcode and payload. Signed-off-by: Srinivas Kandagatla Reviewed-by: Pierre-Louis Bossart Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20210927135559.738-6-srinivas.kandagatla@linaro.org --- drivers/soc/qcom/Kconfig | 2 +- drivers/soc/qcom/apr.c | 166 +++++++++++++++++++++++++++++++++++++++++-- include/linux/soc/qcom/apr.h | 58 +++++++++++++++ 3 files changed, 219 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 79b568f82a1c..bfa2ab5772cf 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -199,7 +199,7 @@ config QCOM_WCNSS_CTRL firmware to a newly booted WCNSS chip. config QCOM_APR - tristate "Qualcomm APR Bus (Asynchronous Packet Router)" + tristate "Qualcomm APR/GPR Bus (Asynchronous/Generic Packet Router)" depends on ARCH_QCOM || COMPILE_TEST depends on RPMSG depends on NET diff --git a/drivers/soc/qcom/apr.c b/drivers/soc/qcom/apr.c index bfad71e540ad..8a9bfbcd4bb9 100644 --- a/drivers/soc/qcom/apr.c +++ b/drivers/soc/qcom/apr.c @@ -17,8 +17,13 @@ enum { PR_TYPE_APR = 0, + PR_TYPE_GPR, }; +/* Some random values tbh which does not collide with static modules */ +#define GPR_DYNAMIC_PORT_START 0x10000000 +#define GPR_DYNAMIC_PORT_END 0x20000000 + struct packet_router { struct rpmsg_endpoint *ch; struct device *dev; @@ -69,6 +74,83 @@ int apr_send_pkt(struct apr_device *adev, struct apr_pkt *pkt) } EXPORT_SYMBOL_GPL(apr_send_pkt); +void gpr_free_port(gpr_port_t *port) +{ + struct packet_router *gpr = port->pr; + unsigned long flags; + + spin_lock_irqsave(&gpr->svcs_lock, flags); + idr_remove(&gpr->svcs_idr, port->id); + spin_unlock_irqrestore(&gpr->svcs_lock, flags); + + kfree(port); +} +EXPORT_SYMBOL_GPL(gpr_free_port); + +gpr_port_t *gpr_alloc_port(struct apr_device *gdev, struct device *dev, + gpr_port_cb cb, void *priv) +{ + struct packet_router *pr = dev_get_drvdata(gdev->dev.parent); + gpr_port_t *port; + struct pkt_router_svc *svc; + int id; + + port = kzalloc(sizeof(*port), GFP_KERNEL); + if (!port) + return ERR_PTR(-ENOMEM); + + svc = port; + svc->callback = cb; + svc->pr = pr; + svc->priv = priv; + svc->dev = dev; + spin_lock_init(&svc->lock); + + spin_lock(&pr->svcs_lock); + id = idr_alloc_cyclic(&pr->svcs_idr, svc, GPR_DYNAMIC_PORT_START, + GPR_DYNAMIC_PORT_END, GFP_ATOMIC); + if (id < 0) { + dev_err(dev, "Unable to allocate dynamic GPR src port\n"); + kfree(port); + spin_unlock(&pr->svcs_lock); + return ERR_PTR(id); + } + + svc->id = id; + spin_unlock(&pr->svcs_lock); + + return port; +} +EXPORT_SYMBOL_GPL(gpr_alloc_port); + +static int pkt_router_send_svc_pkt(struct pkt_router_svc *svc, struct gpr_pkt *pkt) +{ + struct packet_router *pr = svc->pr; + struct gpr_hdr *hdr; + unsigned long flags; + int ret; + + hdr = &pkt->hdr; + + spin_lock_irqsave(&svc->lock, flags); + ret = rpmsg_trysend(pr->ch, pkt, hdr->pkt_size); + spin_unlock_irqrestore(&svc->lock, flags); + + return ret ? ret : hdr->pkt_size; +} + +int gpr_send_pkt(struct apr_device *gdev, struct gpr_pkt *pkt) +{ + return pkt_router_send_svc_pkt(&gdev->svc, pkt); +} +EXPORT_SYMBOL_GPL(gpr_send_pkt); + +int gpr_send_port_pkt(gpr_port_t *port, struct gpr_pkt *pkt) +{ + return pkt_router_send_svc_pkt(port, pkt); +} +EXPORT_SYMBOL_GPL(gpr_send_port_pkt); + static void apr_dev_release(struct device *dev) { struct apr_device *adev = to_apr_device(dev); @@ -177,6 +259,59 @@ static int apr_do_rx_callback(struct packet_router *apr, struct apr_rx_buf *abuf return 0; } +static int gpr_do_rx_callback(struct packet_router *gpr, struct apr_rx_buf *abuf) +{ + uint16_t hdr_size, ver; + struct pkt_router_svc *svc = NULL; + struct gpr_resp_pkt resp; + struct gpr_hdr *hdr; + unsigned long flags; + void *buf = abuf->buf; + int len = abuf->len; + + hdr = buf; + ver = hdr->version; + if (ver > GPR_PKT_VER + 1) + return -EINVAL; + + hdr_size = hdr->hdr_size; + if (hdr_size < GPR_PKT_HEADER_WORD_SIZE) { + dev_err(gpr->dev, "GPR: Wrong hdr size:%d\n", hdr_size); + return -EINVAL; + } + + if (hdr->pkt_size < GPR_PKT_HEADER_BYTE_SIZE || hdr->pkt_size != len) { + dev_err(gpr->dev, "GPR: Wrong packet size\n"); + return -EINVAL; + } + + resp.hdr = *hdr; + resp.payload_size = hdr->pkt_size - (hdr_size * 4); + + /* + * NOTE: hdr_size is not same as GPR_HDR_SIZE as remote can include + * optional headers in to gpr_hdr which should be ignored + */ + if (resp.payload_size > 0) + resp.payload = buf + (hdr_size * 4); + + + spin_lock_irqsave(&gpr->svcs_lock, flags); + svc = idr_find(&gpr->svcs_idr, hdr->dest_port); + spin_unlock_irqrestore(&gpr->svcs_lock, flags); + + if (!svc) { + dev_err(gpr->dev, "GPR: Port(%x) is not registered\n", + hdr->dest_port); + return -EINVAL; + } + + if (svc->callback) + svc->callback(&resp, svc->priv, 0); + + return 0; +} + static void apr_rxwq(struct work_struct *work) { struct packet_router *apr = container_of(work, struct packet_router, rx_work); @@ -189,6 +324,9 @@ static void apr_rxwq(struct work_struct *work) case PR_TYPE_APR: apr_do_rx_callback(apr, abuf); break; + case PR_TYPE_GPR: + gpr_do_rx_callback(apr, abuf); + break; default: break; } @@ -227,8 +365,13 @@ static int apr_device_probe(struct device *dev) { struct apr_device *adev = to_apr_device(dev); struct apr_driver *adrv = to_apr_driver(dev->driver); + int ret; - return adrv->probe(adev); + ret = adrv->probe(adev); + if (!ret) + adev->svc.callback = adrv->gpr_callback; + + return ret; } static void apr_device_remove(struct device *dev) @@ -299,6 +442,10 @@ static int apr_add_device(struct device *dev, struct device_node *np, dev_set_name(&adev->dev, "aprsvc:%s:%x:%x", adev->name, domain_id, svc_id); break; + case PR_TYPE_GPR: + dev_set_name(&adev->dev, "gprsvc:%s:%x:%x", adev->name, + domain_id, svc_id); + break; default: break; } @@ -316,7 +463,7 @@ static int apr_add_device(struct device *dev, struct device_node *np, of_property_read_string_index(np, "qcom,protection-domain", 1, &adev->service_path); - dev_info(dev, "Adding APR dev: %s\n", dev_name(&adev->dev)); + dev_info(dev, "Adding APR/GPR dev: %s\n", dev_name(&adev->dev)); ret = device_register(&adev->dev); if (ret) { @@ -446,10 +593,16 @@ static int apr_probe(struct rpmsg_device *rpdev) return -ENOMEM; ret = of_property_read_u32(dev->of_node, "qcom,domain", &apr->dest_domain_id); - if (ret) /* try deprecated apr-domain property */ - ret = of_property_read_u32(dev->of_node, "qcom,apr-domain", - &apr->dest_domain_id); - apr->type = PR_TYPE_APR; + + if (of_device_is_compatible(dev->of_node, "qcom,gpr")) { + apr->type = PR_TYPE_GPR; + } else { + if (ret) /* try deprecated apr-domain property */ + ret = of_property_read_u32(dev->of_node, "qcom,apr-domain", + &apr->dest_domain_id); + apr->type = PR_TYPE_APR; + } + if (ret) { dev_err(dev, "Domain ID not specified in DT\n"); return ret; @@ -534,6 +687,7 @@ EXPORT_SYMBOL_GPL(apr_driver_unregister); static const struct of_device_id pkt_router_of_match[] = { { .compatible = "qcom,apr"}, { .compatible = "qcom,apr-v2"}, + { .compatible = "qcom,gpr"}, {} }; MODULE_DEVICE_TABLE(of, pkt_router_of_match); diff --git a/include/linux/soc/qcom/apr.h b/include/linux/soc/qcom/apr.h index 7bca213a3f83..23c5b30f3511 100644 --- a/include/linux/soc/qcom/apr.h +++ b/include/linux/soc/qcom/apr.h @@ -7,6 +7,7 @@ #include #include #include +#include extern struct bus_type aprbus; @@ -75,19 +76,65 @@ struct apr_resp_pkt { int payload_size; }; +struct gpr_hdr { + uint32_t version:4; + uint32_t hdr_size:4; + uint32_t pkt_size:24; + uint32_t dest_domain:8; + uint32_t src_domain:8; + uint32_t reserved:16; + uint32_t src_port; + uint32_t dest_port; + uint32_t token; + uint32_t opcode; +} __packed; + +struct gpr_pkt { + struct gpr_hdr hdr; + uint32_t payload[]; +}; + +struct gpr_resp_pkt { + struct gpr_hdr hdr; + void *payload; + int payload_size; +}; + +#define GPR_HDR_SIZE sizeof(struct gpr_hdr) +#define GPR_PKT_VER 0x0 +#define GPR_PKT_HEADER_WORD_SIZE ((sizeof(struct gpr_pkt) + 3) >> 2) +#define GPR_PKT_HEADER_BYTE_SIZE (GPR_PKT_HEADER_WORD_SIZE << 2) + +#define GPR_BASIC_RSP_RESULT 0x02001005 + +struct gpr_ibasic_rsp_result_t { + uint32_t opcode; + uint32_t status; +}; + +#define GPR_BASIC_EVT_ACCEPTED 0x02001006 + +struct gpr_ibasic_rsp_accepted_t { + uint32_t opcode; +}; + /* Bits 0 to 15 -- Minor version, Bits 16 to 31 -- Major version */ #define APR_SVC_MAJOR_VERSION(v) ((v >> 16) & 0xFF) #define APR_SVC_MINOR_VERSION(v) (v & 0xFF) +typedef int (*gpr_port_cb) (struct gpr_resp_pkt *d, void *priv, int op); struct packet_router; struct pkt_router_svc { struct device *dev; + gpr_port_cb callback; struct packet_router *pr; spinlock_t lock; int id; void *priv; }; +typedef struct pkt_router_svc gpr_port_t; + struct apr_device { struct device dev; uint16_t svc_id; @@ -99,6 +146,8 @@ struct apr_device { struct list_head node; }; +typedef struct apr_device gpr_device_t; + #define to_apr_device(d) container_of(d, struct apr_device, dev) #define svc_to_apr_device(d) container_of(d, struct apr_device, svc) @@ -107,10 +156,12 @@ struct apr_driver { int (*remove)(struct apr_device *sl); int (*callback)(struct apr_device *a, struct apr_resp_pkt *d); + int (*gpr_callback)(struct gpr_resp_pkt *d, void *data, int op); struct device_driver driver; const struct apr_device_id *id_table; }; +typedef struct apr_driver gpr_driver_t; #define to_apr_driver(d) container_of(d, struct apr_driver, driver) /* @@ -133,7 +184,14 @@ void apr_driver_unregister(struct apr_driver *drv); #define module_apr_driver(__apr_driver) \ module_driver(__apr_driver, apr_driver_register, \ apr_driver_unregister) +#define module_gpr_driver(__gpr_driver) module_apr_driver(__gpr_driver) int apr_send_pkt(struct apr_device *adev, struct apr_pkt *pkt); +gpr_port_t *gpr_alloc_port(gpr_device_t *gdev, struct device *dev, + gpr_port_cb cb, void *priv); +void gpr_free_port(gpr_port_t *port); +int gpr_send_port_pkt(gpr_port_t *port, struct gpr_pkt *pkt); +int gpr_send_pkt(gpr_device_t *gdev, struct gpr_pkt *pkt); + #endif /* __QCOM_APR_H_ */ -- cgit v1.2.3 From e3008b7ccb1dedcea954505ff964a53641d2b980 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 26 Oct 2021 12:16:41 +0100 Subject: ASoC: dt-bindings: rename q6afe.h to q6dsp-lpass-ports.h move all LPASS audio ports defines from q6afe.h to q6dsp-lpass-ports.h as these belong to LPASS IP. Also this move helps in reusing this header across multiple audio frameworks on Qualcomm Audio DSP. This patch is split out of the dt-bindings patch to enable easy review. Signed-off-by: Srinivas Kandagatla Acked-by: Rob Herring Link: https://lore.kernel.org/r/20211026111655.1702-4-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- include/dt-bindings/sound/qcom,q6afe.h | 203 +------------------- include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h | 208 +++++++++++++++++++++ 2 files changed, 210 insertions(+), 201 deletions(-) create mode 100644 include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h (limited to 'include') diff --git a/include/dt-bindings/sound/qcom,q6afe.h b/include/dt-bindings/sound/qcom,q6afe.h index 66c21ab03eef..9d5d89cfabcf 100644 --- a/include/dt-bindings/sound/qcom,q6afe.h +++ b/include/dt-bindings/sound/qcom,q6afe.h @@ -2,207 +2,8 @@ #ifndef __DT_BINDINGS_Q6_AFE_H__ #define __DT_BINDINGS_Q6_AFE_H__ -/* Audio Front End (AFE) virtual ports IDs */ -#define HDMI_RX 1 -#define SLIMBUS_0_RX 2 -#define SLIMBUS_0_TX 3 -#define SLIMBUS_1_RX 4 -#define SLIMBUS_1_TX 5 -#define SLIMBUS_2_RX 6 -#define SLIMBUS_2_TX 7 -#define SLIMBUS_3_RX 8 -#define SLIMBUS_3_TX 9 -#define SLIMBUS_4_RX 10 -#define SLIMBUS_4_TX 11 -#define SLIMBUS_5_RX 12 -#define SLIMBUS_5_TX 13 -#define SLIMBUS_6_RX 14 -#define SLIMBUS_6_TX 15 -#define PRIMARY_MI2S_RX 16 -#define PRIMARY_MI2S_TX 17 -#define SECONDARY_MI2S_RX 18 -#define SECONDARY_MI2S_TX 19 -#define TERTIARY_MI2S_RX 20 -#define TERTIARY_MI2S_TX 21 -#define QUATERNARY_MI2S_RX 22 -#define QUATERNARY_MI2S_TX 23 -#define PRIMARY_TDM_RX_0 24 -#define PRIMARY_TDM_TX_0 25 -#define PRIMARY_TDM_RX_1 26 -#define PRIMARY_TDM_TX_1 27 -#define PRIMARY_TDM_RX_2 28 -#define PRIMARY_TDM_TX_2 29 -#define PRIMARY_TDM_RX_3 30 -#define PRIMARY_TDM_TX_3 31 -#define PRIMARY_TDM_RX_4 32 -#define PRIMARY_TDM_TX_4 33 -#define PRIMARY_TDM_RX_5 34 -#define PRIMARY_TDM_TX_5 35 -#define PRIMARY_TDM_RX_6 36 -#define PRIMARY_TDM_TX_6 37 -#define PRIMARY_TDM_RX_7 38 -#define PRIMARY_TDM_TX_7 39 -#define SECONDARY_TDM_RX_0 40 -#define SECONDARY_TDM_TX_0 41 -#define SECONDARY_TDM_RX_1 42 -#define SECONDARY_TDM_TX_1 43 -#define SECONDARY_TDM_RX_2 44 -#define SECONDARY_TDM_TX_2 45 -#define SECONDARY_TDM_RX_3 46 -#define SECONDARY_TDM_TX_3 47 -#define SECONDARY_TDM_RX_4 48 -#define SECONDARY_TDM_TX_4 49 -#define SECONDARY_TDM_RX_5 50 -#define SECONDARY_TDM_TX_5 51 -#define SECONDARY_TDM_RX_6 52 -#define SECONDARY_TDM_TX_6 53 -#define SECONDARY_TDM_RX_7 54 -#define SECONDARY_TDM_TX_7 55 -#define TERTIARY_TDM_RX_0 56 -#define TERTIARY_TDM_TX_0 57 -#define TERTIARY_TDM_RX_1 58 -#define TERTIARY_TDM_TX_1 59 -#define TERTIARY_TDM_RX_2 60 -#define TERTIARY_TDM_TX_2 61 -#define TERTIARY_TDM_RX_3 62 -#define TERTIARY_TDM_TX_3 63 -#define TERTIARY_TDM_RX_4 64 -#define TERTIARY_TDM_TX_4 65 -#define TERTIARY_TDM_RX_5 66 -#define TERTIARY_TDM_TX_5 67 -#define TERTIARY_TDM_RX_6 68 -#define TERTIARY_TDM_TX_6 69 -#define TERTIARY_TDM_RX_7 70 -#define TERTIARY_TDM_TX_7 71 -#define QUATERNARY_TDM_RX_0 72 -#define QUATERNARY_TDM_TX_0 73 -#define QUATERNARY_TDM_RX_1 74 -#define QUATERNARY_TDM_TX_1 75 -#define QUATERNARY_TDM_RX_2 76 -#define QUATERNARY_TDM_TX_2 77 -#define QUATERNARY_TDM_RX_3 78 -#define QUATERNARY_TDM_TX_3 79 -#define QUATERNARY_TDM_RX_4 80 -#define QUATERNARY_TDM_TX_4 81 -#define QUATERNARY_TDM_RX_5 82 -#define QUATERNARY_TDM_TX_5 83 -#define QUATERNARY_TDM_RX_6 84 -#define QUATERNARY_TDM_TX_6 85 -#define QUATERNARY_TDM_RX_7 86 -#define QUATERNARY_TDM_TX_7 87 -#define QUINARY_TDM_RX_0 88 -#define QUINARY_TDM_TX_0 89 -#define QUINARY_TDM_RX_1 90 -#define QUINARY_TDM_TX_1 91 -#define QUINARY_TDM_RX_2 92 -#define QUINARY_TDM_TX_2 93 -#define QUINARY_TDM_RX_3 94 -#define QUINARY_TDM_TX_3 95 -#define QUINARY_TDM_RX_4 96 -#define QUINARY_TDM_TX_4 97 -#define QUINARY_TDM_RX_5 98 -#define QUINARY_TDM_TX_5 99 -#define QUINARY_TDM_RX_6 100 -#define QUINARY_TDM_TX_6 101 -#define QUINARY_TDM_RX_7 102 -#define QUINARY_TDM_TX_7 103 -#define DISPLAY_PORT_RX 104 -#define WSA_CODEC_DMA_RX_0 105 -#define WSA_CODEC_DMA_TX_0 106 -#define WSA_CODEC_DMA_RX_1 107 -#define WSA_CODEC_DMA_TX_1 108 -#define WSA_CODEC_DMA_TX_2 109 -#define VA_CODEC_DMA_TX_0 110 -#define VA_CODEC_DMA_TX_1 111 -#define VA_CODEC_DMA_TX_2 112 -#define RX_CODEC_DMA_RX_0 113 -#define TX_CODEC_DMA_TX_0 114 -#define RX_CODEC_DMA_RX_1 115 -#define TX_CODEC_DMA_TX_1 116 -#define RX_CODEC_DMA_RX_2 117 -#define TX_CODEC_DMA_TX_2 118 -#define RX_CODEC_DMA_RX_3 119 -#define TX_CODEC_DMA_TX_3 120 -#define RX_CODEC_DMA_RX_4 121 -#define TX_CODEC_DMA_TX_4 122 -#define RX_CODEC_DMA_RX_5 123 -#define TX_CODEC_DMA_TX_5 124 -#define RX_CODEC_DMA_RX_6 125 -#define RX_CODEC_DMA_RX_7 126 -#define QUINARY_MI2S_RX 127 -#define QUINARY_MI2S_TX 128 +/* This file exists due to backward compatibility reasons, Please do not DELETE! */ -#define LPASS_CLK_ID_PRI_MI2S_IBIT 1 -#define LPASS_CLK_ID_PRI_MI2S_EBIT 2 -#define LPASS_CLK_ID_SEC_MI2S_IBIT 3 -#define LPASS_CLK_ID_SEC_MI2S_EBIT 4 -#define LPASS_CLK_ID_TER_MI2S_IBIT 5 -#define LPASS_CLK_ID_TER_MI2S_EBIT 6 -#define LPASS_CLK_ID_QUAD_MI2S_IBIT 7 -#define LPASS_CLK_ID_QUAD_MI2S_EBIT 8 -#define LPASS_CLK_ID_SPEAKER_I2S_IBIT 9 -#define LPASS_CLK_ID_SPEAKER_I2S_EBIT 10 -#define LPASS_CLK_ID_SPEAKER_I2S_OSR 11 -#define LPASS_CLK_ID_QUI_MI2S_IBIT 12 -#define LPASS_CLK_ID_QUI_MI2S_EBIT 13 -#define LPASS_CLK_ID_SEN_MI2S_IBIT 14 -#define LPASS_CLK_ID_SEN_MI2S_EBIT 15 -#define LPASS_CLK_ID_INT0_MI2S_IBIT 16 -#define LPASS_CLK_ID_INT1_MI2S_IBIT 17 -#define LPASS_CLK_ID_INT2_MI2S_IBIT 18 -#define LPASS_CLK_ID_INT3_MI2S_IBIT 19 -#define LPASS_CLK_ID_INT4_MI2S_IBIT 20 -#define LPASS_CLK_ID_INT5_MI2S_IBIT 21 -#define LPASS_CLK_ID_INT6_MI2S_IBIT 22 -#define LPASS_CLK_ID_QUI_MI2S_OSR 23 -#define LPASS_CLK_ID_PRI_PCM_IBIT 24 -#define LPASS_CLK_ID_PRI_PCM_EBIT 25 -#define LPASS_CLK_ID_SEC_PCM_IBIT 26 -#define LPASS_CLK_ID_SEC_PCM_EBIT 27 -#define LPASS_CLK_ID_TER_PCM_IBIT 28 -#define LPASS_CLK_ID_TER_PCM_EBIT 29 -#define LPASS_CLK_ID_QUAD_PCM_IBIT 30 -#define LPASS_CLK_ID_QUAD_PCM_EBIT 31 -#define LPASS_CLK_ID_QUIN_PCM_IBIT 32 -#define LPASS_CLK_ID_QUIN_PCM_EBIT 33 -#define LPASS_CLK_ID_QUI_PCM_OSR 34 -#define LPASS_CLK_ID_PRI_TDM_IBIT 35 -#define LPASS_CLK_ID_PRI_TDM_EBIT 36 -#define LPASS_CLK_ID_SEC_TDM_IBIT 37 -#define LPASS_CLK_ID_SEC_TDM_EBIT 38 -#define LPASS_CLK_ID_TER_TDM_IBIT 39 -#define LPASS_CLK_ID_TER_TDM_EBIT 40 -#define LPASS_CLK_ID_QUAD_TDM_IBIT 41 -#define LPASS_CLK_ID_QUAD_TDM_EBIT 42 -#define LPASS_CLK_ID_QUIN_TDM_IBIT 43 -#define LPASS_CLK_ID_QUIN_TDM_EBIT 44 -#define LPASS_CLK_ID_QUIN_TDM_OSR 45 -#define LPASS_CLK_ID_MCLK_1 46 -#define LPASS_CLK_ID_MCLK_2 47 -#define LPASS_CLK_ID_MCLK_3 48 -#define LPASS_CLK_ID_MCLK_4 49 -#define LPASS_CLK_ID_INTERNAL_DIGITAL_CODEC_CORE 50 -#define LPASS_CLK_ID_INT_MCLK_0 51 -#define LPASS_CLK_ID_INT_MCLK_1 52 -#define LPASS_CLK_ID_MCLK_5 53 -#define LPASS_CLK_ID_WSA_CORE_MCLK 54 -#define LPASS_CLK_ID_WSA_CORE_NPL_MCLK 55 -#define LPASS_CLK_ID_VA_CORE_MCLK 56 -#define LPASS_CLK_ID_TX_CORE_MCLK 57 -#define LPASS_CLK_ID_TX_CORE_NPL_MCLK 58 -#define LPASS_CLK_ID_RX_CORE_MCLK 59 -#define LPASS_CLK_ID_RX_CORE_NPL_MCLK 60 -#define LPASS_CLK_ID_VA_CORE_2X_MCLK 61 - -#define LPASS_HW_AVTIMER_VOTE 101 -#define LPASS_HW_MACRO_VOTE 102 -#define LPASS_HW_DCODEC_VOTE 103 - -#define Q6AFE_MAX_CLK_ID 104 - -#define LPASS_CLK_ATTRIBUTE_INVALID 0x0 -#define LPASS_CLK_ATTRIBUTE_COUPLE_NO 0x1 -#define LPASS_CLK_ATTRIBUTE_COUPLE_DIVIDEND 0x2 -#define LPASS_CLK_ATTRIBUTE_COUPLE_DIVISOR 0x3 +#include #endif /* __DT_BINDINGS_Q6_AFE_H__ */ diff --git a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h new file mode 100644 index 000000000000..0d3276c8fc11 --- /dev/null +++ b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h @@ -0,0 +1,208 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __DT_BINDINGS_Q6_AUDIO_PORTS_H__ +#define __DT_BINDINGS_Q6_AUDIO_PORTS_H__ + +/* LPASS Audio virtual ports IDs */ +#define HDMI_RX 1 +#define SLIMBUS_0_RX 2 +#define SLIMBUS_0_TX 3 +#define SLIMBUS_1_RX 4 +#define SLIMBUS_1_TX 5 +#define SLIMBUS_2_RX 6 +#define SLIMBUS_2_TX 7 +#define SLIMBUS_3_RX 8 +#define SLIMBUS_3_TX 9 +#define SLIMBUS_4_RX 10 +#define SLIMBUS_4_TX 11 +#define SLIMBUS_5_RX 12 +#define SLIMBUS_5_TX 13 +#define SLIMBUS_6_RX 14 +#define SLIMBUS_6_TX 15 +#define PRIMARY_MI2S_RX 16 +#define PRIMARY_MI2S_TX 17 +#define SECONDARY_MI2S_RX 18 +#define SECONDARY_MI2S_TX 19 +#define TERTIARY_MI2S_RX 20 +#define TERTIARY_MI2S_TX 21 +#define QUATERNARY_MI2S_RX 22 +#define QUATERNARY_MI2S_TX 23 +#define PRIMARY_TDM_RX_0 24 +#define PRIMARY_TDM_TX_0 25 +#define PRIMARY_TDM_RX_1 26 +#define PRIMARY_TDM_TX_1 27 +#define PRIMARY_TDM_RX_2 28 +#define PRIMARY_TDM_TX_2 29 +#define PRIMARY_TDM_RX_3 30 +#define PRIMARY_TDM_TX_3 31 +#define PRIMARY_TDM_RX_4 32 +#define PRIMARY_TDM_TX_4 33 +#define PRIMARY_TDM_RX_5 34 +#define PRIMARY_TDM_TX_5 35 +#define PRIMARY_TDM_RX_6 36 +#define PRIMARY_TDM_TX_6 37 +#define PRIMARY_TDM_RX_7 38 +#define PRIMARY_TDM_TX_7 39 +#define SECONDARY_TDM_RX_0 40 +#define SECONDARY_TDM_TX_0 41 +#define SECONDARY_TDM_RX_1 42 +#define SECONDARY_TDM_TX_1 43 +#define SECONDARY_TDM_RX_2 44 +#define SECONDARY_TDM_TX_2 45 +#define SECONDARY_TDM_RX_3 46 +#define SECONDARY_TDM_TX_3 47 +#define SECONDARY_TDM_RX_4 48 +#define SECONDARY_TDM_TX_4 49 +#define SECONDARY_TDM_RX_5 50 +#define SECONDARY_TDM_TX_5 51 +#define SECONDARY_TDM_RX_6 52 +#define SECONDARY_TDM_TX_6 53 +#define SECONDARY_TDM_RX_7 54 +#define SECONDARY_TDM_TX_7 55 +#define TERTIARY_TDM_RX_0 56 +#define TERTIARY_TDM_TX_0 57 +#define TERTIARY_TDM_RX_1 58 +#define TERTIARY_TDM_TX_1 59 +#define TERTIARY_TDM_RX_2 60 +#define TERTIARY_TDM_TX_2 61 +#define TERTIARY_TDM_RX_3 62 +#define TERTIARY_TDM_TX_3 63 +#define TERTIARY_TDM_RX_4 64 +#define TERTIARY_TDM_TX_4 65 +#define TERTIARY_TDM_RX_5 66 +#define TERTIARY_TDM_TX_5 67 +#define TERTIARY_TDM_RX_6 68 +#define TERTIARY_TDM_TX_6 69 +#define TERTIARY_TDM_RX_7 70 +#define TERTIARY_TDM_TX_7 71 +#define QUATERNARY_TDM_RX_0 72 +#define QUATERNARY_TDM_TX_0 73 +#define QUATERNARY_TDM_RX_1 74 +#define QUATERNARY_TDM_TX_1 75 +#define QUATERNARY_TDM_RX_2 76 +#define QUATERNARY_TDM_TX_2 77 +#define QUATERNARY_TDM_RX_3 78 +#define QUATERNARY_TDM_TX_3 79 +#define QUATERNARY_TDM_RX_4 80 +#define QUATERNARY_TDM_TX_4 81 +#define QUATERNARY_TDM_RX_5 82 +#define QUATERNARY_TDM_TX_5 83 +#define QUATERNARY_TDM_RX_6 84 +#define QUATERNARY_TDM_TX_6 85 +#define QUATERNARY_TDM_RX_7 86 +#define QUATERNARY_TDM_TX_7 87 +#define QUINARY_TDM_RX_0 88 +#define QUINARY_TDM_TX_0 89 +#define QUINARY_TDM_RX_1 90 +#define QUINARY_TDM_TX_1 91 +#define QUINARY_TDM_RX_2 92 +#define QUINARY_TDM_TX_2 93 +#define QUINARY_TDM_RX_3 94 +#define QUINARY_TDM_TX_3 95 +#define QUINARY_TDM_RX_4 96 +#define QUINARY_TDM_TX_4 97 +#define QUINARY_TDM_RX_5 98 +#define QUINARY_TDM_TX_5 99 +#define QUINARY_TDM_RX_6 100 +#define QUINARY_TDM_TX_6 101 +#define QUINARY_TDM_RX_7 102 +#define QUINARY_TDM_TX_7 103 +#define DISPLAY_PORT_RX 104 +#define WSA_CODEC_DMA_RX_0 105 +#define WSA_CODEC_DMA_TX_0 106 +#define WSA_CODEC_DMA_RX_1 107 +#define WSA_CODEC_DMA_TX_1 108 +#define WSA_CODEC_DMA_TX_2 109 +#define VA_CODEC_DMA_TX_0 110 +#define VA_CODEC_DMA_TX_1 111 +#define VA_CODEC_DMA_TX_2 112 +#define RX_CODEC_DMA_RX_0 113 +#define TX_CODEC_DMA_TX_0 114 +#define RX_CODEC_DMA_RX_1 115 +#define TX_CODEC_DMA_TX_1 116 +#define RX_CODEC_DMA_RX_2 117 +#define TX_CODEC_DMA_TX_2 118 +#define RX_CODEC_DMA_RX_3 119 +#define TX_CODEC_DMA_TX_3 120 +#define RX_CODEC_DMA_RX_4 121 +#define TX_CODEC_DMA_TX_4 122 +#define RX_CODEC_DMA_RX_5 123 +#define TX_CODEC_DMA_TX_5 124 +#define RX_CODEC_DMA_RX_6 125 +#define RX_CODEC_DMA_RX_7 126 +#define QUINARY_MI2S_RX 127 +#define QUINARY_MI2S_TX 128 + +#define LPASS_CLK_ID_PRI_MI2S_IBIT 1 +#define LPASS_CLK_ID_PRI_MI2S_EBIT 2 +#define LPASS_CLK_ID_SEC_MI2S_IBIT 3 +#define LPASS_CLK_ID_SEC_MI2S_EBIT 4 +#define LPASS_CLK_ID_TER_MI2S_IBIT 5 +#define LPASS_CLK_ID_TER_MI2S_EBIT 6 +#define LPASS_CLK_ID_QUAD_MI2S_IBIT 7 +#define LPASS_CLK_ID_QUAD_MI2S_EBIT 8 +#define LPASS_CLK_ID_SPEAKER_I2S_IBIT 9 +#define LPASS_CLK_ID_SPEAKER_I2S_EBIT 10 +#define LPASS_CLK_ID_SPEAKER_I2S_OSR 11 +#define LPASS_CLK_ID_QUI_MI2S_IBIT 12 +#define LPASS_CLK_ID_QUI_MI2S_EBIT 13 +#define LPASS_CLK_ID_SEN_MI2S_IBIT 14 +#define LPASS_CLK_ID_SEN_MI2S_EBIT 15 +#define LPASS_CLK_ID_INT0_MI2S_IBIT 16 +#define LPASS_CLK_ID_INT1_MI2S_IBIT 17 +#define LPASS_CLK_ID_INT2_MI2S_IBIT 18 +#define LPASS_CLK_ID_INT3_MI2S_IBIT 19 +#define LPASS_CLK_ID_INT4_MI2S_IBIT 20 +#define LPASS_CLK_ID_INT5_MI2S_IBIT 21 +#define LPASS_CLK_ID_INT6_MI2S_IBIT 22 +#define LPASS_CLK_ID_QUI_MI2S_OSR 23 +#define LPASS_CLK_ID_PRI_PCM_IBIT 24 +#define LPASS_CLK_ID_PRI_PCM_EBIT 25 +#define LPASS_CLK_ID_SEC_PCM_IBIT 26 +#define LPASS_CLK_ID_SEC_PCM_EBIT 27 +#define LPASS_CLK_ID_TER_PCM_IBIT 28 +#define LPASS_CLK_ID_TER_PCM_EBIT 29 +#define LPASS_CLK_ID_QUAD_PCM_IBIT 30 +#define LPASS_CLK_ID_QUAD_PCM_EBIT 31 +#define LPASS_CLK_ID_QUIN_PCM_IBIT 32 +#define LPASS_CLK_ID_QUIN_PCM_EBIT 33 +#define LPASS_CLK_ID_QUI_PCM_OSR 34 +#define LPASS_CLK_ID_PRI_TDM_IBIT 35 +#define LPASS_CLK_ID_PRI_TDM_EBIT 36 +#define LPASS_CLK_ID_SEC_TDM_IBIT 37 +#define LPASS_CLK_ID_SEC_TDM_EBIT 38 +#define LPASS_CLK_ID_TER_TDM_IBIT 39 +#define LPASS_CLK_ID_TER_TDM_EBIT 40 +#define LPASS_CLK_ID_QUAD_TDM_IBIT 41 +#define LPASS_CLK_ID_QUAD_TDM_EBIT 42 +#define LPASS_CLK_ID_QUIN_TDM_IBIT 43 +#define LPASS_CLK_ID_QUIN_TDM_EBIT 44 +#define LPASS_CLK_ID_QUIN_TDM_OSR 45 +#define LPASS_CLK_ID_MCLK_1 46 +#define LPASS_CLK_ID_MCLK_2 47 +#define LPASS_CLK_ID_MCLK_3 48 +#define LPASS_CLK_ID_MCLK_4 49 +#define LPASS_CLK_ID_INTERNAL_DIGITAL_CODEC_CORE 50 +#define LPASS_CLK_ID_INT_MCLK_0 51 +#define LPASS_CLK_ID_INT_MCLK_1 52 +#define LPASS_CLK_ID_MCLK_5 53 +#define LPASS_CLK_ID_WSA_CORE_MCLK 54 +#define LPASS_CLK_ID_WSA_CORE_NPL_MCLK 55 +#define LPASS_CLK_ID_VA_CORE_MCLK 56 +#define LPASS_CLK_ID_TX_CORE_MCLK 57 +#define LPASS_CLK_ID_TX_CORE_NPL_MCLK 58 +#define LPASS_CLK_ID_RX_CORE_MCLK 59 +#define LPASS_CLK_ID_RX_CORE_NPL_MCLK 60 +#define LPASS_CLK_ID_VA_CORE_2X_MCLK 61 + +#define LPASS_HW_AVTIMER_VOTE 101 +#define LPASS_HW_MACRO_VOTE 102 +#define LPASS_HW_DCODEC_VOTE 103 + +#define Q6AFE_MAX_CLK_ID 104 + +#define LPASS_CLK_ATTRIBUTE_INVALID 0x0 +#define LPASS_CLK_ATTRIBUTE_COUPLE_NO 0x1 +#define LPASS_CLK_ATTRIBUTE_COUPLE_DIVIDEND 0x2 +#define LPASS_CLK_ATTRIBUTE_COUPLE_DIVISOR 0x3 + +#endif /* __DT_BINDINGS_Q6_AUDIO_PORTS_H__ */ -- cgit v1.2.3 From 36ad9bf1d93d66b901342eab9f8ed6c1537655a6 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 26 Oct 2021 12:16:51 +0100 Subject: ASoC: qdsp6: audioreach: add topology support Add ASoC topology support in audioreach Signed-off-by: Srinivas Kandagatla Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20211026111655.1702-14-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- include/uapi/sound/snd_ar_tokens.h | 208 +++++++ sound/soc/qcom/Kconfig | 1 + sound/soc/qcom/qdsp6/Makefile | 2 +- sound/soc/qcom/qdsp6/audioreach.h | 3 + sound/soc/qcom/qdsp6/q6apm.c | 2 +- sound/soc/qcom/qdsp6/topology.c | 1113 ++++++++++++++++++++++++++++++++++++ 6 files changed, 1327 insertions(+), 2 deletions(-) create mode 100644 include/uapi/sound/snd_ar_tokens.h create mode 100644 sound/soc/qcom/qdsp6/topology.c (limited to 'include') diff --git a/include/uapi/sound/snd_ar_tokens.h b/include/uapi/sound/snd_ar_tokens.h new file mode 100644 index 000000000000..440c0725660b --- /dev/null +++ b/include/uapi/sound/snd_ar_tokens.h @@ -0,0 +1,208 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ + +#ifndef __SND_AR_TOKENS_H__ +#define __SND_AR_TOKENS_H__ + +#define APM_SUB_GRAPH_PERF_MODE_LOW_POWER 0x1 +#define APM_SUB_GRAPH_PERF_MODE_LOW_LATENCY 0x2 + +#define APM_SUB_GRAPH_DIRECTION_TX 0x1 +#define APM_SUB_GRAPH_DIRECTION_RX 0x2 + +/** Scenario ID Audio Playback */ +#define APM_SUB_GRAPH_SID_AUDIO_PLAYBACK 0x1 +/* Scenario ID Audio Record */ +#define APM_SUB_GRAPH_SID_AUDIO_RECORD 0x2 +/* Scenario ID Voice call. */ +#define APM_SUB_GRAPH_SID_VOICE_CALL 0x3 + +/* container capability ID Pre/Post Processing (PP) */ +#define APM_CONTAINER_CAP_ID_PP 0x1 +/* container capability ID Compression/Decompression (CD) */ +#define APM_CONTAINER_CAP_ID_CD 0x2 +/* container capability ID End Point(EP) */ +#define APM_CONTAINER_CAP_ID_EP 0x3 +/* container capability ID Offload (OLC) */ +#define APM_CONTAINER_CAP_ID_OLC 0x4 + +/* container graph position Stream */ +#define APM_CONT_GRAPH_POS_STREAM 0x1 +/* container graph position Per Stream Per Device*/ +#define APM_CONT_GRAPH_POS_PER_STR_PER_DEV 0x2 +/* container graph position Stream-Device */ +#define APM_CONT_GRAPH_POS_STR_DEV 0x3 +/* container graph position Global Device */ +#define APM_CONT_GRAPH_POS_GLOBAL_DEV 0x4 + +#define APM_PROC_DOMAIN_ID_MDSP 0x1 +#define APM_PROC_DOMAIN_ID_ADSP 0x2 +#define APM_PROC_DOMAIN_ID_SDSP 0x4 +#define APM_PROC_DOMAIN_ID_CDSP 0x5 + +#define PCM_INTERLEAVED 1 +#define PCM_DEINTERLEAVED_PACKED 2 +#define PCM_DEINTERLEAVED_UNPACKED 3 +#define AR_I2S_WS_SRC_EXTERNAL 0 +#define AR_I2S_WS_SRC_INTERNAL 1 + +enum ar_event_types { + AR_EVENT_NONE = 0, + AR_PGA_DAPM_EVENT +}; + +/* + * Kcontrol IDs + */ +#define SND_SOC_AR_TPLG_FE_BE_GRAPH_CTL_MIX 256 +#define SND_SOC_AR_TPLG_VOL_CTL 257 + +/** + * %AR_TKN_U32_SUB_GRAPH_INSTANCE_ID: Sub Graph Instance Id + * + * %AR_TKN_U32_SUB_GRAPH_PERF_MODE: Performance mode of subgraph + * APM_SUB_GRAPH_PERF_MODE_LOW_POWER = 1, + * APM_SUB_GRAPH_PERF_MODE_LOW_LATENCY = 2 + * + * %AR_TKN_U32_SUB_GRAPH_DIRECTION: Direction of subgraph + * APM_SUB_GRAPH_DIRECTION_TX = 1, + * APM_SUB_GRAPH_DIRECTION_RX = 2 + * + * %AR_TKN_U32_SUB_GRAPH_SCENARIO_ID: Scenario ID for subgraph + * APM_SUB_GRAPH_SID_AUDIO_PLAYBACK = 1, + * APM_SUB_GRAPH_SID_AUDIO_RECORD = 2, + * APM_SUB_GRAPH_SID_VOICE_CALL = 3 + * + * %AR_TKN_U32_CONTAINER_INSTANCE_ID: Container Instance ID + * + * %AR_TKN_U32_CONTAINER_CAPABILITY_ID: Container capability ID + * APM_CONTAINER_CAP_ID_PP = 1, + * APM_CONTAINER_CAP_ID_CD = 2, + * APM_CONTAINER_CAP_ID_EP = 3, + * APM_CONTAINER_CAP_ID_OLC = 4 + * + * %AR_TKN_U32_CONTAINER_STACK_SIZE: Stack size in the container. + * + * %AR_TKN_U32_CONTAINER_GRAPH_POS: Graph Position + * APM_CONT_GRAPH_POS_STREAM = 1, + * APM_CONT_GRAPH_POS_PER_STR_PER_DEV = 2, + * APM_CONT_GRAPH_POS_STR_DEV = 3, + * APM_CONT_GRAPH_POS_GLOBAL_DEV = 4 + * + * %AR_TKN_U32_CONTAINER_PROC_DOMAIN: Processor domain of container + * APM_PROC_DOMAIN_ID_MDSP = 1, + * APM_PROC_DOMAIN_ID_ADSP = 2, + * APM_PROC_DOMAIN_ID_SDSP = 4, + * APM_PROC_DOMAIN_ID_CDSP = 5 + * + * %AR_TKN_U32_MODULE_ID: Module ID + * + * %AR_TKN_U32_MODULE_INSTANCE_ID: Module Instance ID. + * + * %AR_TKN_U32_MODULE_MAX_IP_PORTS: Module maximum input ports + * + * %AR_TKN_U32_MODULE_MAX_OP_PORTS: Module maximum output ports. + * + * %AR_TKN_U32_MODULE_IN_PORTS: Number of in ports + * + * %AR_TKN_U32_MODULE_OUT_PORTS: Number of out ports. + * + * %AR_TKN_U32_MODULE_SRC_OP_PORT_ID: Source module output port ID + * + * %AR_TKN_U32_MODULE_DST_IN_PORT_ID: Destination module input port ID + * + * %AR_TKN_U32_MODULE_HW_IF_IDX: Interface index types for I2S/LPAIF + * + * %AR_TKN_U32_MODULE_HW_IF_TYPE: Interface type + * LPAIF = 0, + * LPAIF_RXTX = 1, + * LPAIF_WSA = 2, + * LPAIF_VA = 3, + * LPAIF_AXI = 4 + * + * %AR_TKN_U32_MODULE_FMT_INTERLEAVE: PCM Interleaving + * PCM_INTERLEAVED = 1, + * PCM_DEINTERLEAVED_PACKED = 2, + * PCM_DEINTERLEAVED_UNPACKED = 3 + * + * %AR_TKN_U32_MODULE_FMT_DATA: data format + * FIXED POINT = 1, + * IEC60958 PACKETIZED = 3, + * IEC60958 PACKETIZED NON LINEAR = 8, + * COMPR OVER PCM PACKETIZED = 7, + * IEC61937 PACKETIZED = 2, + * GENERIC COMPRESSED = 5 + * + * %AR_TKN_U32_MODULE_FMT_SAMPLE_RATE: sample rate + * + * %AR_TKN_U32_MODULE_FMT_BIT_DEPTH: bit depth + * + * %AR_TKN_U32_MODULE_SD_LINE_IDX: I2S serial data line idx + * I2S_SD0 = 1, + * I2S_SD1 = 2, + * I2S_SD2 = 3, + * I2S_SD3 = 4, + * I2S_QUAD01 = 5, + * I2S_QUAD23 = 6, + * I2S_6CHS = 7, + * I2S_8CHS = 8 + * + * %AR_TKN_U32_MODULE_WS_SRC: Word Select Source + * AR_I2S_WS_SRC_EXTERNAL = 0, + * AR_I2S_WS_SRC_INTERNAL = 1, + * + * %AR_TKN_U32_MODULE_FRAME_SZ_FACTOR: Frame size factor + * + * %AR_TKN_U32_MODULE_LOG_CODE: Log Module Code + * + * %AR_TKN_U32_MODULE_LOG_TAP_POINT_ID: logging tap point of this module + * + * %AR_TKN_U32_MODULE_LOG_MODE: logging mode + * LOG_WAIT = 0, + * LOG_IMMEDIATELY = 1 + * + * %AR_TKN_DAI_INDEX: dai index + * + */ + +/* DAI Tokens */ +#define AR_TKN_DAI_INDEX 1 +/* SUB GRAPH Tokens */ +#define AR_TKN_U32_SUB_GRAPH_INSTANCE_ID 2 +#define AR_TKN_U32_SUB_GRAPH_PERF_MODE 3 +#define AR_TKN_U32_SUB_GRAPH_DIRECTION 4 +#define AR_TKN_U32_SUB_GRAPH_SCENARIO_ID 5 + +/* Container Tokens */ +#define AR_TKN_U32_CONTAINER_INSTANCE_ID 100 +#define AR_TKN_U32_CONTAINER_CAPABILITY_ID 101 +#define AR_TKN_U32_CONTAINER_STACK_SIZE 102 +#define AR_TKN_U32_CONTAINER_GRAPH_POS 103 +#define AR_TKN_U32_CONTAINER_PROC_DOMAIN 104 + +/* Module Tokens */ +#define AR_TKN_U32_MODULE_ID 200 +#define AR_TKN_U32_MODULE_INSTANCE_ID 201 +#define AR_TKN_U32_MODULE_MAX_IP_PORTS 202 +#define AR_TKN_U32_MODULE_MAX_OP_PORTS 203 +#define AR_TKN_U32_MODULE_IN_PORTS 204 +#define AR_TKN_U32_MODULE_OUT_PORTS 205 +#define AR_TKN_U32_MODULE_SRC_OP_PORT_ID 206 +#define AR_TKN_U32_MODULE_DST_IN_PORT_ID 207 +#define AR_TKN_U32_MODULE_SRC_INSTANCE_ID 208 +#define AR_TKN_U32_MODULE_DST_INSTANCE_ID 209 + + +#define AR_TKN_U32_MODULE_HW_IF_IDX 250 +#define AR_TKN_U32_MODULE_HW_IF_TYPE 251 +#define AR_TKN_U32_MODULE_FMT_INTERLEAVE 252 +#define AR_TKN_U32_MODULE_FMT_DATA 253 +#define AR_TKN_U32_MODULE_FMT_SAMPLE_RATE 254 +#define AR_TKN_U32_MODULE_FMT_BIT_DEPTH 255 +#define AR_TKN_U32_MODULE_SD_LINE_IDX 256 +#define AR_TKN_U32_MODULE_WS_SRC 257 +#define AR_TKN_U32_MODULE_FRAME_SZ_FACTOR 258 +#define AR_TKN_U32_MODULE_LOG_CODE 259 +#define AR_TKN_U32_MODULE_LOG_TAP_POINT_ID 260 +#define AR_TKN_U32_MODULE_LOG_MODE 261 + +#endif /* __SND_AR_TOKENS_H__ */ diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index 6aaf36b44725..75f447de1faf 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -101,6 +101,7 @@ config SND_SOC_QDSP6 select SND_SOC_QDSP6_ROUTING select SND_SOC_QDSP6_ASM select SND_SOC_QDSP6_ASM_DAI + select SND_SOC_TOPOLOGY select SND_SOC_QDSP6_APM help To add support for MSM QDSP6 Soc Audio. diff --git a/sound/soc/qcom/qdsp6/Makefile b/sound/soc/qcom/qdsp6/Makefile index 1a0803d97eec..766b824f6597 100644 --- a/sound/soc/qcom/qdsp6/Makefile +++ b/sound/soc/qcom/qdsp6/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only snd-q6dsp-common-objs := q6dsp-common.o q6dsp-lpass-ports.o q6dsp-lpass-clocks.o -snd-q6apm-objs := q6apm.o audioreach.o +snd-q6apm-objs := q6apm.o audioreach.o topology.o obj-$(CONFIG_SND_SOC_QDSP6_COMMON) += snd-q6dsp-common.o obj-$(CONFIG_SND_SOC_QDSP6_CORE) += q6core.o diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h index 821e1511140f..4f693a2660b5 100644 --- a/sound/soc/qcom/qdsp6/audioreach.h +++ b/sound/soc/qcom/qdsp6/audioreach.h @@ -693,6 +693,9 @@ void *audioreach_alloc_pkt(int payload_size, uint32_t opcode, void *audioreach_alloc_graph_pkt(struct q6apm *apm, struct list_head *sg_list, int graph_id); +/* Topology specific */ +int audioreach_tplg_init(struct snd_soc_component *component); + /* Module specific */ void audioreach_graph_free_buf(struct q6apm_graph *graph); int audioreach_map_memory_regions(struct q6apm_graph *graph, diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index 046f8f2e0c58..13598ef5bacb 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -694,7 +694,7 @@ EXPORT_SYMBOL_GPL(q6apm_graph_flush); static int q6apm_audio_probe(struct snd_soc_component *component) { - return 0; + return audioreach_tplg_init(component); } static void q6apm_audio_remove(struct snd_soc_component *component) diff --git a/sound/soc/qcom/qdsp6/topology.c b/sound/soc/qcom/qdsp6/topology.c new file mode 100644 index 000000000000..f31895379925 --- /dev/null +++ b/sound/soc/qcom/qdsp6/topology.c @@ -0,0 +1,1113 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2020, Linaro Limited + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "q6apm.h" +#include "audioreach.h" + +struct snd_ar_control { + u32 sgid; /* Sub Graph ID */ + struct snd_soc_component *scomp; +}; + +static struct audioreach_graph_info *audioreach_tplg_alloc_graph_info(struct q6apm *apm, + uint32_t graph_id, + bool *found) +{ + struct audioreach_graph_info *info; + int ret; + + mutex_lock(&apm->lock); + info = idr_find(&apm->graph_info_idr, graph_id); + mutex_unlock(&apm->lock); + + if (info) { + *found = true; + return info; + } + + *found = false; + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(&info->sg_list); + + mutex_lock(&apm->lock); + ret = idr_alloc(&apm->graph_info_idr, info, graph_id, graph_id + 1, GFP_KERNEL); + mutex_unlock(&apm->lock); + + if (ret < 0) { + dev_err(apm->dev, "Failed to allocate Graph ID (%x)\n", graph_id); + kfree(info); + return ERR_PTR(ret); + } + + info->id = ret; + + return info; +} + +static void audioreach_tplg_add_sub_graph(struct audioreach_sub_graph *sg, + struct audioreach_graph_info *info) +{ + list_add_tail(&sg->node, &info->sg_list); + sg->info = info; + info->num_sub_graphs++; +} + +static struct audioreach_sub_graph *audioreach_tplg_alloc_sub_graph(struct q6apm *apm, + uint32_t sub_graph_id, + bool *found) +{ + struct audioreach_sub_graph *sg; + int ret; + + if (!sub_graph_id) + return ERR_PTR(-EINVAL); + + /* Find if there is already a matching sub-graph */ + mutex_lock(&apm->lock); + sg = idr_find(&apm->sub_graphs_idr, sub_graph_id); + mutex_unlock(&apm->lock); + + if (sg) { + *found = true; + return sg; + } + + *found = false; + sg = kzalloc(sizeof(*sg), GFP_KERNEL); + if (!sg) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(&sg->container_list); + + mutex_lock(&apm->lock); + ret = idr_alloc(&apm->sub_graphs_idr, sg, sub_graph_id, sub_graph_id + 1, GFP_KERNEL); + mutex_unlock(&apm->lock); + + if (ret < 0) { + dev_err(apm->dev, "Failed to allocate Sub-Graph Instance ID (%x)\n", sub_graph_id); + kfree(sg); + return ERR_PTR(ret); + } + + sg->sub_graph_id = ret; + + return sg; +} + +static struct audioreach_container *audioreach_tplg_alloc_container(struct q6apm *apm, + struct audioreach_sub_graph *sg, + uint32_t container_id, + bool *found) +{ + struct audioreach_container *cont; + int ret; + + if (!container_id) + return ERR_PTR(-EINVAL); + + mutex_lock(&apm->lock); + cont = idr_find(&apm->containers_idr, container_id); + mutex_unlock(&apm->lock); + + if (cont) { + *found = true; + return cont; + } + *found = false; + + cont = kzalloc(sizeof(*cont), GFP_KERNEL); + if (!cont) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(&cont->modules_list); + + mutex_lock(&apm->lock); + ret = idr_alloc(&apm->containers_idr, cont, container_id, container_id + 1, GFP_KERNEL); + mutex_unlock(&apm->lock); + + if (ret < 0) { + dev_err(apm->dev, "Failed to allocate Container Instance ID (%x)\n", container_id); + kfree(cont); + return ERR_PTR(ret); + } + + cont->container_id = ret; + cont->sub_graph = sg; + /* add to container list */ + list_add_tail(&cont->node, &sg->container_list); + sg->num_containers++; + + return cont; +} + +static struct audioreach_module *audioreach_tplg_alloc_module(struct q6apm *apm, + struct audioreach_container *cont, + struct snd_soc_dapm_widget *w, + uint32_t module_id, bool *found) +{ + struct audioreach_module *mod; + int ret; + + mutex_lock(&apm->lock); + mod = idr_find(&apm->modules_idr, module_id); + mutex_unlock(&apm->lock); + + if (mod) { + *found = true; + return mod; + } + *found = false; + mod = kzalloc(sizeof(*mod), GFP_KERNEL); + if (!mod) + return ERR_PTR(-ENOMEM); + + mutex_lock(&apm->lock); + if (!module_id) { /* alloc module id dynamically */ + ret = idr_alloc_cyclic(&apm->modules_idr, mod, + AR_MODULE_DYNAMIC_INSTANCE_ID_START, + AR_MODULE_DYNAMIC_INSTANCE_ID_END, GFP_KERNEL); + } else { + ret = idr_alloc(&apm->modules_idr, mod, module_id, module_id + 1, GFP_KERNEL); + } + mutex_unlock(&apm->lock); + + if (ret < 0) { + dev_err(apm->dev, "Failed to allocate Module Instance ID (%x)\n", module_id); + kfree(mod); + return ERR_PTR(ret); + } + + mod->instance_id = ret; + /* add to module list */ + list_add_tail(&mod->node, &cont->modules_list); + mod->container = cont; + mod->widget = w; + cont->num_modules++; + + return mod; +} + +static struct snd_soc_tplg_vendor_array *audioreach_get_sg_array( + struct snd_soc_tplg_private *private) +{ + struct snd_soc_tplg_vendor_array *sg_array = NULL; + bool found = false; + int sz; + + for (sz = 0; !found && (sz < le32_to_cpu(private->size)); ) { + struct snd_soc_tplg_vendor_value_elem *sg_elem; + int tkn_count = 0; + + sg_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz); + sg_elem = sg_array->value; + sz = sz + le32_to_cpu(sg_array->size); + while (!found && tkn_count <= (le32_to_cpu(sg_array->num_elems) - 1)) { + switch (le32_to_cpu(sg_elem->token)) { + case AR_TKN_U32_SUB_GRAPH_INSTANCE_ID: + found = true; + break; + default: + break; + } + tkn_count++; + sg_elem++; + } + } + + if (found) + return sg_array; + + return NULL; +} + +static struct snd_soc_tplg_vendor_array *audioreach_get_cont_array( + struct snd_soc_tplg_private *private) +{ + struct snd_soc_tplg_vendor_array *cont_array = NULL; + bool found = false; + int sz; + + for (sz = 0; !found && (sz < le32_to_cpu(private->size)); ) { + struct snd_soc_tplg_vendor_value_elem *cont_elem; + int tkn_count = 0; + + cont_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz); + cont_elem = cont_array->value; + sz = sz + le32_to_cpu(cont_array->size); + while (!found && tkn_count <= (le32_to_cpu(cont_array->num_elems) - 1)) { + switch (le32_to_cpu(cont_elem->token)) { + case AR_TKN_U32_CONTAINER_INSTANCE_ID: + found = true; + break; + default: + break; + } + tkn_count++; + cont_elem++; + } + } + + if (found) + return cont_array; + + return NULL; +} + +static struct snd_soc_tplg_vendor_array *audioreach_get_module_array( + struct snd_soc_tplg_private *private) +{ + struct snd_soc_tplg_vendor_array *mod_array = NULL; + bool found = false; + int sz = 0; + + for (sz = 0; !found && (sz < le32_to_cpu(private->size)); ) { + struct snd_soc_tplg_vendor_value_elem *mod_elem; + int tkn_count = 0; + + mod_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz); + mod_elem = mod_array->value; + sz = sz + le32_to_cpu(mod_array->size); + while (!found && tkn_count <= (le32_to_cpu(mod_array->num_elems) - 1)) { + switch (le32_to_cpu(mod_elem->token)) { + case AR_TKN_U32_MODULE_INSTANCE_ID: + found = true; + break; + default: + break; + } + tkn_count++; + mod_elem++; + } + } + + if (found) + return mod_array; + + return NULL; +} + +static struct audioreach_sub_graph *audioreach_parse_sg_tokens(struct q6apm *apm, + struct snd_soc_tplg_private *private) +{ + struct snd_soc_tplg_vendor_value_elem *sg_elem; + struct snd_soc_tplg_vendor_array *sg_array; + struct audioreach_graph_info *info = NULL; + int graph_id, sub_graph_id, tkn_count = 0; + struct audioreach_sub_graph *sg; + bool found; + + sg_array = audioreach_get_sg_array(private); + sg_elem = sg_array->value; + + while (tkn_count <= (le32_to_cpu(sg_array->num_elems) - 1)) { + switch (le32_to_cpu(sg_elem->token)) { + case AR_TKN_U32_SUB_GRAPH_INSTANCE_ID: + sub_graph_id = le32_to_cpu(sg_elem->value); + sg = audioreach_tplg_alloc_sub_graph(apm, sub_graph_id, &found); + if (IS_ERR(sg)) { + return sg; + } else if (found) { + /* Already parsed data for this sub-graph */ + return sg; + } + break; + case AR_TKN_DAI_INDEX: + /* Sub graph is associated with predefined graph */ + graph_id = le32_to_cpu(sg_elem->value); + info = audioreach_tplg_alloc_graph_info(apm, graph_id, &found); + if (IS_ERR(info)) + return ERR_CAST(info); + break; + case AR_TKN_U32_SUB_GRAPH_PERF_MODE: + sg->perf_mode = le32_to_cpu(sg_elem->value); + break; + case AR_TKN_U32_SUB_GRAPH_DIRECTION: + sg->direction = le32_to_cpu(sg_elem->value); + break; + case AR_TKN_U32_SUB_GRAPH_SCENARIO_ID: + sg->scenario_id = le32_to_cpu(sg_elem->value); + break; + default: + dev_err(apm->dev, "Not a valid token %d for graph\n", sg_elem->token); + break; + + } + tkn_count++; + sg_elem++; + } + + /* Sub graph is associated with predefined graph */ + if (info) + audioreach_tplg_add_sub_graph(sg, info); + + return sg; +} + +static struct audioreach_container *audioreach_parse_cont_tokens(struct q6apm *apm, + struct audioreach_sub_graph *sg, + struct snd_soc_tplg_private *private) +{ + struct snd_soc_tplg_vendor_value_elem *cont_elem; + struct snd_soc_tplg_vendor_array *cont_array; + struct audioreach_container *cont; + int container_id, tkn_count = 0; + bool found = false; + + cont_array = audioreach_get_cont_array(private); + cont_elem = cont_array->value; + + while (tkn_count <= (le32_to_cpu(cont_array->num_elems) - 1)) { + switch (le32_to_cpu(cont_elem->token)) { + case AR_TKN_U32_CONTAINER_INSTANCE_ID: + container_id = le32_to_cpu(cont_elem->value); + cont = audioreach_tplg_alloc_container(apm, sg, container_id, &found); + if (IS_ERR(cont) || found)/* Error or Already parsed container data */ + return cont; + break; + case AR_TKN_U32_CONTAINER_CAPABILITY_ID: + cont->capability_id = le32_to_cpu(cont_elem->value); + break; + case AR_TKN_U32_CONTAINER_STACK_SIZE: + cont->stack_size = le32_to_cpu(cont_elem->value); + break; + case AR_TKN_U32_CONTAINER_GRAPH_POS: + cont->graph_pos = le32_to_cpu(cont_elem->value); + break; + case AR_TKN_U32_CONTAINER_PROC_DOMAIN: + cont->proc_domain = le32_to_cpu(cont_elem->value); + break; + default: + dev_err(apm->dev, "Not a valid token %d for graph\n", cont_elem->token); + break; + + } + tkn_count++; + cont_elem++; + } + + return cont; +} + +static struct audioreach_module *audioreach_parse_common_tokens(struct q6apm *apm, + struct audioreach_container *cont, + struct snd_soc_tplg_private *private, + struct snd_soc_dapm_widget *w) +{ + uint32_t max_ip_port = 0, max_op_port = 0, in_port = 0, out_port = 0; + uint32_t src_mod_inst_id = 0, src_mod_op_port_id = 0; + uint32_t dst_mod_inst_id = 0, dst_mod_ip_port_id = 0; + int module_id = 0, instance_id = 0, tkn_count = 0; + struct snd_soc_tplg_vendor_value_elem *mod_elem; + struct snd_soc_tplg_vendor_array *mod_array; + struct audioreach_module *mod = NULL; + bool found; + + mod_array = audioreach_get_module_array(private); + mod_elem = mod_array->value; + + while (tkn_count <= (le32_to_cpu(mod_array->num_elems) - 1)) { + switch (le32_to_cpu(mod_elem->token)) { + /* common module info */ + case AR_TKN_U32_MODULE_ID: + module_id = le32_to_cpu(mod_elem->value); + break; + case AR_TKN_U32_MODULE_INSTANCE_ID: + instance_id = le32_to_cpu(mod_elem->value); + mod = audioreach_tplg_alloc_module(apm, cont, w, + instance_id, &found); + if (IS_ERR(mod)) { + return mod; + } else if (found) { + dev_err(apm->dev, "Duplicate Module Instance ID 0x%08x found\n", + instance_id); + return ERR_PTR(-EINVAL); + } + + break; + case AR_TKN_U32_MODULE_MAX_IP_PORTS: + max_ip_port = le32_to_cpu(mod_elem->value); + break; + case AR_TKN_U32_MODULE_MAX_OP_PORTS: + max_op_port = le32_to_cpu(mod_elem->value); + break; + case AR_TKN_U32_MODULE_IN_PORTS: + in_port = le32_to_cpu(mod_elem->value); + break; + case AR_TKN_U32_MODULE_OUT_PORTS: + out_port = le32_to_cpu(mod_elem->value); + break; + case AR_TKN_U32_MODULE_SRC_OP_PORT_ID: + src_mod_op_port_id = le32_to_cpu(mod_elem->value); + break; + case AR_TKN_U32_MODULE_SRC_INSTANCE_ID: + src_mod_inst_id = le32_to_cpu(mod_elem->value); + break; + case AR_TKN_U32_MODULE_DST_INSTANCE_ID: + dst_mod_inst_id = le32_to_cpu(mod_elem->value); + break; + case AR_TKN_U32_MODULE_DST_IN_PORT_ID: + dst_mod_ip_port_id = le32_to_cpu(mod_elem->value); + + default: + break; + + } + tkn_count++; + mod_elem++; + } + + if (mod) { + mod->module_id = module_id; + mod->max_ip_port = max_ip_port; + mod->max_op_port = max_op_port; + mod->in_port = in_port; + mod->out_port = out_port; + mod->src_mod_inst_id = src_mod_inst_id; + mod->src_mod_op_port_id = src_mod_op_port_id; + mod->dst_mod_inst_id = dst_mod_inst_id; + mod->dst_mod_ip_port_id = dst_mod_ip_port_id; + } + + return mod; +} + +static int audioreach_widget_load_module_common(struct snd_soc_component *component, + int index, struct snd_soc_dapm_widget *w, + struct snd_soc_tplg_dapm_widget *tplg_w) +{ + struct q6apm *apm = dev_get_drvdata(component->dev); + struct audioreach_container *cont; + struct audioreach_sub_graph *sg; + struct audioreach_module *mod; + struct snd_soc_dobj *dobj; + + sg = audioreach_parse_sg_tokens(apm, &tplg_w->priv); + if (IS_ERR(sg)) + return PTR_ERR(sg); + + cont = audioreach_parse_cont_tokens(apm, sg, &tplg_w->priv); + if (IS_ERR(cont)) + return PTR_ERR(cont); + + mod = audioreach_parse_common_tokens(apm, cont, &tplg_w->priv, w); + if (IS_ERR(mod)) + return PTR_ERR(mod); + + dobj = &w->dobj; + dobj->private = mod; + + return 0; +} + +static int audioreach_widget_load_enc_dec_cnv(struct snd_soc_component *component, + int index, struct snd_soc_dapm_widget *w, + struct snd_soc_tplg_dapm_widget *tplg_w) +{ + struct snd_soc_tplg_vendor_value_elem *mod_elem; + struct snd_soc_tplg_vendor_array *mod_array; + struct audioreach_module *mod; + struct snd_soc_dobj *dobj; + int tkn_count = 0; + int ret; + + ret = audioreach_widget_load_module_common(component, index, w, tplg_w); + if (ret) + return ret; + + dobj = &w->dobj; + mod = dobj->private; + mod_array = audioreach_get_module_array(&tplg_w->priv); + mod_elem = mod_array->value; + + while (tkn_count <= (le32_to_cpu(mod_array->num_elems) - 1)) { + switch (le32_to_cpu(mod_elem->token)) { + case AR_TKN_U32_MODULE_FMT_INTERLEAVE: + mod->interleave_type = le32_to_cpu(mod_elem->value); + break; + case AR_TKN_U32_MODULE_FMT_SAMPLE_RATE: + mod->rate = le32_to_cpu(mod_elem->value); + break; + case AR_TKN_U32_MODULE_FMT_BIT_DEPTH: + mod->bit_depth = le32_to_cpu(mod_elem->value); + break; + default: + break; + } + tkn_count++; + mod_elem++; + } + + return 0; +} + +static int audioreach_widget_log_module_load(struct audioreach_module *mod, + struct snd_soc_tplg_vendor_array *mod_array) +{ + struct snd_soc_tplg_vendor_value_elem *mod_elem; + int tkn_count = 0; + + mod_elem = mod_array->value; + + while (tkn_count <= (le32_to_cpu(mod_array->num_elems) - 1)) { + switch (le32_to_cpu(mod_elem->token)) { + + case AR_TKN_U32_MODULE_LOG_CODE: + mod->log_code = le32_to_cpu(mod_elem->value); + break; + case AR_TKN_U32_MODULE_LOG_TAP_POINT_ID: + mod->log_tap_point_id = le32_to_cpu(mod_elem->value); + break; + case AR_TKN_U32_MODULE_LOG_MODE: + mod->log_mode = le32_to_cpu(mod_elem->value); + break; + default: + break; + } + tkn_count++; + mod_elem++; + } + + return 0; +} + +static int audioreach_widget_dma_module_load(struct audioreach_module *mod, + struct snd_soc_tplg_vendor_array *mod_array) +{ + struct snd_soc_tplg_vendor_value_elem *mod_elem; + int tkn_count = 0; + + mod_elem = mod_array->value; + + while (tkn_count <= (le32_to_cpu(mod_array->num_elems) - 1)) { + switch (le32_to_cpu(mod_elem->token)) { + case AR_TKN_U32_MODULE_HW_IF_IDX: + mod->hw_interface_idx = le32_to_cpu(mod_elem->value); + break; + case AR_TKN_U32_MODULE_FMT_DATA: + mod->data_format = le32_to_cpu(mod_elem->value); + break; + case AR_TKN_U32_MODULE_HW_IF_TYPE: + mod->hw_interface_type = le32_to_cpu(mod_elem->value); + break; + default: + break; + } + tkn_count++; + mod_elem++; + } + + return 0; +} + +static int audioreach_widget_i2s_module_load(struct audioreach_module *mod, + struct snd_soc_tplg_vendor_array *mod_array) +{ + struct snd_soc_tplg_vendor_value_elem *mod_elem; + int tkn_count = 0; + + mod_elem = mod_array->value; + + while (tkn_count <= (le32_to_cpu(mod_array->num_elems) - 1)) { + switch (le32_to_cpu(mod_elem->token)) { + case AR_TKN_U32_MODULE_HW_IF_IDX: + mod->hw_interface_idx = le32_to_cpu(mod_elem->value); + break; + case AR_TKN_U32_MODULE_FMT_DATA: + mod->data_format = le32_to_cpu(mod_elem->value); + break; + case AR_TKN_U32_MODULE_HW_IF_TYPE: + mod->hw_interface_type = le32_to_cpu(mod_elem->value); + break; + case AR_TKN_U32_MODULE_SD_LINE_IDX: + mod->sd_line_idx = le32_to_cpu(mod_elem->value); + break; + case AR_TKN_U32_MODULE_WS_SRC: + mod->ws_src = le32_to_cpu(mod_elem->value); + break; + default: + break; + } + tkn_count++; + mod_elem++; + } + + return 0; +} + +static int audioreach_widget_load_buffer(struct snd_soc_component *component, + int index, struct snd_soc_dapm_widget *w, + struct snd_soc_tplg_dapm_widget *tplg_w) +{ + struct snd_soc_tplg_vendor_array *mod_array; + struct audioreach_module *mod; + struct snd_soc_dobj *dobj; + int ret; + + ret = audioreach_widget_load_module_common(component, index, w, tplg_w); + if (ret) + return ret; + + dobj = &w->dobj; + mod = dobj->private; + + mod_array = audioreach_get_module_array(&tplg_w->priv); + + switch (mod->module_id) { + case MODULE_ID_CODEC_DMA_SINK: + case MODULE_ID_CODEC_DMA_SOURCE: + audioreach_widget_dma_module_load(mod, mod_array); + break; + case MODULE_ID_DATA_LOGGING: + audioreach_widget_log_module_load(mod, mod_array); + break; + case MODULE_ID_I2S_SINK: + case MODULE_ID_I2S_SOURCE: + audioreach_widget_i2s_module_load(mod, mod_array); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int audioreach_widget_load_mixer(struct snd_soc_component *component, + int index, struct snd_soc_dapm_widget *w, + struct snd_soc_tplg_dapm_widget *tplg_w) +{ + struct snd_soc_tplg_vendor_value_elem *w_elem; + struct snd_soc_tplg_vendor_array *w_array; + struct snd_ar_control *scontrol; + struct snd_soc_dobj *dobj; + int tkn_count = 0; + + w_array = &tplg_w->priv.array[0]; + + scontrol = kzalloc(sizeof(*scontrol), GFP_KERNEL); + if (!scontrol) + return -ENOMEM; + + scontrol->scomp = component; + dobj = &w->dobj; + dobj->private = scontrol; + + w_elem = w_array->value; + while (tkn_count <= (le32_to_cpu(w_array->num_elems) - 1)) { + switch (le32_to_cpu(w_elem->token)) { + case AR_TKN_U32_SUB_GRAPH_INSTANCE_ID: + scontrol->sgid = le32_to_cpu(w_elem->value); + break; + default: /* ignore other tokens */ + break; + } + tkn_count++; + w_elem++; + } + + return 0; +} + +static int audioreach_pga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) + +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_component *c = snd_soc_dapm_to_component(dapm); + struct audioreach_module *mod = w->dobj.private; + struct q6apm *apm = dev_get_drvdata(c->dev); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* apply gain after power up of widget */ + audioreach_gain_set_vol_ctrl(apm, mod, mod->gain); + break; + default: + break; + } + + return 0; +} + +static const struct snd_soc_tplg_widget_events audioreach_widget_ops[] = { + { AR_PGA_DAPM_EVENT, audioreach_pga_event }, +}; + +static int audioreach_widget_load_pga(struct snd_soc_component *component, + int index, struct snd_soc_dapm_widget *w, + struct snd_soc_tplg_dapm_widget *tplg_w) +{ + struct audioreach_module *mod; + struct snd_soc_dobj *dobj; + int ret; + + ret = audioreach_widget_load_module_common(component, index, w, tplg_w); + if (ret) + return ret; + + dobj = &w->dobj; + mod = dobj->private; + mod->gain = VOL_CTRL_DEFAULT_GAIN; + + ret = snd_soc_tplg_widget_bind_event(w, audioreach_widget_ops, + ARRAY_SIZE(audioreach_widget_ops), + le16_to_cpu(tplg_w->event_type)); + if (ret) { + dev_err(component->dev, "matching event handlers NOT found for %d\n", + le16_to_cpu(tplg_w->event_type)); + return -EINVAL; + } + + return 0; +} + +static int audioreach_widget_ready(struct snd_soc_component *component, + int index, struct snd_soc_dapm_widget *w, + struct snd_soc_tplg_dapm_widget *tplg_w) +{ + switch (w->id) { + case snd_soc_dapm_aif_in: + case snd_soc_dapm_aif_out: + audioreach_widget_load_buffer(component, index, w, tplg_w); + break; + case snd_soc_dapm_decoder: + case snd_soc_dapm_encoder: + case snd_soc_dapm_src: + audioreach_widget_load_enc_dec_cnv(component, index, w, tplg_w); + break; + case snd_soc_dapm_buffer: + audioreach_widget_load_buffer(component, index, w, tplg_w); + break; + case snd_soc_dapm_mixer: + return audioreach_widget_load_mixer(component, index, w, tplg_w); + case snd_soc_dapm_pga: + return audioreach_widget_load_pga(component, index, w, tplg_w); + case snd_soc_dapm_dai_link: + case snd_soc_dapm_scheduler: + case snd_soc_dapm_out_drv: + default: + dev_err(component->dev, "Widget type (0x%x) not yet supported\n", w->id); + break; + } + + return 0; +} + +static int audioreach_widget_unload(struct snd_soc_component *scomp, + struct snd_soc_dobj *dobj) +{ + struct snd_soc_dapm_widget *w = container_of(dobj, struct snd_soc_dapm_widget, dobj); + struct q6apm *apm = dev_get_drvdata(scomp->dev); + struct audioreach_container *cont; + struct audioreach_module *mod; + + mod = dobj->private; + cont = mod->container; + + if (w->id == snd_soc_dapm_mixer) { + /* virtual widget */ + kfree(dobj->private); + return 0; + } + + mutex_lock(&apm->lock); + idr_remove(&apm->modules_idr, mod->instance_id); + cont->num_modules--; + + list_del(&mod->node); + kfree(mod); + /* Graph Info has N sub-graphs, sub-graph has N containers, Container has N Modules */ + if (list_empty(&cont->modules_list)) { /* if no modules in the container then remove it */ + struct audioreach_sub_graph *sg = cont->sub_graph; + + idr_remove(&apm->containers_idr, cont->container_id); + list_del(&cont->node); + sg->num_containers--; + kfree(cont); + /* check if there are no more containers in the sub graph and remove it */ + if (list_empty(&sg->container_list)) { + struct audioreach_graph_info *info = sg->info; + + idr_remove(&apm->sub_graphs_idr, sg->sub_graph_id); + list_del(&sg->node); + info->num_sub_graphs--; + kfree(sg); + /* Check if there are no more sub-graphs left then remove graph info */ + if (list_empty(&info->sg_list)) { + idr_remove(&apm->graph_info_idr, info->id); + kfree(info); + } + } + } + + mutex_unlock(&apm->lock); + + return 0; +} + +static struct audioreach_module *audioreach_find_widget(struct snd_soc_component *comp, + const char *name) +{ + struct q6apm *apm = dev_get_drvdata(comp->dev); + struct audioreach_module *module; + int id; + + idr_for_each_entry(&apm->modules_idr, module, id) { + if (!strcmp(name, module->widget->name)) + return module; + } + + return NULL; +} + +static int audioreach_route_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dapm_route *route) +{ + struct audioreach_module *src, *sink; + + src = audioreach_find_widget(scomp, route->source); + sink = audioreach_find_widget(scomp, route->sink); + + if (src && sink) { + src->dst_mod_inst_id = sink->instance_id; + sink->src_mod_inst_id = src->instance_id; + } + + return 0; +} + +static int audioreach_route_unload(struct snd_soc_component *scomp, + struct snd_soc_dobj *dobj) +{ + return 0; +} + +static int audioreach_tplg_complete(struct snd_soc_component *component) +{ + /* TBD */ + return 0; +} + +/* DAI link - used for any driver specific init */ +static int audioreach_link_load(struct snd_soc_component *component, int index, + struct snd_soc_dai_link *link, + struct snd_soc_tplg_link_config *cfg) +{ + link->nonatomic = true; + link->dynamic = true; + link->platforms->name = NULL; + link->platforms->of_node = of_get_compatible_child(component->dev->of_node, + "qcom,q6apm-dais"); + return 0; +} + +static int audioreach_get_audio_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); + struct snd_soc_dapm_widget *dw = snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_component *c = snd_soc_dapm_to_component(dapm); + struct snd_ar_control *dapm_scontrol = dw->dobj.private; + struct snd_ar_control *scontrol = mc->dobj.private; + struct q6apm *data = dev_get_drvdata(c->dev); + bool connected; + + connected = q6apm_is_sub_graphs_connected(data, scontrol->sgid, dapm_scontrol->sgid); + if (connected) + ucontrol->value.integer.value[0] = 1; + else + ucontrol->value.integer.value[0] = 0; + + return 0; +} + +static int audioreach_put_audio_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); + struct snd_soc_dapm_widget *dw = snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_component *c = snd_soc_dapm_to_component(dapm); + struct snd_ar_control *dapm_scontrol = dw->dobj.private; + struct snd_ar_control *scontrol = mc->dobj.private; + struct q6apm *data = dev_get_drvdata(c->dev); + + if (ucontrol->value.integer.value[0]) { + q6apm_connect_sub_graphs(data, scontrol->sgid, dapm_scontrol->sgid, true); + snd_soc_dapm_mixer_update_power(dapm, kcontrol, 1, NULL); + } else { + q6apm_connect_sub_graphs(data, scontrol->sgid, dapm_scontrol->sgid, false); + snd_soc_dapm_mixer_update_power(dapm, kcontrol, 0, NULL); + } + return 0; +} + +static int audioreach_get_vol_ctrl_audio_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *dw = snd_soc_dapm_kcontrol_widget(kcontrol); + struct audioreach_module *mod = dw->dobj.private; + + ucontrol->value.integer.value[0] = mod->gain; + + return 0; +} + +static int audioreach_put_vol_ctrl_audio_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *dw = snd_soc_dapm_kcontrol_widget(kcontrol); + struct audioreach_module *mod = dw->dobj.private; + + mod->gain = ucontrol->value.integer.value[0]; + + return 1; +} + +static int audioreach_control_load_mix(struct snd_soc_component *scomp, + struct snd_ar_control *scontrol, + struct snd_kcontrol_new *kc, + struct snd_soc_tplg_ctl_hdr *hdr) +{ + struct snd_soc_tplg_vendor_value_elem *c_elem; + struct snd_soc_tplg_vendor_array *c_array; + struct snd_soc_tplg_mixer_control *mc; + int tkn_count = 0; + + mc = container_of(hdr, struct snd_soc_tplg_mixer_control, hdr); + c_array = (struct snd_soc_tplg_vendor_array *)mc->priv.data; + + c_elem = c_array->value; + + while (tkn_count <= (le32_to_cpu(c_array->num_elems) - 1)) { + switch (le32_to_cpu(c_elem->token)) { + case AR_TKN_U32_SUB_GRAPH_INSTANCE_ID: + scontrol->sgid = le32_to_cpu(c_elem->value); + break; + default: + /* Ignore other tokens */ + break; + } + c_elem++; + tkn_count++; + } + + return 0; +} + +static int audioreach_control_load(struct snd_soc_component *scomp, int index, + struct snd_kcontrol_new *kc, + struct snd_soc_tplg_ctl_hdr *hdr) +{ + struct snd_ar_control *scontrol; + struct soc_mixer_control *sm; + struct snd_soc_dobj *dobj; + int ret = 0; + + scontrol = kzalloc(sizeof(*scontrol), GFP_KERNEL); + if (!scontrol) + return -ENOMEM; + + scontrol->scomp = scomp; + + switch (le32_to_cpu(hdr->ops.get)) { + case SND_SOC_AR_TPLG_FE_BE_GRAPH_CTL_MIX: + sm = (struct soc_mixer_control *)kc->private_value; + dobj = &sm->dobj; + ret = audioreach_control_load_mix(scomp, scontrol, kc, hdr); + break; + case SND_SOC_AR_TPLG_VOL_CTL: + sm = (struct soc_mixer_control *)kc->private_value; + dobj = &sm->dobj; + break; + default: + dev_warn(scomp->dev, "control type not supported %d:%d:%d\n", + hdr->ops.get, hdr->ops.put, hdr->ops.info); + kfree(scontrol); + return -EINVAL; + } + + dobj->private = scontrol; + return ret; +} + +static int audioreach_control_unload(struct snd_soc_component *scomp, + struct snd_soc_dobj *dobj) +{ + struct snd_ar_control *scontrol = dobj->private; + + kfree(scontrol); + + return 0; +} + +static const struct snd_soc_tplg_kcontrol_ops audioreach_io_ops[] = { + {SND_SOC_AR_TPLG_FE_BE_GRAPH_CTL_MIX, audioreach_get_audio_mixer, + audioreach_put_audio_mixer, snd_soc_info_volsw}, + {SND_SOC_AR_TPLG_VOL_CTL, audioreach_get_vol_ctrl_audio_mixer, + audioreach_put_vol_ctrl_audio_mixer, snd_soc_info_volsw}, +}; + +static struct snd_soc_tplg_ops audioreach_tplg_ops = { + .io_ops = audioreach_io_ops, + .io_ops_count = ARRAY_SIZE(audioreach_io_ops), + + .control_load = audioreach_control_load, + .control_unload = audioreach_control_unload, + + .widget_ready = audioreach_widget_ready, + .widget_unload = audioreach_widget_unload, + + .complete = audioreach_tplg_complete, + .link_load = audioreach_link_load, + + .dapm_route_load = audioreach_route_load, + .dapm_route_unload = audioreach_route_unload, +}; + +int audioreach_tplg_init(struct snd_soc_component *component) +{ + struct snd_soc_card *card = component->card; + struct device *dev = component->dev; + const struct firmware *fw; + char *tplg_fw_name; + int ret; + + /* Inline with Qualcomm UCM configs and linux-firmware path */ + tplg_fw_name = kasprintf(GFP_KERNEL, "qcom/%s/%s-tplg.bin", card->driver_name, card->name); + if (!tplg_fw_name) + return -ENOMEM; + + ret = request_firmware(&fw, tplg_fw_name, dev); + if (ret < 0) { + dev_err(dev, "tplg firmware loading %s failed %d \n", tplg_fw_name, ret); + goto err; + } + + ret = snd_soc_tplg_component_load(component, &audioreach_tplg_ops, fw); + if (ret < 0) { + dev_err(dev, "tplg component load failed%d\n", ret); + ret = -EINVAL; + } + + release_firmware(fw); +err: + kfree(tplg_fw_name); + + return ret; +} +EXPORT_SYMBOL_GPL(audioreach_tplg_init); -- cgit v1.2.3