From patchwork Wed Jan 25 10:28:08 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marek Szyprowski X-Patchwork-Id: 9536721 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 57E62604A0 for ; Wed, 25 Jan 2017 10:29:30 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 4678F2787C for ; Wed, 25 Jan 2017 10:29:30 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 3AD6727EE9; Wed, 25 Jan 2017 10:29:30 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 6980327DF9 for ; Wed, 25 Jan 2017 10:29:29 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751853AbdAYK3P (ORCPT ); Wed, 25 Jan 2017 05:29:15 -0500 Received: from mailout1.w1.samsung.com ([210.118.77.11]:45614 "EHLO mailout1.w1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751800AbdAYK2a (ORCPT ); Wed, 25 Jan 2017 05:28:30 -0500 Received: from eucas1p2.samsung.com (unknown [182.198.249.207]) by mailout1.w1.samsung.com (Oracle Communications Messaging Server 7.0.5.31.0 64bit (built May 5 2014)) with ESMTP id <0OKB00HDVZRE8U90@mailout1.w1.samsung.com>; Wed, 25 Jan 2017 10:28:27 +0000 (GMT) Received: from eusmges4.samsung.com (unknown [203.254.199.244]) by eucas1p1.samsung.com (KnoxPortal) with ESMTP id 20170125102826eucas1p112b2a5eda751642f967d87af6fc56198~c-PUVfqzM2537225372eucas1p1Q; Wed, 25 Jan 2017 10:28:26 +0000 (GMT) Received: from eucas1p2.samsung.com ( [182.198.249.207]) by eusmges4.samsung.com (EUCPMTA) with SMTP id 5E.84.28517.ACD78885; Wed, 25 Jan 2017 10:28:26 +0000 (GMT) Received: from eusmgms2.samsung.com (unknown [182.198.249.180]) by eucas1p2.samsung.com (KnoxPortal) with ESMTP id 20170125102825eucas1p22f4f6dbe744d778f0e1b53b6b26da5de~c-PTbDi7m0775107751eucas1p2V; Wed, 25 Jan 2017 10:28:25 +0000 (GMT) X-AuditID: cbfec7f4-f79716d000006f65-c0-58887dcac463 Received: from eusync4.samsung.com ( [203.254.199.214]) by eusmgms2.samsung.com (EUCPMTA) with SMTP id 45.E2.10233.6CD78885; Wed, 25 Jan 2017 10:28:22 +0000 (GMT) Received: from AMDC2765.digital.local ([106.116.147.25]) by eusync4.samsung.com (Oracle Communications Messaging Server 7.0.5.31.0 64bit (built May 5 2014)) with ESMTPA id <0OKB00K87ZR4QX40@eusync4.samsung.com>; Wed, 25 Jan 2017 10:28:25 +0000 (GMT) From: Marek Szyprowski To: linux-samsung-soc@vger.kernel.org, dmaengine@vger.kernel.org, alsa-devel@alsa-project.org, linux-arm-kernel@lists.infradead.org, linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Marek Szyprowski , Krzysztof Kozlowski , Bartlomiej Zolnierkiewicz , Vinod Koul , Ulf Hansson , "Rafael J. Wysocki" , Kuninori Morimoto , Mark Brown , Inki Dae Subject: [PATCH v7 4/4] dmaengine: pl330: Don't require irq-safe runtime PM Date: Wed, 25 Jan 2017 11:28:08 +0100 Message-id: <1485340088-25481-5-git-send-email-m.szyprowski@samsung.com> X-Mailer: git-send-email 1.9.1 In-reply-to: <1485340088-25481-1-git-send-email-m.szyprowski@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFmplleLIzCtJLcpLzFFi42LZduzned1TtR0RBpvXi1tcuXiIyWLjjPWs FlMfPmGzWD31L6vFpPsTWCzOn9/AbvHq8C5Gi02Pr7FaXN41h83ic+8RRosZ5/cxWaw9cpfd 4szpS6wWx9eGW7zs28/iwO+x4XMTm8fiPS+ZPDat6mTzuHNtD5vH5iX1Ht/OTGTx2HK1ncWj b8sqRo/Pm+QCOKO4bFJSczLLUov07RK4MnbtX8NUcDijYs/HBWwNjEfDuhg5OSQETCQer13D DmGLSVy4t56ti5GLQ0hgKaPE8lu7oZzPjBJ7195lhunY3w2TWMYo8bF1DZTTwCSx5ewUVpAq NgFDia63XWAJEYH9QO07HzODOMwCDcwSpw99A9soLOAtsXjSAzCbRUBVYvbDHjYQm1fAQ+Li vq2sEPvkJE4emwxmcwp4Ssz4e4QdZJCEwDF2iaWnZzB2MXIAObISmw5A3ecicat7PlSvsMSr 41ugvpORuDy5mwXC7meUaGrVhrBnMEqce8sLYVtLHD5+EayXWYBPYtK26cwQ43klOtqEIEo8 JJZf/AK1ylFiWccadojv5zBKLDy9j20Co8wCRoZVjCKppcW56anFJnrFibnFpXnpesn5uZsY gYnj9L/jX3YwLj5mdYhRgINRiYf3RUp7hBBrYllxZe4hRgkOZiURXreajggh3pTEyqrUovz4 otKc1OJDjNIcLErivHsWXAkXEkhPLEnNTk0tSC2CyTJxcEo1MMadzNtgnLP9zyn7xbknav8t se9MMFJPluMUvnsyMvHVinjuBP7JL+U0Obf1fbwrNteHM/nOKoW7BmL/ReuLFT1t784N2Jpe dWHl79LTH6vCd2t8EMlirV1XmLLG+tODu4s81hsJfuFXfZdwktltf6KM1rePAdI6OayJVmf0 b7Fsk/qy+PBGXyWW4oxEQy3mouJEAPb2UF0YAwAA X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFlrMIsWRmVeSWpSXmKPExsVy+t/xa7rHajsiDG7M1rS4cvEQk8XGGetZ LaY+fMJmsXrqX1aLSfcnsFicP7+B3eLV4V2MFpseX2O1uLxrDpvF594jjBYzzu9jslh75C67 xZnTl1gtjq8Nt3jZt5/Fgd9jw+cmNo/Fe14yeWxa1cnmcefaHjaPzUvqPb6dmcjiseVqO4tH 35ZVjB6fN8kFcEa52WSkJqakFimk5iXnp2TmpdsqhYa46VooKeQl5qbaKkXo+oYEKSmUJeaU AnlGBmjAwTnAPVhJ3y7BLWPX/jVMBYczKvZ8XMDWwHg0rIuRk0NCwERif/duNghbTOLCvfVA NheHkMASRok5/y+zQDhNTBIH/jxnB6liEzCU6HrbBVYlIrCfUeLDretg7cwCTcwS656C2cIC 3hKLJz0Aa2ARUJWY/bAHLM4r4CFxcd9WVoh1chInj00GszkFPCVm/D0CVM8BtM1DYuN26wmM vAsYGVYxiqSWFuem5xYb6RUn5haX5qXrJefnbmIERtC2Yz+37GDsehd8iFGAg1GJh3dCUnuE EGtiWXFl7iFGCQ5mJRFet5qOCCHelMTKqtSi/Pii0pzU4kOMpkA3TWSWEk3OB0Z3Xkm8oYmh uaWhkbGFhbmRkZI479QPV8KFBNITS1KzU1MLUotg+pg4OKUaGM863Wv1mvCAe7f5ldklW6/P qhAX+yos8Jbh+n/VaUG8B971caz8IKv7XKZB60Lo4YmHH+YETL40/b/y/L8TWiZK1B+9+Txl RYblU76DoifkVCY/aJe1sTJ2rhQXlDYu2Vi7IahkTubzX6f2m2Uyv+5e2lT8xuzqTJMPidVf 2D5fsJx1+nrMzH9KLMUZiYZazEXFiQDdwREGtgIAAA== X-MTR: 20000000000000000@CPGS X-CMS-MailID: 20170125102825eucas1p22f4f6dbe744d778f0e1b53b6b26da5de X-Msg-Generator: CA X-Sender-IP: 182.198.249.180 X-Local-Sender: =?UTF-8?B?TWFyZWsgU3p5cHJvd3NraRtTUlBPTC1LZXJuZWwgKFRQKRs=?= =?UTF-8?B?7IK87ISx7KCE7J6QG1NlbmlvciBTb2Z0d2FyZSBFbmdpbmVlcg==?= X-Global-Sender: =?UTF-8?B?TWFyZWsgU3p5cHJvd3NraRtTUlBPTC1LZXJuZWwgKFRQKRtT?= =?UTF-8?B?YW1zdW5nIEVsZWN0cm9uaWNzG1NlbmlvciBTb2Z0d2FyZSBFbmdpbmVlcg==?= X-Sender-Code: =?UTF-8?B?QzEwG0VIURtDMTBDRDAyQ0QwMjczOTI=?= CMS-TYPE: 201P X-HopCount: 7 X-CMS-RootMailID: 20170125102825eucas1p22f4f6dbe744d778f0e1b53b6b26da5de X-RootMTR: 20170125102825eucas1p22f4f6dbe744d778f0e1b53b6b26da5de References: <1485340088-25481-1-git-send-email-m.szyprowski@samsung.com> Sender: dmaengine-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: dmaengine@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This patch replaces irq-safe runtime PM with non-irq-safe version based on the new approach. Existing, irq-safe runtime PM implementation for PL330 was not bringing much benefits of its own - only clocks were enabled/disabled. Another limitation of irq-safe runtime PM is a fact, that it may prevent the generic PM domain (genpd) from being powered off, particularly in cases when the genpd doesn't have the GENPD_FLAG_IRQ_SAFE set. Till now non-irq-safe runtime PM implementation was only possible by calling pm_runtime_get/put functions from alloc/free_chan_resources. All other DMA engine API functions cannot be called from a context, which permits sleeping. Such implementation, in practice would result in keeping DMA controller's device active almost all the time, because most of the slave device drivers (DMA engine clients) acquire DMA channel in their probe() function and released it during driver removal. This patch provides a new, different approach. It is based on an observation that there can be only one slave device using each DMA channel. PL330 hardware always has dedicated channels for each peripheral device. Using recently introduced device dependencies (links) infrastructure one can ensure proper runtime PM state of PL330 DMA controller basing on the runtime PM state of the slave device. In this approach in pl330_alloc_chan_resources() function a new dependency is being created between PL330 DMA controller device (as a supplier) and given slave device (as a consumer). This way PL330 DMA controller device runtime active counter is increased when the slave device is resumed and decreased the same time when given slave device is put to suspend. This way it has been ensured to keep PL330 DMA controller runtime active if there is an active used of any of its DMA channels. Slave device pointer is initially stored in per-channel data in of_dma_xlate callback. This is similar to what has been already implemented in Exynos IOMMU driver in commit 2f5f44f205cc95 ("iommu/exynos: Use device dependency links to control runtime pm"). If slave device doesn't implement runtime PM or keeps device runtime active all the time, then PL330 DMA controller will be runtime active all the time when channel is being allocated. If one requests memory-to-memory channel, runtime active counter is increased unconditionally. This might be a drawback of this approach, but PL330 is not really used for memory-to-memory operations due to poor performance in such operations compared to the CPU. Removal of irq-safe runtime PM is based on the revert of the following commits: 1. commit 5c9e6c2b2ba3 "dmaengine: pl330: fix runtime pm support" 2. commit 81cc6edc0870 "dmaengine: pl330: Fix hang on dmaengine_terminate_all on certain boards" 3. commit ae43b3289186 "ARM: 8202/1: dmaengine: pl330: Add runtime Power Management support v12" Introducing non-irq-safe runtime power management finally allows to turn off audio power domain on Exynos5 SoCs. Signed-off-by: Marek Szyprowski Reviewed-by: Krzysztof Kozlowski Reviewed-by: Ulf Hansson Acked-by: Arnd Bergmann --- drivers/dma/pl330.c | 166 +++++++++++++++++++++------------------------------- 1 file changed, 66 insertions(+), 100 deletions(-) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index c77a3494659c..dff7228198f6 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -268,9 +268,6 @@ enum pl330_byteswap { #define NR_DEFAULT_DESC 16 -/* Delay for runtime PM autosuspend, ms */ -#define PL330_AUTOSUSPEND_DELAY 20 - /* Populated by the PL330 core driver for DMA API driver's info */ struct pl330_config { u32 periph_id; @@ -449,8 +446,8 @@ struct dma_pl330_chan { bool cyclic; /* for runtime pm tracking */ - bool active; struct device *slave; + struct device_link *slave_link; }; struct pl330_dmac { @@ -2008,7 +2005,6 @@ static void pl330_tasklet(unsigned long data) struct dma_pl330_chan *pch = (struct dma_pl330_chan *)data; struct dma_pl330_desc *desc, *_dt; unsigned long flags; - bool power_down = false; spin_lock_irqsave(&pch->lock, flags); @@ -2023,18 +2019,10 @@ static void pl330_tasklet(unsigned long data) /* Try to submit a req imm. next to the last completed cookie */ fill_queue(pch); - if (list_empty(&pch->work_list)) { - spin_lock(&pch->thread->dmac->lock); - _stop(pch->thread); - spin_unlock(&pch->thread->dmac->lock); - power_down = true; - pch->active = false; - } else { - /* Make sure the PL330 Channel thread is active */ - spin_lock(&pch->thread->dmac->lock); - _start(pch->thread); - spin_unlock(&pch->thread->dmac->lock); - } + /* Make sure the PL330 Channel thread is active */ + spin_lock(&pch->thread->dmac->lock); + _start(pch->thread); + spin_unlock(&pch->thread->dmac->lock); while (!list_empty(&pch->completed_list)) { struct dmaengine_desc_callback cb; @@ -2047,13 +2035,6 @@ static void pl330_tasklet(unsigned long data) if (pch->cyclic) { desc->status = PREP; list_move_tail(&desc->node, &pch->work_list); - if (power_down) { - pch->active = true; - spin_lock(&pch->thread->dmac->lock); - _start(pch->thread); - spin_unlock(&pch->thread->dmac->lock); - power_down = false; - } } else { desc->status = FREE; list_move_tail(&desc->node, &pch->dmac->desc_pool); @@ -2068,12 +2049,6 @@ static void pl330_tasklet(unsigned long data) } } spin_unlock_irqrestore(&pch->lock, flags); - - /* If work list empty, power down */ - if (power_down) { - pm_runtime_mark_last_busy(pch->dmac->ddma.dev); - pm_runtime_put_autosuspend(pch->dmac->ddma.dev); - } } static struct dma_chan *of_dma_pl330_xlate(struct of_phandle_args *dma_spec, @@ -2105,11 +2080,63 @@ static struct dma_chan *of_dma_pl330_xlate(struct of_phandle_args *dma_spec, return dma_get_slave_channel(&pl330->peripherals[chan_id].chan); } +static int pl330_add_slave_pm_link(struct pl330_dmac *pl330, + struct dma_pl330_chan *pch) +{ + int i; + + /* No slave device means memory-to-memory channels */ + if (!pch->slave) + return pm_runtime_get_sync(pl330->ddma.dev); + + /* + * No additional locking is needed, {alloc,free}_chan_resources + * are called under dma_list_mutex in dmaengine core + */ + for (i = 0; i < pl330->num_peripherals; i++) { + if (pl330->peripherals[i].slave == pch->slave && + pl330->peripherals[i].slave_link) { + pch->slave_link = pl330->peripherals[i].slave_link; + return 0; + } + } + + pch->slave_link = device_link_add(pch->slave, pl330->ddma.dev, + DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE); + if (!pch->slave_link) + return -ENODEV; + + return 0; +} + +static void pl330_del_slave_pm_link(struct pl330_dmac *pl330, + struct dma_pl330_chan *pch) +{ + struct device_link *link = pch->slave_link; + int i, count = 0; + + if (!pch->slave) + pm_runtime_put(pl330->ddma.dev); + + for (i = 0; i < pl330->num_peripherals; i++) + if (pl330->peripherals[i].slave_link == link) + count++; + + pch->slave_link = NULL; + if (count == 1) + device_link_del(link); +} + static int pl330_alloc_chan_resources(struct dma_chan *chan) { struct dma_pl330_chan *pch = to_pchan(chan); struct pl330_dmac *pl330 = pch->dmac; unsigned long flags; + int ret; + + ret = pl330_add_slave_pm_link(pl330, pch); + if (ret < 0) + return ret; spin_lock_irqsave(&pl330->lock, flags); @@ -2119,6 +2146,7 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan) pch->thread = pl330_request_channel(pl330); if (!pch->thread) { spin_unlock_irqrestore(&pl330->lock, flags); + pl330_del_slave_pm_link(pl330, pch); return -ENOMEM; } @@ -2160,9 +2188,7 @@ static int pl330_terminate_all(struct dma_chan *chan) unsigned long flags; struct pl330_dmac *pl330 = pch->dmac; LIST_HEAD(list); - bool power_down = false; - pm_runtime_get_sync(pl330->ddma.dev); spin_lock_irqsave(&pch->lock, flags); spin_lock(&pl330->lock); _stop(pch->thread); @@ -2171,8 +2197,6 @@ static int pl330_terminate_all(struct dma_chan *chan) pch->thread->req[0].desc = NULL; pch->thread->req[1].desc = NULL; pch->thread->req_running = -1; - power_down = pch->active; - pch->active = false; /* Mark all desc done */ list_for_each_entry(desc, &pch->submitted_list, node) { @@ -2189,10 +2213,6 @@ static int pl330_terminate_all(struct dma_chan *chan) list_splice_tail_init(&pch->work_list, &pl330->desc_pool); list_splice_tail_init(&pch->completed_list, &pl330->desc_pool); spin_unlock_irqrestore(&pch->lock, flags); - pm_runtime_mark_last_busy(pl330->ddma.dev); - if (power_down) - pm_runtime_put_autosuspend(pl330->ddma.dev); - pm_runtime_put_autosuspend(pl330->ddma.dev); return 0; } @@ -2210,7 +2230,6 @@ static int pl330_pause(struct dma_chan *chan) struct pl330_dmac *pl330 = pch->dmac; unsigned long flags; - pm_runtime_get_sync(pl330->ddma.dev); spin_lock_irqsave(&pch->lock, flags); spin_lock(&pl330->lock); @@ -2218,8 +2237,6 @@ static int pl330_pause(struct dma_chan *chan) spin_unlock(&pl330->lock); spin_unlock_irqrestore(&pch->lock, flags); - pm_runtime_mark_last_busy(pl330->ddma.dev); - pm_runtime_put_autosuspend(pl330->ddma.dev); return 0; } @@ -2232,7 +2249,6 @@ static void pl330_free_chan_resources(struct dma_chan *chan) tasklet_kill(&pch->task); - pm_runtime_get_sync(pch->dmac->ddma.dev); spin_lock_irqsave(&pl330->lock, flags); pl330_release_channel(pch->thread); @@ -2242,19 +2258,17 @@ static void pl330_free_chan_resources(struct dma_chan *chan) list_splice_tail_init(&pch->work_list, &pch->dmac->desc_pool); spin_unlock_irqrestore(&pl330->lock, flags); - pm_runtime_mark_last_busy(pch->dmac->ddma.dev); - pm_runtime_put_autosuspend(pch->dmac->ddma.dev); + + pl330_del_slave_pm_link(pl330, pch); } static int pl330_get_current_xferred_count(struct dma_pl330_chan *pch, struct dma_pl330_desc *desc) { struct pl330_thread *thrd = pch->thread; - struct pl330_dmac *pl330 = pch->dmac; void __iomem *regs = thrd->dmac->base; u32 val, addr; - pm_runtime_get_sync(pl330->ddma.dev); val = addr = 0; if (desc->rqcfg.src_inc) { val = readl(regs + SA(thrd->id)); @@ -2263,8 +2277,6 @@ static int pl330_get_current_xferred_count(struct dma_pl330_chan *pch, val = readl(regs + DA(thrd->id)); addr = desc->px.dst_addr; } - pm_runtime_mark_last_busy(pch->dmac->ddma.dev); - pm_runtime_put_autosuspend(pl330->ddma.dev); /* If DMAMOV hasn't finished yet, SAR/DAR can be zero */ if (!val) @@ -2350,16 +2362,6 @@ static void pl330_issue_pending(struct dma_chan *chan) unsigned long flags; spin_lock_irqsave(&pch->lock, flags); - if (list_empty(&pch->work_list)) { - /* - * Warn on nothing pending. Empty submitted_list may - * break our pm_runtime usage counter as it is - * updated on work_list emptiness status. - */ - WARN_ON(list_empty(&pch->submitted_list)); - pch->active = true; - pm_runtime_get_sync(pch->dmac->ddma.dev); - } list_splice_tail_init(&pch->submitted_list, &pch->work_list); spin_unlock_irqrestore(&pch->lock, flags); @@ -2787,44 +2789,12 @@ static irqreturn_t pl330_irq_handler(int irq, void *data) BIT(DMA_SLAVE_BUSWIDTH_8_BYTES) /* - * Runtime PM callbacks are provided by amba/bus.c driver. - * - * It is assumed here that IRQ safe runtime PM is chosen in probe and amba - * bus driver will only disable/enable the clock in runtime PM callbacks. + * Runtime PM callbacks are provided by amba/bus.c driver, system sleep + * suspend/resume is implemented by generic helpers, which use existing + * runtime PM callbacks. */ -static int __maybe_unused pl330_suspend(struct device *dev) -{ - struct amba_device *pcdev = to_amba_device(dev); - - pm_runtime_disable(dev); - - if (!pm_runtime_status_suspended(dev)) { - /* amba did not disable the clock */ - amba_pclk_disable(pcdev); - } - amba_pclk_unprepare(pcdev); - - return 0; -} - -static int __maybe_unused pl330_resume(struct device *dev) -{ - struct amba_device *pcdev = to_amba_device(dev); - int ret; - - ret = amba_pclk_prepare(pcdev); - if (ret) - return ret; - - if (!pm_runtime_status_suspended(dev)) - ret = amba_pclk_enable(pcdev); - - pm_runtime_enable(dev); - - return ret; -} - -static SIMPLE_DEV_PM_OPS(pl330_pm, pl330_suspend, pl330_resume); +static SIMPLE_DEV_PM_OPS(pl330_pm, pm_runtime_force_suspend, + pm_runtime_force_resume); static int pl330_probe(struct amba_device *adev, const struct amba_id *id) @@ -2977,11 +2947,7 @@ static int __maybe_unused pl330_resume(struct device *dev) pcfg->data_buf_dep, pcfg->data_bus_width / 8, pcfg->num_chan, pcfg->num_peri, pcfg->num_events); - pm_runtime_irq_safe(&adev->dev); - pm_runtime_use_autosuspend(&adev->dev); - pm_runtime_set_autosuspend_delay(&adev->dev, PL330_AUTOSUSPEND_DELAY); - pm_runtime_mark_last_busy(&adev->dev); - pm_runtime_put_autosuspend(&adev->dev); + pm_runtime_put(&adev->dev); return 0; probe_err3: