From patchwork Thu Dec 14 17:33:53 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Srinivas Kandagatla X-Patchwork-Id: 122013 Delivered-To: patch@linaro.org Received: by 10.140.22.227 with SMTP id 90csp7110737qgn; Thu, 14 Dec 2017 09:41:52 -0800 (PST) X-Google-Smtp-Source: ACJfBot/2yaw/L9nWAB+gDh027hr+mtA7gV93WATxur327EWij+wsjPAO8XxsIlSv3uSmEXpIVFd X-Received: by 10.98.135.138 with SMTP id i132mr10222712pfe.130.1513273312212; Thu, 14 Dec 2017 09:41:52 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1513273312; cv=none; d=google.com; s=arc-20160816; b=e6J/mc4nRkGIUg6SRXBViaj1BXhcdn7pQPEoDg+/TddrA5p3Jgt8k+4evEY2K5+nzi NJQx31lY3Q5VwrSe6vHfnyvJK3xGdsCe75F8CE8XfOlUIqpZYGILIaZmoTzh5N0eUVP7 axCx5XBO0mEny1v15tengCCwS97oFTQSHjIZ//hsrZ7wWhQ+RU0XXkifP5oo1Lzh/sdD XThP9uWGhzE33qqRCwntqdDpJHxffwDtVHebk0JfsK7a3isrHZHIim+JWtBMArcHpTwN hG5aTCZT7oZHY7P479OmvpQkZnYL7pvUipOMlvOYvZO+l0JeZWuOECD0mcWP5uOITMfr w+hw== 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=EaGdKQ75qLPmdkyoqpfr5BGU14ZHnd8D48G7JV0Vh08=; b=V7l4MMlax5Piy7Bpduao8FSbC5GoQFFQg/wAH4o82hlI5t9KQ/7iMo/b3TqlG3N70Q 3Lttjm4FlnYB2tHijNrbKioJTTFx/Hw47z1geBOYsDCqm9kmUfPMHLUM8Qz/zBJ9PMj5 DyvF9/UkmdqPCcDcVe3dSrqB2eLdVOmkd6ZK3vfKngqRnY+nd3ZvdA+IZearH+M+0V0M KYyuce149arqsSa67CdTpSp43viqaWbJDszwZ/gUyzOhSHP9nmoSVhLPvwJDUULUyK/1 BHy8uea4QO2zEy1d509qeD4s3Ahz99kzPQ0dQOgcipYYEs6X48xHe6LykyxHh2mF8PsI 4aJw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=iUQpwWlP; 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 n22si3168767pgc.51.2017.12.14.09.41.51; Thu, 14 Dec 2017 09:41:52 -0800 (PST) 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=iUQpwWlP; 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 S1753932AbdLNRlt (ORCPT + 20 others); Thu, 14 Dec 2017 12:41:49 -0500 Received: from mail-wr0-f196.google.com ([209.85.128.196]:43351 "EHLO mail-wr0-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753819AbdLNRgi (ORCPT ); Thu, 14 Dec 2017 12:36:38 -0500 Received: by mail-wr0-f196.google.com with SMTP id z34so5817503wrz.10 for ; Thu, 14 Dec 2017 09:36:37 -0800 (PST) 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=EaGdKQ75qLPmdkyoqpfr5BGU14ZHnd8D48G7JV0Vh08=; b=iUQpwWlPiNwDsfqkI2mAnEphZtguwBMtZX3V3VLyCO6iNzJx7kfu7AUcS59/u2KX/z nX4IOV93VNqotDD5TewW8uPjqboX3+TlTtkZKkNdlHQdNc4f5mfDar3StSv4yV2hyHEn pNp08jxzJ0XzJ5A18omV2PDSiF2AaxM1Ft3pE= 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=EaGdKQ75qLPmdkyoqpfr5BGU14ZHnd8D48G7JV0Vh08=; b=LO87NzCg2DM1YZcgMwVYGW2bCbk4LaUf9up8hR0SmUjpfS7xh20jfLh8fK9dEhgBNr mAWrd5yjEwrwjYpFvqxGUeM0ipxE3QnO3J4bRHmiezXhKYPK00FxEYTE2KS4QMgwcQ2+ t+r96mwWwMiI2cPHHomUFkrSzbKuFFJiskrY6i5T+0+iI03kB2syRRKo/OZhrksb1LrY OR5ll0WNugT6d+4Kbz6xXKuXbVihW8zDEQEMZTqHCs9/JVeMKZi8p0MmGzQnFNnithGv NZiHa5OVLZ8ITMh8cK50wTpo4KhBPFnNP8wJKcOuATnPlUSghJYs/Xpgw4WzDZk6Pygb TWQQ== X-Gm-Message-State: AKGB3mJFucCqEv+wQHvgpDnkGG/6TLjybCs6b8emPnmK8Gwn0jZl3s89 Q24GNrwdQ0mNSMyqsraTcsIkeA== X-Received: by 10.223.172.115 with SMTP id v106mr6919348wrc.195.1513272996659; Thu, 14 Dec 2017 09:36:36 -0800 (PST) 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 r82sm1741757wme.31.2017.12.14.09.36.34 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 14 Dec 2017 09:36:35 -0800 (PST) From: srinivas.kandagatla@linaro.org To: Andy Gross , Mark Brown , linux-arm-msm@vger.kernel.org, alsa-devel@alsa-project.org Cc: David Brown , Rob Herring , Mark Rutland , Liam Girdwood , Patrick Lai , Banajit Goswami , Jaroslav Kysela , Takashi Iwai , linux-soc@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, sboyd@codeaurora.org, Srinivas Kandagatla Subject: [RESEND PATCH v2 06/15] ASoC: qcom: qdsp6: Add support to Q6ASM Date: Thu, 14 Dec 2017 17:33:53 +0000 Message-Id: <20171214173402.19074-7-srinivas.kandagatla@linaro.org> X-Mailer: git-send-email 2.15.0 In-Reply-To: <20171214173402.19074-1-srinivas.kandagatla@linaro.org> References: <20171214173402.19074-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 basic support to Q6 ASM (Audio Stream Manager) module on Q6DSP. ASM supports up to 8 concurrent streams. each stream can be setup as playback/capture. ASM provides top control functions like Pause/flush/resume for playback and record. ASM can Create/destroy encoder, decoder and also provides POPP dynamic services. This patch adds support to basic features to allow hdmi playback. Signed-off-by: Srinivas Kandagatla --- sound/soc/qcom/Kconfig | 5 + sound/soc/qcom/qdsp6/Makefile | 1 + sound/soc/qcom/qdsp6/q6asm.c | 250 ++++++++++++++++++++++++++++++++++++++++++ sound/soc/qcom/qdsp6/q6asm.h | 14 +++ 4 files changed, 270 insertions(+) create mode 100644 sound/soc/qcom/qdsp6/q6asm.c create mode 100644 sound/soc/qcom/qdsp6/q6asm.h -- 2.15.0 diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index a307880dc992..7ebdb879a8a3 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -52,10 +52,15 @@ config SND_SOC_QDSP6_ADM tristate default n +config SND_SOC_QDSP6_ASM + tristate + default n + config SND_SOC_QDSP6 tristate "SoC ALSA audio driver for QDSP6" select SND_SOC_QDSP6_AFE select SND_SOC_QDSP6_ADM + select SND_SOC_QDSP6_ASM 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 052813ea7062..49dd3ccab27b 100644 --- a/sound/soc/qcom/qdsp6/Makefile +++ b/sound/soc/qcom/qdsp6/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_SND_SOC_QDSP6_AFE) += q6afe.o obj-$(CONFIG_SND_SOC_QDSP6_ADM) += q6adm.o +obj-$(CONFIG_SND_SOC_QDSP6_ASM) += q6asm.o diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c new file mode 100644 index 000000000000..9cc583afef4d --- /dev/null +++ b/sound/soc/qcom/qdsp6/q6asm.c @@ -0,0 +1,250 @@ +/* SPDX-License-Identifier: GPL-2.0 +* Copyright (c) 2011-2016, The Linux Foundation +* Copyright (c) 2017, Linaro Limited +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "q6asm.h" +#include "common.h" + +#define TUN_READ_IO_MODE 0x0004 /* tunnel read write mode */ +#define SYNC_IO_MODE 0x0001 +#define ASYNC_IO_MODE 0x0002 + +struct audio_client { + int session; + app_cb cb; + int cmd_state; + void *priv; + uint32_t io_mode; + uint64_t time_stamp; + struct apr_device *adev; + struct mutex cmd_lock; + wait_queue_head_t cmd_wait; + int perf_mode; + int stream_id; + struct device *dev; +}; + +struct q6asm { + struct apr_device *adev; + int mem_state; + struct device *dev; + wait_queue_head_t mem_wait; + struct mutex session_lock; + struct platform_device *pcmdev; + struct audio_client *session[MAX_SESSIONS + 1]; +}; + +static int q6asm_session_alloc(struct audio_client *ac, struct q6asm *a) +{ + int n = -EINVAL; + + mutex_lock(&a->session_lock); + for (n = 1; n <= MAX_SESSIONS; n++) { + if (!a->session[n]) { + a->session[n] = ac; + break; + } + } + mutex_unlock(&a->session_lock); + + return n; +} + +static bool q6asm_is_valid_audio_client(struct audio_client *ac) +{ + struct q6asm *a = dev_get_drvdata(ac->dev->parent); + int n; + + for (n = 1; n <= MAX_SESSIONS; n++) { + if (a->session[n] == ac) + return 1; + } + + return 0; +} + +static void q6asm_session_free(struct audio_client *ac) +{ + struct q6asm *a = dev_get_drvdata(ac->dev->parent); + + if (!a) + return; + + mutex_lock(&a->session_lock); + a->session[ac->session] = 0; + ac->session = 0; + ac->perf_mode = LEGACY_PCM_MODE; + mutex_unlock(&a->session_lock); +} + +/** + * q6asm_audio_client_free() - Freee allocated audio client + * + * @ac: audio client to free + */ +void q6asm_audio_client_free(struct audio_client *ac) +{ + q6asm_session_free(ac); + kfree(ac); +} +EXPORT_SYMBOL_GPL(q6asm_audio_client_free); + +static struct audio_client *q6asm_get_audio_client(struct q6asm *a, + int session_id) +{ + if ((session_id <= 0) || (session_id > MAX_SESSIONS)) { + dev_err(a->dev, "invalid session: %d\n", session_id); + goto err; + } + + if (!a->session[session_id]) { + dev_err(a->dev, "session not active: %d\n", session_id); + goto err; + } + return a->session[session_id]; +err: + return NULL; +} + +static int q6asm_srvc_callback(struct apr_device *adev, struct apr_client_data *data) +{ + struct q6asm *q6asm = dev_get_drvdata(&adev->dev); + struct audio_client *ac = NULL; + uint32_t sid = 0; + uint32_t *payload; + + if (!data) { + dev_err(&adev->dev, "%s: Invalid CB\n", __func__); + return 0; + } + + payload = data->payload; + sid = (data->token >> 8) & 0x0F; + ac = q6asm_get_audio_client(q6asm, sid); + if (!ac) { + dev_err(&adev->dev, "Audio Client not active\n"); + return 0; + } + + if (ac->cb) + ac->cb(data->opcode, data->token, data->payload, ac->priv); + return 0; +} + +/** + * q6asm_get_session_id() - get session id for audio client + * + * @ac: audio client pointer + * + * Return: Will be an session id of the audio client. + */ +int q6asm_get_session_id(struct audio_client *c) +{ + return c->session; +} +EXPORT_SYMBOL_GPL(q6asm_get_session_id); + +/** + * q6asm_audio_client_alloc() - Allocate a new audio client + * + * @dev: Pointer to asm child device. + * @cb: event callback. + * @priv: private data associated with this client. + * + * Return: Will be an error pointer on error or a valid audio client + * on success. + */ +struct audio_client *q6asm_audio_client_alloc(struct device *dev, + app_cb cb, void *priv) +{ + struct q6asm *a = dev_get_drvdata(dev->parent); + struct audio_client *ac; + int n; + + ac = kzalloc(sizeof(struct audio_client), GFP_KERNEL); + if (!ac) + return NULL; + + n = q6asm_session_alloc(ac, a); + if (n <= 0) { + dev_err(dev, "ASM Session alloc fail n=%d\n", n); + kfree(ac); + return NULL; + } + + ac->session = n; + ac->cb = cb; + ac->dev = dev; + ac->priv = priv; + ac->io_mode = SYNC_IO_MODE; + ac->perf_mode = LEGACY_PCM_MODE; + /* DSP expects stream id from 1 */ + ac->stream_id = 1; + ac->adev = a->adev; + + init_waitqueue_head(&ac->cmd_wait); + mutex_init(&ac->cmd_lock); + ac->cmd_state = 0; + + return ac; +} +EXPORT_SYMBOL_GPL(q6asm_audio_client_alloc); + + +static int q6asm_probe(struct apr_device *adev) +{ + struct q6asm *q6asm; + + q6asm = devm_kzalloc(&adev->dev, sizeof(*q6asm), GFP_KERNEL); + if (!q6asm) + return -ENOMEM; + + q6asm->dev = &adev->dev; + q6asm->adev = adev; + q6asm->mem_state = 0; + init_waitqueue_head(&q6asm->mem_wait); + mutex_init(&q6asm->session_lock); + dev_set_drvdata(&adev->dev, q6asm); + + q6asm->pcmdev = platform_device_register_data(&adev->dev, + "q6asm_dai", -1, NULL, 0); + + return 0; +} + +static int q6asm_remove(struct apr_device *adev) +{ + struct q6asm *q6asm = dev_get_drvdata(&adev->dev); + + platform_device_unregister(q6asm->pcmdev); + + return 0; +} + +static const struct apr_device_id q6asm_id[] = { + {"Q6ASM", APR_DOMAIN_ADSP, APR_SVC_ASM, APR_CLIENT_AUDIO}, + {} +}; + +static struct apr_driver qcom_q6asm_driver = { + .probe = q6asm_probe, + .remove = q6asm_remove, + .callback = q6asm_srvc_callback, + .id_table = q6asm_id, + .driver = { + .name = "qcom-q6asm", + }, +}; + +module_apr_driver(qcom_q6asm_driver); +MODULE_DESCRIPTION("Q6 Audio Stream Manager driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/qcom/qdsp6/q6asm.h b/sound/soc/qcom/qdsp6/q6asm.h new file mode 100644 index 000000000000..7a8a9039fd89 --- /dev/null +++ b/sound/soc/qcom/qdsp6/q6asm.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __Q6_ASM_H__ +#define __Q6_ASM_H__ + +#define MAX_SESSIONS 16 + +typedef void (*app_cb) (uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv); +struct audio_client; +struct audio_client *q6asm_audio_client_alloc(struct device *dev, + app_cb cb, void *priv); +void q6asm_audio_client_free(struct audio_client *ac); +int q6asm_get_session_id(struct audio_client *ac); +#endif /* __Q6_ASM_H__ */ From patchwork Thu Dec 14 17:33:55 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Srinivas Kandagatla X-Patchwork-Id: 122012 Delivered-To: patch@linaro.org Received: by 10.140.22.227 with SMTP id 90csp7109725qgn; Thu, 14 Dec 2017 09:40:53 -0800 (PST) X-Google-Smtp-Source: ACJfBovM92MGlUUwEKux3rUnDN5lbOsPWDP611foNetBRq1OMPFsSokX8UBu7F2jZ36Q9lzysJBB X-Received: by 10.99.95.70 with SMTP id t67mr9273296pgb.49.1513273253668; Thu, 14 Dec 2017 09:40:53 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1513273253; cv=none; d=google.com; s=arc-20160816; b=DKWgUOKOYFuuSLq6f7/9PsRJxAVk5R4tO9PK90rwnViFwsL78OJ06I2Hw2HOjzRaKM P1JU3Aotcv0FT8VkODHaogbInqhcJavVMeJDB9sBuk9kx8cwfKW0ZOLfjB00zL225I1m 19KHWFBAbhe1DdElVXohD0M38KkjGcwmQfnoAloY5Cjl5nIG5eTKhz9Tvu2INH2NP42a nrS7SrhHaG0PDNKSM5PGurC0dR1STiwPIG4lO4Jt1inLwbS/oF8CLzWxlU/kw/9blQHV IKPfgsB2ved/KvA3LpeRF9V2UGxWDPv9Kd+6Xw7Cdr2C8JZLTFX3BRLWr4E1B0Iexqev MbfQ== 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=lyDSytykL6/GEF2GBqE/UFmRdNv9oB4GeJrqQrtV21I=; b=tZoW4MljZYw9ENtJOMyG12ViUfANQGp327O6QQ/ziANu0wlDfrjE3eZx9C7Gf+O15L I/fq2ZE4WiZ2QX/6JytzVUzGotk1lNhAyD3ROReV/k5nuDq/B1QgZOuCaQPqLQsM49tf KaqapsXbNeW7ZtCWVPn1jxiJLtfvaqXXpgLUw1fWsC9fVQ+6G49vVkW4nRr3X8T9GH7T 53dMn/o6cHGmD/HNkDcOixv24OAiEw8IvBWKWJXBC2OVfWcQBLCdCVQnjVT6yzr17MOS XBEIf/X/Chyx4Y3geotuhE8apbW8aJ/FuofOSnTecS8pFLB2j6c88lo83plccsgyRRqJ EJeQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=Cdv5JMrg; 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 y5si3577788pfg.75.2017.12.14.09.40.53; Thu, 14 Dec 2017 09:40:53 -0800 (PST) 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=Cdv5JMrg; 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 S1753963AbdLNRku (ORCPT + 20 others); Thu, 14 Dec 2017 12:40:50 -0500 Received: from mail-wr0-f194.google.com ([209.85.128.194]:35391 "EHLO mail-wr0-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753624AbdLNRgm (ORCPT ); Thu, 14 Dec 2017 12:36:42 -0500 Received: by mail-wr0-f194.google.com with SMTP id g53so5850180wra.2 for ; Thu, 14 Dec 2017 09:36:41 -0800 (PST) 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=lyDSytykL6/GEF2GBqE/UFmRdNv9oB4GeJrqQrtV21I=; b=Cdv5JMrgxZ3aUGBZYfvWAOiGxg3AfX4nzaRMDg/cTXdQM+/PdClnNHUBPJ6uKdshHj z+Kmmqq4ygXJuqLRTFHEVWUGTpCUnXTOuF4TifVCKzUm0W8qM8KsWijrPVvWtD0aATpE WUien13eGRvZXUjHzruoBK2fgigG45kzcnC/E= 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=lyDSytykL6/GEF2GBqE/UFmRdNv9oB4GeJrqQrtV21I=; b=FLf0LPPDE8VJKIBu/lgp52Ff4o/jp9ZeRhMTQZUblzXjxRv4f1KUc7Tz2EeO5rbk8N +/7opC4qxW7c0H89HeVS+WDKCYzInNo291xgVLuYwdF+hNc/TqVGNuACnr8pyoXX8JcT Pfif5G8dpfkpZf8La4k+7UOI/tp176zk4kY/yPcSz1ojIRnT7mIfP/Jf30fLu5zioAMB Bt1m6u2EJM1M+6YYjB9MLvfTXOMnFfkqAooM+bQvX6v8t/OatUuQZQEQZyGktZRMwlZp 6a05iSZxlNPXvG46dRy2LF38LYBXtkuXXkD07t6vPoYM3VOhGESiZuUDt9Gv96d6Vuxw hSKQ== X-Gm-Message-State: AKGB3mKp+lmDYZqFv+16x+AZW1dh3g6ypGe1JUuDnaRvNKbm/7dg19Ih lqnsQNJJnowH5iBfJEqgZPWvWA== X-Received: by 10.223.134.1 with SMTP id 1mr6603449wrv.213.1513273000230; Thu, 14 Dec 2017 09:36:40 -0800 (PST) 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 r82sm1741757wme.31.2017.12.14.09.36.38 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 14 Dec 2017 09:36:39 -0800 (PST) From: srinivas.kandagatla@linaro.org To: Andy Gross , Mark Brown , linux-arm-msm@vger.kernel.org, alsa-devel@alsa-project.org Cc: David Brown , Rob Herring , Mark Rutland , Liam Girdwood , Patrick Lai , Banajit Goswami , Jaroslav Kysela , Takashi Iwai , linux-soc@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, sboyd@codeaurora.org, Srinivas Kandagatla Subject: [RESEND PATCH v2 08/15] ASoC: qcom: q6asm: add support to audio stream apis Date: Thu, 14 Dec 2017 17:33:55 +0000 Message-Id: <20171214173402.19074-9-srinivas.kandagatla@linaro.org> X-Mailer: git-send-email 2.15.0 In-Reply-To: <20171214173402.19074-1-srinivas.kandagatla@linaro.org> References: <20171214173402.19074-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 open, write and media format commands in the q6asm module. Signed-off-by: Srinivas Kandagatla --- sound/soc/qcom/qdsp6/q6asm.c | 530 ++++++++++++++++++++++++++++++++++++++++++- sound/soc/qcom/qdsp6/q6asm.h | 42 ++++ 2 files changed, 571 insertions(+), 1 deletion(-) -- 2.15.0 diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c index 4be92441f524..dabd6509ef99 100644 --- a/sound/soc/qcom/qdsp6/q6asm.c +++ b/sound/soc/qcom/qdsp6/q6asm.c @@ -8,16 +8,34 @@ #include #include #include +#include #include #include #include #include "q6asm.h" #include "common.h" +#define ASM_STREAM_CMD_CLOSE 0x00010BCD +#define ASM_STREAM_CMD_FLUSH 0x00010BCE +#define ASM_SESSION_CMD_PAUSE 0x00010BD3 +#define ASM_DATA_CMD_EOS 0x00010BDB +#define DEFAULT_POPP_TOPOLOGY 0x00010BE4 +#define ASM_STREAM_CMD_FLUSH_READBUFS 0x00010C09 #define ASM_CMD_SHARED_MEM_MAP_REGIONS 0x00010D92 #define ASM_CMDRSP_SHARED_MEM_MAP_REGIONS 0x00010D93 #define ASM_CMD_SHARED_MEM_UNMAP_REGIONS 0x00010D94 - +#define ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2 0x00010D98 +#define ASM_DATA_EVENT_WRITE_DONE_V2 0x00010D99 +#define ASM_SESSION_CMD_RUN_V2 0x00010DAA +#define ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2 0x00010DA5 +#define ASM_DATA_CMD_WRITE_V2 0x00010DAB +#define ASM_SESSION_CMD_SUSPEND 0x00010DEC +#define ASM_STREAM_CMD_OPEN_WRITE_V3 0x00010DB3 + +#define ASM_LEGACY_STREAM_SESSION 0 +#define ASM_END_POINT_DEVICE_MATRIX 0 +#define DEFAULT_APP_TYPE 0 +#define TUN_WRITE_IO_MODE 0x0008 /* tunnel read write mode */ #define TUN_READ_IO_MODE 0x0004 /* tunnel read write mode */ #define SYNC_IO_MODE 0x0001 #define ASYNC_IO_MODE 0x0002 @@ -42,6 +60,49 @@ struct avs_cmd_shared_mem_unmap_regions { u32 mem_map_handle; } __packed; +struct asm_data_cmd_media_fmt_update_v2 { + u32 fmt_blk_size; +} __packed; + +struct asm_multi_channel_pcm_fmt_blk_v2 { + struct apr_hdr hdr; + struct asm_data_cmd_media_fmt_update_v2 fmt_blk; + u16 num_channels; + u16 bits_per_sample; + u32 sample_rate; + u16 is_signed; + u16 reserved; + u8 channel_mapping[8]; +} __packed; + +struct asm_data_cmd_write_v2 { + struct apr_hdr hdr; + u32 buf_addr_lsw; + u32 buf_addr_msw; + u32 mem_map_handle; + u32 buf_size; + u32 seq_id; + u32 timestamp_lsw; + u32 timestamp_msw; + u32 flags; +} __packed; + +struct asm_stream_cmd_open_write_v3 { + struct apr_hdr hdr; + uint32_t mode_flags; + uint16_t sink_endpointype; + uint16_t bits_per_sample; + uint32_t postprocopo_id; + uint32_t dec_fmt_id; +} __packed; + +struct asm_session_cmd_run_v2 { + struct apr_hdr hdr; + u32 flags; + u32 time_lsw; + u32 time_msw; +} __packed; + struct audio_buffer { dma_addr_t phys; uint32_t used; @@ -408,6 +469,111 @@ static struct audio_client *q6asm_get_audio_client(struct q6asm *a, return NULL; } +static int32_t q6asm_callback(struct apr_device *adev, + struct apr_client_data *data, int session_id) +{ + struct audio_client *ac;// = (struct audio_client *)priv; + uint32_t token; + uint32_t *payload; + uint32_t wakeup_flag = 1; + uint32_t client_event = 0; + struct q6asm *q6asm = dev_get_drvdata(&adev->dev); + + if (data == NULL) + return -EINVAL; + + ac = q6asm_get_audio_client(q6asm, session_id); + if (!q6asm_is_valid_audio_client(ac)) + return -EINVAL; + + payload = data->payload; + + if (data->opcode == APR_BASIC_RSP_RESULT) { + token = data->token; + switch (payload[0]) { + case ASM_SESSION_CMD_PAUSE: + client_event = ASM_CLIENT_EVENT_CMD_PAUSE_DONE; + break; + case ASM_SESSION_CMD_SUSPEND: + client_event = ASM_CLIENT_EVENT_CMD_SUSPEND_DONE; + break; + case ASM_DATA_CMD_EOS: + client_event = ASM_CLIENT_EVENT_CMD_EOS_DONE; + break; + break; + case ASM_STREAM_CMD_FLUSH: + client_event = ASM_CLIENT_EVENT_CMD_FLUSH_DONE; + break; + case ASM_SESSION_CMD_RUN_V2: + client_event = ASM_CLIENT_EVENT_CMD_RUN_DONE; + break; + + case ASM_STREAM_CMD_FLUSH_READBUFS: + if (token != ac->session) { + dev_err(ac->dev, "session invalid\n"); + return -EINVAL; + } + case ASM_STREAM_CMD_CLOSE: + client_event = ASM_CLIENT_EVENT_CMD_CLOSE_DONE; + break; + case ASM_STREAM_CMD_OPEN_WRITE_V3: + case ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2: + if (payload[1] != 0) { + dev_err(ac->dev, + "cmd = 0x%x returned error = 0x%x\n", + payload[0], payload[1]); + if (wakeup_flag) { + ac->cmd_state = payload[1]; + wake_up(&ac->cmd_wait); + } + return 0; + } + break; + default: + dev_err(ac->dev, "command[0x%x] not expecting rsp\n", + payload[0]); + break; + } + + if (ac->cmd_state && wakeup_flag) { + ac->cmd_state = 0; + wake_up(&ac->cmd_wait); + } + if (ac->cb) + ac->cb(client_event, data->token, + data->payload, ac->priv); + + return 0; + } + + switch (data->opcode) { + case ASM_DATA_EVENT_WRITE_DONE_V2:{ + struct audio_port_data *port = + &ac->port[SNDRV_PCM_STREAM_PLAYBACK]; + + client_event = ASM_CLIENT_EVENT_DATA_WRITE_DONE; + + if (ac->io_mode & SYNC_IO_MODE) { + dma_addr_t phys = port->buf[data->token].phys; + + if (lower_32_bits(phys) != payload[0] || + upper_32_bits(phys) != payload[1]) { + dev_err(ac->dev, "Expected addr %pa\n", + &port->buf[data->token].phys); + return -EINVAL; + } + token = data->token; + port->buf[token].used = 1; + } + break; + } + } + if (ac->cb) + ac->cb(client_event, data->token, data->payload, ac->priv); + + return 0; +} + static int q6asm_srvc_callback(struct apr_device *adev, struct apr_client_data *data) { struct q6asm *a, *q6asm = dev_get_drvdata(&adev->dev); @@ -415,12 +581,16 @@ static int q6asm_srvc_callback(struct apr_device *adev, struct apr_client_data * struct audio_port_data *port; uint32_t dir = 0; uint32_t sid = 0; + int dest_port; uint32_t *payload; if (!data) { dev_err(&adev->dev, "%s: Invalid CB\n", __func__); return 0; } + dest_port = (data->dest_port >> 8) & 0xFF; + if (dest_port) + return q6asm_callback(adev, data, dest_port); payload = data->payload; sid = (data->token >> 8) & 0x0F; @@ -540,6 +710,364 @@ struct audio_client *q6asm_audio_client_alloc(struct device *dev, } EXPORT_SYMBOL_GPL(q6asm_audio_client_alloc); +static int __q6asm_open_write(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample, uint32_t stream_id, + bool is_gapless_mode) +{ + struct asm_stream_cmd_open_write_v3 open; + int rc; + + q6asm_add_hdr(ac, &open.hdr, sizeof(open), true, stream_id); + ac->cmd_state = -1; + + open.hdr.opcode = ASM_STREAM_CMD_OPEN_WRITE_V3; + open.mode_flags = 0x00; + open.mode_flags |= ASM_LEGACY_STREAM_SESSION; + if (is_gapless_mode) + open.mode_flags |= 1 << ASM_SHIFT_GAPLESS_MODE_FLAG; + + /* source endpoint : matrix */ + open.sink_endpointype = ASM_END_POINT_DEVICE_MATRIX; + open.bits_per_sample = bits_per_sample; + open.postprocopo_id = DEFAULT_POPP_TOPOLOGY; + + switch (format) { + case FORMAT_LINEAR_PCM: + open.dec_fmt_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2; + break; + default: + dev_err(ac->dev, "Invalid format 0x%x\n", format); + return -EINVAL; + } + rc = apr_send_pkt(ac->adev, (uint32_t *) &open); + if (rc < 0) + return rc; + + rc = wait_event_timeout(ac->cmd_wait, (ac->cmd_state >= 0), 5 * HZ); + if (!rc) { + dev_err(ac->dev, "timeout on open write\n"); + return -ETIMEDOUT; + } + + if (ac->cmd_state > 0) + return adsp_err_get_lnx_err_code(ac->cmd_state); + + ac->io_mode |= TUN_WRITE_IO_MODE; + + return 0; +} + +/** + * q6asm_open_write() - Open audio client for writing + * + * @ac: audio client pointer + * @format: audio sample format + * @bits_per_sample: bits per sample + * + * Return: Will be an negative value on error or zero on success + */ +int q6asm_open_write(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample) +{ + return __q6asm_open_write(ac, format, bits_per_sample, + ac->stream_id, false); +} +EXPORT_SYMBOL_GPL(q6asm_open_write); + +static int __q6asm_run(struct audio_client *ac, uint32_t flags, + uint32_t msw_ts, uint32_t lsw_ts, bool wait) +{ + struct asm_session_cmd_run_v2 run; + int rc; + + q6asm_add_hdr(ac, &run.hdr, sizeof(run), true, ac->stream_id); + ac->cmd_state = -1; + + run.hdr.opcode = ASM_SESSION_CMD_RUN_V2; + run.flags = flags; + run.time_lsw = lsw_ts; + run.time_msw = msw_ts; + + rc = apr_send_pkt(ac->adev, (uint32_t *) &run); + if (rc < 0) + return rc; + + if (wait) { + rc = wait_event_timeout(ac->cmd_wait, (ac->cmd_state >= 0), + 5 * HZ); + if (!rc) { + dev_err(ac->dev, "timeout on run cmd\n"); + return -ETIMEDOUT; + } + if (ac->cmd_state > 0) + return adsp_err_get_lnx_err_code(ac->cmd_state); + } + + return 0; +} + +/** + * q6asm_run() - start the audio client + * + * @ac: audio client pointer + * @flags: flags associated with write + * @msw_ts: timestamp msw + * @lsw_ts: timestamp lsw + * + * Return: Will be an negative value on error or zero on success + */ +int q6asm_run(struct audio_client *ac, uint32_t flags, + uint32_t msw_ts, uint32_t lsw_ts) +{ + return __q6asm_run(ac, flags, msw_ts, lsw_ts, true); +} +EXPORT_SYMBOL_GPL(q6asm_run); + +/** + * q6asm_run_nowait() - start the audio client withou blocking + * + * @ac: audio client pointer + * @flags: flags associated with write + * @msw_ts: timestamp msw + * @lsw_ts: timestamp lsw + * + * Return: Will be an negative value on error or zero on success + */ +int q6asm_run_nowait(struct audio_client *ac, uint32_t flags, + uint32_t msw_ts, uint32_t lsw_ts) +{ + return __q6asm_run(ac, flags, msw_ts, lsw_ts, false); +} +EXPORT_SYMBOL_GPL(q6asm_run_nowait); + +/** + * q6asm_media_format_block_multi_ch_pcm() - setup pcm configuration + * + * @ac: audio client pointer + * @rate: audio sample rate + * @channels: number of audio channels. + * @use_default_chmap: flag to use default ch map. + * @channel_map: channel map pointer + * @bits_per_sample: bits per sample + * + * Return: Will be an negative value on error or zero on success + */ +int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac, + uint32_t rate, uint32_t channels, + bool use_default_chmap, + char *channel_map, + uint16_t bits_per_sample) +{ + struct asm_multi_channel_pcm_fmt_blk_v2 fmt; + u8 *channel_mapping; + int rc = 0; + + q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), true, ac->stream_id); + ac->cmd_state = -1; + + fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) - + sizeof(fmt.fmt_blk); + fmt.num_channels = channels; + fmt.bits_per_sample = bits_per_sample; + fmt.sample_rate = rate; + fmt.is_signed = 1; + + channel_mapping = fmt.channel_mapping; + + if (use_default_chmap) { + if (q6dsp_map_channels(channel_mapping, channels)) { + dev_err(ac->dev, " map channels failed %d\n", channels); + return -EINVAL; + } + } else { + memcpy(channel_mapping, channel_map, + PCM_FORMAT_MAX_NUM_CHANNEL); + } + + rc = apr_send_pkt(ac->adev, (uint32_t *) &fmt); + if (rc < 0) + goto fail_cmd; + + rc = wait_event_timeout(ac->cmd_wait, (ac->cmd_state >= 0), 5 * HZ); + if (!rc) { + dev_err(ac->dev, "timeout on format update\n"); + return -ETIMEDOUT; + } + if (ac->cmd_state > 0) + return adsp_err_get_lnx_err_code(ac->cmd_state); + + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL_GPL(q6asm_media_format_block_multi_ch_pcm); + +/** + * q6asm_write_nolock() - non blocking write + * + * @ac: audio client pointer + * @len: lenght in bytes + * @msw_ts: timestamp msw + * @lsw_ts: timestamp lsw + * @flags: flags associated with write + * + * Return: Will be an negative value on error or zero on success + */ +int q6asm_write_nolock(struct audio_client *ac, uint32_t len, uint32_t msw_ts, + uint32_t lsw_ts, uint32_t flags) +{ + struct asm_data_cmd_write_v2 write; + struct audio_port_data *port; + struct audio_buffer *ab; + int dsp_buf = 0; + int rc = 0; + + if (ac->io_mode & SYNC_IO_MODE) { + port = &ac->port[SNDRV_PCM_STREAM_PLAYBACK]; + q6asm_add_hdr(ac, &write.hdr, sizeof(write), false, + ac->stream_id); + + dsp_buf = port->dsp_buf; + ab = &port->buf[dsp_buf]; + + write.hdr.token = port->dsp_buf; + write.hdr.opcode = ASM_DATA_CMD_WRITE_V2; + write.buf_addr_lsw = lower_32_bits(ab->phys); + write.buf_addr_msw = upper_32_bits(ab->phys); + write.buf_size = len; + write.seq_id = port->dsp_buf; + write.timestamp_lsw = lsw_ts; + write.timestamp_msw = msw_ts; + write.mem_map_handle = + ac->port[SNDRV_PCM_STREAM_PLAYBACK].mem_map_handle; + + if (flags == NO_TIMESTAMP) + write.flags = (flags & 0x800000FF); + else + write.flags = (0x80000000 | flags); + + port->dsp_buf++; + + if (port->dsp_buf >= port->max_buf_cnt) + port->dsp_buf = 0; + + rc = apr_send_pkt(ac->adev, (uint32_t *) &write); + if (rc < 0) + return rc; + } + + return 0; +} +EXPORT_SYMBOL_GPL(q6asm_write_nolock); + +static void q6asm_reset_buf_state(struct audio_client *ac) +{ + int cnt = 0; + int loopcnt = 0; + int used; + struct audio_port_data *port = NULL; + + if (ac->io_mode & SYNC_IO_MODE) { + used = (ac->io_mode & TUN_WRITE_IO_MODE ? 1 : 0); + mutex_lock(&ac->cmd_lock); + for (loopcnt = 0; loopcnt <= SNDRV_PCM_STREAM_CAPTURE; + loopcnt++) { + port = &ac->port[loopcnt]; + cnt = port->max_buf_cnt - 1; + port->dsp_buf = 0; + while (cnt >= 0) { + if (!port->buf) + continue; + port->buf[cnt].used = used; + cnt--; + } + } + mutex_unlock(&ac->cmd_lock); + } +} + +static int __q6asm_cmd(struct audio_client *ac, int cmd, bool wait) +{ + int stream_id = ac->stream_id; + struct apr_hdr hdr; + int rc; + + q6asm_add_hdr(ac, &hdr, sizeof(hdr), true, stream_id); + ac->cmd_state = -1; + switch (cmd) { + case CMD_PAUSE: + hdr.opcode = ASM_SESSION_CMD_PAUSE; + break; + case CMD_SUSPEND: + hdr.opcode = ASM_SESSION_CMD_SUSPEND; + break; + case CMD_FLUSH: + hdr.opcode = ASM_STREAM_CMD_FLUSH; + break; + case CMD_OUT_FLUSH: + hdr.opcode = ASM_STREAM_CMD_FLUSH_READBUFS; + break; + case CMD_EOS: + hdr.opcode = ASM_DATA_CMD_EOS; + ac->cmd_state = 0; + break; + case CMD_CLOSE: + hdr.opcode = ASM_STREAM_CMD_CLOSE; + break; + default: + return -EINVAL; + } + + rc = apr_send_pkt(ac->adev, (uint32_t *) &hdr); + if (rc < 0) + return rc; + + if (!wait) + return 0; + + rc = wait_event_timeout(ac->cmd_wait, (ac->cmd_state >= 0), 5 * HZ); + if (!rc) { + dev_err(ac->dev, "timeout response for opcode[0x%x]\n", + hdr.opcode); + return -ETIMEDOUT; + } + if (ac->cmd_state > 0) + return adsp_err_get_lnx_err_code(ac->cmd_state); + + if (cmd == CMD_FLUSH) + q6asm_reset_buf_state(ac); + + return 0; +} + +/** + * q6asm_cmd() - run cmd on audio client + * + * @ac: audio client pointer + * @cmd: command to run on audio client. + * + * Return: Will be an negative value on error or zero on success + */ +int q6asm_cmd(struct audio_client *ac, int cmd) +{ + return __q6asm_cmd(ac, cmd, true); +} +EXPORT_SYMBOL_GPL(q6asm_cmd); + +/** + * q6asm_cmd_nowait() - non blocking, run cmd on audio client + * + * @ac: audio client pointer + * @cmd: command to run on audio client. + * + * Return: Will be an negative value on error or zero on success + */ +int q6asm_cmd_nowait(struct audio_client *ac, int cmd) +{ + return __q6asm_cmd(ac, cmd, false); +} +EXPORT_SYMBOL_GPL(q6asm_cmd_nowait); static int q6asm_probe(struct apr_device *adev) { diff --git a/sound/soc/qcom/qdsp6/q6asm.h b/sound/soc/qcom/qdsp6/q6asm.h index e1409c368600..b4896059da79 100644 --- a/sound/soc/qcom/qdsp6/q6asm.h +++ b/sound/soc/qcom/qdsp6/q6asm.h @@ -2,7 +2,34 @@ #ifndef __Q6_ASM_H__ #define __Q6_ASM_H__ +/* ASM client callback events */ +#define CMD_PAUSE 0x0001 +#define ASM_CLIENT_EVENT_CMD_PAUSE_DONE 0x1001 +#define CMD_FLUSH 0x0002 +#define ASM_CLIENT_EVENT_CMD_FLUSH_DONE 0x1002 +#define CMD_EOS 0x0003 +#define ASM_CLIENT_EVENT_CMD_EOS_DONE 0x1003 +#define CMD_CLOSE 0x0004 +#define ASM_CLIENT_EVENT_CMD_CLOSE_DONE 0x1004 +#define CMD_OUT_FLUSH 0x0005 +#define ASM_CLIENT_EVENT_CMD_OUT_FLUSH_DONE 0x1005 +#define CMD_SUSPEND 0x0006 +#define ASM_CLIENT_EVENT_CMD_SUSPEND_DONE 0x1006 +#define ASM_CLIENT_EVENT_CMD_RUN_DONE 0x1008 +#define ASM_CLIENT_EVENT_DATA_WRITE_DONE 0x1009 + +#define MSM_FRONTEND_DAI_MULTIMEDIA1 0 +#define MSM_FRONTEND_DAI_MULTIMEDIA2 1 +#define MSM_FRONTEND_DAI_MULTIMEDIA3 2 +#define MSM_FRONTEND_DAI_MULTIMEDIA4 3 +#define MSM_FRONTEND_DAI_MULTIMEDIA5 4 +#define MSM_FRONTEND_DAI_MULTIMEDIA6 5 +#define MSM_FRONTEND_DAI_MULTIMEDIA7 6 +#define MSM_FRONTEND_DAI_MULTIMEDIA8 7 + #define MAX_SESSIONS 16 +#define NO_TIMESTAMP 0xFF00 +#define FORMAT_LINEAR_PCM 0x0000 typedef void (*app_cb) (uint32_t opcode, uint32_t token, uint32_t *payload, void *priv); @@ -10,6 +37,21 @@ struct audio_client; struct audio_client *q6asm_audio_client_alloc(struct device *dev, app_cb cb, void *priv); void q6asm_audio_client_free(struct audio_client *ac); +int q6asm_write_nolock(struct audio_client *ac, uint32_t len, uint32_t msw_ts, + uint32_t lsw_ts, uint32_t flags); +int q6asm_open_write(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample); +int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac, + uint32_t rate, uint32_t channels, + bool use_default_chmap, + char *channel_map, + uint16_t bits_per_sample); +int q6asm_run(struct audio_client *ac, uint32_t flags, uint32_t msw_ts, + uint32_t lsw_ts); +int q6asm_run_nowait(struct audio_client *ac, uint32_t flags, uint32_t msw_ts, + uint32_t lsw_ts); +int q6asm_cmd(struct audio_client *ac, int cmd); +int q6asm_cmd_nowait(struct audio_client *ac, int cmd); int q6asm_get_session_id(struct audio_client *ac); int q6asm_map_memory_regions(unsigned int dir, struct audio_client *ac, From patchwork Thu Dec 14 17:33:56 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Srinivas Kandagatla X-Patchwork-Id: 122011 Delivered-To: patch@linaro.org Received: by 10.140.22.227 with SMTP id 90csp7109099qgn; Thu, 14 Dec 2017 09:40:18 -0800 (PST) X-Google-Smtp-Source: ACJfBos0B0lg0z3bd0JQU56erzqDBYIYtxrrxA1366iQ0ZMxLlpcV9c8f87XAA1tF0s6pQ5dOE/e X-Received: by 10.99.181.25 with SMTP id y25mr9075226pge.270.1513273218812; Thu, 14 Dec 2017 09:40:18 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1513273218; cv=none; d=google.com; s=arc-20160816; b=IBYscQS4ORb6tis0djw9Lw6m2E2uOMnd83ELVEp6uiTLAiZX3kp7MysAVlh8sgwlkR o1uXomqQjR6xsxznW92TteszN28MZDs/5HSMFJ+eytiQv9k5t0zKRM+R4pHrj9gk6uDh 3TmCT6iIbRyfebhzaJR6qLBfwMC6gL+5/Qn5WnkQjIKJ275q8r3EY5LP/B8Lm537E7We JsWEwEqnU7lXRR+x2K6RGTsn+4oAnoA//ick0ZFj8ZEvdJhkiSdVwy4aszewyu09+tO0 RGWP2qZQtXA9wbk7FdCsvOLddvVJj2n+XJOXBORR92F3h1wvxYCnPucD63YCqx5yfeN+ XUwQ== 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=cPZtQqR0yiseM2a32yfW3rRraXfHOAS2FUKLHWw3kvY=; b=d7leHnQMr54jpnxGJ1FtKJHQ7fBGSfwpNJ1z/WDbVPVZI5JN0Kr4nc+pI2uFd8HqYR dptYxPt0l2gseuSoekGwvcR9IJcWC8eb3kzjCZ0/KWxez0VzruFeRga/zuHzXkImcS4R dp0JDb3kmPgPiLfwpc7S7q3MXhaOQdZJY52q3rB0yVFo2sei3DLgqtrdszZFf2HSRtX6 9gGmnnJ31U/456t9OjF1taREGL3v/68GAJjsDxgofhrIaeRNf//oEnoodwTU0ovqQBe3 uo6soX+eJ3c9pYLHsU7fv9/sJXI2hbNllHiPUDC0NOIlZjOhljOtRqr6+5U2mBxiBxYt 4qDQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=WUvxBGhX; 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 m12si3156139pgs.784.2017.12.14.09.40.18; Thu, 14 Dec 2017 09:40:18 -0800 (PST) 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=WUvxBGhX; 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 S1753974AbdLNRkP (ORCPT + 20 others); Thu, 14 Dec 2017 12:40:15 -0500 Received: from mail-wr0-f194.google.com ([209.85.128.194]:44161 "EHLO mail-wr0-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753870AbdLNRgn (ORCPT ); Thu, 14 Dec 2017 12:36:43 -0500 Received: by mail-wr0-f194.google.com with SMTP id l22so5820444wrc.11 for ; Thu, 14 Dec 2017 09:36:42 -0800 (PST) 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=cPZtQqR0yiseM2a32yfW3rRraXfHOAS2FUKLHWw3kvY=; b=WUvxBGhX0pA5DiuI7jk24xqsZf6sU8sx7EbOSbIf85OXsii+RnYQvkGcJrqqLqarFu /YvZgKfEyxVKvncUvMPujxL0O4mT9eeNItz/rfPlj8q911az6WGx3XfTklSL8yPsguVQ /HGOv0NtT7kO0Awwy4HmguZW2yeu3vUIsHUD8= 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=cPZtQqR0yiseM2a32yfW3rRraXfHOAS2FUKLHWw3kvY=; b=L8fNsJgsI9LcIUnL2FBvIFpUlbfXjYoKZgSWW9Mi8eWOH2bS/foUfuJp7bz4mZjdDS 31vi+f1vPqiYmA18cMyArXFyodxcfsbhpAdocZTWyl29HUFJXszpNRUvm6bBFx4YT3LH OgY0k1aKi8PZpT65kswq4w3Uv3x1rqQx3+mB+7xDUYN+EI32WzgI3/2cdQqh3Edujxa4 f/HzNFJ+VQFhV+hZCEK1oDFKZ8O+RSW78rzlF+eRhHAILg5kkWI9k8uJu5BQPEw4rYI3 nSTimuzbEqsYTaUUg6+2R3jTMxK7Vb0aZqvFHIfBIOysskNZEpJKXE1ofQbvJmCEXiSl vmBg== X-Gm-Message-State: AKGB3mI51c38FUxpXEsJq+fVPj4X/f3vTUmoqzJNl76eecrEuXbd08nH PpjO6AaIl/Oh1FuMFRpC1rAShg== X-Received: by 10.223.191.13 with SMTP id p13mr7228517wrh.69.1513273001926; Thu, 14 Dec 2017 09:36:41 -0800 (PST) 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 r82sm1741757wme.31.2017.12.14.09.36.40 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 14 Dec 2017 09:36:41 -0800 (PST) From: srinivas.kandagatla@linaro.org To: Andy Gross , Mark Brown , linux-arm-msm@vger.kernel.org, alsa-devel@alsa-project.org Cc: David Brown , Rob Herring , Mark Rutland , Liam Girdwood , Patrick Lai , Banajit Goswami , Jaroslav Kysela , Takashi Iwai , linux-soc@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, sboyd@codeaurora.org, Srinivas Kandagatla Subject: [RESEND PATCH v2 09/15] ASoC: qcom: qdsp6: Add support to Q6CORE Date: Thu, 14 Dec 2017 17:33:56 +0000 Message-Id: <20171214173402.19074-10-srinivas.kandagatla@linaro.org> X-Mailer: git-send-email 2.15.0 In-Reply-To: <20171214173402.19074-1-srinivas.kandagatla@linaro.org> References: <20171214173402.19074-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 core apr service, which is used to query status of other static and dynamic services on the dsp. Signed-off-by: Srinivas Kandagatla --- sound/soc/qcom/Kconfig | 5 + sound/soc/qcom/qdsp6/Makefile | 1 + sound/soc/qcom/qdsp6/q6core.c | 227 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 233 insertions(+) create mode 100644 sound/soc/qcom/qdsp6/q6core.c -- 2.15.0 diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index 7ebdb879a8a3..121b9c957024 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -56,11 +56,16 @@ config SND_SOC_QDSP6_ASM tristate default n +config SND_SOC_QDSP6_CORE + tristate + default n + config SND_SOC_QDSP6 tristate "SoC ALSA audio driver for QDSP6" select SND_SOC_QDSP6_AFE select SND_SOC_QDSP6_ADM select SND_SOC_QDSP6_ASM + select SND_SOC_QDSP6_CORE 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 49dd3ccab27b..ad7f10691e54 100644 --- a/sound/soc/qcom/qdsp6/Makefile +++ b/sound/soc/qcom/qdsp6/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_SND_SOC_QDSP6_AFE) += q6afe.o obj-$(CONFIG_SND_SOC_QDSP6_ADM) += q6adm.o obj-$(CONFIG_SND_SOC_QDSP6_ASM) += q6asm.o +obj-$(CONFIG_SND_SOC_QDSP6_CORE) += q6core.o diff --git a/sound/soc/qcom/qdsp6/q6core.c b/sound/soc/qcom/qdsp6/q6core.c new file mode 100644 index 000000000000..d4e2dbc62489 --- /dev/null +++ b/sound/soc/qcom/qdsp6/q6core.c @@ -0,0 +1,227 @@ +/* SPDX-License-Identifier: GPL-2.0 +* Copyright (c) 2017, Linaro Limited +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "common.h" + +#define ADSP_STATE_READY_TIMEOUT_MS 3000 +#define Q6_READY_TIMEOUT_MS 100 +#define AVCS_CMD_ADSP_EVENT_GET_STATE 0x0001290C +#define AVCS_CMDRSP_ADSP_EVENT_GET_STATE 0x0001290D +#define AVCS_GET_VERSIONS 0x00012905 +#define AVCS_GET_VERSIONS_RSP 0x00012906 + +struct avcs_svc_info { + uint32_t service_id; + uint32_t version; +} __packed; + +struct q6core { + struct apr_device *adev; + wait_queue_head_t wait; + uint32_t avcs_state; + int resp_received; + uint32_t num_services; + struct avcs_svc_info *svcs_info; +}; + +static struct apr_device_id static_services[] = { + ADSP_AUDIO_APR_DEV("AFE", APR_SVC_AFE), + ADSP_AUDIO_APR_DEV("ASM", APR_SVC_ASM), + ADSP_AUDIO_APR_DEV("ADM", APR_SVC_ADM), + ADSP_AUDIO_APR_DEV("TEST", APR_SVC_TEST_CLIENT), + ADSP_AUDIO_APR_DEV("MVM", APR_SVC_ADSP_MVM), + ADSP_AUDIO_APR_DEV("CVS", APR_SVC_ADSP_CVS), + ADSP_AUDIO_APR_DEV("CVP", APR_SVC_ADSP_CVP), + ADSP_AUDIO_APR_DEV("USM", APR_SVC_USM), + ADSP_AUDIO_APR_DEV("VIDC", APR_SVC_VIDC), + ADSP_AUDIO_APR_DEV("LSM", APR_SVC_LSM), +}; + +static int core_callback(struct apr_device *adev, struct apr_client_data *data) +{ + struct q6core *core = dev_get_drvdata(&adev->dev); + uint32_t *payload; + + switch (data->opcode) { + case AVCS_GET_VERSIONS_RSP: + payload = data->payload; + core->num_services = payload[1]; + + if (!core->svcs_info) + core->svcs_info = kcalloc(core->num_services, + sizeof(*core->svcs_info), + GFP_ATOMIC); + if (!core->svcs_info) + return -ENOMEM; + + /* svc info is after 8 bytes */ + memcpy(core->svcs_info, payload + 2, + core->num_services * sizeof(*core->svcs_info)); + + core->resp_received = 1; + wake_up(&core->wait); + + break; + case AVCS_CMDRSP_ADSP_EVENT_GET_STATE: + payload = data->payload; + core->avcs_state = payload[0]; + + core->resp_received = 1; + wake_up(&core->wait); + break; + default: + dev_err(&adev->dev, "Message id from adsp core svc: 0x%x\n", + data->opcode); + break; + } + + return 0; +} + +void q6core_add_service(struct device *dev, uint32_t svc_id, uint32_t version) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(static_services); i++) { + if (static_services[i].svc_id == svc_id) { + static_services[i].svc_version = version; + apr_add_device(dev->parent, &static_services[i]); + dev_info(dev, + "Adding SVC: %s: id 0x%x API Ver 0x%x:0x%x\n", + static_services[i].name, svc_id, + APR_SVC_MAJOR_VERSION(version), + APR_SVC_MINOR_VERSION(version)); + } + } +} + +static void q6core_add_static_services(struct q6core *core) +{ + int i; + struct apr_device *adev = core->adev; + struct avcs_svc_info *svcs_info = core->svcs_info; + + for (i = 0; i < core->num_services; i++) + q6core_add_service(&adev->dev, svcs_info[i].service_id, + svcs_info[i].version); +} + +static int q6core_get_svc_versions(struct q6core *core) +{ + struct apr_device *adev = core->adev; + struct apr_hdr hdr = {0}; + int rc; + + hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, 0); + hdr.opcode = AVCS_GET_VERSIONS; + + rc = apr_send_pkt(adev, (uint32_t *)&hdr); + if (rc < 0) + return rc; + + rc = wait_event_timeout(core->wait, (core->resp_received == 1), + msecs_to_jiffies(Q6_READY_TIMEOUT_MS)); + if (rc > 0 && core->resp_received) { + core->resp_received = 0; + return 0; + } + + return rc; +} + +static bool q6core_is_adsp_ready(struct q6core *core) +{ + struct apr_device *adev = core->adev; + struct apr_hdr hdr = {0}; + int rc; + + hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, 0); + hdr.opcode = AVCS_CMD_ADSP_EVENT_GET_STATE; + + rc = apr_send_pkt(adev, (uint32_t *)&hdr); + if (rc < 0) + return false; + + rc = wait_event_timeout(core->wait, (core->resp_received == 1), + msecs_to_jiffies(Q6_READY_TIMEOUT_MS)); + if (rc > 0 && core->resp_received) { + core->resp_received = 0; + if (core->avcs_state == 0x1) + return true; + } + + return false; +} + +static int q6core_probe(struct apr_device *adev) +{ + struct q6core *core; + unsigned long timeout = jiffies + + msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS); + int ret = 0; + + core = devm_kzalloc(&adev->dev, sizeof(*core), GFP_KERNEL); + if (!core) + return -ENOMEM; + + dev_set_drvdata(&adev->dev, core); + + core->adev = adev; + init_waitqueue_head(&core->wait); + + do { + if (!q6core_is_adsp_ready(core)) { + dev_info(&adev->dev, "ADSP Audio isn't ready\n"); + } else { + dev_info(&adev->dev, "ADSP Audio is ready\n"); + + ret = q6core_get_svc_versions(core); + if (!ret) + q6core_add_static_services(core); + + break; + } + } while (time_after(timeout, jiffies)); + + return ret; +} + +static int q6core_exit(struct apr_device *adev) +{ + return 0; +} + +static const struct apr_device_id core_id[] = { + {"Q6CORE", APR_DOMAIN_ADSP, APR_SVC_ADSP_CORE, APR_CLIENT_AUDIO}, + { }, +}; + +static struct apr_driver qcom_q6core_driver = { + .probe = q6core_probe, + .remove = q6core_exit, + .callback = core_callback, + .id_table = core_id, + .driver = { + .name = "qcom-q6core", + }, +}; + +module_apr_driver(qcom_q6core_driver); + +MODULE_AUTHOR("Srinivas Kandagatla X-Patchwork-Id: 122002 Delivered-To: patch@linaro.org Received: by 10.140.22.227 with SMTP id 90csp7106037qgn; Thu, 14 Dec 2017 09:37:13 -0800 (PST) X-Google-Smtp-Source: ACJfBos3a4LNSIKB+7qZEf+Jtwi3G7I/B9SlkCCSkMuHbUE6NKbR9MRZwWIhjK8LAsnVIeuVclXe X-Received: by 10.84.140.5 with SMTP id 5mr5928082pls.149.1513273033623; Thu, 14 Dec 2017 09:37:13 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1513273033; cv=none; d=google.com; s=arc-20160816; b=IaRTeMzgxV5zoL4ARmEhv3VCBpljX7fwd2F/Khg1mAfUW01jLvgpNrd7zDH6kJK58y 8jy1gZdv5+X1pHogpSrfa1D7Iea9AfhRnt5nv0ifk7iSqn5mdrRO+G5WxSBxAkrhqSUN up2Jqf8gGMuHtNt/ToW06YKT2EgLhGFDaOooATCnYUNbw71Wv0Sb/GtuTy7FUIWpyOU5 gjKQn1rhAgtLIgpK/YGSaGCOQlLyYb2UTth7LRYy4Apx7D4cvGRgtRZ4fzFQVFV3wCNP 3Ydp+UG9Ki3Ckj6LDdUw5NCE1uVsIDUMtBOcqs39mx2Fs+j4N1cEJgt5PLPAu8eyuxhY mL9A== 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=YF29kSvkkTFI/EITT+oLSPbAKuiEdfsC9uRjYX9htTI=; b=sLTJlVqkPoe7Zem6SH0kTW2Jp+9uHF9QgcyJcLD5t3tagkI8rq4wp7wCSHmoCuPtK5 qMv71JAQWn6zNolUzZ6UiCehmphjmE9TcVwPPf2bTJCmz+dpcLx2yi3Kf2OWLGSsZEwk fQKtqmaDlFFUUQt0Y6odlUvIztAZne2hPlZ8u0glCBmJCkEb4rbsgHmObSOEwQeyBkGp GjaG3XAHUq1a7/Khve4IZV+ePPibaupyzM4pcdE1T7X5mBouhOndoUIf8tCDVNBdXzZl gQMLxtXbEGNp0rvNW6HFUT4B0kmXIRkJK8lBIDZyf3LMRTC3yXOBbWgTpNFIzFZT6VOp VPSg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=MUgYWcRk; 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 r59si3420215plb.830.2017.12.14.09.37.13; Thu, 14 Dec 2017 09:37:13 -0800 (PST) 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=MUgYWcRk; 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 S1753917AbdLNRgw (ORCPT + 20 others); Thu, 14 Dec 2017 12:36:52 -0500 Received: from mail-wr0-f196.google.com ([209.85.128.196]:34068 "EHLO mail-wr0-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753880AbdLNRgp (ORCPT ); Thu, 14 Dec 2017 12:36:45 -0500 Received: by mail-wr0-f196.google.com with SMTP id y21so5846324wrc.1 for ; Thu, 14 Dec 2017 09:36:44 -0800 (PST) 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=YF29kSvkkTFI/EITT+oLSPbAKuiEdfsC9uRjYX9htTI=; b=MUgYWcRkB55P1kNBeW3uQgP1KFHmJKFyjZBxhA7HHV03mkkgLThgbgFP7MzuZYbS2V 2RGw5vVqzht2mbeVgm7DpcPX93vurvh8zhYfVmwg19CLanzTN1WoudNBXP2CmfomNKOz zjcTtDMbTwZcH6uCSTD5bAl+RETNg9+hx6nUg= 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=YF29kSvkkTFI/EITT+oLSPbAKuiEdfsC9uRjYX9htTI=; b=kT7usC11FTavYzLoH0ccdXqHKU2XL5P72uIMpyEEHuJaRaT7qMG7mW4FMXbzRU3+ON sV0e3rp4DiI09PQwiRqzSaPvBtxixj+GgPqtULcoVcZvNjX7EPaVEFH6KHazT3HWdtlL cpCcYZ/wVsnAZr4Gi06/8oWo1IZldnSKim9zbiWUcJ8ZTSkNGKRxrGdv/TFJLEyFQLcf oNG+Vh1v6GoUfIxJpc62LxjTl9u5S8PvStZ6Ht+EMsvLYUEOWUW71xh8wR2rlyRL1aDr 6Nr1u6FabI6rXbyCGVB6S5+yJ52Qp6Q2VgHmFX8cBdt6EXiHoO5MDyqzAG06wD3/wFWp M3tQ== X-Gm-Message-State: AKGB3mKWKAjFJ+7B/+PsSmIxbKneZy4OJGygX5CRiRRLMSZMmNxk/1RF 1BGkiTthnvmvQ99GChvNATLNWA== X-Received: by 10.223.155.131 with SMTP id d3mr7296971wrc.29.1513273003753; Thu, 14 Dec 2017 09:36:43 -0800 (PST) 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 r82sm1741757wme.31.2017.12.14.09.36.41 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 14 Dec 2017 09:36:43 -0800 (PST) From: srinivas.kandagatla@linaro.org To: Andy Gross , Mark Brown , linux-arm-msm@vger.kernel.org, alsa-devel@alsa-project.org Cc: David Brown , Rob Herring , Mark Rutland , Liam Girdwood , Patrick Lai , Banajit Goswami , Jaroslav Kysela , Takashi Iwai , linux-soc@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, sboyd@codeaurora.org, Srinivas Kandagatla Subject: [RESEND PATCH v2 10/15] ASoC: qcom: qdsp6: Add support to q6routing driver Date: Thu, 14 Dec 2017 17:33:57 +0000 Message-Id: <20171214173402.19074-11-srinivas.kandagatla@linaro.org> X-Mailer: git-send-email 2.15.0 In-Reply-To: <20171214173402.19074-1-srinivas.kandagatla@linaro.org> References: <20171214173402.19074-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 --- sound/soc/qcom/Kconfig | 5 + sound/soc/qcom/qdsp6/Makefile | 1 + sound/soc/qcom/qdsp6/q6routing.c | 386 +++++++++++++++++++++++++++++++++++++++ sound/soc/qcom/qdsp6/q6routing.h | 9 + 4 files changed, 401 insertions(+) create mode 100644 sound/soc/qcom/qdsp6/q6routing.c create mode 100644 sound/soc/qcom/qdsp6/q6routing.h -- 2.15.0 diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index 121b9c957024..dd8fb0cde614 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -60,12 +60,17 @@ config SND_SOC_QDSP6_CORE tristate default n +config SND_SOC_QDSP6_ROUTING + tristate + default n + config SND_SOC_QDSP6 tristate "SoC ALSA audio driver for QDSP6" select SND_SOC_QDSP6_AFE select SND_SOC_QDSP6_ADM select SND_SOC_QDSP6_ASM select SND_SOC_QDSP6_CORE + select SND_SOC_QDSP6_ROUTING 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 ad7f10691e54..c1ad060a2341 100644 --- a/sound/soc/qcom/qdsp6/Makefile +++ b/sound/soc/qcom/qdsp6/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_SND_SOC_QDSP6_AFE) += q6afe.o obj-$(CONFIG_SND_SOC_QDSP6_ADM) += q6adm.o obj-$(CONFIG_SND_SOC_QDSP6_ASM) += q6asm.o obj-$(CONFIG_SND_SOC_QDSP6_CORE) += q6core.o +obj-$(CONFIG_SND_SOC_QDSP6_ROUTING) += q6routing.o diff --git a/sound/soc/qcom/qdsp6/q6routing.c b/sound/soc/qcom/qdsp6/q6routing.c new file mode 100644 index 000000000000..f5f12d61a1ee --- /dev/null +++ b/sound/soc/qcom/qdsp6/q6routing.c @@ -0,0 +1,386 @@ +/* SPDX-License-Identifier: GPL-2.0 +* Copyright (c) 2011-2016, The Linux Foundation +* Copyright (c) 2017, Linaro Limited +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "q6afe.h" +#include "q6asm.h" +#include "q6adm.h" +#include "q6routing.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 device *dev; + struct mutex lock; +}; + +static struct msm_routing_data *routing_data; + +/** + * q6routing_reg_phy_stream() - Register a new stream for route setup + * + * @fedai_id: Frontend dai id. + * @perf_mode: Performace mode. + * @stream_id: ASM stream id to map. + * @stream_type: Direction of stream + * + * Return: Will be an negative on error or a zero on success. + */ +int q6routing_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->dev, 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)) { + 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; + q6adm_matrix_map(routing_data->dev, session->path_type, + payload, perf_mode); + } + mutex_unlock(&routing_data->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(q6routing_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; +} +/** + * q6routing_dereg_phy_stream() - Deregister a stream + * + * @fedai_id: Frontend dai id. + * @stream_type: Direction of stream + * + * Return: Will be an negative on error or a zero on success. + */ +void q6routing_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(routing_data->dev, session->port_id, + session->perf_mode, idx); + + session->fedai_id = -1; + session->copp_map = 0; +} +EXPORT_SYMBOL_GPL(q6routing_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; + + 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) +{ + + routing_data = devm_kzalloc(&pdev->dev, + sizeof(*routing_data), GFP_KERNEL); + if (!routing_data) + return -ENOMEM; + + routing_data->dev = &pdev->dev; + + mutex_init(&routing_data->lock); + dev_set_drvdata(&pdev->dev, routing_data); + + return devm_snd_soc_register_platform(&pdev->dev, + &msm_soc_routing_platform); +} + +static int q6pcm_routing_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver q6pcm_routing_driver = { + .driver = { + .name = "q6routing", + .owner = THIS_MODULE, + }, + .probe = q6pcm_routing_probe, + .remove = q6pcm_routing_remove, +}; + +module_platform_driver(q6pcm_routing_driver); + +MODULE_DESCRIPTION("Q6 Routing platform"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/qcom/qdsp6/q6routing.h b/sound/soc/qcom/qdsp6/q6routing.h new file mode 100644 index 000000000000..7f0feb196acc --- /dev/null +++ b/sound/soc/qcom/qdsp6/q6routing.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _Q6_PCM_ROUTING_H +#define _Q6_PCM_ROUTING_H + +int q6routing_reg_phy_stream(int fedai_id, int perf_mode, + int stream_id, int stream_type); +void q6routing_dereg_phy_stream(int fedai_id, int stream_type); + +#endif /*_Q6_PCM_ROUTING_H */ From patchwork Thu Dec 14 17:33:58 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Srinivas Kandagatla X-Patchwork-Id: 122009 Delivered-To: patch@linaro.org Received: by 10.140.22.227 with SMTP id 90csp7108383qgn; Thu, 14 Dec 2017 09:39:35 -0800 (PST) X-Google-Smtp-Source: ACJfBosF57iL4vlwxdibIkbwviZTRwV2kVbySCZe4JaRUnSQWke3lHjTtfcU5B8nmm0Yp074v5pB X-Received: by 10.84.160.204 with SMTP id v12mr5657898plg.80.1513273175349; Thu, 14 Dec 2017 09:39:35 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1513273175; cv=none; d=google.com; s=arc-20160816; b=RvYc6rDYwd/DBbnF9gTXuBkoCmOiG5GfmhBofX9pebj1aSJ4yNq00RlaNpldOSFS/z 7Lv88aTpaTExGcqUJkYXm5v6YbrG5ZSC9TPijc/gx3twwF2DNc0oP5CdKNWWd9koUYUs mVsXbLAzoU1Qwn2EBxqDVJswk6/JnJTVti+y1jQAWapePe+tfSr2vj5M6zwl1G1mNrw2 xOgTSloHSGbSBa2h/FPMUirj3DgrR1K3sueBMwyj4t9ZuciJFN+f7Y5gbxraYm9K0Ce3 sTMSuWxTpNDKSiOsrm9roGzGhJIt1o5vGvhQ7O7ED+XvVzW/L4r5CcIVAdNZzrIm8BCM OvJg== 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=/6OvCdALqvPvLxUhpXcW0GuutrQiSpZnYZoud1azjzg=; b=zboMbhcV9olP4cjzV+qNAH+4u2UrRXwNOQV0zbV7VgQqDFoQS8TR3nQOn9nrjuctiG XBjuQIpntgaSV1ThAGw27jOvnJCKVS2FbnU6+V5FNe/qgAuVZ9xCOa1RghIvsM5wcs84 0j5Rpo4Vt0JJlDlHVix2/fJ9is/GRnZZlFQSSbvqWcxgwCSnLRi3EyHWLao3ysqCgx3J EIPhgVXPTUerPoBmAFgDEmxtE3ZnTaVlgox5ppQZLKG7V1c48Ncd3Y6hUpswJ8ZqkUSc kh1YHmLmsnB18E3u9QskLR4pC0RvI35WJfZN7rx6xwi3b9ZNWuBFQC+rjqW9F/1AbYPk btXw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=D9CAgDSF; 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 x1si3402391pln.620.2017.12.14.09.39.35; Thu, 14 Dec 2017 09:39:35 -0800 (PST) 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=D9CAgDSF; 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 S1754065AbdLNRjb (ORCPT + 20 others); Thu, 14 Dec 2017 12:39:31 -0500 Received: from mail-wr0-f193.google.com ([209.85.128.193]:38662 "EHLO mail-wr0-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753888AbdLNRgq (ORCPT ); Thu, 14 Dec 2017 12:36:46 -0500 Received: by mail-wr0-f193.google.com with SMTP id o2so5826758wro.5 for ; Thu, 14 Dec 2017 09:36:46 -0800 (PST) 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=/6OvCdALqvPvLxUhpXcW0GuutrQiSpZnYZoud1azjzg=; b=D9CAgDSFw7PbOPOJPTeTWmOLoEWYLDoNCHqoIQpDFLkF7mwLBlYdTjnrixFjybkEG6 7v4UK/1W5AA14B6Ego1dvfJNPDbN79pOZdG+UB+V7MoUrkr+CWgKN5ZqNN62gwuMQiHT dhxeYTbZkE+h+fVbPaVjWAyVafl3ZsTn6mnxc= 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=/6OvCdALqvPvLxUhpXcW0GuutrQiSpZnYZoud1azjzg=; b=LOT7TExKwG/GSlxPhBnh1Ch0/WXo4yQCaAaReXXaQBlGxHt/FDy6vuMmZr5lxVkPkC Op66itvm3NMlvJ1QCuhg3q9NR+Ck3uvgnkNAMfhR0DlDwE3ZoHUm7MyAz3anlr4rq6NV JZ/FuENYSH9CFrOyo4tkVgRbPgO+tSEvS88tVNTTynoLd61WmkHf7LtGDPrIMp10ivAD Jcg6hQTgMfwuR3ENWPsGfIBjMH43m1b8jWWEOHZ+AVULeTz7jBAUt4UADNcdXvLCURSm VuXtk7eG69f7XZDO+3Y9Yvxcjcn5yvyKfs0zgU0gpnL+1Iq8EtPMjh10qBvVyudqDhsi k20A== X-Gm-Message-State: AKGB3mIFLlzEsEzP3cGFY15nm3ZXLKaXbbfASOVqxcyai5DU0+s1vNWH mzRU35F8dZl6RFdaUDYgIYoAuQ== X-Received: by 10.223.173.67 with SMTP id p61mr6136111wrc.226.1513273005522; Thu, 14 Dec 2017 09:36:45 -0800 (PST) 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 r82sm1741757wme.31.2017.12.14.09.36.43 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 14 Dec 2017 09:36:45 -0800 (PST) From: srinivas.kandagatla@linaro.org To: Andy Gross , Mark Brown , linux-arm-msm@vger.kernel.org, alsa-devel@alsa-project.org Cc: David Brown , Rob Herring , Mark Rutland , Liam Girdwood , Patrick Lai , Banajit Goswami , Jaroslav Kysela , Takashi Iwai , linux-soc@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, sboyd@codeaurora.org, Srinivas Kandagatla Subject: [RESEND PATCH v2 11/15] ASoC: qcom: qdsp6: Add support to q6afe dai driver Date: Thu, 14 Dec 2017 17:33:58 +0000 Message-Id: <20171214173402.19074-12-srinivas.kandagatla@linaro.org> X-Mailer: git-send-email 2.15.0 In-Reply-To: <20171214173402.19074-1-srinivas.kandagatla@linaro.org> References: <20171214173402.19074-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 q6afe backend dais driver. Signed-off-by: Srinivas Kandagatla --- sound/soc/qcom/Kconfig | 5 + sound/soc/qcom/qdsp6/Makefile | 1 + sound/soc/qcom/qdsp6/q6afe-dai.c | 241 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 247 insertions(+) create mode 100644 sound/soc/qcom/qdsp6/q6afe-dai.c -- 2.15.0 diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index dd8fb0cde614..003ce182691c 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -64,6 +64,10 @@ config SND_SOC_QDSP6_ROUTING tristate default n +config SND_SOC_QDSP6_AFE_DAI + tristate + default n + config SND_SOC_QDSP6 tristate "SoC ALSA audio driver for QDSP6" select SND_SOC_QDSP6_AFE @@ -71,6 +75,7 @@ config SND_SOC_QDSP6 select SND_SOC_QDSP6_ASM select SND_SOC_QDSP6_CORE select SND_SOC_QDSP6_ROUTING + select SND_SOC_QDSP6_AFE_DAI 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 c1ad060a2341..bd8bd02bf09e 100644 --- a/sound/soc/qcom/qdsp6/Makefile +++ b/sound/soc/qcom/qdsp6/Makefile @@ -3,3 +3,4 @@ obj-$(CONFIG_SND_SOC_QDSP6_ADM) += q6adm.o obj-$(CONFIG_SND_SOC_QDSP6_ASM) += q6asm.o obj-$(CONFIG_SND_SOC_QDSP6_CORE) += q6core.o obj-$(CONFIG_SND_SOC_QDSP6_ROUTING) += q6routing.o +obj-$(CONFIG_SND_SOC_QDSP6_AFE_DAI) += q6afe-dai.o diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c new file mode 100644 index 000000000000..e9865c684bcb --- /dev/null +++ b/sound/soc/qcom/qdsp6/q6afe-dai.c @@ -0,0 +1,241 @@ +/* SPDX-License-Identifier: GPL-2.0 +* Copyright (c) 2011-2016, The Linux Foundation +* Copyright (c) 2017, Linaro Limited +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "q6afe.h" + +struct q6hdmi_dai_data { + struct q6afe_port *port; + struct q6afe_hdmi_cfg port_config; + bool is_port_started; +}; + +static int q6hdmi_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct q6hdmi_dai_data *dai_data = kcontrol->private_data; + int value = ucontrol->value.integer.value[0]; + + dai_data->port_config.datatype = value; + + return 0; +} + +static int q6hdmi_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct q6hdmi_dai_data *dai_data = kcontrol->private_data; + + ucontrol->value.integer.value[0] = + dai_data->port_config.datatype; + + return 0; +} + +static const char * const hdmi_format[] = { + "LPCM", + "Compr" +}; + +static const struct soc_enum hdmi_config_enum[] = { + SOC_ENUM_SINGLE_EXT(2, hdmi_format), +}; + +static const struct snd_kcontrol_new hdmi_config_controls[] = { + SOC_ENUM_EXT("HDMI RX Format", hdmi_config_enum[0], + q6hdmi_format_get, + q6hdmi_format_put), +}; + +static int q6hdmi_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct q6hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev); + int channels = params_channels(params); + + dai_data->port_config.sample_rate = params_rate(params); + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + dai_data->port_config.bit_width = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + dai_data->port_config.bit_width = 24; + break; + } + + /*refer to HDMI spec CEA-861-E: Table 28 Audio InfoFrame Data Byte 4*/ + switch (channels) { + case 2: + dai_data->port_config.channel_allocation = 0; + break; + case 3: + dai_data->port_config.channel_allocation = 0x02; + break; + case 4: + dai_data->port_config.channel_allocation = 0x06; + break; + case 5: + dai_data->port_config.channel_allocation = 0x0A; + break; + case 6: + dai_data->port_config.channel_allocation = 0x0B; + break; + case 7: + dai_data->port_config.channel_allocation = 0x12; + break; + case 8: + dai_data->port_config.channel_allocation = 0x13; + break; + default: + dev_err(dai->dev, "invalid Channels = %u\n", channels); + return -EINVAL; + } + + return 0; +} + +static int q6hdmi_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct q6hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev); + + dai_data->is_port_started = false; + + return 0; +} + +static void q6hdmi_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct q6hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev); + int rc; + + rc = q6afe_port_stop(dai_data->port); + if (rc < 0) + dev_err(dai->dev, "fail to close AFE port\n"); + + dai_data->is_port_started = false; + +} + +static int q6hdmi_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct q6hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev); + int rc; + + if (dai_data->is_port_started) { + /* stop the port and restart with new port config */ + rc = q6afe_port_stop(dai_data->port); + if (rc < 0) { + dev_err(dai->dev, "fail to close AFE port\n"); + return rc; + } + } + + q6afe_hdmi_port_prepare(dai_data->port, &dai_data->port_config); + rc = q6afe_port_start(dai_data->port); + if (rc < 0) { + dev_err(dai->dev, "fail to start AFE port %x\n", dai->id); + return rc; + } + dai_data->is_port_started = true; + + return 0; +} + +static const struct snd_soc_dapm_route hdmi_dapm_routes[] = { + {"HDMI Playback", NULL, "HDMI"}, +}; + +static struct snd_soc_dai_ops q6hdmi_ops = { + .prepare = q6hdmi_prepare, + .hw_params = q6hdmi_hw_params, + .shutdown = q6hdmi_shutdown, + .startup = q6hdmi_startup, +}; + +static struct snd_soc_dai_driver q6afe_dai_hdmi_rx = { + .playback = { + .stream_name = "HDMI Playback", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 2, + .channels_max = 8, + .rate_max = 192000, + .rate_min = 48000, + }, + .ops = &q6hdmi_ops, + .id = AFE_PORT_HDMI_RX, + .name = "HDMI", +}; + +static const struct snd_soc_dapm_widget hdmi_dapm_widgets[] = { + SND_SOC_DAPM_AIF_OUT("HDMI", "HDMI Playback", 0, 0, 0, 0), + SND_SOC_DAPM_OUTPUT("HDMI-RX"), +}; + +static const struct snd_soc_component_driver msm_dai_hdmi_q6_component = { + .name = "msm-dai-q6-hdmi", + .dapm_widgets = hdmi_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(hdmi_dapm_widgets), + .controls = hdmi_config_controls, + .num_controls = ARRAY_SIZE(hdmi_config_controls), + .dapm_routes = hdmi_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(hdmi_dapm_routes), +}; + +static int q6afe_dai_dev_probe(struct platform_device *pdev) +{ + struct q6hdmi_dai_data *dai_data; + int rc = 0; + struct q6afe_port *port; + + dai_data = devm_kzalloc(&pdev->dev, sizeof(*dai_data), GFP_KERNEL); + if (!dai_data) + rc = -ENOMEM; + + port = q6afe_port_get_from_id(&pdev->dev, AFE_PORT_HDMI_RX); + if (IS_ERR(port)) { + dev_err(&pdev->dev, "Unable to get afe port\n"); + return -EPROBE_DEFER; + } + dai_data->port = port; + dev_set_drvdata(&pdev->dev, dai_data); + + return devm_snd_soc_register_component(&pdev->dev, + &msm_dai_hdmi_q6_component, + &q6afe_dai_hdmi_rx, 1); +} + +static int q6afe_dai_dev_remove(struct platform_device *pdev) +{ + struct q6hdmi_dai_data *dai_data = dev_get_drvdata(&pdev->dev); + + q6afe_port_put(dai_data->port); + + return 0; +} + +static struct platform_driver q6afe_dai_driver = { + .probe = q6afe_dai_dev_probe, + .remove = q6afe_dai_dev_remove, + .driver = { + .name = "q6afe_dai", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(q6afe_dai_driver); From patchwork Thu Dec 14 17:33:59 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Srinivas Kandagatla X-Patchwork-Id: 122010 Delivered-To: patch@linaro.org Received: by 10.140.22.227 with SMTP id 90csp7108742qgn; Thu, 14 Dec 2017 09:39:57 -0800 (PST) X-Google-Smtp-Source: ACJfBoujCR5D/wS68gdK9FUfRS10I2BTtRxSYlaSbO//VkPVnCBBDOyvaCjJ/vqHLNEzX0FpOUk7 X-Received: by 10.99.114.30 with SMTP id n30mr9464590pgc.271.1513273197626; Thu, 14 Dec 2017 09:39:57 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1513273197; cv=none; d=google.com; s=arc-20160816; b=uBhvFqyOFL3C1nvog47NsUIvermxdmbNWNOpHMXONllo5AxNtyfwVMy35KE/nf3oj5 WLgVwglwyRoRXRbt6GL0pkAWhqrIZxInCHMX11jXrcCgamgp0cABpwjPXoxX/yV6hgTq 9ZhytwWYyog+F0cARUPC15gcNP2YJVc5XlTvSn2F+h9f9LcA4bc/OSGBAOWnhSBSBjyr VB5RagkoLAJUnqJqsOWdTOths0s5vY9Qsgn2blFboRFGYgCL9DWH3Wlkf05SOJqkXEPl VTYsPOk/eZnviqEURYUznHCVSlYJ9euY/gqMIgzE47HtAqdPoUkO2lKR1/BIznFq98MQ BbsQ== 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=a4Nye70Z/4V59BxT5yqwQWB6aMjM38xOlv74JwyTt7A=; b=dfPRmn6cfefAZb0RQt/DXK/IIEzY+fPTq0rtD9ipy3sIw88oAuqJxx/zMpYjf1Rr9g tKJf6dy9yxW+bwnKf7f3rlDONNSCUPTxbGWlKZkWa8jb22lHySLDx38JJHxR9i2W6jbv +OoMWXkuN0zWmCGaImeg39ozVCXsd4DB38orzB0AEtAC2Z/I0i/jryJM0y6f6YPt1ahV elGNor9kln+U3Urc0z8/IWxk41Lv85p22P5BHUXLEer5TCXA4Xtl9FuFAeLiyTeR43xC Uuc6gyzly1HSppUOTVOIc9zpepDNmx2LlFh0OoWNtoD6ftn96+EAQJOhbQ1ihDlruwIk Xevw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=Lus2NbHM; 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 73si1643212ple.357.2017.12.14.09.39.57; Thu, 14 Dec 2017 09:39:57 -0800 (PST) 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=Lus2NbHM; 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 S1754051AbdLNRja (ORCPT + 20 others); Thu, 14 Dec 2017 12:39:30 -0500 Received: from mail-wm0-f67.google.com ([74.125.82.67]:44139 "EHLO mail-wm0-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753890AbdLNRgs (ORCPT ); Thu, 14 Dec 2017 12:36:48 -0500 Received: by mail-wm0-f67.google.com with SMTP id t8so12836836wmc.3 for ; Thu, 14 Dec 2017 09:36:48 -0800 (PST) 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=a4Nye70Z/4V59BxT5yqwQWB6aMjM38xOlv74JwyTt7A=; b=Lus2NbHMvodKRyEckN+CLhsu89c08enWKun3uwBUTf6knzuZR+ygyZZcCf9Xpg0+Zm afuu+6oe0Nl1bdoE9sosHvc5qJ43T6vEEZXP43uAM1W1n+y6f1gWiE2uLv+cgZfoxIB7 OggjxjuOBz6r5EHwj95iCLr7+bnCNv1/OQXGc= 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=a4Nye70Z/4V59BxT5yqwQWB6aMjM38xOlv74JwyTt7A=; b=QfL5MEG0aG0/91UgSksP6OBXN8J0o4v9fj3OlDBL9hF0/SB+cL/oyAIyWITnZT87bH LgLpwm8/zWzpXSgF59uJf+TM6ygRfWVqAE2/seIcCNTQkum1QyExC4IbdlAT/KdEidCc Pmrb392qK0kUEx8v1iFHmCgrh25g3Z6OH+alk8SH95ktU0WWX1Zvv3aZwNZ02bRRUkuj Yzp2wM5jchsaztu3p6UE1YRaVm1RZVViHWGW3Sft8o4FYaOE+YmV2e003L0CTql1lj2o Kz3R7tk6im/bxUBu37ylkQzG//l4QA5DCIPfpYzt6KiDpSDUNla4xOnpmDhPuDKx2Xf3 Za0w== X-Gm-Message-State: AKGB3mJmqtLO6hpTGRo3lAO+6Dp+Hlfx5jalEV2cZOn+9RsjNwebTIZJ nm5P/t/MFVVIUDIafkARvvr4rQ== X-Received: by 10.28.6.148 with SMTP id 142mr2722799wmg.26.1513273007316; Thu, 14 Dec 2017 09:36:47 -0800 (PST) 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 r82sm1741757wme.31.2017.12.14.09.36.45 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 14 Dec 2017 09:36:46 -0800 (PST) From: srinivas.kandagatla@linaro.org To: Andy Gross , Mark Brown , linux-arm-msm@vger.kernel.org, alsa-devel@alsa-project.org Cc: David Brown , Rob Herring , Mark Rutland , Liam Girdwood , Patrick Lai , Banajit Goswami , Jaroslav Kysela , Takashi Iwai , linux-soc@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, sboyd@codeaurora.org, Srinivas Kandagatla Subject: [RESEND PATCH v2 12/15] ASoC: qcom: qdsp6: Add support to q6asm dai driver Date: Thu, 14 Dec 2017 17:33:59 +0000 Message-Id: <20171214173402.19074-13-srinivas.kandagatla@linaro.org> X-Mailer: git-send-email 2.15.0 In-Reply-To: <20171214173402.19074-1-srinivas.kandagatla@linaro.org> References: <20171214173402.19074-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 q6asm dai driver which configures Q6ASM streams to pass pcm data. Currently the driver only exposes 2 playback streams for hdmi playback support, it can be easily extended to add all 8 streams. Signed-off-by: Srinivas Kandagatla --- sound/soc/qcom/Kconfig | 6 + sound/soc/qcom/qdsp6/Makefile | 1 + sound/soc/qcom/qdsp6/q6asm-dai.c | 534 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 541 insertions(+) create mode 100644 sound/soc/qcom/qdsp6/q6asm-dai.c -- 2.15.0 diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index 003ce182691c..ecd1e4ba834d 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -68,6 +68,11 @@ config SND_SOC_QDSP6_AFE_DAI tristate default n +config SND_SOC_QDSP6_ASM_DAI + tristate + default n + + config SND_SOC_QDSP6 tristate "SoC ALSA audio driver for QDSP6" select SND_SOC_QDSP6_AFE @@ -76,6 +81,7 @@ config SND_SOC_QDSP6 select SND_SOC_QDSP6_CORE select SND_SOC_QDSP6_ROUTING select SND_SOC_QDSP6_AFE_DAI + select SND_SOC_QDSP6_ASM_DAI 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 bd8bd02bf09e..03576a442fb5 100644 --- a/sound/soc/qcom/qdsp6/Makefile +++ b/sound/soc/qcom/qdsp6/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_SND_SOC_QDSP6_ASM) += q6asm.o obj-$(CONFIG_SND_SOC_QDSP6_CORE) += q6core.o obj-$(CONFIG_SND_SOC_QDSP6_ROUTING) += q6routing.o obj-$(CONFIG_SND_SOC_QDSP6_AFE_DAI) += q6afe-dai.o +obj-$(CONFIG_SND_SOC_QDSP6_ASM_DAI) += q6asm-dai.o diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c new file mode 100644 index 000000000000..709c5de230fa --- /dev/null +++ b/sound/soc/qcom/qdsp6/q6asm-dai.c @@ -0,0 +1,534 @@ +/* SPDX-License-Identifier: GPL-2.0 +* Copyright (c) 2011-2016, The Linux Foundation +* Copyright (c) 2017, Linaro Limited +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "q6asm.h" +#include "q6routing.h" +#include "common.h" + +#define PLAYBACK_MIN_NUM_PERIODS 2 +#define PLAYBACK_MAX_NUM_PERIODS 8 +#define PLAYBACK_MAX_PERIOD_SIZE 65536 +#define PLAYBACK_MIN_PERIOD_SIZE 128 + +enum stream_state { + IDLE = 0, + STOPPED, + RUNNING, +}; + +struct q6asm_dai_rtd { + struct snd_pcm_substream *substream; + dma_addr_t phys; + unsigned int pcm_size; + unsigned int pcm_count; + unsigned int pcm_irq_pos; /* IRQ position */ + unsigned int periods; + uint16_t bits_per_sample; + uint16_t source; /* Encoding source bit mask */ + + struct audio_client *audio_client; + uint16_t session_id; + + enum stream_state state; + bool set_channel_map; + char channel_map[8]; +}; + +struct q6asm_dai_data { + u64 sid; +}; + +static struct snd_pcm_hardware q6asm_dai_hardware_playback = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .rates = SNDRV_PCM_RATE_8000_192000, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 8, + .buffer_bytes_max = (PLAYBACK_MAX_NUM_PERIODS * + PLAYBACK_MAX_PERIOD_SIZE), + .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE, + .period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE, + .periods_min = PLAYBACK_MIN_NUM_PERIODS, + .periods_max = PLAYBACK_MAX_NUM_PERIODS, + .fifo_size = 0, +}; + +/* Conventional and unconventional sample rate supported */ +static unsigned int supported_sample_rates[] = { + 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, + 88200, 96000, 176400, 192000 +}; + +static struct snd_pcm_hw_constraint_list constraints_sample_rates = { + .count = ARRAY_SIZE(supported_sample_rates), + .list = supported_sample_rates, + .mask = 0, +}; + +static void event_handler(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + struct q6asm_dai_rtd *prtd = priv; + struct snd_pcm_substream *substream = prtd->substream; + + switch (opcode) { + case ASM_CLIENT_EVENT_CMD_RUN_DONE: + q6asm_write_nolock(prtd->audio_client, + prtd->pcm_count, 0, 0, NO_TIMESTAMP); + break; + case ASM_CLIENT_EVENT_CMD_EOS_DONE: + prtd->state = STOPPED; + break; + case ASM_CLIENT_EVENT_DATA_WRITE_DONE: { + prtd->pcm_irq_pos += prtd->pcm_count; + snd_pcm_period_elapsed(substream); + if (prtd->state == RUNNING) + q6asm_write_nolock(prtd->audio_client, + prtd->pcm_count, 0, 0, NO_TIMESTAMP); + + break; + } + default: + break; + } +} + +static int q6asm_dai_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; + struct q6asm_dai_rtd *prtd = runtime->private_data; + struct q6asm_dai_data *pdata; + int ret; + + pdata = dev_get_drvdata(soc_prtd->platform->dev); + if (!pdata) + return -EINVAL; + + if (!prtd || !prtd->audio_client) { + pr_err("%s: private data null or audio client freed\n", + __func__); + return -EINVAL; + } + + prtd->pcm_count = snd_pcm_lib_period_bytes(substream); + prtd->pcm_irq_pos = 0; + /* rate and channels are sent to audio driver */ + if (prtd->state) { + /* clear the previous setup if any */ + q6asm_cmd(prtd->audio_client, CMD_CLOSE); + q6asm_unmap_memory_regions(substream->stream, + prtd->audio_client); + q6routing_dereg_phy_stream(soc_prtd->dai_link->id, + SNDRV_PCM_STREAM_PLAYBACK); + } + + ret = q6asm_map_memory_regions(substream->stream, prtd->audio_client, + prtd->phys, + (prtd->pcm_size / prtd->periods), + prtd->periods); + + if (ret < 0) { + pr_err("Audio Start: Buffer Allocation failed rc = %d\n", + ret); + return -ENOMEM; + } + + ret = q6asm_open_write(prtd->audio_client, FORMAT_LINEAR_PCM, + prtd->bits_per_sample); + if (ret < 0) { + pr_err("%s: q6asm_open_write failed\n", __func__); + q6asm_audio_client_free(prtd->audio_client); + prtd->audio_client = NULL; + return -ENOMEM; + } + + prtd->session_id = q6asm_get_session_id(prtd->audio_client); + ret = q6routing_reg_phy_stream(soc_prtd->dai_link->id, LEGACY_PCM_MODE, + prtd->session_id, substream->stream); + if (ret) { + pr_err("%s: stream reg failed ret:%d\n", __func__, ret); + return ret; + } + + ret = q6asm_media_format_block_multi_ch_pcm( + prtd->audio_client, runtime->rate, + runtime->channels, !prtd->set_channel_map, + prtd->channel_map, prtd->bits_per_sample); + if (ret < 0) + pr_info("%s: CMD Format block failed\n", __func__); + + prtd->state = RUNNING; + + return 0; +} + +static int q6asm_dai_trigger(struct snd_pcm_substream *substream, int cmd) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct q6asm_dai_rtd *prtd = runtime->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + ret = q6asm_run_nowait(prtd->audio_client, 0, 0, 0); + break; + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ret = q6asm_run_nowait(prtd->audio_client, 0, 0, 0); + break; + case SNDRV_PCM_TRIGGER_STOP: + prtd->state = STOPPED; + ret = q6asm_cmd_nowait(prtd->audio_client, CMD_EOS); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + ret = q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int q6asm_dai_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; + struct q6asm_dai_rtd *prtd; + struct q6asm_dai_data *pdata; + struct device *dev = soc_prtd->platform->dev; + int ret = 0; + + pdata = dev_get_drvdata(dev); + if (!pdata) { + pr_err("Platform data not found ..\n"); + return -EINVAL; + } + + prtd = kzalloc(sizeof(struct q6asm_dai_rtd), GFP_KERNEL); + if (prtd == NULL) + return -ENOMEM; + + prtd->substream = substream; + prtd->audio_client = q6asm_audio_client_alloc(dev, + (app_cb)event_handler, prtd); + if (!prtd->audio_client) { + pr_info("%s: Could not allocate memory\n", __func__); + kfree(prtd); + return -ENOMEM; + } + +// prtd->audio_client->dev = dev; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + runtime->hw = q6asm_dai_hardware_playback; + + ret = snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_sample_rates); + if (ret < 0) + pr_info("snd_pcm_hw_constraint_list failed\n"); + /* Ensure that buffer size is a multiple of period size */ + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + pr_info("snd_pcm_hw_constraint_integer failed\n"); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + PLAYBACK_MIN_NUM_PERIODS * PLAYBACK_MIN_PERIOD_SIZE, + PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE); + if (ret < 0) { + pr_err("constraint for buffer bytes min max ret = %d\n", + ret); + } + } + + ret = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); + if (ret < 0) { + pr_err("constraint for period bytes step ret = %d\n", + ret); + } + ret = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); + if (ret < 0) { + pr_err("constraint for buffer bytes step ret = %d\n", + ret); + } + + prtd->set_channel_map = false; + runtime->private_data = prtd; + + snd_soc_set_runtime_hwparams(substream, &q6asm_dai_hardware_playback); + + runtime->dma_bytes = q6asm_dai_hardware_playback.buffer_bytes_max; + + + if (pdata->sid < 0) + prtd->phys = substream->dma_buffer.addr; + else + prtd->phys = substream->dma_buffer.addr | (pdata->sid << 32); + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + + return 0; +} + +static int q6asm_dai_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; + struct q6asm_dai_rtd *prtd = runtime->private_data; + + if (prtd->audio_client) { + q6asm_cmd(prtd->audio_client, CMD_CLOSE); + q6asm_unmap_memory_regions(substream->stream, + prtd->audio_client); + q6asm_audio_client_free(prtd->audio_client); + } + q6routing_dereg_phy_stream(soc_prtd->dai_link->id, + SNDRV_PCM_STREAM_PLAYBACK); + kfree(prtd); + return 0; +} + +static snd_pcm_uframes_t q6asm_dai_pointer(struct snd_pcm_substream *substream) +{ + + struct snd_pcm_runtime *runtime = substream->runtime; + struct q6asm_dai_rtd *prtd = runtime->private_data; + + if (prtd->pcm_irq_pos >= prtd->pcm_size) + prtd->pcm_irq_pos = 0; + + return bytes_to_frames(runtime, (prtd->pcm_irq_pos)); +} + +static int q6asm_dai_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; + struct snd_card *card = soc_prtd->card->snd_card; + + return dma_mmap_coherent(card->dev, vma, + runtime->dma_area, runtime->dma_addr, + runtime->dma_bytes); +} + +static int q6asm_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct q6asm_dai_rtd *prtd = runtime->private_data; + + prtd->pcm_size = params_buffer_bytes(params); + prtd->periods = params_periods(params); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + prtd->bits_per_sample = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + prtd->bits_per_sample = 24; + break; + } + + return 0; +} + +static struct snd_pcm_ops q6asm_dai_ops = { + .open = q6asm_dai_open, + .hw_params = q6asm_dai_hw_params, + .close = q6asm_dai_close, + .ioctl = snd_pcm_lib_ioctl, + .prepare = q6asm_dai_prepare, + .trigger = q6asm_dai_trigger, + .pointer = q6asm_dai_pointer, + .mmap = q6asm_dai_mmap, +}; + +static int q6asm_dai_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_pcm *pcm = rtd->pcm; + struct snd_pcm_substream *substream; + struct snd_card *card = rtd->card->snd_card; + struct device *dev = card->dev; + struct device_node *node = dev->of_node; + struct q6asm_dai_data *pdata = dev_get_drvdata(rtd->platform->dev); + struct of_phandle_args args; + + int size, ret = 0; + + ret = of_parse_phandle_with_fixed_args(node, "iommus", 1, 0, &args); + if (ret < 0) + pdata->sid = -1; + else + pdata->sid = args.args[0]; + + + + substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + size = q6asm_dai_hardware_playback.buffer_bytes_max; + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size, + &substream->dma_buffer); + if (ret) { + dev_err(dev, "Cannot allocate buffer(s)\n"); + return ret; + } + + return ret; +} + +static void q6asm_dai_pcm_free(struct snd_pcm *pcm) +{ + struct snd_pcm_substream *substream; + int i; + + for (i = 0; i < ARRAY_SIZE(pcm->streams); i++) { + substream = pcm->streams[i].substream; + if (substream) { + snd_dma_free_pages(&substream->dma_buffer); + substream->dma_buffer.area = NULL; + substream->dma_buffer.addr = 0; + } + } +} + +static struct snd_soc_platform_driver q6asm_soc_platform = { + .ops = &q6asm_dai_ops, + .pcm_new = q6asm_dai_pcm_new, + .pcm_free = q6asm_dai_pcm_free, + +}; + +static const struct snd_soc_dapm_route afe_pcm_routes[] = { + {"MM_DL1", NULL, "MultiMedia1 Playback" }, + {"MM_DL2", NULL, "MultiMedia2 Playback" }, + +}; + +static int fe_dai_probe(struct snd_soc_dai *dai) +{ + struct snd_soc_dapm_context *dapm; + + dapm = snd_soc_component_get_dapm(dai->component); + snd_soc_dapm_add_routes(dapm, afe_pcm_routes, + ARRAY_SIZE(afe_pcm_routes)); + + return 0; +} + +static const struct snd_soc_component_driver q6asm_fe_dai_component = { + .name = "q6asm-fe-dai", +}; + +static struct snd_soc_dai_driver q6asm_fe_dais[] = { + { + .playback = { + .stream_name = "MultiMedia1 Playback", + .rates = (SNDRV_PCM_RATE_8000_192000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, + .name = "MM_DL1", + .probe = fe_dai_probe, + .id = MSM_FRONTEND_DAI_MULTIMEDIA1, + }, + { + .playback = { + .stream_name = "MultiMedia2 Playback", + .rates = (SNDRV_PCM_RATE_8000_192000| + SNDRV_PCM_RATE_KNOT), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, + .name = "MM_DL2", + .probe = fe_dai_probe, + .id = MSM_FRONTEND_DAI_MULTIMEDIA2, + }, +}; + +static int q6asm_dai_probe(struct platform_device *pdev) +{ + struct q6asm_dai_data *pdata; + struct device *dev = &pdev->dev; + int rc; + + pdata = devm_kzalloc(dev, sizeof(struct q6asm_dai_data), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + + dev_set_drvdata(dev, pdata); + + rc = snd_soc_register_platform(dev, &q6asm_soc_platform); + if (rc) { + dev_err(&pdev->dev, "err_dai_platform\n"); + return rc; + } + + rc = snd_soc_register_component(dev, &q6asm_fe_dai_component, + q6asm_fe_dais, + ARRAY_SIZE(q6asm_fe_dais)); + if (rc) + dev_err(dev, "err_dai_component\n"); + + return rc; + +} + +static int q6asm_dai_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static struct platform_driver q6asm_dai_driver = { + .driver = { + .name = "q6asm_dai", + .owner = THIS_MODULE, + }, + .probe = q6asm_dai_probe, + .remove = q6asm_dai_remove, +}; + +module_platform_driver(q6asm_dai_driver); + +MODULE_DESCRIPTION("PCM module platform driver"); +MODULE_LICENSE("GPL v2");