aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/dwc/designware_i2s.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2015-12-23 02:33:52 -0500
committerTakashi Iwai <tiwai@suse.de>2015-12-23 02:33:52 -0500
commitf80e39e0225c01ee68764ef7594c3a29ab5ebabb (patch)
treef5a85085741a173c93fc8f21938528b65ed95e42 /sound/soc/dwc/designware_i2s.c
parent59c8231089be96165735585694a801ae58ec6c95 (diff)
parent822ad70a2f5c420da5baa9f4354e6b7813ca6da9 (diff)
Merge tag 'asoc-v4.5' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-next
ASoC: Updates for v4.5 This is quite a busy release on the driver front with a lot of new drivers being added but comparatively quiet on the core side with only one big change going in and that a fairly straightforward refactoring. - Conversion of the array of DAI links to a list by Mengdong Lin, supporting dynamically adding and removing DAI links. - Some more fixes for the topology code, though it is still not final and ready for enabling in production. We really need to get to the point where that can be done. - A pile of changes for Intel SkyLake drivers which hopefully deliver some useful initial functionality for systems with this chipset, though there is more work still to come. - New drivers for a number of Imagination Technologies IPs. - Lots of new features and cleanups for the Renesas drivers. - ANC support for WM5110. - New driver for Atmel class D speaker drivers. - New drivers for Cirrus CS47L24 and WM1831. - New driver for Dialog DA7128. - New drivers for Realtek RT5659 and RT56156. - New driver for Rockchip RK3036. - New driver for TI PC3168A
Diffstat (limited to 'sound/soc/dwc/designware_i2s.c')
-rw-r--r--sound/soc/dwc/designware_i2s.c113
1 files changed, 82 insertions, 31 deletions
diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c
index 6e6a70c5c2bd..825a1f480aab 100644
--- a/sound/soc/dwc/designware_i2s.c
+++ b/sound/soc/dwc/designware_i2s.c
@@ -18,6 +18,7 @@
18#include <linux/interrupt.h> 18#include <linux/interrupt.h>
19#include <linux/module.h> 19#include <linux/module.h>
20#include <linux/slab.h> 20#include <linux/slab.h>
21#include <linux/pm_runtime.h>
21#include <sound/designware_i2s.h> 22#include <sound/designware_i2s.h>
22#include <sound/pcm.h> 23#include <sound/pcm.h>
23#include <sound/pcm_params.h> 24#include <sound/pcm_params.h>
@@ -93,7 +94,12 @@ struct dw_i2s_dev {
93 struct clk *clk; 94 struct clk *clk;
94 int active; 95 int active;
95 unsigned int capability; 96 unsigned int capability;
97 unsigned int quirks;
98 unsigned int i2s_reg_comp1;
99 unsigned int i2s_reg_comp2;
96 struct device *dev; 100 struct device *dev;
101 u32 ccr;
102 u32 xfer_resolution;
97 103
98 /* data related to DMA transfers b/w i2s and DMAC */ 104 /* data related to DMA transfers b/w i2s and DMAC */
99 union dw_i2s_snd_dma_data play_dma_data; 105 union dw_i2s_snd_dma_data play_dma_data;
@@ -213,31 +219,58 @@ static int dw_i2s_startup(struct snd_pcm_substream *substream,
213 return 0; 219 return 0;
214} 220}
215 221
222static void dw_i2s_config(struct dw_i2s_dev *dev, int stream)
223{
224 u32 ch_reg, irq;
225 struct i2s_clk_config_data *config = &dev->config;
226
227
228 i2s_disable_channels(dev, stream);
229
230 for (ch_reg = 0; ch_reg < (config->chan_nr / 2); ch_reg++) {
231 if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
232 i2s_write_reg(dev->i2s_base, TCR(ch_reg),
233 dev->xfer_resolution);
234 i2s_write_reg(dev->i2s_base, TFCR(ch_reg), 0x02);
235 irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg));
236 i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x30);
237 i2s_write_reg(dev->i2s_base, TER(ch_reg), 1);
238 } else {
239 i2s_write_reg(dev->i2s_base, RCR(ch_reg),
240 dev->xfer_resolution);
241 i2s_write_reg(dev->i2s_base, RFCR(ch_reg), 0x07);
242 irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg));
243 i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x03);
244 i2s_write_reg(dev->i2s_base, RER(ch_reg), 1);
245 }
246
247 }
248}
249
216static int dw_i2s_hw_params(struct snd_pcm_substream *substream, 250static int dw_i2s_hw_params(struct snd_pcm_substream *substream,
217 struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) 251 struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
218{ 252{
219 struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); 253 struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
220 struct i2s_clk_config_data *config = &dev->config; 254 struct i2s_clk_config_data *config = &dev->config;
221 u32 ccr, xfer_resolution, ch_reg, irq;
222 int ret; 255 int ret;
223 256
224 switch (params_format(params)) { 257 switch (params_format(params)) {
225 case SNDRV_PCM_FORMAT_S16_LE: 258 case SNDRV_PCM_FORMAT_S16_LE:
226 config->data_width = 16; 259 config->data_width = 16;
227 ccr = 0x00; 260 dev->ccr = 0x00;
228 xfer_resolution = 0x02; 261 dev->xfer_resolution = 0x02;
229 break; 262 break;
230 263
231 case SNDRV_PCM_FORMAT_S24_LE: 264 case SNDRV_PCM_FORMAT_S24_LE:
232 config->data_width = 24; 265 config->data_width = 24;
233 ccr = 0x08; 266 dev->ccr = 0x08;
234 xfer_resolution = 0x04; 267 dev->xfer_resolution = 0x04;
235 break; 268 break;
236 269
237 case SNDRV_PCM_FORMAT_S32_LE: 270 case SNDRV_PCM_FORMAT_S32_LE:
238 config->data_width = 32; 271 config->data_width = 32;
239 ccr = 0x10; 272 dev->ccr = 0x10;
240 xfer_resolution = 0x05; 273 dev->xfer_resolution = 0x05;
241 break; 274 break;
242 275
243 default: 276 default:
@@ -258,27 +291,9 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream,
258 return -EINVAL; 291 return -EINVAL;
259 } 292 }
260 293
261 i2s_disable_channels(dev, substream->stream); 294 dw_i2s_config(dev, substream->stream);
262 295
263 for (ch_reg = 0; ch_reg < (config->chan_nr / 2); ch_reg++) { 296 i2s_write_reg(dev->i2s_base, CCR, dev->ccr);
264 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
265 i2s_write_reg(dev->i2s_base, TCR(ch_reg),
266 xfer_resolution);
267 i2s_write_reg(dev->i2s_base, TFCR(ch_reg), 0x02);
268 irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg));
269 i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x30);
270 i2s_write_reg(dev->i2s_base, TER(ch_reg), 1);
271 } else {
272 i2s_write_reg(dev->i2s_base, RCR(ch_reg),
273 xfer_resolution);
274 i2s_write_reg(dev->i2s_base, RFCR(ch_reg), 0x07);
275 irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg));
276 i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x03);
277 i2s_write_reg(dev->i2s_base, RER(ch_reg), 1);
278 }
279 }
280
281 i2s_write_reg(dev->i2s_base, CCR, ccr);
282 297
283 config->sample_rate = params_rate(params); 298 config->sample_rate = params_rate(params);
284 299
@@ -394,6 +409,23 @@ static const struct snd_soc_component_driver dw_i2s_component = {
394}; 409};
395 410
396#ifdef CONFIG_PM 411#ifdef CONFIG_PM
412static int dw_i2s_runtime_suspend(struct device *dev)
413{
414 struct dw_i2s_dev *dw_dev = dev_get_drvdata(dev);
415
416 if (dw_dev->capability & DW_I2S_MASTER)
417 clk_disable(dw_dev->clk);
418 return 0;
419}
420
421static int dw_i2s_runtime_resume(struct device *dev)
422{
423 struct dw_i2s_dev *dw_dev = dev_get_drvdata(dev);
424
425 if (dw_dev->capability & DW_I2S_MASTER)
426 clk_enable(dw_dev->clk);
427 return 0;
428}
397 429
398static int dw_i2s_suspend(struct snd_soc_dai *dai) 430static int dw_i2s_suspend(struct snd_soc_dai *dai)
399{ 431{
@@ -410,6 +442,11 @@ static int dw_i2s_resume(struct snd_soc_dai *dai)
410 442
411 if (dev->capability & DW_I2S_MASTER) 443 if (dev->capability & DW_I2S_MASTER)
412 clk_enable(dev->clk); 444 clk_enable(dev->clk);
445
446 if (dai->playback_active)
447 dw_i2s_config(dev, SNDRV_PCM_STREAM_PLAYBACK);
448 if (dai->capture_active)
449 dw_i2s_config(dev, SNDRV_PCM_STREAM_CAPTURE);
413 return 0; 450 return 0;
414} 451}
415 452
@@ -459,8 +496,8 @@ static int dw_configure_dai(struct dw_i2s_dev *dev,
459 * Read component parameter registers to extract 496 * Read component parameter registers to extract
460 * the I2S block's configuration. 497 * the I2S block's configuration.
461 */ 498 */
462 u32 comp1 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_1); 499 u32 comp1 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp1);
463 u32 comp2 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_2); 500 u32 comp2 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp2);
464 u32 idx; 501 u32 idx;
465 502
466 if (COMP1_TX_ENABLED(comp1)) { 503 if (COMP1_TX_ENABLED(comp1)) {
@@ -503,7 +540,7 @@ static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev,
503 struct resource *res, 540 struct resource *res,
504 const struct i2s_platform_data *pdata) 541 const struct i2s_platform_data *pdata)
505{ 542{
506 u32 comp1 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_1); 543 u32 comp1 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp1);
507 u32 idx = COMP1_APB_DATA_WIDTH(comp1); 544 u32 idx = COMP1_APB_DATA_WIDTH(comp1);
508 int ret; 545 int ret;
509 546
@@ -607,6 +644,14 @@ static int dw_i2s_probe(struct platform_device *pdev)
607 if (pdata) { 644 if (pdata) {
608 dev->capability = pdata->cap; 645 dev->capability = pdata->cap;
609 clk_id = NULL; 646 clk_id = NULL;
647 dev->quirks = pdata->quirks;
648 if (dev->quirks & DW_I2S_QUIRK_COMP_REG_OFFSET) {
649 dev->i2s_reg_comp1 = pdata->i2s_reg_comp1;
650 dev->i2s_reg_comp2 = pdata->i2s_reg_comp2;
651 } else {
652 dev->i2s_reg_comp1 = I2S_COMP_PARAM_1;
653 dev->i2s_reg_comp2 = I2S_COMP_PARAM_2;
654 }
610 ret = dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata); 655 ret = dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata);
611 } else { 656 } else {
612 clk_id = "i2sclk"; 657 clk_id = "i2sclk";
@@ -649,7 +694,7 @@ static int dw_i2s_probe(struct platform_device *pdev)
649 goto err_clk_disable; 694 goto err_clk_disable;
650 } 695 }
651 } 696 }
652 697 pm_runtime_enable(&pdev->dev);
653 return 0; 698 return 0;
654 699
655err_clk_disable: 700err_clk_disable:
@@ -665,6 +710,7 @@ static int dw_i2s_remove(struct platform_device *pdev)
665 if (dev->capability & DW_I2S_MASTER) 710 if (dev->capability & DW_I2S_MASTER)
666 clk_disable_unprepare(dev->clk); 711 clk_disable_unprepare(dev->clk);
667 712
713 pm_runtime_disable(&pdev->dev);
668 return 0; 714 return 0;
669} 715}
670 716
@@ -677,12 +723,17 @@ static const struct of_device_id dw_i2s_of_match[] = {
677MODULE_DEVICE_TABLE(of, dw_i2s_of_match); 723MODULE_DEVICE_TABLE(of, dw_i2s_of_match);
678#endif 724#endif
679 725
726static const struct dev_pm_ops dwc_pm_ops = {
727 SET_RUNTIME_PM_OPS(dw_i2s_runtime_suspend, dw_i2s_runtime_resume, NULL)
728};
729
680static struct platform_driver dw_i2s_driver = { 730static struct platform_driver dw_i2s_driver = {
681 .probe = dw_i2s_probe, 731 .probe = dw_i2s_probe,
682 .remove = dw_i2s_remove, 732 .remove = dw_i2s_remove,
683 .driver = { 733 .driver = {
684 .name = "designware-i2s", 734 .name = "designware-i2s",
685 .of_match_table = of_match_ptr(dw_i2s_of_match), 735 .of_match_table = of_match_ptr(dw_i2s_of_match),
736 .pm = &dwc_pm_ops,
686 }, 737 },
687}; 738};
688 739