From patchwork Sat Apr 28 20:51:42 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Alexander Sverdlin X-Patchwork-Id: 10370379 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 D1E4860116 for ; Sat, 28 Apr 2018 20:56:59 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id BF80728F5B for ; Sat, 28 Apr 2018 20:56:59 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id B025B28FB8; Sat, 28 Apr 2018 20:56:59 +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=-2.9 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, DKIM_VALID, FREEMAIL_FROM, MAILING_LIST_MULTI autolearn=ham version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id F29FF28F5B for ; Sat, 28 Apr 2018 20:56:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=DDm1xrlJBzvZxrpZ74OsrO0DvkMpguuU9zW1jQZp74k=; b=Y1pBsGUnlX941r WPokrbx2aUebdARrbWIZiwDqxDrzCK+XxrM6vnaBiauGawQqyqbCcq/8Ko0LMqDpDMsTYTUvrgXvC yfWpFigemIW0vjgRhTfaZQ1MohkwvHy7N1r7s4kN3DsTAOHU/s+g9pvlZbmXmKUp5vjTsFI9gBH89 CCyWzw/HUjlbI4tO12MnmWjoHaRFIdTfVxNGij6WMm7bQhECiLc3VkMnrg7NJvBnv4W02RHmICJAT D70P3B8/Hy/6ME834PIDJAzTLir11kW6HvZhXZGHZjbK8NFvvkaSrMJUMuBu9HFcWw3isf+ztwg+G zibodQqckZLQJY7nn46w==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1fCWtk-0002V4-9R; Sat, 28 Apr 2018 20:56:48 +0000 Received: from mail-lf0-x241.google.com ([2a00:1450:4010:c07::241]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1fCWpv-0007zs-RU for linux-arm-kernel@lists.infradead.org; Sat, 28 Apr 2018 20:52:54 +0000 Received: by mail-lf0-x241.google.com with SMTP id w8-v6so7382666lfe.3 for ; Sat, 28 Apr 2018 13:52:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=gVWmq6g6UoIYLBZrxRTGihJ+r0Z9k4EaukuQP/0G8g0=; b=nD9Mv8HGPqQqGHu+JMiYLvSdfscMdfQvI1cRYdy+VBH+EADb6D38J97IDNuULYKyWg qgyGj+sh6IbKytPgYAEnxBcfqJr5derCrdYV88sNX5bLiKuCcSIfD8jISWNnLlA+qfP7 XV35yWxVoxGK6TekhMKCzFrWJLmIVvqigCgKnzF2QFY8plSA4Y8eu2trj6+q4buLWCHo qZTAj2wBfqbDtOkBjJx9O07fV1JY/X8UX3jliDzpwF1kkwiXamMV6jVqz0o0buvCm2iu +3Et9bb4IhMJE5fG17ZaD5jHjsd2yQf/iEOIF5lXnEXLH1W1/Cb/GrUkWbu6dDzBtliA rMtw== 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:mime-version:content-transfer-encoding; bh=gVWmq6g6UoIYLBZrxRTGihJ+r0Z9k4EaukuQP/0G8g0=; b=GFLe1Ir04AgtCq4JfBDGwTy6RYwiMVFZyXajf+NM4EmJDCn8AM7s7HWxaTZe72rQ/s 3moqCcSllatXSdldZmwalIVj08+ZQOYDACDw6Dh7SClZtwJduc5jJNbzxctenMQjFPpw S9zDZu+Q+Qt8k58VCkcBCBZI9KpDlduC++olBE729QcJLq3LF6eM8DiTfkEIV7rM8h69 nzX1icbGyLy6hxzGvRRMcBMXWlXYamVvqYVgOqp4K8cQrupExmbgWPTav40HP3q4rwVj ZcBTD6xEQc81HbIlhwHGTA96lbs+4m2CuF8zh3VmtLSgm14ERAWv+ztWy+DR2Nno6UqD vXeg== X-Gm-Message-State: ALQs6tAV/uDvVeGOzTDRUAgiBZY8Gwudj7ekUBu3/JCl2C1tnz2POoWZ C1jnIe5dHOGjjq15fnMb/+s= X-Google-Smtp-Source: AB8JxZr2aL7al8GWH/zJGYgkeig3YJ+5Ip8yPUPTD96i1TtxJvadA7oqoHOg1QBziRSq2mL2zglaPQ== X-Received: by 2002:a2e:594e:: with SMTP id n75-v6mr4507305ljb.139.1524948761412; Sat, 28 Apr 2018 13:52:41 -0700 (PDT) Received: from giga1.localdomain ([195.245.44.8]) by smtp.gmail.com with ESMTPSA id t24-v6sm852578ljg.65.2018.04.28.13.52.39 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sat, 28 Apr 2018 13:52:40 -0700 (PDT) From: Alexander Sverdlin To: alsa-devel@alsa-project.org Subject: [PATCH 5/5] ASoC: cirrus: i2s: IRQ-based stream watchdog Date: Sat, 28 Apr 2018 22:51:42 +0200 Message-Id: <20180428205142.31921-6-alexander.sverdlin@gmail.com> X-Mailer: git-send-email 2.16.2 In-Reply-To: <20180428205142.31921-1-alexander.sverdlin@gmail.com> References: <20180428205142.31921-1-alexander.sverdlin@gmail.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20180428_135251_957415_839F282E X-CRM114-Status: GOOD ( 18.77 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Ryan Mallon , Takashi Iwai , Liam Girdwood , Hartley Sweeten , Mark Brown , Jaroslav Kysela , Alexander Sverdlin , linux-arm-kernel@lists.infradead.org Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP I2S controller on EP93xx seems to have undocumented HW issue. According to "EP93xx User’s Guide", controller can handle underflow and either transmit last sample or zeroes in such case until FIFO is filled again. In reality undeflow conditions seem to confuse internal state machine from time to time and the whole stream gets shifted by one byte (as captured by logic analyser on the I2S outputs). One could only hear noise instead of original stream and this continues until the FIFO is disabled and enabled again. Work this around by watching underflow interrupt and resetting I2S TX channel + fill FIFO with zero samples until DMA catches up again. This is a nasty workaround, but it works. Hence, Kconfig option to disable it in case of problems. Signed-off-by: Alexander Sverdlin --- sound/soc/cirrus/Kconfig | 17 +++++++++++ sound/soc/cirrus/ep93xx-i2s.c | 68 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/sound/soc/cirrus/Kconfig b/sound/soc/cirrus/Kconfig index c7cd60f009e9..e09199124c36 100644 --- a/sound/soc/cirrus/Kconfig +++ b/sound/soc/cirrus/Kconfig @@ -9,6 +9,23 @@ config SND_EP93XX_SOC config SND_EP93XX_SOC_I2S tristate +if SND_EP93XX_SOC_I2S + +config SND_EP93XX_SOC_I2S_WATCHDOG + bool "IRQ based underflow watchdog workaround" + default y + help + I2S controller on EP93xx seems to have undocumented HW issue. + Underflow of internal I2S controller FIFO could confuse the + state machine and the whole stream can be shifted by one byte + until I2S is disabled. This option enables IRQ based watchdog + which disables and re-enables I2S in case of underflow and + fills FIFO with zeroes. + + If you are unsure how to answer this question, answer Y. + +endif # if SND_EP93XX_SOC_I2S + config SND_EP93XX_SOC_AC97 tristate select AC97_BUS diff --git a/sound/soc/cirrus/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c index 42cc6d22baac..0918c5da575a 100644 --- a/sound/soc/cirrus/ep93xx-i2s.c +++ b/sound/soc/cirrus/ep93xx-i2s.c @@ -35,8 +35,12 @@ #define EP93XX_I2S_TXCLKCFG 0x00 #define EP93XX_I2S_RXCLKCFG 0x04 +#define EP93XX_I2S_GLSTS 0x08 #define EP93XX_I2S_GLCTRL 0x0C +#define EP93XX_I2S_I2STX0LFT 0x10 +#define EP93XX_I2S_I2STX0RT 0x14 + #define EP93XX_I2S_TXLINCTRLDATA 0x28 #define EP93XX_I2S_TXCTRL 0x2C #define EP93XX_I2S_TXWRDLEN 0x30 @@ -55,12 +59,22 @@ #define EP93XX_I2S_TXLINCTRLDATA_R_JUST BIT(2) /* Right justify */ +/* + * Transmit empty interrupt level select: + * 0 - Generate interrupt when FIFO is half empty + * 1 - Generate interrupt when FIFO is empty + */ +#define EP93XX_I2S_TXCTRL_TXEMPTY_LVL BIT(0) +#define EP93XX_I2S_TXCTRL_TXUFIE BIT(1) /* Transmit interrupt enable */ + #define EP93XX_I2S_CLKCFG_LRS (1 << 0) /* lrclk polarity */ #define EP93XX_I2S_CLKCFG_CKP (1 << 1) /* Bit clock polarity */ #define EP93XX_I2S_CLKCFG_REL (1 << 2) /* First bit transition */ #define EP93XX_I2S_CLKCFG_MASTER (1 << 3) /* Master mode */ #define EP93XX_I2S_CLKCFG_NBCG (1 << 4) /* Not bit clock gating */ +#define EP93XX_I2S_GLSTS_TX0_FIFO_FULL BIT(12) + struct ep93xx_i2s_info { struct clk *mclk; struct clk *sclk; @@ -116,12 +130,24 @@ static void ep93xx_i2s_enable(struct ep93xx_i2s_info *info, int stream) else base_reg = EP93XX_I2S_RX0EN; ep93xx_i2s_write_reg(info, base_reg, 1); + + /* Enable TX IRQs (FIFO empty or underflow) */ + if (IS_ENABLED(CONFIG_SND_EP93XX_SOC_I2S_WATCHDOG) && + stream == SNDRV_PCM_STREAM_PLAYBACK) + ep93xx_i2s_write_reg(info, EP93XX_I2S_TXCTRL, + EP93XX_I2S_TXCTRL_TXEMPTY_LVL | + EP93XX_I2S_TXCTRL_TXUFIE); } static void ep93xx_i2s_disable(struct ep93xx_i2s_info *info, int stream) { unsigned base_reg; + /* Disable IRQs */ + if (IS_ENABLED(CONFIG_SND_EP93XX_SOC_I2S_WATCHDOG) && + stream == SNDRV_PCM_STREAM_PLAYBACK) + ep93xx_i2s_write_reg(info, EP93XX_I2S_TXCTRL, 0); + /* Disable fifo */ if (stream == SNDRV_PCM_STREAM_PLAYBACK) base_reg = EP93XX_I2S_TX0EN; @@ -141,6 +167,37 @@ static void ep93xx_i2s_disable(struct ep93xx_i2s_info *info, int stream) } } +/* + * According to documentation I2S controller can handle underflow conditions + * just fine, but in reality the state machine is sometimes confused so that + * the whole stream is shifted by one byte. The watchdog below disables the TX + * FIFO, fills the buffer with zeroes and re-enables the FIFO. State machine + * is being reset and by filling the buffer we get some time before next + * underflow happens. + */ +static irqreturn_t ep93xx_i2s_interrupt(int irq, void *dev_id) +{ + struct ep93xx_i2s_info *info = dev_id; + + /* Disable FIFO */ + ep93xx_i2s_write_reg(info, EP93XX_I2S_TX0EN, 0); + /* + * Fill TX FIFO with zeroes, this way we can defer next IRQs as much as + * possible and get more time for DMA to catch up. Actually there are + * only 8 samples in this FIFO, so even on 8kHz maximum deferral here is + * 1ms. + */ + while (!(ep93xx_i2s_read_reg(info, EP93XX_I2S_GLSTS) & + EP93XX_I2S_GLSTS_TX0_FIFO_FULL)) { + ep93xx_i2s_write_reg(info, EP93XX_I2S_I2STX0LFT, 0); + ep93xx_i2s_write_reg(info, EP93XX_I2S_I2STX0RT, 0); + } + /* Re-enable FIFO */ + ep93xx_i2s_write_reg(info, EP93XX_I2S_TX0EN, 1); + + return IRQ_HANDLED; +} + static int ep93xx_i2s_dai_probe(struct snd_soc_dai *dai) { struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai); @@ -390,6 +447,17 @@ static int ep93xx_i2s_probe(struct platform_device *pdev) if (IS_ERR(info->regs)) return PTR_ERR(info->regs); + if (IS_ENABLED(CONFIG_SND_EP93XX_SOC_I2S_WATCHDOG)) { + int irq = platform_get_irq(pdev, 0); + if (irq <= 0) + return irq < 0 ? irq : -ENODEV; + + err = devm_request_irq(&pdev->dev, irq, ep93xx_i2s_interrupt, 0, + pdev->name, info); + if (err) + return err; + } + info->mclk = clk_get(&pdev->dev, "mclk"); if (IS_ERR(info->mclk)) { err = PTR_ERR(info->mclk);