From patchwork Thu Dec 11 01:24:34 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kuninori Morimoto X-Patchwork-Id: 5473631 Return-Path: X-Original-To: patchwork-dmaengine@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id A8AE69F2E8 for ; Thu, 11 Dec 2014 01:24:42 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 4619F2012B for ; Thu, 11 Dec 2014 01:24:41 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id E593220172 for ; Thu, 11 Dec 2014 01:24:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S933499AbaLKBYj (ORCPT ); Wed, 10 Dec 2014 20:24:39 -0500 Received: from relmlor3.renesas.com ([210.160.252.173]:16318 "EHLO relmlie2.idc.renesas.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S933151AbaLKBYi (ORCPT ); Wed, 10 Dec 2014 20:24:38 -0500 Received: from unknown (HELO relmlir1.idc.renesas.com) ([10.200.68.151]) by relmlie2.idc.renesas.com with ESMTP; 11 Dec 2014 10:24:37 +0900 Received: from relmlac1.idc.renesas.com (relmlac1.idc.renesas.com [10.200.69.21]) by relmlir1.idc.renesas.com (Postfix) with ESMTP id 8518945C9D; Thu, 11 Dec 2014 10:24:37 +0900 (JST) Received: by relmlac1.idc.renesas.com (Postfix, from userid 0) id 7B43F8002E; Thu, 11 Dec 2014 10:24:37 +0900 (JST) Received: from relmlac1.idc.renesas.com (localhost [127.0.0.1]) by relmlac1.idc.renesas.com (Postfix) with ESMTP id 757B78002D; Thu, 11 Dec 2014 10:24:37 +0900 (JST) Received: from relmlii1.idc.renesas.com [10.200.68.65] by relmlac1.idc.renesas.com with ESMTP id LAM09218; Thu, 11 Dec 2014 10:24:37 +0900 X-IronPort-AV: E=Sophos;i="5.07,554,1413212400"; d="scan'208";a="175473595" Received: from mail-sg1lp0093.outbound.protection.outlook.com (HELO APAC01-SG1-obe.outbound.protection.outlook.com) ([207.46.51.93]) by relmlii1.idc.renesas.com with ESMTP/TLS/AES256-SHA; 11 Dec 2014 10:24:36 +0900 Received: from remon.renesas.com (211.11.155.132) by SINPR06MB172.apcprd06.prod.outlook.com (10.242.57.21) with Microsoft SMTP Server (TLS) id 15.1.31.17; Thu, 11 Dec 2014 01:24:34 +0000 Message-ID: <87mw6v9dwc.wl%kuninori.morimoto.gx@renesas.com> To: Vinod Koul CC: Laurent Pinchart , , Simon , Rob Herring , Linux-SH , Grant Likely In-Reply-To: <87ppbr9dzq.wl%kuninori.morimoto.gx@renesas.com> References: <8761ekokpf.wl%kuninori.morimoto.gx@renesas.com> <87fvd4b2kh.wl%kuninori.morimoto.gx@renesas.com> <20141208124525.GH16827@intel.com> <5437195.1byQrlazfz@avalon> <20141209060834.GW16827@intel.com> <877fy0pbs6.wl%kuninori.morimoto.gx@renesas.com> <87y4qgnmxm.wl%kuninori.morimoto.gx@renesas.com> <87vblknmw3.wl%kuninori.morimoto.gx@renesas.com> <20141210055838.GF16827@intel.com> <871to8dlxa.wl%kuninori.morimoto.gx@renesas.com> <87ppbr9dzq.wl%kuninori.morimoto.gx@renesas.com> From: Kuninori Morimoto Subject: [PATCH 2/2 v3] dmaengine: rcar-audmapp: independent from SH_DMAE_BASE v2 User-Agent: Wanderlust/2.14.0 Emacs/23.3 Mule/6.0 MIME-Version: 1.0 (generated by SEMI 1.14.6 - "Maruoka") Date: Thu, 11 Dec 2014 01:24:34 +0000 X-Originating-IP: [211.11.155.132] X-ClientProxiedBy: SIXPR06CA0059.apcprd06.prod.outlook.com (25.160.171.177) To SINPR06MB172.apcprd06.prod.outlook.com (10.242.57.21) X-Microsoft-Antispam: UriScan:; X-Microsoft-Antispam: BCL:0;PCL:0;RULEID:;SRVR:SINPR06MB172; X-Exchange-Antispam-Report-Test: UriScan:; X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0; RULEID:(601003); SRVR:SINPR06MB172; X-Forefront-PRVS: 0422860ED4 X-Forefront-Antispam-Report: SFV:NSPM; SFS:(10019020)(6009001)(199003)(189002)(53416004)(46406003)(50466002)(40100003)(77156002)(62966003)(105586002)(42186005)(110136001)(106356001)(33646002)(46102003)(31966008)(19580395003)(19580405001)(23726002)(86362001)(77096005)(575784001)(120916001)(107046002)(101416001)(93886004)(97736003)(87976001)(229853001)(64706001)(36756003)(99396003)(20776003)(47776003)(21056001)(122386002)(66066001)(69596002)(4396001)(68736005)(54356999)(92566001)(76176999)(83506001)(50986999)(2004002); DIR:OUT; SFP:1102; SCL:1; SRVR:SINPR06MB172; H:remon.renesas.com; FPR:; SPF:None; MLV:sfv; PTR:InfoNoRecords; A:1; MX:1; LANG:en; X-Exchange-Antispam-Report-CFA-Test: BCL:0;PCL:0;RULEID:;SRVR:SINPR06MB172; X-OriginatorOrg: renesas.com Sender: dmaengine-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: dmaengine@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Kuninori Morimoto Old Renesas Audio DMAC Peri Peri driver was based on SH_DMAE_BASE driver, and it had DMAEngine channel handling issue on DT if multiple DT base DMAEngine drivers were probed. But, now, it has been simply removed. This patch adds new rcar-audmapp driver which can care DT handling. Signed-off-by: Kuninori Morimoto --- v2 -> v3 - use lock - based on topic/slave_caps_device_control_fix - add explain under audmapp_prep_dma_cyclic drivers/dma/sh/Kconfig | 7 + drivers/dma/sh/Makefile | 1 + drivers/dma/sh/rcar-audmapp.c | 348 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 356 insertions(+) create mode 100644 drivers/dma/sh/rcar-audmapp.c diff --git a/drivers/dma/sh/Kconfig b/drivers/dma/sh/Kconfig index 0c43210..68310cf 100644 --- a/drivers/dma/sh/Kconfig +++ b/drivers/dma/sh/Kconfig @@ -46,3 +46,10 @@ config RCAR_HPB_DMAE depends on SH_DMAE_BASE help Enable support for the Renesas R-Car series DMA controllers. + +config RCAR_AUDMAC_PP + tristate "Renesas R-Car Audio DMAC Peripheral Peripheral support" + depends on ARCH_SHMOBILE || COMPILE_TEST + select RENESAS_DMA + help + Enable support for the Renesas R-Car Audio DMAC Peripheral Peripheral controllers. diff --git a/drivers/dma/sh/Makefile b/drivers/dma/sh/Makefile index aede7db..0a5cfdb 100644 --- a/drivers/dma/sh/Makefile +++ b/drivers/dma/sh/Makefile @@ -15,3 +15,4 @@ obj-$(CONFIG_SH_DMAE) += shdma.o obj-$(CONFIG_SUDMAC) += sudmac.o obj-$(CONFIG_RCAR_HPB_DMAE) += rcar-hpbdma.o +obj-$(CONFIG_RCAR_AUDMAC_PP) += rcar-audmapp.o diff --git a/drivers/dma/sh/rcar-audmapp.c b/drivers/dma/sh/rcar-audmapp.c new file mode 100644 index 0000000..eb09cf3 --- /dev/null +++ b/drivers/dma/sh/rcar-audmapp.c @@ -0,0 +1,348 @@ +/* + * This is for Renesas R-Car Audio-DMAC-peri-peri. + * + * Copyright (C) 2014 Renesas Electronics Corporation + * Copyright (C) 2014 Kuninori Morimoto + * + * based on the drivers/dma/sh/rcar-dmac.c + * + * Author: Laurent Pinchart + * + * This 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; either version 2 of the License, or + * (at your option) any later version. + * + */ +#include +#include +#include +#include +#include "../dmaengine.h" + +/* + * DMA register + */ +#define PDMASAR 0x00 +#define PDMADAR 0x04 +#define PDMACHCR 0x0c + +/* PDMACHCR */ +#define PDMACHCR_DE (1 << 0) + +#define AUDMAPP_MAX_CHANNELS 29 + +/* Default MEMCPY transfer size = 2^2 = 4 bytes */ +#define LOG2_DEFAULT_XFER_SIZE 2 + +struct audmapp_priv; +struct audmapp_chan { + struct dma_chan chan; + struct dma_async_tx_descriptor async_tx; + + dma_addr_t src; + dma_addr_t dst; + u32 chcr; + + int id; +}; + +struct audmapp_priv { + struct dma_device dma; + void __iomem *achan_reg; + + struct audmapp_chan achan[AUDMAPP_MAX_CHANNELS]; + spinlock_t lock; +}; + +#define chan_to_achan(chan) container_of(chan, struct audmapp_chan, chan) +#define achan_to_priv(achan) container_of(achan - achan->id, \ + struct audmapp_priv, achan[0]) + +#define priv_to_dev(priv) ((priv)->dma.dev) +#define priv_to_dma(priv) (&(priv)->dma) + +#define audmapp_reg(achan, _reg) (achan_to_priv(achan)->achan_reg + \ + 0x20 + (0x10 * achan->id) + _reg) + +#define audmapp_for_each_achan(achan, priv, i) \ + for (i = 0; \ + (i < AUDMAPP_MAX_CHANNELS && ((achan) = priv->achan + i)); \ + i++) + +static void audmapp_write(struct audmapp_chan *achan, u32 data, u32 _reg) +{ + struct audmapp_priv *priv = achan_to_priv(achan); + struct device *dev = priv_to_dev(priv); + void __iomem *reg = audmapp_reg(achan, _reg); + + dev_dbg(dev, "w %p : %08x\n", reg, data); + + iowrite32(data, reg); +} + +static u32 audmapp_read(struct audmapp_chan *achan, u32 _reg) +{ + return ioread32(audmapp_reg(achan, _reg)); +} + +static int audmapp_alloc_chan_resources(struct dma_chan *chan) +{ + if (chan->private) + return -ENODEV; + + chan->private = chan_to_achan(chan); + + return 0; +} + +static void audmapp_free_chan_resources(struct dma_chan *chan) +{ + chan->private = NULL; +} + +static struct dma_async_tx_descriptor * +audmapp_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, + size_t buf_len, size_t period_len, + enum dma_transfer_direction dir, unsigned long flags) +{ + struct audmapp_chan *achan = chan_to_achan(chan); + + /* + * Audio DMAC peri peri does cyclic transfer automatically + * without special settings. + * Main transfer settings are done in Audio DMAC. + * Nothing to do here. + */ + + return &achan->async_tx; +} + +static int audmapp_device_config(struct dma_chan *chan, + struct dma_slave_config *cfg) +{ + struct audmapp_chan *achan = chan_to_achan(chan); + + achan->src = cfg->src_addr; + achan->dst = cfg->dst_addr; + + return 0; +} + +static int audmapp_terminate_all(struct dma_chan *chan) +{ + struct audmapp_chan *achan = chan_to_achan(chan); + struct audmapp_priv *priv = achan_to_priv(achan); + unsigned long flags; + int i; + + spin_lock_irqsave(&priv->lock, flags); + + audmapp_write(achan, 0, PDMACHCR); + + spin_unlock_irqrestore(&priv->lock, flags); + + for (i = 0; i < 1024; i++) { + if (0 == audmapp_read(achan, PDMACHCR)) + return 0; + udelay(1); + } + + return -EIO; +} + +static enum dma_status audmapp_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, + struct dma_tx_state *txstate) +{ + return dma_cookie_status(chan, cookie, txstate); +} + +static void audmapp_issue_pending(struct dma_chan *chan) +{ + struct audmapp_chan *achan = chan_to_achan(chan); + struct audmapp_priv *priv = achan_to_priv(achan); + struct device *dev = priv_to_dev(priv); + u32 chcr = achan->chcr | PDMACHCR_DE; + unsigned long flags; + + dev_dbg(dev, "src/dst/chcr = %pad/%pad/%08x\n", + &achan->src, &achan->dst, chcr); + + spin_lock_irqsave(&priv->lock, flags); + + audmapp_write(achan, achan->src, PDMASAR); + audmapp_write(achan, achan->dst, PDMADAR); + audmapp_write(achan, chcr, PDMACHCR); + + spin_unlock_irqrestore(&priv->lock, flags); +} + +static bool audmapp_chan_filter(struct dma_chan *chan, void *arg) +{ + struct dma_device *dma = chan->device; + struct of_phandle_args *dma_spec = arg; + + /* + * FIXME: Using a filter on OF platforms is a nonsense. The OF xlate + * function knows from which device it wants to allocate a channel from, + * and would be perfectly capable of selecting the channel it wants. + * Forcing it to call dma_request_channel() and iterate through all + * channels from all controllers is just pointless. + */ + if (dma->device_config != audmapp_device_config || + dma_spec->np != dma->dev->of_node) + return false; + + /* + * see + * audmapp_alloc_chan_resources() + * audmapp_free_chan_resources() + */ + return !chan->private; +} + +static struct dma_chan *audmapp_of_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + struct audmapp_chan *achan; + struct dma_chan *chan; + dma_cap_mask_t mask; + + if (dma_spec->args_count != 1) + return NULL; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + chan = dma_request_channel(mask, audmapp_chan_filter, dma_spec); + if (!chan) + return NULL; + + achan = chan_to_achan(chan); + achan->chcr = dma_spec->args[0] << 16; + + return chan; +} + +static dma_cookie_t audmapp_tx_submit(struct dma_async_tx_descriptor *tx) +{ + return dma_cookie_assign(tx); +} + +static int audmapp_chan_desc_probe(struct platform_device *pdev, + struct audmapp_priv *priv) + +{ + struct dma_device *dma = priv_to_dma(priv); + struct device *dev = priv_to_dev(priv); + struct audmapp_chan *achan; + struct dma_chan *chan; + int i; + + audmapp_for_each_achan(achan, priv, i) { + chan = &achan->chan; + + achan->id = i; + + /* + * Initialize the DMA engine channel and add it to the DMA + * engine channels list. + */ + chan->private = NULL; + chan->device = dma; + dma_cookie_init(chan); + list_add_tail(&chan->device_node, &dma->channels); + + achan->async_tx.tx_submit = audmapp_tx_submit; + dma_async_tx_descriptor_init(&achan->async_tx, chan); + + dev_dbg(dev, "%02d : %p\n", i, audmapp_reg(achan, 0)); + } + + return 0; +} + +static int audmapp_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct audmapp_priv *priv; + struct dma_device *dma; + struct resource *res; + int ret; + + of_dma_controller_register(dev->of_node, audmapp_of_xlate, pdev); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->achan_reg = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->achan_reg)) + return PTR_ERR(priv->achan_reg); + + spin_lock_init(&priv->lock); + + dev_dbg(dev, "%llx => %p\n", (u64)res->start, priv->achan_reg); + + dma = priv_to_dma(priv); + dma->copy_align = LOG2_DEFAULT_XFER_SIZE; + INIT_LIST_HEAD(&dma->channels); + dma_cap_set(DMA_SLAVE, dma->cap_mask); + + dma->device_alloc_chan_resources = audmapp_alloc_chan_resources; + dma->device_free_chan_resources = audmapp_free_chan_resources; + dma->device_prep_dma_cyclic = audmapp_prep_dma_cyclic; + dma->device_config = audmapp_device_config; + dma->device_terminate_all = audmapp_terminate_all; + dma->device_tx_status = audmapp_tx_status; + dma->device_issue_pending = audmapp_issue_pending; + dma->dev = dev; + + platform_set_drvdata(pdev, priv); + + ret = audmapp_chan_desc_probe(pdev, priv); + if (ret) + return ret; + + ret = dma_async_device_register(dma); + if (ret) + return ret; + + dev_info(dev, "probed\n"); + + return ret; +} + +static int audmapp_remove(struct platform_device *pdev) +{ + struct audmapp_priv *priv = platform_get_drvdata(pdev); + struct dma_device *dma = priv_to_dma(priv); + + dma_async_device_unregister(dma); + + of_dma_controller_free(pdev->dev.of_node); + + return 0; +} + +static const struct of_device_id audmapp_of_match[] = { + { .compatible = "renesas,rcar-audmapp", }, + {}, +}; + +static struct platform_driver audmapp_driver = { + .probe = audmapp_probe, + .remove = audmapp_remove, + .driver = { + .name = "rcar-audmapp-engine", + .of_match_table = audmapp_of_match, + }, +}; +module_platform_driver(audmapp_driver); + +MODULE_AUTHOR("Kuninori Morimoto "); +MODULE_DESCRIPTION("Renesas R-Car Audio DMAC peri-peri driver"); +MODULE_LICENSE("GPL");