From patchwork Fri Aug 11 13:29:44 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Srinivas Kandagatla X-Patchwork-Id: 109876 Delivered-To: patch@linaro.org Received: by 10.140.95.78 with SMTP id h72csp921353qge; Fri, 11 Aug 2017 06:33:50 -0700 (PDT) X-Received: by 10.99.129.195 with SMTP id t186mr15518693pgd.287.1502458430324; Fri, 11 Aug 2017 06:33:50 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1502458430; cv=none; d=google.com; s=arc-20160816; b=ASMp/Wm2whvszasVWFP651z06/SDTsx4zfkq3E53yNWk41R7qi99fZCvtV6fFcgwHt 8/YwTwy1Utg6w8MXD16pazBXr3Ub2gRmKvlVxm+1Ouda+V3yC0LeARg0ECFRcvNO1lR9 nAAof9nNB7B7Y4+lANy/OySWMd015CTRJ/iZ0nEzzScH9k85OAmMP//8uN5mefUR6BiU eR/gulcgF5NmgGOjm4b873XdVWNvlzhOABXh7OKV8boFnJMpVjaRN8sIpxdLEOXjrTf+ 0qCRbipqUZYJbUQNh3VHvXHkqM6HyDQkS5PFfnYHl9s04Haiotl0/j8ZdNC08jZLMJWD bWAA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=15+LdxMBk+ztRKsVNKpPJ4dFUUphMb/TnHrHu4IBiF4=; b=XkedNPqvwlu2ApSd+y2H2BaS3gG3GwxJSgfMEOp7tKv6vKbEtQ/h2KmB2hrQm0UhZs iWjTh0DsVUg2T12nwYshT0L1Fy3/IQJzVPc4mTcKyMyxeCGEU6juwG2/IwYdH6X/JCg+ LPWl+YR9dZ3+bQPPmdMqiIcD+/MjqDr2aQvO2gLTzxRkeFNXCBxrG+UdoffKPwAu6WT1 uvZjcpMOPYfdPxN5WaXtt8rpGmdAMFJndxejd9/t3APfp9mtf+gP41U657oviuvX3dJO 8yp/xrVnIgIOK5ITJ3pZDmEaYp2cxHF8Rt1xxaoifAI3cDDR4KlsVRtgGU2dy7B6BN7j DKnw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=hMhwxXUk; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id 71si574007plc.68.2017.08.11.06.33.49; Fri, 11 Aug 2017 06:33:50 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=hMhwxXUk; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753473AbdHKNdr (ORCPT + 25 others); Fri, 11 Aug 2017 09:33:47 -0400 Received: from mail-io0-f181.google.com ([209.85.223.181]:36737 "EHLO mail-io0-f181.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753116AbdHKNaE (ORCPT ); Fri, 11 Aug 2017 09:30:04 -0400 Received: by mail-io0-f181.google.com with SMTP id g35so20049600ioi.3 for ; Fri, 11 Aug 2017 06:30:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=15+LdxMBk+ztRKsVNKpPJ4dFUUphMb/TnHrHu4IBiF4=; b=hMhwxXUkhB+rbkUAwAOrQJH7DCMj/4846FIGphSxj/HdRC0nEp3PuscX0DqzZA4S5G TZ6HaTBrEvq2PDmJDkIUa8T0B8bIsAWi/21uibl9ZQMFE1NTkIfhgHeb3tCJA6LM3QkH Fb0B6QUUuPClkkPRP8LXzhVa299aSfpOmpnqM= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=15+LdxMBk+ztRKsVNKpPJ4dFUUphMb/TnHrHu4IBiF4=; b=I3woBiupmgO/A9lRD0fb7i+H1AXkZRSa64nEQMeaIev0UW2TtIj0Hd1y+qHLVbCjSR 66y1WEuSbpcUIFKtz+dCEnO+qaEXwZJVSaoRVSIlfJIMUDKVfWbootsrSezg2uHccdKz a3xuQCDLx8bx2bltks3L72VEWuOh+0RUxOganZfdGiX/kihz5gpeMbpF+/hF7LxVTEuP cfil6dkB1Uqt68ojxVPpkBzFd/pPIUHogX9dweJqPKe0es9cGbxJI8VcJu99K69ATXaK 9FZTZqkeqt3UcC2Yg5PZPeu1N8oFD7RvPAUxDgK4r4ONwaI3pQNZvV07s8TU0VehgE9B Muyw== X-Gm-Message-State: AIVw1104eXv6ePxZsoQ7DpUaEEP8y0O/qUadGp1Ggw7tZZBdkXsvxd3Q zdH4OdpgTcWjMmkc X-Received: by 10.107.155.69 with SMTP id d66mr14751006ioe.75.1502458203544; Fri, 11 Aug 2017 06:30:03 -0700 (PDT) Received: from localhost.localdomain (static.8.26.4.46.clients.your-server.de. [46.4.26.8]) by smtp.gmail.com with ESMTPSA id y72sm398422iod.40.2017.08.11.06.30.00 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 11 Aug 2017 06:30:03 -0700 (PDT) From: srinivas.kandagatla@linaro.org To: Mark Brown , Banajit Goswami , alsa-devel@alsa-project.org Cc: Takashi Iwai , Patrick Lai , linux-kernel@vger.kernel.org, kwestfie@codeaurora.org, linux-arm-msm@vger.kernel.org, lkasam@qti.qualcomm.com, sboyd@codeaurora.org, Srinivas Kandagatla , Andy Gross Subject: [RFC PATCH 1/9] soc: qcom: add support APR driver Date: Fri, 11 Aug 2017 15:29:44 +0200 Message-Id: <20170811132952.32572-2-srinivas.kandagatla@linaro.org> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20170811132952.32572-1-srinivas.kandagatla@linaro.org> References: <20170811132952.32572-1-srinivas.kandagatla@linaro.org> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Srinivas Kandagatla This patch adds support to APR (Asynchronous Packet Router) driver, which is used communication between application processor and QDSP to use services on QDSP like Audio and others. CC: Andy Gross Signed-off-by: Srinivas Kandagatla --- .../devicetree/bindings/soc/qcom/qcom,apr.txt | 66 ++++ drivers/soc/qcom/Kconfig | 8 + drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/apr.c | 406 +++++++++++++++++++++ include/linux/soc/qcom/apr.h | 163 +++++++++ 5 files changed, 644 insertions(+) create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,apr.txt create mode 100644 drivers/soc/qcom/apr.c create mode 100644 include/linux/soc/qcom/apr.h -- 2.9.3 diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,apr.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,apr.txt new file mode 100644 index 0000000..6fdb8d9 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,apr.txt @@ -0,0 +1,66 @@ +Qualcomm APR (Asynchronous Packet Router) binding + +This binding describes the Qualcomm APR. APR is a IPC protocol for +communication between Application processor and QDSP. APR is mainly +used for audio/voice services on the QDSP. + +- compatible: + Usage: required + Value type: + Definition: must be "qcom,apr-" example: "qcom,apr-msm8996" + + +- qcom,smd-channel: + Usage: required + Value type: + Definition: standard SMD property specifying the SMD channel used for + communication with the APR on QDSP. + Should be "apr_audio_svc". += APR DEVICES +Each subnode of APR node represents services/devices that are only available +when APR is active. + += EXAMPLE +The following example represents a QDSP based sound card on a MSM8996 device +which uses apr as communication between Apps and QDSP. + + apr { + compatible = "qcom,apr-msm8996"; + qcom,smd-channels = "apr_audio_svc"; + + pcm: pcm0 { + compatible = "qcom,msm-pcm-dsp"; + ... + }; + + routing:routing { + compatible = "qcom,msm-pcm-routing"; + #sound-dai-cells = <0>; + ... + }; + + hdmi_dai: dai_hdmi { + compatible = "qcom,msm-dai-q6-hdmi"; + #sound-dai-cells = <0>; + ... + }; + + snd { + compatible = "qcom,snd-apq8096"; + qcom,model = "DB820c"; + ... + }; + + adm { + compatible = "qcom,q6adm"; + }; + + asm { + compatible = "qcom,q6asm"; + }; + + afe: afe { + compatible = "qcom,q6afe-v2"; + }; + + }; diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 6c5ba05..1c0e64a 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -115,3 +115,11 @@ config BUS_TOPOLOGY_ADHOC directionality of connections by explicitly listing device connections thus avoiding illegal routes. +config QCOM_APR + tristate "Qualcomm APR (Asynchronous Packet Router)" + depends on (RPMSG_QCOM_SMD || RPMSG_QCOM_GLINK_RPM) + help + Enable APR IPC protocol support between + application processor and QDSP6. APR is + used by audio driver to configure QDSP6 + ASM, ADM and AFE modules. diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index a946e41..78fa1d8 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o obj-$(CONFIG_MSM_BUS_SCALING) += msm_bus/ obj-$(CONFIG_BUS_TOPOLOGY_ADHOC) += msm_bus/ +obj-$(CONFIG_QCOM_APR) += apr.o diff --git a/drivers/soc/qcom/apr.c b/drivers/soc/qcom/apr.c new file mode 100644 index 0000000..0f10cf2 --- /dev/null +++ b/drivers/soc/qcom/apr.c @@ -0,0 +1,406 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct apr_ops { + int (*get_data_src)(struct apr_hdr *hdr); +}; + +struct apr { + struct rpmsg_endpoint *ch; + struct device *dev; + struct mutex svcs_lock; + struct list_head svcs; + int svc_cnt; + int dest_id; + int client_id; + const struct apr_ops *ops; +}; + +struct apr_svc_table { + char name[64]; + int id; + int client_id; +}; + +static const struct apr_svc_table svc_tbl_qdsp6[] = { + { "AFE", APR_SVC_AFE, APR_CLIENT_AUDIO, }, + { "ASM", APR_SVC_ASM, APR_CLIENT_AUDIO, }, + { "ADM", APR_SVC_ADM, APR_CLIENT_AUDIO, }, + { "CORE", APR_SVC_ADSP_CORE, APR_CLIENT_AUDIO, }, + { "TEST", APR_SVC_TEST_CLIENT, APR_CLIENT_AUDIO, }, + { "MVM", APR_SVC_ADSP_MVM, APR_CLIENT_AUDIO, }, + { "CVS", APR_SVC_ADSP_CVS, APR_CLIENT_AUDIO, }, + { "CVP", APR_SVC_ADSP_CVP, APR_CLIENT_AUDIO, }, + { "USM", APR_SVC_USM, APR_CLIENT_AUDIO, }, + { "VIDC", APR_SVC_VIDC, }, + { "LSM", APR_SVC_LSM, APR_CLIENT_AUDIO, }, +}; + +int apr_send_pkt(void *handle, uint32_t *buf) +{ + struct apr_svc *svc = handle; + struct apr *apr = dev_get_drvdata(svc->dev->parent); + struct apr_hdr *hdr; + unsigned long flags; + int ret; + + if (!handle || !buf) { + dev_err(svc->dev, "APR: Wrong parameters\n"); + return -EINVAL; + } + + spin_lock_irqsave(&svc->w_lock, flags); + + hdr = (struct apr_hdr *)buf; + hdr->src_domain = APR_DOMAIN_APPS; + hdr->src_svc = svc->id; + hdr->dest_domain = svc->dest_domain; + hdr->dest_svc = svc->id; + + ret = rpmsg_send(apr->ch, buf, hdr->pkt_size); + if (ret) { + dev_err(svc->dev, "Unable to send APR pkt %d\n", + hdr->pkt_size); + } else { + ret = hdr->pkt_size; + } + + spin_unlock_irqrestore(&svc->w_lock, flags); + + return ret; +} +EXPORT_SYMBOL_GPL(apr_send_pkt); + +static int apr_find_svc(const char *svc_name, int domain_id, int *client_id, + int *svc_id) +{ + struct apr_svc_table *tbl = (struct apr_svc_table *)&svc_tbl_qdsp6; + int i, size = ARRAY_SIZE(svc_tbl_qdsp6); + + for (i = 0; i < size; i++) { + if (!strcmp(svc_name, tbl[i].name)) { + *client_id = tbl[i].client_id; + *svc_id = tbl[i].id; + return 0; + } + } + + pr_err("%s: APR: Wrong svc name %s\n", __func__, svc_name); + return -EINVAL; +} + +struct apr_svc *apr_register(struct device *dev, char *dest, char *svc_name, + apr_fn svc_fn, uint32_t src_port, void *priv) +{ + int client_id = 0; + int svc_id = 0; + int domain_id = 0; + int temp_port = 0; + struct apr_svc *p, *svc = NULL; + struct apr *apr = dev_get_drvdata(dev->parent); + + if (!apr || !dest || !svc_name || !svc_fn) + return NULL; + + if (!strcmp(dest, "ADSP")) { + domain_id = APR_DOMAIN_ADSP; + } else { + dev_err(dev, "APR: wrong destination\n"); + goto done; + } + + if (apr_find_svc(svc_name, domain_id, &client_id, &svc_id)) { + dev_err(dev, "%s: apr_find_svc failed\n", __func__); + goto done; + } + + list_for_each_entry(p, &apr->svcs, node) { + if (svc_id == p->id) { + svc = p; + break; + } + } + + if (!svc) { + svc = kzalloc(sizeof(*svc), GFP_KERNEL); + if (!svc) + return NULL; + + mutex_init(&svc->m_lock); + spin_lock_init(&svc->w_lock); + } + + mutex_lock(&svc->m_lock); + + svc->priv = priv; + svc->id = svc_id; + svc->dest_id = apr->dest_id; + svc->client_id = client_id; + svc->dest_domain = domain_id; + svc->dev = dev; + if (src_port != 0xFFFFFFFF) { + temp_port = ((src_port >> 8) * 8) + (src_port & 0xFF); + if (temp_port >= APR_MAX_PORTS || temp_port < 0) { + mutex_unlock(&svc->m_lock); + return NULL; + } + if (!svc->port_cnt && !svc->svc_cnt) + apr->svc_cnt++; + svc->port_cnt++; + svc->port_fn[temp_port] = svc_fn; + svc->port_priv[temp_port] = priv; + } else { + if (!svc->fn) { + if (!svc->port_cnt && !svc->svc_cnt) + apr->svc_cnt++; + svc->fn = svc_fn; + if (svc->port_cnt) + svc->svc_cnt++; + } + } + + mutex_unlock(&svc->m_lock); + mutex_lock(&apr->svcs_lock); + list_add_tail(&svc->node, &apr->svcs); + mutex_unlock(&apr->svcs_lock); +done: + return svc; +} +EXPORT_SYMBOL_GPL(apr_register); + +static int qcom_rpmsg_q6_callback(struct rpmsg_device *rpdev, void *buf, + int len, void *priv, u32 addr) +{ + struct apr *apr = dev_get_drvdata(&rpdev->dev); + struct apr_client_data data; + struct apr_svc *p, *c_svc = NULL; + struct apr_hdr *hdr; + uint16_t hdr_size; + uint16_t msg_type; + uint16_t ver; + uint16_t src; + uint16_t svc; + int temp_port = 0; + + if (!buf || len <= APR_HDR_SIZE) { + pr_info("APR: Improper apr pkt received:%p %d\n", buf, len); + return -EINVAL; + } + + hdr = buf; + ver = (hdr->hdr_field) & 0x000F; + if (ver > APR_PKT_VER + 1) { + pr_info("APR: Wrong version: %d\n", ver); + return -EINVAL; + } + + hdr_size = hdr->hdr_field; + hdr_size = ((hdr_size & 0x00F0) >> 0x4) * 4; + if (hdr_size < APR_HDR_SIZE) { + dev_err(apr->dev, "APR: Wrong hdr size:%d\n", hdr_size); + return -EINVAL; + } + + if (hdr->pkt_size < APR_HDR_SIZE) { + dev_err(apr->dev, "APR: Wrong paket size\n"); + return -EINVAL; + } + msg_type = hdr->hdr_field; + msg_type = (msg_type >> 0x08) & 0x0003; + if (msg_type >= APR_MSG_TYPE_MAX && msg_type != APR_BASIC_RSP_RESULT) { + dev_err(apr->dev, "APR: Wrong message type: %d\n", msg_type); + return -EINVAL; + } + + if (hdr->src_domain >= APR_DOMAIN_MAX || + hdr->dest_domain >= APR_DOMAIN_MAX || + hdr->src_svc >= APR_SVC_MAX || hdr->dest_svc >= APR_SVC_MAX) { + dev_err(apr->dev, "APR: Wrong APR header\n"); + return -EINVAL; + } + + svc = hdr->dest_svc; + src = apr->ops->get_data_src(hdr); + if (src == APR_DEST_MAX) + return -EINVAL; + + list_for_each_entry(p, &apr->svcs, node) { + if (svc == p->id) { + c_svc = p; + break; + } + } + + if (!c_svc) { + dev_err(apr->dev, "APR: service is not registered\n"); + return -EINVAL; + } + + data.payload_size = hdr->pkt_size - hdr_size; + data.opcode = hdr->opcode; + data.src = src; + data.src_port = hdr->src_port; + data.dest_port = hdr->dest_port; + data.token = hdr->token; + data.msg_type = msg_type; + + if (data.payload_size > 0) + data.payload = (char *)hdr + hdr_size; + + temp_port = ((data.dest_port >> 8) * 8) + (data.dest_port & 0xFF); + if (c_svc->port_cnt && c_svc->port_fn[temp_port]) + c_svc->port_fn[temp_port] (&data, c_svc->port_priv[temp_port]); + else if (c_svc->fn) + c_svc->fn(&data, c_svc->priv); + else + dev_err(apr->dev, "APR: Rxed a packet for NULL callback\n"); + + return 0; +} + +int apr_deregister(void *handle) +{ + struct apr_svc *svc = handle; + struct apr *apr = dev_get_drvdata(svc->dev->parent); + uint16_t client_id; + + if (!handle) + return -EINVAL; + + mutex_lock(&svc->m_lock); + client_id = svc->client_id; + + if (svc->port_cnt > 0 || svc->svc_cnt > 0) { + if (svc->port_cnt) + svc->port_cnt--; + else if (svc->svc_cnt) + svc->svc_cnt--; + if (!svc->port_cnt && !svc->svc_cnt) + apr->svc_cnt--; + } else if (apr->svc_cnt > 0) { + apr->svc_cnt--; + } + + if (!svc->port_cnt && !svc->svc_cnt) { + mutex_unlock(&svc->m_lock); + + mutex_lock(&apr->svcs_lock); + list_del(&svc->node); + mutex_unlock(&apr->svcs_lock); + kfree(svc); + return 0; + } + + mutex_unlock(&svc->m_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(apr_deregister); + +static int qcom_rpmsg_q6_probe(struct rpmsg_device *rpdev) +{ + struct device *dev = &rpdev->dev; + const char *name; + struct apr *apr; + int ret; + + apr = devm_kzalloc(dev, sizeof(*apr), GFP_KERNEL); + if (!apr) + return -ENOMEM; + + apr->ops = of_device_get_match_data(dev); + if (!apr->ops) + return -ENODEV; + + ret = of_property_read_string(dev->of_node, "qcom,smd-channels", &name); + if (ret) { + dev_err(dev, "qcom,smd-channels name not found\n"); + return -EINVAL; + } + + if (!strcmp(name, "apr_audio_svc")) { + apr->client_id = APR_CLIENT_AUDIO; + } else { + dev_err(dev, "Unsupported srv name\n"); + return -EINVAL; + } + + ret = of_property_read_u32(dev->parent->of_node, "qcom,smd-edge", + &apr->dest_id); + if (ret) { + dev_err(dev, "qcom,smd-edge not found\n"); + return -EINVAL; + } + + dev_set_drvdata(dev, apr); + apr->ch = rpdev->ept; + apr->dev = dev; + INIT_LIST_HEAD(&apr->svcs); + + dev_info(dev, "APR service up for apr id %d dest id %d\n", + apr->client_id, apr->dest_id); + + return of_platform_populate(dev->of_node, NULL, NULL, dev); +} + +static void qcom_rpmsg_q6_remove(struct rpmsg_device *rpdev) +{ + of_platform_depopulate(&rpdev->dev); +} + +static int apr_v2_get_data_src(struct apr_hdr *hdr) +{ + if (hdr->src_domain == APR_DOMAIN_MODEM) + return APR_DEST_MODEM; + else if (hdr->src_domain == APR_DOMAIN_ADSP) + return APR_DEST_QDSP6; + + pr_err("APR: Pkt from wrong source: %d\n", hdr->src_domain); + + return APR_DEST_MAX; +} + +static const struct apr_ops apr_v2_ops = { + .get_data_src = apr_v2_get_data_src, +}; + +static const struct of_device_id qcom_rpmsg_q6_of_match[] = { + { .compatible = "qcom,apr-msm8996", .data = &apr_v2_ops}, + {} +}; + +static struct rpmsg_driver qcom_rpmsg_q6_driver = { + .probe = qcom_rpmsg_q6_probe, + .remove = qcom_rpmsg_q6_remove, + .callback = qcom_rpmsg_q6_callback, + .drv = { + .name = "qcom_rpmsg_q6", + .owner = THIS_MODULE, + .of_match_table = qcom_rpmsg_q6_of_match, + }, +}; + +module_rpmsg_driver(qcom_rpmsg_q6_driver); + +MODULE_DESCRIPTION("Qualcomm rpmsg backed apr driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/soc/qcom/apr.h b/include/linux/soc/qcom/apr.h new file mode 100644 index 0000000..02175e6 --- /dev/null +++ b/include/linux/soc/qcom/apr.h @@ -0,0 +1,163 @@ +/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __APR_H_ +#define __APR_H_ + +#include + +/* APR Client IDs */ +#define APR_CLIENT_AUDIO 0x0 +#define APR_CLIENT_VOICE 0x1 +#define APR_CLIENT_MAX 0x2 + +#define APR_DL_SMD 0 +#define APR_DL_MAX 1 + +#define APR_DEST_MODEM 0 +#define APR_DEST_QDSP6 1 +#define APR_DEST_MAX 2 +#define APR_MAX_BUF 8192 + +#define APR_HDR_LEN(hdr_len) ((hdr_len)/4) +#define APR_PKT_SIZE(hdr_len, payload_len) ((hdr_len) + (payload_len)) +#define APR_HDR_FIELD(msg_type, hdr_len, ver)\ + (((msg_type & 0x3) << 8) | ((hdr_len & 0xF) << 4) | (ver & 0xF)) + +#define APR_HDR_SIZE sizeof(struct apr_hdr) +#define APR_SEQ_CMD_HDR_FIELD APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, \ + APR_HDR_LEN(APR_HDR_SIZE), \ + APR_PKT_VER) + +/* Version */ +#define APR_PKT_VER 0x0 + +/* Command and Response Types */ +#define APR_MSG_TYPE_EVENT 0x0 +#define APR_MSG_TYPE_CMD_RSP 0x1 +#define APR_MSG_TYPE_SEQ_CMD 0x2 +#define APR_MSG_TYPE_NSEQ_CMD 0x3 +#define APR_MSG_TYPE_MAX 0x04 + +/* APR Basic Response Message */ +#define APR_BASIC_RSP_RESULT 0x000110E8 +#define APR_RSP_ACCEPTED 0x000100BE + +/* Domain IDs */ +#define APR_DOMAIN_SIM 0x1 +#define APR_DOMAIN_PC 0x2 +#define APR_DOMAIN_MODEM 0x3 +#define APR_DOMAIN_ADSP 0x4 +#define APR_DOMAIN_APPS 0x5 +#define APR_DOMAIN_MAX 0x6 + +/* ADSP service IDs */ +#define APR_SVC_TEST_CLIENT 0x2 +#define APR_SVC_ADSP_CORE 0x3 +#define APR_SVC_AFE 0x4 +#define APR_SVC_VSM 0x5 +#define APR_SVC_VPM 0x6 +#define APR_SVC_ASM 0x7 +#define APR_SVC_ADM 0x8 +#define APR_SVC_ADSP_MVM 0x09 +#define APR_SVC_ADSP_CVS 0x0A +#define APR_SVC_ADSP_CVP 0x0B +#define APR_SVC_USM 0x0C +#define APR_SVC_LSM 0x0D +#define APR_SVC_VIDC 0x16 +#define APR_SVC_MAX 0x17 + +/* Modem Service IDs */ +#define APR_SVC_MVS 0x3 +#define APR_SVC_MVM 0x4 +#define APR_SVC_CVS 0x5 +#define APR_SVC_CVP 0x6 +#define APR_SVC_SRD 0x7 + +/* APR Port IDs */ +#define APR_MAX_PORTS 0x80 +#define APR_NAME_MAX 0x40 +#define RESET_EVENTS 0x000130D7 + +struct apr_hdr { + uint16_t hdr_field; + uint16_t pkt_size; + uint8_t src_svc; + uint8_t src_domain; + uint16_t src_port; + uint8_t dest_svc; + uint8_t dest_domain; + uint16_t dest_port; + uint32_t token; + uint32_t opcode; +}; + +struct apr_client_data { + uint16_t payload_size; + uint16_t hdr_len; + uint16_t msg_type; + uint16_t src; + uint16_t dest_svc; + uint16_t src_port; + uint16_t dest_port; + uint32_t token; + uint32_t opcode; + void *payload; +}; + +typedef int32_t (*apr_fn) (struct apr_client_data *data, void *priv); +struct apr_svc { + uint16_t id; + uint16_t dest_id; + uint16_t client_id; + uint16_t dest_domain; + uint8_t rvd; + uint8_t port_cnt; + uint8_t svc_cnt; + + apr_fn port_fn[APR_MAX_PORTS]; + void *port_priv[APR_MAX_PORTS]; + apr_fn fn; + void *priv; + struct mutex m_lock; + spinlock_t w_lock; + struct device *dev; + struct list_head node; +}; + +#if IS_ENABLED(CONFIG_QCOM_APR) +struct apr_svc *apr_register(struct device *dev, char *dest, char *svc_name, + apr_fn svc_fn, uint32_t src_port, void *priv); +int apr_send_pkt(void *handle, uint32_t *buf); +int apr_deregister(void *handle); + +#else + +static inline struct apr_svc *apr_register(struct device *dev, char *dest, + char *svc_name, apr_fn svc_fn, + uint32_t src_port, void *priv) +{ + return ERR_PTR(-ENOSYS); +} + +static inline int apr_send_pkt(void *handle, uint32_t *buf) +{ + return -ENOSYS; +} + +static inline int apr_deregister(void *handle) +{ + return -ENOSYS; +} + +#endif /* CONFIG_QCOM_APR */ +#endif /* __APR_H_ */ From patchwork Fri Aug 11 13:29:45 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Srinivas Kandagatla X-Patchwork-Id: 109864 Delivered-To: patch@linaro.org Received: by 10.140.95.78 with SMTP id h72csp916660qge; Fri, 11 Aug 2017 06:30:15 -0700 (PDT) X-Received: by 10.99.146.67 with SMTP id s3mr15190592pgn.130.1502458215305; Fri, 11 Aug 2017 06:30:15 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1502458215; cv=none; d=google.com; s=arc-20160816; b=zlScTYVhc44uriIG4vT8emA+/lyUXVl91v6nvOA6C4qQXb/p59ejxNcsfPUeKyuKdQ zCxluUFMvTl8cYP3gGMFLE04GdGNQgCg94uqFzfSLl8xG6CLc8aasO+KrAnO7vqaN1oV hhOQGtQS6LH3HK2I/4JPsWYntPKu52lBQUIQVGZYRGqbFvA40ZfFo8S0GCxlRMokmpYn ZD/jea7FaMoQMGUVQeA9IPYP+HZZ5c59lFwrUVr4Ad1tG6TgEmXjTETOX36V2VQJRw/a +HJSY4fxvBVWd9wpQtu4/iCCPrkHbbmH9iy8gL7G1OHGWKjQ+wt4GIHCJTNvwHEauKlo J5Bg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=YGHgzNnX+qSB6xTDU4kos2wEJqEQXAnc5B8aPRWLHic=; b=diT5OIpEQqT+5nwO2/LA9/qyu/xeQc3E2Z8n1wdPPMeuUNDCqKw678ciIcNVN7nkbU 9f7J1CnPXxWCw0RJnNye+rAJjP/cUAQnjT+LBOg+98+DOo3a9zyI6Qn6wHRHTI+yIZQp vzJcV/9uAW84yxdGbN5AImjUGs0snGdj1KYn73BLCzPJahC3fmQIg0f1GYLWxJ0QaibA ZOKPlqBMCbQoJlK0R5M8+rZS9e5CY1sejR9/3ADTC1re0uoLNRgtq33TFXjj+T+/eH4m SP4WBwZoyFx7sV9zR4cWpDe39xsS4J41Y2r5rGdBPrXq15c77RR45uibLEFW8ljJAe/0 ij8A== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=eqT98Tty; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id e7si561984plk.249.2017.08.11.06.30.14; Fri, 11 Aug 2017 06:30:15 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=eqT98Tty; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753236AbdHKNaL (ORCPT + 25 others); Fri, 11 Aug 2017 09:30:11 -0400 Received: from mail-io0-f173.google.com ([209.85.223.173]:37190 "EHLO mail-io0-f173.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753201AbdHKNaH (ORCPT ); Fri, 11 Aug 2017 09:30:07 -0400 Received: by mail-io0-f173.google.com with SMTP id c74so20006358iod.4 for ; Fri, 11 Aug 2017 06:30:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=YGHgzNnX+qSB6xTDU4kos2wEJqEQXAnc5B8aPRWLHic=; b=eqT98TtyrCZNQP9F2/XHDaRzubbzomcBn8ikFwlNazLoajA6KO4CoWjIK5sBlcIBxh p3ZH9Zr5qV0p7Nc0yfkpv2b30pdPQL2rAxJ0RwAq7+zzM7CcQ76DwDEkE/LbUBrrPx+S Xq9578HRfSr0JoI7RiUqJQAvTpl3Fkz2msNM8= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=YGHgzNnX+qSB6xTDU4kos2wEJqEQXAnc5B8aPRWLHic=; b=Kn+/VBVv7CNzngf6sl78MQoUrfjzjy4ZjHXwNYZsK7RJsPAaJJPxWkJS/tzUa74Ul5 XjremDEzQmbCAngQhb3J7sVQuBHezDMJIO0uRwtHnhq3AVzrSt9G0qMd+WUJ+MjRtS6r ZlfYLgtLtiQYpBwRz1OyQopCy2vMSx3piHD1gCXpN2iMekPwCDZwDBLhzg7Zgjj6GjaK wUi43rXOa3nzfDkre9x41unQa3v1GmYf5RrOXHnYs072yUzzNIumurtLsTw1pueE4uKs G1ZCWMOMxebWAe3fjFeKfWry8VRKNhsXOLRTDl59u1YUZWnwtE29NA4PhOzBqJ4WWAuu rR/w== X-Gm-Message-State: AHYfb5jNR1Xk1Cs4EQQ57dyNr5UDwHcQlxHDr8gkdbRXXy3hwvrXwrZB IAD1XYAXGZyJ5k2w X-Received: by 10.107.188.197 with SMTP id m188mr12373234iof.239.1502458206171; Fri, 11 Aug 2017 06:30:06 -0700 (PDT) Received: from localhost.localdomain (static.8.26.4.46.clients.your-server.de. [46.4.26.8]) by smtp.gmail.com with ESMTPSA id y72sm398422iod.40.2017.08.11.06.30.03 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 11 Aug 2017 06:30:05 -0700 (PDT) From: srinivas.kandagatla@linaro.org To: Mark Brown , Banajit Goswami , alsa-devel@alsa-project.org Cc: Takashi Iwai , Patrick Lai , linux-kernel@vger.kernel.org, kwestfie@codeaurora.org, linux-arm-msm@vger.kernel.org, lkasam@qti.qualcomm.com, sboyd@codeaurora.org, Srinivas Kandagatla Subject: [RFC PATCH 2/9] ASoC: qcom: qdsp6v2: Add support to Q6AFE Date: Fri, 11 Aug 2017 15:29:45 +0200 Message-Id: <20170811132952.32572-3-srinivas.kandagatla@linaro.org> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20170811132952.32572-1-srinivas.kandagatla@linaro.org> References: <20170811132952.32572-1-srinivas.kandagatla@linaro.org> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Srinivas Kandagatla This patch adds support to q6 AFE (Audio Front End) module on Q6DSP. AFE module sits right at the other end of cpu where the codec/audio devices are connected. AFE provides abstraced interfaces to both hardware and virtual devices. Each AFE tx/rx port can be configured to connect to one of the hardware devices like codec, hdmi, slimbus, i2s and so on. AFE services include starting, stopping, and if needed, any configurations of the ports. Signed-off-by: Srinivas Kandagatla --- .../devicetree/bindings/sound/qcom,q6afe.txt | 15 + include/dt-bindings/sound/qcom,afe.h | 8 + sound/soc/qcom/Kconfig | 13 + sound/soc/qcom/Makefile | 3 + sound/soc/qcom/qdsp6v2/Makefile | 1 + sound/soc/qcom/qdsp6v2/common.h | 225 ++++++++++ sound/soc/qcom/qdsp6v2/q6afe-v2.h | 62 +++ sound/soc/qcom/qdsp6v2/q6afe.c | 499 +++++++++++++++++++++ 8 files changed, 826 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/qcom,q6afe.txt create mode 100644 include/dt-bindings/sound/qcom,afe.h create mode 100644 sound/soc/qcom/qdsp6v2/Makefile create mode 100644 sound/soc/qcom/qdsp6v2/common.h create mode 100644 sound/soc/qcom/qdsp6v2/q6afe-v2.h create mode 100644 sound/soc/qcom/qdsp6v2/q6afe.c -- 2.9.3 diff --git a/Documentation/devicetree/bindings/sound/qcom,q6afe.txt b/Documentation/devicetree/bindings/sound/qcom,q6afe.txt new file mode 100644 index 0000000..0ae5771 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/qcom,q6afe.txt @@ -0,0 +1,15 @@ +Qualcomm Q6AFE (Q6 Audio FrontEnd) binding + +This bindings describe the Qualcomm Q6 AFE module on QDSP, +which is used by audio drivers. + +- compatible: + + Usage: required + Value type: + Definition: must be "qcom,q6afe-v" example: "qcom,q6asm-v2" + += EXAMPLE + q6afe { + compatible = "qcom,q6afe-v2"; + }; diff --git a/include/dt-bindings/sound/qcom,afe.h b/include/dt-bindings/sound/qcom,afe.h new file mode 100644 index 0000000..2e58cde --- /dev/null +++ b/include/dt-bindings/sound/qcom,afe.h @@ -0,0 +1,8 @@ +#ifndef __DT_AFE_H__ +#define __DT_AFE_H__ + +/* Audio Front End (AFE) Ports */ +#define AFE_PORT_HDMI_RX 8 +#define AFE_PORT_MAX 9 + +#endif diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index 8ec9a07..dad3fa1 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -43,3 +43,16 @@ config SND_SOC_APQ8016_SBC Support for Qualcomm Technologies LPASS audio block in APQ8016 SOC-based systems. Say Y if you want to use audio devices on MI2S. + +config SND_SOC_QDSP6V2_AFE + tristate + default n + +config SND_SOC_QDSP6V2 + tristate "SoC ALSA audio driver for QDSP6V2" + select SND_SOC_QDSP6V2_AFE + help + To add support for MSM QDSP6V2 Soc Audio. + This will enable sound soc platform specific + audio drivers. This includes q6asm, q6adm, + q6afe interfaces to DSP using apr. diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile index 79e5c50..a51bf0a 100644 --- a/sound/soc/qcom/Makefile +++ b/sound/soc/qcom/Makefile @@ -15,3 +15,6 @@ snd-soc-apq8016-sbc-objs := apq8016_sbc.o obj-$(CONFIG_SND_SOC_STORM) += snd-soc-storm.o obj-$(CONFIG_SND_SOC_APQ8016_SBC) += snd-soc-apq8016-sbc.o + +#DSP lib +obj-$(CONFIG_SND_SOC_QDSP6V2) += qdsp6v2/ diff --git a/sound/soc/qcom/qdsp6v2/Makefile b/sound/soc/qcom/qdsp6v2/Makefile new file mode 100644 index 0000000..8069891 --- /dev/null +++ b/sound/soc/qcom/qdsp6v2/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_SND_SOC_QDSP6V2_AFE) += q6afe.o diff --git a/sound/soc/qcom/qdsp6v2/common.h b/sound/soc/qcom/qdsp6v2/common.h new file mode 100644 index 0000000..cbd6e00 --- /dev/null +++ b/sound/soc/qcom/qdsp6v2/common.h @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __Q6DSP_COMMON_H__ +#define __Q6DSP_COMMON_H__ + +/* ERROR CODES */ +/* Success. The operation completed with no errors. */ +#define ADSP_EOK 0x00000000 +/* General failure. */ +#define ADSP_EFAILED 0x00000001 +/* Bad operation parameter. */ +#define ADSP_EBADPARAM 0x00000002 +/* Unsupported routine or operation. */ +#define ADSP_EUNSUPPORTED 0x00000003 +/* Unsupported version. */ +#define ADSP_EVERSION 0x00000004 +/* Unexpected problem encountered. */ +#define ADSP_EUNEXPECTED 0x00000005 +/* Unhandled problem occurred. */ +#define ADSP_EPANIC 0x00000006 +/* Unable to allocate resource. */ +#define ADSP_ENORESOURCE 0x00000007 +/* Invalid handle. */ +#define ADSP_EHANDLE 0x00000008 +/* Operation is already processed. */ +#define ADSP_EALREADY 0x00000009 +/* Operation is not ready to be processed. */ +#define ADSP_ENOTREADY 0x0000000A +/* Operation is pending completion. */ +#define ADSP_EPENDING 0x0000000B +/* Operation could not be accepted or processed. */ +#define ADSP_EBUSY 0x0000000C +/* Operation aborted due to an error. */ +#define ADSP_EABORTED 0x0000000D +/* Operation preempted by a higher priority. */ +#define ADSP_EPREEMPTED 0x0000000E +/* Operation requests intervention to complete. */ +#define ADSP_ECONTINUE 0x0000000F +/* Operation requests immediate intervention to complete. */ +#define ADSP_EIMMEDIATE 0x00000010 +/* Operation is not implemented. */ +#define ADSP_ENOTIMPL 0x00000011 +/* Operation needs more data or resources. */ +#define ADSP_ENEEDMORE 0x00000012 +/* Operation does not have memory. */ +#define ADSP_ENOMEMORY 0x00000014 +/* Item does not exist. */ +#define ADSP_ENOTEXIST 0x00000015 +/* Max count for adsp error code sent to HLOS*/ +#define ADSP_ERR_MAX (ADSP_ENOTEXIST + 1) +/* Operation is finished. */ +#define ADSP_ETERMINATED 0x00011174 + +/* ERROR STRING */ +/* Success. The operation completed with no errors. */ +#define ADSP_EOK_STR "ADSP_EOK" +/* General failure. */ +#define ADSP_EFAILED_STR "ADSP_EFAILED" +/* Bad operation parameter. */ +#define ADSP_EBADPARAM_STR "ADSP_EBADPARAM" +/* Unsupported routine or operation. */ +#define ADSP_EUNSUPPORTED_STR "ADSP_EUNSUPPORTED" +/* Unsupported version. */ +#define ADSP_EVERSION_STR "ADSP_EVERSION" +/* Unexpected problem encountered. */ +#define ADSP_EUNEXPECTED_STR "ADSP_EUNEXPECTED" +/* Unhandled problem occurred. */ +#define ADSP_EPANIC_STR "ADSP_EPANIC" +/* Unable to allocate resource. */ +#define ADSP_ENORESOURCE_STR "ADSP_ENORESOURCE" +/* Invalid handle. */ +#define ADSP_EHANDLE_STR "ADSP_EHANDLE" +/* Operation is already processed. */ +#define ADSP_EALREADY_STR "ADSP_EALREADY" +/* Operation is not ready to be processed. */ +#define ADSP_ENOTREADY_STR "ADSP_ENOTREADY" +/* Operation is pending completion. */ +#define ADSP_EPENDING_STR "ADSP_EPENDING" +/* Operation could not be accepted or processed. */ +#define ADSP_EBUSY_STR "ADSP_EBUSY" +/* Operation aborted due to an error. */ +#define ADSP_EABORTED_STR "ADSP_EABORTED" +/* Operation preempted by a higher priority. */ +#define ADSP_EPREEMPTED_STR "ADSP_EPREEMPTED" +/* Operation requests intervention to complete. */ +#define ADSP_ECONTINUE_STR "ADSP_ECONTINUE" +/* Operation requests immediate intervention to complete. */ +#define ADSP_EIMMEDIATE_STR "ADSP_EIMMEDIATE" +/* Operation is not implemented. */ +#define ADSP_ENOTIMPL_STR "ADSP_ENOTIMPL" +/* Operation needs more data or resources. */ +#define ADSP_ENEEDMORE_STR "ADSP_ENEEDMORE" +/* Operation does not have memory. */ +#define ADSP_ENOMEMORY_STR "ADSP_ENOMEMORY" +/* Item does not exist. */ +#define ADSP_ENOTEXIST_STR "ADSP_ENOTEXIST" +/* Unexpected error code. */ +#define ADSP_ERR_MAX_STR "ADSP_ERR_MAX" + +#define PCM_FORMAT_MAX_NUM_CHANNEL 8 +#define PCM_CHANNEL_NULL 0 + +#define PCM_CHANNEL_FL 1 /* Front left channel. */ +#define PCM_CHANNEL_FR 2 /* Front right channel. */ +#define PCM_CHANNEL_FC 3 /* Front center channel. */ +#define PCM_CHANNEL_LS 4 /* Left surround channel. */ +#define PCM_CHANNEL_RS 5 /* Right surround channel. */ +#define PCM_CHANNEL_LFE 6 /* Low frequency effect channel. */ +#define PCM_CHANNEL_CS 7 /* Center surround channel; Rear center ch */ +#define PCM_CHANNEL_LB 8 /* Left back channel; Rear left channel. */ +#define PCM_CHANNEL_RB 9 /* Right back channel; Rear right channel. */ +#define PCM_CHANNELS 10 /* Top surround channel. */ + +static inline int q6dsp_map_channels(u8 *ch_map, int ch) +{ + memset(ch_map, 0, PCM_FORMAT_MAX_NUM_CHANNEL); + + if (ch == 1) { + ch_map[0] = PCM_CHANNEL_FC; + } else if (ch == 2) { + ch_map[0] = PCM_CHANNEL_FL; + ch_map[1] = PCM_CHANNEL_FR; + } else if (ch == 3) { + ch_map[0] = PCM_CHANNEL_FL; + ch_map[1] = PCM_CHANNEL_FR; + ch_map[2] = PCM_CHANNEL_FC; + } else if (ch == 4) { + ch_map[0] = PCM_CHANNEL_FL; + ch_map[1] = PCM_CHANNEL_FR; + ch_map[2] = PCM_CHANNEL_LS; + ch_map[3] = PCM_CHANNEL_RS; + } else if (ch == 5) { + ch_map[0] = PCM_CHANNEL_FL; + ch_map[1] = PCM_CHANNEL_FR; + ch_map[2] = PCM_CHANNEL_FC; + ch_map[3] = PCM_CHANNEL_LS; + ch_map[4] = PCM_CHANNEL_RS; + } else if (ch == 6) { + ch_map[0] = PCM_CHANNEL_FL; + ch_map[1] = PCM_CHANNEL_FR; + ch_map[2] = PCM_CHANNEL_LFE; + ch_map[3] = PCM_CHANNEL_FC; + ch_map[4] = PCM_CHANNEL_LS; + ch_map[5] = PCM_CHANNEL_RS; + } else if (ch == 8) { + ch_map[0] = PCM_CHANNEL_FL; + ch_map[1] = PCM_CHANNEL_FR; + ch_map[2] = PCM_CHANNEL_LFE; + ch_map[3] = PCM_CHANNEL_FC; + ch_map[4] = PCM_CHANNEL_LS; + ch_map[5] = PCM_CHANNEL_RS; + ch_map[6] = PCM_CHANNEL_LB; + ch_map[7] = PCM_CHANNEL_RB; + } else { + return -EINVAL; + } + + return 0; +} + +enum { + LEGACY_PCM_MODE = 0, + LOW_LATENCY_PCM_MODE, + ULTRA_LOW_LATENCY_PCM_MODE, + ULL_POST_PROCESSING_PCM_MODE, +}; + +struct adsp_err_code { + int lnx_err_code; + char *adsp_err_str; +}; + +static struct adsp_err_code adsp_err_code_info[ADSP_ERR_MAX+1] = { + { 0, ADSP_EOK_STR}, + { -ENOTRECOVERABLE, ADSP_EFAILED_STR}, + { -EINVAL, ADSP_EBADPARAM_STR}, + { -ENOSYS, ADSP_EUNSUPPORTED_STR}, + { -ENOPROTOOPT, ADSP_EVERSION_STR}, + { -ENOTRECOVERABLE, ADSP_EUNEXPECTED_STR}, + { -ENOTRECOVERABLE, ADSP_EPANIC_STR}, + { -ENOSPC, ADSP_ENORESOURCE_STR}, + { -EBADR, ADSP_EHANDLE_STR}, + { -EALREADY, ADSP_EALREADY_STR}, + { -EPERM, ADSP_ENOTREADY_STR}, + { -EINPROGRESS, ADSP_EPENDING_STR}, + { -EBUSY, ADSP_EBUSY_STR}, + { -ECANCELED, ADSP_EABORTED_STR}, + { -EAGAIN, ADSP_EPREEMPTED_STR}, + { -EAGAIN, ADSP_ECONTINUE_STR}, + { -EAGAIN, ADSP_EIMMEDIATE_STR}, + { -EAGAIN, ADSP_ENOTIMPL_STR}, + { -ENODATA, ADSP_ENEEDMORE_STR}, + { -EADV, ADSP_ERR_MAX_STR}, + { -ENOMEM, ADSP_ENOMEMORY_STR}, + { -ENODEV, ADSP_ENOTEXIST_STR}, + { -EADV, ADSP_ERR_MAX_STR}, +}; + +static inline int adsp_err_get_lnx_err_code(u32 adsp_error) +{ + if (adsp_error > ADSP_ERR_MAX) + return adsp_err_code_info[ADSP_ERR_MAX].lnx_err_code; + else + return adsp_err_code_info[adsp_error].lnx_err_code; +} + +static inline char *adsp_err_get_err_str(u32 adsp_error) +{ + if (adsp_error > ADSP_ERR_MAX) + return adsp_err_code_info[ADSP_ERR_MAX].adsp_err_str; + else + return adsp_err_code_info[adsp_error].adsp_err_str; +} + +#endif /* __Q6DSP_COMMON_H__ */ diff --git a/sound/soc/qcom/qdsp6v2/q6afe-v2.h b/sound/soc/qcom/qdsp6v2/q6afe-v2.h new file mode 100644 index 0000000..bb73709 --- /dev/null +++ b/sound/soc/qcom/qdsp6v2/q6afe-v2.h @@ -0,0 +1,62 @@ +#ifndef __Q6AFE_V2_H__ +#define __Q6AFE_V2_H__ +#include +#define MSM_AFE_PORT_TYPE_RX 0 +#define MSM_AFE_PORT_TYPE_TX 1 +#define AFE_MAX_PORTS AFE_PORT_MAX + +struct q6afe_hdmi_cfg { + u16 datatype; + u16 channel_allocation; + u32 sample_rate; + u16 bit_width; +}; + +struct q6afe_port; + +#if IS_ENABLED(CONFIG_SND_SOC_QDSP6V2_AFE) + +struct q6afe_port *q6afe_port_get(struct device *dev, const char *name); +int q6afe_get_port_index(struct q6afe_port *p); +int q6afe_port_stop(struct q6afe_port *port); +int q6afe_port_put(struct q6afe_port *port); +int q6afe_get_port_id(int index); +int q6afe_hdmi_port_start(struct q6afe_port *port, + struct q6afe_hdmi_cfg *cfg); + +#else + +static inline int q6afe_get_port_id(int index) +{ + return -ENOSYS; +} + +static inline int q6afe_get_port_index(struct q6afe_port *p) +{ + return -ENOSYS; +} +static inline struct q6afe_port *q6afe_port_get(struct device *dev, + const char *name) +{ + return ERR_PTR(-ENOSYS); +} + +static inline int q6afe_hdmi_port_start(struct q6afe_port *port, + struct q6afe_hdmi_cfg *cfg) +{ + return -ENOSYS; +} + +static inline int q6afe_port_stop(struct q6afe_port *port) +{ + return -ENOSYS; +} + +static inline int q6afe_port_put(struct q6afe_port *port) +{ + return -ENOSYS; +} + +#endif + +#endif /* __Q6AFE_V2_H__ */ diff --git a/sound/soc/qcom/qdsp6v2/q6afe.c b/sound/soc/qcom/qdsp6v2/q6afe.c new file mode 100644 index 0000000..ebb9f54 --- /dev/null +++ b/sound/soc/qcom/qdsp6v2/q6afe.c @@ -0,0 +1,499 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "common.h" +#include "q6afe-v2.h" + +/* AFE Cmds */ +#define AFE_PORT_CMD_DEVICE_START 0x000100E5 +#define AFE_PORT_CMD_DEVICE_STOP 0x000100E6 +#define AFE_PORT_CMD_SET_PARAM_V2 0x000100EF +#define AFE_PORT_CMDRSP_GET_PARAM_V2 0x00010106 +#define AFE_PARAM_ID_HDMI_CONFIG 0x00010210 +#define AFE_MODULE_AUDIO_DEV_INTERFACE 0x0001020C + +/* Port IDs */ +#define AFE_API_VERSION_HDMI_CONFIG 0x1 +#define AFE_PORT_ID_MULTICHAN_HDMI_RX 0x100E +#define TIMEOUT_MS 1000 +#define AFE_CMD_RESP_AVAIL 0 +#define AFE_CMD_RESP_NONE 1 + +struct q6afe_port { + wait_queue_head_t wait; + int token; + int id; + int cfg_type; + union { + struct q6afev2 *v2; + } afe; + struct list_head node; +}; + +struct q6afev2 { + void *apr; + struct device *dev; + int state; + int status; + + struct mutex afe_cmd_lock; + struct list_head port_list; + struct mutex port_list_lock; + struct list_head node; +}; + +struct afe_port_cmd_device_start { + struct apr_hdr hdr; + u16 port_id; + u16 reserved; +} __packed; + +struct afe_port_cmd_device_stop { + struct apr_hdr hdr; + u16 port_id; + u16 reserved; +/* Reserved for 32-bit alignment. This field must be set to 0.*/ +} __packed; + +struct afe_port_param_data_v2 { + u32 module_id; + u32 param_id; + u16 param_size; + u16 reserved; +} __packed; + +struct afe_port_cmd_set_param_v2 { + u16 port_id; + u16 payload_size; + u32 payload_address_lsw; + u32 payload_address_msw; + u32 mem_map_handle; +} __packed; + +struct afe_param_id_hdmi_multi_chan_audio_cfg { + u32 hdmi_cfg_minor_version; + u16 datatype; + u16 channel_allocation; + u32 sample_rate; + u16 bit_width; + u16 reserved; +} __packed; + +union afe_port_config { + struct afe_param_id_hdmi_multi_chan_audio_cfg hdmi_multi_ch; +} __packed; + +struct afe_audioif_config_command { + struct apr_hdr hdr; + struct afe_port_cmd_set_param_v2 param; + struct afe_port_param_data_v2 pdata; + union afe_port_config port; +} __packed; + +struct afe_port_map { + int port_id; + int token; + int is_rx; +}; + +struct q6afev2 *__afe; +/* Port map of index vs real hw port ids */ +static struct afe_port_map port_maps[AFE_PORT_MAX] = { + [AFE_PORT_HDMI_RX] = { AFE_PORT_ID_MULTICHAN_HDMI_RX, + AFE_PORT_HDMI_RX, 1}, +}; + +static struct q6afev2 *of_parse_q6afe_port(struct device *dev, const char *name, + int *id) +{ + struct of_phandle_args args; + int ret; + + ret = of_parse_phandle_with_fixed_args(dev->of_node, name, 1, 0, &args); + if (ret < 0) { + dev_err(dev, "Failed to parse qcom,afe-port\n"); + return NULL; + } + + *id = args.args[0]; + + if (__afe && __afe->dev->of_node == args.np) + return __afe; + + return NULL; +} + +static struct q6afe_port *afe_find_port(struct q6afev2 *afe, int token) +{ + struct q6afe_port *p; + + list_for_each_entry(p, &afe->port_list, node) + if (p->token == token) + return p; + + return NULL; +} + +static int32_t afe_callback(struct apr_client_data *data, void *priv) +{ + struct q6afev2 *afe = priv; + struct q6afe_port *port; + + if (!data) { + dev_err(afe->dev, "%s: Invalid param data\n", __func__); + return -EINVAL; + } + + if (data->payload_size) { + uint32_t *payload = data->payload; + + if (data->opcode == APR_BASIC_RSP_RESULT) { + if (payload[1] != 0) { + afe->status = payload[1]; + dev_err(afe->dev, + "cmd = 0x%x returned error = 0x%x\n", + payload[0], payload[1]); + } + switch (payload[0]) { + case AFE_PORT_CMD_SET_PARAM_V2: + case AFE_PORT_CMD_DEVICE_STOP: + case AFE_PORT_CMD_DEVICE_START: + afe->state = AFE_CMD_RESP_AVAIL; + port = afe_find_port(afe, data->token); + if (port) + wake_up(&port->wait); + + break; + default: + dev_err(afe->dev, "Unknown cmd 0x%x\n", + payload[0]); + break; + } + } + } + return 0; +} + +int q6afe_get_port_id(int index) +{ + if (index < 0 || index > AFE_PORT_MAX) { + pr_err("%s: AFE port index[%d] invalid!\n", __func__, index); + return -EINVAL; + } + + return port_maps[index].port_id; +} +EXPORT_SYMBOL_GPL(q6afe_get_port_id); + +static int afe_apr_send_pkt(struct q6afev2 *afe, void *data, + wait_queue_head_t *wait) +{ + int ret; + + if (wait) + afe->state = AFE_CMD_RESP_NONE; + + afe->status = 0; + ret = apr_send_pkt(afe->apr, data); + if (ret > 0) { + if (wait) { + ret = wait_event_timeout(*wait, + (afe->state == + AFE_CMD_RESP_AVAIL), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + ret = -ETIMEDOUT; + } else if (afe->status > 0) { + dev_err(afe->dev, "DSP returned error[%s]\n", + adsp_err_get_err_str(afe->status)); + ret = adsp_err_get_lnx_err_code(afe->status); + } else { + ret = 0; + } + } else { + ret = 0; + } + } else if (ret == 0) { + dev_err(afe->dev, "packet not transmitted\n"); + ret = -EINVAL; + } + + return ret; +} + +static int afe_send_cmd_port_start(struct q6afe_port *port) +{ + u16 port_id = port->id; + struct afe_port_cmd_device_start start; + struct q6afev2 *afe = port->afe.v2; + int ret, index; + + index = port->token; + start.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + start.hdr.pkt_size = sizeof(start); + start.hdr.src_port = 0; + start.hdr.dest_port = 0; + start.hdr.token = index; + start.hdr.opcode = AFE_PORT_CMD_DEVICE_START; + start.port_id = port_id; + + ret = afe_apr_send_pkt(afe, &start, &port->wait); + if (ret) + dev_err(afe->dev, "AFE enable for port 0x%x failed %d\n", + port_id, ret); + + return ret; +} + +static int afe_port_start(struct q6afe_port *port, + union afe_port_config *afe_config) +{ + struct afe_audioif_config_command config; + struct q6afev2 *afe = port->afe.v2; + int ret = 0; + int port_id = port->id; + int cfg_type; + int index = 0; + + if (!afe_config) { + dev_err(afe->dev, "Error, no configuration data\n"); + ret = -EINVAL; + return ret; + } + + index = port->token; + + mutex_lock(&afe->afe_cmd_lock); + /* Also send the topology id here: */ + 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 = index; + + cfg_type = port->cfg_type; + config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + config.param.port_id = port_id; + config.param.payload_size = sizeof(config) - sizeof(struct apr_hdr) - + sizeof(config.param); + config.param.payload_address_lsw = 0x00; + config.param.payload_address_msw = 0x00; + config.param.mem_map_handle = 0x00; + config.pdata.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + config.pdata.param_id = cfg_type; + config.pdata.param_size = sizeof(config.port); + + config.port = *afe_config; + + ret = afe_apr_send_pkt(afe, &config, &port->wait); + if (ret) { + dev_err(afe->dev, "AFE enable for port 0x%x failed %d\n", + port_id, ret); + goto fail_cmd; + } + + ret = afe_send_cmd_port_start(port); + +fail_cmd: + mutex_unlock(&afe->afe_cmd_lock); + return ret; +} + +int q6afe_get_port_index(struct q6afe_port *p) +{ + return p->token; +} + +int q6afe_port_stop(struct q6afe_port *port) +{ + int port_id = port->id; + struct afe_port_cmd_device_stop stop; + struct q6afev2 *afe = port->afe.v2; + int ret = 0; + int index = 0; + + port_id = port->id; + index = port->token; + if (index < 0 || index > AFE_PORT_MAX) { + dev_err(afe->dev, "AFE port index[%d] invalid!\n", index); + return -EINVAL; + } + + stop.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + stop.hdr.pkt_size = sizeof(stop); + stop.hdr.src_port = 0; + stop.hdr.dest_port = 0; + stop.hdr.token = index; + stop.hdr.opcode = AFE_PORT_CMD_DEVICE_STOP; + stop.port_id = port_id; + stop.reserved = 0; + + ret = afe_apr_send_pkt(afe, &stop, &port->wait); + if (ret) + dev_err(afe->dev, "AFE close failed %d\n", ret); + + return ret; +} +EXPORT_SYMBOL_GPL(q6afe_port_stop); + +int q6afe_hdmi_port_start(struct q6afe_port *port, struct q6afe_hdmi_cfg *cfg) +{ + union afe_port_config port_cfg; + + port_cfg.hdmi_multi_ch.hdmi_cfg_minor_version = + AFE_API_VERSION_HDMI_CONFIG; + port_cfg.hdmi_multi_ch.datatype = cfg->datatype; + port_cfg.hdmi_multi_ch.channel_allocation = cfg->channel_allocation; + port_cfg.hdmi_multi_ch.sample_rate = cfg->sample_rate; + port_cfg.hdmi_multi_ch.bit_width = cfg->bit_width; + + return afe_port_start(port, &port_cfg); +} +EXPORT_SYMBOL_GPL(q6afe_hdmi_port_start); + +struct q6afe_port *q6afe_port_get(struct device *dev, const char *name) +{ + int port_id, index = 0; + struct q6afev2 *afe = of_parse_q6afe_port(dev, name, &index); + struct q6afe_port *port; + int token; + int cfg_type; + + if (!afe) { + dev_err(dev, "Unable to find instance of afe service\n"); + return ERR_PTR(-ENOENT); + } + + token = index; + if (token < 0 || token > AFE_PORT_MAX) { + dev_err(dev, "AFE port token[%d] invalid!\n", token); + return ERR_PTR(-EINVAL); + } + + port_id = port_maps[index].port_id; + + port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); + if (!port) + return ERR_PTR(-ENOMEM); + + init_waitqueue_head(&port->wait); + + port->token = token; + port->id = port_id; + + port->afe.v2 = afe; + switch (port_id) { + case AFE_PORT_ID_MULTICHAN_HDMI_RX: + cfg_type = AFE_PARAM_ID_HDMI_CONFIG; + break; + default: + dev_err(dev, "Invalid port id 0x%x\n", port_id); + return ERR_PTR(-EINVAL); + } + + port->cfg_type = cfg_type; + + mutex_lock(&afe->port_list_lock); + list_add_tail(&port->node, &afe->port_list); + mutex_unlock(&afe->port_list_lock); + + return port; + +} +EXPORT_SYMBOL_GPL(q6afe_port_get); + +int q6afe_port_put(struct q6afe_port *port) +{ + struct q6afev2 *afe = port->afe.v2; + + mutex_lock(&afe->port_list_lock); + list_del(&port->node); + mutex_unlock(&afe->port_list_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(q6afe_port_put); + +static int q6afev2_probe(struct platform_device *pdev) +{ + int ret; + struct device *dev = &pdev->dev; + + __afe = kzalloc(sizeof(*__afe), GFP_KERNEL); + if (!__afe) + return -ENOMEM; + + __afe->apr = apr_register(dev, "ADSP", "AFE", + afe_callback, 0xFFFFFFFF, __afe); + if (__afe->apr == NULL) { + dev_err(dev, "Unable to register AFE\n"); + ret = -ENODEV; + } + + mutex_init(&__afe->afe_cmd_lock); + __afe->dev = dev; + INIT_LIST_HEAD(&__afe->port_list); + mutex_init(&__afe->port_list_lock); + + platform_set_drvdata(pdev, __afe); + return 0; +} + +static int q6afev2_remove(struct platform_device *pdev) +{ + int rval; + + rval = apr_deregister(__afe->apr); + if (rval < 0) + return rval; + + kfree(__afe); + __afe = NULL; + + return 0; +} + +static const struct of_device_id qcom_q6afe_match[] = { + {.compatible = "qcom,q6afe-v2",}, + {} +}; + +static struct platform_driver qcom_q6afe_driver = { + .probe = q6afev2_probe, + .remove = q6afev2_remove, + .driver = { + .name = "qcom-q6afe-v2", + .of_match_table = qcom_q6afe_match, + }, +}; + +module_platform_driver(qcom_q6afe_driver); +MODULE_AUTHOR("Srinivas Kandagatla X-Patchwork-Id: 109865 Delivered-To: patch@linaro.org Received: by 10.140.95.78 with SMTP id h72csp916900qge; Fri, 11 Aug 2017 06:30:26 -0700 (PDT) X-Received: by 10.84.194.3 with SMTP id g3mr7965352pld.0.1502458226055; Fri, 11 Aug 2017 06:30:26 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1502458226; cv=none; d=google.com; s=arc-20160816; b=SWiHcCqXxnYf+hWAkYGg4kqffnxUbrAvj0Xuz4SSnx0k1RB1e/vOljA9/ALMG1C00R ue9Wdmo+7JewPbSjWH4qNe+SiFYbfD655kdq1D/MtdVD32XGECPEheJE/uazZjAhGEBv KEgBxgbuwL1JkbnYSu2ynlcRs274GayXzKJ+XjEvnWLyytxekyfukpBrc8pEAqiTn+ZS sxKO0WxHiK/ArJW4wvhZRyLZcij90ZVXCl/DhjQPD7gXGeQ6S3kelhhZi+T7JUJy2lia AAtd0VhUju7hNAP2HJ+K/JhQnux5lfyVQvUqTkjic951CzxkaNpcHPbeo1akgeICe/zg 2Rng== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=CdrQ2jBaGKEih7UYFlzX1MZ14Sf3wzFQ6QCzEM/CvKU=; b=FLzWxlTdgGyWzC3MfadsVMFP6c2jQI6vpWv6/FRcTyR2F8f8KFnQVNHgs5CKxaGXVe Y6brRqzj0qNL96hZVACMisyT6Q2zdyblYawfiy/0YnGwHZVp4xYXAil95bj20APBtVqJ 13ei7fO0pVdgoDeU27DmlSFucfLRS3LR1GB+KPGvxZ8UAUMQHwGfYW7jAqWLGIPPrgPL dVULgpN+sKuKAkj15MNgAOKux9bFyDd/ypwEGz7YXtehFmCRi2WQYT2oK7eTxlBhU7CD bCixMQvgJdjRpVvmUT9lDT94uRTEOPu8KD41iC+TuxPjfbgh4iBs4VY+I837Dxk7xZb9 S6eA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=CT9f6i08; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id 73si533905pfw.130.2017.08.11.06.30.25; Fri, 11 Aug 2017 06:30:26 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=CT9f6i08; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753295AbdHKNaW (ORCPT + 25 others); Fri, 11 Aug 2017 09:30:22 -0400 Received: from mail-it0-f46.google.com ([209.85.214.46]:37025 "EHLO mail-it0-f46.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753215AbdHKNaK (ORCPT ); Fri, 11 Aug 2017 09:30:10 -0400 Received: by mail-it0-f46.google.com with SMTP id 76so27085786ith.0 for ; Fri, 11 Aug 2017 06:30:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=CdrQ2jBaGKEih7UYFlzX1MZ14Sf3wzFQ6QCzEM/CvKU=; b=CT9f6i083s0t8RDvaigM9nUf0V1H1OerOFHNSXGkYzxBFSsf4JkOau6Q85V914a+ox Bibjv7MM6tI1cXct9t87XzMyidFHckX+mpS5W0QrZCj9HZurCADMXdakeqe0XFWHezvu pTxxXH9Y2Y7CBtqx+RMqHqmLwjl4bzjzyWaks= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=CdrQ2jBaGKEih7UYFlzX1MZ14Sf3wzFQ6QCzEM/CvKU=; b=gJhpaFJedCs17sTQa8RVa1pPWl8hCbyl76favpLoo1E07aY0AqJt4hstLJwC59568Y hRD2to2cZpJRRSL7/Vsg7XNStSlZIddFXYncaNnP6gzxfq7NRiPvwkrhyubqd84SmpD7 vzLC1aFUeOBiOXDpHyLOLUaWpDp5fxbvPTZnrH819zH4v4oijQE9IX1WzoEAyPDyXNmz zU1s6Qtp7Xq5BdNZgyx9yhS3XpNz98DOmIgJWY7wWgPn64lHU1ST8CePr0vaYmSSaQMQ VGox4XjQ71L4liNHU/XmVjjsfULUXRcAiH1DpsZ76/JivNofP0ntGkGAqVytMxm2B7rr BM8A== X-Gm-Message-State: AIVw111MWfLsMUp0DiMGJ2b1dUFOf14UfWmv7k+uWZ7sUhkIxookoQYd yQvwC2mp4xSlpzOP X-Received: by 10.36.28.132 with SMTP id c126mr12250395itc.84.1502458208811; Fri, 11 Aug 2017 06:30:08 -0700 (PDT) Received: from localhost.localdomain (static.8.26.4.46.clients.your-server.de. [46.4.26.8]) by smtp.gmail.com with ESMTPSA id y72sm398422iod.40.2017.08.11.06.30.06 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 11 Aug 2017 06:30:08 -0700 (PDT) From: srinivas.kandagatla@linaro.org To: Mark Brown , Banajit Goswami , alsa-devel@alsa-project.org Cc: Takashi Iwai , Patrick Lai , linux-kernel@vger.kernel.org, kwestfie@codeaurora.org, linux-arm-msm@vger.kernel.org, lkasam@qti.qualcomm.com, sboyd@codeaurora.org, Srinivas Kandagatla Subject: [RFC PATCH 3/9] ASoC: qcom: qdsp6v2: Add support to Q6ADM Date: Fri, 11 Aug 2017 15:29:46 +0200 Message-Id: <20170811132952.32572-4-srinivas.kandagatla@linaro.org> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20170811132952.32572-1-srinivas.kandagatla@linaro.org> References: <20170811132952.32572-1-srinivas.kandagatla@linaro.org> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Srinivas Kandagatla This patch adds support to q6 ADM (Audio Device Manager) module in q6dsp. ADM performs routing between audio streams and AFE ports. It does Rate matching for streams going to devices driven by different clocks, it handles volume ramping, Mixing with channel and bit-width. ADM creates and destroys dynamic COPP services for device-related audio processing as needed. This patch adds basic support to ADM. Signed-off-by: Srinivas Kandagatla --- .../devicetree/bindings/sound/qcom,q6adm.txt | 15 + sound/soc/qcom/Kconfig | 5 + sound/soc/qcom/qdsp6v2/Makefile | 1 + sound/soc/qcom/qdsp6v2/q6adm-v2.h | 76 +++ sound/soc/qcom/qdsp6v2/q6adm.c | 603 +++++++++++++++++++++ 5 files changed, 700 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/qcom,q6adm.txt create mode 100644 sound/soc/qcom/qdsp6v2/q6adm-v2.h create mode 100644 sound/soc/qcom/qdsp6v2/q6adm.c -- 2.9.3 diff --git a/Documentation/devicetree/bindings/sound/qcom,q6adm.txt b/Documentation/devicetree/bindings/sound/qcom,q6adm.txt new file mode 100644 index 0000000..859d0cc --- /dev/null +++ b/Documentation/devicetree/bindings/sound/qcom,q6adm.txt @@ -0,0 +1,15 @@ +Qualcomm Q6ADM (Q6 Audio Device Manager) binding + +This bindings describe the Qualcomm Q6 ADM module on QDSP, +which is used by audio drivers. + +- compatible: + + Usage: required + Value type: + Definition: must be "qcom,q6adm-v" example: "qcom,q6adm-v2" + += EXAMPLE + q6adm { + compatible = "qcom,q6adm-v2"; + }; diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index dad3fa1..72f6fa4 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -48,9 +48,14 @@ config SND_SOC_QDSP6V2_AFE tristate default n +config SND_SOC_QDSP6V2_ADM + tristate + default n + config SND_SOC_QDSP6V2 tristate "SoC ALSA audio driver for QDSP6V2" select SND_SOC_QDSP6V2_AFE + select SND_SOC_QDSP6V2_ADM help To add support for MSM QDSP6V2 Soc Audio. This will enable sound soc platform specific diff --git a/sound/soc/qcom/qdsp6v2/Makefile b/sound/soc/qcom/qdsp6v2/Makefile index 8069891..a2b8b0b 100644 --- a/sound/soc/qcom/qdsp6v2/Makefile +++ b/sound/soc/qcom/qdsp6v2/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_SND_SOC_QDSP6V2_AFE) += q6afe.o +obj-$(CONFIG_SND_SOC_QDSP6V2_ADM) += q6adm.o diff --git a/sound/soc/qcom/qdsp6v2/q6adm-v2.h b/sound/soc/qcom/qdsp6v2/q6adm-v2.h new file mode 100644 index 0000000..9cf369d --- /dev/null +++ b/sound/soc/qcom/qdsp6v2/q6adm-v2.h @@ -0,0 +1,76 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __Q6_ADM_V2_H__ +#define __Q6_ADM_V2_H__ + +#define ADM_PATH_PLAYBACK 0x1 +#define MAX_COPPS_PER_PORT 8 +#define NULL_COPP_TOPOLOGY 0x00010312 + +/* multiple copp per stream. */ +struct route_payload { + unsigned int copp_idx[MAX_COPPS_PER_PORT]; + unsigned short num_copps; + unsigned int session_id; + + unsigned int port_id[MAX_COPPS_PER_PORT]; + int app_type; + int acdb_dev_id; + int sample_rate; +}; + +struct q6adm; +#if IS_ENABLED(CONFIG_SND_SOC_QDSP6V2_ADM) +struct q6adm *q6adm_get(struct device *dev); +void q6adm_put(struct q6adm *adm); +int q6adm_open(struct q6adm *_adm, int port_id, int path, int rate, + int channel_mode, int topology, int perf_mode, + uint16_t bit_width, int app_type, int acdb_id); +int q6adm_close(int port, int topology, int perf_mode); +int q6adm_matrix_map(int path, struct route_payload payload_map, int perf_mode); +int q6adm_connect_afe_port(int mode, int session_id, int port_id); + +#else + +static inline struct q6adm *q6adm_get(struct device *dev) +{ + return PTR_ERR(-ENOSYS); +} + +static inline void q6adm_put(struct q6adm *adm) {} + +static inline int q6adm_open(struct q6adm *_adm, int port_id, int path, + int rate, int channel_mode, int topology, + int perf_mode, uint16_t bit_width, int app_type, + int acdb_id) +{ + return -ENOSYS; +} + +static inline int q6adm_close(int port, int topology, int perf_mode) +{ + return -ENOSYS; +} + +static inline int q6adm_matrix_map(int path, struct route_payload payload_map, + int perf_mode) +{ + return -ENOSYS; +} + +static inline int q6adm_connect_afe_port(int mode, int session_id, int port_id) +{ + return -ENOSYS; +} + +#endif +#endif /* __Q6_ADM_V2_H__ */ diff --git a/sound/soc/qcom/qdsp6v2/q6adm.c b/sound/soc/qcom/qdsp6v2/q6adm.c new file mode 100644 index 0000000..5ffda62 --- /dev/null +++ b/sound/soc/qcom/qdsp6v2/q6adm.c @@ -0,0 +1,603 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "q6adm-v2.h" +#include "q6afe-v2.h" +#include "common.h" + +#define ADM_CMD_DEVICE_OPEN_V5 0x00010326 +#define ADM_CMDRSP_DEVICE_OPEN_V5 0x00010329 +#define ADM_CMD_DEVICE_CLOSE_V5 0x00010327 +#define ADM_CMD_MATRIX_MAP_ROUTINGS_V5 0x00010325 + +#define TIMEOUT_MS 1000 +#define RESET_COPP_ID 99 +#define INVALID_COPP_ID 0xFF +/* Definition for a legacy device session. */ +#define ADM_LEGACY_DEVICE_SESSION 0 +#define ADM_MATRIX_ID_AUDIO_RX 0 + +struct copp { + int afe_port; + int copp_idx; + int id; + int cnt; + int topology; + int mode; + int stat; + int rate; + int bit_width; + int channels; + int app_type; + int acdb_id; + wait_queue_head_t wait; + struct list_head node; +}; + +struct q6adm { + void *apr; + struct device *dev; + unsigned long copp_bitmap[AFE_MAX_PORTS]; + struct list_head copps_list; + struct mutex copps_list_lock; + int matrix_map_stat; + wait_queue_head_t matrix_map_wait; +}; + +static struct q6adm *__adm; + +static struct copp *adm_find_copp(struct q6adm *adm, int port_idx, int copp_idx) +{ + struct copp *c; + + list_for_each_entry(c, &adm->copps_list, node) { + if ((port_idx == c->afe_port) && (copp_idx == c->copp_idx)) + return c; + } + + return NULL; + +} + +static struct copp *adm_find_matching_copp(struct q6adm *adm, + int port_idx, int topology, + int mode, int rate, + int bit_width, int app_type) +{ + struct copp *c; + + list_for_each_entry(c, &adm->copps_list, node) { + if ((port_idx == c->afe_port) && (topology == c->topology) && + (mode == c->mode) && (rate == c->rate) && + (bit_width == c->bit_width) && (app_type == c->app_type)) + return c; + } + + return NULL; + +} + +static int32_t adm_callback(struct apr_client_data *data, void *priv) +{ + uint32_t *payload; + int port_idx, copp_idx; + struct copp *copp; + + if (data == NULL) { + pr_err("%s: data parameter is null\n", __func__); + return -EINVAL; + } + + payload = data->payload; + + if (data->payload_size) { + copp_idx = (data->token) & 0XFF; + port_idx = ((data->token) >> 16) & 0xFF; + if (port_idx < 0 || port_idx >= AFE_MAX_PORTS) { + pr_err("%s: Invalid port idx %d token %d\n", + __func__, port_idx, data->token); + return 0; + } + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp idx %d token %d\n", + __func__, copp_idx, data->token); + return 0; + } + + if (data->opcode == APR_BASIC_RSP_RESULT) { + pr_err("%s: APR_BASIC_RSP_RESULT id 0x%x\n", + __func__, payload[0]); + if (payload[1] != 0) { + pr_err("%s: cmd = 0x%x returned error = 0x%x\n", + __func__, payload[0], payload[1]); + } + switch (payload[0]) { + case ADM_CMD_DEVICE_OPEN_V5: + case ADM_CMD_DEVICE_CLOSE_V5: + pr_err + ("%s: Basic callback received, wake up.\n", + __func__); + copp = adm_find_copp(__adm, port_idx, copp_idx); + if (IS_ERR_OR_NULL(copp)) + return 0; + + copp->stat = payload[1]; + wake_up(&copp->wait); + break; + case ADM_CMD_MATRIX_MAP_ROUTINGS_V5: + __adm->matrix_map_stat = payload[1]; + wake_up(&__adm->matrix_map_wait); + break; + + default: + pr_err("%s: Unknown Cmd: 0x%x\n", __func__, + payload[0]); + break; + } + return 0; + } + + switch (data->opcode) { + case ADM_CMDRSP_DEVICE_OPEN_V5:{ + struct adm_cmd_rsp_device_open_v5 { + u32 status; + u16 copp_id; + u16 reserved; + } __packed *open = data->payload; + copp = adm_find_copp(__adm, port_idx, copp_idx); + if (IS_ERR_OR_NULL(copp)) + return 0; + + if (open->copp_id == INVALID_COPP_ID) { + pr_err("%s: invalid coppid rxed %d\n", + __func__, open->copp_id); + copp->stat = ADSP_EBADPARAM; + wake_up(&copp->wait); + break; + } + copp->stat = payload[0]; + copp->id = open->copp_id; + pr_debug("%s: coppid rxed=%d\n", __func__, + open->copp_id); + wake_up(&copp->wait); + + } + break; + default: + pr_err("%s: Unknown cmd:0x%x\n", __func__, + data->opcode); + break; + } + } + return 0; +} + +static struct copp *adm_alloc_copp(struct q6adm *adm, int port_idx) +{ + struct copp *c; + int idx; + + idx = find_first_zero_bit(&adm->copp_bitmap[port_idx], + MAX_COPPS_PER_PORT); + + if (idx > MAX_COPPS_PER_PORT) + return ERR_PTR(-EBUSY); + + set_bit(idx, &adm->copp_bitmap[port_idx]); + + c = devm_kzalloc(adm->dev, sizeof(*c), GFP_KERNEL); + if (!c) + return ERR_PTR(-ENOMEM); + c->copp_idx = idx; + c->afe_port = port_idx; + + init_waitqueue_head(&c->wait); + + mutex_lock(&adm->copps_list_lock); + list_add_tail(&c->node, &adm->copps_list); + mutex_unlock(&adm->copps_list_lock); + + return c; +} + +static void adm_free_copp(struct q6adm *adm, struct copp *c, int port_idx) +{ + clear_bit(c->copp_idx, &adm->copp_bitmap[port_idx]); + mutex_lock(&adm->copps_list_lock); + list_del(&c->node); + mutex_unlock(&adm->copps_list_lock); +} + +int q6adm_open(struct q6adm *_adm, int port_id, int path, int rate, + int channel_mode, int topology, int perf_mode, + uint16_t bit_width, int app_type, int acdb_id) +{ + struct adm_cmd_device_open_v5 { + struct apr_hdr hdr; + u16 flags; + u16 mode_of_operation; + u16 endpoint_id_1; + u16 endpoint_id_2; + u32 topology_id; + u16 dev_num_channel; + u16 bit_width; + u32 sample_rate; + u8 dev_channel_mapping[8]; + } __packed open; + int ret = 0; + int port_idx, flags; + int tmp_port = q6afe_get_port_id(port_id); + struct copp *copp; + + pr_info("%s:port %#x path:%d rate:%d mode:%d perf_mode:%d,topo_id %d\n", + __func__, port_id, path, rate, channel_mode, perf_mode, + topology); + + port_idx = port_id; + if (port_idx < 0) { + pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id); + return -EINVAL; + } + + flags = ADM_LEGACY_DEVICE_SESSION; + copp = adm_find_matching_copp(__adm, port_idx, topology, perf_mode, + rate, bit_width, app_type); + + if (!copp) { + copp = adm_alloc_copp(__adm, port_idx); + if (IS_ERR_OR_NULL(copp)) + return PTR_ERR(copp); + + copp->cnt = 0; + copp->topology = topology; + copp->mode = perf_mode; + copp->rate = rate; + copp->channels = channel_mode; + copp->bit_width = bit_width; + copp->app_type = app_type; + } + + + /* Create a COPP if port id are not enabled */ + if (copp->cnt == 0) { + pr_err("%s: open ADM: port_idx: %d, copp_idx: %d\n", __func__, + port_idx, copp->copp_idx); + + open.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + open.hdr.pkt_size = sizeof(open); + open.hdr.src_svc = APR_SVC_ADM; + open.hdr.src_domain = APR_DOMAIN_APPS; + open.hdr.src_port = tmp_port; + open.hdr.dest_svc = APR_SVC_ADM; + open.hdr.dest_domain = APR_DOMAIN_ADSP; + open.hdr.dest_port = tmp_port; + open.hdr.token = port_idx << 16 | copp->copp_idx; + open.hdr.opcode = ADM_CMD_DEVICE_OPEN_V5; + open.flags = flags; + open.mode_of_operation = path; + open.endpoint_id_1 = tmp_port; + open.topology_id = topology; + open.dev_num_channel = channel_mode & 0x00FF; + open.bit_width = bit_width; + open.sample_rate = rate; + + ret = q6dsp_map_channels(&open.dev_channel_mapping[0], + channel_mode); + + if (ret) + return ret; + + pr_debug("%s: port_id=0x%x rate=%d topology_id=0x%X\n", + __func__, open.endpoint_id_1, open.sample_rate, + open.topology_id); + + copp->stat = -1; + ret = apr_send_pkt(__adm->apr, (uint32_t *)&open); + if (ret < 0) { + pr_err("%s: port_id: 0x%x for[0x%x] failed %d\n", + __func__, tmp_port, port_id, ret); + return -EINVAL; + } + /* Wait for the callback with copp id */ + ret = + wait_event_timeout(copp->wait, copp->stat >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("ADM timedout port_id: 0x%x for [0x%x]\n", + tmp_port, port_id); + return -EINVAL; + } else if (copp->stat > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str(copp->stat)); + return adsp_err_get_lnx_err_code(copp->stat); + } + } + copp->cnt++; + return copp->copp_idx; +} +EXPORT_SYMBOL_GPL(q6adm_open); + +int q6adm_matrix_map(int path, struct route_payload payload_map, int perf_mode) +{ + struct adm_cmd_matrix_map_routings_v5 { + struct apr_hdr hdr; + u32 matrix_id; + u32 num_sessions; + } __packed *route; + + struct adm_session_map_node_v5 { + u16 session_id; + u16 num_copps; + } __packed *node; + + uint16_t *copps_list; + int cmd_size = 0; + int ret = 0, i = 0; + void *payload = NULL; + void *matrix_map = NULL; + int port_idx, copp_idx; + struct copp *copp; + + /* Assumes port_ids have already been validated during adm_open */ + cmd_size = (sizeof(*route) + + sizeof(*node) + (sizeof(uint32_t) * payload_map.num_copps)); + matrix_map = kzalloc(cmd_size, GFP_KERNEL); + if (!matrix_map) + return -ENOMEM; + + route = (struct adm_cmd_matrix_map_routings_v5 *)matrix_map; + route->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + route->hdr.pkt_size = cmd_size; + route->hdr.src_svc = 0; + route->hdr.src_domain = APR_DOMAIN_APPS; + route->hdr.src_port = 0; /* Ignored */ + route->hdr.dest_svc = APR_SVC_ADM; + route->hdr.dest_domain = APR_DOMAIN_ADSP; + route->hdr.dest_port = 0; /* Ignored */ + route->hdr.token = 0; + route->hdr.opcode = ADM_CMD_MATRIX_MAP_ROUTINGS_V5; + route->num_sessions = 1; + + switch (path) { + case ADM_PATH_PLAYBACK: + route->matrix_id = ADM_MATRIX_ID_AUDIO_RX; + break; + default: + pr_err("%s: Wrong path set[%d]\n", __func__, path); + break; + } + + payload = ((u8 *) matrix_map + sizeof(*route)); + node = (struct adm_session_map_node_v5 *)payload; + + node->session_id = payload_map.session_id; + node->num_copps = payload_map.num_copps; + payload = (u8 *) node + sizeof(*node); + copps_list = (uint16_t *) payload; + + for (i = 0; i < payload_map.num_copps; i++) { + port_idx = payload_map.port_id[i]; + if (port_idx < 0) { + pr_err("%s: Invalid port_id 0x%x\n", __func__, + payload_map.port_id[i]); + return -EINVAL; + } + copp_idx = payload_map.copp_idx[i]; + + copp = adm_find_copp(__adm, port_idx, copp_idx); + if (IS_ERR_OR_NULL(copp)) + return -EINVAL; + + copps_list[i] = copp->id; + } + + __adm->matrix_map_stat = -1; + + ret = apr_send_pkt(__adm->apr, (uint32_t *) matrix_map); + if (ret < 0) { + pr_err("%s: routing for syream %d failed ret %d\n", + __func__, payload_map.session_id, ret); + ret = -EINVAL; + goto fail_cmd; + } + ret = wait_event_timeout(__adm->matrix_map_wait, + __adm->matrix_map_stat >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: routing for syream %d failed\n", __func__, + payload_map.session_id); + ret = -EINVAL; + goto fail_cmd; + } else if (__adm->matrix_map_stat > 0) { + pr_err("%s: DSP returned error[%s]\n", __func__, + adsp_err_get_err_str(__adm->matrix_map_stat)); + ret = adsp_err_get_lnx_err_code(__adm->matrix_map_stat); + goto fail_cmd; + } + +fail_cmd: + kfree(matrix_map); + return ret; +} +EXPORT_SYMBOL_GPL(q6adm_matrix_map); + +static void adm_reset_copp(struct copp *c) +{ + c->id = RESET_COPP_ID; + c->cnt = 0; + c->topology = 0; + c->mode = 0; + c->stat = -1; + c->rate = 0; + c->channels = 0; + c->bit_width = 0; + c->app_type = 0; +} + +int q6adm_close(int port_id, int perf_mode, int copp_idx) +{ + struct apr_hdr close; + struct copp *copp; + + int ret = 0, port_idx; + int copp_id = RESET_COPP_ID; + + pr_err("%s: port_id=0x%x perf_mode: %d copp_idx: %d\n", __func__, + port_id, perf_mode, copp_idx); + + port_idx = port_id; + if (port_idx < 0) { + pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id); + return -EINVAL; + } + + if ((copp_idx < 0) || (copp_idx >= MAX_COPPS_PER_PORT)) { + pr_err("%s: Invalid copp idx: %d\n", __func__, copp_idx); + return -EINVAL; + } + + copp = adm_find_copp(__adm, port_id, copp_idx); + if (IS_ERR_OR_NULL(copp)) + return -EINVAL; + + copp->cnt--; + if (!copp->cnt) { + copp_id = copp->id; + pr_err("%s: Closing ADM port_idx:%d copp_idx:%d copp_id:0x%x\n", + __func__, port_idx, copp_idx, copp_id); + + close.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + close.pkt_size = sizeof(close); + close.src_svc = APR_SVC_ADM; + close.src_domain = APR_DOMAIN_APPS; + close.src_port = port_id; + close.dest_svc = APR_SVC_ADM; + close.dest_domain = APR_DOMAIN_ADSP; + close.dest_port = copp_id; + close.token = port_idx << 16 | copp_idx; + close.opcode = ADM_CMD_DEVICE_CLOSE_V5; + + ret = apr_send_pkt(__adm->apr, (uint32_t *) &close); + if (ret < 0) { + pr_err("%s: ADM close failed %d\n", __func__, ret); + return -EINVAL; + } + + ret = wait_event_timeout(copp->wait, copp->stat >= 0, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: ADM cmd Route timedout for port 0x%x\n", + __func__, port_id); + return -EINVAL; + } else if (copp->stat > 0) { + pr_err("%s: DSP returned error[%s]\n", + __func__, adsp_err_get_err_str(copp->stat)); + return adsp_err_get_lnx_err_code(copp->stat); + } + + adm_reset_copp(copp); + adm_free_copp(__adm, copp, port_id); + } + + return 0; +} +EXPORT_SYMBOL_GPL(q6adm_close); + +void q6adm_put(struct q6adm *adm) +{ + +} +EXPORT_SYMBOL_GPL(q6adm_put); + +struct q6adm *q6adm_get(struct device *dev) +{ + if (!__adm) + return ERR_PTR(-EPROBE_DEFER); + + return __adm; +} +EXPORT_SYMBOL_GPL(q6adm_get); + +static int q6adm_probe(struct platform_device *pdev) +{ + int ret; + + __adm = kzalloc(sizeof(*__adm), GFP_KERNEL); + if (!__adm) + return -ENOMEM; + + __adm->apr = NULL; + __adm->dev = &pdev->dev; + __adm->matrix_map_stat = 0; + init_waitqueue_head(&__adm->matrix_map_wait); + + INIT_LIST_HEAD(&__adm->copps_list); + mutex_init(&__adm->copps_list_lock); + + __adm->apr = apr_register(&pdev->dev, "ADSP", "ADM", adm_callback, + 0xFFFFFFFF, &__adm); + if (__adm->apr == NULL) { + pr_err("%s: Unable to register ADM\n", __func__); + ret = -ENODEV; + return ret; + } + + return 0; +} + +static int q6adm_exit(struct platform_device *pdev) +{ + int rval; + + rval = apr_deregister(__adm->apr); + if (rval < 0) + return rval; + + kfree(__adm); + __adm = NULL; + + return 0; +} + +static const struct of_device_id qcom_q6adm_match[] = { + {.compatible = "qcom,q6adm-v2",}, + {} +}; + +static struct platform_driver qcom_q6adm_driver = { + .probe = q6adm_probe, + .remove = q6adm_exit, + .driver = { + .name = "qcom-q6adm", + .of_match_table = qcom_q6adm_match, + }, +}; + +module_platform_driver(qcom_q6adm_driver); +MODULE_AUTHOR("Srinivas Kandagatla X-Patchwork-Id: 109875 Delivered-To: patch@linaro.org Received: by 10.140.95.78 with SMTP id h72csp920271qge; Fri, 11 Aug 2017 06:32:57 -0700 (PDT) X-Received: by 10.84.212.22 with SMTP id d22mr17807997pli.310.1502458377793; Fri, 11 Aug 2017 06:32:57 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1502458377; cv=none; d=google.com; s=arc-20160816; b=rNwt7NgcQZzoTWlKgedZQFSidOC78aqxqLPQlQ4FfP/276bZC49nQzq3bPkQCP22CM ymobOHeQKh5u1yePXrLBJ3/eE0jzEwtgB7JrCg2Z11RjG6MOW8KC5nBs4IJaggLoS+rv G/Nntzq7aKtFlcRyrPXfU7znKRF/7TmBDA4rjRbVShEQ/gpCUWZWyvPfxY7Mbko38Nn8 4dbpyDR6tWNVZ646B/K8VZiyUTmH6MmbAefOtItFho7F4jAm9zOlSBN/ii58BCE28Gxj /dln1psl8HL5HqUvd4OV2aH2NJxWEQT5lsUOvxrG6lcC+p6rMbBrL0N+/WHY0ivFPaWW 68gQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=sUSf//ckCtWfZvUOUurG2d87QrNUKRiFZcJoQL/3JU0=; b=frBcKPwizMcZrdW7D6Es0l0H/BPw72rl/wxTsfuRGWx1+V+Lbyxx0y3e8dgP3bb2xM s+pbrOgUYC+ic5lf9aP5pLpmi0aqhrabb2uu4LfimI9xbnJsTim1vIY5ITT9Bgj9uRpw voIzT7C+1Se/9oX5YT6f2DJG4lTj058ZXelOUTzZYi1JG0wQAPtXIa73J/veVtw8ep9p zKwj+997XB5gSIiPt2zUAzEtqmmsiC5sKUNwLEstG654S3T6zn+CloEsOjt59aRckDVS GUmDfQbKfbPktQxaSEgudQkV0sJolnZduef+lNpnVgTcph3YWIBAos3ZZShpdtaMb6Cm 69ZQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=beKVg83X; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id z73si504706pgz.402.2017.08.11.06.32.57; Fri, 11 Aug 2017 06:32:57 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=beKVg83X; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753461AbdHKNcy (ORCPT + 25 others); Fri, 11 Aug 2017 09:32:54 -0400 Received: from mail-it0-f42.google.com ([209.85.214.42]:33677 "EHLO mail-it0-f42.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753201AbdHKNaP (ORCPT ); Fri, 11 Aug 2017 09:30:15 -0400 Received: by mail-it0-f42.google.com with SMTP id f16so1670767itb.0 for ; Fri, 11 Aug 2017 06:30:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=sUSf//ckCtWfZvUOUurG2d87QrNUKRiFZcJoQL/3JU0=; b=beKVg83X1RUrDzfF2PFpY/YVKZ5/SMnpS9M7x+Z4lhDGIZ1do5UFBRV4FNJueQkRbi BuphSWOpWVNTgpbZnV1CHajhkiWVtE7pFazpU7Q8NKLL9OlIYaJKUN5D0bnIt3KaJzPR AHBTy81Rz/BAUx9o2HtYh/Z1inAuLNo2e3rc0= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=sUSf//ckCtWfZvUOUurG2d87QrNUKRiFZcJoQL/3JU0=; b=jladEXj5WaeMY8ZC8510a8sp38LpSB6U9Tt4KP4PjtjHC0Ga8yIJP9eNEMvgN4Ef9L 9YDU/hN+2QwlaKH9uiohUSRSNS6acxMXBO2/yw+9cbPS5smGZIe7coV/N58lOiFNzOap +IQTbq8Py+C9U8Wuy4P0d86ugA/AYYig0kdX46+Cdcj0piEnknjBKGi3oATuMmi5ykbh 6w4/OrycEVOICDKPLj2i47e/urBnUC+9wn8TW+wL2hM/ZoRlo1dV2Nr95iNaaJVC+Gro 1NlSJ43H0tgSgAg0E2zn4NojGb9bRh5mfBokQgtbZH9RBo9ZXx0TNCR83UjuxYT+BxXx 4/nw== X-Gm-Message-State: AIVw1117xtDbVKCXCHtoz9MKZdI0VbBx3aSJaTkTzAqghF1dC8tGJugV Hf6tspDRer53iplK X-Received: by 10.36.252.69 with SMTP id b66mr12455574ith.97.1502458213656; Fri, 11 Aug 2017 06:30:13 -0700 (PDT) Received: from localhost.localdomain (static.8.26.4.46.clients.your-server.de. [46.4.26.8]) by smtp.gmail.com with ESMTPSA id y72sm398422iod.40.2017.08.11.06.30.11 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 11 Aug 2017 06:30:13 -0700 (PDT) From: srinivas.kandagatla@linaro.org To: Mark Brown , Banajit Goswami , alsa-devel@alsa-project.org Cc: Takashi Iwai , Patrick Lai , linux-kernel@vger.kernel.org, kwestfie@codeaurora.org, linux-arm-msm@vger.kernel.org, lkasam@qti.qualcomm.com, sboyd@codeaurora.org, Srinivas Kandagatla Subject: [RFC PATCH 5/9] ASoC: qcom: qdsp6v2: Add support to q6 routing driver Date: Fri, 11 Aug 2017 15:29:48 +0200 Message-Id: <20170811132952.32572-6-srinivas.kandagatla@linaro.org> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20170811132952.32572-1-srinivas.kandagatla@linaro.org> References: <20170811132952.32572-1-srinivas.kandagatla@linaro.org> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Srinivas Kandagatla This patch adds support to q6 routing driver which configures route between ASM and AFE module using ADM apis. This driver uses dapm widgets to setup the matrix between AFE ports and ASM streams. Signed-off-by: Srinivas Kandagatla --- .../bindings/sound/qcom,q6pcm-routing.txt | 21 ++ sound/soc/qcom/Kconfig | 5 + sound/soc/qcom/qdsp6v2/Makefile | 1 + sound/soc/qcom/qdsp6v2/q6routing-v2.h | 8 + sound/soc/qcom/qdsp6v2/q6routing.c | 403 +++++++++++++++++++++ 5 files changed, 438 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/qcom,q6pcm-routing.txt create mode 100644 sound/soc/qcom/qdsp6v2/q6routing-v2.h create mode 100644 sound/soc/qcom/qdsp6v2/q6routing.c -- 2.9.3 diff --git a/Documentation/devicetree/bindings/sound/qcom,q6pcm-routing.txt b/Documentation/devicetree/bindings/sound/qcom,q6pcm-routing.txt new file mode 100644 index 0000000..e3de107 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/qcom,q6pcm-routing.txt @@ -0,0 +1,21 @@ +Qualcomm Q6 PCM Routing binding + +This bindings describe the Qualcomm Q6 PCM routing module + +- compatible: + + Usage: required + Value type: + Definition: must be "qcom,q6pcm-routing-v" + example "qcom,q6pcm-routing-v2" +#sound-dai-cells: + Usage: required + Value type: + Definition: Must be equal to 0 + + += EXAMPLE + q6pcm_routing { + compatible = "qcom,q6pcm-routing-v2"; + #sound-dai-cells = <0>; + }; diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index bd49813..9a5b3f0 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -56,12 +56,17 @@ config SND_SOC_QDSP6V2_ASM tristate default n +config SND_SOC_QDSP6V2_ROUTING + tristate + default n + config SND_SOC_QDSP6V2 tristate "SoC ALSA audio driver for QDSP6V2" select SND_SOC_QDSP6V2_AFE select SND_SOC_QDSP6V2_ADM select SND_SOC_QDSP6V2_ASM + select SND_SOC_QDSP6V2_ROUTING help To add support for MSM QDSP6V2 Soc Audio. This will enable sound soc platform specific diff --git a/sound/soc/qcom/qdsp6v2/Makefile b/sound/soc/qcom/qdsp6v2/Makefile index 00b0549..81d2341 100644 --- a/sound/soc/qcom/qdsp6v2/Makefile +++ b/sound/soc/qcom/qdsp6v2/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_SND_SOC_QDSP6V2_AFE) += q6afe.o obj-$(CONFIG_SND_SOC_QDSP6V2_ADM) += q6adm.o obj-$(CONFIG_SND_SOC_QDSP6V2_ASM) += q6asm.o +obj-$(CONFIG_SND_SOC_QDSP6V2_ROUTING) += q6routing.o diff --git a/sound/soc/qcom/qdsp6v2/q6routing-v2.h b/sound/soc/qcom/qdsp6v2/q6routing-v2.h new file mode 100644 index 0000000..2cbf572 --- /dev/null +++ b/sound/soc/qcom/qdsp6v2/q6routing-v2.h @@ -0,0 +1,8 @@ +#ifndef _MSM_PCM_ROUTING_H +#define _MSM_PCM_ROUTING_H + +int routing_reg_phy_stream(int fedai_id, int perf_mode, + int stream_id, int stream_type); +void routing_dereg_phy_stream(int fedai_id, int stream_type); + +#endif /*_MSM_PCM_ROUTING_H */ diff --git a/sound/soc/qcom/qdsp6v2/q6routing.c b/sound/soc/qcom/qdsp6v2/q6routing.c new file mode 100644 index 0000000..a741ab3 --- /dev/null +++ b/sound/soc/qcom/qdsp6v2/q6routing.c @@ -0,0 +1,403 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "q6afe-v2.h" +#include "q6asm-v2.h" +#include "q6adm-v2.h" +#include "q6routing-v2.h" + +struct session_data { + int state; + int port_id; + int path_type; + int app_type; + int acdb_id; + int sample_rate; + int bits_per_sample; + int channels; + int format; + int perf_mode; + int numcopps; + int fedai_id; + unsigned long copp_map; + +}; + +struct msm_routing_data { + struct session_data sessions[MAX_SESSIONS]; + struct q6adm *adm; + struct mutex lock; +}; + +static struct msm_routing_data *routing_data; + +int routing_reg_phy_stream(int fedai_id, int perf_mode, + int stream_id, int stream_type) +{ + int j, topology, num_copps = 0; + struct route_payload payload; + int copp_idx; + struct session_data *session; + + if (!routing_data) { + pr_err("Routing driver not yet ready\n"); + return -EINVAL; + } + + session = &routing_data->sessions[stream_id - 1]; + mutex_lock(&routing_data->lock); + session->fedai_id = fedai_id; + payload.num_copps = 0; /* only RX needs to use payload */ + topology = NULL_COPP_TOPOLOGY; + copp_idx = q6adm_open(routing_data->adm, session->port_id, + session->path_type, session->sample_rate, + session->channels, topology, perf_mode, + session->bits_per_sample, 0, 0); + if ((copp_idx < 0) || (copp_idx >= MAX_COPPS_PER_PORT)) { + pr_err("%s: adm open failed copp_idx:%d\n", __func__, copp_idx); + mutex_unlock(&routing_data->lock); + return -EINVAL; + } + + set_bit(copp_idx, &session->copp_map); + for (j = 0; j < MAX_COPPS_PER_PORT; j++) { + unsigned long copp = session->copp_map; + + if (test_bit(j, &copp)) { + payload.port_id[num_copps] = session->port_id; + payload.copp_idx[num_copps] = j; + num_copps++; + } + } + + if (num_copps) { + payload.num_copps = num_copps; + payload.session_id = stream_id; + payload.app_type = 0; + payload.acdb_dev_id = 0; + payload.sample_rate = session->sample_rate; + q6adm_matrix_map(session->path_type, payload, perf_mode); + } + mutex_unlock(&routing_data->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(routing_reg_phy_stream); + +static struct session_data *routing_get_session(struct msm_routing_data *data, + int port_id, int port_type) +{ + int i; + + for (i = 0; i < MAX_SESSIONS; i++) + if (port_id == data->sessions[i].port_id) + return &data->sessions[i]; + + return NULL; +} + +static struct session_data *get_session_from_id(struct msm_routing_data *data, + int fedai_id) +{ + int i; + + for (i = 0; i < MAX_SESSIONS; i++) { + if (fedai_id == data->sessions[i].fedai_id) + return &data->sessions[i]; + } + + return NULL; +} + +void routing_dereg_phy_stream(int fedai_id, int stream_type) +{ + struct session_data *session; + int idx; + + session = get_session_from_id(routing_data, fedai_id); + if (!session) + return; + + for_each_set_bit(idx, &session->copp_map, MAX_COPPS_PER_PORT) + q6adm_close(session->port_id, session->perf_mode, idx); + + session->fedai_id = -1; + session->copp_map = 0; +} +EXPORT_SYMBOL_GPL(routing_dereg_phy_stream); + +static int msm_routing_get_audio_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int session_id = mc->shift; + struct snd_soc_platform *platform = snd_soc_dapm_to_platform(dapm); + struct msm_routing_data *priv = snd_soc_platform_get_drvdata(platform); + struct session_data *session = &priv->sessions[session_id]; + + if (session->port_id != -1) + ucontrol->value.integer.value[0] = 1; + else + ucontrol->value.integer.value[0] = 0; + + pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int msm_routing_put_audio_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct snd_soc_platform *platform = snd_soc_dapm_to_platform(dapm); + struct msm_routing_data *data = snd_soc_platform_get_drvdata(platform); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_dapm_update *update = NULL; + int be_id = mc->reg; + int session_id = mc->shift; + struct session_data *session = &data->sessions[session_id]; + + if (ucontrol->value.integer.value[0]) { + session->port_id = be_id; + snd_soc_dapm_mixer_update_power(dapm, kcontrol, 1, update); + } else { + session->port_id = -1; + snd_soc_dapm_mixer_update_power(dapm, kcontrol, 0, update); + } + + return 1; +} + +static const struct snd_kcontrol_new hdmi_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", AFE_PORT_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia2", AFE_PORT_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia3", AFE_PORT_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia4", AFE_PORT_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia5", AFE_PORT_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia6", AFE_PORT_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia7", AFE_PORT_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia8", AFE_PORT_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, + msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { + /* Frontend AIF */ + /* Widget name equals to Front-End DAI name, + * Stream name must contains substring of front-end dai name + */ + SND_SOC_DAPM_AIF_IN("MM_DL1", "MultiMedia1 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL2", "MultiMedia2 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL3", "MultiMedia3 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL4", "MultiMedia4 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL5", "MultiMedia5 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL6", "MultiMedia6 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL7", "MultiMedia7 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL8", "MultiMedia8 Playback", 0, 0, 0, 0), + + /* Mixer definitions */ + SND_SOC_DAPM_MIXER("HDMI Mixer", SND_SOC_NOPM, 0, 0, + hdmi_mixer_controls, + ARRAY_SIZE(hdmi_mixer_controls)), +}; + +static const struct snd_soc_dapm_route intercon[] = { + {"HDMI Mixer", "MultiMedia1", "MM_DL1"}, + {"HDMI Mixer", "MultiMedia2", "MM_DL2"}, + {"HDMI Mixer", "MultiMedia3", "MM_DL3"}, + {"HDMI Mixer", "MultiMedia4", "MM_DL4"}, + {"HDMI Mixer", "MultiMedia5", "MM_DL5"}, + {"HDMI Mixer", "MultiMedia6", "MM_DL6"}, + {"HDMI Mixer", "MultiMedia7", "MM_DL7"}, + {"HDMI Mixer", "MultiMedia8", "MM_DL8"}, + {"HDMI", NULL, "HDMI Mixer"}, + {"HDMI-RX", NULL, "HDMI"}, +}; + +static int routing_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + unsigned int be_id = rtd->cpu_dai->id; + struct snd_soc_platform *platform = rtd->platform; + struct msm_routing_data *data = snd_soc_platform_get_drvdata(platform); + struct session_data *session; + int port_id, port_type, path_type, bits_per_sample; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + path_type = ADM_PATH_PLAYBACK; + port_type = MSM_AFE_PORT_TYPE_RX; + } + + port_id = be_id; + + session = routing_get_session(data, port_id, port_type); + + if (!session) { + pr_err("No session matrix setup yet..\n"); + return -EINVAL; + } + + mutex_lock(&data->lock); + + session->path_type = path_type; + session->sample_rate = params_rate(params); + session->channels = params_channels(params); + session->format = params_format(params); + + if (session->format == SNDRV_PCM_FORMAT_S16_LE) + session->bits_per_sample = 16; + else if (session->format == SNDRV_PCM_FORMAT_S24_LE) + bits_per_sample = 24; + + mutex_unlock(&data->lock); + return 0; +} + +static int routing_close(struct snd_pcm_substream *substream) +{ + return 0; +} + +static int routing_prepare(struct snd_pcm_substream *substream) +{ + return 0; +} + +static struct snd_pcm_ops q6pcm_routing_ops = { + .hw_params = routing_hw_params, + .close = routing_close, + .prepare = routing_prepare, +}; + +/* Not used but frame seems to require it */ +static int msm_routing_probe(struct snd_soc_platform *platform) +{ + int i; + + for (i = 0; i < MAX_SESSIONS; i++) + routing_data->sessions[i].port_id = -1; + + snd_soc_platform_set_drvdata(platform, routing_data); + + return 0; +} + +static struct snd_soc_platform_driver msm_soc_routing_platform = { + .ops = &q6pcm_routing_ops, + .probe = msm_routing_probe, + .component_driver = { + .dapm_widgets = msm_qdsp6_widgets, + .num_dapm_widgets = ARRAY_SIZE(msm_qdsp6_widgets), + .dapm_routes = intercon, + .num_dapm_routes = ARRAY_SIZE(intercon), + }, +}; + +static int q6pcm_routing_probe(struct platform_device *pdev) +{ + + struct q6adm *adm = q6adm_get(&pdev->dev); + + if (IS_ERR(adm)) { + dev_err(&pdev->dev, "unable to get instance of adm\n"); + return PTR_ERR(adm); + } + + routing_data = devm_kzalloc(&pdev->dev, + sizeof(*routing_data), GFP_KERNEL); + if (!routing_data) + return -ENOMEM; + + routing_data->adm = adm; + mutex_init(&routing_data->lock); + dev_set_drvdata(&pdev->dev, routing_data); + + return snd_soc_register_platform(&pdev->dev, &msm_soc_routing_platform); +} + +static int q6pcm_routing_remove(struct platform_device *pdev) +{ + struct msm_routing_data *data = dev_get_drvdata(&pdev->dev); + + q6adm_put(data->adm); + + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static const struct of_device_id routing_dt_match[] = { + {.compatible = "qcom,q6pcm-routing-v2"}, + {} +}; + +MODULE_DEVICE_TABLE(of, routing_dt_match); + +static struct platform_driver q6pcm_routing_driver = { + .driver = { + .name = "q6pcm-routing", + .owner = THIS_MODULE, + .of_match_table = routing_dt_match, + }, + .probe = q6pcm_routing_probe, + .remove = q6pcm_routing_remove, +}; + +module_platform_driver(q6pcm_routing_driver); + +MODULE_DESCRIPTION("MSM routing platform driver"); +MODULE_LICENSE("GPL v2"); From patchwork Fri Aug 11 13:29:51 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Srinivas Kandagatla X-Patchwork-Id: 109872 Delivered-To: patch@linaro.org Received: by 10.140.95.78 with SMTP id h72csp918891qge; Fri, 11 Aug 2017 06:31:53 -0700 (PDT) X-Received: by 10.84.217.13 with SMTP id o13mr18029094pli.315.1502458313667; Fri, 11 Aug 2017 06:31:53 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1502458313; cv=none; d=google.com; s=arc-20160816; b=VpvtuTOA/LM4dHXcqbfHRK7hpf8AmRn67jq8i0tpUe5Tr+cWoYoIlr4qe7MI8P9bvT mdifPNjzgCIl43W0jaPhrjAZWhb8HxnMVdHoprdw/R9JSQ8qV4nUbI9gLUcUNOHoyKGF QXRuEjj4Hif3XPqOz7CskiNuTxabEFZxZCAvEWFRDx6UIMXOq3YWwRD9skqvVj+YYjGk B30wSP1q1560I6mCM8pdS2rPcXjN9NEIi3LusFL61HZocjnuwSIdiDFqLeyHjewmaMJC bsTykNhJCqMH0I1UJXw/BALr2ID5BR0exjlFOMkLmVkaqxS+tNUh8kR3fN+eOVYDTcaD U9Ww== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=q7gdXYnxY6xYfOj4oelxzFeuMkBRh+V87Ll6XqPslvM=; b=tFUnOL2isYUURLOg/VgErFLJPGK6e2E+2G+OAJxRPblqsIRJMPAY4/j3oJ06d9CE8D T/aP32CUX/gCzNm9diodDtx5zWMGJYaoh1hMUCEOOq8EwWOvN6pEbndvhfjk/NkhE8eL R0vill8synxD0ANI+nWUO4uRcHNc87UVVtN1/aJKFhfJXydxkNEnbl0MgZ6P/Lo3YQhm IR6AXFqXndnUw7lHGTKrcO1Oj860+xhB613SctUSLprCUEm78hqNfb+B8EHxvzqsNyPu rV6N1UgG0irrWlWWj1+Hj7ieYtCa8KxIccWhGAOK0t5Mnn4ijRRR6fMKKRkN78RB/Nt9 7yRw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=hZMxzrl+; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id s11si493039pgn.850.2017.08.11.06.31.53; Fri, 11 Aug 2017 06:31:53 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=hZMxzrl+; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753425AbdHKNbj (ORCPT + 25 others); Fri, 11 Aug 2017 09:31:39 -0400 Received: from mail-it0-f47.google.com ([209.85.214.47]:35915 "EHLO mail-it0-f47.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753283AbdHKNaV (ORCPT ); Fri, 11 Aug 2017 09:30:21 -0400 Received: by mail-it0-f47.google.com with SMTP id 77so27768895itj.1 for ; Fri, 11 Aug 2017 06:30:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=q7gdXYnxY6xYfOj4oelxzFeuMkBRh+V87Ll6XqPslvM=; b=hZMxzrl+v3YhcXDNfKCI/mDFvuGlVn1wZ6fzbPDqvG7tBtPU6Q6k/TfM4ZL5hxnQJn pfpfFTMVO2wqUO9NQ/gr8owydTIf8BvMFkmO4/XNkuqty2ffmAA7NPns0PWYhLFC7UjT t0dbptYtizIXPGgNGZ1L9ejorRlJ1GQrQk9qE= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=q7gdXYnxY6xYfOj4oelxzFeuMkBRh+V87Ll6XqPslvM=; b=NKESesYjvwuIogbWv4JGa2fWREGdcnMD3Qnvj4Wfjqt0OlL12rFavEINgBc0UgJQs7 yi86hyaHgMyVDZpouO9ZKNxG4/A+8S9JNquKjWuXTtiQxAx0yrgy6o1fJQotDajnJreT S1LybLIx+ed+q0tpPz/2IL3lego/f+V5RDtYDNRuOmu07NvZiDuBtS7E5nc18mz2ubO/ pqKUPqtfdtioFONkXa5c9Hfq0BsLBmxYFZQ2nnEC6+L1YvNpQTbosX67dUEOEdtYD/el kWpERWqm4zNNYWUqRFfh+djNL3c4aNpMQKUvzDvCVC6BjsfAWF//1slZfrbfA5O3X4Vt kHoQ== X-Gm-Message-State: AHYfb5hBDYeLOwTkSzDddgkuwtO8O1jcyfWU6EbieGX1I/Uv2mVm05uE 3XAPLX2lL493ZR6f X-Received: by 10.36.125.11 with SMTP id b11mr14090864itc.47.1502458220662; Fri, 11 Aug 2017 06:30:20 -0700 (PDT) Received: from localhost.localdomain (static.8.26.4.46.clients.your-server.de. [46.4.26.8]) by smtp.gmail.com with ESMTPSA id y72sm398422iod.40.2017.08.11.06.30.18 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 11 Aug 2017 06:30:20 -0700 (PDT) From: srinivas.kandagatla@linaro.org To: Mark Brown , Banajit Goswami , alsa-devel@alsa-project.org Cc: Takashi Iwai , Patrick Lai , linux-kernel@vger.kernel.org, kwestfie@codeaurora.org, linux-arm-msm@vger.kernel.org, lkasam@qti.qualcomm.com, sboyd@codeaurora.org, Srinivas Kandagatla Subject: [RFC PATCH 8/9] ASoC: qcom: apq8096: Add db820c machine driver Date: Fri, 11 Aug 2017 15:29:51 +0200 Message-Id: <20170811132952.32572-9-srinivas.kandagatla@linaro.org> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20170811132952.32572-1-srinivas.kandagatla@linaro.org> References: <20170811132952.32572-1-srinivas.kandagatla@linaro.org> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Srinivas Kandagatla uThis patch adds support to DB820c machine driver. Signed-off-by: Srinivas Kandagatla --- .../devicetree/bindings/sound/qcom,apq8096.txt | 85 ++++++++++ sound/soc/qcom/Kconfig | 9 +- sound/soc/qcom/Makefile | 2 + sound/soc/qcom/apq8096.c | 174 +++++++++++++++++++++ 4 files changed, 269 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/sound/qcom,apq8096.txt create mode 100644 sound/soc/qcom/apq8096.c -- 2.9.3 diff --git a/Documentation/devicetree/bindings/sound/qcom,apq8096.txt b/Documentation/devicetree/bindings/sound/qcom,apq8096.txt new file mode 100644 index 0000000..cf6a15b --- /dev/null +++ b/Documentation/devicetree/bindings/sound/qcom,apq8096.txt @@ -0,0 +1,85 @@ +* Qualcomm Technologies APQ8096 ASoC sound card driver + +This binding describes the APQ8096 sound card, which uses qdsp for audio. + +- compatible: + Usage: required + Value type: + Definition: must be "qcom,apq8096-sndcard" + += FRONTEND and BACKEND dailinks +Each subnode of sndcard represents either frontend or backend dailink, +and subnodes of each backend/frontend dailinks would be +cpu/codec/platform dais. + +- fe: + Usage: required + Value type: + Definition: FrontEnd CPU dailink sub-node + +- be: + Usage: required + Value type: + Definition: BackEnd dailink sub-node + +- link-name: + Usage: required + Value type: msm_dai_q6_hdmi + Definition: User friendly name for dai link + +- is-fe: + Usage: optional + Value type: + Definition: present if the dailink is frontend + + += CPU, PLATFORM, CODEC dais subnodes +- cpu: + Usage: required + Value type: + Definition: cpu dai sub-node + +- codec: + Usage: required + Value type: + Definition: codec dai sub-node + +- platform: + Usage: opional + Value type: + Definition: platform dai sub-node + +- sound-dai: + Usage: required + Value type: + Definition: phandle/s and port of CPU/CODEC/PLATFORM dais + +Example: + sound { + compatible = "qcom,snd-apq8096"; + qcom,model = "DB820c"; + + fe@1 { + is-fe; + link-name = "MultiMedia1 PCM"; + cpu { + sound-dai = <&pcm MSM_FRONTEND_DAI_MULTIMEDIA1>; + }; + platform { + sound-dai = <&pcm MSM_FRONTEND_DAI_MULTIMEDIA1>; + }; + }; + + be@1 { + link-name = "HDMI"; + cpu { + sound-dai = <&hdmi_dai>; + }; + platform { + sound-dai = <&routing>; + }; + codec { + sound-dai = <&hdmi>; + }; + }; + }; diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index a2aa480..c190d11 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -68,7 +68,6 @@ config SND_SOC_QDSP6V2_PCM tristate default n - config SND_SOC_QDSP6V2 tristate "SoC ALSA audio driver for QDSP6V2" select SND_SOC_QDSP6V2_AFE @@ -82,3 +81,11 @@ config SND_SOC_QDSP6V2 This will enable sound soc platform specific audio drivers. This includes q6asm, q6adm, q6afe interfaces to DSP using apr. + +config SND_SOC_MSM8996 + tristate "SoC Machine driver for MSM8996 and APQ8096 boards" + select SND_SOC_QDSP6V2 + default n + help + To add support for SoC audio on MSM8996 and APQ8096 boards + diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile index a51bf0a..f3889c4 100644 --- a/sound/soc/qcom/Makefile +++ b/sound/soc/qcom/Makefile @@ -12,9 +12,11 @@ obj-$(CONFIG_SND_SOC_LPASS_APQ8016) += snd-soc-lpass-apq8016.o # Machine snd-soc-storm-objs := storm.o snd-soc-apq8016-sbc-objs := apq8016_sbc.o +snd-soc-msm8996-objs := apq8096.o obj-$(CONFIG_SND_SOC_STORM) += snd-soc-storm.o obj-$(CONFIG_SND_SOC_APQ8016_SBC) += snd-soc-apq8016-sbc.o +obj-$(CONFIG_SND_SOC_MSM8996) += snd-soc-msm8996.o #DSP lib obj-$(CONFIG_SND_SOC_QDSP6V2) += qdsp6v2/ diff --git a/sound/soc/qcom/apq8096.c b/sound/soc/qcom/apq8096.c new file mode 100644 index 0000000..de62613 --- /dev/null +++ b/sound/soc/qcom/apq8096.c @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int msm8996_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + + return 0; +} + +static int apq8096_sbc_parse_of(struct snd_soc_card *card) +{ + struct device *dev = card->dev; + struct snd_soc_dai_link *link; + struct device_node *np, *codec, *platform, *cpu, *node = dev->of_node; + int ret, num_links; + bool is_fe; + + ret = snd_soc_of_parse_card_name(card, "qcom,model"); + if (ret) { + dev_err(dev, "Error parsing card name: %d\n", ret); + return ret; + } + + num_links = of_get_child_count(node); + + /* Allocate the private data and the DAI link array */ + card->dai_link = devm_kzalloc(dev, sizeof(*link) * num_links, + GFP_KERNEL); + if (!card->dai_link) + return -ENOMEM; + + card->num_links = num_links; + + link = &card->dai_link[0]; + + for_each_child_of_node(node, np) { + is_fe = false; + if (of_property_read_bool(np, "is-fe")) + is_fe = true; + + if (is_fe) { + /* BE is dummy */ + link->codec_of_node = NULL; + link->codec_dai_name = "snd-soc-dummy-dai"; + link->codec_name = "snd-soc-dummy"; + + /* FE settings */ + link->dynamic = 1; + link->dpcm_playback = 1; + + } else { + link->no_pcm = 1; + link->dpcm_playback = 1; + link->be_hw_params_fixup = msm8996_be_hw_params_fixup; + } + + cpu = of_get_child_by_name(np, "cpu"); + platform = of_get_child_by_name(np, "platform"); + codec = of_get_child_by_name(np, "codec"); + + if (!cpu) { + dev_err(dev, "Can't find cpu DT node\n"); + return -EINVAL; + } + + link->cpu_of_node = of_parse_phandle(cpu, "sound-dai", 0); + if (!link->cpu_of_node) { + dev_err(card->dev, "error getting cpu phandle\n"); + return -EINVAL; + } + + link->platform_of_node = of_parse_phandle(platform, + "sound-dai", 0); + if (!link->platform_of_node) { + dev_err(card->dev, "error getting platform phandle\n"); + return -EINVAL; + } + + ret = snd_soc_of_get_dai_name(cpu, &link->cpu_dai_name); + if (ret) { + dev_err(card->dev, "error getting cpu dai name\n"); + return ret; + } + + if (codec) { + ret = snd_soc_of_get_dai_link_codecs(dev, codec, link); + + if (ret < 0) { + dev_err(card->dev, "error getting codec dai name\n"); + return ret; + } + } + + ret = of_property_read_string(np, "link-name", &link->name); + if (ret) { + dev_err(card->dev, "error getting codec dai_link name\n"); + return ret; + } + + link->stream_name = link->name; + link++; + } + + return 0; +} + +static int msm_snd_apq8096_probe(struct platform_device *pdev) +{ + int ret; + struct snd_soc_card *card; + + card = devm_kzalloc(&pdev->dev, sizeof(*card), GFP_KERNEL); + if (!card) + return -ENOMEM; + + card->dev = &pdev->dev; + ret = apq8096_sbc_parse_of(card); + if (ret) { + dev_err(&pdev->dev, "Error parsing OF data\n"); + return ret; + } + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) + dev_err(&pdev->dev, "sound card register faile (%d)!\n", ret); + + return ret; +} + +static const struct of_device_id msm_snd_apq8096_dt_match[] = { + {.compatible = "qcom,apq8096-sndcard"}, + {} +}; + +static struct platform_driver msm_snd_apq8096_driver = { + .probe = msm_snd_apq8096_probe, + .driver = { + .name = "msm-snd-apq8096", + .owner = THIS_MODULE, + .of_match_table = msm_snd_apq8096_dt_match, + }, +}; +module_platform_driver(msm_snd_apq8096_driver); +MODULE_AUTHOR("Srinivas Kandagatla X-Patchwork-Id: 109866 Delivered-To: patch@linaro.org Received: by 10.140.95.78 with SMTP id h72csp917001qge; Fri, 11 Aug 2017 06:30:30 -0700 (PDT) X-Received: by 10.101.69.205 with SMTP id m13mr15242422pgr.105.1502458230563; Fri, 11 Aug 2017 06:30:30 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1502458230; cv=none; d=google.com; s=arc-20160816; b=SvyqBgXUwZTc3Qeazqm4tE2Ad9Kam2LO+Phh24dLyJxR9UgbrAtJ8Fxr1RH3NYNjOb BvIkukiaQGiTH6f2if3gq8IS+c8yFrwFB/0lek7Z5cv+x24HIsb9fiTvZ9d8shQ+hjo7 /qCNIWz4mMYpPRZWc8i+FnPG2mKcF43HekUj/Qw/Cyq3ziINsaP5riYsaNFY2VLt5rCw EXfT7o8M/26V9UFm60SvH6j+E15TV+0S66Xxv02vrjtdwHilWe2Xg6AZkdVT5RO5MZUV JtBd0rGl8/Hs83iG+9qYqcUI6iwB88EuoaJauQ/WlFXCnOe4bx4QtZYQzvyUYs+EWZdE 1h/g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=xY718JSm530KPBX3nqtJ0OAiTyvaDbVHhdGj6+WxN5I=; b=YsrR3kJjVu4uPB3G2xng73xKd8CDmVR1zE8oFxlu1k3pbBOP7ICyE7o7jbbX6QPKTz i3t6G4WbFX6tZxAlkFeWN0nJ3mtcXy/HoztDMIaJowuMIonwhlK4tYyzw2Efo60fMucd 5xWLBwrka80zsXPOVRUsGtykXVq+H2SF5YTsg3qh92WzXD4XIjhXczvK/YGeYP4IFFYA X77SxaIj1gn2OoTO2Hcnlbf4misB1xASLUkN2tiV2ZEuOVJYxyrZeCMkjSsVMx8Qom00 cEC383NX73WGmFPRRCbH3ODuhI8mmA9npvQiUxasITcDBaQ7q4AVqU5XFttDZgUzuz2U gETQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=TtZ5FDny; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id 73si533905pfw.130.2017.08.11.06.30.30; Fri, 11 Aug 2017 06:30:30 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=TtZ5FDny; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753341AbdHKNa1 (ORCPT + 25 others); Fri, 11 Aug 2017 09:30:27 -0400 Received: from mail-it0-f53.google.com ([209.85.214.53]:37095 "EHLO mail-it0-f53.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753094AbdHKNaX (ORCPT ); Fri, 11 Aug 2017 09:30:23 -0400 Received: by mail-it0-f53.google.com with SMTP id 76so27088769ith.0 for ; Fri, 11 Aug 2017 06:30:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=xY718JSm530KPBX3nqtJ0OAiTyvaDbVHhdGj6+WxN5I=; b=TtZ5FDnypQm4h9CRlBJJqEIGHCOtFizXhFscMd8OAodETKMn0lv6bNBERMCHsc/liy DE2mlHO6sAoYehOMjacOsUU35K3CEfZhw8IuX+C6LDwxvKlpdEY109REqxOxQqX5SFCp 9Cf7dyJIbJXcoZKyIUWWO/NkESUpyr+n4f4TU= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=xY718JSm530KPBX3nqtJ0OAiTyvaDbVHhdGj6+WxN5I=; b=EKrPdKpxvcw6fRuRYAZVOqyateGv3SCoH+TFVEHKUixMkITg9skfDW8rZKWVjSFYjO PDP6RA0uoo1VhHmwXk1VFgIqsxKFqjwSlMMADBNALKGG6NQ6AqGmLrE6TQaVaeKKBTIj bSSx8oyGpLSQAOXnfRbYNJm95Itr95fDUyfu1deQpG6ea0li/aVVtmrb0wXaeYRyaAkP NttRSYhFvyI9jwmO7fZT4V5VJdrHTcqk7ONBMgyKgZU8wbRfJY8wOGtUqUJzOnSqb/Jn BGqvQj9LUhjfaF4HQRw5LArsSlTqoDjj1sWCqlzWJqeDe94q5qsoK1A0aH7PLIaKD0OU qeLg== X-Gm-Message-State: AHYfb5iWj5Nxq6of0bKRrCE1dmSQMM+tt18g/Z+Py/jApsILBjtHe2pU /6cYgmT79QnY5+nD X-Received: by 10.36.73.25 with SMTP id z25mr13075280ita.153.1502458222935; Fri, 11 Aug 2017 06:30:22 -0700 (PDT) Received: from localhost.localdomain (static.8.26.4.46.clients.your-server.de. [46.4.26.8]) by smtp.gmail.com with ESMTPSA id y72sm398422iod.40.2017.08.11.06.30.20 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 11 Aug 2017 06:30:22 -0700 (PDT) From: srinivas.kandagatla@linaro.org To: Mark Brown , Banajit Goswami , alsa-devel@alsa-project.org Cc: Takashi Iwai , Patrick Lai , linux-kernel@vger.kernel.org, kwestfie@codeaurora.org, linux-arm-msm@vger.kernel.org, lkasam@qti.qualcomm.com, sboyd@codeaurora.org, Srinivas Kandagatla Subject: [RFC PATCH 9/9] arm64: dts: msm8996: db820c: Add sound card support Date: Fri, 11 Aug 2017 15:29:52 +0200 Message-Id: <20170811132952.32572-10-srinivas.kandagatla@linaro.org> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20170811132952.32572-1-srinivas.kandagatla@linaro.org> References: <20170811132952.32572-1-srinivas.kandagatla@linaro.org> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Srinivas Kandagatla This patch adds hdmi sound card support to db820c via qdsp. Signed-off-by: Srinivas Kandagatla --- arch/arm64/boot/dts/qcom/apq8096-db820c.dtsi | 37 +++++++++++++++ arch/arm64/boot/dts/qcom/msm8996.dtsi | 67 ++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) -- 2.9.3 diff --git a/arch/arm64/boot/dts/qcom/apq8096-db820c.dtsi b/arch/arm64/boot/dts/qcom/apq8096-db820c.dtsi index b9852ba..0157630 100644 --- a/arch/arm64/boot/dts/qcom/apq8096-db820c.dtsi +++ b/arch/arm64/boot/dts/qcom/apq8096-db820c.dtsi @@ -18,6 +18,8 @@ #include "apq8096-db820c-pmic-pins.dtsi" #include #include +#include +#include / { aliases { @@ -207,6 +209,41 @@ }; }; + adsp-pil { + smd-edge { + apr { + snd { + compatible = "qcom,apq8096-sndcard"; + qcom,model = "DB820c"; + + cpu@1 { + is-fe; + link-name = "MultiMedia1 PCM"; + cpu { + sound-dai = <&pcm MSM_FRONTEND_DAI_MULTIMEDIA1>; + }; + platform { + sound-dai = <&pcm MSM_FRONTEND_DAI_MULTIMEDIA1>; + }; + }; + + qdsp@1 { + link-name = "HDMI"; + cpu { + sound-dai = <&hdmi_dai>; + }; + platform { + sound-dai = <&routing>; + }; + codec { + sound-dai = <&hdmi>; + }; + }; + }; + }; + }; + }; + rpm-glink { rpm_requests { pm8994-regulators { diff --git a/arch/arm64/boot/dts/qcom/msm8996.dtsi b/arch/arm64/boot/dts/qcom/msm8996.dtsi index 9e3e3f5..163c251 100644 --- a/arch/arm64/boot/dts/qcom/msm8996.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8996.dtsi @@ -14,6 +14,8 @@ #include #include #include +#include +#include / { model = "Qualcomm Technologies, Inc. MSM8996"; @@ -1157,6 +1159,33 @@ status = "okay"; }; + lpass_q6_smmu: arm,smmu-lpass_q6@1600000 { + compatible = "qcom,msm8996-smmu-v2"; + reg = <0x1600000 0x20000>; + #iommu-cells = <1>; + power-domains = <&gcc HLOS1_VOTE_LPASS_CORE_GDSC>; + + #global-interrupts = <1>; + interrupts = , + , + , + , + , + , + , + , + , + , + , + , + ; + + clocks = <&gcc GCC_HLOS1_VOTE_LPASS_CORE_SMMU_CLK>, + <&gcc GCC_HLOS1_VOTE_LPASS_ADSP_SMMU_CLK>; + clock-names = "iface", "bus"; + status = "okay"; + }; + mdss: mdss@900000 { compatible = "qcom,mdss"; @@ -1240,6 +1269,7 @@ phys = <&hdmi_phy>; phy-names = "hdmi_phy"; + #sound-dai-cells = <0>; ports { #address-cells = <1>; @@ -1303,6 +1333,43 @@ qcom,ipc = <&apcs 16 8>; qcom,smd-edge = <1>; qcom,remote-pid = <2>; + + apr { + compatible = "qcom,apr-msm8996"; + qcom,smd-channels = "apr_audio_svc"; + + pcm: pcm0 { + compatible = "qcom,q6pcm-v2"; + iommus = <&lpass_q6_smmu 1>; + #sound-dai-cells = <1>; + }; + + routing:routing { + compatible = "qcom,q6pcm-routing-v2"; + #sound-dai-cells = <0>; + }; + + hdmi_dai: dai_hdmi { + compatible = "qcom,q6hdmi-v2"; + qcom,afe-port = <&afe AFE_PORT_HDMI_RX>; + #sound-dai-cells = <0>; + }; + + snd { + }; + + adm { + compatible = "qcom,q6adm-v2"; + }; + + asm { + compatible = "qcom,q6asm-v2"; + }; + + afe: afe { + compatible = "qcom,q6afe-v2"; + }; + }; }; };