aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/dwc/designware_i2s.c
diff options
context:
space:
mode:
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