From patchwork Wed Sep 23 17:03:01 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alex Deucher X-Patchwork-Id: 7251361 Return-Path: X-Original-To: patchwork-alsa-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 3A27FBEEC1 for ; Wed, 23 Sep 2015 17:04:53 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id EA26F20977 for ; Wed, 23 Sep 2015 17:04:51 +0000 (UTC) Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) by mail.kernel.org (Postfix) with ESMTP id 44A14209F2 for ; Wed, 23 Sep 2015 17:04:50 +0000 (UTC) Received: by alsa0.perex.cz (Postfix, from userid 1000) id 6A54F26586C; Wed, 23 Sep 2015 19:04:49 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Spam-Level: X-Spam-Status: No, score=-2.5 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_LOW, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from alsa0.perex.cz (localhost [IPv6:::1]) by alsa0.perex.cz (Postfix) with ESMTP id 08A13265158; Wed, 23 Sep 2015 19:03:28 +0200 (CEST) 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 9684E26053B; Wed, 23 Sep 2015 19:03:26 +0200 (CEST) Received: from mail-qg0-f42.google.com (mail-qg0-f42.google.com [209.85.192.42]) by alsa0.perex.cz (Postfix) with ESMTP id 35DC826053B for ; Wed, 23 Sep 2015 19:03:16 +0200 (CEST) Received: by qgt47 with SMTP id 47so24356277qgt.2 for ; Wed, 23 Sep 2015 10:03:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=MW6OUxBN2CnI9YDTIQm6hPeJjzQTW0ZdTU9kQh/eDfg=; b=igLHARSSVQ4CbVX3Kf8W93msRJNuSrqenTwoHpf+wk8sxLB+4qrZGjwP0sBdMnrCbm e5mG1f3qcM6LHWg9pvs0dT0q+GQBlZePpDXZ/S9yqZuF+39tINxhr5LxjUzrrMlEaMsW bqrzpYBtaXhkevt1M1WqbH+J0cqTvouz5m6XiWWtbUgLa8yNuTQjqcCQHzUE+bPfDjkE Hd6ebgcDgNV1l9OR3AfHdcvZyKnfrk9vWf+5dE4DW8HBGA/0Bta2G70HmkzF5gRkVB/Z nPoPCg3QH1YPbKRXJmHlig9P8xZ6oOc03ro8ptlIo3t5zvT0LgOW08cUAjBXSXfGfI0j tbBA== X-Received: by 10.140.235.216 with SMTP id g207mr39284316qhc.98.1443027795622; Wed, 23 Sep 2015 10:03:15 -0700 (PDT) Received: from localhost.localdomain (static-74-96-105-49.washdc.fios.verizon.net. [74.96.105.49]) by smtp.gmail.com with ESMTPSA id 68sm1813151qgy.16.2015.09.23.10.03.14 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 23 Sep 2015 10:03:15 -0700 (PDT) From: Alex Deucher X-Google-Original-From: Alex Deucher To: broonie@kernel.org, airlied@gmail.com, dri-devel@lists.freedesktop.org, alsa-devel@alsa-project.org, maruthi.bayyavarapu@amd.com, rajeevkumar.linux@gmail.com Date: Wed, 23 Sep 2015 13:03:01 -0400 Message-Id: <1443027783-2591-4-git-send-email-alexander.deucher@amd.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1443027783-2591-1-git-send-email-alexander.deucher@amd.com> References: <1443027783-2591-1-git-send-email-alexander.deucher@amd.com> Cc: tiwai@suse.de, Maruthi Srinivas Bayyavarapu , lgirdwood@gmail.com Subject: [alsa-devel] [PATCH 2/5] ASoC : dwc : support dw i2s in AMD platform 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 X-Virus-Scanned: ClamAV using ClamSMTP From: Maruthi Srinivas Bayyavarapu Vendor specific quirk was added to: 1. Support AMD platform which has two dwc controllers with different base address for playback and capture. Also, I2S_COMP_PARAM_* registers offsets differed. 2. Resume audio which was active before system suspend. After 'resume', dwc need to be reconfigured with params configured during 'hw_params' callback. With this, audio usecase continues from where it got stopped because of 'suspend'. Signed-off-by: Maruthi Bayyavarapu --- include/sound/designware_i2s.h | 3 + sound/soc/dwc/designware_i2s.c | 190 +++++++++++++++++++++++++++-------------- 2 files changed, 127 insertions(+), 66 deletions(-) diff --git a/include/sound/designware_i2s.h b/include/sound/designware_i2s.h index 08e3418..546d1a3 100644 --- a/include/sound/designware_i2s.h +++ b/include/sound/designware_i2s.h @@ -43,6 +43,9 @@ struct i2s_platform_data { int channel; u32 snd_fmts; u32 snd_rates; + #define DW_I2S_VENDOR_AMD (1 << 0) + u32 quirks; + void *play_dma_data; void *capture_dma_data; diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index e3dcaca..cade67e 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -89,11 +89,14 @@ union dw_i2s_snd_dma_data { }; struct dw_i2s_dev { - void __iomem *i2s_base; + void __iomem *i2s_pbase; + void __iomem *i2s_cbase; struct clk *clk; int active; unsigned int capability; struct device *dev; + u32 ccr; + u32 xfer_resolution; /* data related to DMA transfers b/w i2s and DMAC */ union dw_i2s_snd_dma_data play_dma_data; @@ -118,10 +121,10 @@ static inline void i2s_disable_channels(struct dw_i2s_dev *dev, u32 stream) if (stream == SNDRV_PCM_STREAM_PLAYBACK) { for (i = 0; i < 4; i++) - i2s_write_reg(dev->i2s_base, TER(i), 0); + i2s_write_reg(dev->i2s_pbase, TER(i), 0); } else { for (i = 0; i < 4; i++) - i2s_write_reg(dev->i2s_base, RER(i), 0); + i2s_write_reg(dev->i2s_cbase, RER(i), 0); } } @@ -131,25 +134,25 @@ static inline void i2s_clear_irqs(struct dw_i2s_dev *dev, u32 stream) if (stream == SNDRV_PCM_STREAM_PLAYBACK) { for (i = 0; i < 4; i++) - i2s_write_reg(dev->i2s_base, TOR(i), 0); + i2s_write_reg(dev->i2s_pbase, TOR(i), 0); } else { for (i = 0; i < 4; i++) - i2s_write_reg(dev->i2s_base, ROR(i), 0); + i2s_write_reg(dev->i2s_cbase, ROR(i), 0); } } static void i2s_start(struct dw_i2s_dev *dev, struct snd_pcm_substream *substream) { - - i2s_write_reg(dev->i2s_base, IER, 1); - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - i2s_write_reg(dev->i2s_base, ITER, 1); - else - i2s_write_reg(dev->i2s_base, IRER, 1); - - i2s_write_reg(dev->i2s_base, CER, 1); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + i2s_write_reg(dev->i2s_pbase, IER, 1); + i2s_write_reg(dev->i2s_pbase, ITER, 1); + i2s_write_reg(dev->i2s_pbase, CER, 1); + } else { + i2s_write_reg(dev->i2s_cbase, IER, 1); + i2s_write_reg(dev->i2s_cbase, IRER, 1); + i2s_write_reg(dev->i2s_cbase, CER, 1); + } } static void i2s_stop(struct dw_i2s_dev *dev, @@ -157,26 +160,40 @@ static void i2s_stop(struct dw_i2s_dev *dev, { u32 i = 0, irq; + const struct i2s_platform_data *pdata = dev_get_platdata(dev->dev); + i2s_clear_irqs(dev, substream->stream); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - i2s_write_reg(dev->i2s_base, ITER, 0); - + i2s_write_reg(dev->i2s_pbase, ITER, 0); for (i = 0; i < 4; i++) { - irq = i2s_read_reg(dev->i2s_base, IMR(i)); - i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x30); + irq = i2s_read_reg(dev->i2s_pbase, IMR(i)); + i2s_write_reg(dev->i2s_pbase, IMR(i), + irq | 0x30); } } else { - i2s_write_reg(dev->i2s_base, IRER, 0); + i2s_write_reg(dev->i2s_cbase, IRER, 0); for (i = 0; i < 4; i++) { - irq = i2s_read_reg(dev->i2s_base, IMR(i)); - i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x03); + irq = i2s_read_reg(dev->i2s_cbase, IMR(i)); + i2s_write_reg(dev->i2s_cbase, IMR(i), + irq | 0x03); } } - if (!dev->active) { - i2s_write_reg(dev->i2s_base, CER, 0); - i2s_write_reg(dev->i2s_base, IER, 0); + if (pdata->quirks & DW_I2S_VENDOR_AMD) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + i2s_write_reg(dev->i2s_pbase, CER, 0); + i2s_write_reg(dev->i2s_pbase, IER, 0); + } else { + i2s_write_reg(dev->i2s_cbase, CER, 0); + i2s_write_reg(dev->i2s_cbase, IER, 0); + } + + } else { + if (!dev->active) { + i2s_write_reg(dev->i2s_pbase, CER, 0); + i2s_write_reg(dev->i2s_pbase, IER, 0); + } } } @@ -204,32 +221,60 @@ static int dw_i2s_startup(struct snd_pcm_substream *substream, return 0; } +static void dw_i2s_config(struct dw_i2s_dev *dev, int stream) +{ + u32 ch_reg, irq; + struct i2s_clk_config_data *config = &dev->config; + + + i2s_disable_channels(dev, stream); + + for (ch_reg = 0; ch_reg < (config->chan_nr / 2); ch_reg++) { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + i2s_write_reg(dev->i2s_pbase, TCR(ch_reg), + dev->xfer_resolution); + i2s_write_reg(dev->i2s_pbase, TFCR(ch_reg), 0x02); + irq = i2s_read_reg(dev->i2s_pbase, IMR(ch_reg)); + i2s_write_reg(dev->i2s_pbase, IMR(ch_reg), irq & ~0x30); + i2s_write_reg(dev->i2s_pbase, TER(ch_reg), 1); + } else { + i2s_write_reg(dev->i2s_cbase, RCR(ch_reg), + dev->xfer_resolution); + i2s_write_reg(dev->i2s_cbase, RFCR(ch_reg), 0x07); + irq = i2s_read_reg(dev->i2s_cbase, IMR(ch_reg)); + i2s_write_reg(dev->i2s_cbase, IMR(ch_reg), irq & ~0x03); + i2s_write_reg(dev->i2s_cbase, RER(ch_reg), 1); + } + + } +} + static int dw_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); struct i2s_clk_config_data *config = &dev->config; const struct i2s_platform_data *pdata = dev_get_platdata(dai->dev); - u32 ccr, xfer_resolution, ch_reg, irq; + int ret; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: config->data_width = 16; - ccr = 0x00; - xfer_resolution = 0x02; + dev->ccr = 0x00; + dev->xfer_resolution = 0x02; break; case SNDRV_PCM_FORMAT_S24_LE: config->data_width = 24; - ccr = 0x08; - xfer_resolution = 0x04; + dev->ccr = 0x08; + dev->xfer_resolution = 0x04; break; case SNDRV_PCM_FORMAT_S32_LE: config->data_width = 32; - ccr = 0x10; - xfer_resolution = 0x05; + dev->ccr = 0x10; + dev->xfer_resolution = 0x05; break; default: @@ -250,31 +295,13 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - i2s_disable_channels(dev, substream->stream); - - for (ch_reg = 0; ch_reg < (config->chan_nr / 2); ch_reg++) { - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - i2s_write_reg(dev->i2s_base, TCR(ch_reg), - xfer_resolution); - i2s_write_reg(dev->i2s_base, TFCR(ch_reg), 0x02); - irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg)); - i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x30); - i2s_write_reg(dev->i2s_base, TER(ch_reg), 1); - } else { - i2s_write_reg(dev->i2s_base, RCR(ch_reg), - xfer_resolution); - i2s_write_reg(dev->i2s_base, RFCR(ch_reg), 0x07); - irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg)); - i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x03); - i2s_write_reg(dev->i2s_base, RER(ch_reg), 1); - } - } - - i2s_write_reg(dev->i2s_base, CCR, ccr); + dw_i2s_config(dev, substream->stream); config->sample_rate = params_rate(params); if (!(pdata->cap & DW_I2S_SLAVE)) { + i2s_write_reg(dev->i2s_pbase, CCR, dev->ccr); + if (dev->i2s_clk_cfg) { ret = dev->i2s_clk_cfg(config); if (ret < 0) { @@ -288,7 +315,7 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream, ret = clk_set_rate(dev->clk, bitclk); if (ret) { dev_err(dev->dev, "Can't set I2S clock rate: %d\n", - ret); + ret); return ret; } } @@ -308,9 +335,9 @@ static int dw_i2s_prepare(struct snd_pcm_substream *substream, struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - i2s_write_reg(dev->i2s_base, TXFFR, 1); + i2s_write_reg(dev->i2s_pbase, TXFFR, 1); else - i2s_write_reg(dev->i2s_base, RXFFR, 1); + i2s_write_reg(dev->i2s_cbase, RXFFR, 1); return 0; } @@ -373,6 +400,13 @@ static int dw_i2s_resume(struct snd_soc_dai *dai) if (!(pdata->cap & DW_I2S_SLAVE)) clk_enable(dev->clk); + + if (pdata->quirks & DW_I2S_VENDOR_AMD) { + if (dai->playback_active) + dw_i2s_config(dev, SNDRV_PCM_STREAM_PLAYBACK); + if (dai->capture_active) + dw_i2s_config(dev, SNDRV_PCM_STREAM_CAPTURE); + } return 0; } @@ -422,9 +456,16 @@ static int dw_configure_dai(struct dw_i2s_dev *dev, * Read component parameter registers to extract * the I2S block's configuration. */ - u32 comp1 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_1); - u32 comp2 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_2); - u32 idx; + u32 comp1, comp2, idx; + const struct i2s_platform_data *pdata = dev->dev->platform_data; + + if (pdata->quirks & DW_I2S_VENDOR_AMD) { + comp1 = i2s_read_reg(dev->i2s_pbase, 0x124); + comp2 = i2s_read_reg(dev->i2s_pbase, 0x120); + } else { + comp1 = i2s_read_reg(dev->i2s_pbase, I2S_COMP_PARAM_1); + comp2 = i2s_read_reg(dev->i2s_pbase, I2S_COMP_PARAM_2); + } if (COMP1_TX_ENABLED(comp1)) { dev_dbg(dev->dev, " designware: play supported\n"); @@ -458,10 +499,16 @@ static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev, struct resource *res, const struct i2s_platform_data *pdata) { - u32 comp1 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_1); - u32 idx = COMP1_APB_DATA_WIDTH(comp1); + u32 comp1, idx; int ret; + if (pdata->quirks & DW_I2S_VENDOR_AMD) + comp1 = i2s_read_reg(dev->i2s_pbase, 0x6c); + else + comp1 = i2s_read_reg(dev->i2s_pbase, I2S_COMP_PARAM_1); + + idx = COMP1_APB_DATA_WIDTH(comp1); + if (WARN_ON(idx >= ARRAY_SIZE(bus_widths))) return -EINVAL; @@ -469,6 +516,9 @@ static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev, if (ret < 0) return ret; + if (pdata->quirks & DW_I2S_VENDOR_AMD) + goto end; + /* Set DMA slaves info */ dev->play_dma_data.pd.data = pdata->play_dma_data; dev->capture_dma_data.pd.data = pdata->capture_dma_data; @@ -480,7 +530,7 @@ static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev, dev->capture_dma_data.pd.addr_width = bus_widths[idx]; dev->play_dma_data.pd.filter = pdata->filter; dev->capture_dma_data.pd.filter = pdata->filter; - +end: return 0; } @@ -488,8 +538,8 @@ static int dw_configure_dai_by_dt(struct dw_i2s_dev *dev, struct snd_soc_dai_driver *dw_i2s_dai, struct resource *res) { - u32 comp1 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_1); - u32 comp2 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_2); + u32 comp1 = i2s_read_reg(dev->i2s_pbase, I2S_COMP_PARAM_1); + u32 comp2 = i2s_read_reg(dev->i2s_pbase, I2S_COMP_PARAM_2); u32 fifo_depth = 1 << (1 + COMP1_FIFO_DEPTH_GLOBAL(comp1)); u32 idx = COMP1_APB_DATA_WIDTH(comp1); u32 idx2; @@ -553,10 +603,18 @@ static int dw_i2s_probe(struct platform_device *pdev) dw_i2s_dai->resume = dw_i2s_resume; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - dev->i2s_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(dev->i2s_base)) - return PTR_ERR(dev->i2s_base); + dev->i2s_pbase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dev->i2s_pbase)) + return PTR_ERR(dev->i2s_pbase); + + dev->i2s_cbase = dev->i2s_pbase; + if (pdata->quirks & DW_I2S_VENDOR_AMD) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + dev->i2s_cbase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dev->i2s_cbase)) + return PTR_ERR(dev->i2s_cbase); + } dev->dev = &pdev->dev; if (pdata) {