From patchwork Mon Jan 30 12:06:42 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Claudiu Beznea X-Patchwork-Id: 13121523 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id CE741C54EED for ; Mon, 30 Jan 2023 17:52:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:CC:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=LK0pAYaeZ9gfl+bjFGtg4kxUcsrUkcBPMWQdqb3JiGw=; b=stO3EHRJKUUiLc YGDUPWjUuQVK6zYn/7knTHSBCEidGDcHyK3Gq7asWh5IvVTrQe8Yfzq2eAQ1keplmB6pPY6tHMICD sCCFc8dx5hgZqFbFEocQykUu+RQCrA9n0P1VnnfMlwquFN4yOwSIYDm4FSSUextdZXUnVJ9ZROT6+ V++Mgqpt/BOW7jEtEprbNn5g3uaW2jpREkcDWUzJhXBrEigJbWiRiOW0vyrDMMXpr+YNYFM4mjmcH FReSWd9Myph7E1WwIhixxbpyQuoAjXClniZ+aC7bSl2WGhOtIF0zYPUzElCNIL4Pwuopn5LcqcKfc 4ZkJXqbwJaTnNcqTewyQ==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1pMYJZ-004iup-PF; Mon, 30 Jan 2023 17:51:35 +0000 Received: from esa.microchip.iphmx.com ([68.232.154.123]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1pMYH1-004hoy-UW for linux-arm-kernel@lists.infradead.org; Mon, 30 Jan 2023 17:48:57 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=microchip.com; i=@microchip.com; q=dns/txt; s=mchp; t=1675100935; x=1706636935; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=zxwq9uIaU8bzolvs0Ucl4pALHPjSjFBI0eSgfnpCcZo=; b=kEO99hAai1J0nfB+D41lGLhkEzBRkIgUTZaDNk9bY8vXb7HX3mozqWGA x56mMPzRCav00r/6hIZ1RU4pNGLmJRcMjrlwfLfvZ0PrcMSHXaoGIbW/P siR7PSubZ12hGP9C/oHxVENtYp8a/wUJ4ZY/cf829r9F67OotM5l62qKa iMDlZFoaaekMlCSUyHALfVTgPforjl1K6r038I7Gqsh/7lzlysI4CGj3v dh4wiLF4KcA/zcCUQh6STVr+0Dsc9rsYXlcHBKwdaMKx0b5MtGbDR4IGr jxaJ2HNKEDRfHQypzGP2oqof5URfww8HM3cV4owVwZ3DUQafMPwzx+Ire A==; X-IronPort-AV: E=Sophos;i="5.97,257,1669100400"; d="scan'208";a="134605293" Received: from unknown (HELO email.microchip.com) ([170.129.1.10]) by esa6.microchip.iphmx.com with ESMTP/TLS/AES256-SHA256; 30 Jan 2023 05:07:34 -0700 Received: from chn-vm-ex04.mchp-main.com (10.10.85.152) by chn-vm-ex04.mchp-main.com (10.10.85.152) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.16; Mon, 30 Jan 2023 05:07:32 -0700 Received: from m18063-ThinkPad-T460p.mchp-main.com (10.10.115.15) by chn-vm-ex04.mchp-main.com (10.10.85.152) with Microsoft SMTP Server id 15.1.2507.16 via Frontend Transport; Mon, 30 Jan 2023 05:07:28 -0700 From: Claudiu Beznea To: , , , , , CC: , , , Claudiu Beznea Subject: [PATCH 3/8] ASoC: mchp-spdifrx: fix controls that works with completion mechanism Date: Mon, 30 Jan 2023 14:06:42 +0200 Message-ID: <20230130120647.638049-4-claudiu.beznea@microchip.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230130120647.638049-1-claudiu.beznea@microchip.com> References: <20230130120647.638049-1-claudiu.beznea@microchip.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20230130_094856_074936_C61207DF X-CRM114-Status: GOOD ( 26.78 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org Channel status get and channel subcode get controls relies on data returned by controls when certain IRQs are raised. To achieve that completions are used b/w controls and interrupt service routine. The concurrent accesses to these controls are protected by struct snd_card::controls_rwsem. Issues identified: - reinit_completion() may be called while waiting for completion which should be avoided - in case of multiple threads waiting, the complete() call in interrupt will signal only one waiting thread per interrupt which may lead to timeout for the others - in case of channel status get as the CSC interrupt is not refcounted ISR may disable interrupt for threads that were just enabled it. To solve these the access to controls were protected by a mutex. Along with this there is no need for spinlock to protect the software cache reads/updates b/w controls and ISR as the update is happening only when requested from control, and only one reader can reach the control. Fixes: ef265c55c1ac ("ASoC: mchp-spdifrx: add driver for SPDIF RX") Signed-off-by: Claudiu Beznea --- sound/soc/atmel/mchp-spdifrx.c | 143 ++++++++++++++++++--------------- 1 file changed, 77 insertions(+), 66 deletions(-) diff --git a/sound/soc/atmel/mchp-spdifrx.c b/sound/soc/atmel/mchp-spdifrx.c index 7f359371b31b..31ffaaf46dec 100644 --- a/sound/soc/atmel/mchp-spdifrx.c +++ b/sound/soc/atmel/mchp-spdifrx.c @@ -217,7 +217,6 @@ struct mchp_spdifrx_ch_stat { struct mchp_spdifrx_user_data { unsigned char data[SPDIFRX_UD_BITS / 8]; struct completion done; - spinlock_t lock; /* protect access to user data */ }; struct mchp_spdifrx_mixer_control { @@ -231,8 +230,6 @@ struct mchp_spdifrx_mixer_control { struct mchp_spdifrx_dev { struct snd_dmaengine_dai_dma_data capture; struct mchp_spdifrx_mixer_control control; - spinlock_t blockend_lock; /* protect access to blockend_refcount */ - int blockend_refcount; struct mutex mlock; struct device *dev; struct regmap *regmap; @@ -277,37 +274,11 @@ static void mchp_spdifrx_channel_user_data_read(struct mchp_spdifrx_dev *dev, } } -/* called from non-atomic context only */ -static void mchp_spdifrx_isr_blockend_en(struct mchp_spdifrx_dev *dev) -{ - unsigned long flags; - - spin_lock_irqsave(&dev->blockend_lock, flags); - dev->blockend_refcount++; - /* don't enable BLOCKEND interrupt if it's already enabled */ - if (dev->blockend_refcount == 1) - regmap_write(dev->regmap, SPDIFRX_IER, SPDIFRX_IR_BLOCKEND); - spin_unlock_irqrestore(&dev->blockend_lock, flags); -} - -/* called from atomic/non-atomic context */ -static void mchp_spdifrx_isr_blockend_dis(struct mchp_spdifrx_dev *dev) -{ - unsigned long flags; - - spin_lock_irqsave(&dev->blockend_lock, flags); - dev->blockend_refcount--; - /* don't enable BLOCKEND interrupt if it's already enabled */ - if (dev->blockend_refcount == 0) - regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_BLOCKEND); - spin_unlock_irqrestore(&dev->blockend_lock, flags); -} - static irqreturn_t mchp_spdif_interrupt(int irq, void *dev_id) { struct mchp_spdifrx_dev *dev = dev_id; struct mchp_spdifrx_mixer_control *ctrl = &dev->control; - u32 sr, imr, pending, idr = 0; + u32 sr, imr, pending; irqreturn_t ret = IRQ_NONE; int ch; @@ -322,13 +293,10 @@ static irqreturn_t mchp_spdif_interrupt(int irq, void *dev_id) if (pending & SPDIFRX_IR_BLOCKEND) { for (ch = 0; ch < SPDIFRX_CHANNELS; ch++) { - spin_lock(&ctrl->user_data[ch].lock); mchp_spdifrx_channel_user_data_read(dev, ch); - spin_unlock(&ctrl->user_data[ch].lock); - complete(&ctrl->user_data[ch].done); } - mchp_spdifrx_isr_blockend_dis(dev); + regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_BLOCKEND); ret = IRQ_HANDLED; } @@ -336,7 +304,7 @@ static irqreturn_t mchp_spdif_interrupt(int irq, void *dev_id) if (pending & SPDIFRX_IR_CSC(ch)) { mchp_spdifrx_channel_status_read(dev, ch); complete(&ctrl->ch_stat[ch].done); - idr |= SPDIFRX_IR_CSC(ch); + regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_CSC(ch)); ret = IRQ_HANDLED; } } @@ -346,8 +314,6 @@ static irqreturn_t mchp_spdif_interrupt(int irq, void *dev_id) ret = IRQ_HANDLED; } - regmap_write(dev->regmap, SPDIFRX_IDR, idr); - return ret; } @@ -517,23 +483,51 @@ static int mchp_spdifrx_cs_get(struct mchp_spdifrx_dev *dev, { struct mchp_spdifrx_mixer_control *ctrl = &dev->control; struct mchp_spdifrx_ch_stat *ch_stat = &ctrl->ch_stat[channel]; - int ret; + int ret = 0; + + mutex_lock(&dev->mlock); - regmap_write(dev->regmap, SPDIFRX_IER, SPDIFRX_IR_CSC(channel)); - /* check for new data available */ - ret = wait_for_completion_interruptible_timeout(&ch_stat->done, - msecs_to_jiffies(100)); - /* IP might not be started or valid stream might not be present */ - if (ret <= 0) { - dev_dbg(dev->dev, "channel status for channel %d timeout\n", - channel); - return ret ? : -ETIMEDOUT; + /* + * We may reach this point with both clocks enabled but the receiver + * still disabled. To void waiting for completion and return with + * timeout check the dev->trigger_enabled. + * + * To retrieve data: + * - if the receiver is enabled CSC IRQ will update the data in software + * caches (ch_stat->data) + * - otherwise we just update it here the software caches with latest + * available information and return it; in this case we don't need + * spin locking as the IRQ is disabled and will not be raised from + * anywhere else. + */ + + if (dev->trigger_enabled) { + reinit_completion(&ch_stat->done); + regmap_write(dev->regmap, SPDIFRX_IER, SPDIFRX_IR_CSC(channel)); + /* Check for new data available */ + ret = wait_for_completion_interruptible_timeout(&ch_stat->done, + msecs_to_jiffies(100)); + /* Valid stream might not be present */ + if (ret <= 0) { + dev_dbg(dev->dev, "channel status for channel %d timeout\n", + channel); + regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_CSC(channel)); + ret = ret ? : -ETIMEDOUT; + goto unlock; + } else { + ret = 0; + } + } else { + /* Update software cache with latest channel status. */ + mchp_spdifrx_channel_status_read(dev, channel); } memcpy(uvalue->value.iec958.status, ch_stat->data, sizeof(ch_stat->data)); - return 0; +unlock: + mutex_unlock(&dev->mlock); + return ret; } static int mchp_spdifrx_cs1_get(struct snd_kcontrol *kcontrol, @@ -567,29 +561,49 @@ static int mchp_spdifrx_subcode_ch_get(struct mchp_spdifrx_dev *dev, int channel, struct snd_ctl_elem_value *uvalue) { - unsigned long flags; struct mchp_spdifrx_mixer_control *ctrl = &dev->control; struct mchp_spdifrx_user_data *user_data = &ctrl->user_data[channel]; - int ret; + int ret = 0; + + mutex_lock(&dev->mlock); + + /* + * We may reach this point with both clocks enabled but the receiver + * still disabled. To void waiting for completion to just timeout we + * check here the dev->trigger_enabled flag. + * + * To retrieve data: + * - if the receiver is enabled we need to wait for blockend IRQ to read + * data to and update it for us in software caches + * - otherwise reading the SPDIFRX_CHUD() registers is enough. + */ - reinit_completion(&user_data->done); - mchp_spdifrx_isr_blockend_en(dev); - ret = wait_for_completion_interruptible_timeout(&user_data->done, - msecs_to_jiffies(100)); - /* IP might not be started or valid stream might not be present */ - if (ret <= 0) { - dev_dbg(dev->dev, "user data for channel %d timeout\n", - channel); - mchp_spdifrx_isr_blockend_dis(dev); - return ret ? : -ETIMEDOUT; + if (dev->trigger_enabled) { + reinit_completion(&user_data->done); + regmap_write(dev->regmap, SPDIFRX_IER, SPDIFRX_IR_BLOCKEND); + ret = wait_for_completion_interruptible_timeout(&user_data->done, + msecs_to_jiffies(100)); + /* Valid stream might not be present. */ + if (ret <= 0) { + dev_dbg(dev->dev, "user data for channel %d timeout\n", + channel); + regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_BLOCKEND); + ret = ret ? : -ETIMEDOUT; + goto unlock; + } else { + ret = 0; + } + } else { + /* Update software cache with last available data. */ + mchp_spdifrx_channel_user_data_read(dev, channel); } - spin_lock_irqsave(&user_data->lock, flags); memcpy(uvalue->value.iec958.subcode, user_data->data, sizeof(user_data->data)); - spin_unlock_irqrestore(&user_data->lock, flags); - return 0; +unlock: + mutex_unlock(&dev->mlock); + return ret; } static int mchp_spdifrx_subcode_ch1_get(struct snd_kcontrol *kcontrol, @@ -890,11 +904,9 @@ static int mchp_spdifrx_dai_probe(struct snd_soc_dai *dai) SPDIFRX_MR_AUTORST_NOACTION | SPDIFRX_MR_PACK_DISABLED); - dev->blockend_refcount = 0; for (ch = 0; ch < SPDIFRX_CHANNELS; ch++) { init_completion(&ctrl->ch_stat[ch].done); init_completion(&ctrl->user_data[ch].done); - spin_lock_init(&ctrl->user_data[ch].lock); } /* Add controls */ @@ -1005,7 +1017,6 @@ static int mchp_spdifrx_probe(struct platform_device *pdev) */ clk_set_min_rate(dev->gclk, 48000 * SPDIFRX_GCLK_RATIO_MIN + 1); - spin_lock_init(&dev->blockend_lock); mutex_init(&dev->mlock); dev->dev = &pdev->dev;