From 355314d32d7a1d4e93a027895312aedab79bf9d2 Mon Sep 17 00:00:00 2001 From: Andrew Jackson Date: Wed, 3 Dec 2014 16:38:46 +0000 Subject: ASoC: dwc: Allocate resources with devm_ioremap_resource Prepare for the introduction of device-tree support by re-ordering some of the allocations and using devm_iomap_resource to simplify IO mapping. Signed-off-by: Andrew Jackson Signed-off-by: Mark Brown (cherry picked from commit b163be4cf4a3bb673038a21e368954f7f88347b7) Signed-off-by: Jon Medhurst --- sound/soc/dwc/designware_i2s.c | 46 +++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index e961388e6e9c..08f0229f8d68 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -338,31 +338,34 @@ static int dw_i2s_probe(struct platform_device *pdev) return -EINVAL; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "no i2s resource defined\n"); - return -ENODEV; - } - - if (!devm_request_mem_region(&pdev->dev, res->start, - resource_size(res), pdev->name)) { - dev_err(&pdev->dev, "i2s region already claimed\n"); - return -EBUSY; - } - dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); if (!dev) { dev_warn(&pdev->dev, "kzalloc fail\n"); return -ENOMEM; } - dev->i2s_base = devm_ioremap(&pdev->dev, res->start, - resource_size(res)); - if (!dev->i2s_base) { - dev_err(&pdev->dev, "ioremap fail for i2s_region\n"); + dw_i2s_dai = devm_kzalloc(&pdev->dev, sizeof(*dw_i2s_dai), GFP_KERNEL); + if (!dw_i2s_dai) { + dev_err(&pdev->dev, "mem allocation failed for dai driver\n"); return -ENOMEM; } + dw_i2s_dai->ops = &dw_i2s_dai_ops; + dw_i2s_dai->suspend = dw_i2s_suspend; + dw_i2s_dai->resume = dw_i2s_resume; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "no i2s resource defined\n"); + return -ENODEV; + } + + dev->i2s_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dev->i2s_base)) { + dev_err(&pdev->dev, "ioremap fail for i2s_region\n"); + return PTR_ERR(dev->i2s_base); + } + cap = pdata->cap; dev->capability = cap; dev->i2s_clk_cfg = pdata->i2s_clk_cfg; @@ -388,13 +391,6 @@ static int dw_i2s_probe(struct platform_device *pdev) if (ret < 0) goto err_clk_put; - dw_i2s_dai = devm_kzalloc(&pdev->dev, sizeof(*dw_i2s_dai), GFP_KERNEL); - if (!dw_i2s_dai) { - dev_err(&pdev->dev, "mem allocation failed for dai driver\n"); - ret = -ENOMEM; - goto err_clk_disable; - } - if (cap & DWC_I2S_PLAY) { dev_dbg(&pdev->dev, " designware: play supported\n"); dw_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM; @@ -411,10 +407,6 @@ static int dw_i2s_probe(struct platform_device *pdev) dw_i2s_dai->capture.rates = pdata->snd_rates; } - dw_i2s_dai->ops = &dw_i2s_dai_ops; - dw_i2s_dai->suspend = dw_i2s_suspend; - dw_i2s_dai->resume = dw_i2s_resume; - dev->dev = &pdev->dev; dev_set_drvdata(&pdev->dev, dev); ret = snd_soc_register_component(&pdev->dev, &dw_i2s_component, -- cgit v1.2.3 From eaabed913d4ceb68659f1d3c21c27afb3b1375ae Mon Sep 17 00:00:00 2001 From: Andrew Jackson Date: Fri, 19 Dec 2014 16:18:06 +0000 Subject: ASoC: dwc: Iterate over all channels The Designware core can be configured with up to four stereo channels. Each stereo channel is individually configured so, when the driver's hw_params call is made, each requested stereo channel has to be programmed. Signed-off-by: Andrew Jackson Signed-off-by: Mark Brown (cherry picked from commit db2c1f9e38a38e0f9294b5a8a83d744f68fbe726) Signed-off-by: Jon Medhurst --- sound/soc/dwc/designware_i2s.c | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index 08f0229f8d68..a3c06cc97535 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -209,16 +209,9 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream, switch (config->chan_nr) { case EIGHT_CHANNEL_SUPPORT: - ch_reg = 3; - break; case SIX_CHANNEL_SUPPORT: - ch_reg = 2; - break; case FOUR_CHANNEL_SUPPORT: - ch_reg = 1; - break; case TWO_CHANNEL_SUPPORT: - ch_reg = 0; break; default: dev_err(dev->dev, "channel not supported\n"); @@ -227,18 +220,22 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream, i2s_disable_channels(dev, substream->stream); - 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); + 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); -- cgit v1.2.3 From 0e6dfecbcd111c3b1c72583443d84cab7a5b7c99 Mon Sep 17 00:00:00 2001 From: Andrew Jackson Date: Wed, 31 Dec 2014 16:20:37 +0000 Subject: ASoC: adi: Add missing return statement. The probe routine was disabling the clock even if the system was configured successfully. Add a return statement to leave clocks enabled. Signed-off-by: Andrew Jackson Acked-by: Lars-Peter Clausen Signed-off-by: Mark Brown (cherry picked from commit ae6f636b8b2ea9d297a07fc7ef8ae54707d67b36) Signed-off-by: Jon Medhurst --- sound/soc/adi/axi-i2s.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/adi/axi-i2s.c b/sound/soc/adi/axi-i2s.c index 6058c1fd5070..8520bff3a61b 100644 --- a/sound/soc/adi/axi-i2s.c +++ b/sound/soc/adi/axi-i2s.c @@ -240,6 +240,8 @@ static int axi_i2s_probe(struct platform_device *pdev) if (ret) goto err_clk_disable; + return 0; + err_clk_disable: clk_disable_unprepare(i2s->clk); return ret; -- cgit v1.2.3 From a240c0cb6fa949a48319a156c946220c304b9408 Mon Sep 17 00:00:00 2001 From: Andrew Jackson Date: Fri, 12 Dec 2014 09:25:00 +0000 Subject: ASoC: dwc: Remove unnecessary debug messages and tests The devm_XXX allocation functions print a message on failure, so additional messages are not required. Signed-off-by: Andrew Jackson Signed-off-by: Mark Brown (cherry picked from commit be33465e58ab3a3300c5182844d45c67e73b15e0) Signed-off-by: Jon Medhurst --- sound/soc/dwc/designware_i2s.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index a3c06cc97535..1fc757268670 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -342,26 +342,17 @@ static int dw_i2s_probe(struct platform_device *pdev) } dw_i2s_dai = devm_kzalloc(&pdev->dev, sizeof(*dw_i2s_dai), GFP_KERNEL); - if (!dw_i2s_dai) { - dev_err(&pdev->dev, "mem allocation failed for dai driver\n"); + if (!dw_i2s_dai) return -ENOMEM; - } dw_i2s_dai->ops = &dw_i2s_dai_ops; dw_i2s_dai->suspend = dw_i2s_suspend; dw_i2s_dai->resume = dw_i2s_resume; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "no i2s resource defined\n"); - return -ENODEV; - } - dev->i2s_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(dev->i2s_base)) { - dev_err(&pdev->dev, "ioremap fail for i2s_region\n"); + if (IS_ERR(dev->i2s_base)) return PTR_ERR(dev->i2s_base); - } cap = pdata->cap; dev->capability = cap; -- cgit v1.2.3 From f911d5adc941a2f4e63c53d1a29e4f3e3f5a8cdd Mon Sep 17 00:00:00 2001 From: Andrew Jackson Date: Fri, 19 Dec 2014 16:18:07 +0000 Subject: ASoC: dwc: Reorder code in preparation for DT support Move code that configures the DAI and DMA into a separate function. This reduces the size of the dw_i2s_probe function and will make it easier to add support for device tree to the driver. Signed-off-by: Andrew Jackson Signed-off-by: Mark Brown (cherry picked from commit afa8603c6253204bf96c88739f711e89c2b00cd5) Signed-off-by: Jon Medhurst --- sound/soc/dwc/designware_i2s.c | 73 ++++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index 1fc757268670..de390ad32f8a 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -321,13 +321,47 @@ static int dw_i2s_resume(struct snd_soc_dai *dai) #define dw_i2s_resume NULL #endif +static void dw_configure_dai_by_pd(struct dw_i2s_dev *dev, + struct snd_soc_dai_driver *dw_i2s_dai, + struct resource *res, + const struct i2s_platform_data *pdata) +{ + /* Set DMA slaves info */ + + dev->play_dma_data.data = pdata->play_dma_data; + dev->capture_dma_data.data = pdata->capture_dma_data; + dev->play_dma_data.addr = res->start + I2S_TXDMA; + dev->capture_dma_data.addr = res->start + I2S_RXDMA; + dev->play_dma_data.max_burst = 16; + dev->capture_dma_data.max_burst = 16; + dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + dev->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + dev->play_dma_data.filter = pdata->filter; + dev->capture_dma_data.filter = pdata->filter; + + if (pdata->cap & DWC_I2S_PLAY) { + dev_dbg(dev->dev, " designware: play supported\n"); + dw_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM; + dw_i2s_dai->playback.channels_max = pdata->channel; + dw_i2s_dai->playback.formats = pdata->snd_fmts; + dw_i2s_dai->playback.rates = pdata->snd_rates; + } + + if (pdata->cap & DWC_I2S_RECORD) { + dev_dbg(dev->dev, "designware: record supported\n"); + dw_i2s_dai->capture.channels_min = MIN_CHANNEL_NUM; + dw_i2s_dai->capture.channels_max = pdata->channel; + dw_i2s_dai->capture.formats = pdata->snd_fmts; + dw_i2s_dai->capture.rates = pdata->snd_rates; + } +} + static int dw_i2s_probe(struct platform_device *pdev) { const struct i2s_platform_data *pdata = pdev->dev.platform_data; struct dw_i2s_dev *dev; struct resource *res; int ret; - unsigned int cap; struct snd_soc_dai_driver *dw_i2s_dai; if (!pdata) { @@ -354,23 +388,11 @@ static int dw_i2s_probe(struct platform_device *pdev) if (IS_ERR(dev->i2s_base)) return PTR_ERR(dev->i2s_base); - cap = pdata->cap; - dev->capability = cap; - dev->i2s_clk_cfg = pdata->i2s_clk_cfg; - - /* Set DMA slaves info */ - - dev->play_dma_data.data = pdata->play_dma_data; - dev->capture_dma_data.data = pdata->capture_dma_data; - dev->play_dma_data.addr = res->start + I2S_TXDMA; - dev->capture_dma_data.addr = res->start + I2S_RXDMA; - dev->play_dma_data.max_burst = 16; - dev->capture_dma_data.max_burst = 16; - dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; - dev->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; - dev->play_dma_data.filter = pdata->filter; - dev->capture_dma_data.filter = pdata->filter; + dev->dev = &pdev->dev; + dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata); + dev->capability = pdata->cap; + dev->i2s_clk_cfg = pdata->i2s_clk_cfg; dev->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(dev->clk)) return PTR_ERR(dev->clk); @@ -379,23 +401,6 @@ static int dw_i2s_probe(struct platform_device *pdev) if (ret < 0) goto err_clk_put; - if (cap & DWC_I2S_PLAY) { - dev_dbg(&pdev->dev, " designware: play supported\n"); - dw_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM; - dw_i2s_dai->playback.channels_max = pdata->channel; - dw_i2s_dai->playback.formats = pdata->snd_fmts; - dw_i2s_dai->playback.rates = pdata->snd_rates; - } - - if (cap & DWC_I2S_RECORD) { - dev_dbg(&pdev->dev, "designware: record supported\n"); - dw_i2s_dai->capture.channels_min = MIN_CHANNEL_NUM; - dw_i2s_dai->capture.channels_max = pdata->channel; - dw_i2s_dai->capture.formats = pdata->snd_fmts; - dw_i2s_dai->capture.rates = pdata->snd_rates; - } - - dev->dev = &pdev->dev; dev_set_drvdata(&pdev->dev, dev); ret = snd_soc_register_component(&pdev->dev, &dw_i2s_component, dw_i2s_dai, 1); -- cgit v1.2.3 From 4d0fa1e22641b912782761e8fb19491aa479cca4 Mon Sep 17 00:00:00 2001 From: Andrew Jackson Date: Tue, 30 Dec 2014 10:55:43 +0000 Subject: ASoC: dwc: Switch to managed clock resource Simplify error handling during probe by using managed clock resources. Signed-off-by: Andrew Jackson Signed-off-by: Mark Brown (cherry picked from commit a56257c657eab392d579e6be70e2f8430eef1aa3) Signed-off-by: Jon Medhurst --- sound/soc/dwc/designware_i2s.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index de390ad32f8a..b25dbd322e8c 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -393,13 +393,13 @@ static int dw_i2s_probe(struct platform_device *pdev) dev->capability = pdata->cap; dev->i2s_clk_cfg = pdata->i2s_clk_cfg; - dev->clk = clk_get(&pdev->dev, NULL); + dev->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(dev->clk)) return PTR_ERR(dev->clk); ret = clk_enable(dev->clk); if (ret < 0) - goto err_clk_put; + return ret; dev_set_drvdata(&pdev->dev, dev); ret = snd_soc_register_component(&pdev->dev, &dw_i2s_component, @@ -413,19 +413,13 @@ static int dw_i2s_probe(struct platform_device *pdev) err_clk_disable: clk_disable(dev->clk); -err_clk_put: - clk_put(dev->clk); return ret; } static int dw_i2s_remove(struct platform_device *pdev) { - struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev); - snd_soc_unregister_component(&pdev->dev); - clk_put(dev->clk); - return 0; } -- cgit v1.2.3 From 6da896be0c34de3a58e1b54acf4c18a772b4d3b0 Mon Sep 17 00:00:00 2001 From: Andrew Jackson Date: Tue, 30 Dec 2014 10:55:45 +0000 Subject: ASoC: dwc: Read I2S block configuration from registers The I2S block provides component parameter registers which describe how the block is instantiated. Use these registers to extract the block's configuration rather than relying on platform data. Signed-off-by: Andrew Jackson Signed-off-by: Mark Brown (cherry picked from commit b226efe5818bf01cecc8a3e0fbd0def4ebbcedaa) Signed-off-by: Jon Medhurst --- sound/soc/dwc/designware_i2s.c | 96 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 86 insertions(+), 10 deletions(-) diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index b25dbd322e8c..e4b0c5e2dc9a 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -54,6 +54,31 @@ #define I2S_COMP_VERSION 0x01F8 #define I2S_COMP_TYPE 0x01FC +/* + * Component parameter register fields - define the I2S block's + * configuration. + */ +#define COMP1_TX_WORDSIZE_3(r) (((r) & GENMASK(27, 25)) >> 25) +#define COMP1_TX_WORDSIZE_2(r) (((r) & GENMASK(24, 22)) >> 22) +#define COMP1_TX_WORDSIZE_1(r) (((r) & GENMASK(21, 19)) >> 19) +#define COMP1_TX_WORDSIZE_0(r) (((r) & GENMASK(18, 16)) >> 16) +#define COMP1_TX_CHANNELS(r) (((r) & GENMASK(10, 9)) >> 9) +#define COMP1_RX_CHANNELS(r) (((r) & GENMASK(8, 7)) >> 7) +#define COMP1_RX_ENABLED(r) (((r) & BIT(6)) >> 6) +#define COMP1_TX_ENABLED(r) (((r) & BIT(5)) >> 5) +#define COMP1_MODE_EN(r) (((r) & BIT(4)) >> 4) +#define COMP1_FIFO_DEPTH_GLOBAL(r) (((r) & GENMASK(3, 2)) >> 2) +#define COMP1_APB_DATA_WIDTH(r) (((r) & GENMASK(1, 0)) >> 0) + +#define COMP2_RX_WORDSIZE_3(r) (((r) & GENMASK(12, 10)) >> 10) +#define COMP2_RX_WORDSIZE_2(r) (((r) & GENMASK(9, 7)) >> 7) +#define COMP2_RX_WORDSIZE_1(r) (((r) & GENMASK(5, 3)) >> 3) +#define COMP2_RX_WORDSIZE_0(r) (((r) & GENMASK(2, 0)) >> 0) + +/* Number of entries in WORDSIZE and DATA_WIDTH parameter registers */ +#define COMP_MAX_WORDSIZE (1 << 3) +#define COMP_MAX_DATA_WIDTH (1 << 2) + #define MAX_CHANNEL_NUM 8 #define MIN_CHANNEL_NUM 2 @@ -321,11 +346,50 @@ static int dw_i2s_resume(struct snd_soc_dai *dai) #define dw_i2s_resume NULL #endif -static void dw_configure_dai_by_pd(struct dw_i2s_dev *dev, +/* + * The following tables allow a direct lookup of various parameters + * defined in the I2S block's configuration in terms of sound system + * parameters. Each table is sized to the number of entries possible + * according to the number of configuration bits describing an I2S + * block parameter. + */ + +/* Width of (DMA) bus */ +static const u32 bus_widths[COMP_MAX_DATA_WIDTH] = { + DMA_SLAVE_BUSWIDTH_1_BYTE, + DMA_SLAVE_BUSWIDTH_2_BYTES, + DMA_SLAVE_BUSWIDTH_4_BYTES, + DMA_SLAVE_BUSWIDTH_UNDEFINED +}; + +/* PCM format to support channel resolution */ +static const u32 formats[COMP_MAX_WORDSIZE] = { + SNDRV_PCM_FMTBIT_S16_LE, + SNDRV_PCM_FMTBIT_S16_LE, + SNDRV_PCM_FMTBIT_S24_LE, + SNDRV_PCM_FMTBIT_S24_LE, + SNDRV_PCM_FMTBIT_S32_LE, + 0, + 0, + 0 +}; + +static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev, struct snd_soc_dai_driver *dw_i2s_dai, struct resource *res, const struct i2s_platform_data *pdata) { + /* + * 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 = COMP1_APB_DATA_WIDTH(comp1); + + if (WARN_ON(idx >= ARRAY_SIZE(bus_widths))) + return -EINVAL; + /* Set DMA slaves info */ dev->play_dma_data.data = pdata->play_dma_data; @@ -334,26 +398,36 @@ static void dw_configure_dai_by_pd(struct dw_i2s_dev *dev, dev->capture_dma_data.addr = res->start + I2S_RXDMA; dev->play_dma_data.max_burst = 16; dev->capture_dma_data.max_burst = 16; - dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; - dev->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + dev->play_dma_data.addr_width = bus_widths[idx]; + dev->capture_dma_data.addr_width = bus_widths[idx]; dev->play_dma_data.filter = pdata->filter; dev->capture_dma_data.filter = pdata->filter; - if (pdata->cap & DWC_I2S_PLAY) { + if (COMP1_TX_ENABLED(comp1)) { dev_dbg(dev->dev, " designware: play supported\n"); + idx = COMP1_TX_WORDSIZE_0(comp1); + if (WARN_ON(idx >= ARRAY_SIZE(formats))) + return -EINVAL; dw_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM; - dw_i2s_dai->playback.channels_max = pdata->channel; - dw_i2s_dai->playback.formats = pdata->snd_fmts; + dw_i2s_dai->playback.channels_max = + 1 << (COMP1_TX_CHANNELS(comp1) + 1); + dw_i2s_dai->playback.formats = formats[idx]; dw_i2s_dai->playback.rates = pdata->snd_rates; } - if (pdata->cap & DWC_I2S_RECORD) { + if (COMP1_RX_ENABLED(comp1)) { dev_dbg(dev->dev, "designware: record supported\n"); + idx = COMP2_RX_WORDSIZE_0(comp2); + if (WARN_ON(idx >= ARRAY_SIZE(formats))) + return -EINVAL; dw_i2s_dai->capture.channels_min = MIN_CHANNEL_NUM; - dw_i2s_dai->capture.channels_max = pdata->channel; - dw_i2s_dai->capture.formats = pdata->snd_fmts; + dw_i2s_dai->capture.channels_max = + 1 << (COMP1_RX_CHANNELS(comp1) + 1); + dw_i2s_dai->capture.formats = formats[idx]; dw_i2s_dai->capture.rates = pdata->snd_rates; } + + return 0; } static int dw_i2s_probe(struct platform_device *pdev) @@ -389,7 +463,9 @@ static int dw_i2s_probe(struct platform_device *pdev) return PTR_ERR(dev->i2s_base); dev->dev = &pdev->dev; - dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata); + ret = dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata); + if (ret < 0) + return ret; dev->capability = pdata->cap; dev->i2s_clk_cfg = pdata->i2s_clk_cfg; -- cgit v1.2.3 From 84e3cacca28279597fafb806ddd665bea497c928 Mon Sep 17 00:00:00 2001 From: Andrew Jackson Date: Tue, 30 Dec 2014 10:55:44 +0000 Subject: ASoC: dwc: Prepare clock before use Some I2S clocks may require some time to get the clock ready for operation and so need to be prepared before they are enabled. So, prepare the clock as well as enabling it, but combine the two through clk_prepare_enable. Signed-off-by: Andrew Jackson Signed-off-by: Mark Brown (cherry picked from commit 3a19272a507cd5ad214e0082c3ff7355dfe71329) Signed-off-by: Jon Medhurst --- sound/soc/dwc/designware_i2s.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index e4b0c5e2dc9a..00e77037adcc 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -473,7 +473,7 @@ static int dw_i2s_probe(struct platform_device *pdev) if (IS_ERR(dev->clk)) return PTR_ERR(dev->clk); - ret = clk_enable(dev->clk); + ret = clk_prepare_enable(dev->clk); if (ret < 0) return ret; @@ -488,13 +488,16 @@ static int dw_i2s_probe(struct platform_device *pdev) return 0; err_clk_disable: - clk_disable(dev->clk); + clk_disable_unprepare(dev->clk); return ret; } static int dw_i2s_remove(struct platform_device *pdev) { + struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev); + snd_soc_unregister_component(&pdev->dev); + clk_disable_unprepare(dev->clk); return 0; } -- cgit v1.2.3 From 4c517af0cbd61ddb4013982b81b04ad616573aca Mon Sep 17 00:00:00 2001 From: Andrew Jackson Date: Tue, 30 Dec 2014 10:55:46 +0000 Subject: ASoC: dwc: Register components with managed interface Register SOC component using managed interface to simplify error handling and future introduction of device tree. Signed-off-by: Andrew Jackson Signed-off-by: Mark Brown (cherry picked from commit 758c2debcbf1a4ba6a787d276ca0fb3333105b06) Signed-off-by: Jon Medhurst --- sound/soc/dwc/designware_i2s.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index 00e77037adcc..1dcf2727b1c0 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -478,7 +478,7 @@ static int dw_i2s_probe(struct platform_device *pdev) return ret; dev_set_drvdata(&pdev->dev, dev); - ret = snd_soc_register_component(&pdev->dev, &dw_i2s_component, + ret = devm_snd_soc_register_component(&pdev->dev, &dw_i2s_component, dw_i2s_dai, 1); if (ret != 0) { dev_err(&pdev->dev, "not able to register dai\n"); @@ -496,7 +496,6 @@ static int dw_i2s_remove(struct platform_device *pdev) { struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev); - snd_soc_unregister_component(&pdev->dev); clk_disable_unprepare(dev->clk); return 0; -- cgit v1.2.3 From fe3473e26aa6f1503ff71995e5e78893c8e86308 Mon Sep 17 00:00:00 2001 From: Andrew Jackson Date: Tue, 30 Dec 2014 10:55:47 +0000 Subject: ASoC: dwc: Add documentation for I2S DT Add documentation for Designware I2S hardware block. The block requires one clock (for audio sampling) and DMA channels for receive and transmit. Signed-off-by: Andrew Jackson Signed-off-by: Mark Brown (cherry picked from commit d8b58e0b5322f91eb6fcffc337a74083a24c7149) Signed-off-by: Jon Medhurst --- .../devicetree/bindings/sound/designware-i2s.txt | 31 ++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/designware-i2s.txt diff --git a/Documentation/devicetree/bindings/sound/designware-i2s.txt b/Documentation/devicetree/bindings/sound/designware-i2s.txt new file mode 100644 index 000000000000..7bb54247f8e8 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/designware-i2s.txt @@ -0,0 +1,31 @@ +DesignWare I2S controller + +Required properties: + - compatible : Must be "snps,designware-i2s" + - reg : Must contain the I2S core's registers location and length + - clocks : Pairs of phandle and specifier referencing the controller's + clocks. The controller expects one clock: the clock used as the sampling + rate reference clock sample. + - clock-names : "i2sclk" for the sample rate reference clock. + - dmas: Pairs of phandle and specifier for the DMA channels that are used by + the core. The core expects one or two dma channels: one for transmit and + one for receive. + - dma-names : "tx" for the transmit channel, "rx" for the receive channel. + +For more details on the 'dma', 'dma-names', 'clock' and 'clock-names' +properties please check: + * resource-names.txt + * clock/clock-bindings.txt + * dma/dma.txt + +Example: + + soc_i2s: i2s@7ff90000 { + compatible = "snps,designware-i2s"; + reg = <0x0 0x7ff90000 0x0 0x1000>; + clocks = <&scpi_i2sclk 0>; + clock-names = "i2sclk"; + #sound-dai-cells = <0>; + dmas = <&dma0 5>; + dma-names = "tx"; + }; -- cgit v1.2.3 From e82935cfa9f7950599e39fda9248c1d8e106f2ab Mon Sep 17 00:00:00 2001 From: Andrew Jackson Date: Tue, 30 Dec 2014 10:55:48 +0000 Subject: ASoC: dwc: Add devicetree support for Designware I2S Allow the driver to be configured through a device tree rather than platform data. Signed-off-by: Andrew Jackson Signed-off-by: Mark Brown (cherry picked from commit 0d274544bfee721b22b484a10b1480e237c0e258) Signed-off-by: Jon Medhurst --- sound/soc/dwc/Kconfig | 1 + sound/soc/dwc/designware_i2s.c | 193 ++++++++++++++++++++++++++++++++--------- 2 files changed, 151 insertions(+), 43 deletions(-) diff --git a/sound/soc/dwc/Kconfig b/sound/soc/dwc/Kconfig index e334900cf0b8..d50e08517dce 100644 --- a/sound/soc/dwc/Kconfig +++ b/sound/soc/dwc/Kconfig @@ -1,6 +1,7 @@ config SND_DESIGNWARE_I2S tristate "Synopsys I2S Device Driver" depends on CLKDEV_LOOKUP + select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for I2S driver for Synopsys desigwnware I2S device. The device supports upto diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index 1dcf2727b1c0..71f3fbbefaa2 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -22,6 +22,7 @@ #include #include #include +#include /* common register for all channel */ #define IER 0x000 @@ -82,6 +83,11 @@ #define MAX_CHANNEL_NUM 8 #define MIN_CHANNEL_NUM 2 +union dw_i2s_snd_dma_data { + struct i2s_dma_data pd; + struct snd_dmaengine_dai_dma_data dt; +}; + struct dw_i2s_dev { void __iomem *i2s_base; struct clk *clk; @@ -90,8 +96,8 @@ struct dw_i2s_dev { struct device *dev; /* data related to DMA transfers b/w i2s and DMAC */ - struct i2s_dma_data play_dma_data; - struct i2s_dma_data capture_dma_data; + union dw_i2s_snd_dma_data play_dma_data; + union dw_i2s_snd_dma_data capture_dma_data; struct i2s_clk_config_data config; int (*i2s_clk_cfg)(struct i2s_clk_config_data *config); }; @@ -178,7 +184,7 @@ static int dw_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); - struct i2s_dma_data *dma_data = NULL; + union dw_i2s_snd_dma_data *dma_data = NULL; if (!(dev->capability & DWC_I2S_RECORD) && (substream->stream == SNDRV_PCM_STREAM_CAPTURE)) @@ -267,13 +273,21 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream, config->sample_rate = params_rate(params); - if (!dev->i2s_clk_cfg) - return -EINVAL; + if (dev->i2s_clk_cfg) { + ret = dev->i2s_clk_cfg(config); + if (ret < 0) { + dev_err(dev->dev, "runtime audio clk config fail\n"); + return ret; + } + } else { + u32 bitclk = config->sample_rate * config->data_width * 2; - ret = dev->i2s_clk_cfg(config); - if (ret < 0) { - dev_err(dev->dev, "runtime audio clk config fail\n"); - return ret; + ret = clk_set_rate(dev->clk, bitclk); + if (ret) { + dev_err(dev->dev, "Can't set I2S clock rate: %d\n", + ret); + return ret; + } } return 0; @@ -354,6 +368,11 @@ static int dw_i2s_resume(struct snd_soc_dai *dai) * block parameter. */ +/* Maximum bit resolution of a channel - not uniformly spaced */ +static const u32 fifo_width[COMP_MAX_WORDSIZE] = { + 12, 16, 20, 24, 32, 0, 0, 0 +}; + /* Width of (DMA) bus */ static const u32 bus_widths[COMP_MAX_DATA_WIDTH] = { DMA_SLAVE_BUSWIDTH_1_BYTE, @@ -374,10 +393,9 @@ static const u32 formats[COMP_MAX_WORDSIZE] = { 0 }; -static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev, +static int dw_configure_dai(struct dw_i2s_dev *dev, struct snd_soc_dai_driver *dw_i2s_dai, - struct resource *res, - const struct i2s_platform_data *pdata) + unsigned int rates) { /* * Read component parameter registers to extract @@ -385,23 +403,7 @@ static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev, */ 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 = COMP1_APB_DATA_WIDTH(comp1); - - if (WARN_ON(idx >= ARRAY_SIZE(bus_widths))) - return -EINVAL; - - /* Set DMA slaves info */ - - dev->play_dma_data.data = pdata->play_dma_data; - dev->capture_dma_data.data = pdata->capture_dma_data; - dev->play_dma_data.addr = res->start + I2S_TXDMA; - dev->capture_dma_data.addr = res->start + I2S_RXDMA; - dev->play_dma_data.max_burst = 16; - dev->capture_dma_data.max_burst = 16; - dev->play_dma_data.addr_width = bus_widths[idx]; - dev->capture_dma_data.addr_width = bus_widths[idx]; - dev->play_dma_data.filter = pdata->filter; - dev->capture_dma_data.filter = pdata->filter; + u32 idx; if (COMP1_TX_ENABLED(comp1)) { dev_dbg(dev->dev, " designware: play supported\n"); @@ -412,7 +414,7 @@ static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev, dw_i2s_dai->playback.channels_max = 1 << (COMP1_TX_CHANNELS(comp1) + 1); dw_i2s_dai->playback.formats = formats[idx]; - dw_i2s_dai->playback.rates = pdata->snd_rates; + dw_i2s_dai->playback.rates = rates; } if (COMP1_RX_ENABLED(comp1)) { @@ -424,10 +426,86 @@ static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev, dw_i2s_dai->capture.channels_max = 1 << (COMP1_RX_CHANNELS(comp1) + 1); dw_i2s_dai->capture.formats = formats[idx]; - dw_i2s_dai->capture.rates = pdata->snd_rates; + dw_i2s_dai->capture.rates = rates; + } + + return 0; +} + +static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev, + struct snd_soc_dai_driver *dw_i2s_dai, + 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); + int ret; + + if (WARN_ON(idx >= ARRAY_SIZE(bus_widths))) + return -EINVAL; + + ret = dw_configure_dai(dev, dw_i2s_dai, pdata->snd_rates); + if (ret < 0) + return ret; + + /* Set DMA slaves info */ + dev->play_dma_data.pd.data = pdata->play_dma_data; + dev->capture_dma_data.pd.data = pdata->capture_dma_data; + dev->play_dma_data.pd.addr = res->start + I2S_TXDMA; + dev->capture_dma_data.pd.addr = res->start + I2S_RXDMA; + dev->play_dma_data.pd.max_burst = 16; + dev->capture_dma_data.pd.max_burst = 16; + dev->play_dma_data.pd.addr_width = bus_widths[idx]; + 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; + + return 0; +} + +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 fifo_depth = 1 << (1 + COMP1_FIFO_DEPTH_GLOBAL(comp1)); + u32 idx = COMP1_APB_DATA_WIDTH(comp1); + u32 idx2; + int ret; + + if (WARN_ON(idx >= ARRAY_SIZE(bus_widths))) + return -EINVAL; + + ret = dw_configure_dai(dev, dw_i2s_dai, SNDRV_PCM_RATE_8000_192000); + if (ret < 0) + return ret; + + if (COMP1_TX_ENABLED(comp1)) { + idx2 = COMP1_TX_WORDSIZE_0(comp1); + + dev->capability |= DWC_I2S_PLAY; + dev->play_dma_data.dt.addr = res->start + I2S_TXDMA; + dev->play_dma_data.dt.addr_width = bus_widths[idx]; + dev->play_dma_data.dt.chan_name = "TX"; + dev->play_dma_data.dt.fifo_size = fifo_depth * + (fifo_width[idx2]) >> 8; + dev->play_dma_data.dt.maxburst = 16; + } + if (COMP1_RX_ENABLED(comp1)) { + idx2 = COMP2_RX_WORDSIZE_0(comp2); + + dev->capability |= DWC_I2S_RECORD; + dev->capture_dma_data.dt.addr = res->start + I2S_RXDMA; + dev->capture_dma_data.dt.addr_width = bus_widths[idx]; + dev->capture_dma_data.dt.chan_name = "RX"; + dev->capture_dma_data.dt.fifo_size = fifo_depth * + (fifo_width[idx2] >> 8); + dev->capture_dma_data.dt.maxburst = 16; } return 0; + } static int dw_i2s_probe(struct platform_device *pdev) @@ -438,11 +516,6 @@ static int dw_i2s_probe(struct platform_device *pdev) int ret; struct snd_soc_dai_driver *dw_i2s_dai; - if (!pdata) { - dev_err(&pdev->dev, "Invalid platform data\n"); - return -EINVAL; - } - dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); if (!dev) { dev_warn(&pdev->dev, "kzalloc fail\n"); @@ -463,15 +536,28 @@ static int dw_i2s_probe(struct platform_device *pdev) return PTR_ERR(dev->i2s_base); dev->dev = &pdev->dev; - ret = dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata); - if (ret < 0) - return ret; + if (pdata) { + ret = dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata); + if (ret < 0) + return ret; + + dev->capability = pdata->cap; + dev->i2s_clk_cfg = pdata->i2s_clk_cfg; + if (!dev->i2s_clk_cfg) { + dev_err(&pdev->dev, "no clock configure method\n"); + return -ENODEV; + } + + dev->clk = devm_clk_get(&pdev->dev, NULL); + } else { + ret = dw_configure_dai_by_dt(dev, dw_i2s_dai, res); + if (ret < 0) + return ret; - dev->capability = pdata->cap; - dev->i2s_clk_cfg = pdata->i2s_clk_cfg; - dev->clk = devm_clk_get(&pdev->dev, NULL); + dev->clk = devm_clk_get(&pdev->dev, "i2sclk"); + } if (IS_ERR(dev->clk)) - return PTR_ERR(dev->clk); + return PTR_ERR(dev->clk); ret = clk_prepare_enable(dev->clk); if (ret < 0) @@ -485,6 +571,15 @@ static int dw_i2s_probe(struct platform_device *pdev) goto err_clk_disable; } + if (!pdata) { + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + if (ret) { + dev_err(&pdev->dev, + "Could not register PCM: %d\n", ret); + goto err_clk_disable; + } + } + return 0; err_clk_disable: @@ -501,12 +596,24 @@ static int dw_i2s_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id dw_i2s_of_match[] = { + { .compatible = "snps,designware-i2s", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, dw_i2s_of_match); +#endif + static struct platform_driver dw_i2s_driver = { .probe = dw_i2s_probe, .remove = dw_i2s_remove, .driver = { .name = "designware-i2s", .owner = THIS_MODULE, +#ifdef CONFIG_OF + .of_match_table = of_match_ptr(dw_i2s_of_match), +#endif }, }; -- cgit v1.2.3 From 3fe89f1753f39a021d298efc265f4dcf19da8dfc Mon Sep 17 00:00:00 2001 From: Andrew Jackson Date: Tue, 27 Jan 2015 20:38:28 +0000 Subject: ASoC: dwc: Remove unnecessary conditional compilation of_match_ptr is already conditionally compiled based on CONFIG_OF so further conditional compilation is not required. Remove conditional compilation surrounding of_match_ptr. Signed-off-by: Andrew Jackson Signed-off-by: Mark Brown (cherry picked from commit acae254a8e08b9406e62d456000ebd1573af0cb6) Signed-off-by: Jon Medhurst --- sound/soc/dwc/designware_i2s.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index 71f3fbbefaa2..0e697151a246 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -611,9 +611,7 @@ static struct platform_driver dw_i2s_driver = { .driver = { .name = "designware-i2s", .owner = THIS_MODULE, -#ifdef CONFIG_OF .of_match_table = of_match_ptr(dw_i2s_of_match), -#endif }, }; -- cgit v1.2.3