From patchwork Fri May 18 12:55:56 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Srinivas Kandagatla X-Patchwork-Id: 136304 Delivered-To: patch@linaro.org Received: by 2002:a2e:9706:0:0:0:0:0 with SMTP id r6-v6csp1205915lji; Fri, 18 May 2018 05:58:52 -0700 (PDT) X-Google-Smtp-Source: AB8JxZpTj+a16HQ+tdG2oxgWwj1DnG8SXGpXnymiinYRQ8aCEKSGq84/FMhOwwTSa86ui13i5YHk X-Received: by 2002:adf:a0b8:: with SMTP id m53-v6mr7641663wrm.269.1526648332371; Fri, 18 May 2018 05:58:52 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1526648332; cv=none; d=google.com; s=arc-20160816; b=LdTtrXRs5P3p7LSfGwU8AQhn6ZXPKj2lAvDVHIhp9YEnmCMaoZ6IQAZ7wVHLJvahru yOv+478letBDrwdBddb+fz0zeuwY+sa0cH/zkUZiTTMHwWtxyofzqK7lmLobnV/scTLY 3L9c3rPq++tJFB7XzD83af09e4sBc5HjwL5d/LHiPVN2hwkG/jkG7K0aLvl251/KnUyt N4ZPGMmu+1cPbge++G4bT9cg5kjptPUjWcWQvuThYSLDb6mKbs9rrGWktYewQtV6mbEK A8WxjUz2bfczFiJFUDE87cA0wYNHTWtAgjlvQ8wA9dQRRdGysERy+9FDg+6i+a4LD8EJ c9Tw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:cc:references:in-reply-to:message-id :date:to:from:dkim-signature:delivered-to:arc-authentication-results; bh=9A8TN7yf6lqzXBlOIA+PmZxJ4IUvJolaDUTz1Js+Fjk=; b=jvJCMqKknklw7BZYnelOfUHdIjn7Gs9qlGvImKYkhtb7t31rC2CAbYK6mQCCk/4TU8 eh4RUvR/6v9SabkvEeg1RzVIx6WXxdQAd1lKc2JQIH60owLJMHX8TrxD3QsdMMVTcOFc vjOwFVAIbuM8lWO8FTnN5vtyY0v63s31jRDwF9XW0fa9dn8aQ7vJv+Un01sIxNSdyOb+ /07E55eyEV2unHv7vwldSY6BX2Vg5AAcZW7QbT49uuWapUSq0U8Rhmo1rBhnXCLWqc8S G62XHCxPqJ4f6iAopEVh3Xwv/GM7obqFHVYoSAIacXfS8aFfeCpNOz5xxsPpn4rPyj+G uOGg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@linaro.org header.s=google header.b=XXIgNOyK; spf=pass (google.com: domain of alsa-devel-bounces@alsa-project.org designates 77.48.224.243 as permitted sender) smtp.mailfrom=alsa-devel-bounces@alsa-project.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from alsa0.perex.cz (alsa0.perex.cz. [77.48.224.243]) by mx.google.com with ESMTP id l76-v6si5622253wmi.13.2018.05.18.05.58.52; Fri, 18 May 2018 05:58:52 -0700 (PDT) Received-SPF: pass (google.com: domain of alsa-devel-bounces@alsa-project.org designates 77.48.224.243 as permitted sender) client-ip=77.48.224.243; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@linaro.org header.s=google header.b=XXIgNOyK; spf=pass (google.com: domain of alsa-devel-bounces@alsa-project.org designates 77.48.224.243 as permitted sender) smtp.mailfrom=alsa-devel-bounces@alsa-project.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: from alsa0.perex.cz (localhost [127.0.0.1]) by alsa0.perex.cz (Postfix) with ESMTP id 45F8E267345; Fri, 18 May 2018 14:58:44 +0200 (CEST) X-Original-To: alsa-devel@alsa-project.org Delivered-To: alsa-devel@alsa-project.org Received: by alsa0.perex.cz (Postfix, from userid 1000) id 2B74D267281; Fri, 18 May 2018 14:58:40 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on mail1.perex.cz X-Spam-Level: X-Spam-Status: No, score=-0.1 required=5.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_PASS autolearn=disabled version=3.4.0 Received: from mail-wm0-f66.google.com (mail-wm0-f66.google.com [74.125.82.66]) by alsa0.perex.cz (Postfix) with ESMTP id A8521266F88 for ; Fri, 18 May 2018 14:58:37 +0200 (CEST) Received: by mail-wm0-f66.google.com with SMTP id o78-v6so15097321wmg.0 for ; Fri, 18 May 2018 05:58:37 -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=oOyj5cjZXuiudskb4rGSNW+bc8ZdJaZbX0WLt0giMig=; b=XXIgNOyKhxZWJfiupRvfkFWY/xa3sHopYniVqitzq6o30SBfUkrLylmzGNGby77ciW tcI1NnMaIJMzFEtXpEoUigvgdr704duoQxlB1cwO5oScjkWZxxz4rkojt10vRza1K9RP 9HBSIoWYr4O4DVeBuaM8OvCRKoiVureDRe1T0= 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=oOyj5cjZXuiudskb4rGSNW+bc8ZdJaZbX0WLt0giMig=; b=e9mWUHe6jVAC4HUo+EXyIarMbXmIJGH4y3t1CcZNhQj/k4QWsfdxxMSgn2KW4ROkmq uSZr93AVO5VpYMAHytu6Bsf+CqYc/3jEhhquB9ARMmedviKQRLT1kKtXW1qsWvxdaLhR pAohuIHVu2cYaeGlwNh23Yq+aZ5U/WobE5m+I8IaL1rN9VR18cVK5t13Cbm+7nQevb8S dHUJYXSaLopE18AicgSmloT2z+pFEjG3PjUxglE/K0xXO/ZQePsNWa7tUhbSjmgXEtW5 KkKHza9nz3mqrfRnO/fGtpWaRO6kVGY/PvD4Oi3fjWxX+4DgHeVRuvuhZEOmiXYR/six 7Z6g== X-Gm-Message-State: ALKqPwdCfzdgJLkF2pkk8Kwkth4tSMJEgl3PA+i08hD8AcU5I6xITJpt sLkWYK7Ykl3XIwIJUzeBUFWuIQ== X-Received: by 2002:a1c:b595:: with SMTP id e143-v6mr4338254wmf.66.1526648316574; Fri, 18 May 2018 05:58:36 -0700 (PDT) Received: from localhost.localdomain (cpc90716-aztw32-2-0-cust92.18-1.cable.virginm.net. [86.26.100.93]) by smtp.gmail.com with ESMTPSA id x7-v6sm7868034wrm.35.2018.05.18.05.58.35 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 18 May 2018 05:58:35 -0700 (PDT) From: Srinivas Kandagatla To: broonie@kernel.org, linux-arm-msm@vger.kernel.org, alsa-devel@alsa-project.org, bgoswami@codeaurora.org Date: Fri, 18 May 2018 13:55:56 +0100 Message-Id: <20180518125610.26200-2-srinivas.kandagatla@linaro.org> X-Mailer: git-send-email 2.16.2 In-Reply-To: <20180518125610.26200-1-srinivas.kandagatla@linaro.org> References: <20180518125610.26200-1-srinivas.kandagatla@linaro.org> Cc: mark.rutland@arm.com, devicetree@vger.kernel.org, rohkumar@qti.qualcomm.com, gregkh@linuxfoundation.org, plai@codeaurora.org, lgirdwood@gmail.com, tiwai@suse.com, david.brown@linaro.org, robh+dt@kernel.org, Srinivas Kandagatla , linux-arm-kernel@lists.infradead.org, spatakok@qti.qualcomm.com, linux-kernel@vger.kernel.org Subject: [alsa-devel] [PATCH v9 01/15] ASoC: qdsp6: q6afe: Add q6afe driver X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: alsa-devel-bounces@alsa-project.org Sender: alsa-devel-bounces@alsa-project.org This patch adds support to Q6AFE (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 Reviewed-and-tested-by: Rohit kumar Reviewed-by: Banajit Goswami --- sound/soc/qcom/Kconfig | 4 + sound/soc/qcom/qdsp6/Makefile | 1 + sound/soc/qcom/qdsp6/q6afe.c | 549 ++++++++++++++++++++++++++++++++++++++++++ sound/soc/qcom/qdsp6/q6afe.h | 35 +++ 4 files changed, 589 insertions(+) create mode 100644 sound/soc/qcom/qdsp6/q6afe.c create mode 100644 sound/soc/qcom/qdsp6/q6afe.h -- 2.16.2 _______________________________________________ Alsa-devel mailing list Alsa-devel@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/alsa-devel diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index 37ee0d958145..bb0a2afb0563 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -47,11 +47,15 @@ config SND_SOC_QDSP6_COMMON config SND_SOC_QDSP6_CORE tristate +config SND_SOC_QDSP6_AFE + tristate + config SND_SOC_QDSP6 tristate "SoC ALSA audio driver for QDSP6" depends on QCOM_APR && HAS_DMA select SND_SOC_QDSP6_COMMON select SND_SOC_QDSP6_CORE + select SND_SOC_QDSP6_AFE help To add support for MSM QDSP6 Soc Audio. This will enable sound soc platform specific diff --git a/sound/soc/qcom/qdsp6/Makefile b/sound/soc/qcom/qdsp6/Makefile index 03b8e89c9731..7ff666bd10ca 100644 --- a/sound/soc/qcom/qdsp6/Makefile +++ b/sound/soc/qcom/qdsp6/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_SND_SOC_QDSP6_COMMON) += q6dsp-common.o obj-$(CONFIG_SND_SOC_QDSP6_CORE) += q6core.o +obj-$(CONFIG_SND_SOC_QDSP6_AFE) += q6afe.o diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c new file mode 100644 index 000000000000..5e0032c13aab --- /dev/null +++ b/sound/soc/qcom/qdsp6/q6afe.c @@ -0,0 +1,549 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. +// Copyright (c) 2018, Linaro Limited + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "q6dsp-errno.h" +#include "q6core.h" +#include "q6afe.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_SVC_CMD_SET_PARAM 0x000100f3 +#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 { + struct apr_device *apr; + struct device *dev; + struct q6core_svc_api_info ainfo; + struct mutex lock; + struct list_head port_list; + spinlock_t port_list_lock; + struct platform_device *pdev_dais; +}; + +struct afe_port_cmd_device_start { + u16 port_id; + u16 reserved; +} __packed; + +struct afe_port_cmd_device_stop { + 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 q6afe_port { + wait_queue_head_t wait; + union afe_port_config port_cfg; + struct aprv2_ibasic_rsp_result_t result; + int token; + int id; + int cfg_type; + struct q6afe *afe; + struct kref refcount; + struct list_head node; +}; + +struct afe_port_map { + int port_id; + int token; + int is_rx; + int is_dig_pcm; +}; + +/* + * Mapping between Virtual Port IDs to DSP AFE Port ID + * On B Family SoCs DSP Port IDs are consistent across multiple SoCs + * on A Family SoCs DSP port IDs are same as virtual Port IDs. + */ + +static struct afe_port_map port_maps[AFE_PORT_MAX] = { + [HDMI_RX] = { AFE_PORT_ID_MULTICHAN_HDMI_RX, HDMI_RX, 1, 1}, +}; + +static void q6afe_port_free(struct kref *ref) +{ + struct q6afe_port *port; + struct q6afe *afe; + unsigned long flags; + + port = container_of(ref, struct q6afe_port, refcount); + afe = port->afe; + spin_lock_irqsave(&afe->port_list_lock, flags); + list_del(&port->node); + spin_unlock_irqrestore(&afe->port_list_lock, flags); + kfree(port); +} + +static struct q6afe_port *q6afe_find_port(struct q6afe *afe, int token) +{ + struct q6afe_port *p = NULL; + struct q6afe_port *ret = NULL; + unsigned long flags; + + spin_lock_irqsave(&afe->port_list_lock, flags); + list_for_each_entry(p, &afe->port_list, node) + if (p->token == token) { + ret = p; + kref_get(&p->refcount); + break; + } + + spin_unlock_irqrestore(&afe->port_list_lock, flags); + return ret; +} + +static int q6afe_callback(struct apr_device *adev, struct apr_resp_pkt *data) +{ + struct q6afe *afe = dev_get_drvdata(&adev->dev); + struct aprv2_ibasic_rsp_result_t *res; + struct apr_hdr *hdr = &data->hdr; + struct q6afe_port *port; + + if (!data->payload_size) + return 0; + + res = data->payload; + switch (hdr->opcode) { + case APR_BASIC_RSP_RESULT: { + if (res->status) { + dev_err(afe->dev, "cmd = 0x%x returned error = 0x%x\n", + res->opcode, res->status); + } + switch (res->opcode) { + case AFE_PORT_CMD_SET_PARAM_V2: + case AFE_PORT_CMD_DEVICE_STOP: + case AFE_PORT_CMD_DEVICE_START: + case AFE_SVC_CMD_SET_PARAM: + port = q6afe_find_port(afe, hdr->token); + if (port) { + port->result = *res; + wake_up(&port->wait); + kref_put(&port->refcount, q6afe_port_free); + } + break; + default: + dev_err(afe->dev, "Unknown cmd 0x%x\n", res->opcode); + break; + } + } + break; + default: + break; + } + + return 0; +} + +/** + * q6afe_get_port_id() - Get port id from a given port index + * + * @index: port index + * + * Return: Will be an negative on error or valid port_id on success + */ +int q6afe_get_port_id(int index) +{ + if (index < 0 || index > AFE_PORT_MAX) + return -EINVAL; + + return port_maps[index].port_id; +} +EXPORT_SYMBOL_GPL(q6afe_get_port_id); + +static int afe_apr_send_pkt(struct q6afe *afe, struct apr_pkt *pkt, + struct q6afe_port *port) +{ + wait_queue_head_t *wait = &port->wait; + struct apr_hdr *hdr = &pkt->hdr; + int ret; + + mutex_lock(&afe->lock); + port->result.opcode = 0; + port->result.status = 0; + + ret = apr_send_pkt(afe->apr, pkt); + if (ret < 0) { + dev_err(afe->dev, "packet not transmitted (%d)\n", ret); + ret = -EINVAL; + goto err; + } + + ret = wait_event_timeout(*wait, (port->result.opcode == hdr->opcode), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + ret = -ETIMEDOUT; + } else if (port->result.status > 0) { + dev_err(afe->dev, "DSP returned error[%x]\n", + port->result.status); + ret = -EINVAL; + } else { + ret = 0; + } + +err: + mutex_unlock(&afe->lock); + + return ret; +} + +static int q6afe_port_set_param_v2(struct q6afe_port *port, void *data, + int param_id, int module_id, int psize) +{ + struct afe_port_cmd_set_param_v2 *param; + struct afe_port_param_data_v2 *pdata; + struct q6afe *afe = port->afe; + struct apr_pkt *pkt; + u16 port_id = port->id; + int ret, pkt_size; + void *p, *pl; + + pkt_size = APR_HDR_SIZE + sizeof(*param) + sizeof(*pdata) + psize; + p = kzalloc(pkt_size, GFP_KERNEL); + if (!p) + return -ENOMEM; + + pkt = p; + param = p + APR_HDR_SIZE; + pdata = p + APR_HDR_SIZE + sizeof(*param); + pl = p + APR_HDR_SIZE + sizeof(*param) + sizeof(*pdata); + memcpy(pl, data, psize); + + pkt->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + pkt->hdr.pkt_size = pkt_size; + pkt->hdr.src_port = 0; + pkt->hdr.dest_port = 0; + pkt->hdr.token = port->token; + pkt->hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + + param->port_id = port_id; + param->payload_size = sizeof(*pdata) + psize; + param->payload_address_lsw = 0x00; + param->payload_address_msw = 0x00; + param->mem_map_handle = 0x00; + pdata->module_id = module_id; + pdata->param_id = param_id; + pdata->param_size = psize; + + ret = afe_apr_send_pkt(afe, pkt, port); + if (ret) + dev_err(afe->dev, "AFE enable for port 0x%x failed %d\n", + port_id, ret); + + kfree(pkt); + return ret; +} + +/** + * q6afe_port_stop() - Stop a afe port + * + * @port: Instance of port to stop + * + * Return: Will be an negative on packet size on success. + */ +int q6afe_port_stop(struct q6afe_port *port) +{ + struct afe_port_cmd_device_stop *stop; + struct q6afe *afe = port->afe; + struct apr_pkt *pkt; + int port_id = port->id; + int ret = 0; + int index, pkt_size; + void *p; + + 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; + } + + pkt_size = APR_HDR_SIZE + sizeof(*stop); + p = kzalloc(pkt_size, GFP_KERNEL); + if (!p) + return -ENOMEM; + + pkt = p; + stop = p + APR_HDR_SIZE; + + pkt->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + pkt->hdr.pkt_size = pkt_size; + pkt->hdr.src_port = 0; + pkt->hdr.dest_port = 0; + pkt->hdr.token = index; + pkt->hdr.opcode = AFE_PORT_CMD_DEVICE_STOP; + stop->port_id = port_id; + stop->reserved = 0; + + ret = afe_apr_send_pkt(afe, pkt, port); + if (ret) + dev_err(afe->dev, "AFE close failed %d\n", ret); + + kfree(pkt); + return ret; +} +EXPORT_SYMBOL_GPL(q6afe_port_stop); + +/** + * q6afe_hdmi_port_prepare() - Prepare hdmi afe port. + * + * @port: Instance of afe port + * @cfg: HDMI configuration for the afe port + * + */ +void q6afe_hdmi_port_prepare(struct q6afe_port *port, + struct q6afe_hdmi_cfg *cfg) +{ + union afe_port_config *pcfg = &port->port_cfg; + + pcfg->hdmi_multi_ch.hdmi_cfg_minor_version = + AFE_API_VERSION_HDMI_CONFIG; + pcfg->hdmi_multi_ch.datatype = cfg->datatype; + pcfg->hdmi_multi_ch.channel_allocation = cfg->channel_allocation; + pcfg->hdmi_multi_ch.sample_rate = cfg->sample_rate; + pcfg->hdmi_multi_ch.bit_width = cfg->bit_width; +} +EXPORT_SYMBOL_GPL(q6afe_hdmi_port_prepare); + +/** + * q6afe_port_start() - Start a afe port + * + * @port: Instance of port to start + * + * Return: Will be an negative on packet size on success. + */ +int q6afe_port_start(struct q6afe_port *port) +{ + struct afe_port_cmd_device_start *start; + struct q6afe *afe = port->afe; + int port_id = port->id; + int ret, param_id = port->cfg_type; + struct apr_pkt *pkt; + int pkt_size; + void *p; + + ret = q6afe_port_set_param_v2(port, &port->port_cfg, param_id, + AFE_MODULE_AUDIO_DEV_INTERFACE, + sizeof(port->port_cfg)); + if (ret) { + dev_err(afe->dev, "AFE enable for port 0x%x failed %d\n", + port_id, ret); + return ret; + } + + pkt_size = APR_HDR_SIZE + sizeof(*start); + p = kzalloc(pkt_size, GFP_KERNEL); + if (!p) + return -ENOMEM; + + pkt = p; + start = p + APR_HDR_SIZE; + + pkt->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + pkt->hdr.pkt_size = pkt_size; + pkt->hdr.src_port = 0; + pkt->hdr.dest_port = 0; + pkt->hdr.token = port->token; + pkt->hdr.opcode = AFE_PORT_CMD_DEVICE_START; + + start->port_id = port_id; + + ret = afe_apr_send_pkt(afe, pkt, port); + if (ret) + dev_err(afe->dev, "AFE enable for port 0x%x failed %d\n", + port_id, ret); + + kfree(pkt); + return ret; +} +EXPORT_SYMBOL_GPL(q6afe_port_start); + +/** + * q6afe_port_get_from_id() - Get port instance from a port id + * + * @dev: Pointer to afe child device. + * @id: port id + * + * Return: Will be an error pointer on error or a valid afe port + * on success. + */ +struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id) +{ + int port_id; + struct q6afe *afe = dev_get_drvdata(dev->parent); + struct q6afe_port *port; + unsigned long flags; + int cfg_type; + + if (id < 0 || id > AFE_PORT_MAX) { + dev_err(dev, "AFE port token[%d] invalid!\n", id); + return ERR_PTR(-EINVAL); + } + + /* if port is multiple times bind/unbind before callback finishes */ + port = q6afe_find_port(afe, id); + if (port) { + dev_err(dev, "AFE Port already open\n"); + return port; + } + + port_id = port_maps[id].port_id; + + 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 = kzalloc(sizeof(*port), GFP_KERNEL); + if (!port) + return ERR_PTR(-ENOMEM); + + init_waitqueue_head(&port->wait); + + port->token = id; + port->id = port_id; + port->afe = afe; + port->cfg_type = cfg_type; + kref_init(&port->refcount); + + spin_lock_irqsave(&afe->port_list_lock, flags); + list_add_tail(&port->node, &afe->port_list); + spin_unlock_irqrestore(&afe->port_list_lock, flags); + + return port; + +} +EXPORT_SYMBOL_GPL(q6afe_port_get_from_id); + +/** + * q6afe_port_put() - Release port reference + * + * @port: Instance of port to put + */ +void q6afe_port_put(struct q6afe_port *port) +{ + kref_put(&port->refcount, q6afe_port_free); +} +EXPORT_SYMBOL_GPL(q6afe_port_put); + +static int q6afe_probe(struct apr_device *adev) +{ + struct q6afe *afe; + struct device *dev = &adev->dev; + struct device_node *dais_np; + + afe = devm_kzalloc(dev, sizeof(*afe), GFP_KERNEL); + if (!afe) + return -ENOMEM; + + q6core_get_svc_api_info(adev->svc_id, &afe->ainfo); + afe->apr = adev; + mutex_init(&afe->lock); + afe->dev = dev; + INIT_LIST_HEAD(&afe->port_list); + spin_lock_init(&afe->port_list_lock); + + dev_set_drvdata(dev, afe); + + dais_np = of_get_child_by_name(dev->of_node, "dais"); + if (dais_np) { + afe->pdev_dais = of_platform_device_create(dais_np, + "q6afe-dai", dev); + of_node_put(dais_np); + } + + return 0; +} + +static int q6afe_remove(struct apr_device *adev) +{ + struct q6afe *afe = dev_get_drvdata(&adev->dev); + + if (afe->pdev_dais) + of_platform_device_destroy(&afe->pdev_dais->dev, NULL); + + return 0; +} + +static const struct of_device_id q6afe_device_id[] = { + { .compatible = "qcom,q6afe" }, + {}, +}; +MODULE_DEVICE_TABLE(of, q6afe_device_id); + +static struct apr_driver qcom_q6afe_driver = { + .probe = q6afe_probe, + .remove = q6afe_remove, + .callback = q6afe_callback, + .driver = { + .name = "qcom-q6afe", + .of_match_table = of_match_ptr(q6afe_device_id), + + }, +}; + +module_apr_driver(qcom_q6afe_driver); +MODULE_DESCRIPTION("Q6 Audio Front End"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/qcom/qdsp6/q6afe.h b/sound/soc/qcom/qdsp6/q6afe.h new file mode 100644 index 000000000000..3bd991a7c42d --- /dev/null +++ b/sound/soc/qcom/qdsp6/q6afe.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __Q6AFE_H__ +#define __Q6AFE_H__ + +#include + +#define AFE_PORT_MAX 48 + +#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_config { + struct q6afe_hdmi_cfg hdmi; +}; + +struct q6afe_port; + +struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id); +int q6afe_port_start(struct q6afe_port *port); +int q6afe_port_stop(struct q6afe_port *port); +void q6afe_port_put(struct q6afe_port *port); +int q6afe_get_port_id(int index); +void q6afe_hdmi_port_prepare(struct q6afe_port *port, + struct q6afe_hdmi_cfg *cfg); + +#endif /* __Q6AFE_H__ */ From patchwork Fri May 18 12:55:57 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Srinivas Kandagatla X-Patchwork-Id: 136305 Delivered-To: patch@linaro.org Received: by 2002:a2e:9706:0:0:0:0:0 with SMTP id r6-v6csp1206059lji; Fri, 18 May 2018 05:59:02 -0700 (PDT) X-Google-Smtp-Source: AB8JxZpB+R1jR6V8+ZXOx3uw2JjqkhVlGojRqia7HiatMpdOtVus6EvAvAFMiVxmHWlNdxWDoENT X-Received: by 2002:adf:b611:: with SMTP id f17-v6mr8214236wre.139.1526648342222; Fri, 18 May 2018 05:59:02 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1526648342; cv=none; d=google.com; s=arc-20160816; b=gG/OlqxiuEqXJkAz1t/aGfbK8vAbs1TX8AmmyWzHi67qW7SoQvQMQ/ARWS3wLnaOJi wQ3NAbb3RhV8tKqQHefr6lRLRdzMiSGcHtoM9rKm+wpTrnSb3sM7ko1XoWmc8PTYuksL cX4ZkfLssIgKrCsKS077UCumVatmbYUxHd3YS0dxl70ai7sJqx3Tw83MmcOhg4IgfEPp FPysVatxlzWV+m4RgilzLSLUkPal1SvmBnTF+nUUg/IgCKwo6NkCbCtH6I/nZvTm0vZ9 5EvivRfPS7sQnIb8Kz1qXf2bBJILvehGltEwaHnahJ5BNX7urJfqDkjPPFShYJC/CI6K jXwg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:cc:references:in-reply-to:message-id :date:to:from:dkim-signature:delivered-to:arc-authentication-results; bh=rLIiLPpkmQkkhGTC0CF3vn5Zc8qwboo4bwFZL6xN42I=; b=uGTIbsK6/y1O3l593P8GrOciyfZwlM2kdVIkX0Y/0wZYtPh/GccuW+9btXdi3rju9w iLMmbGoos5t9CXRjdRfxkryIqKJCqogQpCG3VEJmq8WpZRO727jco5o8J6Rfd1uByq0v FuM/epT7mu8S2xQafESQQ+WNtTnhyuH0A8mfCyMvAbIiCbuOu6o8F/+a7PzWu5lyhEgk aYeMPKcla6rBS6XjO/qr1mlmMGJPMCgokBWQBBBQQscl219s6trHednBSUlA/xk6ifmj GK5E+zXgKfQ/6lngt4ruDfx8kErGxfPz6CpXEJhFYcKFGdWBH36mQdEQX9wnCrpi6WBq S9/A== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@linaro.org header.s=google header.b=a9BBB6rZ; spf=pass (google.com: domain of alsa-devel-bounces@alsa-project.org designates 77.48.224.243 as permitted sender) smtp.mailfrom=alsa-devel-bounces@alsa-project.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from alsa0.perex.cz (alsa0.perex.cz. [77.48.224.243]) by mx.google.com with ESMTP id j137-v6si5873265wmf.157.2018.05.18.05.59.01; Fri, 18 May 2018 05:59:02 -0700 (PDT) Received-SPF: pass (google.com: domain of alsa-devel-bounces@alsa-project.org designates 77.48.224.243 as permitted sender) client-ip=77.48.224.243; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@linaro.org header.s=google header.b=a9BBB6rZ; spf=pass (google.com: domain of alsa-devel-bounces@alsa-project.org designates 77.48.224.243 as permitted sender) smtp.mailfrom=alsa-devel-bounces@alsa-project.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: from alsa0.perex.cz (localhost [127.0.0.1]) by alsa0.perex.cz (Postfix) with ESMTP id 8FEDB26762C; Fri, 18 May 2018 14:58:45 +0200 (CEST) X-Original-To: alsa-devel@alsa-project.org Delivered-To: alsa-devel@alsa-project.org Received: by alsa0.perex.cz (Postfix, from userid 1000) id 31D56267345; Fri, 18 May 2018 14:58:41 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on mail1.perex.cz X-Spam-Level: X-Spam-Status: No, score=-0.1 required=5.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_PASS autolearn=disabled version=3.4.0 Received: from mail-wm0-f65.google.com (mail-wm0-f65.google.com [74.125.82.65]) by alsa0.perex.cz (Postfix) with ESMTP id 986DA267046 for ; Fri, 18 May 2018 14:58:38 +0200 (CEST) Received: by mail-wm0-f65.google.com with SMTP id m129-v6so14963364wmb.3 for ; Fri, 18 May 2018 05:58:38 -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=nX2ucln+TlxsiPN/r6WU5iNi+j28T92o8UlmP6JYmE8=; b=a9BBB6rZwZI/8hDUfskPgcUsu2ve6Cs+KZ4RMSTRBftyR30PAXI0IgifQ4GVXQmdqS FTgiWiSBLdwtvHpn/28M1n8tcwCbFARy3lCD9EGN6vjxbwAfX/ogW2ISAWa/HsMiaPYE bLjZdTIO3nvmaWXkgtWFffVh1DfkE25LS2lhM= 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=nX2ucln+TlxsiPN/r6WU5iNi+j28T92o8UlmP6JYmE8=; b=gDjbj4o5cWLlDk5Xjlal6kk1AYQGgBzxloLYUny8S961a9JMvsC7cLeDiCbpHGhNSa 9xYCRf7cNxOqN57L2L1Oag0PdyiijlSt+jTW1ahdxT0+CZp9HK71kxN1iHeoCupE44up 5YtT18xJ47J/RNGvb/JxhgXc9s7nEcRVLTG/Ld+3mPpbC2tt5UX//LcOF2dq3ZSWQppC ulPVrQaK57/yvmO2VoV8McINHW8CsubzUPIx8jFy+TQ9VmgJj5rqxUwHQqg7zxIf4omO dU8JAlUcj6FGuWK6hZrn6Ocg9w1fuVZYjctpKOpBJW6RuvoT53AoQcd1PkuzmwotEjGa +kiw== X-Gm-Message-State: ALKqPwdGuw4HEwKA1KSwFDw2GdTtzSLUL24V0H9XmPNB+SJV3/bYo/eH jgRHg6Kn88AXHAVKYGAeZhFyOQ== X-Received: by 2002:a1c:168c:: with SMTP id 134-v6mr4534644wmw.53.1526648317917; Fri, 18 May 2018 05:58:37 -0700 (PDT) Received: from localhost.localdomain (cpc90716-aztw32-2-0-cust92.18-1.cable.virginm.net. [86.26.100.93]) by smtp.gmail.com with ESMTPSA id x7-v6sm7868034wrm.35.2018.05.18.05.58.36 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 18 May 2018 05:58:37 -0700 (PDT) From: Srinivas Kandagatla To: broonie@kernel.org, linux-arm-msm@vger.kernel.org, alsa-devel@alsa-project.org, bgoswami@codeaurora.org Date: Fri, 18 May 2018 13:55:57 +0100 Message-Id: <20180518125610.26200-3-srinivas.kandagatla@linaro.org> X-Mailer: git-send-email 2.16.2 In-Reply-To: <20180518125610.26200-1-srinivas.kandagatla@linaro.org> References: <20180518125610.26200-1-srinivas.kandagatla@linaro.org> Cc: mark.rutland@arm.com, devicetree@vger.kernel.org, rohkumar@qti.qualcomm.com, gregkh@linuxfoundation.org, plai@codeaurora.org, lgirdwood@gmail.com, tiwai@suse.com, david.brown@linaro.org, robh+dt@kernel.org, Srinivas Kandagatla , linux-arm-kernel@lists.infradead.org, spatakok@qti.qualcomm.com, linux-kernel@vger.kernel.org Subject: [alsa-devel] [PATCH v9 02/15] ASoC: qdsp6: qdafe: Add SLIMBus port Support X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: alsa-devel-bounces@alsa-project.org Sender: alsa-devel-bounces@alsa-project.org This patch adds support to 6 SLIMBus AFE ports, which are used as backend dais. Signed-off-by: Srinivas Kandagatla Reviewed-and-tested-by: Rohit kumar Reviewed-by: Banajit Goswami --- sound/soc/qcom/qdsp6/q6afe.c | 129 +++++++++++++++++++++++++++++++++++++++++++ sound/soc/qcom/qdsp6/q6afe.h | 14 +++++ 2 files changed, 143 insertions(+) -- 2.16.2 _______________________________________________ Alsa-devel mailing list Alsa-devel@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/alsa-devel diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c index 5e0032c13aab..735c0b5eb55a 100644 --- a/sound/soc/qcom/qdsp6/q6afe.c +++ b/sound/soc/qcom/qdsp6/q6afe.c @@ -28,9 +28,45 @@ #define AFE_PARAM_ID_HDMI_CONFIG 0x00010210 #define AFE_MODULE_AUDIO_DEV_INTERFACE 0x0001020C +#define AFE_PARAM_ID_CDC_SLIMBUS_SLAVE_CFG 0x00010235 + +#define AFE_PARAM_ID_SLIMBUS_CONFIG 0x00010212 + /* Port IDs */ #define AFE_API_VERSION_HDMI_CONFIG 0x1 #define AFE_PORT_ID_MULTICHAN_HDMI_RX 0x100E + +#define AFE_API_VERSION_SLIMBUS_CONFIG 0x1 + +/* SLIMbus Rx port on channel 0. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_RX 0x4000 +/* SLIMbus Tx port on channel 0. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_TX 0x4001 +/* SLIMbus Rx port on channel 1. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_RX 0x4002 +/* SLIMbus Tx port on channel 1. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_TX 0x4003 +/* SLIMbus Rx port on channel 2. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_RX 0x4004 +/* SLIMbus Tx port on channel 2. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_TX 0x4005 +/* SLIMbus Rx port on channel 3. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_RX 0x4006 +/* SLIMbus Tx port on channel 3. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_TX 0x4007 +/* SLIMbus Rx port on channel 4. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_RX 0x4008 +/* SLIMbus Tx port on channel 4. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_TX 0x4009 +/* SLIMbus Rx port on channel 5. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_RX 0x400a +/* SLIMbus Tx port on channel 5. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_TX 0x400b +/* SLIMbus Rx port on channel 6. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_RX 0x400c +/* SLIMbus Tx port on channel 6. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_TX 0x400d + #define TIMEOUT_MS 1000 #define AFE_CMD_RESP_AVAIL 0 #define AFE_CMD_RESP_NONE 1 @@ -80,8 +116,53 @@ struct afe_param_id_hdmi_multi_chan_audio_cfg { u16 reserved; } __packed; +struct afe_param_id_slimbus_cfg { + u32 sb_cfg_minor_version; +/* Minor version used for tracking the version of the SLIMBUS + * configuration interface. + * Supported values: #AFE_API_VERSION_SLIMBUS_CONFIG + */ + + u16 slimbus_dev_id; +/* SLIMbus hardware device ID, which is required to handle + * multiple SLIMbus hardware blocks. + * Supported values: - #AFE_SLIMBUS_DEVICE_1 - #AFE_SLIMBUS_DEVICE_2 + */ + u16 bit_width; +/* Bit width of the sample. + * Supported values: 16, 24 + */ + u16 data_format; +/* Data format supported by the SLIMbus hardware. The default is + * 0 (#AFE_SB_DATA_FORMAT_NOT_INDICATED), which indicates the + * hardware does not perform any format conversions before the data + * transfer. + */ + u16 num_channels; +/* Number of channels. + * Supported values: 1 to #AFE_PORT_MAX_AUDIO_CHAN_CNT + */ + u8 shared_ch_mapping[AFE_PORT_MAX_AUDIO_CHAN_CNT]; +/* Mapping of shared channel IDs (128 to 255) to which the + * master port is to be connected. + * Shared_channel_mapping[i] represents the shared channel assigned + * for audio channel i in multichannel audio data. + */ + u32 sample_rate; +/* Sampling rate of the port. + * Supported values: + * - #AFE_PORT_SAMPLE_RATE_8K + * - #AFE_PORT_SAMPLE_RATE_16K + * - #AFE_PORT_SAMPLE_RATE_48K + * - #AFE_PORT_SAMPLE_RATE_96K + * - #AFE_PORT_SAMPLE_RATE_192K + */ +} __packed; + + union afe_port_config { struct afe_param_id_hdmi_multi_chan_audio_cfg hdmi_multi_ch; + struct afe_param_id_slimbus_cfg slim_cfg; } __packed; struct q6afe_port { @@ -111,6 +192,20 @@ struct afe_port_map { static struct afe_port_map port_maps[AFE_PORT_MAX] = { [HDMI_RX] = { AFE_PORT_ID_MULTICHAN_HDMI_RX, HDMI_RX, 1, 1}, + [SLIMBUS_0_RX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_RX, + SLIMBUS_0_RX, 1, 1}, + [SLIMBUS_1_RX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_RX, + SLIMBUS_1_RX, 1, 1}, + [SLIMBUS_2_RX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_RX, + SLIMBUS_2_RX, 1, 1}, + [SLIMBUS_3_RX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_RX, + SLIMBUS_3_RX, 1, 1}, + [SLIMBUS_4_RX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_RX, + SLIMBUS_4_RX, 1, 1}, + [SLIMBUS_5_RX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_RX, + SLIMBUS_5_RX, 1, 1}, + [SLIMBUS_6_RX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_RX, + SLIMBUS_6_RX, 1, 1}, }; static void q6afe_port_free(struct kref *ref) @@ -340,6 +435,31 @@ int q6afe_port_stop(struct q6afe_port *port) } EXPORT_SYMBOL_GPL(q6afe_port_stop); +/** + * q6afe_slim_port_prepare() - Prepare slim afe port. + * + * @port: Instance of afe port + * @cfg: SLIM configuration for the afe port + * + */ +void q6afe_slim_port_prepare(struct q6afe_port *port, + struct q6afe_slim_cfg *cfg) +{ + union afe_port_config *pcfg = &port->port_cfg; + + pcfg->slim_cfg.sb_cfg_minor_version = AFE_API_VERSION_SLIMBUS_CONFIG; + pcfg->slim_cfg.sample_rate = cfg->sample_rate; + pcfg->slim_cfg.bit_width = cfg->bit_width; + pcfg->slim_cfg.num_channels = cfg->num_channels; + pcfg->slim_cfg.data_format = cfg->data_format; + pcfg->slim_cfg.shared_ch_mapping[0] = cfg->ch_mapping[0]; + pcfg->slim_cfg.shared_ch_mapping[1] = cfg->ch_mapping[1]; + pcfg->slim_cfg.shared_ch_mapping[2] = cfg->ch_mapping[2]; + pcfg->slim_cfg.shared_ch_mapping[3] = cfg->ch_mapping[3]; + +} +EXPORT_SYMBOL_GPL(q6afe_slim_port_prepare); + /** * q6afe_hdmi_port_prepare() - Prepare hdmi afe port. * @@ -451,6 +571,15 @@ struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id) case AFE_PORT_ID_MULTICHAN_HDMI_RX: cfg_type = AFE_PARAM_ID_HDMI_CONFIG; break; + case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_RX: + case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_RX: + case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_RX: + case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_RX: + case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_RX: + case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_RX: + case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_RX: + cfg_type = AFE_PARAM_ID_SLIMBUS_CONFIG; + break; default: dev_err(dev, "Invalid port id 0x%x\n", port_id); return ERR_PTR(-EINVAL); diff --git a/sound/soc/qcom/qdsp6/q6afe.h b/sound/soc/qcom/qdsp6/q6afe.h index 3bd991a7c42d..5659966c6b1e 100644 --- a/sound/soc/qcom/qdsp6/q6afe.h +++ b/sound/soc/qcom/qdsp6/q6afe.h @@ -11,6 +11,9 @@ #define MSM_AFE_PORT_TYPE_TX 1 #define AFE_MAX_PORTS AFE_PORT_MAX +#define AFE_MAX_CHAN_COUNT 8 +#define AFE_PORT_MAX_AUDIO_CHAN_CNT 0x8 + struct q6afe_hdmi_cfg { u16 datatype; u16 channel_allocation; @@ -18,8 +21,17 @@ struct q6afe_hdmi_cfg { u16 bit_width; }; +struct q6afe_slim_cfg { + u32 sample_rate; + u16 bit_width; + u16 data_format; + u16 num_channels; + u8 ch_mapping[AFE_MAX_CHAN_COUNT]; +}; + struct q6afe_port_config { struct q6afe_hdmi_cfg hdmi; + struct q6afe_slim_cfg slim; }; struct q6afe_port; @@ -31,5 +43,7 @@ void q6afe_port_put(struct q6afe_port *port); int q6afe_get_port_id(int index); void q6afe_hdmi_port_prepare(struct q6afe_port *port, struct q6afe_hdmi_cfg *cfg); +void q6afe_slim_port_prepare(struct q6afe_port *port, + struct q6afe_slim_cfg *cfg); #endif /* __Q6AFE_H__ */ From patchwork Fri May 18 12:56:00 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Srinivas Kandagatla X-Patchwork-Id: 136311 Delivered-To: patch@linaro.org Received: by 2002:a2e:9706:0:0:0:0:0 with SMTP id r6-v6csp1206516lji; Fri, 18 May 2018 05:59:23 -0700 (PDT) X-Google-Smtp-Source: AB8JxZrOrAouHzLmxOssTXiihOitxgNXxmabI23wpjADTYb6Na95FX63WlBZmfL27B/OrOhamROK X-Received: by 2002:adf:8362:: with SMTP id 89-v6mr2519407wrd.17.1526648363883; Fri, 18 May 2018 05:59:23 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1526648363; cv=none; d=google.com; s=arc-20160816; b=bBZA/6Mg8Pv0ibzlURJm+GnB2/14UtYVUbtIsNBoEN/HEkf8kvg7GTJA2o/pbag3D4 b8SnmOQqGH6Njx2hAvvIq5XZF38PVelH/exfgkKqQtMeussYhSuzuzN61G/Ouxg/BM/k BGTAUK5GX2L8xUutj5qcV3TT9h8BNKoxyUBq9JBpKOlVG/StdNmKaz5vqmJLERu2Bgq3 mZ9KnVBGPAx41oHK+HQtSSRrgyJy3jzhjtHTAh45T4ZN9f122F+4oF4Gpff8m8WZ/JJe X/vgbVsnoDYlZLxSDckGyW4hmwVtZAG3JghyjheeejPXcCU2nvdNxzBs64yULgmmdc7Q HjQA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:cc:references:in-reply-to:message-id :date:to:from:dkim-signature:delivered-to:arc-authentication-results; bh=rI9KVIsnVVMdAznU2S81Ye5qYuQnNWDl9OekGvLjGOQ=; b=paLaBgq/qVBVhInuljcwyUqbOfoglQ8A85CXd1YVx6LRWpu5/awT36pYHbnH0ttSO7 z0WR3MyjaFAqYfXwz7LfmSJuHqcRKE5V69/2AYJ621ExSyggzWNBu/64iD84+qvtsmRF gQnaSdw4tUQf7S7cX3T7GVbP/nTSRCEQ0xy2z9kE7EzVBPEEaEs0qVZ2IcOcbZIHDMq+ QW8cBxIQOOnKcVZSR969AnrW4dBc021LpkcqN2pOdf79tnG+k8LfL0fsWW+RDjnuX4V/ kcmUtgR6KqZ/qIwa0E3x5wBG6zAls/v9oJAHaHGOw2vMrZuZ3XGy2xtSM502Uq1HDSCq SX2A== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@linaro.org header.s=google header.b=VBvfe4QC; spf=pass (google.com: domain of alsa-devel-bounces@alsa-project.org designates 77.48.224.243 as permitted sender) smtp.mailfrom=alsa-devel-bounces@alsa-project.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from alsa0.perex.cz (alsa0.perex.cz. [77.48.224.243]) by mx.google.com with ESMTP id w185-v6si5486352wmb.48.2018.05.18.05.59.23; Fri, 18 May 2018 05:59:23 -0700 (PDT) Received-SPF: pass (google.com: domain of alsa-devel-bounces@alsa-project.org designates 77.48.224.243 as permitted sender) client-ip=77.48.224.243; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@linaro.org header.s=google header.b=VBvfe4QC; spf=pass (google.com: domain of alsa-devel-bounces@alsa-project.org designates 77.48.224.243 as permitted sender) smtp.mailfrom=alsa-devel-bounces@alsa-project.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: from alsa0.perex.cz (localhost [127.0.0.1]) by alsa0.perex.cz (Postfix) with ESMTP id CFCE1267645; Fri, 18 May 2018 14:58:50 +0200 (CEST) X-Original-To: alsa-devel@alsa-project.org Delivered-To: alsa-devel@alsa-project.org Received: by alsa0.perex.cz (Postfix, from userid 1000) id 32E3C26762C; Fri, 18 May 2018 14:58:45 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on mail1.perex.cz X-Spam-Level: X-Spam-Status: No, score=-0.1 required=5.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_PASS autolearn=disabled version=3.4.0 Received: from mail-wm0-f66.google.com (mail-wm0-f66.google.com [74.125.82.66]) by alsa0.perex.cz (Postfix) with ESMTP id 02E6B267388 for ; Fri, 18 May 2018 14:58:43 +0200 (CEST) Received: by mail-wm0-f66.google.com with SMTP id o78-v6so15097840wmg.0 for ; Fri, 18 May 2018 05:58:42 -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=A2LDi63brE66h4GmD5/A7IhTxOIYQLFZtEDYZXMatQw=; b=VBvfe4QCN1NSN+UaxmFCJgG/jgPnI3GvKleNA77ZGAoHBqI9JwsdcyAYg2fb9fUf4w UxBESeVa36dD1p8aK1XTme3c2epF2bIW3lRfmJneWJeVWjje585nLuCNwSoARKLBn+6T tZAwf0HTWS2FMGG4ciKrJkUZnAb3wIHezLhQ4= 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=A2LDi63brE66h4GmD5/A7IhTxOIYQLFZtEDYZXMatQw=; b=R1b7z62b0ad5cckMFbIEuPM6vlQiUYpne8NrVXXx9EhpQJ4Qf69MpXLpzaWk80x+qa ZDr/LjJ1pcKCEh+DblV/+nV5L45vqP77arpcKVHbIaAMQx7/eGlJ69Tk4+GJTqGHtLuJ /gswhRoRAxyTJysNMmfCK/03gF93SYkGZrfH848H1svnALzsQnJFC6nBPs6CGL+52wwK Cg3E11HsTqMHdARMmzV+oLETBEYRGoyJBeKPKfwQMjJjRMCEeIKFSqAw+3qhcf4h1/jj PIM+Oj/hvRc2qMr32rGR7FqGTFKtV1p2OpRnMldeY/Zvvk0pBQFVBZ5OUPL3EKsU+j8f lUJw== X-Gm-Message-State: ALKqPwfmndzj7qHNuxD23SOn6IeDTiA2yuTItdELTGscFkgL0aRSmdI4 q5R2YOkEUp2kZURXqsBte3vLLA== X-Received: by 2002:a1c:9383:: with SMTP id v125-v6mr4643013wmd.58.1526648322181; Fri, 18 May 2018 05:58:42 -0700 (PDT) Received: from localhost.localdomain (cpc90716-aztw32-2-0-cust92.18-1.cable.virginm.net. [86.26.100.93]) by smtp.gmail.com with ESMTPSA id x7-v6sm7868034wrm.35.2018.05.18.05.58.40 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 18 May 2018 05:58:41 -0700 (PDT) From: Srinivas Kandagatla To: broonie@kernel.org, linux-arm-msm@vger.kernel.org, alsa-devel@alsa-project.org, bgoswami@codeaurora.org Date: Fri, 18 May 2018 13:56:00 +0100 Message-Id: <20180518125610.26200-6-srinivas.kandagatla@linaro.org> X-Mailer: git-send-email 2.16.2 In-Reply-To: <20180518125610.26200-1-srinivas.kandagatla@linaro.org> References: <20180518125610.26200-1-srinivas.kandagatla@linaro.org> Cc: mark.rutland@arm.com, devicetree@vger.kernel.org, rohkumar@qti.qualcomm.com, gregkh@linuxfoundation.org, plai@codeaurora.org, lgirdwood@gmail.com, tiwai@suse.com, david.brown@linaro.org, robh+dt@kernel.org, Srinivas Kandagatla , linux-arm-kernel@lists.infradead.org, spatakok@qti.qualcomm.com, linux-kernel@vger.kernel.org Subject: [alsa-devel] [PATCH v9 05/15] ASoC: qdsp6: q6adm: Add q6adm driver X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: alsa-devel-bounces@alsa-project.org Sender: alsa-devel-bounces@alsa-project.org This patch adds support to Q6ADM (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 Reviewed-and-tested-by: Rohit kumar Reviewed-by: Banajit Goswami --- sound/soc/qcom/Kconfig | 4 + sound/soc/qcom/qdsp6/Makefile | 1 + sound/soc/qcom/qdsp6/q6adm.c | 646 ++++++++++++++++++++++++++++++++++++++++++ sound/soc/qcom/qdsp6/q6adm.h | 27 ++ 4 files changed, 678 insertions(+) create mode 100644 sound/soc/qcom/qdsp6/q6adm.c create mode 100644 sound/soc/qcom/qdsp6/q6adm.h -- 2.16.2 _______________________________________________ Alsa-devel mailing list Alsa-devel@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/alsa-devel diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index bb0a2afb0563..971127edbc23 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -50,12 +50,16 @@ config SND_SOC_QDSP6_CORE config SND_SOC_QDSP6_AFE tristate +config SND_SOC_QDSP6_ADM + tristate + config SND_SOC_QDSP6 tristate "SoC ALSA audio driver for QDSP6" depends on QCOM_APR && HAS_DMA select SND_SOC_QDSP6_COMMON select SND_SOC_QDSP6_CORE select SND_SOC_QDSP6_AFE + select SND_SOC_QDSP6_ADM help To add support for MSM QDSP6 Soc Audio. This will enable sound soc platform specific diff --git a/sound/soc/qcom/qdsp6/Makefile b/sound/soc/qcom/qdsp6/Makefile index 7ff666bd10ca..95cdb3a12694 100644 --- a/sound/soc/qcom/qdsp6/Makefile +++ b/sound/soc/qcom/qdsp6/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_SND_SOC_QDSP6_COMMON) += q6dsp-common.o obj-$(CONFIG_SND_SOC_QDSP6_CORE) += q6core.o obj-$(CONFIG_SND_SOC_QDSP6_AFE) += q6afe.o +obj-$(CONFIG_SND_SOC_QDSP6_ADM) += q6adm.o diff --git a/sound/soc/qcom/qdsp6/q6adm.c b/sound/soc/qcom/qdsp6/q6adm.c new file mode 100644 index 000000000000..9983c665a941 --- /dev/null +++ b/sound/soc/qcom/qdsp6/q6adm.c @@ -0,0 +1,646 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. +// Copyright (c) 2018, Linaro Limited + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "q6adm.h" +#include "q6afe.h" +#include "q6core.h" +#include "q6dsp-errno.h" +#include "q6dsp-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 +#define ADM_MATRIX_ID_AUDIO_TX 1 + +struct q6copp { + int afe_port; + int copp_idx; + int id; + int topology; + int mode; + int rate; + int bit_width; + int channels; + int app_type; + int acdb_id; + + struct aprv2_ibasic_rsp_result_t result; + struct kref refcount; + wait_queue_head_t wait; + struct list_head node; + struct q6adm *adm; +}; + +struct q6adm { + struct apr_device *apr; + struct device *dev; + struct q6core_svc_api_info ainfo; + unsigned long copp_bitmap[AFE_MAX_PORTS]; + struct list_head copps_list; + spinlock_t copps_list_lock; + struct aprv2_ibasic_rsp_result_t result; + struct mutex lock; + wait_queue_head_t matrix_map_wait; + struct platform_device *pdev_routing; +}; + +struct q6adm_cmd_device_open_v5 { + 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; + +struct q6adm_cmd_matrix_map_routings_v5 { + u32 matrix_id; + u32 num_sessions; +} __packed; + +struct q6adm_session_map_node_v5 { + u16 session_id; + u16 num_copps; +} __packed; + +static struct q6copp *q6adm_find_copp(struct q6adm *adm, int port_idx, + int copp_idx) +{ + struct q6copp *c = NULL; + struct q6copp *ret = NULL; + unsigned long flags; + + spin_lock_irqsave(&adm->copps_list_lock, flags); + list_for_each_entry(c, &adm->copps_list, node) { + if ((port_idx == c->afe_port) && (copp_idx == c->copp_idx)) { + ret = c; + kref_get(&c->refcount); + break; + } + } + + spin_unlock_irqrestore(&adm->copps_list_lock, flags); + + return ret; + +} + +static void q6adm_free_copp(struct kref *ref) +{ + struct q6copp *c = container_of(ref, struct q6copp, refcount); + struct q6adm *adm = c->adm; + unsigned long flags; + + spin_lock_irqsave(&adm->copps_list_lock, flags); + clear_bit(c->copp_idx, &adm->copp_bitmap[c->afe_port]); + list_del(&c->node); + spin_unlock_irqrestore(&adm->copps_list_lock, flags); + kfree(c); +} + +static int q6adm_callback(struct apr_device *adev, struct apr_resp_pkt *data) +{ + struct aprv2_ibasic_rsp_result_t *result = data->payload; + int port_idx, copp_idx; + struct apr_hdr *hdr = &data->hdr; + struct q6copp *copp; + struct q6adm *adm = dev_get_drvdata(&adev->dev); + + if (!data->payload_size) + return 0; + + copp_idx = (hdr->token) & 0XFF; + port_idx = ((hdr->token) >> 16) & 0xFF; + if (port_idx < 0 || port_idx >= AFE_MAX_PORTS) { + dev_err(&adev->dev, "Invalid port idx %d token %d\n", + port_idx, hdr->token); + return 0; + } + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + dev_err(&adev->dev, "Invalid copp idx %d token %d\n", + copp_idx, hdr->token); + return 0; + } + + switch (hdr->opcode) { + case APR_BASIC_RSP_RESULT: { + if (result->status != 0) { + dev_err(&adev->dev, "cmd = 0x%x return error = 0x%x\n", + result->opcode, result->status); + } + switch (result->opcode) { + case ADM_CMD_DEVICE_OPEN_V5: + case ADM_CMD_DEVICE_CLOSE_V5: + copp = q6adm_find_copp(adm, port_idx, copp_idx); + if (!copp) + return 0; + + copp->result = *result; + wake_up(&copp->wait); + kref_put(&copp->refcount, q6adm_free_copp); + break; + case ADM_CMD_MATRIX_MAP_ROUTINGS_V5: + adm->result = *result; + wake_up(&adm->matrix_map_wait); + break; + + default: + dev_err(&adev->dev, "Unknown Cmd: 0x%x\n", + result->opcode); + break; + } + return 0; + } + 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 = q6adm_find_copp(adm, port_idx, copp_idx); + if (!copp) + return 0; + + if (open->copp_id == INVALID_COPP_ID) { + dev_err(&adev->dev, "Invalid coppid rxed %d\n", + open->copp_id); + copp->result.status = ADSP_EBADPARAM; + wake_up(&copp->wait); + kref_put(&copp->refcount, q6adm_free_copp); + break; + } + copp->result.opcode = hdr->opcode; + copp->id = open->copp_id; + wake_up(&copp->wait); + kref_put(&copp->refcount, q6adm_free_copp); + } + break; + default: + dev_err(&adev->dev, "Unknown cmd:0x%x\n", + hdr->opcode); + break; + } + + return 0; +} + +static struct q6copp *q6adm_alloc_copp(struct q6adm *adm, int port_idx) +{ + struct q6copp *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); + + c = kzalloc(sizeof(*c), GFP_ATOMIC); + if (!c) + return ERR_PTR(-ENOMEM); + + set_bit(idx, &adm->copp_bitmap[port_idx]); + c->copp_idx = idx; + c->afe_port = port_idx; + c->adm = adm; + + init_waitqueue_head(&c->wait); + + return c; +} + +static int q6adm_apr_send_copp_pkt(struct q6adm *adm, struct q6copp *copp, + struct apr_pkt *pkt, uint32_t rsp_opcode) +{ + struct device *dev = adm->dev; + uint32_t opcode = pkt->hdr.opcode; + int ret; + + mutex_lock(&adm->lock); + copp->result.opcode = 0; + copp->result.status = 0; + ret = apr_send_pkt(adm->apr, pkt); + if (ret < 0) { + dev_err(dev, "Failed to send APR packet\n"); + ret = -EINVAL; + goto err; + } + + /* Wait for the callback with copp id */ + if (rsp_opcode) + ret = wait_event_timeout(copp->wait, + (copp->result.opcode == opcode) || + (copp->result.opcode == rsp_opcode), + msecs_to_jiffies(TIMEOUT_MS)); + else + ret = wait_event_timeout(copp->wait, + (copp->result.opcode == opcode), + msecs_to_jiffies(TIMEOUT_MS)); + + if (!ret) { + dev_err(dev, "ADM copp cmd timedout\n"); + ret = -ETIMEDOUT; + } else if (copp->result.status > 0) { + dev_err(dev, "DSP returned error[%d]\n", + copp->result.status); + ret = -EINVAL; + } + +err: + mutex_unlock(&adm->lock); + return ret; +} + +static int q6adm_device_close(struct q6adm *adm, struct q6copp *copp, + int port_id, int copp_idx) +{ + struct apr_pkt close; + + close.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + close.hdr.pkt_size = sizeof(close); + close.hdr.src_port = port_id; + close.hdr.dest_port = copp->id; + close.hdr.token = port_id << 16 | copp_idx; + close.hdr.opcode = ADM_CMD_DEVICE_CLOSE_V5; + + return q6adm_apr_send_copp_pkt(adm, copp, &close, 0); +} + +static struct q6copp *q6adm_find_matching_copp(struct q6adm *adm, + int port_id, int topology, + int mode, int rate, + int channel_mode, int bit_width, + int app_type) +{ + struct q6copp *c = NULL; + struct q6copp *ret = NULL; + unsigned long flags; + + spin_lock_irqsave(&adm->copps_list_lock, flags); + + list_for_each_entry(c, &adm->copps_list, node) { + if ((port_id == c->afe_port) && (topology == c->topology) && + (mode == c->mode) && (rate == c->rate) && + (bit_width == c->bit_width) && (app_type == c->app_type)) { + ret = c; + kref_get(&c->refcount); + } + } + spin_unlock_irqrestore(&adm->copps_list_lock, flags); + + return ret; +} + +static int q6adm_device_open(struct q6adm *adm, struct q6copp *copp, + int port_id, int path, int topology, + int channel_mode, int bit_width, int rate) +{ + struct q6adm_cmd_device_open_v5 *open; + int afe_port = q6afe_get_port_id(port_id); + struct apr_pkt *pkt; + void *p; + int ret, pkt_size; + + pkt_size = APR_HDR_SIZE + sizeof(*open); + p = kzalloc(pkt_size, GFP_KERNEL); + if (!p) + return -ENOMEM; + + pkt = p; + open = p + APR_HDR_SIZE; + pkt->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + pkt->hdr.pkt_size = pkt_size; + pkt->hdr.src_port = afe_port; + pkt->hdr.dest_port = afe_port; + pkt->hdr.token = port_id << 16 | copp->copp_idx; + pkt->hdr.opcode = ADM_CMD_DEVICE_OPEN_V5; + open->flags = ADM_LEGACY_DEVICE_SESSION; + open->mode_of_operation = path; + open->endpoint_id_1 = afe_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) + goto err; + + ret = q6adm_apr_send_copp_pkt(adm, copp, pkt, + ADM_CMDRSP_DEVICE_OPEN_V5); + +err: + kfree(pkt); + return ret; +} + +/** + * q6adm_open() - open adm and grab a free copp + * + * @dev: Pointer to adm child device. + * @port_id: port id + * @path: playback or capture path. + * @rate: rate at which copp is required. + * @channel_mode: channel mode + * @topology: adm topology id + * @perf_mode: performace mode. + * @bit_width: audio sample bit width + * @app_type: Application type. + * @acdb_id: ACDB id + * + * Return: Will be an negative on error or a valid copp pointer on success. + */ +struct q6copp *q6adm_open(struct device *dev, 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 q6adm *adm = dev_get_drvdata(dev->parent); + struct q6copp *copp; + unsigned long flags; + int ret = 0; + + if (port_id < 0) { + dev_err(dev, "Invalid port_id 0x%x\n", port_id); + return ERR_PTR(-EINVAL); + } + + copp = q6adm_find_matching_copp(adm, port_id, topology, perf_mode, + rate, channel_mode, bit_width, app_type); + if (copp) { + dev_err(dev, "Found Matching Copp 0x%x\n", copp->copp_idx); + return copp; + } + + spin_lock_irqsave(&adm->copps_list_lock, flags); + copp = q6adm_alloc_copp(adm, port_id); + if (IS_ERR_OR_NULL(copp)) { + spin_unlock_irqrestore(&adm->copps_list_lock, flags); + return ERR_CAST(copp); + } + + list_add_tail(&copp->node, &adm->copps_list); + spin_unlock_irqrestore(&adm->copps_list_lock, flags); + + kref_init(&copp->refcount); + copp->topology = topology; + copp->mode = perf_mode; + copp->rate = rate; + copp->channels = channel_mode; + copp->bit_width = bit_width; + copp->app_type = app_type; + + + ret = q6adm_device_open(adm, copp, port_id, path, topology, + channel_mode, bit_width, rate); + if (ret < 0) { + kref_put(&copp->refcount, q6adm_free_copp); + return ERR_PTR(ret); + } + + return copp; +} +EXPORT_SYMBOL_GPL(q6adm_open); + +/** + * q6adm_get_copp_id() - get copp index + * + * @copp: Pointer to valid copp + * + * Return: Will be an negative on error or a valid copp index on success. + **/ +int q6adm_get_copp_id(struct q6copp *copp) +{ + if (!copp) + return -EINVAL; + + return copp->copp_idx; +} +EXPORT_SYMBOL_GPL(q6adm_get_copp_id); + +/** + * q6adm_matrix_map() - Map asm streams and afe ports using payload + * + * @dev: Pointer to adm child device. + * @path: playback or capture path. + * @payload_map: map between session id and afe ports. + * @perf_mode: Performace mode. + * + * Return: Will be an negative on error or a zero on success. + */ +int q6adm_matrix_map(struct device *dev, int path, + struct route_payload payload_map, int perf_mode) +{ + struct q6adm *adm = dev_get_drvdata(dev->parent); + struct q6adm_cmd_matrix_map_routings_v5 *route; + struct q6adm_session_map_node_v5 *node; + struct apr_pkt *pkt; + uint16_t *copps_list; + int pkt_size, ret, i, copp_idx; + void *matrix_map = NULL; + struct q6copp *copp; + + /* Assumes port_ids have already been validated during adm_open */ + pkt_size = (APR_HDR_SIZE + sizeof(*route) + sizeof(*node) + + (sizeof(uint32_t) * payload_map.num_copps)); + + matrix_map = kzalloc(pkt_size, GFP_KERNEL); + if (!matrix_map) + return -ENOMEM; + + pkt = matrix_map; + route = matrix_map + APR_HDR_SIZE; + node = matrix_map + APR_HDR_SIZE + sizeof(*route); + copps_list = matrix_map + APR_HDR_SIZE + sizeof(*route) + sizeof(*node); + + pkt->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + pkt->hdr.pkt_size = pkt_size; + pkt->hdr.token = 0; + pkt->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; + case ADM_PATH_LIVE_REC: + route->matrix_id = ADM_MATRIX_ID_AUDIO_TX; + break; + default: + dev_err(dev, "Wrong path set[%d]\n", path); + break; + } + + node->session_id = payload_map.session_id; + node->num_copps = payload_map.num_copps; + + for (i = 0; i < payload_map.num_copps; i++) { + int port_idx = payload_map.port_id[i]; + + if (port_idx < 0) { + dev_err(dev, "Invalid port_id 0x%x\n", + payload_map.port_id[i]); + kfree(pkt); + return -EINVAL; + } + copp_idx = payload_map.copp_idx[i]; + + copp = q6adm_find_copp(adm, port_idx, copp_idx); + if (!copp) { + kfree(pkt); + return -EINVAL; + } + + copps_list[i] = copp->id; + kref_put(&copp->refcount, q6adm_free_copp); + } + + mutex_lock(&adm->lock); + adm->result.status = 0; + adm->result.opcode = 0; + + ret = apr_send_pkt(adm->apr, pkt); + if (ret < 0) { + dev_err(dev, "routing for stream %d failed ret %d\n", + payload_map.session_id, ret); + goto fail_cmd; + } + ret = wait_event_timeout(adm->matrix_map_wait, + adm->result.opcode == pkt->hdr.opcode, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + dev_err(dev, "routing for stream %d failed\n", + payload_map.session_id); + ret = -ETIMEDOUT; + goto fail_cmd; + } else if (adm->result.status > 0) { + dev_err(dev, "DSP returned error[%d]\n", + adm->result.status); + ret = -EINVAL; + goto fail_cmd; + } + +fail_cmd: + mutex_unlock(&adm->lock); + kfree(pkt); + return ret; +} +EXPORT_SYMBOL_GPL(q6adm_matrix_map); + +/** + * q6adm_close() - Close adm copp + * + * @dev: Pointer to adm child device. + * @copp: pointer to previously opened copp + * + * Return: Will be an negative on error or a zero on success. + */ +int q6adm_close(struct device *dev, struct q6copp *copp) +{ + struct q6adm *adm = dev_get_drvdata(dev->parent); + int ret = 0; + + ret = q6adm_device_close(adm, copp, copp->afe_port, copp->copp_idx); + if (ret < 0) { + dev_err(adm->dev, "Failed to close copp %d\n", ret); + return ret; + } + + kref_put(&copp->refcount, q6adm_free_copp); + + return 0; +} +EXPORT_SYMBOL_GPL(q6adm_close); + +static int q6adm_probe(struct apr_device *adev) +{ + struct device *dev = &adev->dev; + struct device_node *dais_np; + struct q6adm *adm; + + adm = devm_kzalloc(&adev->dev, sizeof(*adm), GFP_KERNEL); + if (!adm) + return -ENOMEM; + + adm->apr = adev; + dev_set_drvdata(&adev->dev, adm); + adm->dev = dev; + q6core_get_svc_api_info(adev->svc_id, &adm->ainfo); + mutex_init(&adm->lock); + init_waitqueue_head(&adm->matrix_map_wait); + + INIT_LIST_HEAD(&adm->copps_list); + spin_lock_init(&adm->copps_list_lock); + + dais_np = of_get_child_by_name(dev->of_node, "routing"); + if (dais_np) { + adm->pdev_routing = of_platform_device_create(dais_np, + "q6routing", dev); + of_node_put(dais_np); + } + + return 0; +} + +static int q6adm_remove(struct apr_device *adev) +{ + struct q6adm *adm = dev_get_drvdata(&adev->dev); + + if (adm->pdev_routing) + of_platform_device_destroy(&adm->pdev_routing->dev, NULL); + + return 0; +} + +static const struct of_device_id q6adm_device_id[] = { + { .compatible = "qcom,q6adm" }, + {}, +}; +MODULE_DEVICE_TABLE(of, q6adm_device_id); + +static struct apr_driver qcom_q6adm_driver = { + .probe = q6adm_probe, + .remove = q6adm_remove, + .callback = q6adm_callback, + .driver = { + .name = "qcom-q6adm", + .of_match_table = of_match_ptr(q6adm_device_id), + }, +}; + +module_apr_driver(qcom_q6adm_driver); +MODULE_DESCRIPTION("Q6 Audio Device Manager"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/qcom/qdsp6/q6adm.h b/sound/soc/qcom/qdsp6/q6adm.h new file mode 100644 index 000000000000..4f56999b7fab --- /dev/null +++ b/sound/soc/qcom/qdsp6/q6adm.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __Q6_ADM_V2_H__ +#define __Q6_ADM_V2_H__ + +#define ADM_PATH_PLAYBACK 0x1 +#define ADM_PATH_LIVE_REC 0x2 +#define MAX_COPPS_PER_PORT 8 +#define NULL_COPP_TOPOLOGY 0x00010312 + +/* multiple copp per stream. */ +struct route_payload { + int num_copps; + int session_id; + int copp_idx[MAX_COPPS_PER_PORT]; + int port_id[MAX_COPPS_PER_PORT]; +}; + +struct q6copp; +struct q6copp *q6adm_open(struct device *dev, 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(struct device *dev, struct q6copp *copp); +int q6adm_get_copp_id(struct q6copp *copp); +int q6adm_matrix_map(struct device *dev, int path, + struct route_payload payload_map, int perf_mode); + +#endif /* __Q6_ADM_V2_H__ */