From patchwork Fri Jan 19 09:25:30 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Katsuhiro Suzuki X-Patchwork-Id: 125130 Delivered-To: patch@linaro.org Received: by 10.46.66.141 with SMTP id h13csp205782ljf; Fri, 19 Jan 2018 01:26:14 -0800 (PST) X-Google-Smtp-Source: ACJfBotwRSeM16yMzOdCcmfoS1ONOv+RnIxP64OtzkUCVZfgpt6geWimaE5zsShOlv8kXW4p+Yk0 X-Received: by 10.223.157.140 with SMTP id p12mr8588192wre.278.1516353974767; Fri, 19 Jan 2018 01:26:14 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1516353974; cv=none; d=google.com; s=arc-20160816; b=ufjr8JRQvuqmiFAIb/wqXo7xkKkIqtf9JoqrybTK5QUWQENGc2yvIQNfMBVIGF0vim tUfZK4TMjGvuCLEqNptX2iwD9PPTmDS7PwQ2C3HlNn9bs0Q8o8/M7aBuLXTM73nGapSq z8K/W8G+NaWJQuvMkDZnLgAk7nDHUN1lnGfyY+LWTAs0elvjkkG3PACiDE/mLp7p4mrn D7jbD3nchxQizlcUNeygvKCU+eTvCxLh0rnE2+CoVDEdK8rlborH1m7IQmAQ6FBMFf4Z cmz6tP/7cRP+FzGkwUCVbSAdZbBBOGeoZUSYzaT8uu75w00W+uUem/nJOSUw/lkRaBMj vA4Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:cc:references:in-reply-to:message-id :date:to:from:delivered-to:arc-authentication-results; bh=zI0SCLV43rWBvdvds2AWhQMgbjjN/OwQbCE67HNlFjY=; b=kKLPeqJU6+FyKb5jEoschig98aeVoFHZ4OgOeiW9L7eEYqXn0vGONCc2dGJtbxtDP4 F8KNozPchpLdQj5QK5DOEQfvB1nkQ1JYgT44AWc4QDvjn0Eo9s8syYV9kcvGVcRCRXXK sO9bq93pz7PnZNF4LFQhfUKLZVOJIbgmAYwCGpfhafUkHBfY87Bnc7i1nq7hdbsiE+5b MpwchMP1YOE7fXUWGUT5tsfKR2JOOCuMeX7VTHCw6qUZresgIWLa+apacLmaZgwTimoO Nfd4mWnZWarqEjTkxtAW0Rgn6EXidDncdtw8FawsgXOKq95bZlhi2gZ8KYoUmOdSbQ8y pNIg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of alsa-devel-bounces@alsa-project.org designates 77.48.224.243 as permitted sender) smtp.mailfrom=alsa-devel-bounces@alsa-project.org Return-Path: Received: from alsa0.perex.cz (alsa0.perex.cz. [77.48.224.243]) by mx.google.com with ESMTP id 134si627305wmt.54.2018.01.19.01.26.14; Fri, 19 Jan 2018 01:26:14 -0800 (PST) Received-SPF: pass (google.com: domain of alsa-devel-bounces@alsa-project.org designates 77.48.224.243 as permitted sender) client-ip=77.48.224.243; Authentication-Results: mx.google.com; spf=pass (google.com: domain of alsa-devel-bounces@alsa-project.org designates 77.48.224.243 as permitted sender) smtp.mailfrom=alsa-devel-bounces@alsa-project.org Received: from alsa0.perex.cz (localhost [127.0.0.1]) by alsa0.perex.cz (Postfix) with ESMTP id 2A16A2678C8; Fri, 19 Jan 2018 10:25:45 +0100 (CET) X-Original-To: alsa-devel@alsa-project.org Delivered-To: alsa-devel@alsa-project.org Received: by alsa0.perex.cz (Postfix, from userid 1000) id 0E7E12678B6; Fri, 19 Jan 2018 10:25:41 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on mail1.perex.cz X-Spam-Level: X-Spam-Status: No, score=-0.0 required=5.0 tests=SPF_PASS, T_RP_MATCHES_RCVD autolearn=disabled version=3.4.0 Received: from mx.socionext.com (mx.socionext.com [202.248.49.38]) by alsa0.perex.cz (Postfix) with ESMTP id A4B8F2678B2 for ; Fri, 19 Jan 2018 10:25:38 +0100 (CET) Received: from unknown (HELO kinkan-ex.css.socionext.com) ([172.31.9.52]) by mx.socionext.com with ESMTP; 19 Jan 2018 18:25:33 +0900 Received: from mail.mfilter.local (m-filter-2 [10.213.24.62]) by kinkan-ex.css.socionext.com (Postfix) with ESMTP id 6E8F3180D50; Fri, 19 Jan 2018 18:25:33 +0900 (JST) Received: from 172.31.9.51 (172.31.9.51) by m-FILTER with ESMTP; Fri, 19 Jan 2018 18:25:39 +0900 Received: from yuzu.css.socionext.com (yuzu [172.31.8.45]) by kinkan.css.socionext.com (Postfix) with ESMTP id 148591A0DEC; Fri, 19 Jan 2018 18:25:33 +0900 (JST) Received: from aegis.e01.socionext.com (unknown [10.213.134.210]) by yuzu.css.socionext.com (Postfix) with ESMTP id D8B48120B2E; Fri, 19 Jan 2018 18:25:32 +0900 (JST) From: Katsuhiro Suzuki To: Mark Brown , alsa-devel@alsa-project.org, Rob Herring , devicetree@vger.kernel.org, Masahiro Yamada Date: Fri, 19 Jan 2018 18:25:30 +0900 Message-Id: <20180119092536.22501-4-suzuki.katsuhiro@socionext.com> X-Mailer: git-send-email 2.15.0 In-Reply-To: <20180119092536.22501-1-suzuki.katsuhiro@socionext.com> References: <20180119092536.22501-1-suzuki.katsuhiro@socionext.com> Cc: Katsuhiro Suzuki , Jassi Brar , linux-arm-kernel@lists.infradead.org, Masami Hiramatsu , linux-kernel@vger.kernel.org Subject: [alsa-devel] [PATCH v2 3/9] ASoC: uniphier: add support for UniPhier AIO DMA driver X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: alsa-devel-bounces@alsa-project.org Sender: alsa-devel-bounces@alsa-project.org This patch adds supports for UniPhier AIO DMA. This module shared register area with all sound devices for I2S, S/PDIF and so on. Since the AIO has mixed register map for those I/Os, it is hard to split register areas for each sound devices. Signed-off-by: Katsuhiro Suzuki --- Changes in v2: - Change license comment style to C++ from C - Add error checks - Fix typo in error messages - Split DMA, CPU DAI patches from large one - Add comments to aiodma_irq() - Fix missing checks for return value --- sound/soc/uniphier/Makefile | 2 +- sound/soc/uniphier/aio-dma.c | 317 +++++++++++++++++++++++++++++++++++++++++++ sound/soc/uniphier/aio.h | 2 + 3 files changed, 320 insertions(+), 1 deletion(-) create mode 100644 sound/soc/uniphier/aio-dma.c -- 2.15.0 _______________________________________________ Alsa-devel mailing list Alsa-devel@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/alsa-devel diff --git a/sound/soc/uniphier/Makefile b/sound/soc/uniphier/Makefile index f3b36aba4879..9efe0feffdc2 100644 --- a/sound/soc/uniphier/Makefile +++ b/sound/soc/uniphier/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -snd-soc-uniphier-aio-cpu-objs := aio-core.o +snd-soc-uniphier-aio-cpu-objs := aio-core.o aio-dma.o obj-$(CONFIG_SND_SOC_UNIPHIER_AIO) += snd-soc-uniphier-aio-cpu.o diff --git a/sound/soc/uniphier/aio-dma.c b/sound/soc/uniphier/aio-dma.c new file mode 100644 index 000000000000..6d0ca6dde913 --- /dev/null +++ b/sound/soc/uniphier/aio-dma.c @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Socionext UniPhier AIO DMA driver. +// +// Copyright (c) 2016-2018 Socionext Inc. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; version 2 +// of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, see . + +#include +#include +#include +#include +#include +#include +#include + +#include "aio.h" + +static struct snd_pcm_hardware uniphier_aiodma_hw = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED, + .period_bytes_min = 256, + .period_bytes_max = 4096, + .periods_min = 4, + .periods_max = 1024, + .buffer_bytes_max = 128 * 1024, +}; + +static void aiodma_pcm_irq(struct uniphier_aio_sub *sub) +{ + struct snd_pcm_runtime *runtime = sub->substream->runtime; + int bytes = runtime->period_size * + runtime->channels * samples_to_bytes(runtime, 1); + int ret; + + spin_lock(&sub->lock); + ret = aiodma_rb_set_threshold(sub, runtime->dma_bytes, + sub->threshold + bytes); + if (!ret) + sub->threshold += bytes; + + aiodma_rb_sync(sub, runtime->dma_addr, runtime->dma_bytes, bytes); + aiodma_rb_clear_irq(sub); + spin_unlock(&sub->lock); + + snd_pcm_period_elapsed(sub->substream); +} + +static void aiodma_compr_irq(struct uniphier_aio_sub *sub) +{ + struct snd_compr_runtime *runtime = sub->cstream->runtime; + int bytes = runtime->fragment_size; + int ret; + + spin_lock(&sub->lock); + ret = aiodma_rb_set_threshold(sub, sub->compr_bytes, + sub->threshold + bytes); + if (!ret) + sub->threshold += bytes; + + aiodma_rb_sync(sub, sub->compr_addr, sub->compr_bytes, bytes); + aiodma_rb_clear_irq(sub); + spin_unlock(&sub->lock); + + snd_compr_fragment_elapsed(sub->cstream); +} + +static irqreturn_t aiodma_irq(int irq, void *p) +{ + struct platform_device *pdev = p; + struct uniphier_aio_chip *chip = platform_get_drvdata(pdev); + irqreturn_t ret = IRQ_NONE; + int i, j; + + for (i = 0; i < chip->num_aios; i++) { + struct uniphier_aio *aio = &chip->aios[i]; + + for (j = 0; j < ARRAY_SIZE(aio->sub); j++) { + struct uniphier_aio_sub *sub = &aio->sub[j]; + + /* Skip channel that does not trigger */ + if (!sub->running || !aiodma_rb_is_irq(sub)) + continue; + + if (sub->substream) + aiodma_pcm_irq(sub); + if (sub->cstream) + aiodma_compr_irq(sub); + + ret = IRQ_HANDLED; + } + } + + return ret; +} + +static int uniphier_aiodma_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + snd_soc_set_runtime_hwparams(substream, &uniphier_aiodma_hw); + + return snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256); +} + +static int uniphier_aiodma_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + substream->runtime->dma_bytes = params_buffer_bytes(params); + + return 0; +} + +static int uniphier_aiodma_hw_free(struct snd_pcm_substream *substream) +{ + snd_pcm_set_runtime_buffer(substream, NULL); + substream->runtime->dma_bytes = 0; + + return 0; +} + +static int uniphier_aiodma_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai); + struct uniphier_aio_sub *sub = &aio->sub[substream->stream]; + int bytes = runtime->period_size * + runtime->channels * samples_to_bytes(runtime, 1); + unsigned long flags; + int ret; + + ret = aiodma_ch_set_param(sub); + if (ret) + return ret; + + spin_lock_irqsave(&sub->lock, flags); + ret = aiodma_rb_set_buffer(sub, runtime->dma_addr, + runtime->dma_addr + runtime->dma_bytes, + bytes); + spin_unlock_irqrestore(&sub->lock, flags); + if (ret) + return ret; + + return 0; +} + +static int uniphier_aiodma_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai); + struct uniphier_aio_sub *sub = &aio->sub[substream->stream]; + struct device *dev = &aio->chip->pdev->dev; + int bytes = runtime->period_size * + runtime->channels * samples_to_bytes(runtime, 1); + unsigned long flags; + + spin_lock_irqsave(&sub->lock, flags); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + aiodma_rb_sync(sub, runtime->dma_addr, runtime->dma_bytes, + bytes); + aiodma_ch_set_enable(sub, 1); + sub->running = 1; + + break; + case SNDRV_PCM_TRIGGER_STOP: + sub->running = 0; + aiodma_ch_set_enable(sub, 0); + + break; + default: + dev_warn(dev, "Unknown trigger(%d) ignored\n", cmd); + break; + } + spin_unlock_irqrestore(&sub->lock, flags); + + return 0; +} + +static snd_pcm_uframes_t uniphier_aiodma_pointer( + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct uniphier_aio *aio = uniphier_priv(rtd->cpu_dai); + struct uniphier_aio_sub *sub = &aio->sub[substream->stream]; + int bytes = runtime->period_size * + runtime->channels * samples_to_bytes(runtime, 1); + unsigned long flags; + snd_pcm_uframes_t pos; + + spin_lock_irqsave(&sub->lock, flags); + aiodma_rb_sync(sub, runtime->dma_addr, runtime->dma_bytes, bytes); + + if (sub->swm->dir == PORT_DIR_OUTPUT) + pos = bytes_to_frames(runtime, sub->rd_offs); + else + pos = bytes_to_frames(runtime, sub->wr_offs); + spin_unlock_irqrestore(&sub->lock, flags); + + return pos; +} + +static int uniphier_aiodma_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + + return remap_pfn_range(vma, vma->vm_start, + substream->dma_buffer.addr >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, vma->vm_page_prot); +} + +static const struct snd_pcm_ops uniphier_aiodma_ops = { + .open = uniphier_aiodma_open, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = uniphier_aiodma_hw_params, + .hw_free = uniphier_aiodma_hw_free, + .prepare = uniphier_aiodma_prepare, + .trigger = uniphier_aiodma_trigger, + .pointer = uniphier_aiodma_pointer, + .mmap = uniphier_aiodma_mmap, +}; + +static int uniphier_aiodma_new(struct snd_soc_pcm_runtime *rtd) +{ + struct device *dev = rtd->card->snd_card->dev; + struct snd_pcm *pcm = rtd->pcm; + int ret; + + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(33)); + if (ret) + return ret; + + return snd_pcm_lib_preallocate_pages_for_all(pcm, + SNDRV_DMA_TYPE_DEV, dev, + uniphier_aiodma_hw.buffer_bytes_max, + uniphier_aiodma_hw.buffer_bytes_max); +} + +static void uniphier_aiodma_free(struct snd_pcm *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static const struct snd_soc_platform_driver uniphier_soc_platform = { + .pcm_new = uniphier_aiodma_new, + .pcm_free = uniphier_aiodma_free, + .ops = &uniphier_aiodma_ops, +}; + +static const struct regmap_config aiodma_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x7fffc, + .cache_type = REGCACHE_NONE, +}; + +/** + * uniphier_aiodma_soc_register_platform - register the AIO DMA + * @pdev: the platform device + * + * Register and setup the DMA of AIO to transfer the sound data to device. + * This function need to call once at driver startup and need NOT to call + * unregister function. + * + * Return: Zero if successful, otherwise a negative value on error. + */ +int uniphier_aiodma_soc_register_platform(struct platform_device *pdev) +{ + struct uniphier_aio_chip *chip = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + struct resource *res; + void __iomem *preg; + int irq, ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + preg = devm_ioremap_resource(dev, res); + if (IS_ERR(preg)) + return PTR_ERR(preg); + + chip->regmap = devm_regmap_init_mmio(dev, preg, + &aiodma_regmap_config); + if (IS_ERR(chip->regmap)) + return PTR_ERR(chip->regmap); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "Could not get irq.\n"); + return irq; + } + + ret = devm_request_irq(dev, irq, aiodma_irq, + IRQF_SHARED, dev_name(dev), pdev); + if (ret) + return ret; + + return devm_snd_soc_register_platform(dev, &uniphier_soc_platform); +} +EXPORT_SYMBOL_GPL(uniphier_aiodma_soc_register_platform); diff --git a/sound/soc/uniphier/aio.h b/sound/soc/uniphier/aio.h index 774abae28028..3687f6ff30b1 100644 --- a/sound/soc/uniphier/aio.h +++ b/sound/soc/uniphier/aio.h @@ -304,6 +304,8 @@ static inline struct uniphier_aio *uniphier_priv(struct snd_soc_dai *dai) return &chip->aios[dai->id]; } +int uniphier_aiodma_soc_register_platform(struct platform_device *pdev); + u64 aio_rb_cnt(struct uniphier_aio_sub *sub); u64 aio_rbt_cnt_to_end(struct uniphier_aio_sub *sub); u64 aio_rb_space(struct uniphier_aio_sub *sub);