diff options
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 | ||