diff options
author | Takashi Iwai <tiwai@suse.de> | 2015-12-23 02:33:52 -0500 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2015-12-23 02:33:52 -0500 |
commit | f80e39e0225c01ee68764ef7594c3a29ab5ebabb (patch) | |
tree | f5a85085741a173c93fc8f21938528b65ed95e42 /sound/soc/dwc/designware_i2s.c | |
parent | 59c8231089be96165735585694a801ae58ec6c95 (diff) | |
parent | 822ad70a2f5c420da5baa9f4354e6b7813ca6da9 (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.c | 113 |
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 | ||
222 | static 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 | |||
216 | static int dw_i2s_hw_params(struct snd_pcm_substream *substream, | 250 | static 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 |
412 | static 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 | |||
421 | static 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 | ||
398 | static int dw_i2s_suspend(struct snd_soc_dai *dai) | 430 | static 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 | ||
655 | err_clk_disable: | 700 | err_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[] = { | |||
677 | MODULE_DEVICE_TABLE(of, dw_i2s_of_match); | 723 | MODULE_DEVICE_TABLE(of, dw_i2s_of_match); |
678 | #endif | 724 | #endif |
679 | 725 | ||
726 | static const struct dev_pm_ops dwc_pm_ops = { | ||
727 | SET_RUNTIME_PM_OPS(dw_i2s_runtime_suspend, dw_i2s_runtime_resume, NULL) | ||
728 | }; | ||
729 | |||
680 | static struct platform_driver dw_i2s_driver = { | 730 | static 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 | ||